Old source files were removed.

This commit is contained in:
zas 2003-09-19 10:24:20 +00:00
parent ffa8b0bf4e
commit 0ca41693ba
69 changed files with 0 additions and 15640 deletions

View file

@ -1,775 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "actions.hpp"
#include "display.hpp"
#include "game_config.hpp"
#include "game_events.hpp"
#include "key.hpp"
#include "language.hpp"
#include "map.hpp"
#include "pathfind.hpp"
#include "replay.hpp"
#include "sound.hpp"
#include "util.hpp"
#include <cmath>
#include <set>
#include <string>
#include <sstream>
struct castle_cost_calculator
{
castle_cost_calculator(const gamemap& map) : map_(map)
{}
double cost(const gamemap::location& loc) const
{
if(!map_.on_board(loc) || map_[loc.x][loc.y] != gamemap::CASTLE)
return 10000;
return 1;
}
private:
const gamemap& map_;
};
std::string recruit_unit(const gamemap& map, int side,
std::map<gamemap::location,unit>& units, unit& new_unit,
gamemap::location recruit_location, display* disp)
{
typedef std::map<gamemap::location,unit> units_map;
//find the unit that can recruit
units_map::const_iterator u;
for(u = units.begin(); u != units.end(); ++u) {
if(u->second.can_recruit() && u->second.side() == side) {
break;
}
}
if(u == units.end())
return string_table["no_leader_to_recruit"];
if(!map.is_starting_position(u->first))
return string_table["leader_not_on_start"];
if(map.on_board(recruit_location)) {
const paths::route& rt = a_star_search(u->first,recruit_location,
100.0,castle_cost_calculator(map));
if(rt.steps.empty() || units.find(recruit_location) != units.end() ||
map[recruit_location.x][recruit_location.y] != gamemap::CASTLE)
recruit_location = gamemap::location();
}
if(!map.on_board(recruit_location)) {
recruit_location = find_vacant_tile(map,units,u->first,gamemap::CASTLE);
}
if(!map.on_board(recruit_location)) {
return string_table["no_recruit_location"];
}
new_unit.set_movement(0);
new_unit.set_attacked();
units.insert(std::pair<gamemap::location,unit>(
recruit_location,new_unit));
if(disp != NULL && !disp->turbo()) {
disp->draw(true,true);
for(double alpha = 0.0; alpha <= 1.0; alpha += 0.1) {
disp->draw_tile(recruit_location.x,recruit_location.y,NULL,alpha);
disp->update_display();
SDL_Delay(20);
}
}
return std::string();
}
bool under_leadership(const std::map<gamemap::location,unit>& units,
const gamemap::location& loc)
{
gamemap::location adjacent[6];
get_adjacent_tiles(loc,adjacent);
const std::map<gamemap::location,unit>::const_iterator un =
units.find(loc);
if(un == units.end())
return false;
const int side = un->second.side();
const int level = un->second.type().level();
for(int i = 0; i != 6; ++i) {
const std::map<gamemap::location,unit>::const_iterator it =
units.find(adjacent[i]);
if(it != units.end() && it->second.side() == side &&
it->second.type().is_leader() && it->second.type().level() > level)
return true;
}
return false;
}
battle_stats evaluate_battle_stats(
const gamemap& map,
const gamemap::location& attacker,
const gamemap::location& defender,
int attack_with,
std::map<gamemap::location,unit>& units,
const gamestatus& state,
const game_data& info,
gamemap::TERRAIN attacker_terrain_override,
bool include_strings)
{
battle_stats res;
res.attack_with = attack_with;
if(include_strings)
res.defend_name = "none";
const std::map<gamemap::location,unit>::iterator a = units.find(attacker);
const std::map<gamemap::location,unit>::iterator d = units.find(defender);
assert(a != units.end());
assert(d != units.end());
const gamemap::TERRAIN attacker_terrain = attacker_terrain_override ?
attacker_terrain_override : map[attacker.x][attacker.y];
const gamemap::TERRAIN defender_terrain = map[defender.x][defender.y];
res.chance_to_hit_attacker =
a->second.defense_modifier(map,attacker_terrain);
res.chance_to_hit_defender =
d->second.defense_modifier(map,defender_terrain);
const std::vector<attack_type>& attacker_attacks =
a->second.attacks();
const std::vector<attack_type>& defender_attacks =
d->second.attacks();
assert(attack_with >= 0 && attack_with < int(attacker_attacks.size()));
const attack_type& attack = attacker_attacks[attack_with];
static const std::string charge_string("charge");
const bool charge = (attack.special() == charge_string);
bool backstab = false;
static const std::string backstab_string("backstab");
if(attack.special() == backstab_string) {
gamemap::location adj[6];
get_adjacent_tiles(defender,adj);
int i;
for(i = 0; i != 6; ++i) {
if(adj[i] == attacker)
break;
}
if(i != 6) {
const std::map<gamemap::location,unit>::const_iterator u =
units.find(adj[(i+3)%6]);
if(u != units.end() && u->second.side() == a->second.side()) {
backstab = true;
}
}
}
static const std::string plague_string("plague");
res.attacker_plague = (attack.special() == plague_string);
res.defender_plague = false;
res.attack_name = attack.name();
res.attack_type = attack.type();
if(include_strings) {
res.attack_special = attack.special();
//don't show backstabbing unless it's actually happening
if(res.attack_special == "backstab" && !backstab)
res.attack_special = "";
res.range = (attack.range() == attack_type::SHORT_RANGE ?
"Melee" : "Ranged");
}
res.nattacks = attack.num_attacks();
int defend;
res.ndefends = 0;
for(defend = 0; defend != int(defender_attacks.size()); ++defend) {
if(defender_attacks[defend].range() == attack.range())
break;
}
res.defend_with = defend != int(defender_attacks.size()) ? defend : -1;
const bool counterattack = defend != int(defender_attacks.size());
static const std::string drain_string("drain");
static const std::string magical_string("magical");
res.damage_attacker_takes = 0;
if(counterattack) {
//magical attacks always have a 70% chance to hit
if(defender_attacks[defend].special() == magical_string)
res.chance_to_hit_attacker = 0.7;
res.damage_attacker_takes = int(double(
a->second.damage_against(defender_attacks[defend]))
* combat_modifier(state,units,d->first,d->second.type().alignment()));
if(charge)
res.damage_attacker_takes *= 2;
if(under_leadership(units,defender))
res.damage_attacker_takes += res.damage_attacker_takes/8 + 1;
if(res.damage_attacker_takes < 1)
res.damage_attacker_takes = 1;
res.ndefends = defender_attacks[defend].num_attacks();
res.defend_name = defender_attacks[defend].name();
res.defend_type = defender_attacks[defend].type();
if(include_strings) {
res.defend_special = defender_attacks[defend].special();
}
if(defender_attacks[defend].special() == drain_string) {
res.amount_defender_drains = res.damage_attacker_takes/2;
} else {
res.amount_defender_drains = 0;
}
res.defender_plague =
(defender_attacks[defend].special() == plague_string);
}
if(attack.special() == magical_string)
res.chance_to_hit_defender = 0.7;
static const std::string marksman_string("marksman");
//offensive marksman attacks always have at least 60% chance to hit
if(res.chance_to_hit_defender < 0.6 && attack.special() == marksman_string)
res.chance_to_hit_defender = 0.6;
res.damage_defender_takes = int(
double(d->second.damage_against(attack))
* combat_modifier(state,units,a->first,a->second.type().alignment()))
* (charge ? 2 : 1) * (backstab ? 2 : 1);
if(under_leadership(units,attacker))
res.damage_defender_takes += res.damage_defender_takes/8 + 1;
if(attack.special() == drain_string) {
res.amount_attacker_drains = res.damage_defender_takes/2;
} else {
res.amount_attacker_drains = 0;
}
static const std::string slowed_string("slowed");
if(a->second.has_flag(slowed_string) && res.nattacks > 1)
--res.nattacks;
if(d->second.has_flag(slowed_string) && res.ndefends > 1)
--res.ndefends;
return res;
}
void attack(display& gui, const gamemap& map,
const gamemap::location& attacker,
const gamemap::location& defender,
int attack_with,
std::map<gamemap::location,unit>& units,
const gamestatus& state,
const game_data& info, bool player_is_attacker)
{
std::map<gamemap::location,unit>::iterator a = units.find(attacker);
std::map<gamemap::location,unit>::iterator d = units.find(defender);
assert(a != units.end());
assert(d != units.end());
int attackerxp = d->second.type().level();
int defenderxp = a->second.type().level();
a->second.set_attacked();
//if the attacker was invisible, she isn't anymore!
static const std::string forest_invisible("ambush");
a->second.remove_flag(forest_invisible);
battle_stats stats = evaluate_battle_stats(map,attacker,defender,
attack_with,units,state,info);
while(stats.nattacks > 0 || stats.ndefends > 0) {
if(stats.nattacks > 0) {
const double roll = double(recorder.get_random()%1000)/1000.0;
const bool hits = roll < stats.chance_to_hit_defender;
const bool dies = gui.unit_attack(attacker,defender,
hits ? stats.damage_defender_takes : 0,
a->second.attacks()[attack_with]);
if(dies) {
attackerxp = 10*d->second.type().level();
if(d->second.type().level() == 0)
attackerxp = 5;
defenderxp = 0;
gamemap::location loc = d->first;
gamemap::location attacker_loc = a->first;
game_events::fire("die",loc,a->first);
//the handling of the event may have removed the object
//so we have to find it again
units.erase(loc);
//plague units make clones of themselves on the target hex
//units on villages that die cannot be plagued
if(stats.attacker_plague && map[loc.x][loc.y]!=gamemap::TOWER) {
a = units.find(attacker_loc);
if(a != units.end()) {
units.insert(std::pair<gamemap::location,unit>(
loc,a->second));
gui.draw_tile(loc.x,loc.y);
gui.update_display();
}
}
break;
} else if(hits) {
static const std::string poison_string("poison");
if(stats.attack_special == poison_string &&
d->second.has_flag("poisoned") == false) {
d->second.set_flag("poisoned");
}
static const std::string slow_string("slow");
if(stats.attack_special == slow_string &&
d->second.has_flag("slowed") == false) {
d->second.set_flag("slowed");
if(stats.ndefends > 1)
--stats.ndefends;
}
if(stats.amount_attacker_drains > 0) {
a->second.gets_hit(-stats.amount_attacker_drains);
}
}
--stats.nattacks;
}
if(stats.ndefends > 0) {
const double roll = double(recorder.get_random()%1000)/1000.0;
const bool hits = roll < stats.chance_to_hit_attacker;
const bool dies = gui.unit_attack(defender,attacker,
hits ? stats.damage_attacker_takes : 0,
d->second.attacks()[stats.defend_with]);
if(dies) {
defenderxp = 10*a->second.type().level();
if(a->second.type().level() == 0)
defenderxp = 5;
attackerxp = 0;
gamemap::location loc = a->first;
gamemap::location defender_loc = d->first;
game_events::fire("die",loc,d->first);
//the handling of the event may have removed the object
//so we have to find it again
units.erase(loc);
//plague units make clones of themselves on the target hex.
//units on villages that die cannot be plagued
if(stats.defender_plague && map[loc.x][loc.y]!=gamemap::TOWER) {
d = units.find(defender_loc);
if(d != units.end()) {
units.insert(std::pair<gamemap::location,unit>(
loc,d->second));
gui.draw_tile(loc.x,loc.y);
gui.update_display();
}
}
break;
} else if(hits) {
if(stats.defend_special == "poison" &&
a->second.has_flag("poisoned") == false) {
a->second.set_flag("poisoned");
}
static const std::string slow_string("slow");
if(stats.defend_special == slow_string &&
a->second.has_flag("slowed") == false) {
a->second.set_flag("slowed");
if(stats.nattacks > 1)
--stats.nattacks;
}
if(stats.amount_defender_drains > 0) {
d->second.gets_hit(-stats.amount_defender_drains);
}
}
--stats.ndefends;
}
}
if(attackerxp) {
a->second.get_experience(attackerxp);
}
if(defenderxp) {
d->second.get_experience(defenderxp);
}
}
int tower_owner(const gamemap::location& loc, std::vector<team>& teams)
{
for(int i = 0; i != teams.size(); ++i) {
if(teams[i].owns_tower(loc))
return i;
}
return -1;
}
void get_tower(const gamemap::location& loc, std::vector<team>& teams,
int team_num)
{
for(int i = 0; i != teams.size(); ++i) {
if(i != team_num && teams[i].owns_tower(loc)) {
teams[i].lose_tower(loc);
}
}
if(team_num >= 0 && team_num < teams.size())
teams[team_num].get_tower(loc);
}
std::map<gamemap::location,unit>::iterator
find_leader(std::map<gamemap::location,unit>& units, int side)
{
for(std::map<gamemap::location,unit>::iterator i = units.begin();
i != units.end(); ++i) {
if(i->second.side() == side && i->second.can_recruit())
return i;
}
return units.end();
}
void calculate_healing(display& disp, const gamemap& map,
std::map<gamemap::location,unit>& units, int side)
{
std::map<gamemap::location,int> healed_units, max_healing;
std::map<gamemap::location,unit>::iterator i;
for(i = units.begin(); i != units.end(); ++i) {
//the unit heals if it's on this side, and if it's on a tower or
//it has regeneration
if(i->second.side() == side &&
(map[i->first.x][i->first.y] == gamemap::TOWER ||
i->second.type().regenerates())) {
healed_units.insert(std::pair<gamemap::location,int>(
i->first, game_config::cure_amount));
}
//otherwise find the maximum healing for the unit
else {
int max_heal = 0;
gamemap::location adjacent[6];
get_adjacent_tiles(i->first,adjacent);
for(int j = 0; j != 6; ++j) {
std::map<gamemap::location,unit>::const_iterator healer =
units.find(adjacent[j]);
if(healer != units.end() && healer->second.side() == side) {
max_heal = maximum(max_heal,
healer->second.type().max_unit_healing());
}
}
if(max_heal > 0) {
max_healing.insert(std::pair<gamemap::location,int>(i->first,
max_heal));
}
}
}
//now see about units that can heal other units
for(i = units.begin(); i != units.end(); ++i) {
if(i->second.side() == side && i->second.type().heals()) {
gamemap::location adjacent[6];
bool gets_healed[6];
get_adjacent_tiles(i->first,adjacent);
int nhealed = 0;
int j;
for(j = 0; j != 6; ++j) {
const std::map<gamemap::location,unit>::const_iterator adj =
units.find(adjacent[j]);
if(adj != units.end() &&
adj->second.hitpoints() < adj->second.max_hitpoints() &&
adj->second.side() == i->second.side() &&
healed_units[adj->first] < max_healing[adj->first]) {
++nhealed;
gets_healed[j] = true;
} else {
gets_healed[j] = false;
}
}
if(nhealed == 0)
continue;
const int healing_per_unit = i->second.type().heals()/nhealed;
for(j = 0; j != 6; ++j) {
if(!gets_healed[j])
continue;
assert(units.find(adjacent[j]) != units.end());
healed_units[adjacent[j]]
= minimum(max_healing[adjacent[j]],
healed_units[adjacent[j]]+healing_per_unit);
}
}
}
//poisoned units will take the same amount of damage per turn, as
//curing heals until they are reduced to 1 hitpoint. If they are
//cured on a turn, they recover 0 hitpoints that turn, but they
//are no longer poisoned
for(i = units.begin(); i != units.end(); ++i) {
if(i->second.side() == side && i->second.has_flag("poisoned")) {
const int damage = minimum<int>(game_config::cure_amount,
i->second.hitpoints()-1);
if(damage > 0) {
healed_units.insert(std::pair<gamemap::location,int>(
i->first,-damage));
}
}
}
for(std::map<gamemap::location,int>::iterator h = healed_units.begin();
h != healed_units.end(); ++h) {
const gamemap::location& loc = h->first;
const bool show_healing = !disp.turbo() && !recorder.skipping();
assert(units.count(loc) == 1);
unit& u = units.find(loc)->second;
if(h->second > 0 && h->second > u.max_hitpoints()-u.hitpoints()) {
h->second = u.max_hitpoints()-u.hitpoints();
if(h->second <= 0)
continue;
}
if(show_healing) {
disp.scroll_to_tile(loc.x,loc.y,display::WARP);
disp.select_hex(loc);
disp.update_display();
}
const int DelayAmount = 50;
if(u.has_flag("poisoned") && h->second > 0) {
//poison is purged only if we are on a village or next to a curer
if(h->second >= game_config::cure_amount ||
max_healing[h->first] >= game_config::cure_amount) {
u.remove_flag("poisoned");
if(show_healing) {
sound::play_sound("heal.wav");
SDL_Delay(DelayAmount);
disp.invalidate_unit();
disp.update_display();
}
}
h->second = 0;
} else if(h->second < 0) {
if(show_healing)
sound::play_sound("groan.wav");
} else if(h->second > 0) {
if(show_healing)
sound::play_sound("heal.wav");
}
while(h->second > 0) {
const display::Pixel heal_colour = disp.rgb(0,0,200);
u.heal(1);
if(show_healing) {
if((h->second%2) == 1)
disp.draw_tile(loc.x,loc.y,NULL,0.5,heal_colour);
else
disp.draw_tile(loc.x,loc.y);
SDL_Delay(DelayAmount);
disp.update_display();
}
--h->second;
}
while(h->second < 0) {
const display::Pixel damage_colour = disp.rgb(200,0,0);
u.gets_hit(1);
if(show_healing) {
if((h->second%2) == 1)
disp.draw_tile(loc.x,loc.y,NULL,0.5,damage_colour);
else
disp.draw_tile(loc.x,loc.y);
SDL_Delay(DelayAmount);
disp.update_display();
}
++h->second;
}
if(show_healing) {
disp.draw_tile(loc.x,loc.y);
disp.update_display();
}
}
}
unit get_advanced_unit(const game_data& info,
std::map<gamemap::location,unit>& units,
const gamemap::location& loc, const std::string& advance_to)
{
const std::map<std::string,unit_type>::const_iterator new_type =
info.unit_types.find(advance_to);
std::map<gamemap::location,unit>::iterator un = units.find(loc);
if(new_type != info.unit_types.end() && un != units.end()) {
const int side = un->second.side();
unit new_unit(&(new_type->second),un->second);
return new_unit;
} else {
throw gamestatus::game_error("Could not find the unit being advanced"
" to: " + advance_to);
}
}
void advance_unit(const game_data& info,
std::map<gamemap::location,unit>& units,
const gamemap::location& loc, const std::string& advance_to)
{
const unit& new_unit = get_advanced_unit(info,units,loc,advance_to);
units.erase(loc);
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
}
int check_victory(std::map<gamemap::location,unit>& units)
{
std::set<int> seen_leaders;
for(std::map<gamemap::location,unit>::const_iterator i = units.begin();
i != units.end(); ++i) {
if(i->second.can_recruit())
seen_leaders.insert(i->second.side());
}
//if only one leader remains standing, his team has won
if(seen_leaders.size() == 1)
return *seen_leaders.begin();
//if the player (team 1) isn't here, the player has lost
if(seen_leaders.count(1) == 0)
return 2;
//remove any units which are leaderless
for(std::map<gamemap::location,unit>::iterator j = units.begin();
j != units.end(); ++j) {
if(seen_leaders.count(j->second.side()) == 0) {
units.erase(j);
j = units.begin();
}
}
return -1;
}
gamestatus::TIME timeofday_at(const gamestatus& status,
const std::map<gamemap::location,unit>& units,
const gamemap::location& loc)
{
gamemap::location locs[7];
locs[0] = loc;
get_adjacent_tiles(loc,locs+1);
bool lighten = false;
for(int i = 0; i != 7; ++i) {
const std::map<gamemap::location,unit>::const_iterator itor =
units.find(locs[i]);
if(itor != units.end() &&
itor->second.type().is_lightbringer()) {
lighten = true;
}
}
gamestatus::TIME res = status.timeofday();
if(lighten) {
if(res == gamestatus::DUSK)
res = gamestatus::DAY2;
else if(res > gamestatus::DUSK)
res = gamestatus::DUSK;
}
return res;
}
double combat_modifier(const gamestatus& status,
const std::map<gamemap::location,unit>& units,
const gamemap::location& loc,
unit_type::ALIGNMENT alignment)
{
const gamestatus::TIME timeofday = timeofday_at(status,units,loc);
if(alignment == unit_type::LAWFUL) {
if(timeofday > gamestatus::DAWN && timeofday < gamestatus::DUSK)
return 1.25;
else if(timeofday > gamestatus::DUSK &&
units.find(loc)->second.type().nightvision() == false)
return 0.75;
} else if(alignment == unit_type::CHAOTIC) {
if(timeofday > gamestatus::DAWN && timeofday < gamestatus::DUSK)
return 0.75;
else if(timeofday > gamestatus::DUSK)
return 1.25;
}
return 1.0;
}

View file

@ -1,98 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef ACTIONS_H_INCLUDED
#define ACTIONS_H_INCLUDED
#include "display.hpp"
#include "gamestatus.hpp"
#include "map.hpp"
#include "unit.hpp"
#include "unit_types.hpp"
#include <map>
#include <string>
std::string recruit_unit(const gamemap& map, int team,
std::map<gamemap::location,unit>& units,
unit& unit, gamemap::location preferred_location,
display *disp=NULL);
struct battle_stats
{
std::string attack_name, defend_name;
std::string attack_type, defend_type;
std::string attack_special, defend_special;
std::string range;
double chance_to_hit_attacker, chance_to_hit_defender;
int damage_attacker_takes, damage_defender_takes;
int amount_attacker_drains, amount_defender_drains;
int ndefends, nattacks;
int attack_with, defend_with;
bool attacker_plague, defender_plague;
};
battle_stats evaluate_battle_stats(
const gamemap& map,
const gamemap::location& attacker,
const gamemap::location& defender,
int attack_with,
std::map<gamemap::location,unit>& units,
const gamestatus& state,
const game_data& info,
gamemap::TERRAIN attacker_terrain_override=0,
bool include_strings=true);
void attack(display& gui, const gamemap& map,
const gamemap::location& attacker,
const gamemap::location& defender,
int attack_with,
std::map<gamemap::location,unit>& units,
const gamestatus& state,
const game_data& info, bool player_is_attacker);
int tower_owner(const gamemap::location& loc, std::vector<team>& teams);
void get_tower(const gamemap::location& loc, std::vector<team>& teams,
int team_num);
std::map<gamemap::location,unit>::iterator
find_leader(std::map<gamemap::location,unit>& units, int side);
void calculate_healing(display& disp, const gamemap& map,
std::map<gamemap::location,unit>& units, int side);
unit get_advanced_unit(const game_data& info,
std::map<gamemap::location,unit>& units,
const gamemap::location& loc, const std::string& advance_to);
void advance_unit(const game_data& info,
std::map<gamemap::location,unit>& units,
const gamemap::location& loc, const std::string& advance_to);
bool under_leadership(const std::map<gamemap::location,unit>& units,
const gamemap::location& loc);
int check_victory(std::map<gamemap::location,unit>& units);
//gets the time of day at a certain tile. Certain tiles may have a time of
//day that differs from 'the' time of day, if a unit that brings light is there
gamestatus::TIME timeofday_at(const gamestatus& status,
const std::map<gamemap::location,unit>& units,
const gamemap::location& loc);
double combat_modifier(const gamestatus& status,
const std::map<gamemap::location,unit>& units,
const gamemap::location& loc,
unit_type::ALIGNMENT alignment);
#endif

504
ai.cpp
View file

@ -1,504 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "actions.hpp"
#include "ai.hpp"
#include "ai_attack.hpp"
#include "ai_move.hpp"
#include "dialogs.hpp"
#include "game_config.hpp"
#include "log.hpp"
#include "menu.hpp"
#include "pathfind.hpp"
#include "playlevel.hpp"
#include "playturn.hpp"
#include "replay.hpp"
#include <iostream>
namespace {
bool recruit(const gamemap& map, const gamemap::location& leader,
const std::string& usage, const game_data& gameinfo,
int team_num, team& tm, int min_gold,
std::map<gamemap::location,unit>& units, display& disp)
{
log_scope("recruiting troops");
std::cerr << "recruiting " << usage << "\n";
std::vector<std::map<std::string,unit_type>::const_iterator> options;
//record the number of the recruit for replay recording
std::vector<int> option_numbers;
//find an available unit that can be recruited, matches the desired
//usage type, and comes in under budget
const std::set<std::string>& recruits = tm.recruits();
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())
&& tm.gold() - i->second.cost() > min_gold) {
options.push_back(i);
option_numbers.push_back(std::distance(recruits.begin(),
recruits.find(i->first)));
}
}
//from the available options, choose one at random
if(options.empty() == false) {
const int option = rand()%options.size();
recorder.add_recruit(option_numbers[option],gamemap::location());
const unit_type& u = options[option]->second;
tm.spend_gold(u.cost());
unit new_unit(&u,team_num,true);
std::cerr << "recruiting a " << u.name() << " for " << u.cost() << " have " << tm.gold() << " left\n";
const gamemap::location loc;
return recruit_unit(map,team_num,units,new_unit,loc,&disp).empty();
}
return false;
}
}
namespace ai {
void move_unit(const game_data& gameinfo, display& disp,
const gamemap& map,
std::map<gamemap::location,unit>& units,
const location& from, const location& to,
std::map<location,paths>& possible_moves,
std::vector<team>& teams, int team_num)
{
assert(units.find(to) == units.end() || from == to);
disp.select_hex(from);
disp.update_display();
log_scope("move_unit");
std::map<location,unit>::iterator u_it = units.find(from);
if(u_it == units.end()) {
std::cout << "Could not find unit at " << from.x << ", "
<< from.y << "\n";
assert(false);
return;
}
recorder.add_movement(from,to);
const bool ignore_zocs = u_it->second.type().is_skirmisher();
const bool teleport = u_it->second.type().teleports();
paths current_paths = paths(map,gameinfo,units,from,teams,
ignore_zocs,teleport);
paths_wiper wiper(disp);
disp.set_paths(&current_paths);
disp.scroll_to_tiles(from.x,from.y,to.x,to.y);
unit current_unit = u_it->second;
units.erase(u_it);
const std::map<location,paths>::iterator p_it = possible_moves.find(from);
if(p_it != possible_moves.end()) {
paths& p = p_it->second;
std::map<location,paths::route>::iterator rt = p.routes.begin();
for(; rt != p.routes.end(); ++rt) {
if(rt->first == to) {
break;
}
}
if(rt != p.routes.end()) {
std::vector<location> steps = rt->second.steps;
steps.push_back(to); //add the destination to the steps
disp.move_unit(steps,current_unit);
}
}
current_unit.set_movement(0);
units.insert(std::pair<location,unit>(to,current_unit));
if(map[to.x][to.y] == gamemap::TOWER)
get_tower(to,teams,team_num-1);
disp.draw_tile(to.x,to.y);
disp.draw();
game_events::fire("moveto",to);
}
void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
std::map<gamemap::location,unit>& units,
std::vector<team>& teams, int team_num, const gamestatus& state,
bool consider_combat, std::vector<target>* additional_targets)
{
std::vector<target> tgts;
if(additional_targets == NULL)
additional_targets = &tgts;
log_scope("doing ai move");
team& current_team = teams[team_num-1];
typedef paths::route route;
std::multimap<location,location> srcdst;
std::multimap<location,location> dstsrc;
std::vector<gamemap::location> leader_locations;
typedef std::map<location,paths> moves_map;
moves_map possible_moves;
for(std::map<gamemap::location,unit>::const_iterator un_it = units.begin();
un_it != units.end(); ++un_it) {
if(un_it->second.side() != team_num) {
continue;
}
//insert the trivial moves of staying on the same location
if(un_it->second.movement_left() == un_it->second.total_movement()) {
std::pair<location,location> trivial_mv(un_it->first,un_it->first);
srcdst.insert(trivial_mv);
dstsrc.insert(trivial_mv);
}
if(un_it->second.can_recruit()) {
//save so we can remove from possible moves later
leader_locations.push_back(un_it->first);
continue;
}
const bool ignore_zocs = un_it->second.type().is_skirmisher();
const bool teleports = un_it->second.type().teleports();
possible_moves.insert(std::pair<gamemap::location,paths>(
un_it->first,paths(map,gameinfo,units,
un_it->first,teams,ignore_zocs,teleports)));
}
for(moves_map::iterator m = possible_moves.begin();
m != possible_moves.end(); ++m) {
for(std::map<location,route>::iterator rtit =
m->second.routes.begin(); rtit != m->second.routes.end();
++rtit) {
const location& src = m->first;
const location& dst = rtit->first;
if(src != dst && units.find(dst) == units.end()) {
srcdst.insert(std::pair<location,location>(src,dst));
dstsrc.insert(std::pair<location,location>(dst,src));
}
}
}
//no moves left, recruitment phase
//take stock of our current set of units
if(srcdst.empty()) {
std::cout << "recruitment......\n";
location leader;
int num_units = 0;
std::map<std::string,int> unit_types;
for(std::map<location,unit>::const_iterator i = units.begin();
i != units.end(); ++i) {
if(i->second.side() != team_num)
continue;
if(i->second.can_recruit()) {
leader = i->first;
continue;
}
unit_types[i->second.type().usage()]++;
++num_units;
}
const int cash_flow = current_team.towers()*game_config::tower_income +
game_config::base_income - num_units;
const int min_gold = 10 + (cash_flow < 0 ? -cash_flow*10 : 0);
//count the number of scouts we have currently
const int towers = map.towers().size();
int taken_towers = 0;
for(int j = 0; j != teams.size(); ++j) {
taken_towers += teams[j].towers();
}
const int neutral_towers = towers - taken_towers;
//we want at least one scout for every eight neutral towers
int scouts_wanted = neutral_towers/8;
if(scouts_wanted < 1)
scouts_wanted = 1;
while(unit_types["scout"] < scouts_wanted) {
if(recruit(map,leader,"scout",gameinfo,team_num,current_team,
min_gold,units,disp) == false)
break;
++unit_types["scout"];
}
const std::vector<std::string>& options =
current_team.recruitment_pattern();
if(options.empty()) {
assert(false);
return;
}
//buy fighters as long as we have room and can afford it
while(recruit(map,leader,options[rand()%options.size()].c_str(),
gameinfo,team_num,current_team,min_gold,units,disp)) {
}
recorder.end_turn();
return;
}
int ticks = SDL_GetTicks();
//look for targets of opportunity that we are hoping to kill this turn
std::vector<attack_analysis> analysis;
if(consider_combat)
analysis = analyze_targets(map,srcdst,dstsrc,units,
current_team,team_num,state,gameinfo);
int time_taken = SDL_GetTicks() - ticks;
std::cout << "took " << time_taken << " ticks for " << analysis.size() << " positions. Analyzing...\n";
ticks = SDL_GetTicks();
const int max_sims = 30000;
int num_sims = analysis.empty() ? 0 : max_sims/analysis.size();
if(num_sims < 8)
num_sims = 8;
if(num_sims > 20)
num_sims = 20;
std::cout << "simulations: " << num_sims << "\n";
const int max_positions = 20000;
const int skip_num = analysis.size()/max_positions;
std::vector<attack_analysis>::iterator choice_it = analysis.end();
double choice_rating = -1000.0;
for(std::vector<attack_analysis>::iterator it = analysis.begin();
it != analysis.end(); ++it) {
if(skip_num > 0 && ((it - analysis.begin())%skip_num) &&
it->movements.size() > 1)
continue;
const double rating = it->rating(current_team.aggression());
std::cout << "attack option rated at " << rating << " (" << current_team.aggression() << ")\n";
if(rating > choice_rating) {
choice_it = it;
choice_rating = rating;
}
}
time_taken = SDL_GetTicks() - ticks;
std::cout << "analysis took " << time_taken << " ticks\n";
if(choice_rating > 0.0) {
const location& from = choice_it->movements[0].first;
const location& to = choice_it->movements[0].second;
const location& target_loc = choice_it->target;
const int weapon = choice_it->weapons[0];
const std::map<gamemap::location,unit>::const_iterator tgt =
units.find(target_loc);
const bool defender_human = (tgt != units.end()) ?
teams[tgt->second.side()-1].is_human() : false;
move_unit(gameinfo,disp,map,units,from,to,
possible_moves,teams,team_num);
std::cerr << "attacking...\n";
recorder.add_attack(to,target_loc,weapon);
game_events::fire("attack",to,target_loc);
if(units.count(to) && units.count(target_loc)) {
attack(disp,map,to,target_loc,weapon,units,state,gameinfo,false);
const int res = check_victory(units);
if(res == 1)
throw end_level_exception(VICTORY);
else if(res > 1)
throw end_level_exception(DEFEAT);
}
std::cerr << "done attacking...\n";
//if this is the only unit in the planned attack, and the target
//is still alive, then also summon reinforcements
if(choice_it->movements.size() == 1 && units.count(target_loc)) {
additional_targets->push_back(target(target_loc,3.0));
}
dialogs::advance_unit(gameinfo,units,to,disp,true);
dialogs::advance_unit(gameinfo,units,target_loc,disp,!defender_human);
do_move(disp,map,gameinfo,units,teams,team_num,state,consider_combat,
additional_targets);
return;
} else {
log_scope("summoning reinforcements...\n");
consider_combat = false;
std::set<gamemap::location> already_done;
for(std::vector<attack_analysis>::iterator it = analysis.begin();
it != analysis.end(); ++it) {
assert(it->movements.empty() == false);
const gamemap::location& loc = it->movements.front().first;
if(already_done.count(loc) > 0)
continue;
additional_targets->push_back(target(loc,3.0));
already_done.insert(loc);
}
}
//mark the leader as having moved and attacked by now
for(std::vector<location>::const_iterator lead = leader_locations.begin();
lead != leader_locations.end(); ++lead) {
const std::map<location,unit>::iterator leader = units.find(*lead);
if(leader != units.end()) {
leader->second.set_movement(0);
leader->second.set_attacked();
}
//remove leader from further consideration
srcdst.erase(*lead);
dstsrc.erase(*lead);
}
//try to acquire towers
for(std::multimap<location,location>::const_iterator i = dstsrc.begin();
i != dstsrc.end(); ++i) {
if(map[i->first.x][i->first.y] != gamemap::TOWER)
continue;
bool want_tower = true;
for(int j = 0; j != teams.size(); ++j) {
if(!current_team.is_enemy(j+1) && teams[j].owns_tower(i->first)) {
want_tower = false;
break;
}
}
if(want_tower) {
const std::map<location,unit>::iterator un = units.find(i->second);
if(un == units.end()) {
assert(false);
return;
}
if(un->second.is_guardian())
continue;
move_unit(gameinfo,disp,map,units,i->second,i->first,
possible_moves,teams,team_num);
do_move(disp,map,gameinfo,units,teams,team_num,
state,consider_combat,additional_targets);
return;
}
}
std::cout << "b\n";
//find units in need of healing
std::map<location,unit>::iterator u_it = units.begin();
for(; u_it != units.end(); ++u_it) {
unit& u = u_it->second;
//if the unit is on our side, has lost as many or more than 1/2 round
//worth of healing, and doesn't regenerate itself, then try to
//find a vacant village for it to rest in
if(u.side() == team_num &&
u.type().hitpoints() - u.hitpoints() >= game_config::heal_amount/2 &&
!u.type().regenerates()) {
typedef std::multimap<location,location>::iterator Itor;
std::pair<Itor,Itor> it = srcdst.equal_range(u_it->first);
while(it.first != it.second) {
const location& dst = it.first->second;
if(map[dst.x][dst.y] == gamemap::TOWER &&
units.find(dst) == units.end()) {
const location& src = it.first->first;
move_unit(gameinfo,disp,map,units,src,dst,
possible_moves,teams,team_num);
do_move(disp,map,gameinfo,units,teams,team_num,state,
consider_combat,additional_targets);
return;
}
++it.first;
}
}
}
if(dstsrc.empty()) {
do_move(disp,map,gameinfo,units,teams,team_num,state,
consider_combat,additional_targets);
return;
}
std::cout << "finding targets...\n";
std::vector<target> targets = find_targets(map,units,teams,team_num);
targets.insert(targets.end(),additional_targets->begin(),
additional_targets->end());
for(;;) {
if(targets.empty()) {
recorder.end_turn();
return;
}
std::cout << "choosing move...\n";
std::pair<location,location> move = choose_move(targets,dstsrc,units,
map,teams,team_num,
gameinfo);
for(std::vector<target>::const_iterator ittg = targets.begin();
ittg != targets.end(); ++ittg) {
assert(map.on_board(ittg->loc));
}
if(move.first.valid() == false)
break;
std::cout << "move: " << move.first.x << ", " << move.first.y << " - " << move.second.x << ", " << move.second.y << "\n";
std::cout << "calling move_unit\n";
move_unit(gameinfo,disp,map,units,move.first,move.second,
possible_moves,teams,team_num);
std::cout << "end move_unit\n";
//don't allow any other units to move onto the tile our unit
//just moved onto
typedef std::multimap<location,location>::iterator Itor;
std::pair<Itor,Itor> del = dstsrc.equal_range(move.second);
dstsrc.erase(del.first,del.second);
}
do_move(disp,map,gameinfo,units,teams,team_num,state,
consider_combat,additional_targets);
return;
}
}

34
ai.hpp
View file

@ -1,34 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef AI_HPP_INCLUDED
#define AI_HPP_INCLUDED
#include "ai_move.hpp"
#include "display.hpp"
#include "map.hpp"
#include "unit.hpp"
#include "unit_types.hpp"
#include <map>
namespace ai {
typedef gamemap::location location;
void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
std::map<gamemap::location,unit>& units,
std::vector<team>& teams, int team_num, const gamestatus& state,
bool consider_combat=true,
std::vector<target>* additional_targets=NULL);
}
#endif

View file

@ -1,441 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "actions.hpp"
#include "ai_attack.hpp"
#include "game_config.hpp"
#include "log.hpp"
#include "pathfind.hpp"
#include "util.hpp"
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <set>
namespace {
const int max_positions = 10000;
using namespace ai;
void do_analysis(
const gamemap& map,
const location& loc,
const std::multimap<location,location>& srcdst,
const std::multimap<location,location>& dstsrc,
const location* tiles, bool* used_locations,
std::vector<location>& units,
std::map<gamemap::location,unit>& units_map,
std::vector<attack_analysis>& result,
const game_data& data, const gamestatus& status,
attack_analysis& cur_analysis
)
{
if(cur_analysis.movements.size() >= 6)
return;
static double best_results[6];
if(result.empty()) {
for(int i = 0; i != 6; ++i) {
best_results[i] = 0.0;
}
}
// if(result.size() > max_positions && !cur_analysis.movements.empty())
// return;
const double cur_rating = cur_analysis.movements.empty() ? 0 :
cur_analysis.rating(0.0);
double rating_to_beat = cur_rating;
if(!cur_analysis.movements.empty()) {
assert(cur_analysis.movements.size() < 6);
double& best_res = best_results[cur_analysis.movements.size()-1];
rating_to_beat = best_res = maximum(best_res,cur_rating);
}
for(int i = 0; i != units.size(); ++i) {
const location current_unit = units[i];
units.erase(units.begin() + i);
for(int j = 0; j != 6; ++j) {
if(used_locations[j])
continue;
typedef std::multimap<location,location>::const_iterator Itor;
std::pair<Itor,Itor> its = dstsrc.equal_range(tiles[j]);
while(its.first != its.second) {
if(its.first->second == current_unit)
break;
++its.first;
}
if(its.first == its.second)
continue;
cur_analysis.movements.push_back(
std::pair<location,location>(current_unit,tiles[j]));
cur_analysis.analyze(map,units_map,status,data,50);
if(cur_analysis.rating(0.0) > rating_to_beat) {
result.push_back(cur_analysis);
used_locations[j] = true;
do_analysis(map,loc,srcdst,dstsrc,tiles,used_locations,
units,units_map,result,data,status,cur_analysis);
used_locations[j] = false;
}
cur_analysis.movements.pop_back();
}
units.insert(units.begin() + i, current_unit);
}
}
}
namespace ai {
struct battle_type {
battle_type(const gamemap::location& a, const gamemap::location& d,
gamemap::TERRAIN t)
: attacker(a),defender(d),terrain(t),weapon(-1)
{}
const gamemap::location attacker;
const gamemap::location defender;
const gamemap::TERRAIN terrain;
int weapon;
battle_stats stats;
};
bool operator<(const battle_type& a, const battle_type& b)
{
return a.attacker < b.attacker ||
a.attacker == b.attacker && a.defender < b.defender ||
a.attacker == b.attacker && a.defender == b.defender &&
a.terrain < b.terrain;
}
bool operator==(const battle_type& a, const battle_type& b)
{
return a.attacker == b.attacker && a.defender == b.defender &&
a.terrain == b.terrain;
}
std::set<battle_type> weapon_choice_cache;
int choose_weapon(const gamemap& map, std::map<location,unit>& units,
const gamestatus& status, const game_data& info,
const location& att, const location& def,
battle_stats& cur_stats, gamemap::TERRAIN terrain)
{
const std::map<location,unit>::const_iterator itor = units.find(att);
if(itor == units.end())
return -1;
static int cache_hits = 0;
static int cache_misses = 0;
battle_type battle(att,def,terrain);
const std::set<battle_type>::const_iterator cache_itor
= weapon_choice_cache.find(battle);
if(cache_itor != weapon_choice_cache.end()) {
assert(*cache_itor == battle);
++cache_hits;
cur_stats = cache_itor->stats;
if(!(cache_itor->weapon >= 0 &&
cache_itor->weapon < itor->second.attacks().size())) {
}
assert(cache_itor->weapon >= 0 &&
cache_itor->weapon < itor->second.attacks().size());
return cache_itor->weapon;
}
++cache_misses;
if((cache_misses%100) == 0) {
std::cerr << "cache_stats: " << cache_hits << ":" << cache_misses << " " << weapon_choice_cache.size() << "\n";
}
int current_choice = -1;
double current_rating = 0.0;
const std::vector<attack_type>& attacks = itor->second.attacks();
assert(!attacks.empty());
for(int a = 0; a != attacks.size(); ++a) {
const battle_stats stats = evaluate_battle_stats(map,att,def,a,units,
status,info,terrain,false);
//TODO: improve this rating formula!
const double rating =
stats.chance_to_hit_defender*stats.damage_defender_takes*
stats.nattacks -
stats.chance_to_hit_attacker*stats.damage_attacker_takes*
stats.ndefends;
if(rating > current_rating || current_choice == -1) {
current_choice = a;
current_rating = rating;
cur_stats = stats;
}
}
assert(current_choice >= 0 && current_choice < attacks.size());
battle.stats = cur_stats;
battle.weapon = current_choice;
weapon_choice_cache.insert(battle);
return current_choice;
}
void attack_analysis::analyze(const gamemap& map,
std::map<location,unit>& units,
const gamestatus& status,
const game_data& info, int num_sims)
{
const std::map<location,unit>::const_iterator defend_it =units.find(target);
assert(defend_it != units.end());
target_value = defend_it->second.type().cost();
target_value += (double(defend_it->second.experience())/
double(defend_it->second.max_experience()))*target_value;
target_starting_damage = defend_it->second.max_hitpoints() -
defend_it->second.hitpoints();
chance_to_kill = 0.0;
avg_damage_inflicted = 0.0;
avg_damage_taken = 0.0;
resources_used = 0.0;
terrain_quality = 0.0;
counter_strength_ratio = 0.0;
avg_losses = 0.0;
const int target_max_hp = defend_it->second.max_hitpoints();
const int target_hp = defend_it->second.hitpoints();
static std::vector<int> hitpoints;
static std::vector<battle_stats> stats;
hitpoints.clear();
stats.clear();
weapons.clear();
std::vector<std::pair<location,location> >::const_iterator m;
for(m = movements.begin(); m != movements.end(); ++m) {
battle_stats bat_stats;
const int weapon = choose_weapon(map,units,status,info,
m->first,target, bat_stats,
map[m->second.x][m->second.y]);
assert(weapon != -1);
weapons.push_back(weapon);
stats.push_back(bat_stats);
hitpoints.push_back(units.find(m->first)->second.hitpoints());
}
for(int j = 0; j != num_sims; ++j) {
int defenderxp = 0;
int defhp = target_hp;
for(int i = 0; i != movements.size() && defhp; ++i) {
const battle_stats& stat = stats[i];
int atthp = hitpoints[i];
int attacks = stat.nattacks;
int defends = stat.ndefends;
std::map<location,unit>::const_iterator att
= units.find(movements[i].first);
double cost = att->second.type().cost();
//up to double the value of a unit based on experience
cost += (double(att->second.experience())/
double(att->second.max_experience()))*cost;
terrain_quality += stat.chance_to_hit_attacker*cost;
resources_used += cost;
while(attacks || defends) {
if(attacks) {
const double roll = double(rand()%1000)/1000.0;
if(roll < stat.chance_to_hit_defender) {
defhp -= stat.damage_defender_takes;
if(defhp <= 0) {
//the reward for advancing a unit is to
//get a 'negative' loss of that unit
const int xp = defend_it->second.type().level()*10;
if(xp >= att->second.max_experience() -
att->second.experience()) {
avg_losses -= att->second.type().cost();
}
//the reward for killing with a unit that
//plagues is to get a 'negative' loss of that unit
if(stat.attacker_plague) {
avg_losses -= att->second.type().cost();
}
defhp = 0;
break;
}
atthp += stat.amount_attacker_drains;
if(atthp > hitpoints[i])
atthp = hitpoints[i];
}
--attacks;
}
if(defends) {
const double roll = double(rand()%1000)/1000.0;
if(roll < stat.chance_to_hit_attacker) {
atthp -= stat.damage_attacker_takes;
if(atthp <= 0) {
atthp = 0;
//penalty for allowing plague is a 'negative' kill
if(stat.defender_plague) {
chance_to_kill -= 1.0;
}
break;
}
defhp += stat.amount_defender_drains;
if(defhp > target_max_hp)
defhp = target_max_hp;
}
--defends;
}
}
if(defhp <= 0) {
break;
} else if(atthp == 0) {
avg_losses += cost;
} else if(map[movements[i].second.x][movements[i].second.y] ==
gamemap::TOWER) {
atthp += game_config::heal_amount;
if(atthp > hitpoints[i])
atthp = hitpoints[i];
}
defenderxp += (atthp == 0 ? 10:1)*att->second.type().level();
avg_damage_taken += hitpoints[i] - atthp;
}
//penalty for allowing advancement is a 'negative' kill
if(defend_it->second.experience() < defend_it->second.max_experience()&&
defend_it->second.experience() + defenderxp >=
defend_it->second.max_experience()) {
chance_to_kill -= 1.0;
} else if(defhp == 0) {
chance_to_kill += 1.0;
} else if(map[defend_it->first.x][defend_it->first.y]==gamemap::TOWER) {
defhp += game_config::heal_amount;
if(defhp > target_hp)
defhp = target_hp;
}
avg_damage_inflicted += target_hp - defhp;
}
chance_to_kill /= num_sims;
avg_damage_inflicted /= num_sims;
avg_damage_taken /= num_sims;
terrain_quality /= resources_used;
resources_used /= num_sims;
avg_losses /= num_sims;
}
double attack_analysis::rating(double aggression) const
{
double value = chance_to_kill*target_value - avg_losses;
//prefer to attack already damaged targets
value += ((target_starting_damage/3 + avg_damage_inflicted)*
(target_value/resources_used) -
(1.0-aggression)*avg_damage_taken*(resources_used/target_value))/10.0;
value /= ((resources_used/2) + (resources_used/2)*terrain_quality);
return value;
}
std::vector<attack_analysis> analyze_targets(
const gamemap& map,
const std::multimap<location,location>& srcdst,
const std::multimap<location,location>& dstsrc,
std::map<location,unit>& units,
const team& current_team, int team_num,
const gamestatus& status, const game_data& data
)
{
log_scope("analyzing targets...");
weapon_choice_cache.clear();
std::vector<attack_analysis> res;
std::vector<location> unit_locs;
for(std::map<location,unit>::const_iterator i = units.begin();
i != units.end(); ++i) {
if(i->second.side() == team_num) {
unit_locs.push_back(i->first);
}
}
bool used_locations[6];
std::fill(used_locations,used_locations+6,false);
for(std::map<location,unit>::const_iterator j = units.begin();
j != units.end(); ++j) {
//attack anyone who is on the enemy side, and who is not invisible
if(current_team.is_enemy(j->second.side()) &&
j->second.invisible(map.underlying_terrain(
map[j->first.x][j->first.y])) == false) {
location adjacent[6];
get_adjacent_tiles(j->first,adjacent);
attack_analysis analysis;
analysis.target = j->first;
const int ticks = SDL_GetTicks();
do_analysis(map,j->first,srcdst,dstsrc,adjacent,used_locations,
unit_locs,units,res,data,status,analysis);
const int time_taken = SDL_GetTicks() - ticks;
static int max_time = 0;
if(time_taken > max_time)
max_time = time_taken;
std::cerr << "do_analysis took " << time_taken << " (" << max_time << ")\n";
}
}
return res;
}
}

View file

@ -1,77 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef AI_ATTACK_INCLUDED
#define AI_ATTACK_INCLUDED
#include "actions.hpp"
#include "ai.hpp"
#include "gamestatus.hpp"
#include "map.hpp"
#include "unit_types.hpp"
#include <map>
#include <vector>
namespace ai {
struct attack_analysis
{
void analyze(const gamemap& map, std::map<location,unit>& units,
const gamestatus& status, const game_data& info, int sims);
double rating(double aggression) const;
gamemap::location target;
std::vector<std::pair<gamemap::location,gamemap::location> > movements;
std::vector<int> weapons;
//the value of the unit being targeted
double target_value;
//the value on average, of units lost in the combat
double avg_losses;
//estimated % chance to kill the unit
double chance_to_kill;
//the average hitpoints damage inflicted
double avg_damage_inflicted;
int target_starting_damage;
//the average hitpoints damage taken
double avg_damage_taken;
//the sum of the values of units used in the attack
double resources_used;
//the weighted average of the % chance to hit each attacking unit
double terrain_quality;
//the ratio of the attacks the unit being attacked will get to
//the strength of its most powerful attack
double counter_strength_ratio;
};
std::vector<attack_analysis> analyze_targets(
const gamemap& map,
const std::multimap<location,location>& srcdst,
const std::multimap<location,location>& dstsrc,
std::map<location,unit>& units,
const team& current_team, int team_num,
const gamestatus& status, const game_data& data
);
}
#endif

View file

@ -1,292 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "ai_move.hpp"
#include "display.hpp"
#include "game_config.hpp"
#include "log.hpp"
#include "util.hpp"
#include <iostream>
namespace ai {
struct move_cost_calculator
{
move_cost_calculator(const unit& u, const gamemap& map,
const game_data& data,
const std::map<location,unit>& units,
const gamemap::location& loc,
const std::multimap<location,location>& dstsrc)
: unit_(u), map_(map), data_(data), units_(units),
move_type_(u.type().movement_type()), loc_(loc), dstsrc_(dstsrc)
{}
double cost(const location& loc) const
{
if(!map_.on_board(loc))
return 1000.0;
//if this unit can move to that location this turn, it has a very
//very low cost
typedef std::multimap<location,location>::const_iterator Itor;
std::pair<Itor,Itor> range = dstsrc_.equal_range(loc);
while(range.first != range.second) {
if(range.first->first == loc)
return 0.01;
++range.first;
}
const gamemap::TERRAIN terrain =
map_.underlying_terrain(map_[loc.x][loc.y]);
const double modifier = 1.0;//move_type_.defense_modifier(map_,terrain);
const double move_cost = move_type_.movement_cost(map_,terrain);
double enemies = 0;
/* //is this stuff responsible for making it take a long time?
location adj[7];
adj[0] = loc;
get_adjacent_tiles(loc,adj+1);
for(int i = 0; i != 7; ++i) {
const std::map<location,unit>::const_iterator en=units_.find(adj[i]);
//at the moment, don't allow any units to be in the path
if(i == 0 && en != units_.end()) {
return 1000.0;
}
if(en != units_.end() && en->second.side() == enemy_) {
enemies += 1.0;
}
}
*/
const double res = modifier*move_cost + enemies*2.0;
assert(res > 0);
return res;
}
private:
const unit& unit_;
const gamemap& map_;
const game_data& data_;
const std::map<location,unit>& units_;
const unit_movement_type& move_type_;
const gamemap::location loc_;
const std::multimap<location,location> dstsrc_;
};
std::vector<target> find_targets(
const gamemap& map, std::map<location,unit>& units,
std::vector<team>& teams, int current_team
)
{
log_scope("finding targets...");
team& tm = teams[current_team-1];
std::vector<target> targets;
if(tm.village_value() > 0.0) {
const std::vector<location>& towers = map.towers();
for(std::vector<location>::const_iterator t = towers.begin();
t != towers.end(); ++t) {
assert(map.on_board(*t));
bool get_tower = true;
for(int i = 0; i != teams.size(); ++i) {
if(!tm.is_enemy(i+1) && teams[i].owns_tower(*t)) {
get_tower = false;
break;
}
}
if(get_tower) {
targets.push_back(target(*t,tm.village_value()));
}
}
}
std::vector<team::target>& team_targets = tm.targets();
//find the enemy leaders and explicit targets
std::map<location,unit>::const_iterator u;
for(u = units.begin(); u != units.end(); ++u) {
//is an enemy leader
if(u->second.can_recruit() && tm.is_enemy(u->second.side())) {
assert(map.on_board(u->first));
targets.push_back(target(u->first,tm.leader_value()));
}
//explicit targets for this team
for(std::vector<team::target>::iterator j = team_targets.begin();
j != team_targets.end(); ++j) {
if(u->second.matches_filter(j->criteria)) {
std::cerr << "found explicit target..." << j->value << "\n";
targets.push_back(target(u->first,j->value));
}
}
}
std::vector<double> new_values;
for(std::vector<target>::iterator i = targets.begin();
i != targets.end(); ++i) {
new_values.push_back(i->value);
for(std::vector<target>::const_iterator j = targets.begin();
j != targets.end(); ++j) {
if(i->loc == j->loc) {
continue;
}
const double distance = abs(j->loc.x - i->loc.x) +
abs(j->loc.y - i->loc.y);
new_values.back() += j->value/(distance*distance);
}
}
assert(new_values.size() == targets.size());
for(int n = 0; n != new_values.size(); ++n) {
std::cerr << "target value: " << targets[n].value << " -> " << new_values[n] << "\n";
targets[n].value = new_values[n];
}
return targets;
}
std::pair<location,location> choose_move(
std::vector<target>& targets,
const std::multimap<location,location>& dstsrc,
std::map<location,unit>& units,
const gamemap& map, const std::vector<team>& teams,
int current_team,
const game_data& data
)
{
log_scope("choosing move");
std::vector<target>::const_iterator ittg;
for(ittg = targets.begin(); ittg != targets.end(); ++ittg) {
assert(map.on_board(ittg->loc));
}
paths::route best_route;
std::map<location,unit>::iterator best = units.end();
double best_rating = 0.1;
std::vector<target>::iterator best_target = targets.end();
std::map<location,unit>::iterator u;
//find the first eligible unit
for(u = units.begin(); u != units.end(); ++u) {
if(!(u->second.side() != current_team || u->second.can_recruit() ||
u->second.movement_left() <= 0)) {
break;
}
}
if(u == units.end()) {
std::cout << "no eligible units found\n";
return std::pair<location,location>();
}
//guardian units stay put
if(u->second.is_guardian()) {
std::cerr << u->second.type().name() << " is guardian, staying still\n";
return std::pair<location,location>(u->first,u->first);
}
const move_cost_calculator cost_calc(u->second,map,data,units,
u->first,dstsrc);
//choose the best target for that unit
for(std::vector<target>::iterator tg = targets.begin();
tg != targets.end(); ++tg) {
assert(map.on_board(tg->loc));
const paths::route cur_route = a_star_search(u->first,tg->loc,
minimum(tg->value/best_rating,100.0),cost_calc);
const double rating = tg->value/cur_route.move_left;
std::cerr << tg->value << "/" << cur_route.move_left << " = " << rating << "\n";
if(best_target == targets.end() || rating > best_rating) {
best_rating = rating;
best_target = tg;
best = u;
best_route = cur_route;
}
}
if(best_target == targets.end()) {
std::cout << "no eligible targets found\n";
return std::pair<location,location>();
}
//now see if any other unit can put a better bid forward
for(++u; u != units.end(); ++u) {
if(u->second.side() != current_team || u->second.can_recruit() ||
u->second.movement_left() <= 0) {
continue;
}
const move_cost_calculator calc(u->second,map,data,units,
u->first,dstsrc);
const paths::route cur_route = a_star_search(u->first,best_target->loc,
minimum(best_target->value/best_rating,100.0),calc);
const double rating = best_target->value/cur_route.move_left;
if(best == units.end() || rating > best_rating) {
best_rating = rating;
best = u;
best_route = cur_route;
}
}
assert(best_target >= targets.begin() && best_target < targets.end());
best_target->value -= best->second.type().cost()/20.0;
if(best_target->value <= 0.0)
targets.erase(best_target);
for(ittg = targets.begin();
ittg != targets.end(); ++ittg) {
assert(map.on_board(ittg->loc));
}
for(std::vector<location>::reverse_iterator ri =
best_route.steps.rbegin(); ri != best_route.steps.rend(); ++ri) {
if(game_config::debug) {
display::debug_highlight(*ri,0.2);
}
typedef std::multimap<location,location>::const_iterator Itor;
std::pair<Itor,Itor> its = dstsrc.equal_range(*ri);
while(its.first != its.second) {
if(its.first->second == best->first) {
return std::pair<location,location>(its.first->second,
its.first->first);
}
++its.first;
}
}
if(best != units.end()) {
std::cout << "Could not make good move, staying still\n";
return std::pair<location,location>(best->first,best->first);
}
std::cout << "Could not find anywhere to move!\n";
return std::pair<location,location>();
}
}

View file

@ -1,50 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef AI_MOVE_H_INCLUDED
#define AI_MOVE_H_INCLUDED
#include "map.hpp"
#include "pathfind.hpp"
#include "unit.hpp"
#include "unit_types.hpp"
#include <map>
namespace ai {
typedef gamemap::location location;
struct target {
target(const location& pos, double val) : loc(pos), value(val)
{}
location loc;
double value;
};
std::vector<target> find_targets(
const gamemap& map, std::map<location,unit>& units,
std::vector<team>& teams, int current_team
);
std::pair<location,location> choose_move(
std::vector<target>& targets,
const std::multimap<location,location>& dstsrc,
std::map<location,unit>& units,
const gamemap& map, const std::vector<team>& teams,
int current_team,
const game_data& data
);
}
#endif

View file

@ -1,544 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include <algorithm>
#include <cassert>
#include <fstream>
#include <iostream>
#include <stack>
#include <sstream>
#include <vector>
#include "config.hpp"
#include "filesystem.hpp"
#include "log.hpp"
bool operator<(const line_source& a, const line_source& b)
{
return a.linenum < b.linenum;
}
namespace {
line_source get_line_source(const std::vector<line_source>& line_src, int line)
{
line_source res(line,"",0);
std::vector<line_source>::const_iterator it =
std::upper_bound(line_src.begin(),line_src.end(),res);
if(it != line_src.begin()) {
--it;
res.file = it->file;
res.fileline = it->fileline + (line - it->linenum);
}
return res;
}
}
std::string read_file(const std::string& fname)
{
//if we have a path to the data
#ifdef WESNOTH_PATH
//convert any filepath which is relative
if(!fname.empty() && fname[0] != '/' && WESNOTH_PATH[0] == '/') {
std::cerr << "trying to read file: '" <<
(WESNOTH_PATH + std::string("/") + fname) << "'\n";
const std::string& res =
read_file(WESNOTH_PATH + std::string("/") + fname);
if(!res.empty()) {
std::cerr << "success\n";
return res;
}
}
#endif
std::ifstream file(fname.c_str());
std::string res;
char c;
while(file.get(c)) {
res.resize(res.size()+1);
res[res.size()-1] = c;
}
return res;
}
void write_file(const std::string& fname, const std::string& data)
{
std::ofstream file(fname.c_str());
for(std::string::const_iterator i = data.begin(); i != data.end(); ++i) {
file << *i;
}
}
namespace {
void internal_preprocess_file(const std::string& fname,
std::map<std::string,std::string> defines_map,
int depth, std::vector<char>& res,
std::vector<line_source>* lines_src, int& line)
{
//if it's a directory, we process all files in the directory
//that end in .cfg
if(is_directory(fname)) {
std::vector<std::string> files;
get_files_in_dir(fname,&files,NULL,ENTIRE_FILE_PATH);
for(std::vector<std::string>::const_iterator f = files.begin();
f != files.end(); ++f) {
if(f->size() > 4 && std::equal(f->end()-4,f->end(),".cfg")) {
internal_preprocess_file(*f,defines_map,depth,res,
lines_src,line);
}
}
return;
}
int srcline = 1;
if(lines_src != NULL) {
lines_src->push_back(line_source(line,fname,srcline));
}
const std::string data = read_file(fname);
bool in_quotes = false;
for(std::string::const_iterator i = data.begin(); i != data.end(); ++i) {
const char c = *i;
if(c == '"') {
in_quotes = !in_quotes;
}
if(c == '{') {
std::stringstream newfile;
for(++i; i != data.end() && *i != '}'; ++i) {
newfile << *i;
}
if(i == data.end())
break;
const std::string newfilename = newfile.str();
//if this is a known pre-processing symbol, then we insert
//it, otherwise we assume it's a file name to load
if(defines_map.count(newfilename) != 0) {
const std::string& val = defines_map[newfilename];
res.insert(res.end(),val.begin(),val.end());
line += std::count(val.begin(),val.end(),'\n');
} else if(depth < 20) {
internal_preprocess_file("data/" + newfilename,
defines_map, depth+1,res,
lines_src,line);
} else {
const std::string& str = read_file(newfilename);
res.insert(res.end(),str.begin(),str.end());
line += std::count(str.begin(),str.end(),'\n');
}
if(lines_src != NULL) {
lines_src->push_back(line_source(line,fname,srcline));
}
} else if(c == '#' && !in_quotes) {
//we are about to skip some things, so keep track of
//the start of where we're skipping, so we can count
//the number of newlines, so we can track the line number
//in the source file
const std::string::const_iterator begin = i;
//if this is the beginning of a pre-processing definition
static const std::string hash_define("#define");
if(data.end() - i > hash_define.size() &&
std::equal(hash_define.begin(),hash_define.end(),i)) {
i += hash_define.size();
while(i != data.end() && isspace(*i))
++i;
const std::string::const_iterator end =
std::find_if(i,data.end(),isspace);
if(end == data.end())
break;
const std::string symbol(i,end);
std::stringstream value;
for(i = end+1; i != data.end(); ++i) {
static const std::string hash_enddef("#enddef");
if(data.end() - i > hash_enddef.size() &&
std::equal(hash_enddef.begin(),hash_enddef.end(),i)) {
i += hash_enddef.size();
break;
}
value << *i;
}
defines_map.insert(std::pair<std::string,std::string>(
symbol,value.str()));
}
//if this is a pre-processing conditional
static const std::string hash_ifdef("#ifdef");
static const std::string hash_else("#else");
static const std::string hash_endif("#endif");
if(data.end() - i > hash_ifdef.size() &&
std::equal(hash_ifdef.begin(),hash_ifdef.end(),i)) {
i += hash_ifdef.size();
while(i != data.end() && isspace(*i))
++i;
const std::string::const_iterator end =
std::find_if(i,data.end(),isspace);
if(end == data.end())
break;
//if the symbol is not defined, then we want to skip
//to the #endif or #else . Otherwise, continue processing
//as normal. The #endif will just be treated as a comment
//anyway.
const std::string symbol(i,end);
if(defines_map.count(symbol) == 0) {
while(data.end() - i > hash_endif.size() &&
!std::equal(hash_endif.begin(),hash_endif.end(),i) &&
!std::equal(hash_else.begin(),hash_else.end(),i)) {
++i;
}
i = std::find(i,data.end(),'\n');
if(i == data.end())
break;
} else {
i = end;
}
}
//if we come across a #else, it must mean that we found a #ifdef
//earlier, and we should ignore until #endif
if(data.end() - i > hash_else.size() &&
std::equal(hash_else.begin(),hash_else.end(),i)) {
while(data.end() - i > hash_endif.size() &&
!std::equal(hash_endif.begin(),hash_endif.end(),i)) {
++i;
}
i = std::find(i,data.end(),'\n');
if(i == data.end())
break;
}
for(; i != data.end() && *i != '\n'; ++i) {
}
if(i == data.end())
break;
srcline += std::count(begin,i,'\n');
++line;
res.push_back('\n');
} else {
if(c == '\n') {
++line;
++srcline;
}
res.push_back(c);
}
}
return;
}
} //end anonymous namespace
std::string preprocess_file(const std::string& fname,
const std::map<std::string,std::string>* defines,
std::vector<line_source>* line_sources)
{
log_scope("preprocessing file...");
static const std::map<std::string,std::string> default_defines;
if(defines == NULL)
defines = &default_defines;
std::vector<char> res;
int linenum = 0;
internal_preprocess_file(fname,*defines,0,res,line_sources,linenum);
return std::string(res.begin(),res.end());
}
config::config(const std::string& data,
const std::vector<line_source>* line_sources)
{
log_scope("parsing config...");
read(data,line_sources);
}
config::config(const config& cfg) : values(cfg.values)
{
for(std::map<std::string,std::vector<config*> >::const_iterator i =
cfg.children.begin(); i != cfg.children.end(); ++i) {
std::vector<config*> v;
for(std::vector<config*>::const_iterator j = i->second.begin();
j != i->second.end(); ++j) {
v.push_back(new config(**j));
}
children[i->first].swap(v);
}
}
config::~config()
{
clear();
}
config& config::operator=(const config& cfg)
{
clear();
values = cfg.values;
for(std::map<std::string,std::vector<config*> >::const_iterator i =
cfg.children.begin(); i != cfg.children.end(); ++i) {
std::vector<config*> v;
for(std::vector<config*>::const_iterator j = i->second.begin();
j != i->second.end(); ++j) {
v.push_back(new config(**j));
}
children[i->first].swap(v);
}
return *this;
}
void config::read(const std::string& data,
const std::vector<line_source>* line_sources)
{
clear();
std::stack<std::string> element_names;
std::stack<config*> elements;
elements.push(this);
element_names.push("");
enum { ELEMENT_NAME, IN_ELEMENT, VARIABLE_NAME, VALUE }
state = IN_ELEMENT;
std::string var;
std::string value;
bool in_quotes = false;
int line = 0;
for(std::string::const_iterator i = data.begin(); i != data.end(); ++i) {
const char c = *i;
if(c == '\n')
++line;
switch(state) {
case ELEMENT_NAME:
if(c == ']') {
if(value == "end" || !value.empty() && value[0] == '/') {
assert(!elements.empty());
if(value[0] == '/' &&
std::string("/" + element_names.top()) != value) {
std::stringstream err;
if(line_sources != NULL) {
const line_source src =
get_line_source(*line_sources,line);
err << src.file << " " << src.fileline << ": ";
}
err << "Found illegal end tag: '" << value
<< "', at end of '"
<< element_names.top() << "'";
throw error(err.str());
}
elements.pop();
element_names.pop();
if(elements.empty()) {
std::stringstream err;
if(line_sources != NULL) {
const line_source src =
get_line_source(*line_sources,line);
err << src.file << " " << src.fileline << ": ";
}
err << "Unexpected terminating tag\n";
throw error(err.str());
return;
}
state = IN_ELEMENT;
break;
}
config* const new_config = new config();
elements.top()->children[value].push_back(new_config);
elements.push(new_config);
element_names.push(value);
state = IN_ELEMENT;
value = "";
} else {
value.resize(value.size()+1);
value[value.size()-1] = c;
}
break;
case IN_ELEMENT:
if(c == '[') {
state = ELEMENT_NAME;
value = "";
} else if(!isspace(c)) {
value.resize(1);
value[0] = c;
state = VARIABLE_NAME;
}
break;
case VARIABLE_NAME:
if(c == '=') {
state = VALUE;
var = value;
value = "";
} else {
value.resize(value.size()+1);
value[value.size()-1] = c;
}
break;
case VALUE:
if(c == '"') {
in_quotes = !in_quotes;
} else if(c == '\n' && !in_quotes) {
state = IN_ELEMENT;
elements.top()->values.insert(
std::pair<std::string,std::string>(var,value));
var = "";
value = "";
} else {
value.resize(value.size()+1);
value[value.size()-1] = c;
}
break;
}
}
}
std::string config::write() const
{
std::string res;
for(std::map<std::string,std::string>::const_iterator i = values.begin();
i != values.end(); ++i) {
if(i->second.empty() == false) {
res += i->first + "=" + i->second + "\n";
}
}
for(std::map<std::string,std::vector<config*> >::const_iterator j =
children.begin(); j != children.end(); ++j) {
const std::vector<config*>& v = j->second;
for(std::vector<config*>::const_iterator it = v.begin();
it != v.end(); ++it) {
res += "[" + j->first + "]\n";
res += (*it)->write();
res += "[/" + j->first + "]\n";
}
}
res += "\n";
return res;
}
std::vector<std::string> config::split(const std::string& val)
{
std::vector<std::string> res;
std::string::const_iterator i1 = val.begin();
std::string::const_iterator i2 = val.begin();
while(i2 != val.end()) {
if(*i2 == ',') {
std::string new_val(i1,i2);
if(!new_val.empty())
res.push_back(new_val);
++i2;
while(i2 != val.end() && *i2 == ' ')
++i2;
i1 = i2;
} else {
++i2;
}
}
std::string new_val(i1,i2);
if(!new_val.empty())
res.push_back(new_val);
return res;
}
bool config::has_value(const std::string& values, const std::string& val)
{
const std::vector<std::string>& vals = split(values);
return std::count(vals.begin(),vals.end(),val) > 0;
}
void config::clear()
{
for(std::map<std::string,std::vector<config*> >::iterator i =
children.begin(); i != children.end(); ++i) {
std::vector<config*>& v = i->second;
for(std::vector<config*>::iterator j = v.begin(); j != v.end(); ++j)
delete *j;
}
children.clear();
values.clear();
}
//#define TEST_CONFIG
#ifdef TEST_CONFIG
int main()
{
config cfg(read_file("testconfig"));
std::cout << cfg.write() << std::endl;
}
#endif

View file

@ -1,69 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef CONFIG_HPP_INCLUDED
#define CONFIG_HPP_INCLUDED
#include <map>
#include <string>
#include <vector>
struct line_source
{
line_source(int ln,const std::string& fname, int line) :
linenum(ln), file(fname), fileline(line)
{}
int linenum;
std::string file;
int fileline;
};
bool operator<(const line_source& a, const line_source& b);
std::string read_file(const std::string& fname);
void write_file(const std::string& fname, const std::string& data);
std::string preprocess_file(const std::string& fname,
const std::map<std::string,std::string>* defines=0,
std::vector<line_source>* src=0);
typedef std::map<std::string,std::string> string_map;
struct config
{
config() {}
config(const std::string& data,
const std::vector<line_source>* lines=0); //throws config::error
config(const config& cfg);
~config();
config& operator=(const config& cfg);
void read(const std::string& data,
const std::vector<line_source>* lines=0); //throws config::error
std::string write() const;
std::map<std::string,std::string> values;
std::map<std::string,std::vector<config*> > children;
static std::vector<std::string> split(const std::string& val);
static bool has_value(const std::string& values, const std::string& val);
void clear();
struct error {
error(const std::string& msg) : message(msg) {}
std::string message;
};
};
#endif

View file

@ -1,83 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "dialogs.hpp"
#include "language.hpp"
#include "replay.hpp"
#include <map>
#include <string>
#include <vector>
namespace dialogs
{
void advance_unit(const game_data& info,
std::map<gamemap::location,unit>& units,
const gamemap::location& loc,
display& gui, bool random_choice)
{
std::map<gamemap::location,unit>::iterator u = units.find(loc);
if(u == units.end() || u->second.advances() == false)
return;
const std::vector<std::string>& options = u->second.type().advances_to();
std::vector<unit> sample_units;
for(std::vector<std::string>::const_iterator op = options.begin();
op != options.end(); ++op) {
sample_units.push_back(::get_advanced_unit(info,units,loc,*op));
}
int res = 0;
if(options.empty()) {
return;
} else if(random_choice) {
res = rand()%options.size();
} else if(options.size() > 1) {
res = gui::show_dialog(gui,NULL,string_table["advance_unit_heading"],
string_table["advance_unit_message"],
gui::OK_ONLY, &options, &sample_units);
}
//when the unit advances, it fades to white, and then switches to the
//new unit, then fades back to the normal colour
double intensity;
for(intensity = 1.0; intensity >= 0.0; intensity -= 0.05) {
gui.set_advancing_unit(loc,intensity);
gui.draw(false);
gui.update_display();
SDL_Delay(30);
}
recorder.choose_option(res);
::advance_unit(info,units,loc,options[res]);
gui.invalidate_unit();
for(intensity = 0.0; intensity <= 1.0; intensity += 0.05) {
gui.set_advancing_unit(loc,intensity);
gui.draw(false);
gui.update_display();
SDL_Delay(30);
}
gui.set_advancing_unit(gamemap::location(),0.0);
gui.invalidate_all();
gui.draw();
}
} //end namespace dialogs

View file

@ -1,28 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef DIALOGS_H_INCLUDED
#define DIALOGS_H_INCLUDED
#include "actions.hpp"
#include "display.hpp"
#include "menu.hpp"
namespace dialogs
{
void advance_unit(const game_data& info,
std::map<gamemap::location,unit>& units,
const gamemap::location& loc,
display& gui, bool random_choice=false);
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,252 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef DISPLAY_H_INCLUDED
#define DISPLAY_H_INCLUDED
#include "gamestatus.hpp"
#include "key.hpp"
#include "map.hpp"
#include "pathfind.hpp"
#include "team.hpp"
#include "unit.hpp"
#include "video.hpp"
#include "SDL.h"
#include <map>
#include <set>
#include <string>
class display
{
public:
typedef std::map<gamemap::location,unit> unit_map;
typedef short Pixel;
display(unit_map& units, CVideo& video,
const gamemap& map, const gamestatus& status,
const std::vector<team>& t);
~display();
Pixel rgb(int r, int g, int b) const;
void scroll(double xmov, double ymov);
void zoom(double amount);
void default_zoom();
enum SCROLL_TYPE { SCROLL, WARP };
void scroll_to_tile(int x, int y, SCROLL_TYPE scroll_type=SCROLL);
void scroll_to_tiles(int x1, int y1, int x2, int y2,
SCROLL_TYPE scroll_type=SCROLL);
void redraw_everything();
void draw(bool update=true,bool force=false);
int x() const;
int mapx() const;
int y() const;
void select_hex(gamemap::location hex);
void highlight_hex(gamemap::location hex);
gamemap::location hex_clicked_on(int x, int y);
gamemap::location minimap_location_on(int x, int y);
void set_paths(const paths* paths_list);
double get_location_x(const gamemap::location& loc) const;
double get_location_y(const gamemap::location& loc) const;
void move_unit(const std::vector<gamemap::location>& path, unit& u);
bool unit_attack(const gamemap::location& a, const gamemap::location& b,
int damage, const attack_type& attack);
void draw_tile(int x, int y, SDL_Surface* unit_image=NULL,
double alpha=1.0, short blend_to=0);
CVideo& video() { return screen_; }
enum IMAGE_TYPE { UNSCALED, SCALED, GREYED, BRIGHTENED };
SDL_Surface* getImage(const std::string& filename,IMAGE_TYPE type=SCALED);
//blits a surface with black as alpha
void blit_surface(int x, int y, SDL_Surface* surface);
void invalidate_all();
void invalidate_game_status();
void invalidate_unit();
void recalculate_minimap();
void add_overlay(const gamemap::location& loc, const std::string& image);
void remove_overlay(const gamemap::location& loc);
void draw_unit_details(int x, int y, const gamemap::location& loc,
const unit& u, SDL_Rect& description_rect, SDL_Rect& profile_rect);
void update_display();
void update_rect(const SDL_Rect& rect);
void draw_terrain_palette(int x, int y, gamemap::TERRAIN selected);
gamemap::TERRAIN get_terrain_on(int palx, int paly, int x, int y);
void set_team(int team);
void set_advancing_unit(const gamemap::location& loc, double amount);
void lock_updates(bool value);
bool update_locked() const;
bool turbo() const;
void set_turbo(bool turbo);
void set_grid(bool grid);
static void debug_highlight(const gamemap::location& loc, double amount);
static void clear_debug_highlights();
private:
display(const display&);
void operator=(const display&);
void move_unit_between(const gamemap::location& a,
const gamemap::location& b,
const unit& u);
void draw_unit(int x, int y, const SDL_Surface* image,
bool reverse, bool upside_down=false,
double alpha=1.0, short blendto=0);
void unit_die(const gamemap::location& loc, SDL_Surface* image=NULL);
bool unit_attack_ranged(const gamemap::location& a,
const gamemap::location& b,
int damage, const attack_type& attack);
void draw_sidebar();
SDL_Rect get_minimap_location(int x, int y, int w, int h);
void draw_minimap(int x, int y, int w, int h);
void draw_game_status(int x, int y);
SDL_Rect gameStatusRect_;
SDL_Rect unitDescriptionRect_;
SDL_Rect unitProfileRect_;
int lastTimeOfDay_;
void bounds_check_position();
std::vector<SDL_Surface*> getAdjacentTerrain(int x, int y, IMAGE_TYPE type);
SDL_Surface* getTerrain(gamemap::TERRAIN, IMAGE_TYPE type,
int x, int y, const std::string& dir="");
enum TINT { GREY_IMAGE, BRIGHTEN_IMAGE };
SDL_Surface* getImageTinted(const std::string& filename, TINT tint);
SDL_Surface* getMinimap(int w, int h);
void clearImageCache();
CVideo& screen_;
mutable CKey keys_;
double xpos_, ypos_, zoom_;
const gamemap& map_;
gamemap::location selectedHex_;
gamemap::location mouseoverHex_;
unit_map& units_;
std::map<std::string,SDL_Surface*> images_, scaledImages_,
greyedImages_, brightenedImages_;
//function which finds the start and end rows on the energy bar image
//where white pixels are substituted for the colour of the energy
const std::pair<int,int>& calculate_energy_bar();
std::pair<int,int> energy_bar_count_;
SDL_Surface* minimap_;
bool minimapDecorationsDrawn_;
const paths* pathsList_;
const gamestatus& status_;
const std::vector<team>& teams_;
int lastDraw_;
int drawSkips_;
void invalidate(const gamemap::location& loc);
std::set<gamemap::location> invalidated_;
bool invalidateAll_;
bool invalidateUnit_;
bool invalidateGameStatus_;
std::multimap<gamemap::location,std::string> overlays_;
void update_whole_screen();
void update_map_area();
void update_side_bar();
std::vector<SDL_Rect> updateRects_;
bool sideBarBgDrawn_;
int currentTeam_;
//used to store a unit that is not drawn, because it's currently
//being moved or otherwise changed
gamemap::location hiddenUnit_;
//used to store any unit that is currently being hit
gamemap::location hitUnit_;
//used to store any unit that is dying
gamemap::location deadUnit_;
double deadAmount_;
//used to store any unit that is advancing
gamemap::location advancingUnit_;
double advancingAmount_;
int updatesLocked_;
bool turbo_, grid_;
//for debug mode
static std::map<gamemap::location,double> debugHighlights_;
};
struct update_locker
{
update_locker(display& d, bool lock=true) : disp(d), unlock(lock) {
if(lock) {
disp.lock_updates(true);
}
}
~update_locker() {
unlock_update();
}
void unlock_update() {
if(unlock) {
disp.lock_updates(false);
unlock = false;
}
}
private:
display& disp;
bool unlock;
};
#endif

View file

@ -1,248 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
//include files for opendir(3), readdir(3), etc. These files may vary
//from platform to platform, since these functions are NOT ANSI-conforming
//functions. They may have to be altered to port to new platforms
#include <sys/types.h>
#include <dirent.h>
//for mkdir
#include <sys/stat.h>
#include <sys/types.h>
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#define mkdir(a,b) (_mkdir(a))
#define mode_t int
#endif
//for getenv
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include "filesystem.hpp"
namespace {
const mode_t AccessMode = 00770;
}
#ifdef _WIN32
#define DIR_INVALID(d) (d == -1)
#else
#define DIR_INVALID(d) (d == NULL)
#endif
void get_files_in_dir(const std::string& directory,
std::vector<std::string>* files,
std::vector<std::string>* dirs,
FILE_NAME_MODE mode)
{
//if we have a path to find directories in, then convert relative
//pathnames to be rooted on the wesnoth path
#ifdef WESNOTH_PATH
if(!directory.empty() && directory[0] != '/' && WESNOTH_PATH[0] == '/') {
const std::string& dir = WESNOTH_PATH + std::string("/") + directory;
if(is_directory(dir)) {
get_files_in_dir(dir,files,dirs,mode);
return;
}
}
#endif
#ifdef _WIN32
_finddata_t fileinfo;
long dir = _findfirst((directory + "/*.*").c_str(),&fileinfo);
#else
DIR* dir = opendir(directory.c_str());
#endif
if(DIR_INVALID(dir)) {
//try to make the directory
const int res = mkdir(directory.c_str(),AccessMode);
if(res == 0) {
#ifdef _WIN32
dir = _findfirst((directory + "/*.*").c_str(),&fileinfo);
#else
dir = opendir(directory.c_str());
#endif
}
if(DIR_INVALID(dir))
return;
}
#ifdef _WIN32
int res = dir;
do {
if(fileinfo.name[0] != '.') {
const std::string path = (mode == ENTIRE_FILE_PATH ?
directory + "/" : std::string("")) + fileinfo.name;
if(fileinfo.attrib&_A_SUBDIR) {
if(dirs != NULL)
dirs->push_back(path);
} else {
if(files != NULL)
files->push_back(path);
}
}
res = _findnext(dir,&fileinfo);
} while(!DIR_INVALID(res));
_findclose(dir);
#else
struct dirent* entry;
while((entry = readdir(dir)) != NULL) {
if(entry->d_name[0] == '.')
continue;
const std::string name((directory + "/") + entry->d_name);
//try to open it as a directory to test if it is a directory
DIR* const temp_dir = opendir(name.c_str());
if(temp_dir != NULL) {
closedir(temp_dir);
if(dirs != NULL) {
if(mode == ENTIRE_FILE_PATH)
dirs->push_back(name);
else
dirs->push_back(entry->d_name);
}
} else if(files != NULL) {
if(mode == ENTIRE_FILE_PATH)
files->push_back(name);
else
files->push_back(entry->d_name);
}
}
closedir(dir);
#endif
if(files != NULL)
std::sort(files->begin(),files->end());
if(dirs != NULL)
std::sort(dirs->begin(),dirs->end());
}
std::string get_prefs_file()
{
return get_user_data_dir() + "/preferences";
}
std::string get_saves_dir()
{
const std::string dir_path = get_user_data_dir() + "/saves";
#ifdef _WIN32
_mkdir(dir_path.c_str());
#else
DIR* dir = opendir(dir_path.c_str());
if(dir == NULL) {
const int res = mkdir(dir_path.c_str(),AccessMode);
if(res == 0) {
dir = opendir(dir_path.c_str());
} else {
std::cerr << "Could not open or create directory: '" << dir_path
<< "'\n";
}
}
if(dir == NULL)
return "";
#endif
return dir_path;
}
std::string get_user_data_dir()
{
static const char* const current_dir = ".";
#ifdef _WIN32
_mkdir("userdata");
return "userdata";
#else
const char* home_str = getenv("HOME");
if(home_str == NULL)
home_str = current_dir;
const std::string home(home_str);
const std::string dir_path = home + "/.wesnoth";
DIR* dir = opendir(dir_path.c_str());
if(dir == NULL) {
const int res = mkdir(dir_path.c_str(),AccessMode);
if(res == 0) {
dir = opendir(dir_path.c_str());
} else {
std::cerr << "Could not open or create directory: '" << dir_path
<< "'\n";
}
}
if(dir == NULL)
return "";
closedir(dir);
return dir_path;
#endif
}
bool is_directory(const std::string& fname)
{
#ifdef WESNOTH_PATH
if(!fname.empty() && fname[0] != '/' && WESNOTH_PATH[0] == '/') {
if(is_directory(WESNOTH_PATH + std::string("/") + fname))
return true;
}
#endif
#ifdef _WIN32
_finddata_t info;
const long handle = _findfirst((fname + "/*").c_str(),&info);
if(handle >= 0) {
_findclose(handle);
return true;
} else {
return false;
}
#else
DIR* const dir = opendir(fname.c_str());
if(dir != NULL) {
closedir(dir);
return true;
} else {
return false;
}
#endif
}

View file

@ -1,32 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef FILESYSTEM_HPP_INCLUDED
#define FILESYSTEM_HPP_INCLUDED
#include <string>
#include <vector>
enum FILE_NAME_MODE { ENTIRE_FILE_PATH, FILE_NAME_ONLY };
void get_files_in_dir(const std::string& dir,
std::vector<std::string>* files,
std::vector<std::string>* dirs=NULL,
FILE_NAME_MODE mode=FILE_NAME_ONLY);
std::string get_prefs_file();
std::string get_saves_dir();
std::string get_user_data_dir();
bool is_directory(const std::string& fname);
#endif

199
font.cpp
View file

@ -1,199 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "config.hpp"
#include "font.hpp"
#include "SDL_ttf.h"
#include <cstdio>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
namespace {
std::map<int,TTF_Font*> font_table;
//SDL_ttf seems to have a problem where TTF_OpenFont will seg fault if
//the font file doesn't exist, so make sure it exists first.
TTF_Font* open_font(const std::string& fname, int size)
{
if(read_file(fname).empty())
return NULL;
return TTF_OpenFont(fname.c_str(),size);
}
TTF_Font* get_font(int size)
{
const std::map<int,TTF_Font*>::iterator it = font_table.find(size);
if(it != font_table.end())
return it->second;
TTF_Font* font = NULL;
std::cerr << "opening font file...\n";
#ifdef WESNOTH_PATH
font = open_font(std::string(WESNOTH_PATH) + "/images/misc/Vera.ttf",size);
#endif
if(font == NULL) {
font = open_font("images/misc/Vera.ttf",size);
}
if(font == NULL) {
std::cerr << "Could not open font file\n";
return font;
}
TTF_SetFontStyle(font,TTF_STYLE_NORMAL);
std::cerr << "inserting font...\n";
font_table.insert(std::pair<int,TTF_Font*>(size,font));
return font;
}
}
namespace font {
manager::manager()
{
const int res = TTF_Init();
if(res < 0) {
std::cerr << "Could not initialize true type fonts\n";
} else {
std::cerr << "Initialized true type fonts\n";
}
}
manager::~manager()
{
for(std::map<int,TTF_Font*>::iterator i = font_table.begin();
i != font_table.end(); ++i) {
TTF_CloseFont(i->second);
}
TTF_Quit();
}
SDL_Rect draw_text_line(display* gui, const SDL_Rect& area, int size,
COLOUR colour, const std::string& text, int x, int y,
SDL_Surface* bg)
{
static const SDL_Color colours[] =
// neutral good bad
{ {0xFF,0xFF,0,0}, {0,0xFF,0,0}, {0xFF,0,0,0} };
const SDL_Color& col = colours[colour];
TTF_Font* const font = get_font(size);
if(font == NULL) {
std::cerr << "Could not get font\n";
SDL_Rect res;
res.x = 0; res.y = 0; res.w = 0; res.h = 0;
return res;
}
SDL_Surface* const surface = TTF_RenderText_Blended(font,text.c_str(),col);
if(surface == NULL) {
std::cerr << "Could not render ttf: '" << text << "'\n";
SDL_Rect res;
res.x = 0; res.y = 0; res.w = 0; res.h = 0;
return res;
}
SDL_Rect dest;
dest.x = x;
dest.y = y;
dest.w = surface->w;
dest.h = surface->h;
if(dest.x + dest.w > area.x + area.w) {
dest.w = area.x + area.w - dest.x;
}
if(dest.y + dest.h > area.y + area.h) {
dest.h = area.y + area.h - dest.y;
}
if(gui != NULL) {
SDL_Rect src = dest;
src.x = 0;
src.y = 0;
SDL_BlitSurface(surface,&src,gui->video().getSurface(),&dest);
}
SDL_FreeSurface(surface);
return dest;
}
SDL_Rect draw_text(display* gui, const SDL_Rect& area, int size,
COLOUR colour, const std::string& txt, int x, int y,
SDL_Surface* bg)
{
//make sure there's always at least a space, so we can ensure
//that we can return a rectangle for height
static const std::string blank_text(" ");
const std::string& text = txt.empty() ? blank_text : txt;
SDL_Rect res;
res.x = x;
res.y = y;
res.w = 0;
res.h = 0;
std::string::const_iterator i1 = text.begin();
std::string::const_iterator i2 = std::find(i1,text.end(),'\n');
for(;;) {
if(i1 != i2) {
COLOUR col = NORMAL_COLOUR;
int sz = size;
if(*i1 == '#') {
col = BAD_COLOUR;
++i1;
} else if(*i1 == '@') {
col = GOOD_COLOUR;
++i1;
} else if(*i1 == '+') {
sz += 2;
++i1;
} else if(*i1 == '-') {
sz -= 2;
++i1;
}
if(i1 != i2) {
const std::string new_string(i1,i2);
const SDL_Rect rect =
draw_text_line(gui,area,sz,col,new_string,x,y,bg);
if(rect.w > res.w)
res.w = rect.w;
res.h += rect.h;
y += rect.h;
}
}
if(i2 == text.end()) {
break;
}
i1 = i2+1;
i2 = std::find(i1,text.end(),'\n');
}
return res;
}
}

View file

@ -1,37 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef FONT_HPP_INCLUDED
#define FONT_HPP_INCLUDED
#include "SDL.h"
#include "display.hpp"
#include "video.hpp"
#include <string>
namespace font {
struct manager {
manager();
~manager();
};
enum COLOUR { NORMAL_COLOUR, GOOD_COLOUR, BAD_COLOUR };
SDL_Rect draw_text(display* gui, const SDL_Rect& area, int size, COLOUR colour,
const std::string& text, int x, int y, SDL_Surface* bg=NULL);
}
#endif

407
game.cpp
View file

@ -1,407 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "SDL.h"
#include "actions.hpp"
#include "ai.hpp"
#include "config.hpp"
#include "dialogs.hpp"
#include "display.hpp"
#include "font.hpp"
#include "game_config.hpp"
#include "game_events.hpp"
#include "gamestatus.hpp"
#include "key.hpp"
#include "language.hpp"
#include "menu.hpp"
#include "multiplayer.hpp"
#include "pathfind.hpp"
#include "playlevel.hpp"
#include "preferences.hpp"
#include "replay.hpp"
#include "sound.hpp"
#include "team.hpp"
#include "unit_types.hpp"
#include "unit.hpp"
#include "video.hpp"
#include "widgets/button.hpp"
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
LEVEL_RESULT play_game(display& disp, game_state& state, config& game_config,
game_data& units_data, CVideo& video)
{
std::string type = state.campaign_type;
if(type.empty())
type = "scenario";
const std::vector<config*>& scenarios = game_config.children[type];
for(int i = state.scenario; i < scenarios.size(); ++i) {
std::vector<config*>& story = scenarios[i]->children["story"];
try {
LEVEL_RESULT res = REPLAY;
state.label = scenarios[i]->values["name"];
recorder.set_save_info(state);
while(res == REPLAY) {
state = recorder.get_save_info();
if(!recorder.empty()) {
recorder.start_replay();
}
res = play_level(units_data,game_config,scenarios[i],
video,state,story);
}
recorder.clear();
state.replay_data.clear();
if(res != VICTORY) {
return res;
}
} catch(gamestatus::load_game_failed& e) {
std::cerr << "The game could not be loaded: " << e.message << "\n";
return QUIT;
} catch(gamestatus::game_error& e) {
std::cerr << "An error occurred while playing the game: "
<< e.message << "\n";
return QUIT;
} catch(gamemap::incorrect_format_exception& e) {
std::cerr << "The game map could not be loaded: " << e.msg_ << "\n";
return QUIT;
}
//skip over any scenarios which should be skipped, because their
//conditions aren't met
while(i+1 < scenarios.size()) {
bool skip = false;
std::vector<config*>& conditions =
scenarios[i+1]->children["condition"];
for(std::vector<config*>::iterator cond = conditions.begin();
cond != conditions.end(); ++cond) {
if(game_events::conditional_passed(state,NULL,**cond) == false)
skip = true;
}
if(!skip)
break;
++i;
}
//if this isn't the last scenario, then save the game
if(i+1 < scenarios.size()) {
state.label = scenarios[i+1]->values["name"];
state.scenario = i+1;
const int should_save = gui::show_dialog(disp,NULL,"",
string_table["save_game_message"],
gui::YES_NO,NULL,NULL,
string_table["save_game_label"],&state.label);
if(should_save == 0) {
save_game(state);
}
}
}
return VICTORY;
}
int play_game(int argc, char** argv)
{
std::string text_chr = read_file("data/text.chr");
text_chr.resize(256*8);
CVideo video(text_chr.c_str());
const font::manager font_manager;
const sound::manager sound_manager;
const preferences::manager prefs_manager;
std::map<std::string,std::string> defines_map;
defines_map["NORMAL"] = "";
std::vector<line_source> line_src;
std::string game_cfg = preprocess_file("data/game.cfg",&defines_map,
&line_src);
config game_config(game_cfg,&line_src);
//clear game_cfg so it doesn't take up memory
std::string().swap(game_cfg);
const std::vector<config*>& units = game_config.children["units"];
if(units.empty()) {
std::cerr << "Could not find units configuration\n";
std::cerr << game_config.write();
return 0;
}
game_data units_data(*units[0]);
const bool lang_res = set_language(get_locale(), game_config);
if(!lang_res) {
std::cerr << "No translation for locale '" << get_locale()
<< "', default to locale 'en'\n";
const bool lang_res = set_language("en", game_config);
if(!lang_res) {
std::cerr << "Language data not found\n";
}
}
bool test_mode = false;
int video_flags = preferences::fullscreen() ? FULL_SCREEN : 0;
for(int arg = 1; arg != argc; ++arg) {
const std::string val(argv[arg]);
if(val == "-windowed") {
video_flags = 0;
} else if(val == "-test") {
test_mode = true;
} else if(val == "-debug") {
game_config::debug = true;
}
}
const std::pair<int,int>& resolution = preferences::resolution();
std::cerr << "checking mode possible...\n";
const int bpp = video.modePossible(resolution.first,resolution.second,
16,video_flags);
std::cerr << bpp << "\n";
if(bpp == 0) {
std::cerr << "The required video mode, " << resolution.first
<< "x" << resolution.second << "x16 "
<< "is not supported\n";
if(video_flags == FULL_SCREEN && argc == 0)
std::cerr << "Try running the program with the -windowed option "
<< "using a 16bpp X windows setting\n";
if(video_flags == 0 && argc == 0)
std::cerr << "Try running with the -fullscreen option\n";
return 0;
}
if(bpp != 16) {
std::cerr << "Video mode must be emulated; the game may run slowly. "
<< "For best results, run the program on a 16 bpp display\n";
}
std::cerr << "setting mode to " << resolution.first << "x" << resolution.second << "\n";
const int res = video.setMode(resolution.first,resolution.second,
16,video_flags);
if(res != 16) {
std::cerr << "required video mode, " << resolution.first << "x"
<< resolution.second << "x16 is not supported\n";
return 0;
}
SDL_WM_SetCaption(string_table["game_title"].c_str(), NULL);
for(;;) {
sound::play_music("wesnoth-1.ogg");
game_state state;
display::unit_map u_map;
config dummy_cfg("");
display disp(u_map,video,gamemap(dummy_cfg,"1"),gamestatus(0),
std::vector<team>());
if(test_mode) {
state.campaign_type = "test";
state.scenario = 0;
play_game(disp,state,game_config,units_data,video);
return 0;
}
gui::TITLE_RESULT res = gui::show_title(disp);
if(res == gui::QUIT_GAME) {
return 0;
} else if(res == gui::LOAD_GAME) {
srand(SDL_GetTicks());
const std::vector<std::string>& games = get_saves_list();
if(games.empty()) {
gui::show_dialog(disp,NULL,
string_table["no_saves_heading"],
string_table["no_saves_message"],
gui::OK_ONLY);
continue;
}
const int res = gui::show_dialog(disp,NULL,
string_table["load_game_heading"],
string_table["load_game_message"],
gui::OK_CANCEL, &games);
if(res == -1)
continue;
try {
load_game(units_data,games[res],state);
defines_map.clear();
defines_map[state.difficulty] = "";
} catch(gamestatus::load_game_failed& e) {
gui::show_dialog(disp,NULL,string_table["bad_save_heading"],
string_table["bad_save_message"],gui::OK_ONLY);
continue;
}
recorder = replay(state.replay_data);
if(!recorder.empty()) {
const int res = gui::show_dialog(disp,NULL,
"", string_table["replay_game_message"],
gui::YES_NO);
//if yes, then show the replay, otherwise
//skip showing the replay
if(res == 0) {
recorder.set_skip(0);
} else {
std::cerr << "skipping...\n";
recorder.set_skip(-1);
}
}
if(state.campaign_type == "multiplayer") {
recorder.set_save_info(state);
std::vector<config*> story;
try {
play_level(units_data,game_config,&state.starting_pos,
video,state,story);
recorder.clear();
} catch(gamestatus::load_game_failed& e) {
std::cerr << "error loading the game: " << e.message
<< "\n";
return 0;
} catch(gamestatus::game_error& e) {
std::cerr << "error while playing the game: "
<< e.message << "\n";
return 0;
}
continue;
}
} else if(res == gui::TUTORIAL) {
state.campaign_type = "tutorial";
state.scenario = 0;
} else if(res == gui::NEW_CAMPAIGN) {
state.campaign_type = "scenario";
state.scenario = 0;
static const std::string difficulties[] = {"EASY","NORMAL","HARD"};
const int ndiff = sizeof(difficulties)/sizeof(*difficulties);
std::vector<std::string> options;
for(int i = 0; i != ndiff; ++i) {
options.push_back(string_table[difficulties[i]]);
if(options.back().empty())
options.back() = difficulties[i];
}
const int res = gui::show_dialog(disp,NULL,"",
string_table["difficulty_level"],
gui::OK_CANCEL,&options);
if(res == -1)
continue;
assert(res >= 0 && res < options.size());
state.difficulty = difficulties[res];
defines_map.clear();
defines_map[difficulties[res]] = "";
} else if(res == gui::MULTIPLAYER) {
state.campaign_type = "multiplayer";
state.scenario = 0;
try {
play_multiplayer(disp,units_data,game_config,state);
} catch(gamestatus::load_game_failed& e) {
std::cerr << "error loading the game: " << e.message
<< "\n";
return 0;
} catch(gamestatus::game_error& e) {
std::cerr << "error while playing the game: "
<< e.message << "\n";
return 0;
}
continue;
} else if(res == gui::CHANGE_LANGUAGE) {
const std::vector<std::string>& langs = get_languages(game_config);
const int res = gui::show_dialog(disp,NULL,"",
string_table["language_button"] + ":",
gui::OK_CANCEL,&langs);
if(res >= 0 && res < langs.size()) {
set_language(langs[res], game_config);
preferences::set_locale(langs[res]);
}
continue;
}
//make a new game config item based on the difficulty level
config game_config(preprocess_file("data/game.cfg", &defines_map));
const std::vector<config*>& units = game_config.children["units"];
if(units.empty()) {
std::cerr << "Could not find units configuration\n";
std::cerr << game_config.write();
return 0;
}
game_data units_data(*units[0]);
const LEVEL_RESULT result = play_game(disp,state,game_config,
units_data,video);
if(result == VICTORY) {
gui::show_dialog(disp,NULL,
string_table["end_game_heading"],
string_table["end_game_message"],
gui::OK_ONLY);
}
}
return 0;
}
int main(int argc, char** argv)
{
try {
return play_game(argc,argv);
} catch(CVideo::error&) {
std::cerr << "Could not initialize video. Exiting.\n";
} catch(config::error& e) {
std::cerr << e.message << "\n";
} catch(gui::button::error&) {
std::cerr << "Could not create button: Image could not be found\n";
}
return 0;
}

View file

@ -1,27 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "game_config.hpp"
namespace game_config
{
const int unit_cost = 1;
const int base_income = 2;
const int tower_income = 2;
const int heal_amount = 4;
const int healer_heals_per_turn = 8;
const int cure_amount = 8;
const int curer_heals_per_turn = 18;
const int recall_cost = 20;
const std::string version = "0.4.4";
bool debug = false;
}

View file

@ -1,33 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef GAME_CONFIG_H_INCLUDED
#define GAME_CONFIG_H_INCLUDED
#include <string>
namespace game_config
{
extern const int unit_cost;
extern const int base_income;
extern const int tower_income;
extern const int heal_amount;
extern const int healer_heals_per_turn;
extern const int cure_amount;
extern const int curer_heals_per_turn;
extern const int recall_cost;
extern const std::string version;
extern bool debug;
}
#endif

View file

@ -1,740 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "game_events.hpp"
#include "menu.hpp"
#include "language.hpp"
#include "playlevel.hpp"
#include "replay.hpp"
#include <cstdlib>
#include <deque>
#include <iostream>
#include <set>
#include <string>
namespace game_events {
bool conditional_passed(game_state& state_of_game,
const std::map<gamemap::location,unit>* units,
config& cond)
{
//if the if statement requires we have a certain unit, then
//check for that.
std::vector<config*>& have_unit = cond.children["have_unit"];
for(std::vector<config*>::iterator u = have_unit.begin();
u != have_unit.end(); ++u) {
if(units == NULL)
return false;
std::map<gamemap::location,unit>::const_iterator itor;
for(itor = units->begin(); itor != units->end(); ++itor) {
if(itor->second.matches_filter(**u)) {
break;
}
}
if(itor == units->end()) {
return false;
}
}
//check against each variable statement to see if the variable
//matches the conditions or not
std::vector<config*>& variables = cond.children["variable"];
for(std::vector<config*>::iterator var = variables.begin();
var != variables.end(); ++var) {
std::map<std::string,std::string>& values = (*var)->values;
std::map<std::string,std::string>& vars = state_of_game.variables;
const std::string& name = values["name"];
//if we don't have a record of the variable, then the statement
//is not true, unless it's a not equals statement, in which it's
//automatically true
if(vars.find(name) == vars.end()) {
if(values.find("not_equals") == values.end() &&
values.find("numerical_not_equals") == values.end()) {
return false;
}
continue;
}
const std::string& value = vars[name];
const double num_value = atof(value.c_str());
std::map<std::string,std::string>::iterator itor;
itor = values.find("equals");
if(itor != values.end() && itor->second != value) {
return false;
}
itor = values.find("numerical_equals");
if(itor != values.end() && atof(itor->second.c_str()) != num_value){
return false;
}
itor = values.find("not_equals");
if(itor != values.end() && itor->second == value) {
return false;
}
itor = values.find("numerical_not_equals");
if(itor != values.end() && atof(itor->second.c_str()) == num_value){
return false;
}
itor = values.find("greater_than");
if(itor != values.end() && atof(itor->second.c_str()) >= num_value){
return false;
}
itor = values.find("less_than");
if(itor != values.end() && atof(itor->second.c_str()) <= num_value){
return false;
}
itor = values.find("greater_than_equal_to");
if(itor != values.end() && atof(itor->second.c_str()) > num_value){
return false;
}
itor = values.find("less_than_equal_to");
if(itor != values.end() && atof(itor->second.c_str()) < num_value){
return false;
}
}
return true;
}
} //end namespace game_events
namespace {
display* screen = NULL;
gamemap* game_map = NULL;
std::map<gamemap::location,unit>* units = NULL;
game_state* state_of_game = NULL;
game_data* game_data_ptr = NULL;
bool events_init() { return screen != NULL; }
struct queued_event {
queued_event(const std::string& name, const gamemap::location& loc1,
const gamemap::location& loc2)
: name(name), loc1(loc1), loc2(loc2) {}
std::string name;
gamemap::location loc1;
gamemap::location loc2;
};
std::deque<queued_event> events_queue;
class event_handler
{
public:
event_handler(config* cfg) : name_(cfg->values["name"]),
first_time_only_(cfg->values["first_time_only"] != "no"),
disabled_(false),
cfg_(cfg)
{}
const std::string& name() const { return name_; }
bool first_time_only() const { return first_time_only_; }
void disable() { disabled_ = true; }
bool disabled() const { return disabled_; }
std::vector<config*>& first_arg_filters() {
return cfg_->children["filter"];
}
std::vector<config*>& second_arg_filters() {
return cfg_->children["filter_second"];
}
void handle_event(const queued_event& event_info, config* cfg=NULL);
private:
std::string name_;
bool first_time_only_;
bool disabled_;
config* cfg_;
};
std::multimap<std::string,event_handler> events_map;
void event_handler::handle_event(const queued_event& event_info, config* cfg)
{
if(cfg == NULL)
cfg = cfg_;
//sub commands that need to be handled in a guaranteed ordering
std::vector<config*>& commands = cfg->children["command"];
for(std::vector<config*>::iterator cmd = commands.begin();
cmd != commands.end(); ++cmd) {
handle_event(event_info,*cmd);
}
//setting a variable
std::vector<config*>& set_vars = cfg->children["set_variable"];
for(std::vector<config*>::iterator var = set_vars.begin();
var != set_vars.end(); ++var) {
std::map<std::string,std::string>& vals = (*var)->values;
const std::string& name = vals["name"];
const std::string& value = vals["value"];
if(value.empty() == false) {
state_of_game->variables[name] = value;
}
const std::string& add = vals["add"];
if(add.empty() == false) {
double value = atof(state_of_game->variables[name].c_str());
value += atof(add.c_str());
char buf[50];
sprintf(buf,"%f",value);
state_of_game->variables[name] = buf;
}
const std::string& multiply = vals["multiply"];
if(multiply.empty() == false) {
double value = atof(state_of_game->variables[name].c_str());
value *= atof(multiply.c_str());
char buf[50];
sprintf(buf,"%f",value);
state_of_game->variables[name] = buf;
}
}
//conditional statements
std::vector<config*>& conditionals = cfg->children["if"];
for(std::vector<config*>::iterator cond = conditionals.begin();
cond != conditionals.end(); ++cond) {
const std::string type = game_events::conditional_passed(
*state_of_game,units,**cond) ? "then":"else";
//if the if statement passed, then execute all 'then' statements,
//otherwise execute 'else' statements
std::vector<config*>& commands = cfg->children[type];
for(std::vector<config*>::iterator cmd = commands.begin();
cmd != commands.end(); ++cmd) {
handle_event(event_info,*cmd);
}
}
//if we are assigning a role to a unit from the available units list
std::vector<config*>& assign_role = cfg->children["role"];
for(std::vector<config*>::iterator rl = assign_role.begin();
rl != assign_role.end(); ++rl) {
//get a list of the types this unit can be
std::vector<std::string> types = config::split((*rl)->values["type"]);
//iterate over all the types, and for each type, try to find
//a unit that matches
std::vector<std::string>::iterator ti;
for(ti = types.begin(); ti != types.end(); ++ti) {
config cfg;
cfg.values["type"] = *ti;
std::map<gamemap::location,unit>::iterator itor;
for(itor = units->begin(); itor != units->end(); ++itor) {
if(itor->second.matches_filter(cfg)) {
itor->second.assign_role((*rl)->values["role"]);
break;
}
}
if(itor != units->end())
break;
std::vector<unit>::iterator ui;
//iterate over the units, and try to find one that matches
for(ui = state_of_game->available_units.begin();
ui != state_of_game->available_units.end(); ++ui) {
if(ui->matches_filter(cfg)) {
ui->assign_role((*rl)->values["role"]);
break;
}
}
//if we found a unit, we don't have to keep going.
if(ui != state_of_game->available_units.end())
break;
}
//if we found a unit in the current type, we don't have to
//look through any more types
if(ti != types.end())
break;
}
std::vector<config*>& remove_overlays = cfg->children["removeitem"];
for(std::vector<config*>::iterator rm = remove_overlays.begin();
rm != remove_overlays.end(); ++rm) {
gamemap::location loc(**rm);
if(!loc.valid()) {
loc = event_info.loc1;
}
screen->remove_overlay(loc);
}
//adding new items
std::vector<config*>& add_overlays = cfg->children["item"];
for(std::vector<config*>::iterator ni = add_overlays.begin();
ni != add_overlays.end(); ++ni) {
gamemap::location loc(**ni);
const std::string& img = (*ni)->values["image"];
if(!img.empty())
screen->add_overlay(loc,img);
}
//changing the terrain
std::vector<config*>& terrain_changes = cfg->children["terrain"];
for(std::vector<config*>::iterator tc = terrain_changes.begin();
tc != terrain_changes.end(); ++tc) {
gamemap::location loc(**tc);
const std::string& terrain_type = (*tc)->values["letter"];
if(terrain_type.size() > 0) {
game_map->set_terrain(loc,terrain_type[0]);
screen->recalculate_minimap();
screen->invalidate_all();
}
}
//if we should spawn a new unit on the map somewhere
std::vector<config*>& new_units = cfg->children["unit"];
for(std::vector<config*>::iterator ui = new_units.begin();
ui != new_units.end(); ++ui) {
unit new_unit(*game_data_ptr,**ui);
gamemap::location loc(**ui);
if(game_map->on_board(loc)) {
loc = find_vacant_tile(*game_map,*units,loc);
units->insert(std::pair<gamemap::location,unit>(loc,new_unit));
} else {
state_of_game->available_units.push_back(new_unit);
}
}
//if we should recall units that match a certain description
std::vector<config*>& recalls = cfg->children["recall"];
for(std::vector<config*>::iterator ir = recalls.begin();
ir != recalls.end(); ++ir) {
std::vector<unit>& avail = state_of_game->available_units;
for(std::vector<unit>::iterator u = avail.begin();
u != avail.end(); ++u) {
if(u->matches_filter(**ir)) {
recruit_unit(*game_map,1,*units,*u,gamemap::location(),screen);
u = avail.erase(u);
if(u == avail.end())
break;
}
}
}
std::vector<config*>& objects = cfg->children["object"];
for(std::vector<config*>::iterator obj = objects.begin();
obj != objects.end(); ++obj) {
std::map<std::string,std::string>& values = (*obj)->values;
//if this item has already been used
if(values["used"].empty() == false)
continue;
const std::string& id = values["id"];
const std::string image = values["image"];
std::string caption = values["name"];
const std::string& caption_lang = string_table[id + "_name"];
if(caption_lang.empty() == false)
caption = caption_lang;
const std::map<gamemap::location,unit>::iterator u =
units->find(event_info.loc1);
if(u == units->end())
continue;
std::string text;
std::vector<config*>& filters = (*obj)->children["filter"];
if(filters.empty() || u->second.matches_filter(*filters[0])) {
const std::string& lang = string_table[id];
if(!lang.empty())
text = lang;
else
text = values["description"];
u->second.add_modification("object",**obj);
screen->remove_overlay(event_info.loc1);
screen->select_hex(event_info.loc1);
screen->invalidate_unit();
//mark that this item won't be used again
values["used"] = "true";
} else {
const std::string& lang = string_table[id + "_cannot_use"];
if(!lang.empty())
text = lang;
else
text = values["cannot_use_message"];
}
SDL_Surface* surface = NULL;
if(image.empty() == false) {
surface = screen->getImage(image,display::UNSCALED);
}
gui::show_dialog(*screen,surface,caption,text);
//this will redraw the unit, with its new stats
screen->draw();
}
std::vector<config*>& messages = cfg->children["message"];
for(std::vector<config*>::iterator msg = messages.begin();
msg != messages.end(); ++msg) {
std::map<std::string,std::string>& values = (*msg)->values;
std::map<gamemap::location,unit>::iterator speaker = units->end();
if(values["speaker"] == "unit") {
speaker = units->find(event_info.loc1);
} else if(values["speaker"] == "second_unit") {
speaker = units->find(event_info.loc2);
} else if(values["speaker"] != "narrator") {
for(speaker = units->begin(); speaker != units->end();
++speaker){
if(speaker->second.matches_filter(**msg))
break;
}
if(speaker == units->end()) {
//no matching unit found, so the dialog can't come up
//continue onto the next message
continue;
}
}
const std::string& id = values["id"];
std::string image = (*msg)->values["image"];
std::string caption;
const std::string& lang_caption = string_table[id + "_caption"];
if(!lang_caption.empty())
caption = lang_caption;
else
caption = (*msg)->values["caption"];
if(speaker != units->end()) {
screen->highlight_hex(speaker->first);
screen->scroll_to_tile(speaker->first.x,speaker->first.y);
if(image.empty()) {
image = speaker->second.type().image_profile();
}
if(caption.empty()) {
caption = speaker->second.description();
if(caption.empty()) {
caption = speaker->second.type().language_name();
}
}
}
std::vector<std::string> options;
std::vector<std::vector<config*>*> option_events;
std::vector<config*>& menu_items = (*msg)->children["option"];
for(std::vector<config*>::iterator mi = menu_items.begin();
mi != menu_items.end(); ++mi) {
const std::string& lang_msg = string_table[(*mi)->values["id"]];
const std::string& msg = lang_msg.empty() ?
(*mi)->values["message"] : lang_msg;
options.push_back(msg);
option_events.push_back(&(*mi)->children["command"]);
}
SDL_Surface* surface = NULL;
if(image.empty() == false) {
surface = screen->getImage(image,display::UNSCALED);
}
const std::string& lang_message = string_table[id];
int option_chosen = gui::show_dialog(*screen,surface,caption,
lang_message.empty() ? values["message"] : lang_message,
options.empty() ? gui::MESSAGE : gui::OK_ONLY,
options.empty() ? NULL : &options);
if(screen->update_locked() && options.empty() == false) {
config* const cfg = recorder.get_next_action();
if(cfg == NULL || cfg->children["choose"].empty()) {
std::cerr << "choice expected but none found\n";
throw replay::error();
}
const std::string& val =
cfg->children["choose"].front()->values["value"];
option_chosen = atol(val.c_str());
} else if(options.empty() == false) {
recorder.choose_option(option_chosen);
}
if(options.empty() == false) {
assert(option_chosen >= 0 && option_chosen < menu_items.size());
std::vector<config*>& events = *option_events[option_chosen];
for(std::vector<config*>::iterator ev = events.begin();
ev != events.end(); ++ev) {
handle_event(event_info,*ev);
}
}
}
std::vector<config*>& dead_units = cfg->children["kill"];
for(std::vector<config*>::iterator du = dead_units.begin();
du != dead_units.end(); ++du) {
for(std::map<gamemap::location,unit>::iterator i = units->begin();
i != units->end(); ++i) {
while(i->second.matches_filter(**du) && i != units->end()) {
units->erase(i);
i = units->begin();
}
}
std::vector<unit>& avail_units = state_of_game->available_units;
for(std::vector<unit>::iterator j = avail_units.begin();
j != avail_units.end(); ++j) {
while(j->matches_filter(**du) && j != avail_units.end()) {
j = avail_units.erase(j);
}
}
}
//adding of new events
std::vector<config*>& new_events = cfg->children["event"];
for(std::vector<config*>::iterator ne = new_events.begin();
ne != new_events.end(); ++ne) {
event_handler new_handler(*ne);
events_map.insert(std::pair<std::string,event_handler>(
new_handler.name(),new_handler));
}
std::vector<config*>& end_level = cfg->children["endlevel"];
if(end_level.empty() == false) {
config* const end_info = end_level[0];
const std::string& result = end_info->values["result"];
if(result.size() == 0 || result == "victory") {
const bool bonus = (end_info->values["bonus"] == "yes");
throw end_level_exception(VICTORY,bonus);
} else {
throw end_level_exception(DEFEAT);
}
}
}
bool filter_loc_impl(const gamemap::location& loc, const std::string& xloc,
const std::string& yloc)
{
if(std::find(xloc.begin(),xloc.end(),',') != xloc.end()) {
std::vector<std::string> xlocs = config::split(xloc);
std::vector<std::string> ylocs = config::split(yloc);
const int size = xlocs.size() < ylocs.size()?xlocs.size():ylocs.size();
for(int i = 0; i != size; ++i) {
if(filter_loc_impl(loc,xlocs[i],ylocs[i]))
return true;
}
return false;
}
if(!xloc.empty()) {
const std::string::const_iterator dash =
std::find(xloc.begin(),xloc.end(),'-');
if(dash != xloc.end()) {
const std::string beg(xloc.begin(),dash);
const std::string end(dash+1,xloc.end());
const int bot = atoi(beg.c_str()) - 1;
const int top = atoi(end.c_str()) - 1;
if(loc.x < bot || loc.x > top)
return false;
} else {
const int xval = atoi(xloc.c_str()) - 1;
if(xval != loc.x)
return false;
}
}
if(!yloc.empty()) {
const std::string::const_iterator dash =
std::find(yloc.begin(),yloc.end(),'-');
if(dash != yloc.end()) {
const std::string beg(yloc.begin(),dash);
const std::string end(dash+1,yloc.end());
const int bot = atoi(beg.c_str()) - 1;
const int top = atoi(end.c_str()) - 1;
if(loc.y < bot || loc.y > top)
return false;
} else {
const int yval = atoi(yloc.c_str()) - 1;
if(yval != loc.y)
return false;
}
}
return true;
}
bool filter_loc(const gamemap::location& loc, config& cfg)
{
const std::string& xloc = cfg.values["x"];
const std::string& yloc = cfg.values["y"];
return filter_loc_impl(loc,xloc,yloc);
}
bool process_event(event_handler& handler, const queued_event& ev)
{
if(handler.disabled())
return false;
std::map<gamemap::location,unit>::iterator unit1 = units->find(ev.loc1);
std::map<gamemap::location,unit>::iterator unit2 = units->find(ev.loc2);
std::vector<config*>& first_filters = handler.first_arg_filters();
for(std::vector<config*>::iterator ffi = first_filters.begin();
ffi != first_filters.end(); ++ffi) {
if(!filter_loc(ev.loc1,**ffi))
return false;
if(unit1 != units->end() && !unit1->second.matches_filter(**ffi)) {
return false;
}
}
std::vector<config*>& second_filters = handler.second_arg_filters();
for(std::vector<config*>::iterator sfi = second_filters.begin();
sfi != second_filters.end(); ++sfi) {
if(!filter_loc(ev.loc2,**sfi))
return false;
if(unit2 != units->end() && !unit2->second.matches_filter(**sfi)) {
return false;
}
}
//the event hasn't been filtered out, so execute the handler
handler.handle_event(ev);
if(handler.first_time_only())
handler.disable();
return true;
}
} //end anonymous namespace
namespace game_events {
manager::manager(config& cfg, display& gui_, gamemap& map_,
std::map<gamemap::location,unit>& units_,
game_state& state_of_game_, game_data& game_data_)
{
std::vector<config*>& events_list = cfg.children["event"];
for(std::vector<config*>::iterator i = events_list.begin();
i != events_list.end(); ++i) {
event_handler new_handler(*i);
events_map.insert(std::pair<std::string,event_handler>(
new_handler.name(), new_handler));
}
screen = &gui_;
game_map = &map_;
units = &units_;
state_of_game = &state_of_game_;
game_data_ptr = &game_data_;
}
manager::~manager() {
events_queue.clear();
events_map.clear();
screen = NULL;
game_map = NULL;
units = NULL;
state_of_game = NULL;
game_data_ptr = NULL;
}
void raise(const std::string& event,
const gamemap::location& loc1,
const gamemap::location& loc2)
{
if(!events_init())
return;
events_queue.push_back(queued_event(event,loc1,loc2));
}
bool fire(const std::string& event,
const gamemap::location& loc1,
const gamemap::location& loc2)
{
raise(event,loc1,loc2);
return pump();
}
bool pump()
{
if(!events_init())
return false;
bool result = false;
while(events_queue.empty() == false) {
queued_event ev = events_queue.front();
events_queue.pop_front(); //pop now for exception safety
const std::string& event_name = ev.name;
typedef std::multimap<std::string,event_handler>::iterator itor;
//find all handlers for this event in the map
std::pair<itor,itor> i = events_map.equal_range(event_name);
while(i.first != i.second) {
event_handler& handler = i.first->second;
if(process_event(handler, ev))
result = true;
++i.first;
}
}
return result;
}
} //end namespace game_events

View file

@ -1,51 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef GAME_EVENTS_H_INCLUDED
#define GAME_EVENTS_H_INCLUDED
#include "config.hpp"
#include "display.hpp"
#include "gamestatus.hpp"
#include "map.hpp"
#include "unit.hpp"
#include "unit_types.hpp"
#include <map>
namespace game_events
{
bool conditional_passed(game_state& state_of_game,
const std::map<gamemap::location,unit>* units,
config& cond);
struct manager {
manager(config& cfg, display& disp, gamemap& map,
std::map<gamemap::location,unit>& units,
game_state& state_of_game, game_data& data);
~manager();
};
void raise(const std::string& event,
const gamemap::location& loc1=gamemap::location::null_location,
const gamemap::location& loc2=gamemap::location::null_location);
bool fire(const std::string& event,
const gamemap::location& loc1=gamemap::location::null_location,
const gamemap::location& loc2=gamemap::location::null_location);
bool pump();
}
#endif

View file

@ -1,146 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "filesystem.hpp"
#include "gamestatus.hpp"
#include <cstdio>
#include <sstream>
gamestatus::gamestatus(int num_turns) :
timeofday_(DAWN),turn_(1),numTurns_(num_turns)
{}
const std::string& gamestatus::timeofdayDescription(gamestatus::TIME t)
{
static const std::string times[] = { "Dawn", "Morning", "Afternoon",
"Dusk", "FirstWatch", "SecondWatch" };
return times[t];
}
gamestatus::TIME gamestatus::timeofday() const
{
return timeofday_;
}
int gamestatus::turn() const
{
return turn_;
}
int gamestatus::number_of_turns() const
{
return numTurns_;
}
bool gamestatus::next_turn()
{
timeofday_ = static_cast<TIME>(static_cast<int>(timeofday_)+1);
if(timeofday_ == NUM_TIMES)
timeofday_ = DAWN;
++turn_;
return turn_ <= numTurns_;
}
game_state read_game(game_data& data, config* cfg)
{
game_state res;
res.label = cfg->values["label"];
res.gold = atoi(cfg->values["gold"].c_str());
res.scenario = atoi(cfg->values["scenario"].c_str());
res.difficulty = cfg->values["difficulty"];
if(res.difficulty.empty())
res.difficulty = "NORMAL";
res.campaign_type = cfg->values["campaign_type"];
if(res.campaign_type.empty())
res.campaign_type = "scenario";
std::vector<config*>& units = cfg->children["unit"];
for(std::vector<config*>::iterator i = units.begin();
i != units.end(); ++i) {
res.available_units.push_back(unit(data,**i));
}
std::vector<config*>& vars = cfg->children["variables"];
if(vars.empty() == false) {
res.variables = vars[0]->values;
}
std::vector<config*>& replays = cfg->children["replay"];
if(replays.empty() == false) {
res.replay_data = *replays[0];
}
std::vector<config*>& starts = cfg->children["start"];
if(starts.empty() == false) {
res.starting_pos = *starts[0];
}
return res;
}
void write_game(const game_state& game, config& cfg)
{
cfg.values["label"] = game.label;
char buf[50];
sprintf(buf,"%d",game.gold);
cfg.values["gold"] = buf;
sprintf(buf,"%d",game.scenario);
cfg.values["scenario"] = buf;
cfg.values["campaign_type"] = game.campaign_type;
cfg.values["difficulty"] = game.difficulty;
config* vars = new config();
vars->values = game.variables;
cfg.children["variables"].push_back(vars);
for(std::vector<unit>::const_iterator i = game.available_units.begin();
i != game.available_units.end(); ++i) {
config* const new_cfg = new config;
i->write(*new_cfg);
cfg.children["unit"].push_back(new_cfg);
}
if(game.replay_data.children.empty() == false) {
cfg.children["replay"].push_back(new config(game.replay_data));
}
cfg.children["start"].push_back(new config(game.starting_pos));
}
std::vector<std::string> get_saves_list()
{
std::vector<std::string> res;
get_files_in_dir(get_saves_dir(),&res);
return res;
}
void load_game(game_data& data, const std::string& name, game_state& state)
{
config cfg(read_file(get_saves_dir() + "/" + name));
state = read_game(data,&cfg);
}
void save_game(const game_state& state)
{
const std::string& name = state.label;
config cfg;
write_game(state,cfg);
write_file(get_saves_dir() + "/" + name,cfg.write());
}

View file

@ -1,73 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef GAME_STATUS_HPP_INCLUDED
#define GAME_STATUS_HPP_INCLUDED
#include "unit.hpp"
#include "unit_types.hpp"
#include <string>
#include <vector>
class gamestatus
{
public:
gamestatus(int num_turns);
enum TIME { DAWN, DAY1, DAY2, DUSK, NIGHT1, NIGHT2, NUM_TIMES };
static const std::string& timeofdayDescription(TIME t);
TIME timeofday() const;
int turn() const;
int number_of_turns() const;
bool next_turn();
struct load_game_failed {
load_game_failed() {}
load_game_failed(const std::string& msg) : message(msg) {}
std::string message;
};
struct game_error {
game_error(const std::string& msg) : message(msg) {}
std::string message;
};
private:
TIME timeofday_;
int turn_;
int numTurns_;
};
struct game_state
{
game_state() : gold(-1), difficulty("NORMAL") {}
std::string label;
std::string campaign_type;
int scenario;
int gold;
std::vector<unit> available_units;
std::map<std::string,std::string> variables;
std::string difficulty;
config replay_data;
config starting_pos;
};
std::vector<std::string> get_saves_list();
void load_game(game_data& data, const std::string& name, game_state& state);
void save_game(const game_state& state);
#endif

View file

@ -1,183 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "config.hpp"
#include "hotkeys.hpp"
#include "language.hpp"
#include "menu.hpp"
#include "playlevel.hpp"
#include "preferences.hpp"
#include <algorithm>
#include <cstdlib>
#include <map>
namespace {
HOTKEY_COMMAND string_to_command(const std::string& str)
{
static std::map<std::string,HOTKEY_COMMAND> m;
if(m.empty()) {
typedef std::pair<std::string,HOTKEY_COMMAND> val;
m.insert(val("cycle",HOTKEY_CYCLE_UNITS));
m.insert(val("endunitturn",HOTKEY_END_UNIT_TURN));
m.insert(val("leader",HOTKEY_LEADER));
m.insert(val("undo",HOTKEY_UNDO));
m.insert(val("redo",HOTKEY_REDO));
m.insert(val("zoomin",HOTKEY_ZOOM_IN));
m.insert(val("zoomout",HOTKEY_ZOOM_OUT));
m.insert(val("zoomdefault",HOTKEY_ZOOM_DEFAULT));
m.insert(val("fullscreen",HOTKEY_FULLSCREEN));
m.insert(val("accelerated",HOTKEY_ACCELERATED));
m.insert(val("resistance",HOTKEY_ATTACK_RESISTANCE));
m.insert(val("terraintable",HOTKEY_TERRAIN_TABLE));
}
const std::map<std::string,HOTKEY_COMMAND>::const_iterator i = m.find(str);
if(i == m.end())
return HOTKEY_NULL;
else
return i->second;
}
struct hotkey {
explicit hotkey(config& cfg);
HOTKEY_COMMAND action;
int keycode;
bool alt, ctrl, shift;
mutable bool lastres;
};
hotkey::hotkey(config& cfg) : lastres(false)
{
std::map<std::string,std::string>& m = cfg.values;
action = string_to_command(m["command"]);
keycode = m["key"].empty() ? 0 : m["key"][0];
alt = (m["alt"] == "yes");
ctrl = (m["ctrl"] == "yes");
shift = (m["shift"] == "yes");
}
bool operator==(const hotkey& a, const hotkey& b)
{
return a.keycode == b.keycode && a.alt == b.alt &&
a.ctrl == b.ctrl && a.shift == b.shift;
}
bool operator!=(const hotkey& a, const hotkey& b)
{
return !(a == b);
}
std::vector<hotkey> hotkeys;
}
struct hotkey_pressed {
hotkey_pressed(CKey& key);
bool operator()(const hotkey& hk) const;
private:
const bool shift_, ctrl_, alt_;
CKey& key_;
};
hotkey_pressed::hotkey_pressed(CKey& key) :
shift_(key[SDLK_LSHIFT] || key[SDLK_RSHIFT]),
ctrl_(key[SDLK_LCTRL] || key[SDLK_RCTRL]),
alt_(key[SDLK_LALT] || key[SDLK_RALT]), key_(key) {}
bool hotkey_pressed::operator()(const hotkey& hk) const
{
const bool res = shift_ == hk.shift && ctrl_ == hk.ctrl &&
alt_ == hk.alt && key_[hk.keycode];
//for zoom in and zoom out, allow it to happen multiple consecutive times
if(hk.action == HOTKEY_ZOOM_IN || hk.action == HOTKEY_ZOOM_OUT) {
hk.lastres = false;
return res;
}
//don't let it return true on multiple consecutive occurrences
if(hk.lastres) {
hk.lastres = res;
return false;
} else {
hk.lastres = res;
return res;
}
}
void add_hotkey(config& cfg)
{
const hotkey new_hotkey(cfg);
const std::vector<hotkey>::iterator i =
std::find(hotkeys.begin(),hotkeys.end(),new_hotkey);
if(i != hotkeys.end()) {
*i = new_hotkey;
} else {
hotkeys.push_back(new_hotkey);
}
}
void add_hotkeys(config& cfg)
{
std::vector<config*>& children = cfg.children["hotkey"];
for(std::vector<config*>::iterator i = children.begin();
i != children.end(); ++i) {
add_hotkey(**i);
}
}
HOTKEY_COMMAND check_keys(display& disp)
{
const double zoom_amount = 5.0;
CKey key;
if(key[KEY_ESCAPE]) {
const int res = gui::show_dialog(disp,NULL,"",
string_table["quit_message"],gui::YES_NO);
if(res == 0) {
throw end_level_exception(QUIT);
}
}
const std::vector<hotkey>::iterator i =
std::find_if(hotkeys.begin(),hotkeys.end(),hotkey_pressed(key));
if(i == hotkeys.end())
return HOTKEY_NULL;
switch(i->action) {
case HOTKEY_ZOOM_IN:
disp.zoom(zoom_amount);
break;
case HOTKEY_ZOOM_OUT:
disp.zoom(-zoom_amount);
break;
case HOTKEY_ZOOM_DEFAULT:
disp.default_zoom();
break;
case HOTKEY_FULLSCREEN:
preferences::set_fullscreen(!preferences::fullscreen());
break;
case HOTKEY_ACCELERATED:
preferences::set_turbo(!preferences::turbo());
break;
default:
return i->action;
}
return HOTKEY_NULL;
}

View file

@ -1,31 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef HOTKEYS_HPP_INCLUDED
#define HOTKEYS_HPP_INCLUDED
#include "config.hpp"
#include "display.hpp"
#include "key.hpp"
enum HOTKEY_COMMAND { HOTKEY_CYCLE_UNITS, HOTKEY_END_UNIT_TURN, HOTKEY_LEADER,
HOTKEY_UNDO, HOTKEY_REDO,
HOTKEY_ZOOM_IN, HOTKEY_ZOOM_OUT, HOTKEY_ZOOM_DEFAULT,
HOTKEY_FULLSCREEN, HOTKEY_ACCELERATED,
HOTKEY_TERRAIN_TABLE, HOTKEY_ATTACK_RESISTANCE,
HOTKEY_NULL };
void add_hotkeys(config& cfg);
HOTKEY_COMMAND check_keys(display& disp);
#endif

260
intro.cpp
View file

@ -1,260 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "font.hpp"
#include "intro.hpp"
#include "key.hpp"
#include "language.hpp"
#include "menu.hpp"
#include "widgets/button.hpp"
#include <cstdlib>
#include <sstream>
#include <vector>
void show_intro(display& screen, config& data)
{
CKey key;
gui::button next_button(screen,string_table["next_button"] + ">>>");
gui::button skip_button(screen,string_table["skip_button"]);
next_button.set_x(700);
next_button.set_y(500);
skip_button.set_x(700);
skip_button.set_y(550);
std::vector<config*>& parts = data.children["part"];
for(std::vector<config*>::iterator i = parts.begin(); i != parts.end();++i){
gui::draw_solid_tinted_rectangle(0,0,screen.x()-1,screen.y()-1,
0,0,0,1.0,screen.video().getSurface());
const std::string& image_name = (*i)->values["image"];
SDL_Surface* image = NULL;
if(image_name.empty() == false) {
image = screen.getImage(image_name,display::UNSCALED);
}
int textx = 200;
int texty = 400;
if(image != NULL) {
SDL_Rect dstrect;
dstrect.x = screen.x()/2 - image->w/2;
dstrect.y = screen.y()/2 - image->h/2;
dstrect.w = image->w;
dstrect.h = image->h;
SDL_BlitSurface(image,NULL,screen.video().getSurface(),&dstrect);
textx = dstrect.x;
texty = dstrect.y + dstrect.h + 10;
next_button.set_x(dstrect.x+dstrect.w);
next_button.set_y(dstrect.y+dstrect.h+20);
skip_button.set_x(dstrect.x+dstrect.w);
skip_button.set_y(dstrect.y+dstrect.h+70);
}
const std::string& id = (*i)->values["id"];
const std::string& lang_story = string_table[id];
const std::string& story = lang_story.empty() ? (*i)->values["story"] :
lang_story;
const int max_length = 60;
std::stringstream stream;
int cur_length = 0;
for(std::string::const_iterator j = story.begin(); j!=story.end();++j){
char c = *j;
if(c == ' ' && cur_length >= max_length)
c = '\n';
if(c == '\n') {
cur_length = 0;
} else {
++cur_length;
}
stream << c;
}
static const SDL_Rect area = {0,0,screen.x(),screen.y()};
font::draw_text(&screen,area,16,font::NORMAL_COLOUR,stream.str(),
textx,texty);
next_button.draw();
skip_button.draw();
screen.video().update(0,0,screen.x(),screen.y());
const std::string& delay = (*i)->values["delay"];
const int ndelay = atoi(delay.c_str());
bool last = true;
for(int d = 0; d < ndelay*10; d += 50) {
SDL_Delay(10);
int mousex, mousey;
const int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
const bool right_button = mouse_flags&SDL_BUTTON_RMASK;
const bool left_button = mouse_flags&SDL_BUTTON_LMASK;
if(key[KEY_ESCAPE] ||
skip_button.process(mousex,mousey,left_button))
return;
if(key[KEY_SPACE] || key[KEY_ENTER] ||
next_button.process(mousex,mousey,left_button)) {
if(!last)
break;
} else {
last = false;
}
}
}
gui::draw_solid_tinted_rectangle(0,0,screen.x()-1,screen.y()-1,0,0,0,1.0,
screen.video().getSurface());
}
void show_map_scene(display& screen, config& data)
{
//clear the screen
gui::draw_solid_tinted_rectangle(0,0,screen.x()-1,screen.y()-1,0,0,0,1.0,
screen.video().getSurface());
std::vector<config*>& sequence = data.children["bigmap"];
if(sequence.empty()) {
return;
}
config& cfg = *sequence[0];
std::vector<config*>& dots = cfg.children["dot"];
const std::string& image_file = cfg.values["image"];
SDL_Surface* const image = screen.getImage(image_file,display::UNSCALED);
SDL_Surface* const dot_image =
screen.getImage("misc/dot.png",display::UNSCALED);
SDL_Surface* const cross_image =
screen.getImage("misc/cross.png",display::UNSCALED);
if(image == NULL || dot_image == NULL || cross_image == NULL) {
return;
}
SDL_Rect dstrect;
dstrect.x = screen.x()/2 - image->w/2;
dstrect.y = screen.y()/2 - image->h/2;
dstrect.w = image->w;
dstrect.h = image->h;
SDL_BlitSurface(image,NULL,screen.video().getSurface(),&dstrect);
const std::string& id = data.values["id"];
const std::string& scenario_name = string_table[id];
const std::string& scenario = scenario_name.empty() ? data.values["name"] :
scenario_name;
screen.video().update(0,0,screen.x(),screen.y());
CKey key;
for(std::vector<config*>::iterator d = dots.begin(); d != dots.end(); ++d){
const std::string& xloc = (*d)->values["x"];
const std::string& yloc = (*d)->values["y"];
const int x = atoi(xloc.c_str());
const int y = atoi(yloc.c_str());
if(x < 0 || x >= image->w || y < 0 || y >= image->w)
continue;
SDL_Surface* img = dot_image;
if((*d)->values["type"] == "cross") {
img = cross_image;
}
int xdot = x - img->w/2;
int ydot = y - img->h/2;
if(xdot < 0)
xdot = 0;
if(ydot < 0)
ydot = 0;
SDL_Rect dot_rect;
dot_rect.x = xdot;
dot_rect.y = ydot;
dot_rect.w = img->w;
dot_rect.h = img->h;
SDL_BlitSurface(img,NULL,image,&dot_rect);
SDL_BlitSurface(image,NULL,screen.video().getSurface(),&dstrect);
for(int i = 0; i != 10; ++i) {
if(key[KEY_ESCAPE]) {
break;
}
SDL_Delay(50);
int a, b;
const int mouse_flags = SDL_GetMouseState(&a,&b);
if(key[KEY_ENTER] || key[KEY_SPACE] || mouse_flags) {
break;
}
}
if(key[KEY_ESCAPE]) {
break;
}
screen.video().update(0,0,screen.x(),screen.y());
}
if(!key[KEY_ESCAPE]) {
SDL_Delay(500);
}
static const SDL_Rect area = {0,0,screen.x(),screen.y()};
const SDL_Rect scenario_size =
font::draw_text(NULL,area,24,font::NORMAL_COLOUR,scenario,0,0);
font::draw_text(&screen,area,24,font::NORMAL_COLOUR,scenario,
dstrect.x,dstrect.y - scenario_size.h - 4);
SDL_BlitSurface(image,NULL,screen.video().getSurface(),&dstrect);
screen.video().update(0,0,screen.x(),screen.y());
bool last_state = true;
for(;;) {
int a, b;
const int mouse_flags = SDL_GetMouseState(&a,&b);
const bool new_state = mouse_flags || key[KEY_ESCAPE] ||
key[KEY_ENTER] || key[KEY_SPACE];
if(new_state && !last_state)
break;
last_state = new_state;
SDL_Delay(20);
SDL_PumpEvents();
}
//clear the screen
gui::draw_solid_tinted_rectangle(0,0,screen.x()-1,screen.y()-1,0,0,0,1.0,
screen.video().getSurface());
}

View file

@ -1,26 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef INTRO_HPP_INCLUDED
#define INTRO_HPP_INCLUDED
#include "SDL.h"
#include "config.hpp"
#include "display.hpp"
#include <string>
void show_intro(display& screen, config& data);
void show_map_scene(display& screen, config& data);
#endif

52
key.cpp
View file

@ -1,52 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "key.hpp"
#define KEY_TEST 0
#if (KEY_TEST == 1)
#include "video.hpp"
int main( void )
{
SDL_Init(SDL_INIT_VIDEO);
CVideo video( 640, 480, 16, 0 );
CKey key;
printf( "press enter (escape exits)...\n" );
for(;;) {
if( key[KEY_RETURN] != 0 )
printf( "key(ENTER) pressed\n" );
if( key[KEY_ESCAPE] != 0 )
return 1;
}
}
#endif
CKey::CKey() : is_enabled(true)
{
static int num_keys = 300;
key_list = SDL_GetKeyState( &num_keys );
}
int CKey::operator[]( int code )
{
SDL_PumpEvents();
return (code == KEY_ESCAPE || is_enabled) && int(key_list[code]);
}
void CKey::SetEnabled( bool enable )
{
is_enabled = enable;
}

96
key.hpp
View file

@ -1,96 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef KEY_HPP_INCLUDED
#define KEY_HPP_INCLUDED
#include "SDL.h"
class CKey {
public:
CKey();
int operator[](int);
void SetEnabled(bool enable);
private:
Uint8 *key_list;
bool is_enabled;
};
#define KEY_PAGEUP SDLK_PAGEUP
#define KEY_PAGEDOWN SDLK_PAGEDOWN
#define KEY_BACKSPACE SDLK_BACKSPACE
#define KEY_DELETE SDLK_DELETE
#define KEY_TAB SDLK_TAB
#define KEY_CLEAR SDLK_CLEAR
#define KEY_RETURN SDLK_RETURN
#define KEY_ENTER KEY_RETURN
#define KEY_ESCAPE SDLK_ESCAPE
#define KEY_SPACE SDLK_SPACE
#define KEY_0 SDLK_0
#define KEY_1 SDLK_1
#define KEY_2 SDLK_2
#define KEY_3 SDLK_3
#define KEY_4 SDLK_4
#define KEY_5 SDLK_5
#define KEY_6 SDLK_6
#define KEY_7 SDLK_7
#define KEY_8 SDLK_8
#define KEY_9 SDLK_9
#define KEY_KP0 SDLK_KP0
#define KEY_KP1 SDLK_KP1
#define KEY_KP2 SDLK_KP2
#define KEY_KP3 SDLK_KP3
#define KEY_KP4 SDLK_KP4
#define KEY_KP5 SDLK_KP5
#define KEY_KP6 SDLK_KP6
#define KEY_KP7 SDLK_KP7
#define KEY_KP8 SDLK_KP8
#define KEY_KP9 SDLK_KP9
#define KEY_A SDLK_a
#define KEY_B SDLK_b
#define KEY_C SDLK_c
#define KEY_D SDLK_d
#define KEY_E SDLK_e
#define KEY_F SDLK_f
#define KEY_G SDLK_g
#define KEY_H SDLK_h
#define KEY_I SDLK_i
#define KEY_J SDLK_j
#define KEY_K SDLK_k
#define KEY_L SDLK_l
#define KEY_M SDLK_m
#define KEY_N SDLK_n
#define KEY_O SDLK_o
#define KEY_P SDLK_p
#define KEY_Q SDLK_q
#define KEY_R SDLK_r
#define KEY_S SDLK_s
#define KEY_T SDLK_t
#define KEY_U SDLK_u
#define KEY_V SDLK_v
#define KEY_W SDLK_w
#define KEY_X SDLK_x
#define KEY_Y SDLK_y
#define KEY_Z SDLK_z
#define KEY_RSHIFT SDLK_RSHIFT
#define KEY_LSHIFT SDLK_LSHIFT
#define KEY_RCONTROL SDLK_RCTRL
#define KEY_LCONTROL SDLK_LCTRL
#define KEY_RALT SDLK_RALT
#define KEY_LALT SDLK_LALT
#define KEY_UP SDLK_UP
#define KEY_DOWN SDLK_DOWN
#define KEY_LEFT SDLK_LEFT
#define KEY_RIGHT SDLK_RIGHT
#endif

View file

@ -1,91 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "config.hpp"
#include "hotkeys.hpp"
#include "language.hpp"
#include "preferences.hpp"
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
std::map<std::string,std::string> string_table;
std::vector<std::string> get_languages(config& cfg)
{
std::vector<std::string> res;
const std::vector<config*>& lang = cfg.children["language"];
for(std::vector<config*>::const_iterator i = lang.begin();
i != lang.end(); ++i) {
res.push_back((*i)->values["language"]);
}
return res;
}
namespace {
bool internal_set_language(const std::string& locale, config& cfg)
{
const std::vector<config*>& lang = cfg.children["language"];
for(std::vector<config*>::const_iterator i = lang.begin();
i != lang.end(); ++i) {
if((*i)->values["id"] == locale || (*i)->values["language"] == locale) {
for(std::map<std::string,std::string>::const_iterator j =
(*i)->values.begin(); j != (*i)->values.end(); ++j) {
string_table[j->first] = j->second;
}
add_hotkeys(**i);
return true;
}
}
return false;
}
}
bool set_language(const std::string& locale, config& cfg)
{
string_table.clear();
//default to English locale first, then set desired locale
internal_set_language("en",cfg);
return internal_set_language(locale,cfg);
}
std::string get_locale()
{
//TODO: Add in support for querying the locale on Windows
const std::string& prefs_locale = preferences::locale();
if(prefs_locale.empty() == false) {
return prefs_locale;
}
const char* const locale = getenv("LANG");
if(locale != NULL && strlen(locale) >= 2) {
//we can't pass pointers into the string to the std::string
//constructor because some STL implementations don't support
//it (*cough* MSVC++6)
std::string res(2,'z');
res[0] = tolower(locale[0]);
res[1] = tolower(locale[1]);
return res;
}
std::cerr << "locale could not be determined; defaulting to locale 'en'\n";
return "en";
}

View file

@ -1,29 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef LANGUAGE_HPP_INCLUDED
#define LANGUAGE_HPP_INCLUDED
#include "config.hpp"
#include <map>
#include <string>
#include <vector>
extern std::map<std::string,std::string> string_table;
std::vector<std::string> get_languages(config& cfg);
bool set_language(const std::string& locale, config& cfg);
std::string get_locale();
#endif

16
log.cpp
View file

@ -1,16 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "log.hpp"
int scope_logger::indent = 0;

62
log.hpp
View file

@ -1,62 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef LOG_HPP_INCLUDED
#define LOG_HPP_INCLUDED
#define LOG_DATA
#ifdef LOG_DATA
#include <iostream>
#include <string>
#include "SDL.h"
struct scope_logger
{
scope_logger(const std::string& str) : ticks_(SDL_GetTicks()), str_(str) {
for(int i = 0; i != indent; ++i)
std::cerr << " ";
++indent;
std::cerr << "BEGIN: " << str_ << "\n";
}
~scope_logger() {
const int ticks = SDL_GetTicks() - ticks_;
--indent;
for(int i = 0; i != indent; ++i)
std::cerr << " ";
std::cerr << "END: " << str_ << " (took " << ticks << "ms)\n";
}
private:
int ticks_;
std::string str_;
static int indent;
};
#define log(a) std::cerr << a << "\n";
#define log1(a,b) std::cerr << a << " info: " << b << "\n";
#define log2(a,b,c) std::cerr << a << " info: " << b << ", " << c << "\n";
#define log_scope(a) scope_logger scope_logging_object__(a);
#else
#define log(a)
#define log1(a,b)
#define log2(a,b,c)
#define log_scope(a)
#endif
#endif

View file

@ -1,145 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "config.hpp"
using namespace std;
void process_config(const std::string& element_name, const config& cfg,
std::map<std::string,std::string>& out)
{
typedef std::pair<string,string> pair;
for(map<string,vector<config*> >::const_iterator i =
cfg.children.begin(); i != cfg.children.end(); ++i) {
for(vector<config*>::const_iterator j = i->second.begin();
j != i->second.end(); ++j) {
process_config(i->first,**j,out);
}
}
const map<string,string>& table = cfg.values;
const map<string,string>::const_iterator id = table.find("id");
if(element_name == "message") {
const map<string,string>::const_iterator msg = table.find("message");
if(id == table.end()) {
static const std::string dummy;
const std::string& text = msg != table.end() ? msg->second:dummy;
std::cerr << "message found with no id: " << text << "\n";
return;
}
if(msg == table.end()) {
std::cerr << "message found with no text\n";
return;
}
out.insert(std::pair<string,string>(id->second,msg->second));
} else if(element_name == "part") {
const map<string,string>::const_iterator msg = table.find("story");
if(id == table.end() || msg == table.end()) {
return;
}
out.insert(std::pair<string,string>(id->second,msg->second));
} else if(element_name == "multiplayer" || element_name == "scenario") {
map<string,string>::const_iterator msg = table.find("name");
if(id == table.end() || msg == table.end()) {
return;
}
out.insert(std::pair<string,string>(id->second,msg->second));
if(element_name == "scenario") {
msg = table.find("objectives");
if(msg != table.end()) {
out.insert(std::pair<string,string>(id->second + "_objectives",
msg->second));
}
}
} else if(element_name == "language") {
const map<string,string>::const_iterator name = table.find("language");
if(name != table.end() && name->second == "English") {
for(map<string,string>::const_iterator i = table.begin();
i != table.end(); ++i) {
if(i->first != "language") {
out.insert(*i);
}
}
}
} else if(element_name == "unit") {
const map<string,string>::const_iterator name_it = table.find("name");
if(name_it == table.end()) {
return;
}
std::string name = name_it->second;
name.erase(std::remove(name.begin(),name.end(),' '),name.end());
out.insert(std::pair<string,string>(name,name_it->second));
const map<string,string>::const_iterator description_it =
table.find("unit_description");
if(description_it != table.end()) {
out.insert(std::pair<string,string>(name + "_description",
description_it->second));
}
const map<string,string>::const_iterator ability_it =
table.find("ability");
if(ability_it != table.end()) {
out.insert(pair("ability_" + ability_it->second,
ability_it->second));
}
} else if(element_name == "attack") {
const map<string,string>::const_iterator name_it=table.find("name");
const map<string,string>::const_iterator type_it=table.find("type");
const map<string,string>::const_iterator spec_it=table.find("special");
if(name_it == table.end() || type_it == table.end()) {
return;
}
out.insert(pair("weapon_name_" + name_it->second,name_it->second));
out.insert(pair("weapon_type_" + type_it->second,type_it->second));
if(spec_it == table.end()) {
return;
}
out.insert(pair("weapon_special_" + spec_it->second,spec_it->second));
}
}
int main()
{
config cfg(preprocess_file("data/game.cfg"));
map<string,string> table;
process_config("",cfg,table);
std::cout << "[language]\n\tlanguage=\"Language Name Goes Here\"\n" <<
"id=en #language code - English=en, French=fr, etc\n";
for(map<string,string>::const_iterator i = table.begin();
i != table.end(); ++i) {
std::cout << "\t" << i->first << "=\"" << i->second << "\"\n";
}
std::cout << "[/language]\n";
return 0;
}

224
map.cpp
View file

@ -1,224 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "map.hpp"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstdlib>
#include <iostream>
#include <sstream>
gamemap::location gamemap::location::null_location;
const std::string& gamemap::terrain_name(gamemap::TERRAIN terrain) const
{
static const std::string default_val;
const std::map<TERRAIN,terrain_type>::const_iterator i =
letterToTerrain_.find(terrain);
if(i == letterToTerrain_.end())
return default_val;
else
return i->second.name();
}
const std::string& gamemap::underlying_terrain_name(gamemap::TERRAIN terrain) const
{
static const std::string default_val;
const std::map<TERRAIN,terrain_type>::const_iterator i =
letterToTerrain_.find(terrain);
if(i == letterToTerrain_.end()) {
return default_val;
} else {
if(i->second.is_alias()) {
//we could call underlying_terrain_name, but that could allow
//infinite recursion with bad data files, so we call terrain_name
//to be safe
return terrain_name(i->second.type());
} else {
return i->second.name();
}
}
}
gamemap::TERRAIN gamemap::underlying_terrain(TERRAIN terrain) const
{
const std::map<TERRAIN,terrain_type>::const_iterator i =
letterToTerrain_.find(terrain);
if(i == letterToTerrain_.end()) {
return terrain;
} else {
return i->second.type();
}
}
gamemap::location::location(config& cfg) : x(-1), y(-1)
{
const std::string& xstr = cfg.values["x"];
const std::string& ystr = cfg.values["y"];
//the co-ordinates in config files will be 1-based, while we
//want them as 0-based
if(xstr.empty() == false)
x = atoi(xstr.c_str()) - 1;
if(ystr.empty() == false)
y = atoi(ystr.c_str()) - 1;
}
bool gamemap::location::operator==(const gamemap::location& a) const
{
return x == a.x && y == a.y;
}
bool gamemap::location::operator!=(const gamemap::location& a) const
{
return !operator==(a);
}
bool gamemap::location::operator<(const gamemap::location& a) const
{
return x < a.x || x == a.x && y < a.y;
}
gamemap::location gamemap::location::get_direction(
gamemap::location::DIRECTION dir) const
{
switch(dir) {
case NORTH: return gamemap::location(x,y-1);
case NORTH_EAST: return gamemap::location(x+1,y-((x%2) == 0 ? 1:0));
case SOUTH_EAST: return gamemap::location(x+1,y+((x%2) == 0 ? 0:1));
case SOUTH: return gamemap::location(x,y+1);
case SOUTH_WEST: return gamemap::location(x-1,y+((x%2) == 0 ? 0:1));
case NORTH_WEST: return gamemap::location(x-1,y+((x%2) == 0 ? 1:0));
default:
assert(false);
return gamemap::location();
}
}
gamemap::gamemap(config& cfg, const std::string& data) : tiles_(1)
{
std::vector<config*>& terrains = cfg.children["terrain"];
create_terrain_maps(terrains,terrainPrecedence_,letterToTerrain_,terrain_);
int x = 0, y = 0;
for(std::string::const_iterator i = data.begin(); i != data.end(); ++i) {
char c = *i;
if(c == '\n') {
tiles_.push_back(std::vector<TERRAIN>());
y = 0;
++x;
} else {
if(letterToTerrain_.count(c) == 0) {
if(isdigit(*i)) {
startingPositions_[c - '0'] = location(x,y);
c = CASTLE;
} else {
std::cerr << "Illegal character in map: '" << c << "'\n";
throw incorrect_format_exception("Illegal character");
}
}
if(c == TOWER) {
towers_.push_back(location(x,y));
}
tiles_.back().push_back(c);
++y;
}
}
if(tiles_.empty())
throw incorrect_format_exception("empty map");
for(int n = 0; n != tiles_.size(); ++n) {
if(tiles_[n].size() != this->y()) {
tiles_.erase(tiles_.begin()+n);
--n;
}
}
}
std::string gamemap::write() const
{
std::stringstream str;
for(int i = 0; i != x(); ++i) {
for(int j = 0; j != y(); ++j) {
int n;
for(n = 0; n != 10; ++n) {
if(startingPositions_[n] == location(i,j))
break;
}
if(n < 10)
str << n;
else
str << tiles_[i][j];
}
str << "\n";
}
return str.str();
}
int gamemap::x() const { return tiles_.size(); }
int gamemap::y() const { return tiles_[0].size(); }
const std::vector<gamemap::TERRAIN>& gamemap::operator[](int index) const
{
return tiles_[index];
}
const gamemap::location& gamemap::starting_position(int n) const
{
return startingPositions_[n];
}
int gamemap::num_starting_positions() const
{
return sizeof(startingPositions_)/sizeof(*startingPositions_);
}
bool gamemap::is_starting_position(const gamemap::location& loc) const
{
const gamemap::location* const end
= startingPositions_+num_starting_positions();
return std::find(startingPositions_,end,loc) != end;
}
const terrain_type& gamemap::get_terrain_info(TERRAIN terrain) const
{
static const terrain_type default_terrain;
const std::map<TERRAIN,terrain_type>::const_iterator i =
letterToTerrain_.find(terrain);
if(i != letterToTerrain_.end())
return i->second;
else
return default_terrain;
}
const std::vector<gamemap::TERRAIN>& gamemap::get_terrain_precedence() const
{
return terrainPrecedence_;
}
void gamemap::set_terrain(const gamemap::location& loc, gamemap::TERRAIN ter)
{
if(!on_board(loc))
return;
tiles_[loc.x][loc.y] = ter;
}

101
map.hpp
View file

@ -1,101 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef MAP_H_INCLUDED
#define MAP_H_INCLUDED
#include "config.hpp"
#include "terrain.hpp"
#include <map>
#include <string>
#include <vector>
class gamemap
{
public:
typedef char TERRAIN;
enum { VOID_TERRAIN = ' ', CASTLE = 'C', TOWER = 't', FOREST = 'f' };
//the name of the terrain is the terrain itself, the underlying terrain
//is the name of the terrain for game-logic purposes. I.e. if the terrain
//is simply an alias, the underlying terrain name is the name of the
//terrain that it's aliased to
const std::string& terrain_name(TERRAIN terrain) const;
const std::string& underlying_terrain_name(TERRAIN terrain) const;
TERRAIN underlying_terrain(TERRAIN terrain) const;
struct incorrect_format_exception {
incorrect_format_exception(const char* msg) : msg_(msg) {}
const char* const msg_;
};
struct location {
enum DIRECTION { NORTH, NORTH_EAST, SOUTH_EAST, SOUTH,
SOUTH_WEST, NORTH_WEST };
location() : x(-1), y(-1) {}
location(int x, int y) : x(x), y(y) {}
location(config& cfg);
bool valid() const { return x >= 0 && y >= 0; }
int x, y;
bool operator<(const location& a) const;
bool operator==(const location& a) const;
bool operator!=(const location& a) const;
location get_direction(DIRECTION d) const;
static location null_location;
};
gamemap(config& cfg,
const std::string& data); //throw(incorrect_format_exception)
std::string write() const;
int x() const;
int y() const;
const std::vector<TERRAIN>& operator[](int index) const;
const location& starting_position(int n) const;
int num_starting_positions() const;
bool is_starting_position(const location& loc) const;
bool on_board(const location& loc) const
{
return loc.valid() && loc.x < x() && loc.y < y();
}
const std::vector<location>& towers() const { return towers_; }
const terrain_type& get_terrain_info(TERRAIN terrain) const;
const std::vector<TERRAIN>& get_terrain_precedence() const;
void set_terrain(const location& loc, TERRAIN ter);
private:
std::vector<TERRAIN> terrainPrecedence_;
std::map<TERRAIN,terrain_type> letterToTerrain_;
std::map<std::string,terrain_type> terrain_;
std::vector<std::vector<TERRAIN> > tiles_;
std::vector<location> towers_;
location startingPositions_[10];
};
#endif

910
menu.cpp
View file

@ -1,910 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "config.hpp"
#include "font.hpp"
#include "language.hpp"
#include "menu.hpp"
#include "playlevel.hpp"
#include "language.hpp"
#include "sdl_utils.hpp"
#include "widgets/button.hpp"
#include "widgets/textbox.hpp"
#include <iostream>
#include <numeric>
namespace gui {
void draw_dialog_frame(int x, int y, int w, int h, display& disp)
{
const int border_size = 6;
SDL_Surface* const scr = disp.video().getSurface();
const display::Pixel border_colour = SDL_MapRGB(scr->format,200,0,0);
draw_solid_tinted_rectangle(x-border_size,y-border_size,
w+border_size,h+border_size,0,0,0,0.6,scr);
draw_solid_tinted_rectangle(x,y,w+border_size,h+border_size,0,0,0,0.6,scr);
draw_rectangle(x-border_size,y-border_size,w+border_size,h+border_size,
border_colour,scr);
draw_rectangle(x,y,w+border_size,h+border_size,border_colour,scr);
SDL_Rect update = {x-border_size,y-border_size,
w+border_size*2+1,h+border_size*2+1};
disp.update_rect(update);
}
void draw_rectangle(int x, int y, int w, int h, short colour,
SDL_Surface* target)
{
if(x < 0 || y < 0 || x+w >= target->w || y+h >= target->h) {
std::cerr << "Rectangle has illegal co-ordinates: " << x << "," << y
<< "," << w << "," << h << "\n";
return;
}
short* top_left = reinterpret_cast<short*>(target->pixels) + target->w*y+x;
short* top_right = top_left + w;
short* bot_left = top_left + target->w*h;
short* bot_right = bot_left + w;
std::fill(top_left,top_right+1,colour);
std::fill(bot_left,bot_right+1,colour);
while(top_left != bot_left) {
*top_left = colour;
*top_right = colour;
top_left += target->w;
top_right += target->w;
}
}
void draw_solid_tinted_rectangle(int x, int y, int w, int h,
int r, int g, int b,
double alpha, SDL_Surface* target)
{
if(x < 0 || y < 0 || x+w >= target->w || y+h >= target->h) {
std::cerr << "Rectangle has illegal co-ordinates: " << x << "," << y
<< "," << w << "," << h << "\n";
return;
}
const SDL_PixelFormat* const fmt = target->format;
short* p = reinterpret_cast<short*>(target->pixels) + target->w*y + x;
while(h > 0) {
short* beg = p;
short* const end = p + w;
while(beg != end) {
const int cur_r = ((*beg&fmt->Rmask) >> fmt->Rshift) << fmt->Rloss;
const int cur_g = ((*beg&fmt->Gmask) >> fmt->Gshift) << fmt->Gloss;
const int cur_b = ((*beg&fmt->Bmask) >> fmt->Bshift) << fmt->Bloss;
const int new_r = int(double(cur_r)*(1.0-alpha) + double(r)*alpha);
const int new_g = int(double(cur_g)*(1.0-alpha) + double(g)*alpha);
const int new_b = int(double(cur_b)*(1.0-alpha) + double(b)*alpha);
*beg = ((new_r >> fmt->Rloss) << fmt->Rshift) |
((new_g >> fmt->Gloss) << fmt->Gshift) |
((new_b >> fmt->Bloss) << fmt->Bshift);
++beg;
}
p += target->w;
--h;
}
}
} //end namespace gui
namespace {
const int max_menu_items = 18;
const int menu_font_size = 16;
const int menu_cell_padding = 10;
class menu
{
display* display_;
int x_, y_;
std::vector<std::vector<std::string> > items_;
mutable std::vector<int> column_widths_;
scoped_sdl_surface buffer_;
int selected_;
bool click_selects_;
bool previous_button_;
bool drawn_;
mutable int height_;
mutable int width_;
mutable int first_item_on_screen_;
gui::button uparrow_, downarrow_;
const std::vector<int>& column_widths() const
{
if(column_widths_.empty()) {
for(int row = 0; row != items_.size(); ++row) {
for(int col = 0; col != items_[row].size(); ++col) {
static const SDL_Rect area =
{0,0,display_->x(),display_->y()};
const SDL_Rect res =
font::draw_text(NULL,area,menu_font_size,
font::NORMAL_COLOUR,items_[row][col],x_,y_);
if(col == column_widths_.size()) {
column_widths_.push_back(res.w + menu_cell_padding);
} else if(res.w > column_widths_[col] - menu_cell_padding) {
column_widths_[col] = res.w + menu_cell_padding;
}
}
}
}
return column_widths_;
}
void draw_item(int item) {
SDL_Rect rect = get_item_rect(item);
if(rect.w == 0)
return;
short* const dstptr = reinterpret_cast<short*>(
display_->video().getSurface()->pixels) +
rect.y*display_->x() + x_;
if(buffer_.get() != NULL) {
const int ypos = items_start()+(item-first_item_on_screen_)*rect.h;
SDL_Rect srcrect = {0,ypos,rect.w,rect.h};
SDL_BlitSurface(buffer_,&srcrect,
display_->video().getSurface(),&rect);
}
short* dst = dstptr;
gui::draw_solid_tinted_rectangle(x_,rect.y,width(),rect.h,
item == selected_ ? 150:0,0,0,
0.7,display_->video().getSurface());
SDL_Rect area = {0,0,display_->x(),display_->y()};
const std::vector<int>& widths = column_widths();
int xpos = rect.x;
for(int i = 0; i != items_[item].size(); ++i) {
font::draw_text(display_,area,menu_font_size,font::NORMAL_COLOUR,
items_[item][i],xpos,rect.y);
xpos += widths[i];
}
}
void draw() {
drawn_ = true;
for(int i = 0; i != items_.size(); ++i)
draw_item(i);
display_->video().update(x_,y_,width(),height());
}
int hit(int x, int y) const {
if(x > x_ && x < x_ + width() && y > y_ && y < y_ + height()){
for(int i = 0; i != items_.size(); ++i) {
const SDL_Rect& rect = get_item_rect(i);
if(y > rect.y && y < rect.y + rect.h)
return i;
}
}
return -1;
}
mutable std::map<int,SDL_Rect> itemRects_;
SDL_Rect get_item_rect(int item) const
{
const SDL_Rect empty_rect = {0,0,0,0};
if(item < first_item_on_screen_ ||
item >= first_item_on_screen_ + max_menu_items) {
return empty_rect;
}
const std::map<int,SDL_Rect>::const_iterator i = itemRects_.find(item);
if(i != itemRects_.end())
return i->second;
int y = y_ + items_start();
if(item != first_item_on_screen_) {
const SDL_Rect& prev = get_item_rect(item-1);
y = prev.y + prev.h;
}
static const SDL_Rect area = {0,0,display_->x(),display_->y()};
SDL_Rect res = font::draw_text(NULL,area,menu_font_size,
font::NORMAL_COLOUR,items_[item][0],x_,y);
res.w = width();
//only insert into the cache if the menu's co-ordinates have
//been initialized
if(x_ > 0 && y_ > 0)
itemRects_.insert(std::pair<int,SDL_Rect>(item,res));
return res;
}
int items_start() const
{
if(items_.size() > max_menu_items)
return uparrow_.height();
else
return 0;
}
int items_end() const
{
if(items_.size() > max_menu_items)
return height() - downarrow_.height();
else
return height();
}
int items_height() const
{
return items_end() - items_start();
}
public:
menu(display& disp, const std::vector<std::string>& items,
bool click_selects=false)
: display_(&disp), x_(0), y_(0), buffer_(NULL),
selected_(-1), click_selects_(click_selects),
previous_button_(true), drawn_(false), height_(-1), width_(-1),
first_item_on_screen_(0),
uparrow_(disp,"",gui::button::TYPE_PRESS,"uparrow"),
downarrow_(disp,"",gui::button::TYPE_PRESS,"downarrow")
{
for(std::vector<std::string>::const_iterator item = items.begin();
item != items.end(); ++item) {
items_.push_back(config::split(*item));
//make sure there is always at least one item
if(items_.back().empty())
items_.back().push_back(" ");
}
}
int height() const {
if(height_ == -1) {
const SDL_Rect area = { 0, 0, display_->x(), display_->y() };
height_ = 0;
for(int i = 0; i != items_.size() && i != max_menu_items; ++i) {
height_ += get_item_rect(i).h;
}
if(items_.size() > max_menu_items) {
height_ += uparrow_.height() + downarrow_.height();
}
}
return height_;
}
int width() const {
if(width_ == -1) {
const std::vector<int>& widths = column_widths();
width_ = std::accumulate(widths.begin(),widths.end(),0);
}
return width_;
}
int selection() const { return selected_; }
void set_loc(int x, int y) {
x_ = x;
y_ = y;
const int w = width();
SDL_Rect portion = {x_,y_,w,height()};
SDL_Surface* const screen = display_->video().getSurface();
buffer_.assign(get_surface_portion(screen, portion));
if(items_.size() > max_menu_items) {
uparrow_.set_x(x_);
uparrow_.set_y(y_);
downarrow_.set_x(x_);
downarrow_.set_y(y_+items_end());
}
}
int process(int x, int y, bool button,bool up_arrow,bool down_arrow,
bool page_up, bool page_down) {
if(items_.size() > max_menu_items) {
const bool up = uparrow_.process(x,y,button);
if(up && first_item_on_screen_ > 0) {
itemRects_.clear();
--first_item_on_screen_;
draw();
}
const bool down = downarrow_.process(x,y,button);
if(down && first_item_on_screen_ + max_menu_items < items_.size()) {
itemRects_.clear();
++first_item_on_screen_;
draw();
}
}
if(up_arrow && !click_selects_ && selected_ > 0) {
--selected_;
if(selected_ < first_item_on_screen_) {
itemRects_.clear();
first_item_on_screen_ = selected_;
}
draw();
}
if(down_arrow && !click_selects_ && selected_ < items_.size()-1) {
++selected_;
if(selected_ - first_item_on_screen_ == max_menu_items) {
itemRects_.clear();
++first_item_on_screen_;
}
draw();
}
if(page_up && !click_selects_) {
selected_ -= max_menu_items;
if(selected_ < 0)
selected_ = 0;
itemRects_.clear();
first_item_on_screen_ = selected_;
draw();
}
if(page_down && !click_selects_) {
selected_ += max_menu_items;
if(selected_ >= items_.size())
selected_ = items_.size()-1;
first_item_on_screen_ = selected_ - (max_menu_items-1);
if(first_item_on_screen_ < 0)
first_item_on_screen_ = 0;
itemRects_.clear();
draw();
}
const int starting_selected = selected_;
const int hit_item = hit(x,y);
if(click_selects_) {
selected_ = hit_item;
if(button && !previous_button_)
return selected_;
else {
if(!drawn_ || selected_ != starting_selected)
draw();
previous_button_ = button;
return -1;
}
}
if(button && hit_item != -1){
selected_ = hit_item;
}
if(selected_ == -1)
selected_ = 0;
if(selected_ != starting_selected)
draw();
return -1;
}
};
}
namespace gui
{
int show_dialog(display& disp, SDL_Surface* image,
const std::string& caption, const std::string& msg,
DIALOG_TYPE type,
const std::vector<std::string>* menu_items_ptr,
const std::vector<unit>* units_ptr,
const std::string& text_widget_label,
std::string* text_widget_text)
{
if(disp.update_locked())
return -1;
const std::vector<std::string>& menu_items =
(menu_items_ptr == NULL) ? std::vector<std::string>() : *menu_items_ptr;
const std::vector<unit>& units =
(units_ptr == NULL) ? std::vector<unit>() : *units_ptr;
static const int message_font_size = 16;
static const int caption_font_size = 18;
CVideo& screen = disp.video();
SDL_Surface* const scr = screen.getSurface();
SDL_Rect clipRect = { 0, 0, disp.x(), disp.y() };
const bool use_textbox = text_widget_text != NULL;
static const std::string default_text_string = "";
const unsigned int text_box_width = 350;
textbox text_widget(disp,text_box_width,
use_textbox ? *text_widget_text : default_text_string);
int text_widget_width = 0;
int text_widget_height = 0;
if(use_textbox) {
text_widget_width =
font::draw_text(NULL, clipRect, message_font_size,
font::NORMAL_COLOUR, text_widget_label, 0, 0, NULL).w +
text_widget.width();
text_widget_height = text_widget.height();
}
menu menu_(disp,menu_items,type == MESSAGE);
const int border_size = 6;
const short text_colour = 0xFFFF;
const short border_colour = 0xF000;
int nlines = 1;
int longest_line = 0;
int cur_line = 0;
const int max_line_length = 58;
std::string message = msg;
for(std::string::iterator message_it = message.begin();
message_it != message.end(); ++message_it) {
if(*message_it == ' ' && cur_line > max_line_length)
*message_it = '\n';
if(*message_it == '\n') {
if(cur_line > longest_line)
longest_line = cur_line;
cur_line = 0;
++nlines;
} else {
++cur_line;
}
}
SDL_Rect text_size = { 0, 0, 0, 0 };
if(!message.empty()) {
text_size = font::draw_text(NULL, clipRect, message_font_size,
font::NORMAL_COLOUR, message, 0, 0, NULL);
}
SDL_Rect caption_size = { 0, 0, 0, 0 };
if(!caption.empty()) {
caption_size = font::draw_text(NULL, clipRect, caption_font_size,
font::NORMAL_COLOUR,caption,0,0,NULL);
}
const std::string* button_list = NULL;
std::vector<button> buttons;
switch(type) {
case MESSAGE:
break;
case OK_ONLY: {
static const std::string thebuttons[] = { "ok_button", "" };
button_list = thebuttons;
break;
}
case YES_NO: {
static const std::string thebuttons[] = { "yes_button",
"no_button", ""};
button_list = thebuttons;
break;
}
case OK_CANCEL: {
static const std::string thebuttons[] = { "ok_button",
"cancel_button",""};
button_list = thebuttons;
break;
}
}
const int button_height_padding = 10;
int button_width_padding = 0;
int button_heights = 0;
int button_widths = 0;
if(button_list != NULL) {
try {
while(button_list->empty() == false) {
buttons.push_back(button(disp,string_table[*button_list]));
if(buttons.back().height() > button_heights)
button_heights = buttons.back().height();
button_widths += buttons.back().width();
button_width_padding += 5;
++button_list;
}
} catch(button::error&) {
std::cerr << "error initializing button!\n";
}
}
if(button_heights > 0) {
button_heights += button_height_padding;
}
if(cur_line > longest_line)
longest_line = cur_line;
const int left_padding = 10;
const int right_padding = 10;
const int image_h_padding = image != NULL ? 10 : 0;
const int top_padding = 10;
const int bottom_padding = 10;
const int padding_width = left_padding + right_padding + image_h_padding;
const int padding_height = top_padding + bottom_padding;
const int caption_width = caption_size.w;
const int image_width = image != NULL ? image->w : 0;
const int total_image_width = caption_width > image_width ?
caption_width : image_width;
const int image_height = image != NULL ? image->h : 0;
int text_width = text_size.w;
if(menu_.width() > text_width)
text_width = menu_.width();
int total_width = total_image_width + text_width +
padding_width;
if(button_widths + button_width_padding > total_width)
total_width = button_widths + button_width_padding;
if(text_widget_width+left_padding+right_padding > total_width)
total_width = text_widget_width+left_padding+right_padding;
const int total_height = (image_height+8 > text_size.h ?
image_height+8 : text_size.h) +
padding_height + button_heights + menu_.height() +
text_widget_height;
if(total_width > scr->w - 100 || total_height > scr->h - 100)
return false;
int xloc = scr->w/2 - total_width/2;
int yloc = scr->h/2 - total_height/2;
int unitx = 0;
int unity = 0;
//if we are showing a dialog with unit details, then we have
//to make more room for it
if(!units.empty()) {
xloc += scr->w/10;
unitx = xloc - 300;
if(unitx < 10)
unitx = 10;
unity = yloc;
}
//make sure that the dialog doesn't overlap the right part of the screen
if(xloc + total_width+border_size >= disp.mapx()-1) {
xloc = disp.mapx()-(total_width+border_size+2);
if(xloc < 0)
return -1;
}
const int button_hpadding = total_width - button_widths;
int button_offset = 0;
for(int button_num = 0; button_num != buttons.size(); ++button_num) {
const int padding_amount = button_hpadding/(buttons.size()+1);
buttons[button_num].set_x(xloc + padding_amount*(button_num+1) +
button_offset);
buttons[button_num].set_y(yloc + total_height - button_heights);
button_offset += buttons[button_num].width();
}
if(menu_.height() > 0)
menu_.set_loc(xloc+total_image_width+left_padding+image_h_padding,
yloc+top_padding+text_size.h);
draw_dialog_frame(xloc,yloc,total_width,total_height,disp);
if(image != NULL) {
const int x = xloc + left_padding;
const int y = yloc + top_padding;
disp.blit_surface(x,y,image);
int center_font = 0;
if(caption_size.w < image->w) {
center_font = image->w/2 - caption_size.w/2;
}
font::draw_text(&disp, clipRect, caption_font_size,
font::NORMAL_COLOUR, caption,
xloc+left_padding+center_font,
yloc+top_padding+image->h-6, NULL);
}
if(!units.empty()) {
const int unitw = 200;
const int unith = disp.y()/2;
draw_solid_tinted_rectangle(unitx,unity,unitw,unith,
0,0,0,1.0,scr);
draw_rectangle(unitx,unity,unitw,unith,border_colour,scr);
}
font::draw_text(&disp, clipRect, message_font_size,
font::NORMAL_COLOUR, message,
xloc+total_image_width+left_padding+image_h_padding,
yloc+top_padding);
if(use_textbox) {
const int image_h = image != NULL ? image->h : 0;
const int text_widget_y = yloc+top_padding+image_h-6+text_size.h;
text_widget.set_location(xloc + left_padding +
text_widget_width - text_widget.width(),
text_widget_y);
text_widget.draw();
font::draw_text(&disp, clipRect, message_font_size,
font::NORMAL_COLOUR, text_widget_label,
xloc + left_padding,text_widget_y);
}
screen.update(0,0,scr->w,scr->h);
CKey key;
bool left_button = true, right_button = true, key_down = true,
up_arrow = false, down_arrow = false,
page_up = false, page_down = false;
disp.invalidate_all();
int cur_selection = -1;
SDL_Rect unit_details_rect, unit_profile_rect;
unit_details_rect.w = 0;
unit_profile_rect.w = 0;
bool first_time = true;
for(;;) {
int mousex, mousey;
const int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
const bool new_right_button = mouse_flags&SDL_BUTTON_RMASK;
const bool new_left_button = mouse_flags&SDL_BUTTON_LMASK;
const bool new_key_down = key[KEY_SPACE] || key[KEY_ENTER] ||
key[KEY_ESCAPE];
const bool new_up_arrow = key[KEY_UP];
const bool new_down_arrow = key[KEY_DOWN];
const bool new_page_up = key[SDLK_PAGEUP];
const bool new_page_down = key[SDLK_PAGEDOWN];
if(!key_down && key[KEY_ENTER] &&
(type == YES_NO || type == OK_CANCEL)) {
if(menu_.height() == 0) {
return 0;
} else {
return menu_.selection();
}
}
if(menu_.selection() != cur_selection || first_time) {
cur_selection = menu_.selection();
int selection = cur_selection;
if(first_time)
selection = 0;
if(selection >= 0 && selection < units.size()) {
SDL_Surface* const screen = disp.video().getSurface();
if(unit_details_rect.w > 0) {
SDL_FillRect(screen,&unit_details_rect,0);
disp.update_rect(unit_details_rect);
}
if(unit_profile_rect.w > 0) {
SDL_FillRect(screen,&unit_profile_rect,0);
disp.update_rect(unit_profile_rect);
}
disp.draw_unit_details(unitx+left_padding,
unity+top_padding, gamemap::location(), units[selection],
unit_details_rect, unit_profile_rect);
disp.update_display();
}
}
first_time = false;
if(menu_.height() > 0) {
const int res = menu_.process(mousex,mousey,new_left_button,
!up_arrow && new_up_arrow,
!down_arrow && new_down_arrow,
!page_up && new_page_up,
!page_down && new_page_down);
if(res != -1)
return res;
}
up_arrow = new_up_arrow;
down_arrow = new_down_arrow;
page_up = new_page_up;
page_down = new_page_down;
if(use_textbox) {
text_widget.process();
}
if(buttons.empty() && (new_left_button && !left_button ||
new_right_button && !right_button) ||
buttons.size() < 2 && new_key_down && !key_down &&
menu_.height() == 0)
break;
left_button = new_left_button;
right_button = new_right_button;
key_down = new_key_down;
for(std::vector<button>::iterator button_it = buttons.begin();
button_it != buttons.end(); ++button_it) {
if(button_it->process(mousex,mousey,left_button)) {
if(text_widget_text != NULL && use_textbox)
*text_widget_text = text_widget.text();
//if the menu is not used, then return the index of the
//button pressed, otherwise return the index of the menu
//item selected if the last button is not pressed, and
//cancel (-1) otherwise
if(menu_.height() == 0) {
return button_it - buttons.begin();
} else if(buttons.size() <= 1 ||
button_it - buttons.begin() != buttons.size()-1) {
return menu_.selection();
} else {
return -1;
}
}
}
SDL_PumpEvents();
}
return -1;
}
TITLE_RESULT show_title(display& screen)
{
SDL_Surface* const title_surface = screen.getImage("title.png",
display::UNSCALED);
if(title_surface == NULL) {
std::cerr << "Could not find title image 'title.png'\n";
return QUIT_GAME;
}
const int x = screen.x()/2 - title_surface->w/2;
const int y = 100;
screen.blit_surface(x,y,title_surface);
button tutorial_button(screen,string_table["tutorial_button"]);
button new_button(screen,string_table["campaign_button"]);
button load_button(screen,string_table["load_button"]);
button multi_button(screen,string_table["multiplayer_button"]);
button quit_button(screen,string_table["quit_button"]);
button language_button(screen,string_table["language_button"]);
tutorial_button.set_x(700);
new_button.set_x(700);
load_button.set_x(700);
multi_button.set_x(700);
quit_button.set_x(700);
language_button.set_x(700);
tutorial_button.set_y(200);
new_button.set_y(250);
load_button.set_y(300);
multi_button.set_y(350);
language_button.set_y(400);
quit_button.set_y(450);
bool right_button = true;
bool left_button = true;
tutorial_button.draw();
new_button.draw();
load_button.draw();
multi_button.draw();
quit_button.draw();
language_button.draw();
screen.video().update(0,0,screen.x(),screen.y());
CKey key;
for(;;) {
int mousex, mousey;
const int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
const bool right_button = mouse_flags&SDL_BUTTON_RMASK;
const bool left_button = mouse_flags&SDL_BUTTON_LMASK;
if(tutorial_button.process(mousex,mousey,left_button))
return TUTORIAL;
if(new_button.process(mousex,mousey,left_button))
return NEW_CAMPAIGN;
if(load_button.process(mousex,mousey,left_button))
return LOAD_GAME;
if(multi_button.process(mousex,mousey,left_button))
return MULTIPLAYER;
if(quit_button.process(mousex,mousey,left_button))
return QUIT_GAME;
if(key[KEY_ESCAPE])
return QUIT_GAME;
if(language_button.process(mousex,mousey,left_button))
return CHANGE_LANGUAGE;
SDL_PumpEvents();
SDL_Delay(20);
}
return QUIT_GAME;
}
void check_quit(display& gui)
{
CKey key;
if(key[KEY_ESCAPE]) {
const int res = gui::show_dialog(gui,NULL,"",
string_table["quit_message"],gui::YES_NO);
if(res == 0) {
throw end_level_exception(QUIT);
}
}
}
} //end namespace gui

View file

@ -1,58 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef MENU_HPP_INCLUDED
#define MENU_HPP_INCLUDED
#include "display.hpp"
#include "SDL.h"
#include "unit.hpp"
#include "video.hpp"
#include <string>
#include <vector>
namespace gui
{
void draw_dialog_frame(int x, int y, int w, int h, display& disp);
void draw_rectangle(int x, int y, int w, int h, short colour, SDL_Surface* tg);
void draw_solid_tinted_rectangle(int x, int y, int w, int h,
int r, int g, int b,
double alpha, SDL_Surface* target);
enum DIALOG_TYPE { MESSAGE, OK_ONLY, YES_NO, OK_CANCEL };
//if a menu is given, then returns -1 if the dialog was cancelled, and the
//index of the selection otherwise. If no menu is given, returns the index
//of the button that was pressed
int show_dialog(display& screen, SDL_Surface* image,
const std::string& caption, const std::string& message,
DIALOG_TYPE type=MESSAGE,
const std::vector<std::string>* menu_items=NULL,
const std::vector<unit>* units=NULL,
const std::string& text_widget_label="",
std::string* text_widget_text=NULL
);
enum TITLE_RESULT { TUTORIAL, NEW_CAMPAIGN, MULTIPLAYER, LOAD_GAME, QUIT_GAME,
CHANGE_LANGUAGE };
TITLE_RESULT show_title(display& screen);
void check_quit(display& screen);
}
#endif

View file

@ -1,104 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include <iostream>
#include <map>
#include <string>
#include "config.hpp"
int main(int argc, char** argv)
{
if(argc != 3) {
std::cerr << "Usage: " << argv[0]
<< " translation default-translation\n";
return 0;
}
const std::string& data1 = read_file(argv[1]);
const std::string& data2 = read_file(argv[2]);
if(data1.empty()) {
std::cerr << "Could not read '" << argv[1] << "'\n";
return 0;
}
if(data2.empty()) {
std::cerr << "Could not read '" << argv[2] << "'\n";
return 0;
}
config cfg;
try {
cfg.read(data1);
} catch(config::error& e) {
std::cerr << "error parsing '" << argv[1] << "': " << e.message << "\n";
return 0;
}
std::vector<config*> translations = cfg.children["language"];
if(translations.empty()) {
std::cerr << "no translation data found in '" << argv[1] << "'\n";
return 0;
}
if(translations.size() > 1) {
std::cerr << "warning: found multiple translations in '" << argv[1]
<< "'\n";
}
const std::map<std::string,std::string> strings = translations[0]->values;
try {
cfg.read(data2);
} catch(config::error& e) {
std::cerr << "error parsing '" << argv[2] << "': " << e.message << "\n";
return 0;
}
translations = cfg.children["language"];
if(translations.empty()) {
std::cerr << "no translation data found in '" << argv[2] << "'\n";
return 0;
}
if(translations.size() > 1) {
std::cerr << "warning: found multiple translations in '" << argv[2]
<< "'\n";
}
const std::map<std::string,std::string> default_strings =
translations[0]->values;
std::cout << "[language]\n\n"
<< "#strings that were not found in the translation, \n"
<< "#and which should be translated now:\n";
for(std::map<std::string,std::string>::const_iterator i =
default_strings.begin(); i != default_strings.end(); ++i) {
if(strings.find(i->first) == strings.end()) {
std::cout << i->first << "=\"" << i->second << "\"\n";
}
}
std::cout << "\n#---------------------------------"
<< "\n#strings that have already been translated\n";
for(std::map<std::string,std::string>::const_iterator i = strings.begin();
i != strings.end(); ++i) {
std::cout << i->first << "=\"" << i->second << "\"\n";
}
std::cout << "[/language]\n";
return 0;
}

View file

@ -1,122 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "language.hpp"
#include "menu.hpp"
#include "multiplayer.hpp"
#include "playlevel.hpp"
#include "replay.hpp"
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
void play_multiplayer(display& disp, game_data& units_data, config& cfg,
game_state& state)
{
std::vector<std::string> options;
std::vector<config*>& levels = cfg.children["multiplayer"];
for(std::vector<config*>::iterator i = levels.begin(); i!=levels.end();++i){
const std::string& lang_name = string_table[(*i)->values["id"]];
if(lang_name.empty() == false)
options.push_back(lang_name);
else
options.push_back((*i)->values["name"]);
}
int res = gui::show_dialog(disp,NULL,"",
string_table["choose_scenario"],gui::OK_CANCEL,
&options);
if(res == -1)
return;
config& level = *levels[res];
state.label = level.values["name"];
state.scenario = res;
std::vector<config*>& sides = level.children["side"];
std::vector<config*>& possible_sides = cfg.children["multiplayer_side"];
if(sides.empty() || possible_sides.empty()) {
std::cerr << "no multiplayer sides found\n";
return;
}
for(std::vector<config*>::iterator sd = sides.begin();
sd != sides.end(); ++sd) {
(*sd)->values["name"] = possible_sides.front()->values["name"];
(*sd)->values["type"] = possible_sides.front()->values["type"];
(*sd)->values["recruit"] = possible_sides.front()->values["recruit"];
}
res = 0;
while(res != sides.size()) {
std::vector<std::string> sides_list;
for(std::vector<config*>::iterator sd = sides.begin();
sd != sides.end(); ++sd) {
std::stringstream details;
details << (*sd)->values["side"] << ","
<< (*sd)->values["name"] << ","
<< ((*sd)->values["controller"] == "human" ?
string_table["human_controlled"] :
string_table["ai_controlled"]);
sides_list.push_back(details.str());
}
sides_list.push_back(string_table["start_game"]);
res = gui::show_dialog(disp,NULL,"",string_table["configure_sides"],
gui::MESSAGE,&sides_list);
if(res >= 0 && res < sides.size()) {
std::vector<std::string> choices;
for(int n = 0; n != 2; ++n) {
for(std::vector<config*>::iterator i = possible_sides.begin();
i != possible_sides.end(); ++i) {
std::stringstream choice;
choice << (*i)->values["name"] << " - "
<< (n == 0 ? string_table["human_controlled"] :
string_table["ai_controlled"]);
choices.push_back(choice.str());
}
}
int result = gui::show_dialog(disp,NULL,"",
string_table["choose_side"],
gui::MESSAGE,&choices);
if(result >= 0) {
sides[res]->values["controller"] = (result >= choices.size()/2)
? "ai" : "human";
if(result >= choices.size()/2)
result -= choices.size()/2;
assert(result < possible_sides.size());
std::map<std::string,std::string>& values =
possible_sides[result]->values;
sides[res]->values["name"] = values["name"];
sides[res]->values["type"] = values["type"];
sides[res]->values["recruit"] = values["recruit"];
}
}
}
state.starting_pos = level;
recorder.set_save_info(state);
std::vector<config*> story;
play_level(units_data,cfg,&level,disp.video(),state,story);
recorder.clear();
}

View file

@ -1,25 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef MULTIPLAYER_HPP_INCLUDED
#define MULTIPLAYER_HPP_INCLUDED
#include "config.hpp"
#include "display.hpp"
#include "gamestatus.hpp"
#include "unit_types.hpp"
#include "video.hpp"
void play_multiplayer(display& disp, game_data& units_data,
config& cfg, game_state& state);
#endif

View file

@ -1,238 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "pathfind.hpp"
#include <cmath>
#include <iostream>
#include <set>
namespace {
gamemap::location find_vacant(const gamemap& map,
const std::map<gamemap::location,unit>& units,
const gamemap::location& loc, int depth,
gamemap::TERRAIN terrain,
std::set<gamemap::location>& touched)
{
if(touched.count(loc))
return gamemap::location();
touched.insert(loc);
if(map.on_board(loc) && units.find(loc) == units.end() &&
map[loc.x][loc.y] != gamemap::TOWER &&
(terrain == 0 || terrain == map[loc.x][loc.y])) {
return loc;
} else if(depth == 0) {
return gamemap::location();
} else {
gamemap::location adj[6];
get_adjacent_tiles(loc,adj);
for(int i = 0; i != 6; ++i) {
if(!map.on_board(adj[i]) ||
terrain != 0 && terrain != map[adj[i].x][adj[i].y])
continue;
const gamemap::location res =
find_vacant(map,units,adj[i],depth-1,terrain,touched);
if(map.on_board(res))
return res;
}
return gamemap::location();
}
}
}
gamemap::location find_vacant_tile(const gamemap& map,
const std::map<gamemap::location,unit>& units,
const gamemap::location& loc,
gamemap::TERRAIN terrain)
{
for(int i = 1; i != 50; ++i) {
std::set<gamemap::location> touch;
const gamemap::location res = find_vacant(map,units,loc,i,terrain,touch);
if(map.on_board(res))
return res;
}
return gamemap::location();
}
void get_adjacent_tiles(const gamemap::location& a, gamemap::location* res)
{
res->x = a.x;
res->y = a.y-1;
++res;
res->x = a.x+1;
res->y = a.y + ((a.x%2) == 0 ? -1:0);
++res;
res->x = a.x+1;
res->y = a.y + ((a.x%2) == 0 ? 0:1);
++res;
res->x = a.x;
res->y = a.y+1;
++res;
res->x = a.x-1;
res->y = a.y + ((a.x%2) == 0 ? 0:1);
++res;
res->x = a.x-1;
res->y = a.y + ((a.x%2) == 0 ? -1:0);
}
bool tiles_adjacent(const gamemap::location& a, const gamemap::location& b)
{
//two tiles are adjacent if y is different by 1, and x by 0, or if
//x is different by 1 and y by 0, or if x and y are each different by 1,
//and the x value of the hex with the greater y value is odd
const int xdiff = abs(a.x - b.x);
const int ydiff = abs(a.y - b.y);
return ydiff == 1 && a.x == b.x || xdiff == 1 && a.y == b.y ||
xdiff == 1 && ydiff == 1 && (a.y > b.y ? (a.x%2) == 1 : (b.x%2) == 1);
}
namespace {
bool enemy_zoc(const gamemap& map,const std::map<gamemap::location,unit>& units,
const gamemap::location& loc, const team& current_team, int side)
{
gamemap::location locs[6];
get_adjacent_tiles(loc,locs);
for(int i = 0; i != 6; ++i) {
const std::map<gamemap::location,unit>::const_iterator it
= units.find(locs[i]);
if(it != units.end() && it->second.side() != side &&
current_team.is_enemy(it->second.side()) &&
!it->second.invisible(map.underlying_terrain(
map[it->first.x][it->first.y]))) {
return true;
}
}
return false;
}
void find_routes(const gamemap& map, const game_data& gamedata,
const std::map<gamemap::location,unit>& units,
const unit& u,
const gamemap::location& loc,
int move_left,
std::map<gamemap::location,paths::route>& routes,
std::vector<team>& teams,
bool ignore_zocs, bool allow_teleport)
{
//find adjacent tiles
std::vector<gamemap::location> locs(6);
get_adjacent_tiles(loc,&locs[0]);
//check for teleporting units
if(allow_teleport && map[loc.x][loc.y] == gamemap::TOWER) {
const std::vector<gamemap::location>& towers = map.towers();
//if we are on a tower, see all friendly towers that we can
//teleport to
for(std::vector<gamemap::location>::const_iterator t = towers.begin();
t != towers.end(); ++t) {
if(!teams[u.side()-1].owns_tower(*t) ||
units.find(*t) != units.end())
continue;
locs.push_back(*t);
}
}
//iterate over all adjacent tiles
for(int i = 0; i != locs.size(); ++i) {
const gamemap::location& currentloc = locs[i];
//check if the adjacent location is off the board
if(currentloc.x < 0 || currentloc.y < 0 ||
currentloc.x >= map.x() || currentloc.y >= map.y())
continue;
//see if the tile is on top of an enemy unit
const std::map<gamemap::location,unit>::const_iterator unit_it =
units.find(locs[i]);
if(unit_it != units.end() && unit_it->second.side() != u.side())
continue;
//find the terrain of the adjacent location
const gamemap::TERRAIN terrain = map[currentloc.x][currentloc.y];
//find the movement cost of this type onto the terrain
const int move_cost = u.movement_cost(map,terrain);
if(move_cost <= move_left) {
const std::map<gamemap::location,paths::route>::const_iterator
rtit = routes.find(currentloc);
//if a better route to that tile has already been found
if(rtit != routes.end() &&
rtit->second.move_left >= move_left - move_cost)
continue;
const bool zoc = enemy_zoc(map,units,currentloc,
teams[u.side()-1],u.side()) &&
!ignore_zocs;
paths::route new_route = routes[loc];
new_route.steps.push_back(loc);
new_route.move_left = zoc ? 0 : move_left - move_cost;
routes[currentloc] = new_route;
if(new_route.move_left > 0) {
find_routes(map,gamedata,units,u,currentloc,
new_route.move_left,routes,teams,ignore_zocs,
allow_teleport);
}
}
}
}
} //end anon namespace
paths::paths(const gamemap& map, const game_data& gamedata,
const std::map<gamemap::location,unit>& units,
const gamemap::location& loc,
std::vector<team>& teams,
bool ignore_zocs, bool allow_teleport)
{
const std::map<gamemap::location,unit>::const_iterator i = units.find(loc);
if(i == units.end()) {
std::cerr << "unit not found\n";
return;
}
routes[loc].move_left = i->second.movement_left();
find_routes(map,gamedata,units,i->second,loc,
i->second.movement_left(),routes,teams,
ignore_zocs,allow_teleport);
if(i->second.can_attack()) {
gamemap::location adjacent[6];
get_adjacent_tiles(loc,adjacent);
for(int j = 0; j != 6; ++j) {
const std::map<gamemap::location,unit>::const_iterator enemy =
units.find(adjacent[j]);
if(enemy != units.end() &&
enemy->second.side() != i->second.side() &&
teams[i->second.side()-1].is_enemy(enemy->second.side())) {
route new_route;
new_route.move_left = -1;
routes.insert(std::pair<gamemap::location,route>(
adjacent[j],new_route));
}
}
}
}

View file

@ -1,153 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef PATHFIND_H_INCLUDED
#define PATHFIND_H_INCLUDED
#include "map.hpp"
#include "unit.hpp"
#include "unit_types.hpp"
#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>
#include <list>
#include <vector>
void get_adjacent_tiles(const gamemap::location& a, gamemap::location* res);
bool tiles_adjacent(const gamemap::location& a, const gamemap::location& b);
gamemap::location find_vacant_tile(const gamemap& map,
const std::map<gamemap::location,unit>& un,
const gamemap::location& loc,
gamemap::TERRAIN terrain=0);
struct paths
{
paths() {}
paths(const gamemap& map, const game_data& gamedata,
const std::map<gamemap::location,unit>& units,
const gamemap::location& loc, std::vector<team>& teams,
bool ignore_zocs, bool allow_teleport);
struct route
{
route() : move_left(0) {}
std::vector<gamemap::location> steps;
int move_left;
};
std::map<gamemap::location,route> routes;
};
namespace detail {
struct node {
node(const gamemap::location& pos, const gamemap::location& dst,
double cost, node* parent)
: parent(parent), loc(pos), g(cost),
h(sqrt(pow(abs(dst.x-pos.x),2) + pow(abs(dst.y-pos.y),2)))
{
f = g + h;
}
node* parent;
gamemap::location loc;
double g, h, f;
};
}
template<typename T>
paths::route a_star_search(const gamemap::location& src,
const gamemap::location& dst, double stop_at, T obj)
{
std::cout << "a* search: " << src.x << ", " << src.y << " - " << dst.x << ", " << dst.y << "\n";
using namespace detail;
typedef gamemap::location location;
std::list<node> open_list, closed_list;
open_list.push_back(node(src,dst,0.0,NULL));
while(!open_list.empty()) {
//find the lowest estimated cost node on the open list
std::list<node>::iterator lowest = open_list.end(), i;
for(i = open_list.begin(); i != open_list.end(); ++i) {
if(lowest == open_list.end() || i->f < lowest->f) {
lowest = i;
}
}
if(lowest->f > stop_at) {
break;
}
//std::cerr << "processing " << (lowest->loc.x+1) << "," << (lowest->loc.y+1) << " with cost = " << lowest->g << " (known) + " << lowest->h << " (estimated) = " << lowest->f << "\n";
//move the lowest element from the open list to the closed list
closed_list.splice(closed_list.begin(),open_list,lowest);
//find nodes we can go to from this node
location locs[6];
get_adjacent_tiles(lowest->loc,locs);
for(int j = 0; j != 6; ++j) {
//if we have found a solution
if(locs[j] == dst) {
paths::route rt;
for(node* n = &*lowest; n != NULL; n = n->parent) {
rt.steps.push_back(n->loc);
}
std::reverse(rt.steps.begin(),rt.steps.end());
rt.steps.push_back(dst);
rt.move_left = int(lowest->f);
std::cout << "exiting a* search (solved)\n";
return rt;
}
const node nd(locs[j],dst,lowest->g+obj.cost(locs[j]),&*lowest);
for(i = open_list.begin(); i != open_list.end(); ++i) {
if(i->loc == nd.loc && i->f <= nd.f) {
break;
}
}
if(i != open_list.end()) {
continue;
}
for(i = closed_list.begin(); i != closed_list.end(); ++i) {
if(i != lowest && i->loc == nd.loc && i->f <= nd.f) {
break;
}
}
if(i != closed_list.end()) {
continue;
}
open_list.push_back(nd);
}
}
std::cout << "aborted a* search\n";
paths::route val;
val.move_left = 100000;
return val;
}
#endif

View file

@ -1,293 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "game_events.hpp"
#include "intro.hpp"
#include "language.hpp"
#include "playlevel.hpp"
#include "playturn.hpp"
#include "preferences.hpp"
#include "replay.hpp"
#include "sound.hpp"
#include <iostream>
LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config,
config* level, CVideo& video,
game_state& state_of_game,
std::vector<config*>& story)
{
const int num_turns = atoi(level->values["turns"].c_str());
gamestatus status(num_turns);
gamemap map(terrain_config,read_file("data/maps/" + level->values["map"]));
CKey key;
typedef std::map<gamemap::location,unit> units_map;
units_map units;
std::vector<team> teams;
std::vector<config*>& unit_cfg = level->children["side"];
for(std::vector<config*>::iterator ui = unit_cfg.begin();
ui != unit_cfg.end(); ++ui) {
unit new_unit(gameinfo, **ui);
if(ui == unit_cfg.begin()) {
for(std::vector<unit>::iterator it =
state_of_game.available_units.begin();
it != state_of_game.available_units.end(); ++it) {
if(it->can_recruit()) {
new_unit = *it;
state_of_game.available_units.erase(it);
break;
}
}
}
std::string gold = (*ui)->values["gold"];
if(gold.empty())
gold = "100";
int ngold = ::atoi(gold.c_str());
if(ui == unit_cfg.begin() && state_of_game.gold >= 0)
ngold = state_of_game.gold;
units.insert(std::pair<gamemap::location,unit>(
map.starting_position(new_unit.side()), new_unit));
teams.push_back(team(**ui,ngold));
//if there are additional starting units on this side
std::vector<config*>& starting_units = (*ui)->children["unit"];
for(std::vector<config*>::iterator su = starting_units.begin();
su != starting_units.end(); ++su) {
unit new_unit(gameinfo,**su);
const std::string& x = (*su)->values["x"];
const std::string& y = (*su)->values["y"];
const gamemap::location loc(**su);
if(x.size() == 0 || y.size() == 0 || !map.on_board(loc)) {
state_of_game.available_units.push_back(new_unit);
} else {
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
}
}
}
display gui(units,video,map,status,teams);
const preferences::display_manager prefs_disp_manager(&gui);
if(recorder.skipping() == false) {
for(std::vector<config*>::iterator story_i = story.begin();
story_i != story.end(); ++story_i) {
show_intro(gui,**story_i);
}
show_map_scene(gui,*level);
}
const std::string& music = level->values["music"];
if(!music.empty()) {
sound::play_music(music);
}
game_events::manager events_manager(*level,gui,map,units,
state_of_game,gameinfo);
//find a list of 'items' (i.e. overlays) on the level, and add them
std::vector<config*>& overlays = level->children["item"];
for(std::vector<config*>::iterator overlay = overlays.begin();
overlay != overlays.end(); ++overlay) {
gui.add_overlay(gamemap::location(**overlay),
(*overlay)->values["image"]);
}
const double scroll_speed = 30.0;
const double zoom_amount = 5.0;
for(units_map::iterator i = units.begin(); i != units.end(); ++i) {
i->second.new_turn();
}
bool left_button = false, right_button = false;
gamemap::location selected_hex;
gui.scroll_to_tile(map.starting_position(1).x,map.starting_position(1).y,
display::WARP);
bool replaying = (recorder.empty() == false);
std::cout << "starting main loop\n";
for(bool first_time = true; true; first_time = false) {
try {
if(first_time) {
update_locker lock_display(gui,recorder.skipping());
game_events::fire("start");
}
gui.invalidate_game_status();
for(std::vector<team>::iterator team_it = teams.begin();
team_it != teams.end(); ++team_it) {
const int player_number = (team_it - teams.begin()) + 1;
calculate_healing(gui,map,units,player_number);
//scroll the map to the leader
const units_map::iterator leader =
find_leader(units,player_number);
if(leader != units.end() && !recorder.skipping()) {
gui.scroll_to_tile(leader->first.x,leader->first.y);
}
if(replaying) {
replaying = do_replay(gui,map,gameinfo,units,teams,
player_number,status,state_of_game);
}
if(!replaying && team_it->is_human()) {
play_turn(gameinfo,state_of_game,status,terrain_config,
level, video, key, gui, events_manager, map,
teams, player_number, units);
if(game_config::debug)
display::clear_debug_highlights();
} else if(!replaying) {
ai::do_move(gui,map,gameinfo,units,teams,
player_number,status);
gui.invalidate_unit();
gui.invalidate_game_status();
gui.invalidate_all();
gui.draw();
SDL_Delay(1000);
}
for(units_map::iterator uit = units.begin();
uit != units.end(); ++uit) {
if(uit->second.side() == player_number)
uit->second.end_turn();
}
game_events::pump();
const int victory = check_victory(units);
if(victory > 1) {
throw end_level_exception(DEFEAT);
} else if(victory == 1) {
throw end_level_exception(VICTORY);
}
}
//time has run out
if(!status.next_turn()) {
game_events::fire("time over");
throw end_level_exception(DEFEAT);
}
std::stringstream event_stream;
event_stream << "turn " << status.turn();
{
update_locker lock_display(gui,recorder.skipping());
game_events::fire(event_stream.str());
}
std::map<int,int> expenditure;
for(units_map::iterator i = units.begin();
i != units.end(); ++i) {
i->second.new_turn();
expenditure[i->second.side()]++;
}
int team_num = 1;
for(std::vector<team>::iterator it = teams.begin();
it != teams.end(); ++it, ++team_num) {
it->new_turn();
it->spend_gold(expenditure[team_num]);
}
} catch(end_level_exception& end_level) {
if(end_level.result == QUIT || end_level.result == REPLAY) {
return end_level.result;
} else if(end_level.result == DEFEAT) {
try {
game_events::fire("defeat");
} catch(end_level_exception&) {
}
gui::show_dialog(gui,NULL,
string_table["defeat_heading"],
string_table["defeat_message"],
gui::OK_ONLY);
return DEFEAT;
} else if(end_level.result == VICTORY) {
try {
game_events::fire("victory");
} catch(end_level_exception&) {
}
//add all the units that survived the scenario
for(std::map<gamemap::location,unit>::iterator un =
units.begin(); un != units.end(); ++un) {
if(un->second.side() == 1) {
un->second.new_level();
state_of_game.available_units.
push_back(un->second);
}
}
const int remaining_gold = teams[0].gold();
const int finishing_bonus_per_turn =
map.towers().size()*game_config::tower_income;
const int turns_left = status.number_of_turns() - status.turn();
const int finishing_bonus = end_level.gold_bonus ?
(finishing_bonus_per_turn * turns_left) : 0;
state_of_game.gold = (remaining_gold+finishing_bonus)/2;
gui::show_dialog(gui,NULL,string_table["victory_heading"],
string_table["victory_message"],gui::OK_ONLY);
std::stringstream report;
report << string_table["remaining_gold"] << ": "
<< remaining_gold << "\n";
if(end_level.gold_bonus) {
report << string_table["early_finish_bonus"] << ": "
<< finishing_bonus_per_turn
<< " " << string_table["per_turn"] << "\n"
<< string_table["turns_finished_early"] << ": "
<< string_table["bonus"] << ": "
<< finishing_bonus << "\n"
<< string_table["gold"] << ": "
<< (remaining_gold+finishing_bonus);
}
report << "\n" << string_table["fifty_percent"] << "\n"
<< string_table["retained_gold"] << ": "
<< state_of_game.gold;
gui::show_dialog(gui,NULL,"",report.str(),gui::OK_ONLY);
return VICTORY;
}
} //end catch
catch(replay::error& e) {
gui::show_dialog(gui,NULL,"","The file you loaded is corrupt "
"or from a different version of the game",gui::OK_ONLY);
return DEFEAT;
}
} //end for(;;)
return QUIT;
}

View file

@ -1,52 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef PLAY_LEVEL_HPP_INCLUDED
#define PLAY_LEVEL_HPP_INCLUDED
#include "actions.hpp"
#include "ai.hpp"
#include "config.hpp"
#include "dialogs.hpp"
#include "display.hpp"
#include "game_config.hpp"
#include "gamestatus.hpp"
#include "key.hpp"
#include "menu.hpp"
#include "pathfind.hpp"
#include "team.hpp"
#include "unit_types.hpp"
#include "unit.hpp"
#include "video.hpp"
#include <cmath>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
enum LEVEL_RESULT { VICTORY, DEFEAT, QUIT, REPLAY };
struct end_level_exception {
end_level_exception(LEVEL_RESULT res, bool bonus=true)
: result(res), gold_bonus(bonus)
{}
LEVEL_RESULT result;
bool gold_bonus;
};
LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config,
config* level, CVideo& video,
game_state& state_of_game,
std::vector<config*>& story);
#endif

View file

@ -1,838 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "hotkeys.hpp"
#include "language.hpp"
#include "playlevel.hpp"
#include "playturn.hpp"
#include "preferences.hpp"
#include "replay.hpp"
#include <cmath>
#include <deque>
#include <iostream>
#include <sstream>
#include <string>
struct undo_action {
undo_action(const std::vector<gamemap::location>& rt,int sm,int orig=-1)
: route(rt), starting_moves(sm), original_village_owner(orig) {}
std::vector<gamemap::location> route;
int starting_moves;
int original_village_owner;
};
void play_turn(game_data& gameinfo, game_state& state_of_game,
gamestatus& status, config& terrain_config, config* level,
CVideo& video, CKey& key, display& gui,
game_events::manager& events_manager, gamemap& map,
std::vector<team>& teams, int team_num,
std::map<gamemap::location,unit>& units)
{
gui.set_team(team_num-1);
gui.invalidate_all();
gui.draw();
gui.update_display();
team& current_team = teams[team_num-1];
const double scroll_speed = 30.0;
const double zoom_amount = 5.0;
const std::string menu_items[] = {"scenario_objectives","recruit",
"recall","unit_list","save_game",
"preferences","end_turn"};
std::vector<std::string> menu;
for(const std::string* menu_items_ptr = menu_items;
menu_items_ptr != menu_items + sizeof(menu_items)/sizeof(*menu_items);
++menu_items_ptr) {
menu.push_back(string_table[*menu_items_ptr]);
}
typedef std::map<gamemap::location,unit> units_map;
gamemap::location next_unit;
bool left_button = false, right_button = false;
const paths_wiper wiper(gui);
paths current_paths;
bool enemy_paths = false;
gamemap::location last_hex;
gamemap::location selected_hex;
std::deque<undo_action> undo_stack, redo_stack;
for(;;) {
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;
gamemap::location new_hex = gui.hex_clicked_on(mousex,mousey);
//highlight the hex that is currently moused over
if(new_hex != last_hex) {
gui.highlight_hex(new_hex);
if(enemy_paths && new_hex != last_hex) {
gui.set_paths(NULL);
current_paths = paths();
enemy_paths = false;
}
}
HOTKEY_COMMAND command = HOTKEY_NULL;
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);
}
} else if(new_hex != last_hex && current_paths.routes.empty()) {
const units_map::iterator u = units.find(new_hex);
if(u != units.end() && u->second.side() != team_num) {
const bool ignore_zocs = u->second.type().is_skirmisher();
const bool teleport = u->second.type().teleports();
current_paths = paths(map,gameinfo,units,new_hex,teams,
ignore_zocs,teleport);
gui.set_paths(&current_paths);
enemy_paths = true;
}
}
last_hex = new_hex;
if(!left_button && new_left_button) {
const gamemap::location& hex = gui.hex_clicked_on(mousex,mousey);
units_map::iterator u = units.find(selected_hex);
//if we can move to that tile
const std::map<gamemap::location,paths::route>::const_iterator
route = enemy_paths ? current_paths.routes.end() :
current_paths.routes.find(hex);
units_map::iterator enemy = units.find(hex);
//see if we're trying to attack an enemy
if(route != current_paths.routes.end() && enemy != units.end() &&
hex != selected_hex &&
enemy->second.side() != u->second.side()) {
const unit_type& type = u->second.type();
const unit_type& enemy_type = enemy->second.type();
const std::vector<attack_type>& attacks = u->second.attacks();
std::vector<std::string> items;
const std::vector<attack_type>& defends =
enemy->second.attacks();
std::vector<unit> units_list;
for(int a = 0; a != attacks.size(); ++a) {
const battle_stats stats = evaluate_battle_stats(
map,selected_hex,hex,
a,units,status,gameinfo);
const std::string& lang_attack_name =
string_table["weapon_name_"+stats.attack_name];
const std::string& lang_attack_type =
string_table["weapon_type_"+stats.attack_type];
const std::string& lang_range =
string_table[stats.range == "Melee" ?
"short_range" : "long_range"];
const std::string& lang_defend_name =
string_table["weapon_name_"+stats.defend_name];
const std::string& lang_defend_type =
string_table["weapon_type_"+stats.defend_type];
const std::string& attack_name = lang_attack_name.empty() ?
stats.attack_name : lang_attack_name;
const std::string& attack_type = lang_attack_type.empty() ?
stats.attack_type : lang_attack_type;
const std::string& defend_name = lang_defend_name.empty() ?
stats.defend_name : lang_defend_name;
const std::string& defend_type = lang_defend_type.empty() ?
stats.defend_type : lang_defend_type;
const std::string& range = lang_range.empty() ?
stats.range : lang_range;
std::stringstream att;
att << attack_name << " (" << attack_type
<< ") " << stats.damage_defender_takes << "-"
<< stats.nattacks << " " << range << " "
<< int(ceil(100.0*stats.chance_to_hit_defender)) << "%";
att << "," << string_table["versus"] << ",";
att << defend_name << " (" << defend_type
<< ") " << stats.damage_attacker_takes << "-"
<< stats.ndefends << " "
<< int(ceil(100.0*stats.chance_to_hit_attacker)) << "%";
items.push_back(att.str());
units_list.push_back(enemy->second);
}
//make it so that when we attack an enemy, the attacking unit
//is again shown in the status bar, so that we can easily
//compare between the attacking and defending unit
gui.highlight_hex(gamemap::location());
gui.draw(true,true);
const int res = gui::show_dialog(gui,NULL,"",
string_table["choose_weapon"]+":\n",
gui::OK_CANCEL,&items,&units_list);
if(res >= 0 && res < attacks.size()) {
undo_stack.clear();
redo_stack.clear();
current_paths = paths();
gui.set_paths(NULL);
game_events::fire("attack",selected_hex,hex);
//the event could have killed either the attacker or
//defender, so we have to make sure they still exist
u = units.find(selected_hex);
enemy = units.find(hex);
if(u == units.end() || enemy == units.end() ||
res >= u->second.attacks().size())
continue;
gui.invalidate_all();
gui.draw();
recorder.add_attack(selected_hex,hex,res);
attack(gui,map,selected_hex,hex,res,units,
status,gameinfo,true);
dialogs::advance_unit(gameinfo,units,selected_hex,gui);
dialogs::advance_unit(gameinfo,units,hex,gui);
selected_hex = gamemap::location();
gui.invalidate_unit();
gui.draw(); //clear the screen
const int winner = check_victory(units);
//if the player has won
if(winner == team_num) {
throw end_level_exception(VICTORY);
} else if(winner >= 1) {
throw end_level_exception(DEFEAT);
}
}
}
//otherwise we're just trying to move to a hex
else if(selected_hex.valid() && selected_hex != hex &&
enemy == units.end() &&
route != current_paths.routes.end()) {
std::vector<gamemap::location> steps = route->second.steps;
steps.push_back(hex);
//if an event mutates the game environment, then this
//move must be marked un-redoable
bool event_mutated = false;
int orig_tower_owner = -1;
int starting_moves = 0;
int unit_side = 0;
if(u != units.end()) {
unit un = u->second;
starting_moves = un.movement_left();
unit_side = un.side();
units.erase(u);
gui.move_unit(steps,un);
recorder.add_movement(selected_hex,hex);
un.set_movement(route->second.move_left);
//see if the unit has gotten a tower
if(map[hex.x][hex.y] == gamemap::TOWER) {
orig_tower_owner = tower_owner(hex,teams);
get_tower(hex,teams,team_num-1);
un.set_movement(0); //end of turn if you get a tower
}
units.insert(std::pair<gamemap::location,unit>(hex,un));
gui.invalidate_unit();
//fire the move to event. If the event mutates
//the game state, then the user cannot undo the move
event_mutated = game_events::fire("moveto",hex);
} else {
assert(false);
continue;
}
selected_hex = gamemap::location();
gui.select_hex(gamemap::location());
gui.set_paths(NULL);
current_paths = paths();
//if there is an enemy in a surrounding hex, then
//highlight attack options
gamemap::location adj[6];
get_adjacent_tiles(steps.back(),adj);
int n;
for(n = 0; n != 6; ++n) {
const units_map::const_iterator u_it = units.find(adj[n]);
if(u_it != units.end() && u_it->second.side() != unit_side
&& current_team.is_enemy(u_it->second.side())){
current_paths.routes[adj[n]] = paths::route();
}
}
if(current_paths.routes.empty() == false) {
current_paths.routes[steps.back()] = paths::route();
selected_hex = steps.back();
gui.select_hex(steps.back());
gui.set_paths(&current_paths);
}
undo_stack.push_back(undo_action(steps,starting_moves,
orig_tower_owner));
redo_stack.clear();
if(event_mutated) {
undo_stack.clear();
}
} else {
gui.set_paths(NULL);
current_paths = paths();
selected_hex = hex;
gui.select_hex(hex);
const units_map::iterator it = units.find(hex);
if(it != units.end() && it->second.side() == team_num) {
const bool ignore_zocs = it->second.type().is_skirmisher();
const bool teleport = it->second.type().teleports();
current_paths = paths(map,gameinfo,units,hex,teams,
ignore_zocs,teleport);
gui.set_paths(&current_paths);
}
}
}
left_button = new_left_button;
if(!right_button && new_right_button) {
if(!current_paths.routes.empty()) {
selected_hex = gamemap::location();
gui.select_hex(gamemap::location());
gui.set_paths(NULL);
current_paths = paths();
} else {
const units_map::const_iterator un = units.find(
gui.hex_clicked_on(mousex,mousey));
if(un != units.end()) {
menu.push_back(string_table["describe_unit"]);
}
const int res = gui::show_dialog(gui,NULL,"",
string_table["options"]+":\n",
gui::MESSAGE,&menu);
const std::string result = res != -1 ? menu[res] : "";
if(un != units.end()) {
menu.pop_back();
}
if(result == string_table["describe_unit"]) {
const std::string description
= un->second.type().unit_description() +
"\n\n" + string_table["see_also"];
std::vector<std::string> options;
options.push_back(string_table["terrain_info"]);
options.push_back(string_table["attack_resistance"]);
options.push_back(string_table["close_window"]);
std::vector<unit> units_list(options.size(),un->second);
SDL_Surface* const unit_image =
gui.getImage(un->second.type().image_profile(),
display::UNSCALED);
const int res = gui::show_dialog(gui,unit_image,
un->second.type().language_name(),
description,gui::MESSAGE,&options,&units_list);
//terrain table
if(res == 0) {
command = HOTKEY_TERRAIN_TABLE;
}
//attack resistance table
else if(res == 1) {
command = HOTKEY_ATTACK_RESISTANCE;
}
}
else if(result == string_table["preferences"]) {
preferences::show_preferences_dialog(gui);
}
else if(result == string_table["end_turn"]) {
recorder.end_turn();
return;
}
else if(result == string_table["scenario_objectives"]) {
static const std::string no_objectives(
string_table["no_objectives"]);
const std::string& id = level->values["id"];
const std::string& lang_objectives =
string_table[id + "_objectives"];
const std::string& objectives = lang_objectives.empty() ?
level->values["objectives"] : lang_objectives;
gui::show_dialog(
gui, NULL, "",
objectives.empty() ? no_objectives : objectives,
gui::OK_ONLY);
}
else if(result == string_table["recall"]) {
//sort the available units into order by value
//so that the most valuable units are shown first
std::sort(state_of_game.available_units.begin(),
state_of_game.available_units.end(),
compare_unit_values());
gui.draw(); //clear the old menu
if(state_of_game.available_units.empty()) {
gui::show_dialog(gui,NULL,"",string_table["no_recall"]);
} else if(current_team.gold() < game_config::recall_cost) {
std::stringstream msg;
msg << string_table["not_enough_gold_to_recall_1"]
<< " " << game_config::recall_cost << " "
<< string_table["not_enough_gold_to_recall_2"];
gui::show_dialog(gui, NULL,"",msg.str());
} else {
std::vector<std::string> options;
for(std::vector<unit>::const_iterator unit =
state_of_game.available_units.begin();
unit != state_of_game.available_units.end(); ++unit) {
std::stringstream option;
option << unit->type().language_name() << ","
<< string_table["level"] << ": "
<< unit->type().level() << ","
<< string_table["xp"] << ": "
<< unit->experience() << "/"
<< unit->max_experience();
options.push_back(option.str());
}
const int res = gui::show_dialog(gui,NULL,"",
string_table["select_unit"] + ":\n",
gui::OK_CANCEL,&options,
&state_of_game.available_units);
if(res >= 0) {
const std::string err = recruit_unit(map,team_num,
units,state_of_game.available_units[res],
last_hex,&gui);
if(!err.empty()) {
gui::show_dialog(gui,NULL,"",err,gui::OK_ONLY);
} else {
current_team.spend_gold(
game_config::recall_cost);
state_of_game.available_units.erase(
state_of_game.available_units.begin()+res);
recorder.add_recall(res,last_hex);
undo_stack.clear();
redo_stack.clear();
gui.invalidate_game_status();
}
}
}
}
else if(result == string_table["recruit"]) {
std::vector<unit> sample_units;
gui.draw(); //clear the old menu
std::vector<std::string> item_keys;
std::vector<std::string> items;
const std::set<std::string>& recruits
= current_team.recruits();
for(std::set<std::string>::const_iterator it =
recruits.begin(); it != recruits.end(); ++it) {
item_keys.push_back(*it);
const std::map<std::string,unit_type>::const_iterator
u_type = gameinfo.unit_types.find(*it);
if(u_type == gameinfo.unit_types.end()) {
std::cerr << "could not find " << *it << std::endl;
assert(false);
continue;
}
const unit_type& type = u_type->second;
std::stringstream description;
description << type.language_name() << ": "
<< type.cost() << " gold";
items.push_back(description.str());
sample_units.push_back(unit(&type,team_num));
}
const int recruit_res =
gui::show_dialog(gui,NULL,"",
string_table["recruit_unit"] + ":\n",
gui::OK_CANCEL,&items,&sample_units);
if(recruit_res != -1) {
const std::string& name = item_keys[recruit_res];
const std::map<std::string,unit_type>::const_iterator
u_type = gameinfo.unit_types.find(name);
assert(u_type != gameinfo.unit_types.end());
if(u_type->second.cost() > current_team.gold()) {
gui::show_dialog(gui,NULL,"",
string_table["not_enough_gold_to_recruit"],
gui::OK_ONLY);
} else {
recorder.add_recruit(recruit_res,last_hex);
//create a unit with traits
unit new_unit(&(u_type->second),team_num,true);
const std::string& msg =
recruit_unit(map,team_num,units,new_unit,
last_hex,&gui);
if(msg.empty())
current_team.spend_gold(u_type->second.cost());
else
gui::show_dialog(gui,NULL,"",msg,gui::OK_ONLY);
undo_stack.clear();
redo_stack.clear();
gui.invalidate_game_status();
}
}
} else if(result == string_table["unit_list"]) {
const std::string heading = string_table["name"] + "," +
string_table["hp"] + "," +
string_table["xp"] + "," +
string_table["moves"] + "," +
string_table["location"];
std::vector<std::string> items;
items.push_back(heading);
std::vector<unit> units_list;
for(units_map::const_iterator i = units.begin();
i != units.end(); ++i) {
if(i->second.side() != team_num)
continue;
std::stringstream row;
row << i->second.name() << "," << i->second.hitpoints()
<< "/" << i->second.max_hitpoints() << ","
<< i->second.experience() << "/"
<< i->second.max_experience() << ","
<< i->second.movement_left() << "/"
<< i->second.total_movement() << ","
<< i->first.x << "-" << i->first.y;
items.push_back(row.str());
//extra unit for the first row to make up the heading
if(units_list.empty())
units_list.push_back(i->second);
units_list.push_back(i->second);
}
gui::show_dialog(gui,NULL,string_table["unit_list"],"",
gui::OK_ONLY,&items,&units_list);
} else if(result == string_table["save_game"]) {
std::stringstream stream;
stream << string_table["scenario"]
<< " " << (state_of_game.scenario+1)
<< " " << string_table["turn"]
<< " " << status.turn();
std::string label = stream.str();
const int res = gui::show_dialog(gui, NULL, "", "",
gui::OK_CANCEL,NULL,NULL,
string_table["save_game_label"],
&label);
if(res == 0) {
recorder.save_game(gameinfo,label);
gui::show_dialog(gui,NULL,"",
string_table["save_confirm_message"], gui::OK_ONLY);
}
}
}
}
right_button = new_right_button;
if(key[KEY_UP] || mousey == 0)
gui.scroll(0.0,-scroll_speed);
if(key[KEY_DOWN] || mousey == gui.y()-1)
gui.scroll(0.0,scroll_speed);
if(key[KEY_LEFT] || mousex == 0)
gui.scroll(-scroll_speed,0.0);
if(key[KEY_RIGHT] || mousex == gui.x()-1)
gui.scroll(scroll_speed,0.0);
if(command == HOTKEY_NULL)
command = check_keys(gui);
units_map::const_iterator un = units.find(new_hex);
if(un == units.end())
un = units.find(selected_hex);
if(un != units.end() && command == HOTKEY_ATTACK_RESISTANCE) {
gui.draw();
std::vector<std::string> items;
items.push_back(string_table["attack_type"] + "," +
string_table["attack_resistance"]);
const std::map<std::string,std::string>& table =
un->second.type().movement_type().damage_table();
for(std::map<std::string,std::string>::const_iterator i
= table.begin(); i != table.end(); ++i) {
double resistance = atof(i->second.c_str());
//if resistance is less than 0, display in red
std::string prefix = "";
if(resistance > 1.0) {
prefix = "#";
}
const int resist=int(100.0-ceil(100.0*resistance));
std::stringstream str;
str << i->first << "," << prefix << resist << "%";
items.push_back(str.str());
}
const std::vector<unit> units_list(items.size(),
un->second);
SDL_Surface* const unit_image =
gui.getImage(un->second.type().image_profile(),display::UNSCALED);
gui::show_dialog(gui,unit_image,
un->second.type().language_name(),
"Unit resistance table",
gui::MESSAGE,&items,&units_list);
}
if(un != units.end() && command == HOTKEY_TERRAIN_TABLE) {
gui.draw();
std::vector<std::string> items;
items.push_back(string_table["terrain"] + "," +
string_table["movement"] + "," +
string_table["defense"]);
const unit_type& type = un->second.type();
const unit_movement_type& move_type =
type.movement_type();
const std::vector<gamemap::TERRAIN>& terrains =
map.get_terrain_precedence();
for(std::vector<gamemap::TERRAIN>::const_iterator t =
terrains.begin(); t != terrains.end(); ++t) {
const terrain_type& info = map.get_terrain_info(*t);
if(!info.is_alias()) {
const std::string& name = map.terrain_name(*t);
const std::string& lang_name = string_table[name];
const int moves = move_type.movement_cost(map,*t);
const double defense = move_type.defense_modifier(map,*t);
const int def = int(100.0-ceil(100.0*defense));
std::stringstream str;
str << lang_name << ",";
if(moves < 10)
str << moves;
else
str << "--";
str << "," << def << "%";
items.push_back(str.str());
}
}
const std::vector<unit> units_list(items.size(),un->second);
SDL_Surface* const unit_image =
gui.getImage(un->second.type().image_profile(),display::UNSCALED);
gui::show_dialog(gui,unit_image,un->second.type().language_name(),
string_table["terrain_info"],
gui::MESSAGE,&items,&units_list);
}
if(command == HOTKEY_END_UNIT_TURN) {
const units_map::iterator un = units.find(selected_hex);
if(un != units.end() && un->second.side() == team_num &&
un->second.movement_left() > 0) {
std::vector<gamemap::location> steps;
steps.push_back(selected_hex);
undo_stack.push_back(undo_action(
steps,un->second.movement_left(),-1));
redo_stack.clear();
un->second.set_movement(0);
gui.draw_tile(selected_hex.x,selected_hex.y);
gui.set_paths(NULL);
current_paths = paths();
recorder.add_movement(selected_hex,selected_hex);
command = HOTKEY_CYCLE_UNITS;
}
}
//look for the next unit that is unmoved on our side
if(command == HOTKEY_CYCLE_UNITS) {
units_map::const_iterator it = units.find(next_unit);
if(it != units.end()) {
for(++it; it != units.end(); ++it) {
if(it->second.side() == team_num &&
it->second.movement_left() > 0) {
break;
}
}
}
if(it == units.end()) {
for(it = units.begin(); it != units.end();
++it) {
if(it->second.side() == team_num &&
it->second.movement_left() > 0) {
break;
}
}
}
if(it != units.end()) {
const bool ignore_zocs =
it->second.type().is_skirmisher();
const bool teleport = it->second.type().teleports();
current_paths = paths(map,gameinfo,units,
it->first,teams,ignore_zocs,teleport);
gui.set_paths(&current_paths);
gui.scroll_to_tile(it->first.x,it->first.y,
display::WARP);
}
if(it != units.end()) {
next_unit = it->first;
selected_hex = next_unit;
gui.select_hex(selected_hex);
} else
next_unit = gamemap::location();
}
if(command == HOTKEY_LEADER) {
for(units_map::const_iterator i = units.begin(); i != units.end();
++i) {
if(i->second.side() == team_num && i->second.can_recruit()) {
gui.scroll_to_tile(i->first.x,i->first.y,display::WARP);
break;
}
}
}
//undo
if(command == HOTKEY_UNDO && !undo_stack.empty()) {
const int starting_moves = undo_stack.back().starting_moves;
std::vector<gamemap::location> route = undo_stack.back().route;
std::reverse(route.begin(),route.end());
const units_map::iterator u = units.find(route.front());
if(u == units.end()) {
assert(false);
continue;
}
if(map[route.front().x][route.front().y] == gamemap::TOWER) {
get_tower(route.front(),teams,
undo_stack.back().original_village_owner);
}
undo_stack.back().starting_moves = u->second.movement_left();
unit un = u->second;
units.erase(u);
gui.move_unit(route,un);
un.set_movement(starting_moves);
units.insert(std::pair<gamemap::location,unit>(route.back(),un));
gui.invalidate_unit();
gui.draw_tile(route.back().x,route.back().y);
redo_stack.push_back(undo_stack.back());
undo_stack.pop_back();
gui.set_paths(NULL);
current_paths = paths();
recorder.undo();
}
if(command == HOTKEY_REDO && !redo_stack.empty()) {
const int starting_moves = redo_stack.back().starting_moves;
std::vector<gamemap::location> route = redo_stack.back().route;
const units_map::iterator u = units.find(route.front());
if(u == units.end()) {
assert(false);
continue;
}
redo_stack.back().starting_moves = u->second.movement_left();
unit un = u->second;
units.erase(u);
gui.move_unit(route,un);
un.set_movement(starting_moves);
units.insert(std::pair<gamemap::location,unit>(route.back(),un));
gui.invalidate_unit();
recorder.add_movement(route.front(),route.back());
if(map[route.back().x][route.back().y] == gamemap::TOWER) {
get_tower(route.back(),teams,un.side()-1);
}
gui.draw_tile(route.back().x,route.back().y);
undo_stack.push_back(redo_stack.back());
redo_stack.pop_back();
}
gui.draw();
game_events::pump();
}
}

View file

@ -1,53 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef PLAYTURN_HPP_INCLUDED
#define PLAYTURN_HPP_INCLUDED
#include "actions.hpp"
#include "ai.hpp"
#include "config.hpp"
#include "dialogs.hpp"
#include "display.hpp"
#include "game_config.hpp"
#include "game_events.hpp"
#include "gamestatus.hpp"
#include "key.hpp"
#include "menu.hpp"
#include "pathfind.hpp"
#include "team.hpp"
#include "unit_types.hpp"
#include "unit.hpp"
#include "video.hpp"
#include <map>
#include <vector>
struct paths_wiper
{
paths_wiper(display& gui) : gui_(gui)
{}
~paths_wiper() { gui_.set_paths(NULL); }
private:
display& gui_;
};
void play_turn(game_data& gameinfo, game_state& state_of_game,
gamestatus& status, config& terrain_config, config* level,
CVideo& video, CKey& key, display& gui,
game_events::manager& events_manager, gamemap& map,
std::vector<team>& teams, int team_num,
std::map<gamemap::location,unit>& units);
#endif

View file

@ -1,393 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "filesystem.hpp"
#include "font.hpp"
#include "language.hpp"
#include "menu.hpp"
#include "preferences.hpp"
#include "sound.hpp"
#include "util.hpp"
#include "widgets/button.hpp"
#include "widgets/slider.hpp"
#include <cstdlib>
#include <sstream>
namespace {
config prefs;
display* disp;
}
namespace preferences {
manager::manager()
{
prefs.read(read_file(get_prefs_file()));
set_music_volume(music_volume());
set_sound_volume(sound_volume());
}
manager::~manager()
{
write_file(get_prefs_file(),prefs.write());
}
display_manager::display_manager(display* d)
{
disp = d;
set_grid(grid());
set_turbo(turbo());
set_fullscreen(fullscreen());
}
display_manager::~display_manager()
{
disp = NULL;
}
bool fullscreen()
{
const string_map::const_iterator fullscreen =
prefs.values.find("fullscreen");
return fullscreen == prefs.values.end() || fullscreen->second == "true";
}
void set_fullscreen(bool ison)
{
prefs.values["fullscreen"] = (ison ? "true" : "false");
if(disp != NULL) {
const std::pair<int,int>& res = resolution();
CVideo& video = disp->video();
if(video.isFullScreen() != ison) {
const int flags = ison ? FULL_SCREEN : 0;
if(video.modePossible(res.first,res.second,16,flags)) {
video.setMode(res.first,res.second,16,flags);
disp->redraw_everything();
} else {
gui::show_dialog(*disp,NULL,"",string_table["video_mode_fail"],
gui::MESSAGE);
}
}
}
}
std::pair<int,int> resolution()
{
const string_map::const_iterator x = prefs.values.find("xresolution");
const string_map::const_iterator y = prefs.values.find("yresolution");
if(x != prefs.values.end() && y != prefs.values.end() &&
x->second.empty() == false && y->second.empty() == false) {
return std::pair<int,int>(maximum(atoi(x->second.c_str()),1024),
maximum(atoi(y->second.c_str()),768));
} else {
return std::pair<int,int>(1024,768);
}
}
void set_resolution(const std::pair<int,int>& res)
{
if(disp != NULL) {
CVideo& video = disp->video();
const int flags = video.isFullScreen() ? FULL_SCREEN : 0;
if(video.modePossible(res.first,res.second,16,flags)) {
video.setMode(res.first,res.second,16,flags);
disp->redraw_everything();
char buf[50];
sprintf(buf,"%d",res.first);
prefs.values["xresolution"] = buf;
sprintf(buf,"%d",res.second);
prefs.values["yresolution"] = buf;
} else {
gui::show_dialog(*disp,NULL,"",string_table["video_mode_fail"],
gui::MESSAGE);
}
}
}
bool turbo()
{
const string_map::const_iterator turbo = prefs.values.find("turbo");
return turbo != prefs.values.end() && turbo->second == "true";
}
void set_turbo(bool ison)
{
prefs.values["turbo"] = (ison ? "true" : "false");
if(disp != NULL) {
disp->set_turbo(ison);
}
}
const std::string& locale()
{
return prefs.values["locale"];
}
void set_locale(const std::string& s)
{
prefs.values["locale"] = s;
}
double music_volume()
{
static const double default_value = 1.0;
const string_map::const_iterator volume = prefs.values.find("music_volume");
if(volume != prefs.values.end() && volume->second.empty() == false)
return atof(volume->second.c_str());
else
return default_value;
}
void set_music_volume(double vol)
{
std::stringstream stream;
stream << vol;
prefs.values["music_volume"] = stream.str();
sound::set_music_volume(vol);
}
double sound_volume()
{
static const double default_value = 1.0;
const string_map::const_iterator volume = prefs.values.find("sound_volume");
if(volume != prefs.values.end() && volume->second.empty() == false)
return atof(volume->second.c_str());
else
return default_value;
}
void set_sound_volume(double vol)
{
std::stringstream stream;
stream << vol;
prefs.values["sound_volume"] = stream.str();
sound::set_sound_volume(vol);
}
bool grid()
{
const string_map::const_iterator turbo = prefs.values.find("grid");
return turbo != prefs.values.end() && turbo->second == "true";
}
void set_grid(bool ison)
{
prefs.values["grid"] = (ison ? "true" : "false");
if(disp != NULL) {
disp->set_grid(ison);
}
}
void show_preferences_dialog(display& disp)
{
const int border_size = 6;
const int xpos = disp.x()/2 - 300;
const int ypos = disp.y()/2 - 200;
const int width = 600;
const int height = 400;
disp.invalidate_all();
disp.draw();
SDL_Rect clip_rect = {0,0,disp.x(),disp.y()};
SDL_Rect title_rect = font::draw_text(NULL,clip_rect,16,font::NORMAL_COLOUR,
string_table["preferences"],0,0);
gui::button close_button(disp,string_table["close_window"]);
close_button.set_x(xpos + width/2 - close_button.width()/2);
close_button.set_y(ypos + height - close_button.height()-14);
const std::string& music_label = string_table["music_volume"];
const std::string& sound_label = string_table["sound_volume"];
SDL_Rect music_rect = {0,0,0,0};
music_rect = font::draw_text(NULL,clip_rect,14,font::NORMAL_COLOUR,
music_label,0,0);
SDL_Rect sound_rect = {0,0,0,0};
sound_rect = font::draw_text(NULL,clip_rect,14,font::NORMAL_COLOUR,
sound_label,0,0);
const int text_right = xpos + maximum(music_rect.w,sound_rect.w) + 5;
const int music_pos = ypos + title_rect.h + 20;
const int sound_pos = music_pos + 50;
music_rect.x = text_right - music_rect.w;
music_rect.y = music_pos;
sound_rect.x = text_right - sound_rect.w;
sound_rect.y = sound_pos;
const int slider_left = text_right + 10;
const int slider_right = xpos + width - 5;
if(slider_left >= slider_right)
return;
SDL_Rect slider_rect = { slider_left,sound_pos,slider_right-slider_left,10};
gui::slider sound_slider(disp,slider_rect,sound_volume());
slider_rect.y = music_pos;
gui::slider music_slider(disp,slider_rect,music_volume());
gui::button fullscreen_button(disp,string_table["full_screen"],
gui::button::TYPE_CHECK);
fullscreen_button.set_check(fullscreen());
fullscreen_button.set_x(slider_left);
fullscreen_button.set_y(sound_pos + 80);
gui::button turbo_button(disp,string_table["speed_turbo"],
gui::button::TYPE_CHECK);
turbo_button.set_check(turbo());
turbo_button.set_x(slider_left);
turbo_button.set_y(sound_pos + 80 + 50);
gui::button grid_button(disp,string_table["grid_button"],
gui::button::TYPE_CHECK);
grid_button.set_check(grid());
grid_button.set_x(slider_left);
grid_button.set_y(sound_pos + 80 + 100);
gui::button resolution_button(disp,"Video Mode");
resolution_button.set_x(slider_left);
resolution_button.set_y(sound_pos + 80 + 150);
bool redraw_all = true;
for(;;) {
int mousex, mousey;
const int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
const bool left_button = mouse_flags&SDL_BUTTON_LMASK;
const bool right_button = mouse_flags&SDL_BUTTON_RMASK;
if(close_button.process(mousex,mousey,left_button)) {
break;
}
const double new_music=music_slider.process(mousex,mousey,left_button);
const double new_sound=sound_slider.process(mousex,mousey,left_button);
if(new_sound >= 0.0) {
set_sound_volume(new_sound);
}
if(new_music >= 0.0) {
set_music_volume(new_music);
}
if(fullscreen_button.process(mousex,mousey,left_button)) {
set_fullscreen(fullscreen_button.checked());
redraw_all = true;
}
if(redraw_all) {
gui::draw_dialog_frame(xpos,ypos,width,height,disp);
sound_slider.background_changed();
music_slider.background_changed();
sound_slider.draw();
music_slider.draw();
fullscreen_button.draw();
turbo_button.draw();
grid_button.draw();
close_button.draw();
resolution_button.draw();
font::draw_text(&disp,clip_rect,14,font::NORMAL_COLOUR,music_label,
music_rect.x,music_rect.y);
font::draw_text(&disp,clip_rect,14,font::NORMAL_COLOUR,sound_label,
sound_rect.x,sound_rect.y);
font::draw_text(&disp,clip_rect,18,font::NORMAL_COLOUR,
string_table["preferences"],
xpos+(width-title_rect.w)/2,ypos+10);
redraw_all = false;
}
if(turbo_button.process(mousex,mousey,left_button)) {
set_turbo(turbo_button.checked());
}
if(grid_button.process(mousex,mousey,left_button)) {
set_grid(grid_button.checked());
}
if(resolution_button.process(mousex,mousey,left_button)) {
show_video_mode_dialog(disp);
break;
}
disp.update_display();
SDL_Delay(10);
SDL_PumpEvents();
}
disp.invalidate_all();
disp.draw();
}
void show_video_mode_dialog(display& disp)
{
std::vector<std::pair<int,int> > resolutions;
std::vector<std::string> options;
CVideo& video = disp.video();
SDL_Rect** modes = SDL_ListModes(video.getSurface()->format,FULL_SCREEN);
if(reinterpret_cast<int>(modes) == -1 || modes == NULL)
return;
for(int i = 0; modes[i] != NULL; ++i) {
if(modes[i]->w >= 1024 && modes[i]->h >= 768) {
const std::pair<int,int> new_res(modes[i]->w,modes[i]->h);
if(std::count(resolutions.begin(),resolutions.end(),new_res) > 0)
continue;
resolutions.push_back(new_res);
std::stringstream option;
option << modes[i]->w << "x" << modes[i]->h;
options.push_back(option.str());
}
}
if(resolutions.size() < 2)
return;
const int result = gui::show_dialog(disp,NULL,"","Choose Resolution",
gui::MESSAGE,&options);
if(result >= 0 && result < resolutions.size()) {
set_resolution(resolutions[result]);
}
}
}

View file

@ -1,61 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef PREFERENCES_HPP_INCLUDED
#define PREFERENCES_HPP_INCLUDED
#include "config.hpp"
#include "display.hpp"
#include <string>
#include <utility>
namespace preferences {
struct manager
{
manager();
~manager();
};
struct display_manager
{
display_manager(display* disp);
~display_manager();
};
bool fullscreen();
void set_fullscreen(bool ison);
std::pair<int,int> resolution();
void set_resolution(const std::pair<int,int>& res);
bool turbo();
void set_turbo(bool ison);
const std::string& locale();
void set_locale(const std::string& s);
double music_volume();
void set_music_volume(double vol);
double sound_volume();
void set_sound_volume(double vol);
bool grid();
void set_grid(bool ison);
void show_preferences_dialog(display& disp);
void show_video_mode_dialog(display& disp);
}
#endif

View file

@ -1,483 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "actions.hpp"
#include "ai.hpp"
#include "ai_attack.hpp"
#include "ai_move.hpp"
#include "dialogs.hpp"
#include "game_config.hpp"
#include "log.hpp"
#include "menu.hpp"
#include "pathfind.hpp"
#include "playlevel.hpp"
#include "playturn.hpp"
#include "replay.hpp"
#include <cstdio>
#include <cstdlib>
#include <deque>
#include <iostream>
#include <sstream>
replay recorder;
replay::replay() : pos_(0), current_(NULL), skip_(0)
{}
replay::replay(config& cfg) : cfg_(cfg), pos_(0), current_(NULL), skip_(0)
{}
config& replay::get_config()
{
return cfg_;
}
void replay::set_save_info(const game_state& save)
{
saveInfo_ = save;
}
const game_state& replay::get_save_info() const
{
return saveInfo_;
}
void replay::set_skip(int turns_to_skip)
{
skip_ = turns_to_skip;
}
void replay::next_skip()
{
if(skip_ > 0)
--skip_;
}
bool replay::skipping() const
{
return skip_ != 0;
}
void replay::save_game(game_data& data, const std::string& label)
{
saveInfo_.replay_data = cfg_;
saveInfo_.label = label;
::save_game(saveInfo_);
saveInfo_.replay_data = config();
}
void replay::add_recruit(int value, const gamemap::location& loc)
{
config* const cmd = add_command();
config* const val = new config();
char buf[100];
sprintf(buf,"%d",value);
val->values["value"] = buf;
sprintf(buf,"%d",loc.x+1);
val->values["x"] = buf;
sprintf(buf,"%d",loc.y+1);
val->values["y"] = buf;
cmd->children["recruit"].push_back(val);
}
void replay::add_recall(int value, const gamemap::location& loc)
{
config* const cmd = add_command();
config* const val = new config();
char buf[100];
sprintf(buf,"%d",value);
val->values["value"] = buf;
sprintf(buf,"%d",loc.x+1);
val->values["x"] = buf;
sprintf(buf,"%d",loc.y+1);
val->values["y"] = buf;
cmd->children["recall"].push_back(val);
}
void replay::add_movement(const gamemap::location& a,const gamemap::location& b)
{
add_pos("move",a,b);
current_ = NULL;
}
void replay::add_attack(const gamemap::location& a, const gamemap::location& b,
int weapon)
{
add_pos("attack",a,b);
char buf[100];
sprintf(buf,"%d",weapon);
current_->children["attack"][0]->values["weapon"] = buf;
}
void replay::add_pos(const std::string& type,
const gamemap::location& a, const gamemap::location& b)
{
config* const cmd = add_command();
config* const move = new config();
config* const src = new config();
config* const dst = new config();
char buf[100];
sprintf(buf,"%d",a.x+1);
src->values["x"] = buf;
sprintf(buf,"%d",a.y+1);
src->values["y"] = buf;
sprintf(buf,"%d",b.x+1);
dst->values["x"] = buf;
sprintf(buf,"%d",b.y+1);
dst->values["y"] = buf;
move->children["source"].push_back(src);
move->children["destination"].push_back(dst);
cmd->children[type].push_back(move);
current_ = cmd;
}
void replay::add_value(const std::string& type, int value)
{
config* const cmd = add_command();
config* const val = new config();
char buf[100];
sprintf(buf,"%d",value);
val->values["value"] = buf;
cmd->children[type].push_back(val);
}
void replay::choose_option(int index)
{
add_value("choose",index);
}
void replay::end_turn()
{
config* const cmd = add_command();
cmd->children["end_turn"].push_back(new config());
}
void replay::undo()
{
std::vector<config*>& cmd = commands();
if(!cmd.empty()) {
delete cmd.back();
cmd.pop_back();
}
}
std::vector<config*>& replay::commands()
{
return cfg_.children["command"];
}
config* replay::add_command()
{
config* const cmd = new config();
commands().push_back(cmd);
current_ = cmd;
return cmd;
}
int replay::get_random()
{
if(current_ == NULL) {
throw error();
}
//random numbers are in a 'list' meaning that each random
//number contains another random numbers unless it's at
//the end of the list. Generating a new random number means
//nesting a new node inside the current node, and making
//the current node the new node
std::vector<config*>& random = current_->children["random"];
if(random.empty()) {
const int res = rand();
char buf[100];
sprintf(buf,"%d",res);
current_ = new config();
current_->values["value"] = buf;
random.push_back(current_);
return res;
} else {
const int res = atol(random.front()->values["value"].c_str());
current_ = random.front();
return res;
}
}
void replay::start_replay()
{
pos_ = 0;
}
config* replay::get_next_action()
{
if(pos_ >= commands().size())
return NULL;
current_ = commands()[pos_];
++pos_;
return current_;
}
void replay::clear()
{
cfg_ = config();
pos_ = 0;
current_ = NULL;
skip_ = 0;
}
bool replay::empty()
{
return commands().empty();
}
bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
std::map<gamemap::location,unit>& units,
std::vector<team>& teams, int team_num, const gamestatus& state,
game_state& state_of_game)
{
update_locker lock_update(disp,recorder.skipping());
//a list of units that have promoted from the last attack
std::deque<gamemap::location> advancing_units;
team& current_team = teams[team_num-1];
for(;;) {
config* const cfg = recorder.get_next_action();
std::map<std::string,std::vector<config*> >::iterator it;
//if we are expecting promotions here
if(advancing_units.empty() == false) {
if(cfg == NULL ||
(it = cfg->children.find("choose")) == cfg->children.end()) {
std::cerr << "promotion expected, but none found\n";
throw replay::error();
}
const std::map<gamemap::location,unit>::iterator u =
units.find(advancing_units.front());
assert(u != units.end());
const std::string& num = it->second.front()->values["value"];
const int val = atoi(num.c_str());
const std::vector<std::string>& options =
u->second.type().advances_to();
if(val < 0 || val >= options.size()) {
std::cerr << "illegal advancement type\n";
throw replay::error();
}
advance_unit(gameinfo,units,advancing_units.front(),options[val]);
advancing_units.pop_front();
}
//if there is nothing more in the records
else if(cfg == NULL) {
recorder.set_skip(0);
return false;
}
//if there is an end turn directive
else if(cfg->children.find("end_turn") != cfg->children.end()) {
recorder.next_skip();
return true;
}
else if((it = cfg->children.find("recruit")) != cfg->children.end()) {
assert(!it->second.empty());
const std::string& recruit_num=it->second.front()->values["value"];
const int val = atoi(recruit_num.c_str());
const gamemap::location loc(*(it->second.front()));
const std::set<std::string>& recruits = current_team.recruits();
std::set<std::string>::const_iterator itor = recruits.begin();
std::advance(itor,val);
const std::map<std::string,unit_type>::const_iterator u_type =
gameinfo.unit_types.find(*itor);
if(u_type == gameinfo.unit_types.end())
throw replay::error();
unit new_unit(&(u_type->second),team_num,true);
recruit_unit(map,team_num,units,new_unit,loc);
current_team.spend_gold(u_type->second.cost());
}
else if((it = cfg->children.find("recall")) != cfg->children.end()) {
std::sort(state_of_game.available_units.begin(),
state_of_game.available_units.end(),
compare_unit_values());
assert(!it->second.empty());
const std::string recall_num = it->second.front()->values["value"];
const int val = atoi(recall_num.c_str());
const gamemap::location loc(*(it->second.front()));
recruit_unit(map,team_num,units,
state_of_game.available_units[val],loc);
state_of_game.available_units.erase(
state_of_game.available_units.begin()+val);
current_team.spend_gold(game_config::recall_cost);
}
else if((it = cfg->children.find("move")) != cfg->children.end()) {
assert(!it->second.empty());
config* const move = it->second.front();
assert(!move->children["destination"].empty());
assert(!move->children["source"].empty());
const gamemap::location src(*(move->children["source"][0]));
const gamemap::location dst(*(move->children["destination"][0]));
const std::map<gamemap::location,unit>::iterator u=units.find(src);
if(u == units.end()) {
std::cerr << "unfound location for source of movement: "
<< src.x << "," << src.y << "-"
<< dst.x << "," << dst.y << "\n";
throw replay::error();
}
const bool ignore_zocs = u->second.type().is_skirmisher();
const bool teleport = u->second.type().teleports();
paths paths_list(map,gameinfo,units,src,teams,ignore_zocs,teleport);
paths_wiper wiper(disp);
if(!recorder.skipping()) {
disp.set_paths(&paths_list);
disp.scroll_to_tiles(src.x,src.y,dst.x,dst.y);
}
unit current_unit = u->second;
units.erase(u);
std::map<gamemap::location,paths::route>::iterator rt =
paths_list.routes.find(dst);
if(rt == paths_list.routes.end()) {
std::cerr << "src cannot get to dst: " << paths_list.routes.size() << "\n";
throw replay::error();
}
rt->second.steps.push_back(dst);
if(!recorder.skipping())
disp.move_unit(rt->second.steps,current_unit);
current_unit.set_movement(rt->second.move_left);
units.insert(std::pair<gamemap::location,unit>(dst,current_unit));
if(map[dst.x][dst.y] == gamemap::TOWER) {
current_unit.set_movement(0);
get_tower(dst,teams,team_num-1);
}
if(!recorder.skipping()) {
disp.draw_tile(dst.x,dst.y);
disp.update_display();
}
game_events::fire("moveto",dst);
}
else if((it = cfg->children.find("attack")) != cfg->children.end()) {
assert(!it->second.empty());
config* const move = it->second.front();
assert(!move->children["destination"].empty());
assert(!move->children["source"].empty());
const gamemap::location src(*(move->children["source"][0]));
const gamemap::location dst(*(move->children["destination"][0]));
const std::string& weapon = move->values["weapon"];
const int weapon_num = atoi(weapon.c_str());
std::map<gamemap::location,unit>::iterator u=units.find(src);
if(u == units.end()) {
std::cerr << "unfound location for source of attack\n";
throw replay::error();
}
if(weapon_num < 0 || weapon_num >= u->second.attacks().size()) {
std::cerr << "illegal weapon type in attack\n";
throw replay::error();
}
std::map<gamemap::location,unit>::const_iterator tgt =
units.find(dst);
if(tgt == units.end()) {
std::cerr << "unfound defender for attack\n";
throw replay::error();
}
game_events::fire("attack",src,dst);
u = units.find(src);
tgt = units.find(dst);
if(u != units.end() && tgt != units.end()) {
attack(disp,map,src,dst,weapon_num,units,state,gameinfo,false);
const int res = check_victory(units);
if(res == 1)
throw end_level_exception(VICTORY);
else if(res > 1)
throw end_level_exception(DEFEAT);
}
u = units.find(src);
tgt = units.find(dst);
if(u != units.end() && u->second.advances() &&
u->second.type().advances_to().empty() == false) {
advancing_units.push_back(u->first);
}
if(tgt != units.end() && tgt->second.advances() &&
tgt->second.type().advances_to().empty() == false) {
advancing_units.push_back(tgt->first);
}
} else {
std::cerr << "unrecognized action\n";
throw replay::error();
}
}
}

View file

@ -1,85 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef REPLAY_H_INCLUDED
#define REPLAY_H_INCLUDED
#include "config.hpp"
#include "display.hpp"
#include "map.hpp"
class replay
{
public:
replay();
replay(config& cfg);
config& get_config();
void set_save_info(const game_state& save);
const game_state& get_save_info() const;
void set_skip(int turns_to_skip);
void next_skip();
bool skipping() const;
void save_game(game_data& data, const std::string& label);
void add_recruit(int unit_index, const gamemap::location& loc);
void add_recall(int unit_index, const gamemap::location& loc);
void add_movement(const gamemap::location& a, const gamemap::location& b);
void add_attack(const gamemap::location& a, const gamemap::location& b,
int weapon);
void choose_option(int index);
void end_turn();
void undo();
int get_random();
void start_replay();
config* get_next_action();
void clear();
bool empty();
struct error {};
private:
//generic for add_movement and add_attack
void add_pos(const std::string& type,
const gamemap::location& a, const gamemap::location& b);
void add_value(const std::string& type, int value);
std::vector<config*>& commands();
config* add_command();
config cfg_;
unsigned int pos_;
config* current_;
game_state saveInfo_;
int skip_;
};
extern replay recorder;
//replays up to one turn from the recorder object
//returns true if it got to the end of the turn without data running out
bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
std::map<gamemap::location,unit>& units,
std::vector<team>& teams, int team_num, const gamestatus& state,
game_state& state_of_game);
#endif

View file

@ -1,178 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef SCOPED_RESOURCE_H_INCLUDED
#define SCOPED_RESOURCE_H_INCLUDED
/**
* The util namespace should take all classes which are of a generic type,
* used to perform common tasks which are not BibleTime-specific. See
* @ref scoped_resource for an example.
*/
namespace util
{
/**
* A class template, scoped_resource, designed to
* implement the Resource Acquisition Is Initialization (RAII) approach
* to resource management. scoped_resource is designed to be used when
* a resource is initialized at the beginning or middle of a scope,
* and released at the end of the scope. The template argument
* ReleasePolicy is a functor which takes an argument of the
* type of the resource, and releases it.
*
* Usage example, for working with files:
*
* @code
* struct close_file { void operator(int fd) const {close(fd);} };
* ...
* {
* const scoped_resource<int,close_file> file(open("file.txt",O_RDONLY));
* read(file, buf, 1000);
* } // file is automatically closed here
* @endcode
*
* Note that scoped_resource has an explicit constructor, and prohibits
* copy-construction, and thus the initialization syntax, rather than
* the assignment syntax must be used when initializing.
*
* i.e. using scoped_resource<int,close_file> file = open("file.txt",O_RDONLY);
* in the above example is illegal.
*
*/
template<typename T,typename ReleasePolicy>
class scoped_resource
{
T resource;
ReleasePolicy release;
//prohibited operations
scoped_resource(const scoped_resource&);
scoped_resource& operator=(const scoped_resource&);
public:
typedef T resource_type;
typedef ReleasePolicy release_type;
/**
* Constructor
*
* @ param res This is the resource to be managed
* @ param rel This is the functor to release the object
*/
scoped_resource(resource_type res,release_type rel=release_type())
: resource(res), release(rel) {}
/**
* The destructor is the main point in this class. It takes care of proper
* deletion of the resource, using the provided release policy.
*/
~scoped_resource()
{
release(resource);
}
/**
* This operator makes sure you can access and use the scoped_resource
* just like you were using the resource itself.
*
* @ret the underlying resource
*/
operator resource_type() const { return resource; }
/**
* This function provides explicit access to the resource. Its behaviour
* is identical to operator resource_type()
*
* @ret the underlying resource
*/
resource_type get() const { return resource; }
/**
* This function provides convenient direct access to the -> operator
* if the underlying resource is a pointer. Only call this function
* if resource_type is a pointer type.
*/
resource_type operator->() const { return resource; }
resource_type& assign(const resource_type& o) {
release(resource);
resource = o;
return resource;
}
};
/**
* A helper policy for scoped_ptr.
* It will call the delete operator on a pointer, and assign the pointer to 0
*/
struct delete_item {
template<typename T>
void operator()(T*& p) const { delete p; p = 0; }
};
/**
* A helper policy for scoped_array.
* It will call the delete[] operator on a pointer, and assign the pointer to 0
*/
struct delete_array {
template<typename T>
void operator()(T*& p) const { delete [] p; p = 0; }
};
/**
* A class which implements an approximation of
* template<typename T>
* typedef scoped_resource<T*,delete_item> scoped_ptr<T>;
*
* It is a convenient synonym for a common usage of @ref scoped_resource.
* See scoped_resource for more details on how this class behaves.
*
* Usage example:
* @code
* {
* const scoped_ptr<Object> ptr(new Object);
* ...use ptr as you would a normal Object*...
* } // ptr is automatically deleted here
* @endcode
*
* NOTE: use this class only to manage a single object, *never* an array.
* Use scoped_array to manage arrays. This distinction is because you
* may call delete only on objects allocated with new, delete[] only
* on objects allocated with new[].
*/
template<typename T>
struct scoped_ptr : public scoped_resource<T*,delete_item>
{
explicit scoped_ptr(T* p) : scoped_resource<T*,delete_item>(p) {}
};
/**
* This class has identical behaviour to @ref scoped_ptr, except it manages
* heap-allocated arrays instead of heap-allocated single objects
*
* Usage example:
* @code
* {
* const scoped_array<char> ptr(new char[n]);
* ...use ptr as you would a normal char*...
* } // ptr is automatically deleted here
* @endcode
*
*/
template<typename T>
struct scoped_array : public scoped_resource<T*,delete_array>
{
explicit scoped_array(T* p) : scoped_resource<T*,delete_array>(p) {}
};
}
#endif

View file

@ -1,69 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "sdl_utils.hpp"
SDL_Surface* scale_surface(SDL_Surface* surface, int w, int h)
{
SDL_Surface* const dest = SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,
surface->format->BitsPerPixel,
surface->format->Rmask,
surface->format->Gmask,
surface->format->Bmask,
surface->format->Amask);
if(dest == NULL)
return NULL;
const double xratio = static_cast<double>(surface->w)/
static_cast<double>(w);
const double yratio = static_cast<double>(surface->h)/
static_cast<double>(h);
const int srcxpad = (surface->w%2) == 1 ? 1:0;
const int dstxpad = (dest->w%2) == 1 ? 1:0;
double ysrc = 0.0;
for(int ydst = 0; ydst != h; ++ydst, ysrc += yratio) {
double xsrc = 0.0;
for(int xdst = 0; xdst != w; ++xdst, xsrc += xratio) {
int xsrcint = static_cast<int>(xsrc);
const int ysrcint = static_cast<int>(ysrc);
xsrcint += srcxpad*ysrcint;
const int dstpad = dstxpad*ydst;
reinterpret_cast<short*>(dest->pixels)[ydst*w + xdst + dstpad] =
reinterpret_cast<short*>(surface->pixels)[
ysrcint*surface->w + xsrcint];
}
}
return dest;
}
SDL_Surface* get_surface_portion(SDL_Surface* src, SDL_Rect& area)
{
if(area.x + area.w >= src->w || area.y + area.h >= src->h)
return NULL;
const SDL_PixelFormat* const fmt = src->format;
SDL_Surface* const dst = SDL_CreateRGBSurface(0,area.w,area.h,
fmt->BitsPerPixel,fmt->Rmask,
fmt->Gmask,fmt->Bmask,
fmt->Amask);
SDL_Rect dstarea = {0,0,0,0};
SDL_BlitSurface(src,&area,dst,&dstarea);
return dst;
}

View file

@ -1,84 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef SDL_UTILS_INCLUDED
#define SDL_UTILS_INCLUDED
#include "config.hpp"
#include "scoped_resource.hpp"
#include "SDL.h"
#include <cstdlib>
#include <string>
struct free_sdl_surface {
void operator()(SDL_Surface* surface) const { SDL_FreeSurface(surface); }
};
typedef util::scoped_resource<SDL_Surface*,free_sdl_surface> scoped_sdl_surface;
SDL_Surface* scale_surface(SDL_Surface* surface, int w, int h);
SDL_Surface* get_surface_portion(SDL_Surface* src, SDL_Rect& rect);
struct pixel_data
{
pixel_data() : r(0), g(0), b(0)
{}
pixel_data(int red, int green, int blue) : r(red), g(green), b(blue)
{}
pixel_data(int pixel, SDL_PixelFormat* fmt) {
unformat(pixel, fmt);
}
pixel_data(config& cfg) {
read(cfg);
}
int format(SDL_PixelFormat* fmt) const {
return SDL_MapRGB(fmt,r,g,b);
}
void unformat(int pixel, SDL_PixelFormat* fmt) {
r = ((pixel&fmt->Rmask) >> fmt->Rshift);
g = ((pixel&fmt->Gmask) >> fmt->Gshift);
b = ((pixel&fmt->Bmask) >> fmt->Bshift);
}
void read(config& cfg) {
const std::string& red = cfg.values["red"];
const std::string& green = cfg.values["green"];
const std::string& blue = cfg.values["blue"];
if(red.empty())
r = 0;
else
r = atoi(red.c_str());
if(green.empty())
g = 0;
else
g = atoi(green.c_str());
if(blue.empty())
b = 0;
else
b = atoi(blue.c_str());
}
int r, g, b;
};
#endif

196
sound.cpp
View file

@ -1,196 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "sound.hpp"
#include "SDL_mixer.h"
#include <iostream>
#include <map>
namespace {
bool mix_ok = false;
std::map<std::string,Mix_Chunk*> sound_cache;
std::map<std::string,Mix_Music*> music_cache;
std::string current_music = "";
bool music_off = false;
bool sound_off = false;
}
namespace sound {
manager::manager()
{
const int res =
Mix_OpenAudio(MIX_DEFAULT_FREQUENCY,MIX_DEFAULT_FORMAT,2,1024);
if(res >= 0) {
mix_ok = true;
Mix_AllocateChannels(8);
} else {
mix_ok = false;
std::cerr << "Could not initialize audio: " << SDL_GetError() << "\n";
}
}
manager::~manager()
{
if(!mix_ok)
return;
Mix_HaltMusic();
Mix_HaltChannel(-1);
for(std::map<std::string,Mix_Chunk*>::iterator i = sound_cache.begin();
i != sound_cache.end(); ++i) {
Mix_FreeChunk(i->second);
}
for(std::map<std::string,Mix_Music*>::iterator j = music_cache.begin();
j != music_cache.end(); ++j) {
Mix_FreeMusic(j->second);
}
Mix_CloseAudio();
}
void play_music(const std::string& file)
{
if(!mix_ok || current_music == file)
return;
if(music_off) {
current_music = file;
return;
}
std::map<std::string,Mix_Music*>::const_iterator itor =
music_cache.find(file);
if(itor == music_cache.end()) {
static const std::string music_prefix = "music/";
std::string filename;
Mix_Music* music = NULL;
#ifdef WESNOTH_PATH
filename = WESNOTH_PATH + std::string("/") + music_prefix + file;
music = Mix_LoadMUS(filename.c_str());
#endif
if(music == NULL) {
filename = music_prefix + file;
music = Mix_LoadMUS(filename.c_str());
}
if(music == NULL) {
std::cerr << "Could not load music file '" << filename << "': "
<< SDL_GetError() << "\n";
return;
}
itor = music_cache.insert(std::pair<std::string,Mix_Music*>(
file,music)).first;
}
if(Mix_PlayingMusic()) {
Mix_FadeOutMusic(500);
}
const int res = Mix_FadeInMusic(itor->second,-1,500);
if(res < 0) {
std::cerr << "Could not play music: " << SDL_GetError() << "\n";
}
current_music = file;
}
void play_sound(const std::string& file)
{
if(!mix_ok || sound_off)
return;
std::map<std::string,Mix_Chunk*>::const_iterator itor =
sound_cache.find(file);
if(itor == sound_cache.end()) {
static const std::string sound_prefix = "sounds/";
std::string filename;
Mix_Chunk* sfx = NULL;
#ifdef WESNOTH_PATH
filename = WESNOTH_PATH + std::string("/") + sound_prefix + file;
sfx = Mix_LoadWAV(filename.c_str());
#endif
if(sfx == NULL) {
filename = sound_prefix + file;
sfx = Mix_LoadWAV(filename.c_str());
}
if(sfx == NULL) {
std::cerr << "Could not load sound file '" << filename << "': "
<< SDL_GetError() << "\n";
return;
}
itor = sound_cache.insert(std::pair<std::string,Mix_Chunk*>(
file,sfx)).first;
}
//play on the first available channel
const int res = Mix_PlayChannel(-1,itor->second,0);
if(res < 0) {
std::cerr << "error playing sound effect: " << SDL_GetError() << "\n";
}
}
void set_music_volume(double vol)
{
if(!mix_ok)
return;
if(vol < 0.05) {
Mix_HaltMusic();
music_off = true;
return;
}
Mix_VolumeMusic(int(vol*double(MIX_MAX_VOLUME)));
//if the music was off completely, start playing it again now
if(music_off) {
music_off = false;
const std::string music = current_music;
current_music = "";
if(!music.empty()) {
play_music(music);
}
}
}
void set_sound_volume(double vol)
{
if(!mix_ok)
return;
if(vol < 0.05) {
sound_off = true;
return;
} else {
sound_off = false;
Mix_Volume(-1,int(vol*double(MIX_MAX_VOLUME)));
}
}
}

View file

@ -1,33 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef SOUND_HPP_INCLUDED
#define SOUND_HPP_INCLUDED
#include <string>
namespace sound {
struct manager {
manager();
~manager();
};
void play_music(const std::string& file);
void play_sound(const std::string& file);
void set_music_volume(double vol);
void set_sound_volume(double vol);
}
#endif

178
team.cpp
View file

@ -1,178 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "game_config.hpp"
#include "replay.hpp"
#include "team.hpp"
#include <algorithm>
#include <cstdlib>
team::target::target(config& cfg)
: criteria(cfg), value(atof(cfg.values["value"].c_str()))
{
}
team::team_info::team_info(config& cfg)
{
gold = cfg.values["gold"];
name = cfg.values["name"];
aggression = atof(cfg.values["aggression"].c_str());
if(aggression == 0.0)
aggression = 0.5;
const std::string& enemies_list = cfg.values["enemy"];
if(!enemies_list.empty()) {
std::vector<std::string> venemies = config::split(enemies_list);
for(std::vector<std::string>::const_iterator i = venemies.begin();
i != venemies.end(); ++i) {
enemies.push_back(atoi(i->c_str()));
}
}
if(cfg.values["controller"] == "human")
human = true;
else
human = false;
const std::string& leader_val = cfg.values["leader_value"];
if(leader_val.empty()) {
leader_value = 3.0;
} else {
leader_value = atof(leader_val.c_str());
}
const std::string& village_val = cfg.values["village_value"];
if(village_val.empty()) {
village_value = 1.0;
} else {
village_value = atof(village_val.c_str());
}
std::vector<std::string> recruits = config::split(cfg.values["recruit"]);
for(std::vector<std::string>::const_iterator i = recruits.begin();
i != recruits.end(); ++i) {
can_recruit.insert(*i);
}
recruitment_pattern = config::split(cfg.values["recruitment_pattern"]);
//default recruitment pattern is to buy 2 fighters for every 1 archer
if(recruitment_pattern.empty()) {
recruitment_pattern.push_back("fighter");
recruitment_pattern.push_back("fighter");
recruitment_pattern.push_back("archer");
}
//additional targets
std::vector<config*>& tgts = cfg.children["target"];
for(std::vector<config*>::iterator tgt = tgts.begin();
tgt != tgts.end(); ++tgt) {
targets.push_back(target(**tgt));
}
}
team::team(config& cfg, int gold) : gold_(gold), info_(cfg)
{
if(info_.gold.empty() == false)
gold_ = ::atoi(info_.gold.c_str());
}
void team::get_tower(const gamemap::location& loc)
{
towers_.insert(loc);
}
void team::lose_tower(const gamemap::location& loc)
{
towers_.erase(towers_.find(loc));
}
int team::towers() const
{
return towers_.size();
}
bool team::owns_tower(const gamemap::location& loc) const
{
return towers_.count(loc) > 0;
}
int team::gold() const
{
return gold_;
}
int team::income() const
{
return towers_.size()*game_config::tower_income+game_config::base_income;
}
void team::new_turn()
{
gold_ += income();
}
void team::spend_gold(int amount)
{
gold_ -= amount;
}
const std::set<std::string>& team::recruits() const
{
return info_.can_recruit;
}
const std::vector<std::string>& team::recruitment_pattern() const
{
return info_.recruitment_pattern;
}
const std::string& team::name() const
{
return info_.name;
}
bool team::is_enemy(int n) const
{
//if enemies aren't listed, then everyone is an enemy
if(info_.enemies.empty())
return true;
return std::find(info_.enemies.begin(),info_.enemies.end(),n) !=
info_.enemies.end();
}
double team::aggression() const
{
return info_.aggression;
}
bool team::is_human() const
{
return info_.human;
}
double team::leader_value() const
{
return info_.leader_value;
}
double team::village_value() const
{
return info_.village_value;
}
std::vector<team::target>& team::targets()
{
return info_.targets;
}

View file

@ -1,81 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef TEAM_H_INCLUDED
#define TEAM_H_INCLUDED
#include "config.hpp"
#include "map.hpp"
#include <cassert>
#include <set>
#include <string>
#include <vector>
class team
{
public:
struct target {
explicit target(config& cfg);
config criteria;
double value;
};
struct team_info
{
team_info(config& cfg);
std::string name;
std::string gold;
std::set<std::string> can_recruit;
std::vector<std::string> recruitment_pattern;
double aggression;
std::vector<int> enemies;
bool human;
double leader_value, village_value;
std::vector<target> targets;
};
team(config& cfg, int gold=100);
void get_tower(const gamemap::location&);
void lose_tower(const gamemap::location&);
int towers() const;
bool owns_tower(const gamemap::location&) const;
int gold() const;
int income() const;
void new_turn();
void spend_gold(int amount);
const std::set<std::string>& recruits() const;
const std::vector<std::string>& recruitment_pattern() const;
const std::string& name() const;
bool is_enemy(int side) const;
double aggression() const;
bool is_human() const;
double leader_value() const;
double village_value() const;
std::vector<target>& targets();
private:
int gold_;
std::set<gamemap::location> towers_;
team_info info_;
};
#endif

View file

@ -1,81 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "terrain.hpp"
#include <cassert>
#include <cstdlib>
terrain_type::terrain_type() : image_("void"), type_(' '), letter_(' ')
{}
terrain_type::terrain_type(config& cfg)
{
image_ = cfg.values["image"];
name_ = cfg.values["name"];
const std::string& letter = cfg.values["char"];
assert(!letter.empty());
letter_ = letter[0];
const std::string& alias = cfg.values["aliasof"];
if(alias.empty())
type_ = letter_;
else
type_ = alias[0];
colour_.read(cfg);
}
const std::string& terrain_type::image() const
{
return image_;
}
const std::string& terrain_type::name() const
{
return name_;
}
char terrain_type::letter() const
{
return letter_;
}
char terrain_type::type() const
{
return type_;
}
pixel_data terrain_type::get_rgb() const
{
return colour_;
}
bool terrain_type::is_alias() const
{
return type_ != letter_;
}
void create_terrain_maps(std::vector<config*>& cfgs,
std::vector<char>& terrain_precedence,
std::map<char,terrain_type>& letter_to_terrain,
std::map<std::string,terrain_type>& str_to_terrain)
{
for(std::vector<config*>::iterator i = cfgs.begin(); i != cfgs.end(); ++i) {
terrain_type terrain(**i);
terrain_precedence.push_back(terrain.letter());
letter_to_terrain.insert(std::pair<char,terrain_type>(
terrain.letter(),terrain));
str_to_terrain.insert(std::pair<std::string,terrain_type>(
terrain.name(),terrain));
}
}

View file

@ -1,52 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef TERRAIN_H_INCLUDED
#define TERRAIN_H_INCLUDED
#include "config.hpp"
#include "sdl_utils.hpp"
#include <string>
class terrain_type
{
public:
terrain_type();
terrain_type(config& cfg);
const std::string& image() const;
const std::string& name() const;
char letter() const;
char type() const;
pixel_data get_rgb() const;
bool is_alias() const;
private:
std::string image_, name_;
//the 'letter' is the letter that represents this
//terrain type. The 'type' is the letter of the
//terrain type which this is equivalent to, which
//may be the same as 'letter'
char type_, letter_;
pixel_data colour_;
};
void create_terrain_maps(std::vector<config*>& cfgs,
std::vector<char>& terrain_precedence,
std::map<char,terrain_type>& letter_to_terrain,
std::map<std::string,terrain_type>& str_to_terrain);
#endif

641
unit.cpp
View file

@ -1,641 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "game_config.hpp"
#include "gamestatus.hpp"
#include "replay.hpp"
#include "unit.hpp"
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <sstream>
namespace {
const std::string ModificationTypes[] = { "object", "trait" };
const int NumModificationTypes = sizeof(ModificationTypes)/
sizeof(*ModificationTypes);
}
bool compare_unit_values::operator()(const unit& a, const unit& b) const
{
const int lvla = a.type().level();
const int lvlb = b.type().level();
const std::string& namea = a.type().name();
const std::string& nameb = b.type().name();
const int xpa = a.max_experience() - a.experience();
const int xpb = b.max_experience() - b.experience();
return lvla > lvlb || lvla == lvlb && namea < nameb ||
lvla == lvlb && namea == nameb && xpa < xpb;
}
//constructor for reading a unit
unit::unit(game_data& data, config& cfg) : moves_(0), facingLeft_(true),
recruit_(false),
state_(STATE_NORMAL),
guardian_(false)
{
read(data,cfg);
}
//constructor for creating a new unit
unit::unit(const unit_type* t, int side, bool use_traits) :
type_(t), facingLeft_(side != 1), state_(STATE_NORMAL),
hitpoints_(t->hitpoints()),
experience_(0), side_(side), moves_(0),
recruit_(false), attacks_(t->attacks()),
backupAttacks_(t->attacks()),
maxHitpoints_(t->hitpoints()),
backupMaxHitpoints_(t->hitpoints()),
maxMovement_(t->movement()),
backupMaxMovement_(t->movement()),
maxExperience_(t->experience_needed()),
backupMaxExperience_(t->experience_needed()),
guardian_(false)
{
//calculate the unit's traits
std::vector<config*> traits = t->possible_traits();
const int num_traits = 2;
if(use_traits && traits.size() >= num_traits) {
std::set<int> chosen_traits;
for(int i = 0; i != num_traits; ++i) {
int num = recorder.get_random()%(traits.size()-i);
while(chosen_traits.count(num)) {
++num;
}
chosen_traits.insert(num);
add_modification("trait",*traits[num]);
}
//build the traits description, making sure the traits are always
//in the same order.
for(std::set<int>::const_iterator itor = chosen_traits.begin();
itor != chosen_traits.end(); ++itor) {
traitsDescription_ += traits[*itor]->values["name"];
traitsDescription_ += ",";
}
//get rid of the trailing comma
if(!traitsDescription_.empty())
traitsDescription_.resize(traitsDescription_.size()-1);
}
}
//constructor for advancing a unit from a lower level
unit::unit(const unit_type* t, const unit& u) :
type_(t), facingLeft_(u.facingLeft_), state_(STATE_NORMAL),
hitpoints_(t->hitpoints()),
experience_(0), side_(u.side()), moves_(u.moves_),
recruit_(u.recruit_), description_(u.description_),
role_(u.role_), statusFlags_(u.statusFlags_),
attacks_(t->attacks()), backupAttacks_(t->attacks()),
maxHitpoints_(t->hitpoints()),
backupMaxHitpoints_(t->hitpoints()),
maxMovement_(t->movement()),
backupMaxMovement_(t->movement()),
maxExperience_(t->experience_needed()),
backupMaxExperience_(t->experience_needed()),
modifications_(u.modifications_),
traitsDescription_(u.traitsDescription_),
guardian_(false)
{
//apply modifications etc, refresh the unit
new_level();
}
const unit_type& unit::type() const
{
return *type_;
}
std::string unit::name() const
{
if(description_.empty() == false)
return description_;
else
return type().language_name();
}
const std::string& unit::description() const
{
return description_;
}
int unit::side() const
{
return side_;
}
double unit::alpha() const
{
return type().alpha();
}
void unit::make_recruiter()
{
recruit_ = true;
}
bool unit::can_recruit() const
{
return recruit_;
}
int unit::total_movement() const
{
return maxMovement_;
}
int unit::movement_left() const
{
return moves_ < 0 ? 0 : moves_;
}
bool unit::can_attack() const
{
return moves_ != -1;
}
void unit::set_movement(int moves)
{
if(moves_ != -1)
moves_ = moves;
}
void unit::set_attacked()
{
moves_ = -1;
}
void unit::new_turn()
{
moves_ = total_movement();
if(type().has_ability("ambush"))
set_flag("ambush");
}
void unit::end_turn()
{
remove_flag("slowed");
}
void unit::new_level()
{
//revert stats to the beginning of the level
attacks_ = backupAttacks_;
maxHitpoints_ = backupMaxHitpoints_;
maxMovement_ = backupMaxMovement_;
maxExperience_ = backupMaxExperience_;
//reapply all permanent modifications
apply_modifications();
heal_all();
statusFlags_.clear();
}
int unit::hitpoints() const
{
return hitpoints_;
}
int unit::max_hitpoints() const
{
return maxHitpoints_;
}
int unit::experience() const
{
return experience_;
}
int unit::max_experience() const
{
return maxExperience_;
}
bool unit::get_experience(int xp)
{
experience_ += xp;
if(experience_ > max_experience())
experience_ = max_experience();
return advances();
}
bool unit::advances() const
{
return experience_ >= max_experience() && !type().advances_to().empty();
}
bool unit::gets_hit(int damage)
{
hitpoints_ -= damage;
if(hitpoints_ > max_hitpoints() && damage > 0)
hitpoints_ = max_hitpoints();
return hitpoints_ <= 0;
}
void unit::heal()
{
heal(game_config::heal_amount);
}
void unit::heal(int amount)
{
if(hitpoints_ < max_hitpoints()) {
hitpoints_ += amount;
if(hitpoints_ > max_hitpoints())
hitpoints_ = max_hitpoints();
}
}
void unit::heal_all()
{
hitpoints_ = max_hitpoints();
}
bool unit::invisible(gamemap::TERRAIN terrain) const
{
static const std::string forest_invisible("ambush");
if(terrain == gamemap::FOREST && has_flag(forest_invisible)) {
return true;
}
return false;
}
bool unit::matches_filter(config& cfg) const
{
const std::string& description = cfg.values["description"];
const std::string& type = cfg.values["type"];
const std::string& ability = cfg.values["ability"];
const std::string& side = cfg.values["side"];
const std::string& weapon = cfg.values["has_weapon"];
const std::string& role = cfg.values["role"];
if(description.empty() == false && description != this->description()) {
return false;
}
const std::string& this_type = this->type().name();
//the type could be a comma-seperated list of types
if(type.empty() == false && type != this_type) {
//we only do the full CSV search if we find a comma in there,
//and if the subsequence is found within the main sequence. This
//is because doing the full CSV split is expensive
if(std::find(type.begin(),type.end(),',') != type.end() &&
std::search(type.begin(),type.end(),this_type.begin(),this_type.end()) !=
type.end()) {
const std::vector<std::string>& vals = config::split(type);
if(std::find(vals.begin(),vals.end(),this_type) == vals.end()) {
return false;
}
} else {
return false;
}
}
if(ability.empty() == false && this->type().has_ability(ability) == false)
return false;
if(side.empty() == false && this->side() != atoi(side.c_str()))
return false;
if(weapon.empty() == false) {
bool has_weapon = false;
const std::vector<attack_type>& attacks = this->type().attacks();
for(std::vector<attack_type>::const_iterator i = attacks.begin();
i != attacks.end(); ++i) {
if(i->name() == weapon) {
has_weapon = true;
}
}
if(!has_weapon)
return false;
}
if(role.empty() == false && role_ != role)
return false;
return true;
}
void unit::set_flag(const std::string& flag)
{
statusFlags_.insert(flag);
}
void unit::remove_flag(const std::string& flag)
{
statusFlags_.erase(flag);
}
bool unit::has_flag(const std::string& flag) const
{
return statusFlags_.count(flag) != 0;
}
void unit::read(game_data& data, config& cfg)
{
std::map<std::string,unit_type>::iterator i = data.unit_types.find(
cfg.values["type"]);
if(i != data.unit_types.end())
type_ = &i->second;
else
throw gamestatus::load_game_failed("Unit not found: '"
+ cfg.values["type"] + "'");
attacks_ = type_->attacks();
backupAttacks_ = attacks_;
maxHitpoints_ = type_->hitpoints();
backupMaxHitpoints_ = type_->hitpoints();
maxMovement_ = type_->movement();
backupMaxMovement_ = type_->movement();
maxExperience_ = type_->experience_needed();
backupMaxExperience_ = type_->experience_needed();
const std::string& hitpoints = cfg.values["hitpoints"];
if(hitpoints.size() == 0)
hitpoints_ = type().hitpoints();
else
hitpoints_ = atoi(hitpoints.c_str());
const std::string& experience = cfg.values["experience"];
if(experience.size() == 0)
experience_ = 0;
else
experience_ = atoi(experience.c_str());
side_ = atoi(cfg.values["side"].c_str());
description_ = cfg.values["description"];
traitsDescription_ = cfg.values["traits_description"];
const std::map<std::string,std::string>::const_iterator recruit_itor =
cfg.values.find("canrecruit");
if(recruit_itor != cfg.values.end() && recruit_itor->second == "1") {
recruit_ = true;
}
const std::vector<config*>& mods = cfg.children["modifications"];
if(!mods.empty()) {
modifications_ = *mods.front();
apply_modifications();
}
const std::string& facing = cfg.values["facing"];
if(facing == "reverse")
facingLeft_ = false;
else
facingLeft_ = true;
const std::string& ai_special = cfg.values["ai_special"];
if(ai_special == "guardian") {
guardian_ = true;
}
}
void unit::write(config& cfg) const
{
cfg.values["type"] = type_->name();
std::stringstream hp;
hp << hitpoints_;
cfg.values["hitpoints"] = hp.str();
std::stringstream xp;
xp << experience_;
cfg.values["experience"] = xp.str();
std::stringstream sd;
sd << side_;
cfg.values["side"] = sd.str();
cfg.values["description"] = description_;
cfg.values["traits_description"] = traitsDescription_;
if(can_recruit())
cfg.values["canrecruit"] = "1";
cfg.children["modifications"].push_back(new config(modifications_));
cfg.values["facing"] = facingLeft_ ? "normal" : "reverse";
}
void unit::assign_role(const std::string& role)
{
role_ = role;
}
const std::vector<attack_type>& unit::attacks() const
{
return attacks_;
}
int unit::movement_cost(const gamemap& map, gamemap::TERRAIN terrain) const
{
const int res = type_->movement_type().movement_cost(map,terrain);
static const std::string slowed_string("slowed");
if(has_flag(slowed_string)) {
return res*2;
}
return res;
}
double unit::defense_modifier(const gamemap& map,
gamemap::TERRAIN terrain) const
{
return type_->movement_type().defense_modifier(map,terrain);
}
int unit::damage_against(const attack_type& attack) const
{
return type_->movement_type().damage_against(attack);
}
const std::string& unit::image() const
{
switch(state_) {
case STATE_NORMAL: return type_->image();
case STATE_DEFENDING: return type_->image_defensive();
case STATE_ATTACKING: {
if(attackType_ == NULL)
return type_->image();
const std::string* const img =
attackType_->get_frame(attackingMilliseconds_);
if(img == NULL)
return type_->image();
else
return *img;
}
default: return type_->image();
}
}
void unit::set_defending(bool newval)
{
state_ = newval ? STATE_DEFENDING : STATE_NORMAL;
}
void unit::set_attacking(bool newval, const attack_type* type, int ms)
{
state_ = newval ? STATE_ATTACKING : STATE_NORMAL;
attackType_ = type;
attackingMilliseconds_ = ms;
}
bool unit::facing_left() const
{
return facingLeft_;
}
void unit::set_facing_left(bool newval)
{
facingLeft_ = newval;
}
const std::string& unit::traits_description() const
{
return traitsDescription_;
}
int unit::value() const
{
return type().cost();
}
bool unit::is_guardian() const
{
return guardian_;
}
void unit::add_modification(const std::string& type, config& mod, bool no_add)
{
const std::string& span = mod.values["duration"];
if(no_add == false && (span.empty() || span == "forever"))
modifications_.children[type].push_back(new config(mod));
const std::vector<config*>& effects = mod.children["effect"];
for(std::vector<config*>::const_iterator i = effects.begin();
i != effects.end(); ++i) {
const std::string& apply_to = (*i)->values["apply_to"];
if(apply_to == "new_attack") {
attacks_.push_back(attack_type(**i));
} else if(apply_to == "attack") {
for(std::vector<attack_type>::iterator a = attacks_.begin();
a != attacks_.end(); ++a) {
a->apply_modification(**i);
}
} else if(apply_to == "hitpoints") {
const std::string& increase_hp = (*i)->values["increase"];
const std::string& heal_full = (*i)->values["heal_full"];
const std::string& increase_total = (*i)->values["increase_total"];
const std::string& mult_total = (*i)->values["multiply_total"];
//if the hitpoints are allowed to end up greater than max hitpoints
const std::string& violate_max = (*i)->values["violate_maximum"];
if(increase_total.empty() == false) {
const int increase = atoi(increase_total.c_str());
maxHitpoints_ += increase;
}
if(mult_total.empty() == false) {
const double factor = atoi(mult_total.c_str());
maxHitpoints_ = int(double(maxHitpoints_)*factor);
}
if(maxHitpoints_ < 1)
maxHitpoints_ = 1;
if(heal_full.empty() == false && heal_full != "no") {
heal_all();
}
if(increase_hp.empty() == false) {
const int increase = atoi(increase_hp.c_str());
hitpoints_ += increase;
}
if(hitpoints_ > maxHitpoints_ && violate_max.empty())
hitpoints_ = maxHitpoints_;
if(hitpoints_ < 1)
hitpoints_ = 1;
} else if(apply_to == "movement") {
const std::string& increase = (*i)->values["increase"];
const std::string& mult = (*i)->values["multiply"];
const std::string& set_to = (*i)->values["set"];
if(increase.empty() == false) {
maxMovement_ += atoi(increase.c_str());
if(maxMovement_ < 1)
maxMovement_ = 1;
}
if(mult.empty() == false) {
maxMovement_ = int(double(maxMovement_)*atof(mult.c_str()));
}
if(set_to.empty() == false) {
maxMovement_ = atoi(set_to.c_str());
}
if(moves_ > maxMovement_)
moves_ = maxMovement_;
} else if(apply_to == "max_experience") {
const std::string& increase = (*i)->values["increase"];
const std::string& multiply = (*i)->values["multiply"];
if(increase.empty() == false) {
maxExperience_ += atoi(increase.c_str());
}
if(multiply.empty() == false) {
maxExperience_ = int(double(maxExperience_)*
atof(multiply.c_str()));
}
if(maxExperience_ < 1) {
maxExperience_ = 1;
}
}
}
}
void unit::apply_modifications()
{
for(int i = 0; i != NumModificationTypes; ++i) {
const std::string& mod = ModificationTypes[i];
std::vector<config*>& mods = modifications_.children[mod];
for(std::vector<config*>::iterator j = mods.begin();
j != mods.end(); ++j) {
add_modification(ModificationTypes[i],**j,true);
}
}
}

144
unit.hpp
View file

@ -1,144 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef UNIT_H_INCLUDED
#define UNIT_H_INCLUDED
#include "config.hpp"
#include "map.hpp"
#include "unit_types.hpp"
#include <set>
#include <string>
#include <vector>
class unit
{
public:
unit(game_data& data, config& cfg);
unit(const unit_type* t, int side, bool use_traits=false);
//a constructor used when advancing a unit
unit(const unit_type* t, const unit& u);
const unit_type& type() const;
std::string name() const;
const std::string& description() const;
int hitpoints() const;
int max_hitpoints() const;
int experience() const;
int max_experience() const;
bool get_experience(int xp);
bool advances() const;
int side() const;
double alpha() const;
void make_recruiter();
bool can_recruit() const;
int total_movement() const;
int movement_left() const;
bool can_attack() const;
void set_movement(int moves);
void set_attacked();
void new_turn();
void end_turn();
void new_level();
bool gets_hit(int damage);
void heal();
void heal(int amount);
void heal_all();
bool invisible(gamemap::TERRAIN terrain) const;
bool matches_filter(config& cfg) const;
void set_flag(const std::string& flag);
void remove_flag(const std::string& flag);
bool has_flag(const std::string& flag) const;
void read(game_data& data, config& cfg);
void write(config& cfg) const;
void assign_role(const std::string& role);
const std::vector<attack_type>& attacks() const;
int movement_cost(const gamemap& map, gamemap::TERRAIN terrain) const;
double defense_modifier(const gamemap& map, gamemap::TERRAIN terrain) const;
int damage_against(const attack_type& attack) const;
//gets the unit image that should currently be displayed
//(could be in the middle of an attack etc)
const std::string& image() const;
void set_defending(bool newval);
void set_attacking(bool newval, const attack_type* type=NULL, int ms=0);
bool facing_left() const;
void set_facing_left(bool newval);
const std::string& traits_description() const;
int value() const;
bool is_guardian() const;
void add_modification(const std::string& type, config& modification,
bool no_add=false);
private:
const unit_type* type_;
bool facingLeft_;
enum STATE { STATE_NORMAL, STATE_ATTACKING, STATE_DEFENDING };
STATE state_;
const attack_type* attackType_;
int attackingMilliseconds_;
int hitpoints_;
int maxHitpoints_, backupMaxHitpoints_;
int experience_;
int maxExperience_, backupMaxExperience_;
int side_;
//is set to the number of moves left, and -1 if the unit has attacked
int moves_;
int maxMovement_, backupMaxMovement_;
std::string description_;
bool recruit_;
std::string role_;
std::set<std::string> statusFlags_;
std::vector<attack_type> attacks_;
std::vector<attack_type> backupAttacks_;
config modifications_;
std::string traitsDescription_;
bool guardian_;
void apply_modifications();
};
struct compare_unit_values
{
bool operator()(const unit& a, const unit& b) const;
};
#endif

View file

@ -1,581 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include "game_config.hpp"
#include "gamestatus.hpp"
#include "language.hpp"
#include "unit_types.hpp"
#include "util.hpp"
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <iostream>
//these headers are used to check for file existence
#ifdef linux
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
attack_type::attack_type(config& cfg)
{
name_ = cfg.values["name"];
type_ = cfg.values["type"];
special_ = cfg.values["special"];
range_ = cfg.values["range"] == "long" ? LONG_RANGE : SHORT_RANGE;
damage_ = atol(cfg.values["damage"].c_str());
num_attacks_ = atol(cfg.values["number"].c_str());
std::vector<config*>& frames = cfg.children["frame"];
std::vector<config*>::iterator i;
for(i = frames.begin(); i != frames.end(); ++i){
const int beg = atoi((*i)->values["begin"].c_str());
const int end = atoi((*i)->values["end"].c_str());
const std::string& img = (*i)->values["image"];
frames_[UNIT_FRAME].push_back(frame(beg,end,img));
}
std::vector<config*>& missile_frames = cfg.children["missile_frame"];
for(i = missile_frames.begin(); i != missile_frames.end(); ++i){
const int beg = atoi((*i)->values["begin"].c_str());
const int end = atoi((*i)->values["end"].c_str());
const std::string& img = (*i)->values["image"];
const std::string& img_diag = (*i)->values["image_diagonal"];
if(img_diag.empty())
frames_[MISSILE_FRAME].push_back(frame(beg,end,img));
else
frames_[MISSILE_FRAME].push_back(frame(beg,end,img,img_diag));
}
std::vector<config*>& sounds = cfg.children["sound"];
for(i = sounds.begin(); i != sounds.end(); ++i) {
sfx sound;
sound.time = atoi((*i)->values["time"].c_str());
sound.on_hit = (*i)->values["sound"];
sound.on_miss = (*i)->values["sound_miss"];
if(sound.on_miss.empty())
sound.on_miss = sound.on_hit;
if(sound.on_miss == "null")
sound.on_miss = "";
sfx_.push_back(sound);
}
}
const std::string& attack_type::name() const
{
return name_;
}
const std::string& attack_type::type() const
{
return type_;
}
const std::string& attack_type::special() const
{
return special_;
}
attack_type::RANGE attack_type::range() const
{
return range_;
}
int attack_type::damage() const
{
return damage_;
}
int attack_type::num_attacks() const
{
return num_attacks_;
}
int attack_type::get_first_frame(attack_type::FRAME_TYPE type) const
{
if(frames_[type].empty())
return 0;
else
return minimum<int>(frames_[type].front().start,0);
}
int attack_type::get_last_frame(attack_type::FRAME_TYPE type) const
{
if(frames_[type].empty())
return 0;
else
return maximum<int>(frames_[type].back().end,0);
}
const std::string* attack_type::get_frame(int milliseconds,
attack_type::FRAME_TYPE type,
attack_type::FRAME_DIRECTION dir) const
{
for(std::vector<frame>::const_iterator i = frames_[type].begin();
i != frames_[type].end(); ++i) {
if(i->start > milliseconds)
return NULL;
if(i->start <= milliseconds && i->end > milliseconds) {
if(dir == DIAGONAL && i->image_diagonal != NULL)
return i->image_diagonal;
else
return i->image;
}
}
return NULL;
}
const std::vector<attack_type::sfx>& attack_type::sound_effects() const
{
return sfx_;
}
bool attack_type::matches_filter(config& cfg) const
{
const std::string& filter_range = cfg.values["range"];
const std::string& filter_name = cfg.values["name"];
const std::string& filter_type = cfg.values["type"];
const std::string& filter_special = cfg.values["special"];
if(filter_range.empty() == false) {
if(filter_range == "short" && range() == LONG_RANGE ||
filter_range == "long" && range() == SHORT_RANGE) {
return false;
}
}
if(filter_name.empty() == false && filter_name != name())
return false;
if(filter_type.empty() == false && filter_type != type())
return false;
if(filter_special.empty() == false && filter_special != special())
return false;
return true;
}
void attack_type::apply_modification(config& cfg)
{
if(!matches_filter(cfg))
return;
const std::string& set_name = cfg.values["set_name"];
const std::string& set_type = cfg.values["set_type"];
const std::string& set_special = cfg.values["set_special"];
const std::string& increase_damage = cfg.values["increase_damage"];
const std::string& multiply_damage = cfg.values["multiply_damage"];
const std::string& increase_attacks = cfg.values["increase_attacks"];
if(set_name.empty() == false) {
name_ = set_name;
}
if(set_type.empty() == false) {
type_ = set_type;
}
if(set_special.empty() == false) {
special_ = set_special;
}
if(increase_damage.empty() == false) {
const int increase = atoi(increase_damage.c_str());
damage_ += increase;
if(damage_ < 1)
damage_ = 1;
}
if(multiply_damage.empty() == false) {
const double multiply = atof(increase_damage.c_str());
if(multiply != 0.0) {
damage_ = int(double(damage_)*multiply);
if(damage_ < 1)
damage_ = 1;
}
}
if(increase_attacks.empty() == false) {
const int increase = atoi(increase_attacks.c_str());
num_attacks_ += increase;
if(num_attacks_ < 1) {
num_attacks_ = 1;
}
}
}
unit_movement_type::unit_movement_type(config& cfg) : cfg_(cfg)
{}
const std::string& unit_movement_type::name() const
{
return cfg_.values["name"];
}
int unit_movement_type::movement_cost(const gamemap& map,
gamemap::TERRAIN terrain) const
{
const std::map<gamemap::TERRAIN,int>::const_iterator i =
moveCosts_.find(terrain);
if(i != moveCosts_.end()) {
return i->second;
}
const std::vector<config*>& v = cfg_.children["movement costs"];
if(v.empty())
return 1;
config* movement_costs = v[0];
const std::string& name = map.underlying_terrain_name(terrain);
if(terrain == 'b') {
std::cout << "underlying terrain: " << name << "\n";
}
const std::string& val = movement_costs->values[name];
int res = atoi(val.c_str());
//don't allow 0-movement terrain
if(res == 0) {
res = 100;
}
moveCosts_.insert(std::pair<gamemap::TERRAIN,int>(terrain,res));
return res;
}
double unit_movement_type::defense_modifier(const gamemap& map,
gamemap::TERRAIN terrain) const
{
const std::map<gamemap::TERRAIN,double>::const_iterator i =
defenseMods_.find(terrain);
if(i != defenseMods_.end()) {
return i->second;
}
const std::vector<config*>& v = cfg_.children["defense"];
if(v.empty())
return 1;
config* defense = v[0];
const std::string& name = map.underlying_terrain_name(terrain);
const std::string& val = defense->values[name];
const double res = atof(val.c_str());
defenseMods_.insert(std::pair<gamemap::TERRAIN,double>(terrain,res));
return res;
}
int unit_movement_type::damage_against(const attack_type& attack) const
{
const std::vector<config*>& v = cfg_.children["resistance"];
if(v.empty())
return 1;
config* resistance = v[0];
const std::string& val = resistance->values[attack.type()];
const double resist = atof(val.c_str());
return static_cast<int>(resist * static_cast<double>(attack.damage()));
}
const std::map<std::string,std::string>& unit_movement_type::damage_table() const
{
const std::vector<config*>& v = cfg_.children["resistance"];
if(v.empty()) {
static const std::map<std::string,std::string> default_val;
return default_val;
}
return v[0]->values;
}
unit_type::unit_type(config& cfg, const movement_type_map& mv_types,
std::vector<config*>& traits)
: cfg_(cfg), possibleTraits_(traits), alpha_(1.0)
{
if(has_ability("heals")) {
heals_ = game_config::healer_heals_per_turn;
max_heals_ = game_config::heal_amount;
} else if(has_ability("cures")) {
heals_ = game_config::curer_heals_per_turn;
max_heals_ = game_config::cure_amount;
} else {
heals_ = 0;
max_heals_ = 0;
}
heals_ = has_ability("heals");
regenerates_ = has_ability("regenerates");
leadership_ = has_ability("leadership");
lightbringer_ = has_ability("lightbringer");
skirmish_ = has_ability("skirmisher");
teleport_ = has_ability("teleport");
nightvision_ = has_ability("night vision");
const std::string& alpha_blend = cfg_.values["alpha"];
if(alpha_blend.empty() == false) {
alpha_ = atof(alpha_blend.c_str());
}
const std::string& move_type = cfg_.values["movement_type"];
if(move_type.empty()) {
throw gamestatus::load_game_failed("Movement type not specified for "
"unit '" + name() + "'");
}
const movement_type_map::const_iterator it = mv_types.find(move_type);
if(it == mv_types.end()) {
throw gamestatus::load_game_failed("Undefined movement type '" +
move_type + "'");
}
movementType_ = &(it->second);
//check if the images necessary for units exist
#ifdef linux
struct stat stat_buf;
#ifdef WESNOTH_PATH
if(::stat((WESNOTH_PATH + std::string("/images/") +
cfg_.values["image"]).c_str(),&stat_buf) >= 0) {
return;
}
#endif
if(::stat(("images/" + cfg_.values["image"]).c_str(),&stat_buf) < 0) {
std::cerr << "image '" << cfg_.values["image"] << "' does not exist!\n";
}
#endif
}
std::string unit_type::id() const
{
std::string n = name();
n.erase(std::remove(n.begin(),n.end(),' '),n.end());
return n;
}
std::string unit_type::language_name() const
{
const std::string& lang_name = string_table[id()];
if(lang_name.empty() == false)
return lang_name;
else
return name();
}
const std::string& unit_type::name() const
{
return cfg_.values["name"];
}
const std::string& unit_type::image() const
{
return cfg_.values["image"];
}
const std::string& unit_type::image_defensive() const
{
const std::string& val = cfg_.values["image_defensive"];
if(val.empty())
return cfg_.values["image"];
else
return val;
}
const std::string& unit_type::image_profile() const
{
const std::string& val = cfg_.values["profile"];
if(val.size() == 0)
return image();
else
return val;
}
const std::string& unit_type::unit_description() const
{
static const std::string default_val("No description available");
const std::string& lang_desc = string_table[id() + "_description"];
if(lang_desc.empty() == false)
return lang_desc;
const std::string& desc = cfg_.values["unit_description"];
if(desc.empty())
return default_val;
else
return desc;
}
int unit_type::hitpoints() const
{
return atoi(cfg_.values["hitpoints"].c_str());
}
std::vector<attack_type> unit_type::attacks() const
{
std::vector<attack_type> res;
const std::vector<config*>& v = cfg_.children["attack"];
for(std::vector<config*>::const_iterator i = v.begin(); i != v.end(); ++i)
res.push_back(attack_type(**i));
return res;
}
const unit_movement_type& unit_type::movement_type() const
{
return *movementType_;
}
int unit_type::cost() const
{
return atoi(cfg_.values["cost"].c_str());
}
int unit_type::experience_needed() const
{
return atoi(cfg_.values["experience"].c_str());
}
std::vector<std::string> unit_type::advances_to() const
{
const std::string& val = cfg_.values["advanceto"];
if(val == "null" || val == "")
return std::vector<std::string>();
else
return config::split(val);
}
const std::string& unit_type::usage() const
{
return cfg_.values["usage"];
}
int unit_type::level() const
{
return atoi(cfg_.values["level"].c_str());
}
int unit_type::movement() const
{
return atoi(cfg_.values["movement"].c_str());
}
unit_type::ALIGNMENT unit_type::alignment() const
{
const std::string& align = cfg_.values["alignment"];
if(align == "lawful")
return LAWFUL;
else if(align == "chaotic")
return CHAOTIC;
else
return NEUTRAL;
}
const std::string& unit_type::alignment_description(unit_type::ALIGNMENT align)
{
static const std::string aligns[] = { "lawful", "neutral", "chaotic" };
const std::map<std::string,std::string>::const_iterator i =
string_table.find(aligns[align]);
if(i != string_table.end())
return i->second;
else
return aligns[align];
}
double unit_type::alpha() const
{
return alpha_;
}
const std::string& unit_type::ability() const
{
return cfg_.values["ability"];
}
int unit_type::max_unit_healing() const
{
return max_heals_;
}
int unit_type::heals() const
{
return heals_;
}
bool unit_type::regenerates() const
{
return regenerates_;
}
bool unit_type::is_leader() const
{
return leadership_;
}
bool unit_type::is_lightbringer() const
{
return lightbringer_;
}
bool unit_type::is_skirmisher() const
{
return skirmish_;
}
bool unit_type::teleports() const
{
return teleport_;
}
bool unit_type::nightvision() const
{
return nightvision_;
}
bool unit_type::has_ability(const std::string& ability) const
{
return config::has_value(this->ability(),ability);
}
const std::vector<config*>& unit_type::possible_traits() const
{
return possibleTraits_;
}
game_data::game_data(config& cfg)
{
std::vector<config*>& unit_traits = cfg.children["trait"];
std::vector<config*>& move_types = cfg.children["movetype"];
for(std::vector<config*>::iterator i = move_types.begin();
i != move_types.end(); ++i) {
const unit_movement_type move_type(**i);
movement_types.insert(
std::pair<std::string,unit_movement_type>(move_type.name(),
move_type));
}
std::vector<config*>& u_types = cfg.children["unit"];
for(std::vector<config*>::iterator j = u_types.begin();
j != u_types.end(); ++j) {
const unit_type u_type(**j,movement_types,unit_traits);
unit_types.insert(
std::pair<std::string,unit_type>(u_type.name(),u_type));
}
}

View file

@ -1,192 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef UNIT_TYPES_H_INCLUDED
#define UNIT_TYPES_H_INCLUDED
#include "config.hpp"
#include "map.hpp"
#include "team.hpp"
#include <string>
#include <vector>
//the 'attack type' is the type of attack, how many times it strikes,
//and how much damage it does.
class attack_type
{
public:
enum RANGE { SHORT_RANGE, LONG_RANGE };
attack_type(config& cfg);
const std::string& name() const;
const std::string& type() const;
const std::string& special() const;
RANGE range() const;
int damage() const;
int num_attacks() const;
enum FRAME_TYPE { UNIT_FRAME, MISSILE_FRAME };
enum FRAME_DIRECTION { VERTICAL, DIAGONAL };
int get_first_frame(FRAME_TYPE type=UNIT_FRAME) const;
int get_last_frame(FRAME_TYPE type=UNIT_FRAME) const;
//function which gets an attack animation frame. The argument
//is 0 for the frame at the time of impact, and negative for
//frames before the time of impact
const std::string* get_frame(int milliseconds, FRAME_TYPE type=UNIT_FRAME,
FRAME_DIRECTION direction=VERTICAL) const;
struct sfx {
int time;
std::string on_hit, on_miss;
};
const std::vector<sfx>& sound_effects() const;
bool matches_filter(config& cfg) const;
void apply_modification(config& cfg);
private:
std::string name_;
std::string type_;
std::string special_;
RANGE range_;
int damage_;
int num_attacks_;
struct frame {
frame(int i1, int i2, const std::string& img)
: start(i1), end(i2), image(&img), image_diagonal(NULL)
{}
frame(int i1, int i2, const std::string& img, const std::string& diag)
: start(i1), end(i2), image(&img), image_diagonal(&diag)
{}
int start, end;
const std::string* image;
const std::string* image_diagonal;
};
std::vector<frame> frames_[2];
std::vector<sfx> sfx_;
};
//the 'unit movement type' is the basic size of the unit - flying, small land,
//large land, etc etc.
class unit_movement_type
{
public:
//this class assumes that the passed in reference will remain valid
//for at least as long as the class instance
unit_movement_type(config& cfg);
const std::string& name() const;
int movement_cost(const gamemap& map, gamemap::TERRAIN terrain) const;
double defense_modifier(const gamemap& map, gamemap::TERRAIN terrain) const;
int damage_against(const attack_type& attack) const;
const std::map<std::string,std::string>& damage_table() const;
private:
mutable config& cfg_;
mutable std::map<gamemap::TERRAIN,int> moveCosts_;
mutable std::map<gamemap::TERRAIN,double> defenseMods_;
};
typedef std::map<std::string,unit_movement_type> movement_type_map;
class unit_type
{
public:
//this class assumes that the passed in references will remain valid
//for at least as long as the class instance
unit_type(config& cfg, const movement_type_map& movement_types,
std::vector<config*>& traits);
//the name of the unit in the current language setting
std::string language_name() const;
//unique identifier that doesn't have any whitespace
std::string id() const;
const std::string& name() const;
const std::string& image() const;
const std::string& image_profile() const;
const std::string& image_defensive() const;
const std::string& unit_description() const;
int hitpoints() const;
std::vector<attack_type> attacks() const;
const unit_movement_type& movement_type() const;
int experience_needed() const;
std::vector<std::string> advances_to() const;
const std::string& usage() const;
int level() const;
int movement() const;
int cost() const;
enum ALIGNMENT { LAWFUL, NEUTRAL, CHAOTIC };
ALIGNMENT alignment() const;
static const std::string& alignment_description(ALIGNMENT align);
double alpha() const;
const std::string& ability() const;
//max_unit_healing returns the maximum hitpoints a unit next to this
//unit can heal per turn. heals returns the total amount of hitpoints
//this unit can heal out of all adjacent units
int max_unit_healing() const;
int heals() const;
bool regenerates() const;
bool is_leader() const;
bool is_lightbringer() const;
bool is_skirmisher() const;
bool teleports() const;
bool nightvision() const;
bool has_ability(const std::string& ability) const;
const std::vector<config*>& possible_traits() const;
private:
mutable config& cfg_;
double alpha_;
int max_heals_;
int heals_;
bool regenerates_;
bool leadership_;
bool lightbringer_;
bool skirmish_;
bool teleport_;
bool nightvision_;
const unit_movement_type* movementType_;
std::vector<config*>& possibleTraits_;
};
struct game_data
{
game_data(config& cfg);
movement_type_map movement_types;
std::map<std::string,unit_type> unit_types;
};
#endif

View file

@ -1,32 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef UTIL_H_INCLUDED
#define UTIL_H_INCLUDED
#include <map>
//instead of playing with VC++'s crazy definitions of min and max,
//just define our own
template<typename T>
T& minimum(T& a, T& b) { return a < b ? a : b; }
template<typename T>
const T& minimum(const T& a, const T& b) { return a < b ? a : b; }
template<typename T>
T& maximum(T& a, T& b) { return a < b ? b : a; }
template<typename T>
const T& maximum(const T& a, const T& b) { return a < b ? b : a; }
#endif

328
video.cpp
View file

@ -1,328 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#include <stdio.h>
#include <iostream>
#include "video.hpp"
#define TEST_VIDEO_ON 0
#if (TEST_VIDEO_ON==1)
#include <stdlib.h>
//test program takes three args - x-res y-res colour-depth
int main( int argc, char** argv )
{
if( argc != 4 ) {
printf( "usage: %s x-res y-res bitperpixel\n", argv[0] );
return 1;
}
SDL_Init(SDL_INIT_VIDEO);
CVideo video;
printf( "args: %s, %s, %s\n", argv[1], argv[2], argv[3] );
printf( "(%d,%d,%d)\n", strtoul(argv[1],0,10), strtoul(argv[2],0,10),
strtoul(argv[3],0,10) );
if( video.setMode( strtoul(argv[1],0,10), strtoul(argv[2],0,10),
strtoul(argv[3],0,10), FULL_SCREEN ) ) {
printf( "video mode possible\n" );
} else printf( "video mode NOT possible\n" );
printf( "%d, %d, %d\n", video.getx(), video.gety(),
video.getBitsPerPixel() );
for( int s = 0; s < 50; s++ ) {
video.lock();
for( int i = 0; i < video.getx(); i++ )
video.setPixel( i, 90, 255, 0, 0 );
if( s%10==0)
printf( "frame %d\n", s );
video.unlock();
video.update( 0, 90, video.getx(), 1 );
}
return 0;
}
#endif
namespace {
bool fullScreen = false;
}
CVideo::CVideo(const char* text) : frameBuffer(NULL), backBuffer(NULL)
{
const int res =
SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
if(res < 0) {
std::cerr << "Could not initialize SDL: " << SDL_GetError() << "\n";
throw CVideo::error();
}
for(int i = 0; i != sizeof(text_); ++i) {
text_[i] = text[i];
}
}
CVideo::CVideo( int x, int y, int bits_per_pixel, int flags, const char* text )
: frameBuffer(NULL), backBuffer(NULL)
{
const int res = SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
if(res < 0) {
throw error();
}
setMode( x, y, bits_per_pixel, flags );
for(int i = 0; i != sizeof(text_); ++i) {
text_[i] = text[i];
}
}
CVideo::~CVideo()
{
SDL_FreeSurface( backBuffer );
SDL_Quit();
}
int CVideo::modePossible( int x, int y, int bits_per_pixel, int flags )
{
return SDL_VideoModeOK( x, y, bits_per_pixel, flags );
}
int CVideo::setMode( int x, int y, int bits_per_pixel, int flags )
{
const int res = SDL_VideoModeOK( x, y, bits_per_pixel, flags );
if( res == 0 )
return 0;
fullScreen = (flags & FULL_SCREEN) != 0;
frameBuffer = SDL_SetVideoMode( x, y, bits_per_pixel, flags );
if( frameBuffer != NULL ) {
if(backBuffer != NULL)
SDL_FreeSurface(backBuffer);
backBuffer = SDL_ConvertSurface( frameBuffer,
frameBuffer->format, 0 );
if( backBuffer == NULL )
fprintf( stderr, "out of memory\n" );
return bits_per_pixel;
} else return 0;
}
int CVideo::getx() const
{
return backBuffer->w;
}
int CVideo::gety() const
{
return backBuffer->h;
}
int CVideo::getBitsPerPixel()
{
return backBuffer->format->BitsPerPixel;
}
int CVideo::getBytesPerPixel()
{
return backBuffer->format->BytesPerPixel;
}
int CVideo::getRedMask()
{
return backBuffer->format->Rmask;
}
int CVideo::getGreenMask()
{
return backBuffer->format->Gmask;
}
int CVideo::getBlueMask()
{
return backBuffer->format->Bmask;
}
void* CVideo::getAddress()
{
return backBuffer->pixels;
}
void CVideo::lock()
{
if( SDL_MUSTLOCK(backBuffer) )
SDL_LockSurface( backBuffer );
}
void CVideo::unlock()
{
if( SDL_MUSTLOCK(backBuffer) )
SDL_UnlockSurface( backBuffer );
}
int CVideo::mustLock()
{
return SDL_MUSTLOCK(backBuffer);
}
void CVideo::setPixel( int x, int y, int r, int g, int b )
{
int pixel = ((r<<(backBuffer->format->Rshift-backBuffer->format->Rloss))
& backBuffer->format->Rmask)+
((g<<(backBuffer->format->Gshift-backBuffer->format->Gloss))
& backBuffer->format->Gmask)+
((b>>(backBuffer->format->Bloss-backBuffer->format->Bshift))
& backBuffer->format->Bmask);
setPixel( x, y, pixel );
}
int CVideo::convertColour(int r, int g, int b)
{
return ((r<<(backBuffer->format->Rshift-backBuffer->format->Rloss))
& backBuffer->format->Rmask)+
((g<<(backBuffer->format->Gshift-backBuffer->format->Gloss))
& backBuffer->format->Gmask)+
((b>>(backBuffer->format->Bloss-backBuffer->format->Bshift))
& backBuffer->format->Bmask);
}
void CVideo::setPixel( int x, int y, int p )
{
static int pixel;
static char* p1 = ((char*)&pixel);
static char* p2 = ((char*)&pixel)+1;
static char* p3 = ((char*)&pixel)+2;
static short* sp = ((short*)&pixel);
pixel = p;
if( x < 0 || x >= backBuffer->w || y < 0 || y >= backBuffer->h )
return;
switch( backBuffer->format->BytesPerPixel ) {
case 1:
*((char*)backBuffer->pixels+y*backBuffer->w+x) = *p1;
break;
case 2:
*((short*)backBuffer->pixels+y*backBuffer->w+x) = *sp;
break;
case 3:
*((char*)backBuffer->pixels+y*backBuffer->w*3+x*3)
= *p1;
*((char*)backBuffer->pixels+y*backBuffer->w*3+x*3+1)
= *p2;
*((char*)backBuffer->pixels+y*backBuffer->w*3+x*3+2)
= *p3;
break;
case 4:
*((int*)backBuffer->pixels+y*backBuffer->w+x) = pixel;
break;
default:
fprintf( stderr, "Unknown colour depth\n" );
}
}
void CVideo::update( int x, int y, int w, int h )
{
if( w == 0 || h == 0 )
return;
if( x < 0 ) {
w += x;
x = 0;
}
if( y < 0 ) {
h += y;
y = 0;
}
if( x+w > frameBuffer->w )
w = frameBuffer->w - x;
if( y+h > frameBuffer->h )
h = frameBuffer->h - y;
SRectangle rect = {x,y,w,h};
SDL_BlitSurface( backBuffer, &rect, frameBuffer, &rect );
SDL_UpdateRect( frameBuffer, x, y, w, h );
}
void CVideo::update( SRectangle* rect )
{
SDL_BlitSurface( backBuffer, rect, frameBuffer, rect );
SDL_UpdateRect( frameBuffer, rect->x, rect->y, rect->w, rect->h );
}
SDL_Surface* CVideo::getSurface( void )
{
return backBuffer;
}
void CVideo::drawChar(int x, int y, int pixel, int bg, char c, int sz)
{
const char* const data = text_ + c*8;
for(int i = 0; i != 8*sz; ++i) {
if(y+i >= backBuffer->h)
return;
for(int j = 0; j != 8*sz; ++j) {
if(x+j >= backBuffer->w)
break;
if(data[i/sz] & (128 >> (j/sz))) {
setPixel(x+j,y+i,pixel);
} else {
if(bg != pixel)
setPixel(x+j,y+i,bg);
}
}
}
}
int CVideo::drawText(int x, int y, int pixel, int bg, const char* text, int sz)
{
const int good_colour = 0x0F00;
const int bad_colour = 0xF000;
int colour = pixel;
const int startx = x;
const int starty = y;
while(*text) {
if(*text == '@') {
colour = good_colour;
} else if(*text == '#') {
colour = bad_colour;
} else if(*text == '\n') {
colour = pixel;
y += 8*sz;
x = startx;
} else {
drawChar(x,y,colour,bg == pixel ? colour : bg,*text,sz);
x += 8*sz;
}
++text;
}
y += 8*sz;
return y - starty;
}
bool CVideo::isFullScreen() const { return fullScreen; }

View file

@ -1,71 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <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.
See the COPYING file for more details.
*/
#ifndef VIDEO_HPP_INCLUDED
#define VIDEO_HPP_INCLUDED
#include "SDL.h"
//possible flags when setting video modes
#define FULL_SCREEN SDL_FULLSCREEN
#define VIDEO_MEMORY SDL_HWSURFACE
#define SYSTEM_MEMORY SDL_SWSURFACE
typedef SDL_Rect SRectangle;
class CVideo {
public:
CVideo(const char* text);
CVideo( int x, int y, int bits_per_pixel, int flags, const char* text );
~CVideo();
int modePossible( int x, int y, int bits_per_pixel, int flags );
int setMode( int x, int y, int bits_per_pixel, int flags );
//functions to get the dimensions of the current video-mode
int getx() const;
int gety() const;
int getBitsPerPixel();
int getBytesPerPixel();
int getRedMask();
int getGreenMask();
int getBlueMask();
//functions to access the screen
void* getAddress();
void lock();
void unlock();
int mustLock();
void setPixel( int x, int y, int r, int g, int b );
void setPixel( int x, int y, int pixel );
int convertColour(int r, int g, int b);
void update( int x, int y, int w, int h );
void update( SRectangle* area );
SDL_Surface* getSurface( void );
int drawText(int x, int y, int pixel, int bg, const char* text,int size=1);
bool isFullScreen() const;
struct error {};
private:
void drawChar(int x, int y, int pixel, int bg, char c, int size=1);
SDL_Surface* frameBuffer;
SDL_Surface* backBuffer;
char text_[256*8];
};
#endif