Adding support for multi-hex tiles. Some features still missing.

This commit is contained in:
Philippe Plantier 2004-05-14 19:52:03 +00:00
parent a9fcf3554d
commit 405610f2b0
20 changed files with 844 additions and 214 deletions

View file

@ -1,42 +1,70 @@
[building_rule]
map="
C
C
!KC"
image_background="castle-wall-concave-$R"
rotations=ne,e,se,sw,w,nw
[/building_rule]
[built_terrain]
type=angle
terrains=!KC,C,C
image=castle-wall-concave
layer=foreground
[/built_terrain]
[building_rule]
map="
!KC
!KC
C"
[built_terrain]
type=angle
terrains=C,!KC,!KC
image=castle-wall-convex
layer=foreground
[/built_terrain]
image_background="castle-wall-convex-$R"
rotations=ne,e,se,sw,w,nw
[/building_rule]
[built_terrain]
type=angle
terrains=K,!KC,!KC
image=keep-wall
layer=foreground
[/built_terrain]
[building_rule]
map="
C
C
1"
[terrain]
# Using anchors for demonstrations's sake
pos=1
type=K
[/terrain]
[built_terrain]
type=angle
terrains=K,C,!KC
image=keep-wall-0
layer=foreground
[/built_terrain]
image_foreground="keep-inside-$R"
rotations=ne,e,se,sw,w,nw
[/building_rule]
[building_rule]
map="
!KC
K"
[terrain]
# Using [terrain] element just for demonstration's sake
x=1
y=0
type=!KC
[/terrain]
image_foreground="keep-wall-$R"
rotations=ne,e,se,sw,w,nw
[/building_rule]
[building_rule]
map="
C
!KC
K"
image_foreground="keep-wall-0-$R"
rotations=ne,e,se,sw,w,nw
[/building_rule]
[building_rule]
map="
!KC
C
K"
image_foreground="keep-wall-1-$R"
rotations=ne,e,se,sw,w,nw
[/building_rule]
[built_terrain]
type=angle
terrains=K,!KC,C
image=keep-wall-1
layer=foreground
[/built_terrain]
[built_terrain]
type=angle
terrains=K,C,C
image=keep-inside
layer=foreground
[/built_terrain]

View file

@ -57,6 +57,8 @@
missile_n_image=missile-n.png
missile_ne_image=missile-ne.png
terrain_mask_image=terrain/alphamask.png
observer_image=misc/eye.png
[/game_config]

View file

@ -13,134 +13,364 @@
#include "terrain.hpp"
#include "display.hpp"
#include "util.hpp"
#include "log.hpp"
terrain_builder::terrain_builder(const config& cfg, const gamemap& gmap) :
map_(gmap), cfg_(cfg.get_children("built_terrain"))
map_(gmap), tile_map_(gmap.x(), gmap.y())
{
build_terrains();
parse_config(cfg);
build_terrains(cfg);
}
const std::vector<std::string>* terrain_builder::get_terrain_at(const gamemap::location &loc,
ADJACENT_TERRAIN_TYPE terrain_type) const
const std::vector<image::locator> *terrain_builder::get_terrain_at(const gamemap::location &loc,
ADJACENT_TERRAIN_TYPE terrain_type) const
{
const buildings_map& buildings = (terrain_type == ADJACENT_BACKGROUND) ? buildings_background : buildings_foreground;
const buildings_map::const_iterator itor = buildings.find(loc);
if(itor != buildings.end()) {
return &(itor->second);
} else {
return NULL;
if(terrain_type == ADJACENT_BACKGROUND) {
if(tile_map_[loc].images_background.size())
return &tile_map_[loc].images_background;
}
if(terrain_type == ADJACENT_FOREGROUND) {
if(tile_map_[loc].images_foreground.size())
return &tile_map_[loc].images_foreground;
}
return NULL;
}
void terrain_builder::rebuild_terrain(const gamemap::location &loc)
{
}
terrain_builder::terrain_constraint terrain_builder::rotate(const terrain_builder::terrain_constraint &constraint, int angle)
{
static struct { int ii; int ij; int ji; int jj; } rotations[6] =
{ { 1, 0, 0, 1 }, { 1, 1, -1, 0 }, { 0, 1, -1, -1 },
{ -1, 0, 0, -1 }, { -1, -1, 1, 0 }, { 0, -1, 1, 1 } };
terrain_constraint ret = constraint;
// Vector i is going from n to s, vector j is going from ne to sw.
int vi = ret.loc.y - ret.loc.x/2;
int vj = ret.loc.x;
int ri = rotations[angle].ii * vi + rotations[angle].ij * vj;
int rj = rotations[angle].ji * vi + rotations[angle].jj * vj;
ret.loc.x = rj;
ret.loc.y = ri + (rj >= 0 ? rj/2 : (rj-1)/2);
return ret;
}
void terrain_builder::replace_rotation(std::string &s, const std::string &replacement)
{
int pos;
while((pos = s.find("$R")) != std::string::npos) {
s.replace(pos, 2, replacement);
}
}
void terrain_builder::rebuild_terrain(const gamemap::location &loc) {
const std::vector<std::string> &vback = build_terrain_at(loc, ADJACENT_BACKGROUND);
if (vback.empty()) {
buildings_background.erase(loc);
terrain_builder::building_rule terrain_builder::rotate_rule(const terrain_builder::building_rule &rule,
int angle, const std::string &angle_name)
{
building_rule ret;
ret.image_foreground = rule.image_foreground;
ret.image_background = rule.image_background;
ret.location_constraints = rule.location_constraints;
building_rule::constraint_set::const_iterator cons;
for(cons = rule.constraints.begin(); cons != rule.constraints.end(); ++cons) {
const terrain_constraint &rcons = rotate(cons->second, angle);
ret.constraints[rcons.loc] = rcons;
}
else {
buildings_background[loc] = vback;
// Normalize the rotation, so that it starts on a positive location
int minx = INT_MAX;
int miny = INT_MAX;
building_rule::constraint_set::iterator cons2;
for(cons2 = ret.constraints.begin(); cons2 != ret.constraints.end(); ++cons2) {
minx = minimum<int>(cons2->second.loc.x, minx);
miny = minimum<int>(2*cons2->second.loc.y + (cons2->second.loc.x & 1), miny);
}
const std::vector<std::string> &vfore = build_terrain_at(loc, ADJACENT_FOREGROUND);
if (vfore.empty()) {
buildings_foreground.erase(loc);
}
else {
buildings_foreground[loc] = vfore;
if((miny & 1) && (minx & 1) && (minx < 0))
miny += 2;
if(!(miny & 1) && (minx & 1) && (minx > 0))
miny -= 2;
for(cons2 = ret.constraints.begin(); cons2 != ret.constraints.end(); ++cons2) {
//Adjusts positions
cons2->second.loc += gamemap::location(-minx, -((miny-1)/2));
//Transforms attributes
std::vector<std::string>::iterator flag;
for(flag = cons2->second.set_flag.begin(); flag != cons2->second.set_flag.end(); flag++) {
replace_rotation(*flag, angle_name);
}
for(flag = cons2->second.no_flag.begin(); flag != cons2->second.no_flag.end(); flag++) {
replace_rotation(*flag, angle_name);
}
}
replace_rotation(ret.image_foreground, angle_name);
replace_rotation(ret.image_background, angle_name);
return ret;
}
void terrain_builder::build_terrains()
void terrain_builder::add_constraints(std::map<gamemap::location, terrain_builder::terrain_constraint> & constraints,
const gamemap::location &loc, std::string type,
std::string set_flag, std::string no_flag)
{
for(int x = -1; x <= map_.x(); ++x) {
for(int y = -1; y <= map_.y(); ++y) {
const gamemap::location loc(x, y);
if(constraints.find(loc) == constraints.end()) {
//the terrain at the current location did not exist, so create it
constraints[loc] = terrain_constraint(loc);
}
const std::vector<std::string> &vback = build_terrain_at(loc, ADJACENT_BACKGROUND);
if(!vback.empty())
buildings_background[loc] = vback;
//Add terrain properties here
if(type.size())
constraints[loc].terrain_types = type;
if(set_flag.size())
constraints[loc].set_flag.push_back(set_flag);
if(no_flag.size())
constraints[loc].set_flag.push_back(no_flag);
}
void terrain_builder::parse_mapstring(const std::string &mapstring, struct building_rule &br,
std::map<int, gamemap::location>& anchors)
{
int lineno = 0;
int x = 0;
std::cerr << "Loading map \"" << mapstring << "\"\n";
const std::vector<std::string> &lines = config::split(mapstring, '\n', 0);
std::vector<std::string>::const_iterator line = lines.begin();
//Strips trailing empty lines
while(std::find_if(line->begin(),line->end(),config::notspace) == line->end())
line++;
//If the strings starts with a space, the first line is an odd line, else it is an even one
if((*line)[0] == ' ')
lineno = 1;
std::cerr << "--- Begin map ---\n";
for(; line != lines.end(); ++line) {
//cuts each line into chunks of 4 characters, ignoring the 2 first ones if the line is odd
std::cerr << "Line is " << *line << "\n";
x = 0;
std::string::size_type lpos = 0;
if(lineno % 2) {
lpos = 2;
x = 1;
}
while(lpos < line->size()) {
const std::string types = line->substr(lpos, 4);
std::cerr << types << "/";
const std::vector<std::string> &vfore = build_terrain_at(loc, ADJACENT_FOREGROUND);
if(!vfore.empty())
buildings_foreground[loc] = vfore;
//If there are numbers in the types string, consider it is an anchor
if(types.find_first_of("0123456789") != std::string::npos) {
int anchor = atoi(types.c_str());
anchors[anchor] = gamemap::location(x, lineno / 2);
} else {
const gamemap::location loc(x, lineno / 2);
add_constraints(br.constraints, loc, types);
}
lpos += 4;
x += 2;
}
std::cerr << "\n";
lineno++;
}
std::cerr << "--- End map ---\n";
}
void terrain_builder::parse_config(const config &cfg)
{
log_scope("terrain_builder::parse_config");
//Parses the list of building rules (BRs)
config::child_list brs(cfg.get_children("building_rule"));
for(config::child_list::const_iterator br = brs.begin(); br != brs.end(); ++br) {
building_rule pbr; // Parsed Building rule
pbr.image_foreground = (**br)["image_foreground"];
pbr.image_background = (**br)["image_background"];
//Mapping anchor indices to anchor locations. Using a map for simplicity, but should
//change it to a vector if it proves to be slow
std::map<int, gamemap::location> anchors;
// Parse the map= , if there is one (and fill the anchors list)
parse_mapstring((**br)["map"], pbr, anchors);
// Parses the terrain constraints (TCs)
config::child_list tcs((*br)->get_children("terrain"));
for(config::child_list::const_iterator tc = tcs.begin(); tc != tcs.end(); tc++) {
//Adds the terrain constraint to the current built terrain's list of terrain
//constraints, if it does not exist.
gamemap::location loc;
if((**tc)["x"].size()) {
loc.x = atoi((**tc)["x"].c_str());
}
if((**tc)["y"].size()) {
loc.y = atoi((**tc)["y"].c_str());
}
if((**tc)["pos"].size()) {
int pos = atoi((**tc)["pos"].c_str());
if(anchors.find(pos) == anchors.end()) {
std::cerr << "Invalid anchor!\n";
continue;
}
loc = anchors[pos];
}
if(!loc.valid()) {
//Todo: Change the following by a decent debug message
std::cerr << "Built terrain without position!\n";
continue;
}
add_constraints(pbr.constraints, loc, (**tc)["type"], (**tc)["set_flag"], (**tc)["no_flag"]);
}
const std::string global_set_flag = (**br)["set_flag"];
const std::string global_no_flag = (**br)["no_flag"];
for(building_rule::constraint_set::iterator constraint = pbr.constraints.begin(); constraint != pbr.constraints.end();
constraint++) {
if(global_set_flag.size())
constraint->second.set_flag.push_back(global_set_flag);
if(global_no_flag.size())
constraint->second.no_flag.push_back(global_no_flag);
}
// Handles rotations
const std::string rotations = (**br)["rotations"];
if(rotations.size()) {
const std::vector<std::string>& rot = config::split(rotations, ',');
for(int angle = 0; angle < rot.size(); angle++) {
building_rules_.push_back(rotate_rule(pbr, angle, rot[angle]));
}
} else {
// Adds the parsed built terrain to the list
building_rules_.push_back(pbr);
}
}
}
std::vector<std::string> terrain_builder::build_terrain_at(const gamemap::location &loc,
ADJACENT_TERRAIN_TYPE terrain_type)
bool terrain_builder::rule_matches(const terrain_builder::building_rule &rule, const gamemap::location &loc)
{
std::vector<std::string> res;
gamemap::location adjacent[6];
get_adjacent_tiles(loc,adjacent);
static const int corner_order[] = {0, 5, 1, 4, 2, 3};
if(rule.location_constraints.valid() && rule.location_constraints != loc)
return false;
// Calculate each corner's type.
for(int index = 0; index != 6; ++index) {
int i = corner_order[index];
for(building_rule::constraint_set::const_iterator cons = rule.constraints.begin();
cons != rule.constraints.end(); ++cons) {
const bool angle_northern = angle_is_northern(i);
// translated location
const gamemap::location tloc = loc + cons->second.loc;
if(!map_.on_board(tloc))
return false;
if(terrain_type == ADJACENT_FOREGROUND && angle_northern)
continue;
tile& btile = tile_map_[tloc];
if(!map_.get_terrain_info(tloc).matches(cons->second.terrain_types))
return false;
int j = i+1;
if(j == 6)
j = 0;
for(std::vector<std::string>::const_iterator itor = cons->second.no_flag.begin();
itor != cons->second.no_flag.end(); itor++) {
//If a flag listed in "no_flag" is present, the rule does not match
if(btile.flags.find(*itor) != btile.flags.end())
return false;
}
}
for(config::child_list::const_iterator item = cfg_.begin(); item != cfg_.end(); ++item) {
const std::string &layer = (**item)["layer"];
if(terrain_type == ADJACENT_FOREGROUND && layer == "background" ||
terrain_type == ADJACENT_BACKGROUND && !angle_northern && layer == "foreground" )
continue;
std::vector<std::string> terrain_types = config::split((**item)["terrains"]);
// Ignore invalid terrain types definitions
if(terrain_types.size() != 3)
continue;
// If (loc, adjacent[i], adjacent[j]), or any of their rotations, do match the
// current terrains, the corresponding map will be blitted into the current surface.
gamemap::TERRAIN to = map_.get_terrain(loc);
gamemap::TERRAIN ti = map_.get_terrain(adjacent[i]);
gamemap::TERRAIN tj = map_.get_terrain(adjacent[j]);
for(int r = 0; r != 3; ++r) {
if(map_.get_terrain_info(to).matches(terrain_types[0]) &&
map_.get_terrain_info(ti).matches(terrain_types[1]) &&
map_.get_terrain_info(tj).matches(terrain_types[2])) {
return true;
}
// We found a match. Add the current rotation.
const std::string &image = (**item)["image"];
if(image.size() != 0)
{
std::stringstream imagename;
imagename << image;
int rotation = (2*r + i) % 6;
imagename << get_angle_direction(rotation);
imagename << get_angle_direction(i);
res.push_back(imagename.str());
}
void terrain_builder::apply_rule(const terrain_builder::building_rule &rule, const gamemap::location &loc)
{
for(building_rule::constraint_set::const_iterator constraint = rule.constraints.begin();
constraint != rule.constraints.end(); ++constraint) {
break; // We will not try other rotations if this one matches
}
const gamemap::location tloc = loc + constraint->second.loc;
if(!map_.on_board(tloc)) {
std::cerr << "Error: out-of map tile!\n";
return;
}
// rotate terrains
gamemap::TERRAIN tmp;
tmp = to;
to = ti;
ti = tj;
tj = tmp;
tile& btile = tile_map_[tloc];
if(rule.image_foreground.size()) {
image::locator th(rule.image_foreground, constraint->second.loc);
btile.images_foreground.push_back(th);
}
if(rule.image_background.size()) {
image::locator th(rule.image_background, constraint->second.loc);
btile.images_background.push_back(th);
}
for(std::vector<std::string>::const_iterator itor = constraint->second.set_flag.begin();
itor != constraint->second.set_flag.end(); itor++) {
btile.flags.insert(*itor);
}
}
}
void terrain_builder::build_terrains(const config& cfg)
{
std::cerr << "Built terrain rules: \n";
for(building_ruleset::const_iterator rule = building_rules_.begin();
rule != building_rules_.end(); ++rule) {
std::cerr << ">> New rule: image_background = " << rule->image_background << " , image_foreground = "<< rule->image_foreground << "\n";
for(building_rule::constraint_set::const_iterator constraint = rule->constraints.begin();
constraint != rule->constraints.end(); ++constraint) {
std::cerr << ">>>> New constraint: location = (" << constraint->second.loc.x << ", " << constraint->second.loc.y << "), terrain types = " << constraint->second.terrain_types << "\n";
std::vector<std::string>::const_iterator flag;
for(flag = constraint->second.set_flag.begin(); flag != constraint->second.set_flag.end(); ++flag) {
std::cerr << ">>>>>> Set_flag: " << *flag << "\n";
}
for(flag = constraint->second.no_flag.begin(); flag != constraint->second.no_flag.end(); ++flag) {
std::cerr << ">>>>>> No_flag: " << *flag << "\n";
}
}
}
for(int x = -1; x <= map_.x(); ++x) {
for(int y = -1; y <= map_.y(); ++y) {
for(building_ruleset::const_iterator rule = building_rules_.begin();
rule != building_rules_.end(); ++rule) {
const gamemap::location loc(x,y);
if(rule_matches(*rule, loc))
apply_rule(*rule, loc);
}
}
}
return res;
}

View file

@ -15,7 +15,7 @@
#include "config.hpp"
#include "map.hpp"
#include "image.hpp"
#include "SDL.h"
#include <string>
@ -32,25 +32,73 @@ public:
//returns a vector of string representing the images to load & blit together to get the
//built content for this tile.
//Returns NULL if there is no built content for this tile.
const std::vector<std::string> *terrain_builder::get_terrain_at(const gamemap::location &loc,
ADJACENT_TERRAIN_TYPE terrain_type) const;
const std::vector<image::locator> *get_terrain_at(const gamemap::location &loc,
ADJACENT_TERRAIN_TYPE terrain_type) const;
// regenerate the generated content at the given location.
void rebuild_terrain(const gamemap::location &loc);
void rebuild_terrain(const gamemap::location &loc);
private:
//pre-calculates the list of generated content for all tiles (will slow the game
//too much otherwise)
void build_terrains();
//returns a vector of strings representing the images for a given tile. Same as
//get_terrain_at, except that the content is actually generated there.
std::vector<std::string> build_terrain_at(const gamemap::location &loc,
ADJACENT_TERRAIN_TYPE terrain_type);
struct terrain_constraint
{
terrain_constraint() : loc() {};
const config::child_list cfg_;
const gamemap& map_;
terrain_constraint(gamemap::location loc) : loc(loc) {};
gamemap::location loc;
std::string terrain_types;
std::vector<std::string> set_flag;
std::vector<std::string> no_flag;
};
struct building_rule
{
typedef std::map<gamemap::location, terrain_constraint> constraint_set;
constraint_set constraints;
gamemap::location location_constraints;
std::string image_foreground;
std::string image_background;
};
struct tile
{
std::set<std::string> flags;
std::vector<image::locator> images_foreground;
std::vector<image::locator> images_background;
};
struct tilemap
{
tilemap(int x, int y) : x_(x), y_(y), map_((x+2)*(y+2)) {}
tile &operator[](const gamemap::location &loc) { return map_[(loc.x+1) + (loc.y+1)*(x_+2)]; }
const tile &operator[] (const gamemap::location &loc) const { return map_[(loc.x+1) + (loc.y+1)*(x_+2)]; }
std::vector<tile> map_;
int x_;
int y_;
};
terrain_constraint rotate(const terrain_constraint &constraint, int angle);
void replace_rotation(std::string &s, const std::string &replacement);
building_rule rotate_rule(const building_rule &rule, int angle, const std::string &angle_name);
void add_constraints(std::map<gamemap::location, terrain_constraint>& constraints,
const gamemap::location &loc, std::string type,
std::string set_flag = "", std::string no_flag = "");
void parse_mapstring(const std::string &mapstring, struct building_rule &br,
std::map<int, gamemap::location>& anchors);
void parse_config(const config &cfg);
bool rule_matches(const building_rule &rule, const gamemap::location &loc);
void apply_rule(const building_rule &rule, const gamemap::location &loc);
void build_terrains(const config& cfg);
const gamemap& map_;
tilemap tile_map_;
typedef std::vector<building_rule> building_ruleset;
building_ruleset building_rules_;
typedef std::map<gamemap::location, std::vector<std::string> > buildings_map;
buildings_map buildings_background, buildings_foreground;
};
#endif

View file

@ -1155,7 +1155,7 @@ std::string config::join(const std::vector<std::string>& v, char c)
return str.str();
}
std::vector<std::string> config::split(const std::string& val, char c, bool remove_empty)
std::vector<std::string> config::split(const std::string& val, char c, int flags)
{
std::vector<std::string> res;
@ -1165,12 +1165,15 @@ std::vector<std::string> config::split(const std::string& val, char c, bool remo
while(i2 != val.end()) {
if(*i2 == c) {
std::string new_val(i1,i2);
strip(new_val);
if(!remove_empty || !new_val.empty())
if(flags & STRIP_SPACES)
strip(new_val);
if(!(flags & REMOVE_EMPTY) || !new_val.empty())
res.push_back(new_val);
++i2;
while(i2 != val.end() && *i2 == ' ')
++i2;
if(flags & STRIP_SPACES) {
while(i2 != val.end() && *i2 == ' ')
++i2;
}
i1 = i2;
} else {
@ -1179,8 +1182,9 @@ std::vector<std::string> config::split(const std::string& val, char c, bool remo
}
std::string new_val(i1,i2);
strip(new_val);
if(!remove_empty || !new_val.empty())
if(flags & STRIP_SPACES)
strip(new_val);
if(!(flags & REMOVE_EMPTY) || !new_val.empty())
res.push_back(new_val);
return res;
@ -1192,7 +1196,7 @@ std::vector<std::string> config::split(const std::string& val, char c, bool remo
//this method was added to make it possible to quote user input,
//particularly so commas in user input will not cause visual problems in menus.
//why not change split()? that would change the methods post condition.
std::vector<std::string> config::quoted_split(const std::string& val, char c, bool remove_empty, char quote)
std::vector<std::string> config::quoted_split(const std::string& val, char c, int flags, char quote)
{
std::vector<std::string> res;
@ -1206,12 +1210,15 @@ std::vector<std::string> config::quoted_split(const std::string& val, char c, bo
if(i2 != val.end()) ++i2;
} else if(*i2 == c) {
std::string new_val(i1,i2);
strip(new_val);
if(!remove_empty || !new_val.empty())
if(flags & STRIP_SPACES)
strip(new_val);
if(!(flags & REMOVE_EMPTY) || !new_val.empty())
res.push_back(new_val);
++i2;
while(i2 != val.end() && *i2 == ' ')
++i2;
if(flags & STRIP_SPACES) {
while(i2 != val.end() && *i2 == ' ')
++i2;
}
i1 = i2;
} else {
@ -1220,8 +1227,9 @@ std::vector<std::string> config::quoted_split(const std::string& val, char c, bo
}
std::string new_val(i1,i2);
strip(new_val);
if(!remove_empty || !new_val.empty())
if(flags & STRIP_SPACES)
strip(new_val);
if(!(flags & REMOVE_EMPTY) || !new_val.empty())
res.push_back(new_val);
return res;
@ -1239,11 +1247,9 @@ std::pair<int,int> config::parse_range(const std::string& str)
return res;
}
namespace {
//make sure we regard '\r' and '\n' as a space, since Mac, Unix, and DOS
//all consider these differently.
bool notspace(char c) { return !portable_isspace(c); }
}
bool config::notspace(char c) { return !portable_isspace(c); }
//prepend all special characters with a backslash
//special characters are:

View file

@ -153,11 +153,16 @@ struct config
void clear_children(const std::string& key);
config* remove_child(const std::string& key, size_t index);
// REMOVE_EMPTY : remove empty elements
// STRIP_SPACES : strips leading and trailing blank spaces
enum { REMOVE_EMPTY = 0x01, STRIP_SPACES = 0x02 };
static std::vector<std::string> split(const std::string& val, char c=',', int flags = REMOVE_EMPTY | STRIP_SPACES);
static std::string join(const std::vector<std::string>& v, char c=',');
static std::vector<std::string> split(const std::string& val, char c=',', bool remove_empty=true);
static std::vector<std::string> quoted_split(const std::string& val, char c=',',
bool remove_empty=true, char quote='\\');
int flags = REMOVE_EMPTY | STRIP_SPACES, char quote='\\');
static std::pair<int,int> parse_range(const std::string& str);
static bool notspace(char c);
static std::string& escape(std::string& str);
static std::string& unescape(std::string& str);
static std::string& strip(std::string& str);

View file

@ -1643,11 +1643,13 @@ std::vector<shared_sdl_surface> display::getBuiltTerrain(int x, int y, image::TY
terrain_builder::ADJACENT_TERRAIN_TYPE builder_terrain_type =
(terrain_type == ADJACENT_FOREGROUND ?
terrain_builder::ADJACENT_FOREGROUND : terrain_builder::ADJACENT_BACKGROUND);
const std::vector<std::string>* const terrains = builder_.get_terrain_at(loc,builder_terrain_type);
const std::vector<image::locator>* const terrains = builder_.get_terrain_at(loc,builder_terrain_type);
if(terrains != NULL) {
for(std::vector<std::string>::const_iterator it = terrains->begin(); it != terrains->end(); ++it) {
const std::string image = "terrain/" + *it;
for(std::vector<image::locator>::const_iterator it = terrains->begin(); it != terrains->end(); ++it) {
image::locator image = *it;
image.filename = "terrain/" + it->filename;
const shared_sdl_surface surface(getTerrain(image,image_type,x,y,true));
if(surface != NULL) {
res.push_back(surface);
@ -1658,7 +1660,7 @@ std::vector<shared_sdl_surface> display::getBuiltTerrain(int x, int y, image::TY
return res;
}
SDL_Surface* display::getTerrain(const std::string& image, image::TYPE image_type,
SDL_Surface* display::getTerrain(const image::locator& image, image::TYPE image_type,
int x, int y, bool search_tod)
{
SDL_Surface* im = NULL;
@ -1667,8 +1669,9 @@ SDL_Surface* display::getTerrain(const std::string& image, image::TYPE image_typ
const time_of_day& tod_at = timeofday_at(status_,units_,gamemap::location(x,y));
//see if there is a time-of-day specific version of this image
if(search_tod) {
const std::string tod_image = image + "-" + tod.id + ".png";
if(search_tod) {
image::locator tod_image = image;
tod_image.filename = image.filename + "-" + tod.id + ".png";
im = image::get_image(tod_image,image_type);
if(im != NULL) {
@ -1676,9 +1679,10 @@ SDL_Surface* display::getTerrain(const std::string& image, image::TYPE image_typ
}
}
const std::string file = image + ".png";
image::locator tmp = image;
tmp.filename += ".png";
im = image::get_image(file,image_type);
im = image::get_image(tmp,image_type);
if(im == NULL) {
return NULL;
}

View file

@ -351,7 +351,7 @@ private:
SDL_Surface* getTerrain(gamemap::TERRAIN, image::TYPE type,
int x, int y, const std::string& dir="");
//this surface must be freed by the caller
SDL_Surface* getTerrain(const std::string &image, image::TYPE type,
SDL_Surface* getTerrain(const image::locator &image, image::TYPE type,
int x, int y, bool search_tod);
//this surface must be freed by the caller

View file

@ -33,6 +33,8 @@ namespace game_config
std::string missile_n_image, missile_ne_image;
std::string terrain_mask_image = "terrain/alphamask.png";
std::string map_image = "misc/map.png";
std::string rightside_image = "misc/rightside.png";
std::string rightside_image_bot = "misc/rightside-bottom.png";
@ -45,7 +47,7 @@ namespace game_config
std::string dot_image = "misc/dot.png";
std::string cross_image = "misc/cross.png";
std::string foot_left_nw, foot_left_n, foot_right_nw, foot_right_n;
std::string observer_image;
@ -108,6 +110,8 @@ namespace game_config
missile_n_image = v["missile_n_image"];
missile_ne_image = v["missile_ne_image"];
terrain_mask_image = v["terrain_mask_image"];
observer_image = v["observer_image"];
}
}

View file

@ -40,7 +40,7 @@ namespace game_config
enemy_energy_image,ally_energy_image,
dot_image,cross_image,
foot_left_nw,foot_left_n,foot_right_nw,foot_right_n,
missile_n_image,missile_ne_image, observer_image,
missile_n_image,missile_ne_image,terrain_mask_image,observer_image,
checked_menu_image,unchecked_menu_image;
extern int title_logo_x, title_logo_y, title_buttons_x, title_buttons_y, title_buttons_padding;

View file

@ -17,7 +17,7 @@ namespace {
typedef std::map<gamemap::TERRAIN,SDL_Surface*> mini_terrain_cache_map;
mini_terrain_cache_map mini_terrain_cache;
typedef std::map<std::string,SDL_Surface*> image_map;
typedef std::map<image::locator,SDL_Surface*> image_map;
image_map images_,scaledImages_,unmaskedImages_,greyedImages_,brightenedImages_;
std::map<SDL_Surface*,SDL_Surface*> reversedImages_;
@ -50,20 +50,20 @@ void clear_surfaces(Map& surfaces)
enum TINT { GREY_IMAGE, BRIGHTEN_IMAGE };
SDL_Surface* get_tinted(const std::string& filename, TINT tint)
SDL_Surface* get_tinted(const image::locator& i_locator, TINT tint)
{
image_map& images = tint == GREY_IMAGE ? greyedImages_ : brightenedImages_;
const image_map::iterator itor = images.find(filename);
const image_map::iterator itor = images.find(i_locator);
if(itor != images.end()) {
return itor->second;
}
scoped_sdl_surface base(image::get_image(filename,image::SCALED));
scoped_sdl_surface base(image::get_image(i_locator,image::SCALED));
SDL_Surface* const surface = (tint == GREY_IMAGE ? greyscale_image(base) : brighten_image(base,1.5));
images.insert(std::pair<std::string,SDL_Surface*>(filename,surface));
images.insert(std::pair<image::locator,SDL_Surface*>(i_locator,surface));
return surface;
}
@ -79,10 +79,85 @@ void flush_cache()
clear_surfaces(reversedImages_);
}
SDL_Surface* load_image_file(image::locator i_locator)
{
SDL_Surface* surf = NULL;
const std::string images_path = "images/";
const std::string images_filename = images_path + i_locator.filename;
if(game_config::path.empty() == false) {
const std::string& fullpath = game_config::path + "/" +
images_filename;
surf = IMG_Load(fullpath.c_str());
}
if(surf == NULL) {
surf = IMG_Load(images_filename.c_str());
}
return surf;
}
SDL_Surface * load_image_sub_file(image::locator i_locator)
{
SDL_Surface *surf = NULL;
SDL_Surface *tmp = NULL;
scoped_sdl_surface mother_surface(image::get_image(i_locator.filename, image::UNSCALED, image::NO_ADJUST_COLOUR));
scoped_sdl_surface mask(image::get_image(game_config::terrain_mask_image, image::UNSCALED, image::NO_ADJUST_COLOUR));
if(mother_surface == NULL)
return NULL;
if(mask == NULL)
return NULL;
tmp = SDL_CreateRGBSurface(SDL_SWSURFACE, 72, 72, mother_surface->format->BitsPerPixel, mother_surface->format->Rmask,
mother_surface->format->Gmask, mother_surface->format->Bmask, mother_surface->format->Amask);
SDL_Rect srcrect = { 54 * i_locator.loc.x, 72 * i_locator.loc.y + 36 * (i_locator.loc.x % 2), 72, 72 };
SDL_Rect destrect = { 0, 0, 72, 72 };
SDL_BlitSurface(mother_surface, &srcrect, tmp, &destrect);
surf = mask_surface(tmp, mask);
SDL_FreeSurface(tmp);
return surf;
}
}
namespace image {
bool locator::operator==(const locator& a) const
{
if(a.type != type)
return false;
if(type == FILE)
return filename == a.filename;
if(type == SUB_FILE)
return filename == a.filename && loc == a.loc;
}
bool locator::operator<(const locator &a) const
{
if(type != a.type)
return type < a.type;
if(type == FILE)
return filename < a.filename;
if(type == SUB_FILE) {
if(filename != a.filename)
return filename < a.filename;
return loc < a.loc;
}
}
manager::manager() {}
manager::~manager()
@ -159,19 +234,19 @@ void set_zoom(int amount)
}
}
SDL_Surface* get_image(const std::string& filename, TYPE type, COLOUR_ADJUSTMENT adjust_colour)
SDL_Surface* get_image(const image::locator& i_locator, TYPE type, COLOUR_ADJUSTMENT adjust_colour)
{
SDL_Surface* result = NULL;
if(type == GREYED) {
result = get_tinted(filename,GREY_IMAGE);
result = get_tinted(i_locator,GREY_IMAGE);
} else if(type == BRIGHTENED) {
result = get_tinted(filename,BRIGHTEN_IMAGE);
result = get_tinted(i_locator,BRIGHTEN_IMAGE);
} else {
image_map::iterator i;
if(type == SCALED) {
i = scaledImages_.find(filename);
i = scaledImages_.find(i_locator);
if(i != scaledImages_.end()) {
result = i->second;
sdl_add_ref(result);
@ -180,7 +255,7 @@ SDL_Surface* get_image(const std::string& filename, TYPE type, COLOUR_ADJUSTMENT
}
if(type == UNMASKED) {
i = unmaskedImages_.find(filename);
i = unmaskedImages_.find(i_locator);
if(i != unmaskedImages_.end()) {
result = i->second;
sdl_add_ref(result);
@ -188,25 +263,26 @@ SDL_Surface* get_image(const std::string& filename, TYPE type, COLOUR_ADJUSTMENT
}
}
i = images_.find(filename);
i = images_.find(i_locator);
if(i == images_.end()) {
const std::string images_path = "images/";
const std::string images_filename = images_path + filename;
SDL_Surface* surf = NULL;
if(game_config::path.empty() == false) {
const std::string& fullpath = game_config::path + "/" +
images_filename;
surf = IMG_Load(fullpath.c_str());
switch(i_locator.type)
{
case locator::FILE:
surf = load_image_file(i_locator);
break;
case locator::SUB_FILE:
surf = load_image_sub_file(i_locator);
break;
default:
surf = NULL;
}
if(surf == NULL) {
surf = IMG_Load(images_filename.c_str());
}
if(surf == NULL) {
images_.insert(std::pair<std::string,SDL_Surface*>(filename,NULL));
images_.insert(std::pair<image::locator,SDL_Surface*>(i_locator,NULL));
return NULL;
}
@ -216,7 +292,7 @@ SDL_Surface* get_image(const std::string& filename, TYPE type, COLOUR_ADJUSTMENT
surf = conv;
}
i = images_.insert(std::pair<std::string,SDL_Surface*>(filename,surf)).first;
i = images_.insert(std::pair<image::locator,SDL_Surface*>(i_locator,surf)).first;
}
if(i->second == NULL)
@ -255,9 +331,9 @@ SDL_Surface* get_image(const std::string& filename, TYPE type, COLOUR_ADJUSTMENT
}
if(type == UNMASKED) {
unmaskedImages_.insert(std::pair<std::string,SDL_Surface*>(filename,result));
unmaskedImages_.insert(std::pair<image::locator,SDL_Surface*>(i_locator,result));
} else {
scaledImages_.insert(std::pair<std::string,SDL_Surface*>(filename,result));
scaledImages_.insert(std::pair<image::locator,SDL_Surface*>(i_locator,result));
}
}
}
@ -270,12 +346,12 @@ SDL_Surface* get_image(const std::string& filename, TYPE type, COLOUR_ADJUSTMENT
return result;
}
SDL_Surface* get_image_dim(const std::string& filename, size_t x, size_t y)
SDL_Surface* get_image_dim(const image::locator& i_locator, size_t x, size_t y)
{
SDL_Surface* const surf = get_image(filename,UNSCALED);
SDL_Surface* const surf = get_image(i_locator,UNSCALED);
if(surf != NULL && (size_t(surf->w) != x || size_t(surf->h) != y)) {
SDL_Surface* const new_image = scale_surface(surf,x,y);
images_.erase(filename);
images_.erase(i_locator);
//free surf twice: once because calling get_image adds a reference.
//again because we also want to remove the cache reference, since
@ -283,7 +359,7 @@ SDL_Surface* get_image_dim(const std::string& filename, size_t x, size_t y)
SDL_FreeSurface(surf);
SDL_FreeSurface(surf);
images_[filename] = new_image;
images_[i_locator] = new_image;
sdl_add_ref(new_image);
return new_image;
}
@ -313,7 +389,7 @@ SDL_Surface* reverse_image(SDL_Surface* surf)
return rev;
}
void register_image(const std::string& id, SDL_Surface* surf)
void register_image(const image::locator& id, SDL_Surface* surf)
{
if(surf == NULL) {
return;
@ -327,6 +403,11 @@ void register_image(const std::string& id, SDL_Surface* surf)
images_[id] = surf;
}
void register_image(const std::string &id, SDL_Surface* surf)
{
register_image(locator(id), surf);
}
SDL_Surface* getMinimap(int w, int h, const gamemap& map,
int lawful_bonus, const team* tm)
{

View file

@ -22,6 +22,22 @@ class team;
/// - greyed: images are scaled and in greyscale
/// - brightened: images are scaled and brighter than normal.
namespace image {
///a generic image locator. Abstracts the location of an image.
///used as a key for a std::map
struct locator
{
locator(const char *filename) : filename(filename) { type = FILE; }
locator(const std::string& filename) : filename(filename) { type = FILE; }
locator(const std::string& filename, const gamemap::location& loc) : filename(filename), loc(loc)
{ type = SUB_FILE; }
bool operator==(const locator &a) const;
bool operator<(const locator &a) const;
enum { FILE, SUB_FILE } type;
std::string filename;
gamemap::location loc;
};
///the image manager is responsible for setting up images, and destroying
///all images when the program exits. It should probably
@ -64,13 +80,13 @@ namespace image {
///function to get the surface corresponding to an image.
///note that this surface must be freed by the user by calling
///SDL_FreeSurface()
SDL_Surface* get_image(const std::string& filename,TYPE type=SCALED, COLOUR_ADJUSTMENT adj=ADJUST_COLOUR);
SDL_Surface* get_image(const locator& i_locator,TYPE type=SCALED, COLOUR_ADJUSTMENT adj=ADJUST_COLOUR);
///function to get a scaled image, but scale it to specific dimensions.
///if you later try to get the same image using get_image() the image will
///have the dimensions specified here.
///Note that this surface must be freed by the user by calling SDL_FreeSurface
SDL_Surface* get_image_dim(const std::string& filename, size_t x, size_t y);
SDL_Surface* get_image_dim(const locator& i_locator, size_t x, size_t y);
///function to reverse an image. The image MUST have originally been returned from
///an image:: function. Returned images have the same semantics as for get_image()
@ -82,7 +98,7 @@ namespace image {
///it when the cache is cleared (change of video mode or colour adjustment).
///If there is already an image registered with this id, that image will be freed
///and replaced with this image.
void register_image(const std::string& id, SDL_Surface* surf);
void register_image(const locator& i_locator, SDL_Surface* surf);
///function to create the minimap for a given map
///the surface returned must be freed by the user

View file

@ -147,6 +147,28 @@ bool gamemap::location::operator<(const gamemap::location& a) const
return x < a.x || x == a.x && y < a.y;
}
gamemap::location gamemap::location::operator+(const gamemap::location& a) const
{
gamemap::location ret = *this;
ret += a;
return ret;
}
gamemap::location &gamemap::location::operator+=(const gamemap::location &a)
{
bool parity = (x & 1) != 0;
x += a.x;
y += a.y;
if((a.x > 0) && (a.x % 2) && parity)
y++;
if((a.x < 0) && (a.x % 2) && !parity)
y--;
return *this;
}
gamemap::location gamemap::location::get_direction(
gamemap::location::DIRECTION dir) const
{
@ -339,6 +361,11 @@ const terrain_type& gamemap::get_terrain_info(TERRAIN terrain) const
return default_terrain;
}
const terrain_type& gamemap::get_terrain_info(const gamemap::location &loc) const
{
return get_terrain_info(get_terrain(loc));
}
const std::vector<gamemap::TERRAIN>& gamemap::get_terrain_precedence() const
{
return terrainPrecedence_;
@ -401,6 +428,6 @@ const std::map<gamemap::TERRAIN,size_t>& gamemap::get_weighted_terrain_frequenci
}
void gamemap::remove_from_border_cache(const location &loc) {
borderCache_.erase(loc);
borderCache_.erase(loc);
}

View file

@ -68,6 +68,9 @@ public:
bool operator<(const location& a) const;
bool operator==(const location& a) const;
bool operator!=(const location& a) const;
// Adds an absolute location to a "delta" location
location operator+(const location &a) const;
location &operator+=(const location &a);
location get_direction(DIRECTION d) const;
@ -131,6 +134,9 @@ public:
//for a given type of terrain
const terrain_type& get_terrain_info(TERRAIN terrain) const;
//shortcut to get_terrain_info(get_terrain(loc))
const terrain_type& get_terrain_info(const location &loc) const;
//gets the list of which terrain types should display on top of
//other terrain types. Has no effect on gameplay, only display.
const std::vector<TERRAIN>& get_terrain_precedence() const;

View file

@ -381,6 +381,49 @@ SDL_Surface* adjust_surface_alpha_add(SDL_Surface* surface, int amount)
return clone_surface(surf);
}
// Applies a mask on a surface
SDL_Surface* mask_surface(SDL_Surface* surface, SDL_Surface* mask)
{
if(surface == NULL) {
return NULL;
}
scoped_sdl_surface surf(make_neutral_surface(surface));
scoped_sdl_surface nmask(make_neutral_surface(mask));
if(surf == NULL || nmask == NULL) {
std::cerr << "could not make neutral surface...\n";
return NULL;
}
{
surface_lock lock(surf);
surface_lock mlock(nmask);
Uint32* beg = lock.pixels();
Uint32* end = beg + surf->w*surf->h;
Uint32* mbeg = mlock.pixels();
Uint32* mend = mbeg + nmask->w*nmask->h;
while(beg != end && mbeg != mend) {
Uint8 red, green, blue, alpha;
Uint8 mred, mgreen, mblue, malpha;
SDL_GetRGBA(*beg,surf->format,&red,&green,&blue,&alpha);
SDL_GetRGBA(*mbeg,nmask->format,&mred,&mgreen,&mblue,&malpha);
alpha = Uint8(minimum<int>(malpha, alpha));
*beg = SDL_MapRGBA(surf->format,red,green,blue,alpha);
++beg;
++mbeg;
}
}
return clone_surface(surf);
}
SDL_Surface* blend_surface(SDL_Surface* surface, double amount, Uint32 colour)
{
if(surface == NULL) {

View file

@ -55,6 +55,7 @@ SDL_Surface* brighten_image(SDL_Surface* surface, double amount);
SDL_Surface* get_surface_portion(SDL_Surface* src, SDL_Rect& rect);
SDL_Surface* adjust_surface_alpha(SDL_Surface* surface, double amount);
SDL_Surface* adjust_surface_alpha_add(SDL_Surface* surface, int amount);
SDL_Surface* mask_surface(SDL_Surface* surface, SDL_Surface* mask);
SDL_Surface* blend_surface(SDL_Surface* surface, double amount, Uint32 colour);
SDL_Surface* flip_surface(SDL_Surface* surface);
SDL_Surface* flop_surface(SDL_Surface* surface);

Binary file not shown.

Binary file not shown.

129
tools/mk-castle-mt.sh Executable file
View file

@ -0,0 +1,129 @@
#!/bin/bash
#
syntaxerror() {
echo "usage: $0 [-b <basename>] [-c <cutout>] [-m <mask>] [-o <output>] [-t <template>] file"
exit 1
}
cutoutf="cutout.png"
maskf="mask.png"
output="castle"
basename="castle"
template="castle-walls.tmpl"
while [ $# -ne 0 ]; do
case "$1" in
-c* )
shift
cutoutf="$1"
shift
;;
-m* )
shift
maskf="$1"
shift
;;
-o* )
shift
output="$1"
shift
;;
-b* )
shift
basename="$1"
shift
;;
-t* )
shift
template="$1"
shift
;;
-* )
syntaxerror
;;
* )
file="$1"
shift
;;
esac
done
if [ -z "$file" ]; then
syntaxerror
fi
# File location definitions
tmpdir=`mktemp -d /tmp/mk-castle.XXXXXXX`
mask="$tmpdir/mask.pgm"
cutout="$tmpdir/cutout.pbm"
castle="$tmpdir/castle.ppm"
alpha="$tmpdir/alpha.pgm"
# Creates pnm files from the original png files
pngtopnm "$maskf" | ppmtopgm | pnminvert > "$mask"
pngtopnm "$cutoutf" > "$cutout"
pngtopnm "$file" > "$castle"
pngtopnm -alpha "$file" > "$alpha"
# Generates the 12 cutout masks from the cutout file
colors="rgbi:1/0/0 rgbi:1/0/1 rgbi:0/0/1 rgbi:0/1/1 rgbi:0/1/0 rgbi:1/1/0 rgbi:.5/0/0 rgbi:.5/0/.5 rgbi:0/0/.5 rgbi:0/.5/.5 rgbi:0/.5/0 rgbi:.5/.5/0"
i=0
for color in $colors; do
cmask[$i]="$tmpdir/cmask$i.pbm"
ppmcolormask $color $cutout | pnminvert > ${cmask[$i]}
i=$(($i+1))
done
# Cuts the castle, and the castle's alpha, according to the 12 masks
# directions="concave-ne concave-e concave-se concave-sw concave-w concave-nw convex-ne convex-e convex-se convex-sw convex-w convex-nw"
directions=`cat $template`
i=0
for direction in $directions; do
ccastle[$i]="$tmpdir/$basename-$direction"
calpha[$i]="$tmpdir/alpha-$direction"
pnmarith -multiply ${cmask[$i]} $castle > ${ccastle[$i]}.ppm
pnmarith -multiply ${cmask[$i]} $alpha > ${calpha[$i]}.pgm
i=$(($i+1))
done
# Cuts each image according to the size it should have
dimensions="54,36-126,144 54,36-126,180 54,108-126,144 0,72-126,160 0,72-126,144 0,0-126,180 162,36-126,144 162,36-126,180 162,108-126,144 108,72-126,160 108,72-126,144 108,0-126,180"
i=0
for dimension in $dimensions; do
position=${dimension%-*}
size=${dimension#*-}
posx=${position%,*}
posy=${position#*,}
sizex=${size%,*}
sizey=${size#*,}
pnmcut -left $posx -top $posy -width $sizex -height $sizey ${ccastle[$i]}.ppm > ${ccastle[$i]}-x.ppm
pnmcut -left $posx -top $posy -width $sizex -height $sizey ${calpha[$i]}.pgm > ${calpha[$i]}-x.ppm
pnmtopng -alpha ${calpha[$i]}-x.ppm ${ccastle[$i]}-x.ppm > ${ccastle[$i]}.png
i=$(($i+1))
done
# Harvests the generated PNGs, and erase all those generated files.
if [ ! -d "$output" ]; then
mkdir $output
fi
cp "$tmpdir"/*.png "$output"
rm -r "$tmpdir"

View file

@ -137,8 +137,8 @@ hexcorners_names_even=(ne se w)
hexcorners_odd="70,35 17,70 17,0"
hexcorners_names_odd=(e sw nw)
margin=200 # Adds a margin to files
width=70
height=70
width=72
height=72
i=0
for angle in ${angles[*]}; do