1 |
/* |
2 |
Copyright (C) 2001-2004 Stephane Magnenat & Luc-Olivier de Charrière |
3 |
for any question or comment contact us at nct@ysagoon.com or nuage@ysagoon.com |
4 |
|
5 |
This program is free software; you can redistribute it and/or modify |
6 |
it under the terms of the GNU General Public License as published by |
7 |
the Free Software Foundation; either version 2 of the License, or |
8 |
(at your option) any later version. |
9 |
|
10 |
This program is distributed in the hope that it will be useful, |
11 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
GNU General Public License for more details. |
14 |
|
15 |
You should have received a copy of the GNU General Public License |
16 |
along with this program; if not, write to the Free Software |
17 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 |
*/ |
19 |
|
20 |
/*! \file Map.h |
21 |
\brief The class (and sub-classes) that define the squares making up the map itself. It is made up of 32x32 pixel cells |
22 |
|
23 |
Map can be called as: |
24 |
Map[x, y] to get the cell at (x, y), or |
25 |
Map(x, y) to get the sector at (x, y) |
26 |
|
27 |
By extension, you can do: |
28 |
Map[x, y][TeamNumber] to get the TeamCellData at a given point |
29 |
Map[x, y][TeamNumber][ElevationNumber] to get the given type of elevation |
30 |
|
31 |
All functions are wrap-safe, unless otherwise specified. |
32 |
*/ |
33 |
|
34 |
#ifndef __MAP_H |
35 |
#define __MAP_H |
36 |
|
37 |
#include <list> |
38 |
#include <assert.h> |
39 |
|
40 |
#include "Building.h" |
41 |
#include "Sector.h" |
42 |
#include "Team.h" |
43 |
#include "TerrainType.h" |
44 |
#include "BitArray.h" |
45 |
|
46 |
class Unit; |
47 |
|
48 |
class Map; |
49 |
class Game; |
50 |
class MapGenerationDescriptor; |
51 |
class SessionGame; |
52 |
|
53 |
//! Since water and grass clash but sand doesn't it's useful that bitwise ANDing any two resources tells you whether there's a clash |
54 |
typedef undertile_t enum { NO_RESOURCE=0, WATER=1, GRASS=2, SAND=3 }; |
55 |
const int undertile_total = 4; |
56 |
typedef terrain_t Uint8; |
57 |
typedef terrain_variant_t Uint8; |
58 |
const terrain_variant_t terrain_variant_t_max = 255; |
59 |
|
60 |
typedef cell_xpos_t int; |
61 |
typedef cell_ypos_t int; |
62 |
typedef cell_pos_t unsigned int; |
63 |
typedef cell_width_t unsigned int; |
64 |
typedef cell_height_t unsigned int; |
65 |
typedef cell_size_t unsigned int; |
66 |
typedef cell_length_t unsigned int; |
67 |
|
68 |
|
69 |
typedef sector_xpos_t int; |
70 |
typedef sector_ypos_t int; |
71 |
typedef sector_width_t unsigned int; |
72 |
typedef sector_height_t unsigned int; |
73 |
typedef sector_size_t unsigned int; |
74 |
|
75 |
typedef pixel_xpos_t int; |
76 |
typedef pixel_ypos_t int; |
77 |
typedef pixel_width_t unsigned int; |
78 |
typedef pixel_height_t unsigned int; |
79 |
typedef pixel_size_t unsigned int; |
80 |
|
81 |
typedef team_ID_t unsigned int; |
82 |
typedef team_count_t unsigned int; |
83 |
|
84 |
typedef elevation_t Uint8; |
85 |
typedef elevation_sum_t Uint32; // Can hold the sum of two elevations |
86 |
typedef elevation_offset_t Sint8; |
87 |
typedef elevation_max 255; |
88 |
typedef elevation_min 3; // Least interesting elevation |
89 |
typedef elevation_index_t enum { FORBIDDEN_SWIM=0; FORBIDDEN_NOSWIM=1 GUARD_SWIM=2; GUARD_NOSWIM=3; RESOURCE_SWIM=4; RESOURCE_NOSWIM=RESOURCE_SWIM + MAX_NB_RESOURCES }; |
90 |
static const elevation_index_t TotalElevations = RESOURCE_NOSWIM + MAX_NB_RESOURCES; |
91 |
|
92 |
typedef resource_enum_t enum { WOOD=0; WHEAT=1; PAPYRUS=2; STONE=3; ALGA=4; NO_RES_TYPE=0xFF }; |
93 |
typedef resource_t Uint8; |
94 |
const resource_t resource_t_max = 255; |
95 |
typedef resource_variety_t Uint8; |
96 |
const resource_t resource_variety_t_max = 255; |
97 |
typedef resource_amount_t Uint8; |
98 |
const resource_t resource_amount_t_max = 255; |
99 |
typedef resource_animation_t Uint8; |
100 |
const resource_t resource_animation_t_max = 255; |
101 |
const resource_t resource_t_count = 5; |
102 |
const resource_t resource_t_max = 8; |
103 |
const resource_t resource_t_absolute_max = 15; |
104 |
const resource_t happiness_base = resource_t_count; |
105 |
const resource_t happiness_count = resource_t_max - resource_t_count; |
106 |
|
107 |
//! This should go in Team.h or somewhere more appropriate eventually |
108 |
class TeamCellData : friend Map { |
109 |
private: |
110 |
Forbidden : 1; |
111 |
Guarded : 1; |
112 |
Cleared : 1; |
113 |
Discovered : 1; |
114 |
FogOfWarA : 1; |
115 |
FogOfWarB : 1; |
116 |
|
117 |
elevation_t Elevations[TotalElevations]; |
118 |
|
119 |
public: |
120 |
|
121 |
const elevation_t& GetElevation(const elevation_index_t g) const { assert(g<TotalElevations); return Elevations[g] }; |
122 |
const elevation_t& operator[] (const elevation_index_t g) const { return GetElevation(g) }; |
123 |
|
124 |
const elevation_t ResourceAvailable(const resource_enum_t R, const bool CanSwim) const |
125 |
{ return Elevations[(CanSwim?RESOURCE_SWIM:RESOURCE_NOSWIM) + R]>1?Elevations[(CanSwim?RESOURCE_SWIM:RESOURCE_NOSWIM) + R]:0 }; |
126 |
|
127 |
protected: |
128 |
|
129 |
TeamCellData() : Forbidden(), Guarded(), Cleared(), Discovered(), FogOfWarA(), FogOfWarB() { for (elevation_index_t n=0; n<TotalElevations; ++n) Elevations[n] = 0 }; |
130 |
TeamCellData(const TeamCellData& M) : Forbidden(M.Forbidden), Guarded(M.Guarded), Cleared(M.Gleared), Discovered(M.Discovered), FogOfWarA(M.FogOfWarA), FogOfWarB(M.FogOfWarB) |
131 |
{ for (elevation_index_t n=0; n<TotalElevations; ++n) Elevations[n] = M.Elevations[n] }; |
132 |
TeamCellData(const GAGCore::InputStream *stream, const SessionGame *const sessionGame, const Game *const game=NULL); |
133 |
void save(GAGCore::OutputStream *stream); |
134 |
|
135 |
const bool Forbidden() const { return Forbidden; }; |
136 |
const bool Guarded() const { return Guarded; }; |
137 |
const bool Cleared() const { return Cleared; }; |
138 |
const bool Discovered() const { return Discovered; }; |
139 |
const bool Fog(const bool which) const { return which?FogOfWarA:FogOfWarB; }; |
140 |
|
141 |
const bool Forbidden(const bool F) { return Forbidden = F; }; |
142 |
const bool Guarded(const bool G) { return Guarded = G; }; |
143 |
const bool Cleared(const bool C) { return Cleared = C; }; |
144 |
const bool Discovered(const bool D) { return Discovered = D; }; |
145 |
const bool Fog(const bool which, const bool F) { return which?FogOfWarA=F:FogOfWarB=F }; |
146 |
|
147 |
const bool FlipForbidden() { return Forbidden = !Forbidden; }; |
148 |
const bool FlipGuarded() { return Guarded = !Guarded; }; |
149 |
const bool FlipCleared() { return Cleared = !Cleared; }; |
150 |
const bool FlipDiscovered() { return Discovered = !Discovered; }; |
151 |
const bool Fog(const bool which) { return which?FogOfWarA = !FogOfWarA:FogOfWarB = !FogOfWarB; }; |
152 |
|
153 |
}; |
154 |
|
155 |
template<cell_width_t CellWidth, cell_height_t CellHeight, bool verbose> class Map { |
156 |
|
157 |
public: |
158 |
/** The Map::Cell class defines all information about a single 32x32 pixel piece of the map |
159 |
That includes what terrain, underterrain, resources, elevations, folbidden/guarded/clearing/etc. state, and the ground unit/air unit/building on the tile. |
160 |
**/ |
161 |
class Cell { |
162 |
|
163 |
private: |
164 |
// Terrain is based on the underterrain types for the tiles immediately below-right, below-left, above-left, and above-right the current tile |
165 |
// Underterrain types are not stored explicitly - each cell is considered to hold the canonical version of its top-left subtile |
166 |
terrain_t Terrain; |
167 |
terrain_variant_t TerrainVariant; |
168 |
|
169 |
// Resources |
170 |
resource_t ResourceType; |
171 |
resource_amount_t ResourceAmount; |
172 |
resource_variety_t ResourceVariety; |
173 |
resource_animation_t ResourceAnimation; |
174 |
|
175 |
Unit* groundUnit; |
176 |
Unit* AirUnit; |
177 |
Building* Building; |
178 |
|
179 |
std::Vector<TeamCellData> TeamData; |
180 |
|
181 |
public: |
182 |
|
183 |
Cell(const std::Vector<TeamCellData>::size_type Teams=0, terrain_t T=WATER, |
184 |
const resource_t RT=NO_RES_TYPE, const resource_amount_t RAM=resource_amount_max, const resource_variety_t RV=resource_variety_max, const resource_animation_t RAN=resource_animation_max) |
185 |
: Terrain(T), TerrainSubType(SyncRand%(terrain_variant_max+1)), Resource(), GroundUnit(), AirUnit(), Building(), TeamData(Teams), |
186 |
ResourceType(RT), ResourceVariety(RV), ResourceAmount(RAM), ResourceAnimation(RAN) {}; |
187 |
Cell(const Cell& M) : UnderTile(M.UnderTile), TerrainSubType(M.UnderTile), TerrainType(M.TerrainType), Resource(M.Resource), |
188 |
GroundUnit(M.GroundUnit), AirUnit(M.AirUnit), Building(M.Building), TeamData(M.TeamData) {}; |
189 |
Cell(const GAGCore::InputStream *stream, const SessionGame *const sessionGame, const Game *const game=NULL); |
190 |
void save(GAGCore::OutputStream *stream); |
191 |
|
192 |
//! Simple resource functions |
193 |
const bool HasResource() const { return ResourceType != NO_RES_TYPE }; |
194 |
const resource_t ResourceType() const { return ResourceType }; |
195 |
const resource_amount_t ResourceAmount() const { return ResourceAmount }; |
196 |
const resource_variety_t ResourceVariety() const { return ResourceVariety }; |
197 |
const resource_animation_t ResourceAnimation() const { return ResourceAnimation }; |
198 |
protected: |
199 |
void incResource() { ++ResourceAmount }; |
200 |
void decResource() { --ResourceAmount }; |
201 |
public: |
202 |
Uint32 Resource() { return animation | (amount<<8) | (variety<<16) | (type<<24); } |
203 |
void Resource(const resource_t RT, const resource_amount_t RAM, const resource_variety_t RV=resource_variety_max, const resource_animation_t RAN=resource_animation_max) { if (!GroundUnit && Resource::Compatible(r, Terrain)) { ResourceType=RT; ResourceAmount=RAM; ResourceAmount=0; ResourceVariety=RV; ResourceAnimation=RAN }; |
204 |
|
205 |
//! Simple terrain functions |
206 |
const terrain_t UnderTileBR(const undertile_t br) { return (Terrain &= 0x3F) |= (br << 6) }; |
207 |
const terrain_t UnderTileBL(const undertile_t bl) { return (Terrain &= 0xCF) |= (bl << 4) }; |
208 |
const terrain_t UnderTileTR(const undertile_t tr) { return (Terrain &= 0xF3) |= (tr << 2) }; |
209 |
const terrain_t UnderTileTL(const undertile_t tl) { return (Terrain &= 0xFC) |= (tl << 0); }; |
210 |
const terrain_t UnderTile (const undertile_t t) { return UnderTileTL(t); }; // top-left undertile |
211 |
const terrain_t UnderTile() const { return Terrain & 0x03; }; // top-left undertile |
212 |
|
213 |
const terrain_t Terrain(const undertile_t br, const undertile_t bl, const undertile_t tr) { Terrain = (br << 6) | (bl << 4) | (tr << 2) | UnderTile() }; |
214 |
const terrain_t Terrain(const Cell& br , const Cell& bl , const Cell& tr ) { Terrain = (br.Terrain << 6) | (bl.UnderTile() << 4) | (tr.UnderTile() << 2) | UnderTile() }; |
215 |
const terrain_t Terrain(const terrain_t T) { return Terrain = T; }; |
216 |
const terrain_t Terrain() const { return Terrain }; |
217 |
|
218 |
const terrain_variant_t TerrainVariant(const terrain_variant_t V) { return TerrainVariant = V }; |
219 |
const terrain_variant_t TerrainVariant() { return TerrainVariant }; |
220 |
|
221 |
//! Complex terrain functions |
222 |
|
223 |
//! Is the undertile of this cell allowed next to something? (water is not allowed next to grass) |
224 |
const bool AllowedNextTo(const undertile_t t) const { return Undertile() & t }; |
225 |
const bool AllowedNextTo(const Cell& c) const { return Undertile() & c.UnderTile() }; |
226 |
void MaybeToSand(const undertile_t t) { if (!AllowedNextTo(t)) Undertile(SAND) }; |
227 |
void MaybeToSand(const Cell& c) { if (!AllowedNextTo(c.UnderTile())) Undertile(SAND) }; |
228 |
void MaybeToSand(const Cell& c1, const Cell& c2, const Cell& c3) |
229 |
{ if !(AllowedNextTo(c1) && AllowedNextTo(c2) && AllowedNextTo(c3) && AllowedNextTo(c3)) Undertile(SAND) }; |
230 |
void MaybeToSand(const undertile_t t1, const undertile_t t2, const undertile_t t3) |
231 |
{ if !(AllowedNextTo(t1) && AllowedNextTo(t2) && AllowedNextTo(t3) && AllowedNextTo(t3)) Undertile(SAND) }; |
232 |
//! Constants defining the "proper" terrain types, where all the surrounding undertiles are of the same type |
233 |
static const terrain_t Water = (WATER << 6) | (WATER << 4) | (WATER << 2 ) | WATER; |
234 |
static const terrain_t Sand = (SAND << 6) | (SAND << 4) | (SAND << 2 ) | SAND ; |
235 |
static const terrain_t Grass = (GRASS << 6) | (GRASS << 4) | (GRASS << 2 ) | GRASS; |
236 |
//! Check "Proper" types of terrain |
237 |
const bool isWater() const { return !HasResource() && Terrain == Water; }; |
238 |
const bool isSand () const { return !HasResource() && Terrain == Sand ; }; |
239 |
const bool isGrass() const { return !HasResource() && Terrain == Grass; }; |
240 |
//! do any of the surrounding undertiles disagree? |
241 |
const bool isTransitional() const { return Terrain != Water && Terrain != Sand && Terrain != Grass }; |
242 |
//! Is at least one of the surrounding undertiles of the given type? |
243 |
const bool isWatery() const { return (Terrain&0xC0 == WATER<<6) || (Terrain&0x30 == WATER<<4) || (Terrain&0x0C == WATER<<2) || (Terrain&0x03 == WATER<<0) }; |
244 |
const bool isSandy () const { return (Terrain&0xC0 == SAND <<6) || (Terrain&0x30 == SAND <<4) || (Terrain&0x0C == SAND <<2) || (Terrain&0x03 == SAND <<0) }; |
245 |
const bool isGrassy() const { return (Terrain&0xC0 == GRASS<<6) || (Terrain&0x30 == GRASS<<4) || (Terrain&0x0C == GRASS<<2) || (Terrain&0x03 == GRASS<<0) }; |
246 |
//! Which is the most frequent undertile type? |
247 |
const bool isMostlyWater() { |
248 |
int totals[undertile_total]; totals[WATER]==0; totals[SAND]==0; totals[GRASS]==0; |
249 |
++totals[UnderTileBR]; ++totals[UnderTileBL]; ++totals[UnderTileTR]; ++totals[UnderTileTL]; |
250 |
return totals[WATER] > totals[SAND]; // Note: water can't border on grass |
251 |
}; |
252 |
const bool isMostlySand() { |
253 |
int totals[undertile_total]; totals[WATER]==0; totals[SAND]==0; totals[GRASS]==0; |
254 |
++totals[UnderTileBR]; ++totals[UnderTileBL]; ++totals[UnderTileTR]; ++totals[UnderTileTL]; |
255 |
return totals[SAND] > totals[WATER] && totals[SAND] > totals[GRASS]; |
256 |
}; |
257 |
const bool isMostlyGrass() { |
258 |
int totals[undertile_total]; totals[WATER]==0; totals[SAND]==0; totals[GRASS]==0; |
259 |
++totals[UnderTileBR]; ++totals[UnderTileBL]; ++totals[UnderTileTR]; ++totals[UnderTileTL]; |
260 |
return totals[GRASS] > totals[SAND]; // Note: water can't border on grass |
261 |
}; |
262 |
const terrain_t isMostly() { |
263 |
int totals[undertile_total]; totals[WATER]==0; totals[SAND]==0; totals[GRASS]==0; |
264 |
++totals[UnderTileBR]; ++totals[UnderTileBL]; ++totals[UnderTileTR]; ++totals[UnderTileTL]; |
265 |
if (totals[WATER] > totals[SAND ]) return WATER; |
266 |
if (totals[GRASS] > totals[SAND ]) return GRASS; |
267 |
if (totals[SAND ] > totals[WATER] && totals[SAND ] > totals[GRASS]) return SAND; |
268 |
return NO_RESOURCE; |
269 |
}; |
270 |
|
271 |
// Accessing other members |
272 |
const Unit* groundUnit() const { return groundUnit }; |
273 |
const Unit* airUnit() const { return airUnit }; |
274 |
const Building* Building() const { return Building }; |
275 |
|
276 |
const Unit* groundUnit(Unit* g) { return groundUnit = g }; |
277 |
const Unit* airUnit(Unit* a) { return airUnit = a }; |
278 |
|
279 |
const Building *const Building() const { return Building }; |
280 |
const Building *const Building(Building* b) { return Building = b }; |
281 |
|
282 |
const TeamCellData& teamCellData(team_ID_t Team) const { assert(Team<Teams); return TeamCellData[Team]; }; |
283 |
const TeamCellData& operator[](team_ID_t Team) const { return teamCellData(Team); }; |
284 |
|
285 |
bool isResourceAllowed(const resource_enum_t type) { return UnderTile()==globalContainer->resourcesTypes.get(type)->terrain }; |
286 |
|
287 |
//! Return true if unit can go to position (x,y) |
288 |
bool isFreeForGroundUnit(bool canSwim, team_ID_t Team) const |
289 |
{ return !(Building || groundUnit || Resource.hasResource || TeamData[Team].Forbidden()) && (canSwim || !isWater); }; |
290 |
bool isFreeForGroundUnitNoForbidden(bool canSwim) const |
291 |
{ return !(Building || groundUnit || Resource.hasResource) && (canSwim || !isWater); }; |
292 |
//! i.e. It's free if you don't count other ground units |
293 |
bool isFreeForGroundUnitNoGroundUnits(bool canSwim, team_ID_t Team) const |
294 |
{ return !(Building || Resource.hasResource || TeamData[Team].Forbidden()) && (canSwim || !isWater); }; |
295 |
|
296 |
bool isFreeForAirUnit() { return airUnit; }; |
297 |
bool isFreeForBuilding() { return !(Building || GroundUnit || Resource.hasResource) && isGrass() } |
298 |
bool isFreeForBuildingNoGroundUnits() { return !(Building || Resource.hasResource) && isGrass() } |
299 |
|
300 |
bool checkSum(Uint32& cs) { |
301 |
for (TeamValue P=0; P<Teams; ++P) { TeamData[P].checkSum(cs) }; |
302 |
cs += (Terrain<<4) + TerrainSubType + Building->GID + resource.getUint32() + (groundUnit?groundUnit->GID:0) + (airUnit?airUnit->GID:0); |
303 |
cs = (cs<<1)|(cs>>31); |
304 |
}; |
305 |
}; |
306 |
|
307 |
//! Basic lengths and widths |
308 |
static const sector_width_t SectorWidth = Width / 16; |
309 |
static const sector_height_t SectorHeight = CellHeight / 16; |
310 |
static const sector_size_t SectorSize = SectorWidth * SectorHeight; |
311 |
static const cell_size_t CellSize = CellWidth * CellHeight; |
312 |
|
313 |
//! Conversion functions |
314 |
static const pixel_xpos_t ToPixelXpos(const cell_xpos_t X) { return X * 32 }; |
315 |
static const pixel_ypos_t ToPixelYPos(const cell_ypos_t Y) { return Y * 32 }; |
316 |
static const pixel_xpos_t ToDisplayableXpos(cell_xpos_t X, const cell_xpos_t ViewportX) { return (X -= viewportX)>(CellWidth /2)?X-CellWidth :X }; |
317 |
static const pixel_ypos_t ToDisplayableYpos(cell_ypos_t Y, const cell_ypos_t ViewportY) { return (Y -= viewportY)>(CellHeight/2)?Y-CellHeight:Y }; |
318 |
|
319 |
static const cell_xpos_t ToXposAligned (const pixel_xpos_t X, const cell_xpos_t ViewportX) { return ((X>>5) +ViewportX) % CellWidth }; |
320 |
static const cell_ypos_t ToYposAligned (const pixel_ypos_t Y, const cell_ypos_t ViewportY) { return ((Y>>5) +ViewportY) % CellHeight }; |
321 |
static const cell_xpos_t ToXposUnaligned(const pixel_xpos_t X, const cell_xpos_t ViewportX) { return (((X+16)>>5) +ViewportX) % CellWidth }; |
322 |
static const cell_ypos_t ToYposUnaligned(const pixel_ypos_t Y, const cell_ypos_t ViewportY) { return (((X+16)Y>>5)+ViewportY) % CellHeight }; |
323 |
|
324 |
static const cell_xpos_t ToBuildingXpos(const pixel_xpos_t X, const pixel_width_t W, const cell_xpos_t ViewportX) { return (((X + (W&1)?0:16) >> 5) + ViewportX) % Xpos ; }; |
325 |
static const cell_ypos_t ToBuildingYpos(const pixel_ypos_t Y, const pixel_height_t H, const cell_ypos_t ViewportY) { return (((Y + (H&1)?0:16) >> 5) + ViewportY) % Ypos; }; |
326 |
|
327 |
static const cell_xpos_t ToCursorXpos(const pixel_xpos_t X, const pixel_Xpos_t W, const cell_xpos_t ViewportX) { return ToDisplayableXpos(X, ViewportX) + W*16; }; |
328 |
static const cell_ypos_t ToCursorYpos(const pixel_ypos_t Y, const pixel_Ypos_t H, const cell_ypos_t ViewportY) { return ToDisplayableYpos(Y, ViewportY) + H*16; }; |
329 |
|
330 |
//! Information about grid positions |
331 |
static const cell_length_t const warpDistSquare(cell_xpos_t X1, cell_ypos_t Y1, const cell_xpos_t X2, const cell_ypos_t Y2) { //! The distance between (px, py) and (qx, qy), warp-safe, but not rooted. |
332 |
return ((X1 = abs(X1-X2) % CellWidth )>(CellWidth /2)?(X1 = CellWidth - X1) * X1 : X1*X1) |
333 |
+ |
334 |
((Y1 = abs(Y1-Y2) % CellHeight)>(CellHeight/2)?(Y1 = CellHeight - Y1) * Y1 : Y1*Y1); |
335 |
}; |
336 |
static const cell_length_t const warpDistMax(cell_xpos_t X1, cell_ypos_t Y1, const cell_xpos_t X2, const cell_ypos_t Y2) { //! The max distance on x or y axis, between (px, py) and (qx, qy), warp-safe. |
337 |
return ((X1 = abs(X1-X2) % CellWidth )>(CellWidth /2)?X1 = abs(CellWidth - X1) : X1) |
338 |
> |
339 |
((Y1 = abs(Y1-Y2) % CellHeight)>(CellHeight/2)?Y1 = abs(CellHeight - Y1) : Y1) |
340 |
?X1:Y1; |
341 |
}; |
342 |
static const bool const isInLocalElevation(const cell_xpos_t uX, const cell_ypos_t uY, const cell_xpos_t bX, const cell_ypos_t bY) { //! Return true if the unit @(ux, uy) is close enough to building @(bx, by). |
343 |
cell_xpos_t dX = abs(uX-bX) % CellWidth; |
344 |
cell_ypos_t dY = abs(uY-bY) % CellHeight; |
345 |
if ((dX>(CellWidth /2)?dX = abs(CellWidth - dX) : dX) |
346 |
> |
347 |
(dY>(CellHeight/2)?dY = abs(CellWidth - dY) : dY)) |
348 |
return (dX==15)?((bX + 15) % Width) == (uX % Width):dX<15; |
349 |
else if (dX < dY) |
350 |
return (dY==15)?((bY + 15) % Width) == (uY % Width):dY<15; |
351 |
else |
352 |
return (((bX + 15) % Width) == (uX % Width)) && (((bY + 15) % Width) == (uY % Width)); |
353 |
}; |
354 |
|
355 |
private: |
356 |
Cell Cells[CellWidth, CellHeight]; |
357 |
Sector Sectors[SectorWidth * SectorHeight]; |
358 |
FILE *logFile; |
359 |
team_count_t MaxTeams; // The highest number of teams that have been seen |
360 |
|
361 |
//! Elevations to be dirtied - note that individual resources can be dirtied, or the whole lot can be dirtied at once |
362 |
bool DirtyResource[Teams][MAX_NB_RESOURCES]; |
363 |
bool DirtyResources[Teams]; |
364 |
bool DirtyForbidden[Teams]; |
365 |
bool DirtyGuarded[Teams]; |
366 |
bool DirtyBuildings; |
367 |
|
368 |
bool FogOfWarAB; |
369 |
|
370 |
// computationals pathfinding statistics: |
371 |
int resourceAvailableCount[16][MAX_RESOURCES]; |
372 |
int resourceAvailableCountSuccess[16][MAX_RESOURCES]; |
373 |
int resourceAvailableCountFailure[16][MAX_RESOURCES]; |
374 |
|
375 |
// Profiling variables - used to show how much each function is called, and what it tends to return |
376 |
int pathToResourceCountTot; |
377 |
int pathToResourceCountSuccess; |
378 |
int pathToResourceCountFailure; |
379 |
|
380 |
int localResourcesUpdateCount; |
381 |
|
382 |
int pathfindLocalResourceCount; |
383 |
int pathfindLocalResourceCountWait; |
384 |
int pathfindLocalResourceCountSuccessBase; |
385 |
int pathfindLocalResourceCountSuccessLocked; |
386 |
int pathfindLocalResourceCountSuccessUpdate; |
387 |
int pathfindLocalResourceCountSuccessUpdateLocked; |
388 |
int pathfindLocalResourceCountFailureUnusable; |
389 |
int pathfindLocalResourceCountFailureNone; |
390 |
int pathfindLocalResourceCountFailureBad; |
391 |
|
392 |
int pathToBuildingCountTot; |
393 |
|
394 |
int pathToBuildingCountClose; |
395 |
int pathToBuildingCountCloseSuccessStand; |
396 |
int pathToBuildingCountCloseSuccessBase; |
397 |
int pathToBuildingCountCloseSuccessUpdated; |
398 |
int pathToBuildingCountCloseFailureLocked; |
399 |
int pathToBuildingCountCloseFailureEnd; |
400 |
|
401 |
int pathToBuildingCountIsFar; |
402 |
int pathToBuildingCountFar; |
403 |
int pathToBuildingCountFarIsNew; |
404 |
int pathToBuildingCountFarOldSuccess; |
405 |
int pathToBuildingCountFarOldFailureLocked; |
406 |
int pathToBuildingCountFarOldFailureBad; |
407 |
int pathToBuildingCountFarOldFailureRepeat; |
408 |
int pathToBuildingCountFarOldFailureUnusable; |
409 |
int pathToBuildingCountFarUpdateSuccess; |
410 |
int pathToBuildingCountFarUpdateFailureLocked; |
411 |
int pathToBuildingCountFarUpdateFailureVirtual; |
412 |
int pathToBuildingCountFarUpdateFailureBad; |
413 |
|
414 |
int localBuildingElevationUpdate; |
415 |
int localBuildingElevationUpdateLocked; |
416 |
int globalBuildingElevationUpdate; |
417 |
int globalBuildingElevationUpdateLocked; |
418 |
|
419 |
int buildingAvailableCountTot; |
420 |
|
421 |
int buildingAvailableCountClose; |
422 |
int buildingAvailableCountCloseSuccessFast; |
423 |
int buildingAvailableCountCloseSuccessAround; |
424 |
int buildingAvailableCountCloseSuccessUpdate; |
425 |
int buildingAvailableCountCloseSuccessUpdateAround; |
426 |
int buildingAvailableCountCloseFailureLocked; |
427 |
int buildingAvailableCountCloseFailureEnd; |
428 |
|
429 |
int buildingAvailableCountIsFar; |
430 |
int buildingAvailableCountFar; |
431 |
int buildingAvailableCountFarNew; |
432 |
int buildingAvailableCountFarNewSuccessFast; |
433 |
int buildingAvailableCountFarNewSuccessClosely; |
434 |
int buildingAvailableCountFarNewFailureLocked; |
435 |
int buildingAvailableCountFarNewFailureVirtual; |
436 |
int buildingAvailableCountFarNewFailureEnd; |
437 |
int buildingAvailableCountFarOld; |
438 |
int buildingAvailableCountFarOldSuccessFast; |
439 |
int buildingAvailableCountFarOldSuccessAround; |
440 |
int buildingAvailableCountFarOldFailureLocked; |
441 |
int buildingAvailableCountFarOldFailureEnd; |
442 |
|
443 |
int pathfindForbiddenCount; |
444 |
int pathfindForbiddenCountSuccess; |
445 |
int pathfindForbiddenCountFailure; |
446 |
|
447 |
//Used for scheduling computation time. |
448 |
bool elevationUpdated[Teams][MAX_NB_RESOURCES][2]; |
449 |
|
450 |
public: |
451 |
//! Create a new map |
452 |
Map(team_count_t Teams=0, terrain_t T = WATER); |
453 |
//! Copy another map |
454 |
Map(const Map<CellWidth, CellHeight>& M); |
455 |
//! Map destructor |
456 |
~Map(void); |
457 |
|
458 |
//! Load a map from a stream and relink with associated game |
459 |
bool load(const GAGCore::InputStream *stream, const SessionGame *const sessionGame, const Game *const game=0) |
460 |
//! Save a map |
461 |
void save(GAGCore::OutputStream *stream); |
462 |
|
463 |
//! Map generation functions |
464 |
bool makeRandomMap(MapGenerationDescriptor &descriptor); |
465 |
void addResourcesRandomMap(MapGenerationDescriptor &descriptor); |
466 |
bool makeIslandsMap(MapGenerationDescriptor &descriptor); |
467 |
void addResourcesIslandsMap(MapGenerationDescriptor &descriptor); |
468 |
|
469 |
//! Get/set values |
470 |
const Cell& GetCell (const cell_xpos_t x, const cell_ypos_t y) const { y += x / CellWidth; return Cells[x % CellWidth, y % CellHeight] }; // If X overruns, we go onto the next line |
471 |
const Cell& operator[](const cell_xpos_t x, const cell_ypos_t y) const { return GetCell(x, y) }; |
472 |
const Cell& GetCell (const cell_pos_t p) const { return Cells[p % CellWidth, (p / Width) % cellHeight] }; |
473 |
const Cell& operator[](const cell_pos_t p) const { return GetCell(p) }; |
474 |
const Cell& GetCell (const Unit& U) const { return Cells[U.posX % CellWidth, U.posY % CellHeight] }; |
475 |
const Cell& operator[](const Unit& U) const { return GetCell(U) }; |
476 |
|
477 |
void newBullet(const sector_xpos_t x, const sector_ypos_t y, Sint32 px, Sint32 py, Sint32 speedX, Sint32 speedY, Sint32 ticksLeft, Sint32 shootDamage, Sint32 targetX, Sint32 targetY, Sint32 revealX, Sint32 revealY, Sint32 revealW, Sint32 revealH) { |
478 |
Sectors[wSector*((y%Width)>>4)+((x%Width)>>4)].bullets.push_front(new Bullet(px, py, speedX, speedY, ticksLeft, shootDamage, targetX, targetY, revealX, revealY, revealW, revealH)); |
479 |
}; |
480 |
std::list<Bullet*>::const_iterator GetBullets(sector_pos_t i) { return Sectors[i].bullets.begin() }; |
481 |
std::list<Bullet*>::const_iterator GetBulletsEnd(sector_pos_t i) { return Sectors[i].explosions.begin() }; |
482 |
std::list<BulletExplosion*>::const_iterator GetExplosions(sector_pos_t i) { return Sectors[i].bullets.begin() }; |
483 |
std::list<BulletExplosion*>::const_iterator GetExplosionsEnd(sector_pos_t i) { return Sectors[i].explosions.begin() }; |
484 |
|
485 |
const Cell& GetAdjacentCell(const cell_xpos_t x, const cell_ypos_t y) const { return Cells[(x - 1 + (syncRand()&3)) % CellWidth, (y - 1 + (syncRand()&3)) % CellHeight] }; |
486 |
|
487 |
void RemoveTeam() {}; // In the current implementation, we don't need to do anything when a team is removed |
488 |
void Map::addTeam(const team_length_t NewMaximum); |
489 |
|
490 |
|
491 |
//! Call the specified function on each of a rectangle of cells, returning if the function returns false (returns true/false) |
492 |
typedef const bool (*CellIterator)(const Cell&, const cell_xpos_t, cell_ypos_t); |
493 |
bool IterateOverArea(const CellIterator& f, cell_xpos_t X=0, const cell_xpos_t XMax=Width-1, const cell_ypos_t Y=0, const cell_ypos_t YMax=Height-1) const |
494 |
{ |
495 |
cell_xpos_t XStart = X; |
496 |
while (Y <= YMax) { |
497 |
while (X <= XMax) { if (!f(GetCell(X, Y), X, Y)) { return false; }; ++X }; |
498 |
X = XStart; |
499 |
++Y; |
500 |
}; |
501 |
return true; |
502 |
} |
503 |
//! Call the specified function on each of a rectangle of cells, returning if the function returns true ( returns a pointer to cell that triggered success) |
504 |
typedef const bool (*CellFinder)(const Cell&, const cell_xoffset_t, const cell_yoffset_t); |
505 |
const bool FindAdjacentCell(const CellFunction& f, const cell_xpos_t X, const cell_ypos_t Y, cell_xpos_t& DirectionX, cell_ypos_t& DirectionY) const { |
506 |
for (cell_xoffset_t OffsetX=-1; OffsetX<2; ++x) |
507 |
for (cell_yoffset_t OffsetY=-1; OffsetY<2; ++y) |
508 |
if (f(GetCell(X++, Y), OffsetX, OffsetY)) { |
509 |
DirectionX = OffsetX; |
510 |
DirectionY = OffsetY; |
511 |
return true; |
512 |
}; |
513 |
return false; |
514 |
}; |
515 |
const bool FindAdjacentCell(const CellFunction& f, const cell_xpos_t X, const cell_ypos_t Y) const { |
516 |
for (cell_xoffset_t OffsetX=-1; OffsetX<2; ++x) |
517 |
for (cell_yoffset_t OffsetY=-1; OffsetY<2; ++y) |
518 |
if (f(GetCell(X++, Y), OffsetX, OffsetY)) |
519 |
return true; |
520 |
return false; |
521 |
}; |
522 |
|
523 |
/** |
524 |
This rather clunky syntax for changing cell values allows the map to monitor when the values are changed, |
525 |
so it can dirty the elevations that are affected by the change |
526 |
|
527 |
All elevations for a given team are dirtied when: |
528 |
* A forbidden area for a given team is created/destroyed |
529 |
* resource type is changed from/to no resource |
530 |
* Building type is changed to/from NULL |
531 |
|
532 |
"resources" elevations should also be dirtied when: |
533 |
* resource type is changed from/to the specified type |
534 |
|
535 |
"Guard" elevations should also be changed when: |
536 |
* A guard area for a given team is created/destroyed |
537 |
|
538 |
**/ |
539 |
|
540 |
const bool Forbidden (cell_xpos_t x, cell_ypos_t y, team_ID_t p) const { return GetCell(x, y)[p].Forbidden(); }; |
541 |
const bool Guarded (cell_xpos_t x, cell_ypos_t y, team_ID_t p) const { return GetCell(x, y)[p].Guarded(); }; |
542 |
const bool Discovered (cell_xpos_t x, cell_ypos_t y, team_ID_t p) const { return GetCell(x, y)[p].Discovered(); }; |
543 |
const bool Fog (cell_xpos_t x, cell_ypos_t y, team_ID_t p) const { return GetCell(x, y)[p].Fog(FogOfWarAB); }; |
544 |
|
545 |
const bool FlipForbidden (cell_xpos_t x, cell_ypos_t y, team_ID_t p) const { |
546 |
DirtyForbidden[p] = DirtyResources[p] = DirtyGuarded[p] = DirtyBuildings = 1; |
547 |
return GetCell(x, y)[p].FlipForbidden(); |
548 |
}; |
549 |
const bool FlipGuarded (cell_xpos_t x, cell_ypos_t y, team_ID_t p) const { |
550 |
DirtyGuarded[p] = 1; |
551 |
return GetCell(x, y)[p].FlipGuarded(); |
552 |
}; |
553 |
const bool FlipDiscovered (cell_xpos_t x, cell_ypos_t y, team_ID_t p) const { |
554 |
return GetCell(x, y)[p].FilpDiscovered(); |
555 |
}; |
556 |
const bool FlipFog (cell_xpos_t x, cell_ypos_t y, team_ID_t p) const { return GetCell(x, y)[p].FlipFog(FogOfWarAB); }; |
557 |
|
558 |
const bool Forbidden (cell_xpos_t x, cell_ypos_t y, team_ID_t p, const bool B) const { return (Forbidden (x, y, p) == B)?B:FlipForbidden (x, y, p) }; |
559 |
const bool Guarded (cell_xpos_t x, cell_ypos_t y, team_ID_t p, const bool B) const { return (Guarded (x, y, p) == B)?B:FlipGuarded (x, y, p) }; |
560 |
const bool Discovered (cell_xpos_t x, cell_ypos_t y, team_ID_t p, const bool B) const { return (Discovered (x, y, p) == B)?B:FlipDiscovered(x, y, p) }; |
561 |
const bool Fog (cell_xpos_t x, cell_ypos_t y, team_ID_t p, const bool B) const { return (Fog (x, y, p) == B)?B:FlipFog (x, y, p) }; |
562 |
|
563 |
//! Since a building covers several cells, we need to set it for each one |
564 |
void setBuilding(int XMin, int XMax, int YMin, int YMax, Building* B) { |
565 |
const bool setCell(Cell& C, cell_xpos_t, cell_ypos_t) { |
566 |
if (C.Building() != B) { |
567 |
DirtyBuildings = 1; if (B || C.Building()) DirtyForbidden[p] = DirtyResources[p] = DirtyGuarded[p] = 1; |
568 |
C.setBuilding(B); |
569 |
}; |
570 |
return true; |
571 |
}; |
572 |
IterateOverArea(&setCell, XMin, XMax, YMin, Ymax); |
573 |
} |
574 |
|
575 |
const resource_enum_t Resource(cell_xpos_t x, cell_ypos_t y) const { return GetCell(x, y)[p].Resource().type }; |
576 |
const resource_enum_t Resource(cell_xpos_t x, cell_ypos_t y, Resource_enum T ) { |
577 |
if (GetCell(x, y)[p].Resource().type == T) { return T }; |
578 |
if ((T == NO_RES_TYPE) || (GetCell(x, y)[p].Resource().type == NO_RES_TYPE)) { |
579 |
DirtyForbidden[p] = DirtyResources[p] = DirtyGuarded[p] = DirtyBuildings = 1; |
580 |
} else { |
581 |
DirtyResource[p][T] = DirtyResource[p][GetCell(x, y)[p].Resource().type] = 1; |
582 |
}; |
583 |
return GetCell(x, y)[p].Resource().type = T; |
584 |
}; |
585 |
|
586 |
//! Checks to see if a building can go in a particular area |
587 |
bool FreeAreaForBuilding(cell_xpos_t XMin, cell_xpos_t XMax, cell_ypos_t YMin, cell_ypos_t YMax) { return IterateOverArea(&isFreeForBuilding, x, y, x+w, y+h) }; |
588 |
bool FreeAreaForBuildingGID(cell_xpos_t XMin, cell_xpos_t XMax, cell_ypos_t YMin, cell_ypos_t YMax, Building* B) { |
589 |
bool isFreeForBuildingOrMe(const Cell& C, cell_xpos_t, cell_ypos_t) { return !((Building && Building != B) || GroundUnit || Resource.hasResource) && isGrass() }; |
590 |
return IterateOverArea(&isFreeForBuildingOrMe, x, y, x+w, y+h); |
591 |
}; |
592 |
bool FreeAreaForBuildingNoGroundUnits(cell_xpos_t XMin, cell_xpos_t XMax, cell_ypos_t YMin, cell_ypos_t YMax) { return IterateOverArea(&isFreeForBuildingNoGroundUnits, x, y, x+w, y+h) }; |
593 |
bool FreeAreaForBuildingGIDNoGroundUnits(cell_xpos_t XMin, cell_xpos_t XMax, cell_ypos_t YMin, cell_ypos_t YMax, Building* B) { |
594 |
bool isFreeForBuildingOrMe(const Cell& C, cell_xpos_t, cell_ypos_t) { return !((C.Building && C.Building != B) || C.Resource.hasResource) && C.isGrass() }; |
595 |
return IterateOverArea(&isFreeForBuildingOrMe, x, y, x+w, y+h); |
596 |
}; |
597 |
|
598 |
//! Do a step associated with the map (grow resources and process bullets) |
599 |
void syncStep(Uint32 stepCounter, const Game& game); |
600 |
//! For some reason, we need two fogs of war, which are switched occasionally in a syncStep |
601 |
void switchFogOfWar() { FogOfWarAB = !FogOfWarAB }; |
602 |
|
603 |
const Uint32 checkSum(bool heavy) { |
604 |
Uint32 cs=size; |
605 |
if (heavy) |
606 |
for (int y=0; y<Height; y++) |
607 |
for (int x=0; x<Width; x++) |
608 |
Cells[x, y].checkSum(cs); |
609 |
return cs; |
610 |
}; |
611 |
|
612 |
const bool resourceAvailable(const team_ID_t Team, const resource_enum_t R, const bool CanSwim, const cell_xpos_t x, const cell_ypos_t y, cell_xpos_t& targetX, cell_ypos_t& targetY, const elevation_t* dist) const; |
613 |
|
614 |
Yes //! Starting from position (x, y) using elevation, returns the elevation destination in (targetX, targetY) |
615 |
static const int MaxHillclimbIterations = 256; |
616 |
bool Map::getElevationDestination(cell_xpos_t& x, cell_ypos_t& y, const team_ID_t p, const elevation_index_t i); |
617 |
|
618 |
//! Regenerate actual map tiles from undermap tiles |
619 |
void Map::regenerate(cell_xpos_t x=0, cell_ypos_t y=0, cell_width_t w=Width-1, cell_height_t h=Height-1) { |
620 |
const bool regenerateCell(const Cell& C, const cell_xpos_t x, cell_ypos_t y) { C.Terrain(GetCell(x+1, y+1), GetCell(x, y+1), GetCell(x+1, y)) } |
621 |
IterateOverArea(®enerateCell, x, x+w, y, y+h); |
622 |
} |
623 |
|
624 |
|
625 |
//! Elevation functions |
626 |
elevation_t getElevation(team_ID_t team, resource_t resourceType, bool canSwim, int x, int y) { return GetCell(x, y)[team][(canSwim?RESOURCE_SWIM:RESOURCE_NOSWIM)+resourceType] } |
627 |
|
628 |
void updateResourcesElevation(int teamNumber, Uint8 resourceType, bool canSwim); |
629 |
bool directionFromMinigrad(Uint8 miniGrad[25], int *dx, int *dy, const bool strict); |
630 |
bool directionByMinigrad(Uint32 teamMask, bool canSwim, int x, int y, int *dx, int *dy, Uint8 *elevation, bool strict); |
631 |
bool directionByMinigrad(Uint32 teamMask, bool canSwim, int x, int y, int bx, int by, int *dx, int *dy, Uint8 localElevation[1024], bool strict); |
632 |
bool pathfindResource(int teamNumber, Uint8 resourceType, bool canSwim, int x, int y, int *dx, int *dy, bool *stopWork); |
633 |
void pathfindRandom(Unit *unit); |
634 |
|
635 |
void updateLocalElevation(Building *building, bool canSwim); //The 32*32 elevation |
636 |
void updateGlobalElevation(Building *building, bool canSwim); //The full-sized elevation |
637 |
//!A special elevation for clearing flags. Returns false if there is nothing to clear. |
638 |
bool updateLocalResources(Building *building, bool canSwim); |
639 |
void expandLocalElevation(Uint8 *elevation); |
640 |
|
641 |
bool buildingAvailable(Building *building, bool canSwim, int x, int y, int *dist); |
642 |
bool pathfindBuilding(Building *building, bool canSwim, int x, int y, int *dx, int *dy); |
643 |
bool pathfindLocalResource(Building *building, bool canSwim, int x, int y, int *dx, int *dy); // Used for all resources mixed in clearing flags. |
644 |
|
645 |
//! Make local elevation dirty in the area. Wrap-safe on x,y |
646 |
void dirtyAllBuildingElevations(int x, int y, int wl, int hl, int teamNumber); |
647 |
bool pathfindForbidden(Uint8 *optionElevation, int teamNumber, bool canSwim, int x, int y, int *dx, int *dy); |
648 |
//! Find the best direction toward gaurd area, return true if one has been found, false otherwise |
649 |
bool pathfindGuardArea(int teamNumber, bool canSwim, int x, int y, int *dx, int *dy); |
650 |
//! Update the forbidden elevation, |
651 |
void updateForbiddenElevation(int teamNumber, bool canSwim); |
652 |
void updateForbiddenElevation(int teamNumber); |
653 |
//! Update the guard area elevation |
654 |
void updateGuardAreasElevation(int teamNumber, bool canSwim); |
655 |
void updateGuardAreasElevation(int teamNumber); |
656 |
|
657 |
private: |
658 |
void PropagateElevation(const team_ID_t p, const elevation_index_t i); |
659 |
|
660 |
void smoothResources(int times); |
661 |
|
662 |
}; |
663 |
|
664 |
#endif |
665 |
|