[[Editor and AI improvements]]
- some editor improvements - made AI not recruit units that don't have a suitable movement type for the map
This commit is contained in:
parent
17d1e484bd
commit
4fd5afc631
12 changed files with 1106 additions and 395 deletions
|
@ -1,6 +1,6 @@
|
|||
SSSSSSShhmmmmmmmmmhhhhhhmmmmmmmmmmmmmm
|
||||
SCCCSSVhmmmmmmmmhhhSShSShhmmmmmmmmmmmm
|
||||
SC2CSSSSmmmmmmmhhSShhmhhVhmmmmmmmmmmmm
|
||||
SC2CSSSScmmmmmmhhSShhmhhVhmmmmmmmmmmmm
|
||||
SSChhhSSSmmmmShSSVShmmmmhmmmmhmhmmmmmm
|
||||
ShhmmmSSSSmSSSShhhShmmmmmmmmhSSShSSSmm
|
||||
VmmmmmmmSVSSSmmmmhShmmmmmhhSSSSSSSVSmm
|
||||
|
|
|
@ -52,7 +52,7 @@ Defeat:
|
|||
side=2
|
||||
canrecruit=1
|
||||
recruit=Vampire Bat,Naga
|
||||
recruitment_pattern=scout,swimmer
|
||||
recruitment_pattern=scout,fighter
|
||||
team_name=orcs
|
||||
#ifdef EASY
|
||||
gold=60
|
||||
|
|
337
data/themes/editor.cfg
Normal file
337
data/themes/editor.cfg
Normal file
|
@ -0,0 +1,337 @@
|
|||
[theme]
|
||||
name=null
|
||||
|
||||
[resolution]
|
||||
width=1
|
||||
height=1
|
||||
[/resolution]
|
||||
[/theme]
|
||||
|
||||
[theme]
|
||||
name=editor
|
||||
|
||||
[resolution]
|
||||
width=800
|
||||
height=600
|
||||
|
||||
[main_map]
|
||||
rect=0,26,887,768
|
||||
xanchor=left
|
||||
yanchor=top
|
||||
[/main_map]
|
||||
|
||||
[menu]
|
||||
title=main_menu
|
||||
image=lite
|
||||
items=save,quit
|
||||
rect=3,1,107,22
|
||||
xanchor=fixed
|
||||
yanchor=fixed
|
||||
[/menu]
|
||||
|
||||
#[menu]
|
||||
#title=game_menu
|
||||
#image=lite
|
||||
#items=undo,redo,save,preferences,quit
|
||||
#rect=110,1,215,22
|
||||
#xanchor=fixed
|
||||
#yanchor=fixed
|
||||
|
||||
# [menu]
|
||||
# is_context_menu=true
|
||||
# items=undo,redo,cycle,describeunit,speak,recruit,recall,createunit,renameunit,labelterrain,showenemymoves,bestenemymoves,endturn
|
||||
# [/menu]
|
||||
|
||||
# top panel
|
||||
[panel]
|
||||
image=misc/top-bg.png
|
||||
rect=0,0,1024,26
|
||||
xanchor=top
|
||||
yanchor=fixed
|
||||
[/panel]
|
||||
#[menu]
|
||||
#title=action_preferences
|
||||
#items=preferences
|
||||
#rect=5,1
|
||||
#xanchor=left
|
||||
#yanchor=top
|
||||
#[/menu]
|
||||
|
||||
# # rightside panel
|
||||
# [panel]
|
||||
# image=misc/rightside.png
|
||||
# rect=887,24,1024,284
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/panel]
|
||||
[mini_map]
|
||||
rect=900,32,1016,177
|
||||
xanchor=right
|
||||
yanchor=fixed
|
||||
[/mini_map]
|
||||
# [panel]
|
||||
# image=misc/rightside-bg.png
|
||||
# rect=887,284,1024,768
|
||||
# xanchor=right
|
||||
# yanchor=top
|
||||
# [/panel]
|
||||
# [panel]
|
||||
# image=misc/rightside-bottom.png
|
||||
# rect=887,713,0,768
|
||||
# xanchor=right
|
||||
# yanchor=bottom
|
||||
# [/panel]
|
||||
|
||||
# [menu]
|
||||
# title=action_endturn
|
||||
# items=endturn
|
||||
# rect=904,736
|
||||
# xanchor=right
|
||||
# yanchor=bottom
|
||||
# [/menu]
|
||||
|
||||
# [panel]
|
||||
# image=misc/status-bg.png
|
||||
# rect=175,2,280,21
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/panel]
|
||||
# [panel]
|
||||
# image=misc/status-bg.png
|
||||
# rect=285,2,390,21
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/panel]
|
||||
# [panel]
|
||||
# image=misc/status-bg.png
|
||||
# rect=395,2,495,21
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/panel]
|
||||
# [panel]
|
||||
# image=misc/status-bg.png
|
||||
# rect=500,2,600,21
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/panel]
|
||||
# [panel]
|
||||
# image=misc/status-bg.png
|
||||
# rect=605,2,705,21
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/panel]
|
||||
# [panel]
|
||||
# image=misc/status-bg.png
|
||||
# rect=710,2,810,21
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/panel]
|
||||
|
||||
# # gold icon
|
||||
# [label]
|
||||
# icon=misc/gold.png
|
||||
# text=gold
|
||||
# rect=290,3,306,19
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/label]
|
||||
# # villages icon
|
||||
# [label]
|
||||
# icon=misc/villages.png
|
||||
# text=villages
|
||||
# rect=400,3,416,19
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/label]
|
||||
# # units icon
|
||||
# [label]
|
||||
# icon=misc/units.png
|
||||
# text=units
|
||||
# rect=505,3,521,19
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/label]
|
||||
# # upkeep icon
|
||||
# [label]
|
||||
# icon=misc/upkeep.png
|
||||
# text=upkeep
|
||||
# rect=610,3,642,19
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/label]
|
||||
# # income icon
|
||||
# [label]
|
||||
# icon=misc/income.png
|
||||
# text=income
|
||||
# rect=715,3,747,19
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/label]
|
||||
|
||||
# [status]
|
||||
# # the time of day image
|
||||
# [time_of_day]
|
||||
# rect=900,185,1017,225
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/time_of_day]
|
||||
|
||||
# [observers]
|
||||
# font_size=12
|
||||
# rect=156,4,172,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/observers]
|
||||
|
||||
# #put the side playing indicator next to the turn indicator
|
||||
# [side_playing]
|
||||
# rect=178,4,193,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/side_playing]
|
||||
|
||||
# # the game status
|
||||
# [turn]
|
||||
# font_size=12
|
||||
# rect=200,4,275,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# prefix=
|
||||
# prefix_literal=""
|
||||
# [/turn]
|
||||
# [gold]
|
||||
# font_size=12
|
||||
# rect=310,4,385,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# prefix=
|
||||
# prefix_literal=""
|
||||
# [/gold]
|
||||
# [villages]
|
||||
# font_size=12
|
||||
# rect=420,4,490,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# prefix=
|
||||
# prefix_literal=""
|
||||
# [/villages]
|
||||
# [num_units]
|
||||
# font_size=12
|
||||
# rect=525,4,595,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# prefix=
|
||||
# prefix_literal=""
|
||||
# [/num_units]
|
||||
# [upkeep]
|
||||
# font_size=12
|
||||
# rect=647,4,700,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# prefix=
|
||||
# prefix_literal=""
|
||||
# [/upkeep]
|
||||
# [income]
|
||||
# font_size=12
|
||||
# rect=752,4,805,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# prefix=
|
||||
# prefix_literal=""
|
||||
# [/income]
|
||||
# [terrain]
|
||||
# font_size=12
|
||||
# rect=820,4,920,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/terrain]
|
||||
# [position]
|
||||
# font_size=12
|
||||
# rect=925,4,1020,20
|
||||
# xanchor=proportional
|
||||
# yanchor=fixed
|
||||
# [/position]
|
||||
|
||||
# #unit stats here
|
||||
# [unit_image]
|
||||
# rect=900,235,946,283
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_image]
|
||||
# [unit_description]
|
||||
# font_size=14
|
||||
# rect=900,290,1020,308
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_description]
|
||||
# [unit_type]
|
||||
# font_size=12
|
||||
# rect=900,308,1020,324
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_type]
|
||||
# [unit_level]
|
||||
# font_size=12
|
||||
# rect=900,324,1020,340
|
||||
# prefix=level
|
||||
# prefix_literal=" "
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_level]
|
||||
# [unit_alignment]
|
||||
# font_size=12
|
||||
# rect=900,340,1020,356
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_alignment]
|
||||
# [unit_traits]
|
||||
# font_size=12
|
||||
# rect=900,356,1020,372
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_traits]
|
||||
# [unit_abilities]
|
||||
# font_size=12
|
||||
# rect=900,372,1020,388
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_abilities]
|
||||
# [unit_status]
|
||||
# font_size=12
|
||||
# rect=958,241,1008,291
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_status]
|
||||
# [unit_moves]
|
||||
# font_size=12
|
||||
# rect=900,404,1020,420
|
||||
# prefix=movement
|
||||
# prefix_literal=": "
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_moves]
|
||||
# [unit_hp]
|
||||
# font_size=12
|
||||
# rect=900,420,1020,436
|
||||
# prefix=hp
|
||||
# prefix_literal=": "
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_hp]
|
||||
# [unit_xp]
|
||||
# font_size=12
|
||||
# rect=900,436,1020,452
|
||||
# prefix=xp
|
||||
# prefix_literal=": "
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_xp]
|
||||
# [unit_weapons]
|
||||
# font_size=12
|
||||
# rect=900,452,1020,680
|
||||
# xanchor=right
|
||||
# yanchor=fixed
|
||||
# [/unit_weapons]
|
||||
# [/status]
|
||||
[/resolution]
|
||||
[/theme]
|
|
@ -11,7 +11,7 @@ level=1
|
|||
alignment=lawful
|
||||
advanceto=Triton
|
||||
cost=12
|
||||
usage=swimmer
|
||||
usage=fighter
|
||||
unit_description="Skilled creatures of the sea, Mermen are powerful and quick in any watery environment, but struggle greatly to move on land."
|
||||
get_hit_sound=mermen-hit.wav
|
||||
[attack]
|
||||
|
|
|
@ -10,7 +10,7 @@ level=2
|
|||
alignment=lawful
|
||||
advanceto=null
|
||||
cost=28
|
||||
usage=swimmer
|
||||
usage=fighter
|
||||
unit_description="Merman Lords are masters of the sea. Skilled in use of the trident, Merman Lords easily defeat any enemy foolish enough to wander into their preferred environment."
|
||||
get_hit_sound=mermen-hit.wav
|
||||
[attack]
|
||||
|
|
|
@ -11,7 +11,7 @@ level=1
|
|||
alignment=neutral
|
||||
advanceto=Sea Hag
|
||||
cost=11
|
||||
usage=swimmer
|
||||
usage=fighter
|
||||
unit_description="Like the Mermen, Nagas are inhabitants of the seas. Smaller and more nimble than their counterparts, they share a distaste for dry land."
|
||||
get_hit_sound=mermen-hit.wav
|
||||
[attack]
|
||||
|
|
|
@ -11,7 +11,7 @@ level=2
|
|||
alignment=neutral
|
||||
advanceto=null
|
||||
cost=31
|
||||
usage=swimmer
|
||||
usage=fighter
|
||||
unit_description="Sea Hags are older and more experienced Nagas who have learned to control the water magic."
|
||||
get_hit_sound=mermen-hit.wav
|
||||
[attack]
|
||||
|
|
|
@ -11,7 +11,7 @@ level=2
|
|||
alignment=lawful
|
||||
advanceto=null
|
||||
cost=28
|
||||
usage=swimmer
|
||||
usage=fighter
|
||||
unit_description="Tritons are masters of the sea. Skilled in use of the trident, Tritons easily defeat any enemy foolish enough to wander into their preferred environment."
|
||||
get_hit_sound=mermen-hit.wav
|
||||
[attack]
|
||||
|
@ -40,7 +40,7 @@ level=2
|
|||
alignment=lawful
|
||||
advanceto=null
|
||||
cost=28
|
||||
usage=swimmer
|
||||
usage=fighter
|
||||
unit_description="Merman Lords are masters of the sea. Skilled in use of the trident, Merman Lords easily defeat any enemy foolish enough to wander into their preferred environment."
|
||||
get_hit_sound=mermen-hit.wav
|
||||
[attack]
|
||||
|
|
213
src/ai.cpp
213
src/ai.cpp
|
@ -61,10 +61,12 @@ bool ai::recruit_usage(const std::string& usage)
|
|||
for(std::map<std::string,unit_type>::const_iterator i =
|
||||
gameinfo_.unit_types.begin(); i != gameinfo_.unit_types.end(); ++i) {
|
||||
|
||||
if(i->second.usage() == usage && recruits.count(i->second.name())
|
||||
&& current_team().gold() - i->second.cost() > min_gold) {
|
||||
const std::string& name = i->second.name();
|
||||
|
||||
options.push_back(i->second.name());
|
||||
if(i->second.usage() == usage && recruits.count(name)
|
||||
&& current_team().gold() - i->second.cost() > min_gold
|
||||
&& not_recommended_units_.count(name) == 0) {
|
||||
options.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -771,23 +773,206 @@ bool ai::move_to_targets(std::map<gamemap::location,paths>& possible_moves, move
|
|||
return false;
|
||||
}
|
||||
|
||||
int ai::average_resistance_against(const unit_type& a, const unit_type& b) const
|
||||
{
|
||||
int sum = 0, weight_sum = 0;
|
||||
|
||||
const std::vector<attack_type>& attacks = b.attacks();
|
||||
for(std::vector<attack_type>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
|
||||
const int resistance = a.movement_type().resistance_against(*i);
|
||||
const int weight = i->damage()*i->num_attacks();
|
||||
|
||||
sum += resistance*weight;
|
||||
weight_sum += weight;
|
||||
}
|
||||
|
||||
return sum/weight_sum;
|
||||
}
|
||||
|
||||
int ai::compare_unit_types(const unit_type& a, const unit_type& b) const
|
||||
{
|
||||
const int a_effectiveness_vs_b = average_resistance_against(b,a);
|
||||
const int b_effectiveness_vs_a = average_resistance_against(a,b);
|
||||
|
||||
std::cerr << "comparison of '" << a.name() << " vs " << b.name() << ": "
|
||||
<< a_effectiveness_vs_b << " - " << b_effectiveness_vs_a << " = "
|
||||
<< (a_effectiveness_vs_b - b_effectiveness_vs_a) << "\n";
|
||||
return a_effectiveness_vs_b - b_effectiveness_vs_a;
|
||||
}
|
||||
|
||||
void ai::analyze_potential_recruit_combat()
|
||||
{
|
||||
if(unit_combat_scores_.empty() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_scope("analyze_potential_recruit_combat()");
|
||||
|
||||
const std::set<std::string>& recruits = current_team().recruits();
|
||||
for(std::set<std::string>::const_iterator i = recruits.begin(); i != recruits.end(); ++i) {
|
||||
const game_data::unit_type_map::const_iterator info = gameinfo_.unit_types.find(*i);
|
||||
if(info == gameinfo_.unit_types.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int score = 0;
|
||||
|
||||
for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
|
||||
if(j->second.can_recruit() || current_team().is_enemy(j->second.side()) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
score += compare_unit_types(info->second,j->second.type());
|
||||
}
|
||||
|
||||
std::cerr << "combat score of '" << *i << "': " << score << "\n";
|
||||
unit_combat_scores_[*i] = score;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct target_comparer_distance {
|
||||
target_comparer_distance(const gamemap::location& loc) : loc_(loc) {}
|
||||
|
||||
bool operator()(const ai::target& a, const ai::target& b) const {
|
||||
return distance_between(a.loc,loc_) < distance_between(b.loc,loc_);
|
||||
}
|
||||
|
||||
private:
|
||||
gamemap::location loc_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void ai::analyze_potential_recruit_movements()
|
||||
{
|
||||
if(unit_movement_scores_.empty() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const location& start = map_.starting_position(team_num_);
|
||||
if(map_.on_board(start) == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_scope("analyze_potential_recruit_movements()");
|
||||
|
||||
const unit_map::const_iterator leader = units_.find(start);
|
||||
|
||||
const int max_targets = 5;
|
||||
|
||||
const move_map srcdst, dstsrc;
|
||||
std::vector<target> targets = find_targets(leader,srcdst,dstsrc);
|
||||
if(targets.size() > max_targets) {
|
||||
std::sort(targets.begin(),targets.end(),target_comparer_distance(start));
|
||||
targets.erase(targets.begin()+max_targets,targets.end());
|
||||
}
|
||||
|
||||
const std::set<std::string>& recruits = current_team().recruits();
|
||||
|
||||
std::cerr << "targets: " << targets.size() << "\n";
|
||||
|
||||
int best_score = -1;
|
||||
|
||||
for(std::set<std::string>::const_iterator i = recruits.begin(); i != recruits.end(); ++i) {
|
||||
const game_data::unit_type_map::const_iterator info = gameinfo_.unit_types.find(*i);
|
||||
if(info == gameinfo_.unit_types.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const unit temp_unit(&info->second,team_num_);
|
||||
unit_map units;
|
||||
const temporary_unit_placer placer(units,start,temp_unit);
|
||||
|
||||
int cost = 0;
|
||||
int targets_reached = 0;
|
||||
int targets_missed = 0;
|
||||
|
||||
const shortest_path_calculator calc(temp_unit,current_team(),units,teams_,map_,state_);
|
||||
for(std::vector<target>::const_iterator t = targets.begin(); t != targets.end(); ++t) {
|
||||
std::cerr << "analyzing '" << *i << "' getting to target...\n";
|
||||
const paths::route& route = a_star_search(start,t->loc,100.0,calc);
|
||||
if(route.steps.empty() == false) {
|
||||
std::cerr << "made it: " << route.move_left << "\n";
|
||||
cost += route.move_left;
|
||||
++targets_reached;
|
||||
} else {
|
||||
std::cerr << "failed\n";
|
||||
++targets_missed;
|
||||
}
|
||||
}
|
||||
|
||||
if(targets_reached == 0 || targets_missed >= targets_reached*2) {
|
||||
unit_movement_scores_[*i] = 100000;
|
||||
not_recommended_units_.insert(*i);
|
||||
} else {
|
||||
const int average_cost = cost/targets_reached;
|
||||
const int score = (average_cost * (targets_reached+targets_missed))/targets_reached;
|
||||
unit_movement_scores_[*i] = score;
|
||||
if(best_score == -1 || score < best_score) {
|
||||
best_score = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(std::map<std::string,int>::iterator j = unit_movement_scores_.begin(); j != unit_movement_scores_.end(); ++j) {
|
||||
if(best_score > 0) {
|
||||
j->second = (j->second*10)/best_score;
|
||||
if(j->second > 15) {
|
||||
std::cerr << "recommending against recruiting '" << j->first << "' (score: " << j->second << ")\n";
|
||||
not_recommended_units_.insert(j->first);
|
||||
} else {
|
||||
std::cerr << "recommending recruit of '" << j->first << "' (score: " << j->second << ")\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(not_recommended_units_.size() == unit_movement_scores_.size()) {
|
||||
not_recommended_units_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ai::do_recruitment()
|
||||
{
|
||||
analyze_potential_recruit_movements();
|
||||
analyze_potential_recruit_combat();
|
||||
|
||||
//currently just spend all the gold we can!
|
||||
const int min_gold = 0;
|
||||
|
||||
const int villages = map_.villages().size();
|
||||
int taken_villages = 0;
|
||||
for(size_t j = 0; j != teams_.size(); ++j) {
|
||||
taken_villages += teams_[j].villages().size();
|
||||
size_t neutral_villages = 0;
|
||||
|
||||
//we recruit the initial allocation of scouts based on how many neutral villages
|
||||
//there are that are closer to us than to other keeps.
|
||||
const std::vector<location>& villages = map_.villages();
|
||||
for(std::vector<location>::const_iterator v = villages.begin(); v != villages.end(); ++v) {
|
||||
const int owner = village_owner(*v,teams_);
|
||||
if(owner == -1) {
|
||||
const size_t distance = distance_between(map_.starting_position(team_num_),*v);
|
||||
|
||||
bool closest = true;
|
||||
for(std::vector<team>::const_iterator i = teams_.begin(); i != teams_.end(); ++i) {
|
||||
const int index = i - teams_.begin() + 1;
|
||||
if(team_num_ != index) {
|
||||
const gamemap::location& loc = map_.starting_position(index);
|
||||
if(distance_between(loc,*v) < distance) {
|
||||
closest = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(closest) {
|
||||
++neutral_villages;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int neutral_villages = villages - taken_villages;
|
||||
|
||||
//the villages per scout parameter is assumed to be based on a 2-side battle.
|
||||
//in a greater than 2 side battle, we want to recruit less scouts, since the villages
|
||||
//are going to be taken more quickly, and we will need combat units faster.
|
||||
const int villages_per_scout = (current_team().villages_per_scout()*teams_.size())/2;
|
||||
//the villages per scout is for a two-side battle, accounting for all neutral villages
|
||||
//on the map. We only look at villages closer to us, so we halve it, making us get
|
||||
//twice as many scouts
|
||||
const int villages_per_scout = current_team().villages_per_scout()/2;
|
||||
|
||||
//get scouts depending on how many neutral villages there are
|
||||
int scouts_wanted = villages_per_scout > 0 ? neutral_villages/villages_per_scout : 0;
|
||||
|
@ -947,7 +1132,7 @@ int ai::rate_terrain(const unit& u, const gamemap::location& loc)
|
|||
if(map_.is_village(terrain)) {
|
||||
const int owner = village_owner(loc,teams_);
|
||||
|
||||
if(owner == team_num_) {
|
||||
if(owner+1 == team_num_) {
|
||||
rating += friendly_village_value;
|
||||
} else if(owner == -1) {
|
||||
rating += neutral_village_value;
|
||||
|
|
40
src/ai.hpp
40
src/ai.hpp
|
@ -362,13 +362,6 @@ public:
|
|||
int choose_weapon(const location& att, const location& def,
|
||||
battle_stats& cur_stats, gamemap::TERRAIN terrain);
|
||||
|
||||
private:
|
||||
std::vector<attack_analysis> analyze_targets(
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc
|
||||
);
|
||||
|
||||
|
||||
struct target {
|
||||
target(const location& pos, double val) : loc(pos), value(val)
|
||||
{}
|
||||
|
@ -376,6 +369,13 @@ private:
|
|||
double value;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<attack_analysis> analyze_targets(
|
||||
const move_map& srcdst, const move_map& dstsrc,
|
||||
const move_map& enemy_srcdst, const move_map& enemy_dstsrc
|
||||
);
|
||||
|
||||
|
||||
std::vector<target> find_targets(unit_map::const_iterator leader, const move_map& enemy_srcdst, const move_map& enemy_dstsrc);
|
||||
|
||||
std::pair<location,location> choose_move(std::vector<target>& targets,const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc);
|
||||
|
@ -392,6 +392,32 @@ private:
|
|||
const gamestatus& state_;
|
||||
bool consider_combat_;
|
||||
std::vector<target> additional_targets_;
|
||||
|
||||
//function which will analyze all the units that this side can recruit and rate
|
||||
//their movement types. Ratings will be placed in 'unit_movement_scores_', with
|
||||
//lower scores being better, and the lowest possible rating being '10'.
|
||||
void analyze_potential_recruit_movements();
|
||||
|
||||
std::map<std::string,int> unit_movement_scores_;
|
||||
std::set<std::string> not_recommended_units_;
|
||||
|
||||
//function which will analyze all the units that this side can recruit and rate
|
||||
//their fighting suitability against enemy units. Ratings will be placed in
|
||||
//'unit_combat_scores_' with a '0' rating indicating that the unit is 'average'
|
||||
//against enemy units, negative ratings meaning they are poorly suited, and
|
||||
//positive ratings meaning they are well suited
|
||||
void analyze_potential_recruit_combat();
|
||||
|
||||
std::map<std::string,int> unit_combat_scores_;
|
||||
|
||||
//function which rates two unit types for their suitability against each other.
|
||||
//returns 0 if the units are equally matched, a positive number if a is suited
|
||||
//against b, and a negative number if b is suited against a.
|
||||
int compare_unit_types(const unit_type& a, const unit_type& b) const;
|
||||
|
||||
//function which calculates the average resistance unit type a has against
|
||||
//the attacks of unit type b.
|
||||
int average_resistance_against(const unit_type& a, const unit_type& b) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -540,7 +540,7 @@ void draw_label(display& disp, SDL_Surface* target, const theme::label& label)
|
|||
|
||||
void display::draw(bool update,bool force)
|
||||
{
|
||||
if(!panelsDrawn_ && !teams_.empty()) {
|
||||
if(!panelsDrawn_) {
|
||||
SDL_Surface* const screen = screen_.getSurface();
|
||||
|
||||
const std::vector<theme::panel>& panels = theme_.panels();
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
/*
|
||||
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
|
||||
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
|
||||
Copyright (C) 2003 by David Whire <davidnwhite@optusnet.com.au>
|
||||
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_keysym.h"
|
||||
|
||||
|
@ -18,7 +17,6 @@
|
|||
#include "../config.hpp"
|
||||
#include "../cursor.hpp"
|
||||
#include "../dialogs.hpp"
|
||||
#include "../display.hpp"
|
||||
#include "../filesystem.hpp"
|
||||
#include "../font.hpp"
|
||||
#include "../game_config.hpp"
|
||||
|
@ -30,11 +28,14 @@
|
|||
#include "../pathfind.hpp"
|
||||
#include "../playlevel.hpp"
|
||||
#include "../preferences.hpp"
|
||||
#include "../sdl_utils.hpp"
|
||||
#include "../team.hpp"
|
||||
#include "../unit_types.hpp"
|
||||
#include "../unit.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "../video.hpp"
|
||||
#include "../events.hpp"
|
||||
|
||||
#include "editor.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
|
@ -42,397 +43,559 @@
|
|||
#include <string>
|
||||
|
||||
namespace {
|
||||
const size_t nterrains = 24;
|
||||
const size_t terrain_size = 35;
|
||||
const size_t terrain_padding = 2;
|
||||
const size_t terrain_space = terrain_size + terrain_padding;
|
||||
const size_t button_x = 50;
|
||||
const size_t top_button_y = 200;
|
||||
const size_t palette_x = 40;
|
||||
const size_t palette_y = top_button_y + 40;
|
||||
const size_t bot_button_y = palette_y + terrain_space*nterrains/2;
|
||||
|
||||
bool is_invalid_terrain(char c) { return c == ' ' || c == '~'; }
|
||||
|
||||
const int undo_limit = 20;
|
||||
const unsigned int undo_limit = 100;
|
||||
const double zoom_amount = 5.0;
|
||||
// Milliseconds to sleep in every iteration of the main loop.
|
||||
const unsigned int sdl_delay = 20;
|
||||
const size_t default_terrain_size = 35;
|
||||
}
|
||||
|
||||
struct map_undo_action {
|
||||
map_undo_action(const gamemap::TERRAIN& old_tr,
|
||||
const gamemap::TERRAIN& new_tr,
|
||||
const gamemap::location& lc)
|
||||
: old_terrain(old_tr), new_terrain(new_tr), location(lc) {}
|
||||
gamemap::TERRAIN old_terrain;
|
||||
gamemap::TERRAIN new_terrain;
|
||||
gamemap::location location;
|
||||
};
|
||||
typedef std::deque<map_undo_action> map_undo_list;
|
||||
namespace map_editor {
|
||||
|
||||
void drawbar(display& disp);
|
||||
bool drawterrainpalette(display& disp, int start, gamemap::TERRAIN selected, gamemap map);
|
||||
int tileselected(int x, int y, display& disp);
|
||||
editor_event_handler::editor_event_handler(map_editor &editor)
|
||||
: editor_(editor) {}
|
||||
|
||||
// A handler that intercepts key events.
|
||||
class editor_key_handler : public events::handler {
|
||||
public:
|
||||
editor_key_handler(display &gui, map_undo_list &undo_stack, map_undo_list &redo_stack, gamemap &map);
|
||||
virtual void handle_event(const SDL_Event &event);
|
||||
private:
|
||||
map_undo_list &undo_stack_, &redo_stack_;
|
||||
display &gui_;
|
||||
gamemap &map_;
|
||||
const double zoom_amount_;
|
||||
};
|
||||
void editor_event_handler::handle_event(const SDL_Event &event) {
|
||||
int mousex, mousey;
|
||||
SDL_GetMouseState(&mousex,&mousey);
|
||||
const SDL_KeyboardEvent keyboard_event = event.key;
|
||||
handle_keyboard_event(keyboard_event, mousex, mousey);
|
||||
|
||||
editor_key_handler::editor_key_handler(display &gui, map_undo_list &undo_stack, map_undo_list &redo_stack, gamemap &map)
|
||||
: gui_(gui), undo_stack_(undo_stack), redo_stack_(redo_stack), map_(map), zoom_amount_(5.0) {}
|
||||
const SDL_MouseButtonEvent mouse_button_event = event.button;
|
||||
handle_mouse_button_event(mouse_button_event, mousex, mousey);
|
||||
}
|
||||
|
||||
void editor_key_handler::handle_event(const SDL_Event &event) {
|
||||
const SDL_KeyboardEvent keyboard_event = event.key;
|
||||
if (keyboard_event.type == SDL_KEYDOWN) {
|
||||
const SDLKey sym = keyboard_event.keysym.sym;
|
||||
if(sym == SDLK_z)
|
||||
gui_.zoom(zoom_amount_);
|
||||
void editor_event_handler::handle_keyboard_event(const SDL_KeyboardEvent &event,
|
||||
const int mousex, const int mousey) {
|
||||
if (event.type == SDL_KEYDOWN) {
|
||||
const SDLKey sym = event.keysym.sym;
|
||||
if (sym == SDLK_z) {
|
||||
editor_.zoom_in();
|
||||
}
|
||||
if (sym == SDLK_x) {
|
||||
editor_.zoom_out();
|
||||
}
|
||||
if (sym == SDLK_d) {
|
||||
editor_.zoom_default();
|
||||
}
|
||||
if (sym == SDLK_u) {
|
||||
editor_.undo();
|
||||
}
|
||||
if (sym == SDLK_r) {
|
||||
editor_.redo();
|
||||
}
|
||||
if (sym == SDLK_s) {
|
||||
editor_.save_map("", true);
|
||||
}
|
||||
if(sym == SDLK_ESCAPE) {
|
||||
editor_.set_abort(ABORT_NORMALLY);
|
||||
}
|
||||
|
||||
// Check if a number key was pressed.
|
||||
for(int num_key = SDLK_1; num_key != SDLK_9; ++num_key) {
|
||||
if(sym == num_key) {
|
||||
const unsigned int number_pressed = num_key + 1 - SDLK_1;
|
||||
editor_.set_starting_position(number_pressed, mousex, mousey);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sym == SDLK_x)
|
||||
gui_.zoom(-zoom_amount_);
|
||||
void editor_event_handler::handle_mouse_button_event(const SDL_MouseButtonEvent &event,
|
||||
const int mousex, const int mousey) {
|
||||
if (event.type == SDL_MOUSEBUTTONDOWN) {
|
||||
const Uint8 button = event.button;
|
||||
if (button == SDL_BUTTON_WHEELUP) {
|
||||
editor_.scroll_palette_up();
|
||||
}
|
||||
if (button == SDL_BUTTON_WHEELDOWN) {
|
||||
editor_.scroll_palette_down();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sym == SDLK_d)
|
||||
gui_.default_zoom();
|
||||
map_editor::map_editor(display &gui, gamemap &map)
|
||||
: gui_(gui), map_(map), tup_(gui, "", gui::button::TYPE_PRESS, "uparrow-button"),
|
||||
tdown_(gui, "", gui::button::TYPE_PRESS, "downarrow-button"), abort_(DONT_ABORT),
|
||||
tstart_(0), num_operations_since_save_(0) {
|
||||
|
||||
if(sym == SDLK_u) {
|
||||
if(!undo_stack_.empty()) {
|
||||
map_undo_action action = undo_stack_.back();
|
||||
map_.set_terrain(action.location,action.old_terrain);
|
||||
undo_stack_.pop_back();
|
||||
redo_stack_.push_back(action);
|
||||
if(redo_stack_.size() > undo_limit)
|
||||
redo_stack_.pop_front();
|
||||
gamemap::location locs[7];
|
||||
locs[0] = action.location;
|
||||
get_adjacent_tiles(action.location,locs+1);
|
||||
for(int i = 0; i != 7; ++i) {
|
||||
gui_.draw_tile(locs[i].x,locs[i].y);
|
||||
terrains_ = map_.get_terrain_precedence();
|
||||
terrains_.erase(std::remove_if(terrains_.begin(), terrains_.end(), is_invalid_terrain),
|
||||
terrains_.end());
|
||||
if(terrains_.empty()) {
|
||||
std::cerr << "No terrain found.\n";
|
||||
abort_ = ABORT_HARD;
|
||||
}
|
||||
selected_terrain_ = terrains_[1];
|
||||
|
||||
// Set size specs.
|
||||
adjust_sizes(gui_);
|
||||
|
||||
tup_.set_xy(gui.mapx() + size_specs_.button_x, size_specs_.top_button_y);
|
||||
tdown_.set_xy(gui.mapx() + size_specs_.button_x, size_specs_.bot_button_y);
|
||||
}
|
||||
|
||||
void map_editor::adjust_sizes(const display &disp) {
|
||||
size_specs_.terrain_size = default_terrain_size;
|
||||
size_specs_.terrain_padding = 2;
|
||||
size_specs_.terrain_space = size_specs_.terrain_size + size_specs_.terrain_padding;
|
||||
size_specs_.palette_x = 40;
|
||||
size_specs_.button_x = size_specs_.palette_x + size_specs_.terrain_space - 12;
|
||||
size_specs_.top_button_y = 200;
|
||||
size_specs_.palette_y = size_specs_.top_button_y + 40;
|
||||
size_specs_.bot_button_y = disp.y() - 20 - 24;
|
||||
size_t space_for_terrains = size_specs_.bot_button_y -
|
||||
(size_specs_.top_button_y + 24);
|
||||
space_for_terrains = space_for_terrains / size_specs_.terrain_space % 2 == 0 ?
|
||||
space_for_terrains : space_for_terrains - size_specs_.terrain_space;
|
||||
size_specs_.nterrains = minimum((space_for_terrains / size_specs_.terrain_space) * 2,
|
||||
terrains_.size());
|
||||
}
|
||||
|
||||
void map_editor::undo() {
|
||||
if(!undo_stack_.empty()) {
|
||||
--num_operations_since_save_;
|
||||
map_undo_action action = undo_stack_.back();
|
||||
map_.set_terrain(action.location,action.old_terrain);
|
||||
undo_stack_.pop_back();
|
||||
redo_stack_.push_back(action);
|
||||
if(redo_stack_.size() > undo_limit)
|
||||
redo_stack_.pop_front();
|
||||
gamemap::location locs[7];
|
||||
locs[0] = action.location;
|
||||
get_adjacent_tiles(action.location,locs+1);
|
||||
for(int i = 0; i != 7; ++i) {
|
||||
gui_.draw_tile(locs[i].x,locs[i].y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void map_editor::redo() {
|
||||
if(!redo_stack_.empty()) {
|
||||
++num_operations_since_save_;
|
||||
map_undo_action action = redo_stack_.back();
|
||||
map_.set_terrain(action.location,action.new_terrain);
|
||||
redo_stack_.pop_back();
|
||||
undo_stack_.push_back(action);
|
||||
if(undo_stack_.size() > undo_limit)
|
||||
undo_stack_.pop_front();
|
||||
gamemap::location locs[7];
|
||||
locs[0] = action.location;
|
||||
get_adjacent_tiles(action.location,locs+1);
|
||||
for(int i = 0; i != 7; ++i) {
|
||||
gui_.draw_tile(locs[i].x,locs[i].y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void map_editor::zoom_in() {
|
||||
gui_.zoom(zoom_amount);
|
||||
}
|
||||
|
||||
void map_editor::zoom_out() {
|
||||
gui_.zoom(-zoom_amount);
|
||||
}
|
||||
|
||||
void map_editor::zoom_default() {
|
||||
gui_.default_zoom();
|
||||
}
|
||||
|
||||
void map_editor::set_starting_position(const int player, const int x, const int y) {
|
||||
const gamemap::location loc = gui_.hex_clicked_on(x, y);
|
||||
if(map_.on_board(loc)) {
|
||||
map_.set_terrain(loc, gamemap::CASTLE);
|
||||
}
|
||||
else {
|
||||
std::cerr << "Warning: Set starting position to a hex not on the board." << std::endl;
|
||||
}
|
||||
map_.set_starting_position(player, loc);
|
||||
gui_.invalidate_all();
|
||||
}
|
||||
|
||||
void map_editor::set_abort(const ABORT_MODE abort) {
|
||||
abort_ = abort;
|
||||
}
|
||||
|
||||
void map_editor::set_file_to_save_as(const std::string filename) {
|
||||
filename_ = filename;
|
||||
}
|
||||
|
||||
void map_editor::left_button_down_(const int mousex, const int mousey) {
|
||||
const gamemap::location& loc = gui_.minimap_location_on(mousex,mousey);
|
||||
if (loc.valid()) {
|
||||
gui_.scroll_to_tile(loc.x,loc.y,display::WARP,false);
|
||||
}
|
||||
else {
|
||||
const gamemap::location hex = gui_.hex_clicked_on(mousex,mousey);
|
||||
if(map_.on_board(hex)) {
|
||||
const gamemap::TERRAIN terrain = map_[hex.x][hex.y];
|
||||
if(selected_terrain_ != terrain) {
|
||||
if(key_[SDLK_RCTRL] || key_[SDLK_LCTRL]) {
|
||||
selected_terrain_ = terrain;
|
||||
} else {
|
||||
++num_operations_since_save_;
|
||||
undo_stack_.push_back(map_undo_action(terrain,selected_terrain_,hex));
|
||||
if(undo_stack_.size() > undo_limit)
|
||||
undo_stack_.pop_front();
|
||||
map_.set_terrain(hex,selected_terrain_);
|
||||
|
||||
gamemap::location locs[7];
|
||||
locs[0] = hex;
|
||||
get_adjacent_tiles(hex,locs+1);
|
||||
for(int i = 0; i != 7; ++i) {
|
||||
gui_.draw_tile(locs[i].x,locs[i].y);
|
||||
}
|
||||
gui_.draw();
|
||||
gui_.recalculate_minimap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int tselect = tile_selected(mousex, mousey, gui_, size_specs_);
|
||||
if(tselect >= 0)
|
||||
selected_terrain_ = terrains_[tstart_+tselect];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void map_editor::right_button_down_(const int mousex, const int mousey) {
|
||||
}
|
||||
void map_editor::middle_button_down_(const int mousex, const int mousey) {
|
||||
}
|
||||
|
||||
void map_editor::scroll_palette_down() {
|
||||
if(tstart_ + size_specs_.nterrains + 2 <= terrains_.size()) {
|
||||
tstart_ += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void map_editor::scroll_palette_up() {
|
||||
if(tstart_ >= 2) {
|
||||
tstart_ -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool map_editor::confirm_exit_and_save_() {
|
||||
int exitRes = gui::show_dialog(gui_, NULL, "Exit?",
|
||||
"Do you want to exit the map editor?", gui::YES_NO);
|
||||
if (exitRes != 0) {
|
||||
return false;
|
||||
}
|
||||
if (num_operations_since_save_ > 0) {
|
||||
int saveRes = gui::show_dialog(gui_, NULL, "Save?",
|
||||
"Do you want to save before exiting?", gui::YES_NO);
|
||||
if(saveRes == 0) {
|
||||
if (!save_map("", false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool map_editor::save_map(const std::string fn, const bool display_confirmation) {
|
||||
std::string filename = fn;
|
||||
if (filename == "") {
|
||||
filename = filename_;
|
||||
}
|
||||
try {
|
||||
write_file(filename,map_.write());
|
||||
num_operations_since_save_ = 0;
|
||||
if (display_confirmation) {
|
||||
gui::show_dialog(gui_, NULL, "Saved", "Map saved.", gui::OK_ONLY);
|
||||
}
|
||||
}
|
||||
catch (io_exception) {
|
||||
gui::show_dialog(gui_, NULL, "Save Failed", "Could not save the map.", gui::OK_ONLY);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void map_editor::main_loop() {
|
||||
events::event_context ec;
|
||||
editor_event_handler event_handler(*this);
|
||||
const double scroll_speed = preferences::scroll_speed();
|
||||
|
||||
while (abort_ == DONT_ABORT) {
|
||||
int mousex, mousey;
|
||||
const int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
|
||||
const bool left_button_down = mouse_flags & SDL_BUTTON_LMASK;
|
||||
const bool right_button_down = mouse_flags & SDL_BUTTON_RMASK;
|
||||
const bool middle_button_down = mouse_flags & SDL_BUTTON_MMASK;
|
||||
|
||||
const gamemap::location cur_hex = gui_.hex_clicked_on(mousex,mousey);
|
||||
gui_.highlight_hex(cur_hex);
|
||||
|
||||
const theme::menu* const m = gui_.menu_pressed(mousex,mousey,mouse_flags&SDL_BUTTON_LMASK);
|
||||
|
||||
if(m != NULL) {
|
||||
std::cerr << "menu pressed\n";
|
||||
const SDL_Rect& menu_loc = m->location(gui_.screen_area());
|
||||
static const std::string style = "menu2";
|
||||
const int res = gui::show_dialog(gui_,NULL,"","",
|
||||
gui::MESSAGE,&m->items(),
|
||||
NULL,"",NULL,NULL,NULL,
|
||||
menu_loc.x+1,menu_loc.y,
|
||||
&style);
|
||||
if(0 <= res && res < m->items().size()) {
|
||||
if(m->items()[res] == "save") {
|
||||
try {
|
||||
write_file(filename_,map_.write());
|
||||
} catch(io_exception&) {
|
||||
gui::show_dialog(gui_,NULL,"",
|
||||
"Save failed",gui::MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sym == SDLK_r) {
|
||||
if(!redo_stack_.empty()) {
|
||||
map_undo_action action = redo_stack_.back();
|
||||
map_.set_terrain(action.location,action.new_terrain);
|
||||
redo_stack_.pop_back();
|
||||
undo_stack_.push_back(action);
|
||||
if(undo_stack_.size() > undo_limit)
|
||||
undo_stack_.pop_front();
|
||||
gamemap::location locs[7];
|
||||
locs[0] = action.location;
|
||||
get_adjacent_tiles(action.location,locs+1);
|
||||
for(int i = 0; i != 7; ++i) {
|
||||
gui_.draw_tile(locs[i].x,locs[i].y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int mousex, mousey;
|
||||
const int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
|
||||
const gamemap::location cur_hex = gui_.hex_clicked_on(mousex,mousey);
|
||||
for(int num_key = SDLK_1; num_key != SDLK_9; ++num_key) {
|
||||
if(sym == num_key) {
|
||||
if(map_.on_board(cur_hex)) {
|
||||
map_.set_terrain(cur_hex,gamemap::CASTLE);
|
||||
}
|
||||
map_.set_starting_position(num_key+1-SDLK_1,cur_hex);
|
||||
gui_.invalidate_all();
|
||||
else if(m->items()[res] == "quit")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(key_[SDLK_UP] || mousey == 0) {
|
||||
gui_.scroll(0.0,-scroll_speed);
|
||||
}
|
||||
if(key_[SDLK_DOWN] || mousey == gui_.y()-1) {
|
||||
gui_.scroll(0.0,scroll_speed);
|
||||
}
|
||||
if(key_[SDLK_LEFT] || mousex == 0) {
|
||||
gui_.scroll(-scroll_speed,0.0);
|
||||
}
|
||||
if(key_[SDLK_RIGHT] || mousex == gui_.x()-1) {
|
||||
gui_.scroll(scroll_speed,0.0);
|
||||
}
|
||||
|
||||
if (left_button_down) {
|
||||
left_button_down_(mousex, mousey);
|
||||
}
|
||||
if (right_button_down) {
|
||||
right_button_down_(mousex, mousey);
|
||||
}
|
||||
if (middle_button_down) {
|
||||
middle_button_down_(mousex, mousey);
|
||||
}
|
||||
|
||||
gui_.draw(false);
|
||||
if(drawterrainpalette(gui_, tstart_, selected_terrain_, map_, size_specs_) == false)
|
||||
scroll_palette_down();
|
||||
|
||||
if(tup_.process(mousex,mousey,left_button_down)) {
|
||||
scroll_palette_up();
|
||||
}
|
||||
|
||||
if(tdown_.process(mousex,mousey,left_button_down)) {
|
||||
scroll_palette_down();
|
||||
}
|
||||
|
||||
gui_.update_display();
|
||||
SDL_Delay(sdl_delay);
|
||||
events::pump();
|
||||
if (abort_ == ABORT_NORMALLY) {
|
||||
if (!confirm_exit_and_save_()) {
|
||||
set_abort(DONT_ABORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
game_config::editor = true;
|
||||
|
||||
if(argc > 2) {
|
||||
std::cout << "usage: " << argv[0] << " map-name\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
const double scroll_speed = preferences::scroll_speed();
|
||||
|
||||
CVideo video;
|
||||
|
||||
video.setMode(1024,768,16,0);
|
||||
|
||||
const font::manager font_manager;
|
||||
const preferences::manager prefs_manager;
|
||||
const image::manager image_manager;
|
||||
|
||||
preproc_map defines_map;
|
||||
defines_map["MEDIUM"] = preproc_define();
|
||||
defines_map["NORMAL"] = preproc_define();
|
||||
config cfg(preprocess_file("data/game.cfg",&defines_map));
|
||||
|
||||
set_language("English");
|
||||
|
||||
std::string filename;
|
||||
|
||||
if(argc == 1) {
|
||||
|
||||
const std::string path = "data/maps/";
|
||||
|
||||
display::unit_map u_map;
|
||||
config dummy_cfg("");
|
||||
|
||||
config dummy_theme;
|
||||
display disp(u_map,video,gamemap(dummy_cfg,"1"),gamestatus(dummy_cfg,0),
|
||||
std::vector<team>(), dummy_theme, cfg);
|
||||
|
||||
std::vector<std::string> files;
|
||||
get_files_in_dir(path,&files);
|
||||
|
||||
files.push_back("New Map...");
|
||||
|
||||
const int res = gui::show_dialog(disp,NULL,"","Choose map to edit:",gui::OK_CANCEL,&files);
|
||||
if(res < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(res == int(files.size()-1)) {
|
||||
filename = "new-map";
|
||||
gui::show_dialog(disp,NULL,"","Create new map",gui::OK_ONLY,NULL,NULL,"",&filename);
|
||||
if(filename == "")
|
||||
return 0;
|
||||
} else {
|
||||
filename = files[res];
|
||||
}
|
||||
|
||||
filename = path + filename;
|
||||
|
||||
} else if(argc == 2) {
|
||||
filename = argv[1];
|
||||
}
|
||||
|
||||
std::cout << "a\n";
|
||||
std::string mapdata = read_file(filename);
|
||||
if(mapdata.empty()) {
|
||||
for(int i = 0; i != 30; ++i) {
|
||||
mapdata = mapdata + "gggggggggggggggggggggggggggggggggggggg\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "b\n";
|
||||
gamemap map(cfg,mapdata);
|
||||
|
||||
CKey key;
|
||||
gamestatus status(cfg,0);
|
||||
std::vector<team> teams;
|
||||
|
||||
const config* const theme_cfg = cfg.find_child("theme","name",preferences::theme());
|
||||
config dummy_theme;
|
||||
std::map<gamemap::location,unit> units;
|
||||
display gui(units,video,map,status,teams,theme_cfg ? *theme_cfg : dummy_theme, cfg);
|
||||
|
||||
std::vector<std::string> terrain_names;
|
||||
std::vector<gamemap::TERRAIN> terrains = map.get_terrain_precedence();
|
||||
terrains.erase(std::remove_if(terrains.begin(),terrains.end(),is_invalid_terrain),terrains.end());
|
||||
if(terrains.empty()) {
|
||||
std::cerr << "No terrain found\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(std::vector<gamemap::TERRAIN>::const_iterator t = terrains.begin(); t != terrains.end(); ++t) {
|
||||
terrain_names.push_back(map.terrain_name(*t));
|
||||
}
|
||||
|
||||
gui::button tup(gui, "", gui::button::TYPE_PRESS,"uparrow-button");
|
||||
gui::button tdown(gui, "", gui::button::TYPE_PRESS,"downarrow-button");
|
||||
tup.set_xy(gui.mapx() + button_x, top_button_y);
|
||||
tdown.set_xy(gui.mapx() + button_x, bot_button_y);
|
||||
|
||||
gamemap::TERRAIN selected_terrain = terrains[1];
|
||||
int tstart = 0;
|
||||
|
||||
//Draw the nice background bar
|
||||
drawbar(gui);
|
||||
|
||||
map_undo_list undo_stack;
|
||||
map_undo_list redo_stack;
|
||||
|
||||
events::event_context ec;
|
||||
editor_key_handler key_handler(gui, undo_stack, redo_stack, map);
|
||||
std::cerr << "starting for(;;)\n";
|
||||
for(;;) {
|
||||
if(key[SDLK_ESCAPE])
|
||||
break;
|
||||
|
||||
int mousex, mousey;
|
||||
const int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
|
||||
const bool new_left_button = mouse_flags & SDL_BUTTON_LMASK;
|
||||
const bool new_right_button = mouse_flags & SDL_BUTTON_RMASK;
|
||||
|
||||
if(key[SDLK_UP] || mousey == 0)
|
||||
gui.scroll(0.0,-scroll_speed);
|
||||
|
||||
if(key[SDLK_DOWN] || mousey == gui.y()-1)
|
||||
gui.scroll(0.0,scroll_speed);
|
||||
|
||||
if(key[SDLK_LEFT] || mousex == 0)
|
||||
gui.scroll(-scroll_speed,0.0);
|
||||
|
||||
if(key[SDLK_RIGHT] || mousex == gui.x()-1)
|
||||
gui.scroll(scroll_speed,0.0);
|
||||
|
||||
const gamemap::location cur_hex = gui.hex_clicked_on(mousex,mousey);
|
||||
gui.highlight_hex(cur_hex);
|
||||
if(new_left_button) {
|
||||
|
||||
const gamemap::location& loc = gui.minimap_location_on(mousex,mousey);
|
||||
if (loc.valid()) {
|
||||
gui.scroll_to_tile(loc.x,loc.y,display::WARP,false);
|
||||
continue;
|
||||
}
|
||||
|
||||
const gamemap::location hex = gui.hex_clicked_on(mousex,mousey);
|
||||
if(map.on_board(hex)) {
|
||||
const gamemap::TERRAIN terrain = map[hex.x][hex.y];
|
||||
if(selected_terrain != terrain) {
|
||||
if(key[SDLK_RCTRL] || key[SDLK_LCTRL]) {
|
||||
selected_terrain = terrain;
|
||||
} else {
|
||||
undo_stack.push_back(map_undo_action(terrain,selected_terrain,hex));
|
||||
if(undo_stack.size() > undo_limit)
|
||||
undo_stack.pop_front();
|
||||
map.set_terrain(hex,selected_terrain);
|
||||
|
||||
gamemap::location locs[7];
|
||||
locs[0] = hex;
|
||||
get_adjacent_tiles(hex,locs+1);
|
||||
for(int i = 0; i != 7; ++i) {
|
||||
gui.draw_tile(locs[i].x,locs[i].y);
|
||||
}
|
||||
|
||||
gui.draw();
|
||||
gui.recalculate_minimap();
|
||||
}
|
||||
}
|
||||
}else{
|
||||
int tselect = tileselected(mousex,mousey,gui);
|
||||
if(tselect >= 0)
|
||||
selected_terrain = terrains[tstart+tselect];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gui.draw(false);
|
||||
if(drawterrainpalette(gui, tstart, selected_terrain, map)==false)
|
||||
tstart += 2;
|
||||
|
||||
if(tup.process(mousex,mousey,new_left_button)) {
|
||||
tstart -= 2;
|
||||
if(tstart<0)
|
||||
tstart=0;
|
||||
}
|
||||
|
||||
if(tdown.process(mousex,mousey,new_left_button)) {
|
||||
tstart += 2;
|
||||
if(tstart+nterrains>terrains.size())
|
||||
tstart-=2;
|
||||
}
|
||||
|
||||
gui.update_display();
|
||||
SDL_Delay(20);
|
||||
events::pump();
|
||||
}
|
||||
|
||||
int res = gui::show_dialog(gui,NULL,"Save?","Do you want to save changes?",
|
||||
gui::YES_NO);
|
||||
if(res == 0) {
|
||||
write_file(filename,map.write());
|
||||
}
|
||||
|
||||
return 0;
|
||||
std::string get_filename_from_dialog(CVideo &video, config &cfg) {
|
||||
const std::string path = "data/maps/";
|
||||
|
||||
display::unit_map u_map;
|
||||
config dummy_cfg("");
|
||||
|
||||
config dummy_theme;
|
||||
display disp(u_map, video, gamemap(dummy_cfg,"1"), gamestatus(dummy_cfg,0),
|
||||
std::vector<team>(), dummy_theme, cfg);
|
||||
|
||||
std::vector<std::string> files;
|
||||
get_files_in_dir(path,&files);
|
||||
|
||||
files.push_back("New Map...");
|
||||
|
||||
const int res = gui::show_dialog(disp, NULL, "",
|
||||
"Choose map to edit:", gui::OK_CANCEL, &files);
|
||||
if(res < 0) {
|
||||
return "";
|
||||
}
|
||||
std::string filename;
|
||||
if(res == int(files.size()-1)) {
|
||||
filename = "new-map";
|
||||
gui::show_dialog(disp, NULL, "", "Create new map",
|
||||
gui::OK_ONLY, NULL, NULL, "", &filename);
|
||||
if (filename == "") {
|
||||
// If no name was given, return the empty filename; don't add
|
||||
// the path.
|
||||
return filename;
|
||||
}
|
||||
} else {
|
||||
filename = files[res];
|
||||
}
|
||||
filename = path + filename;
|
||||
return filename;
|
||||
}
|
||||
|
||||
void drawbar(display& disp)
|
||||
{
|
||||
SDL_Surface* const screen = disp.video().getSurface();
|
||||
SDL_Rect dst = {disp.mapx(),0,disp.x()-disp.mapx(),disp.y()};
|
||||
SDL_FillRect(screen,&dst,0);
|
||||
update_rect(dst);
|
||||
SDL_Surface* const screen = disp.video().getSurface();
|
||||
SDL_Rect dst = {disp.mapx(), 0, disp.x() - disp.mapx(), disp.y()};
|
||||
SDL_FillRect(screen,&dst,0);
|
||||
update_rect(dst);
|
||||
}
|
||||
|
||||
bool drawterrainpalette(display& disp, int start, gamemap::TERRAIN selected, gamemap map)
|
||||
bool drawterrainpalette(display& disp, int start, gamemap::TERRAIN selected, gamemap map,
|
||||
size_specs specs)
|
||||
{
|
||||
int x = disp.mapx() + palette_x;
|
||||
int y = palette_y;
|
||||
size_t x = disp.mapx() + specs.palette_x;
|
||||
size_t y = specs.palette_y;
|
||||
|
||||
int starting = start;
|
||||
int ending = starting+nterrains;
|
||||
unsigned int starting = start;
|
||||
unsigned int ending = starting+specs.nterrains;
|
||||
|
||||
bool status = true;
|
||||
bool status = true;
|
||||
|
||||
SDL_Rect invalid_rect;
|
||||
invalid_rect.x = x;
|
||||
invalid_rect.y = y;
|
||||
invalid_rect.w = 0;
|
||||
SDL_Rect invalid_rect;
|
||||
invalid_rect.x = x;
|
||||
invalid_rect.y = y;
|
||||
invalid_rect.w = 0;
|
||||
|
||||
SDL_Surface* const screen = disp.video().getSurface();
|
||||
SDL_Surface* const screen = disp.video().getSurface();
|
||||
|
||||
std::vector<gamemap::TERRAIN> terrains = map.get_terrain_precedence();
|
||||
terrains.erase(std::remove_if(terrains.begin(),terrains.end(),is_invalid_terrain),terrains.end());
|
||||
if(ending > terrains.size()){
|
||||
ending = terrains.size();
|
||||
starting = ending - nterrains;
|
||||
status = false;
|
||||
}
|
||||
std::vector<gamemap::TERRAIN> terrains = map.get_terrain_precedence();
|
||||
terrains.erase(std::remove_if(terrains.begin(), terrains.end(), is_invalid_terrain),
|
||||
terrains.end());
|
||||
if(ending > terrains.size()){
|
||||
ending = terrains.size();
|
||||
starting = ending - specs.nterrains;
|
||||
status = false;
|
||||
}
|
||||
|
||||
for(int counter = starting; counter < ending; counter++){
|
||||
const gamemap::TERRAIN terrain = terrains[counter];
|
||||
scoped_sdl_surface image(image::get_image("terrain/" + map.get_terrain_info(terrain).default_image() + ".png",image::UNSCALED));
|
||||
if(image->w != terrain_size || image->h != terrain_size) {
|
||||
image.assign(scale_surface(image,terrain_size,terrain_size));
|
||||
}
|
||||
for(unsigned int counter = starting; counter < ending; counter++){
|
||||
const gamemap::TERRAIN terrain = terrains[counter];
|
||||
const std::string filename = "terrain/" +
|
||||
map.get_terrain_info(terrain).default_image() + ".png";
|
||||
scoped_sdl_surface image(image::get_image(filename, image::UNSCALED));
|
||||
|
||||
if(image->w != specs.terrain_size || image->h != specs.terrain_size) {
|
||||
image.assign(scale_surface(image, specs.terrain_size, specs.terrain_size));
|
||||
}
|
||||
|
||||
if(image == NULL) {
|
||||
std::cerr << "image for terrain '" << counter << "' not found\n";
|
||||
return status;
|
||||
}
|
||||
if(image == NULL) {
|
||||
std::cerr << "image for terrain '" << counter << "' not found\n";
|
||||
return status;
|
||||
}
|
||||
|
||||
SDL_Rect dstrect;
|
||||
dstrect.x = x + (counter % 2 != 0 ? 0 : terrain_space);
|
||||
dstrect.y = y;
|
||||
dstrect.w = image->w;
|
||||
dstrect.h = image->h;
|
||||
SDL_Rect dstrect;
|
||||
dstrect.x = x + (counter % 2 != 0 ? 0 : specs.terrain_space);
|
||||
dstrect.y = y;
|
||||
dstrect.w = image->w;
|
||||
dstrect.h = image->h;
|
||||
|
||||
SDL_BlitSurface(image,NULL,screen,&dstrect);
|
||||
gui::draw_rectangle(dstrect.x,dstrect.y,image->w,image->h,
|
||||
terrain == selected?0xF000:0,screen);
|
||||
|
||||
if (counter % 2 != 0)
|
||||
y += terrain_space;
|
||||
}
|
||||
invalid_rect.w = terrain_space * 2;
|
||||
|
||||
invalid_rect.h = y - invalid_rect.y;
|
||||
update_rect(invalid_rect);
|
||||
return status;
|
||||
SDL_BlitSurface(image, NULL, screen, &dstrect);
|
||||
gui::draw_rectangle(dstrect.x, dstrect.y, image->w, image->h,
|
||||
terrain == selected ? 0xF000:0 , screen);
|
||||
|
||||
if (counter % 2 != 0)
|
||||
y += specs.terrain_space;
|
||||
}
|
||||
invalid_rect.w = specs.terrain_space * 2;
|
||||
|
||||
invalid_rect.h = y - invalid_rect.y;
|
||||
update_rect(invalid_rect);
|
||||
return status;
|
||||
}
|
||||
|
||||
int tileselected(int x, int y, display& disp)
|
||||
int tile_selected(const unsigned int x, const unsigned int y,
|
||||
const display& disp, const size_specs specs)
|
||||
{
|
||||
for(int i = 0; i != nterrains; i++) {
|
||||
const int px = disp.mapx() + palette_x + (i % 2 != 0 ? 0 : terrain_space);
|
||||
const int py = palette_y + (i/2)*terrain_space;
|
||||
const int pw = terrain_space;
|
||||
const int ph = terrain_space;
|
||||
for(unsigned int i = 0; i != specs.nterrains; i++) {
|
||||
const unsigned int px = disp.mapx() + specs.palette_x + (i % 2 != 0 ? 0 : specs.terrain_space);
|
||||
const unsigned int py = specs.palette_y + (i / 2) * specs.terrain_space;
|
||||
const unsigned int pw = specs.terrain_space;
|
||||
const unsigned int ph = specs.terrain_space;
|
||||
|
||||
if(x>px && x<px+pw && y>py && y<py+ph) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
if(x > px && x < px + pw && y > py && y < py + ph) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool is_invalid_terrain(char c) {
|
||||
return c == ' ' || c == '~';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
game_config::editor = true;
|
||||
|
||||
if(argc > 2) {
|
||||
std::cout << "usage: " << argv[0] << " [map-name]" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
CVideo video;
|
||||
|
||||
const font::manager font_manager;
|
||||
const preferences::manager prefs_manager;
|
||||
const image::manager image_manager;
|
||||
std::pair<int, int> desiredResolution = preferences::resolution();
|
||||
video.setMode(desiredResolution.first,desiredResolution.second,16,0);
|
||||
|
||||
preproc_map defines_map;
|
||||
defines_map["MEDIUM"] = preproc_define();
|
||||
defines_map["NORMAL"] = preproc_define();
|
||||
config cfg(preprocess_file("data/game.cfg",&defines_map));
|
||||
|
||||
set_language("English");
|
||||
|
||||
std::string filename;
|
||||
|
||||
if(argc == 1) {
|
||||
filename = map_editor::get_filename_from_dialog(video, cfg);
|
||||
if (filename == "") {
|
||||
return 0;
|
||||
}
|
||||
} else if(argc == 2) {
|
||||
filename = argv[1];
|
||||
}
|
||||
|
||||
std::string mapdata;
|
||||
try {
|
||||
mapdata = read_file(filename);
|
||||
}
|
||||
catch (io_exception) {
|
||||
std::cerr << "Could not read the map file, sorry." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if(mapdata.empty()) {
|
||||
for(int i = 0; i != 30; ++i) {
|
||||
mapdata = mapdata + "gggggggggggggggggggggggggggggggggggggg\n";
|
||||
}
|
||||
}
|
||||
try {
|
||||
gamemap map(cfg, mapdata);
|
||||
gamestatus status(cfg, 0);
|
||||
std::vector<team> teams;
|
||||
|
||||
const config* const theme_cfg = cfg.find_child("theme", "name", "editor");
|
||||
config dummy_theme;
|
||||
std::map<gamemap::location,unit> units;
|
||||
display gui(units, video, map, status, teams,
|
||||
theme_cfg ? *theme_cfg : dummy_theme, cfg);
|
||||
|
||||
//Draw the nice background bar
|
||||
map_editor::drawbar(gui);
|
||||
map_editor::map_editor editor(gui, map);
|
||||
editor.set_file_to_save_as(filename);
|
||||
editor.main_loop();
|
||||
}
|
||||
catch (gamemap::incorrect_format_exception) {
|
||||
std::cerr << "The map is not in a correct format, sorry." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue