Adding support for multi-hex tiles. Some features still missing.
This commit is contained in:
parent
a9fcf3554d
commit
405610f2b0
20 changed files with 844 additions and 214 deletions
100
data/built.cfg
100
data/built.cfg
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
424
src/builder.cpp
424
src/builder.cpp
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
141
src/image.cpp
141
src/image.cpp
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
29
src/map.cpp
29
src/map.cpp
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
BIN
tools/castle.png
BIN
tools/castle.png
Binary file not shown.
BIN
tools/cutout.png
BIN
tools/cutout.png
Binary file not shown.
129
tools/mk-castle-mt.sh
Executable file
129
tools/mk-castle-mt.sh
Executable 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"
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue