1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name map_wall.cpp - The map wall handling. */
12 //
13 // (c) Copyright 1999-2015 by Vladi Shabanski and Andrettin
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; only version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 // 02111-1307, USA.
28 //
29
30 //@{
31
32 /*----------------------------------------------------------------------------
33 -- Includes
34 ----------------------------------------------------------------------------*/
35
36 #include <stdio.h>
37
38 #include "stratagus.h"
39 #include "map.h"
40 #include "fov.h"
41 #include "tileset.h"
42 #include "ui.h"
43 #include "player.h"
44 #include "unittype.h"
45
46 /*----------------------------------------------------------------------------
47 -- Functions
48 ----------------------------------------------------------------------------*/
49
50 /*----------------------------------------------------------------------------
51 -- Fix walls (connections)
52 ----------------------------------------------------------------------------*/
53
54 /*
55 Vladi:
56 NOTE: this is not the original behaviour of the wall demolishing,
57 instead I'm replacing tiles just as the wood fixing, so if part of
58 a wall is down side neighbours are fixed just as current tile is
59 empty one. It is still nice... :)
60
61 For the connecting new walls -- all's fine.
62 */
63
getWallTile(const CTileset & tileset,bool humanWall,int dirFlag,int value,unsigned int oldTile=0)64 static unsigned int getWallTile(const CTileset &tileset, bool humanWall, int dirFlag, int value, unsigned int oldTile = 0)
65 {
66 unsigned int tileIndex, newTile;
67 if (humanWall) {
68 if (value == 0) {
69 tileIndex = tileset.getHumanWallTileIndex_destroyed(dirFlag);
70 } else if (UnitTypeHumanWall && value <= UnitTypeHumanWall->MapDefaultStat.Variables[HP_INDEX].Max / 2) {
71 tileIndex = tileset.getHumanWallTileIndex_broken(dirFlag);
72 } else {
73 tileIndex = tileset.getHumanWallTileIndex(dirFlag);
74 }
75 } else { // orcWall
76 if (value == 0) {
77 tileIndex = tileset.getOrcWallTileIndex_destroyed(dirFlag);
78 } else if (UnitTypeOrcWall && value <= UnitTypeOrcWall->MapDefaultStat.Variables[HP_INDEX].Max / 2) {
79 tileIndex = tileset.getOrcWallTileIndex_broken(dirFlag);
80 } else {
81 tileIndex = tileset.getOrcWallTileIndex(dirFlag);
82 }
83 }
84 newTile = tileset.tiles[tileIndex].tile;
85 if (!newTile && oldTile) {
86 unsigned int oldTileIndex = tileset.findTileIndexByTile(oldTile);
87 return getWallTile(tileset, humanWall, tileset.getWallDirection(oldTileIndex, humanWall), value);
88 } else {
89 return newTile;
90 }
91 }
92
93
94
95
96 // Calculate the correct tile. Depends on the surrounding.
GetDirectionFromSurrounding(const Vec2i & pos,bool human,bool seen)97 static int GetDirectionFromSurrounding(const Vec2i &pos, bool human, bool seen)
98 {
99 const Vec2i offsets[4] = {Vec2i(0, -1), Vec2i(1, 0), Vec2i(0, 1), Vec2i(-1, 0)};
100 int dirFlag = 0;
101
102 for (int i = 0; i != 4; ++i) {
103 const Vec2i newpos = pos + offsets[i];
104
105 if (!Map.Info.IsPointOnMap(newpos)) {
106 dirFlag |= 1 << i;
107 } else {
108 const CMapField &mf = *Map.Field(newpos);
109 const unsigned int tile = seen ? mf.playerInfo.SeenTile : mf.getGraphicTile();
110
111 if (Map.Tileset->isARaceWallTile(tile, human)) {
112 dirFlag |= 1 << i;
113 }
114 }
115 }
116 return dirFlag;
117 }
118
119 /**
120 ** Correct the seen wall field, depending on the surrounding.
121 **
122 ** @param pos Map tile-position.
123 */
MapFixSeenWallTile(const Vec2i & pos)124 void MapFixSeenWallTile(const Vec2i &pos)
125 {
126 // Outside of map or no wall.
127 if (!Map.Info.IsPointOnMap(pos)) {
128 return;
129 }
130 CMapField &mf = *Map.Field(pos);
131 const CTileset &tileset = *Map.Tileset;
132 const unsigned tile = mf.playerInfo.SeenTile;
133 if (!tileset.isAWallTile(tile)) {
134 return;
135 }
136 const bool human = tileset.isARaceWallTile(tile, true);
137 const int dirFlag = GetDirectionFromSurrounding(pos, human, true);
138 const int wallTile = getWallTile(tileset, human, dirFlag, mf.Value, tile);
139
140 if (mf.playerInfo.SeenTile != wallTile) { // Already there!
141 mf.playerInfo.SeenTile = wallTile;
142 // FIXME: can this only happen if seen?
143 if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
144 UI.Minimap.UpdateSeenXY(pos);
145 }
146 }
147 }
148
149 /**
150 ** Correct the surrounding seen wall fields.
151 **
152 ** @param pos Map tile-position.
153 */
MapFixSeenWallNeighbors(const Vec2i & pos)154 void MapFixSeenWallNeighbors(const Vec2i &pos)
155 {
156 const Vec2i offset[] = {Vec2i(1, 0), Vec2i(-1, 0), Vec2i(0, 1), Vec2i(0, -1)};
157
158 for (unsigned int i = 0; i < 4; ++i) {
159 MapFixSeenWallTile(pos + offset[i]);
160 }
161 }
162
163 /**
164 ** Correct the real wall field, depending on the surrounding.
165 **
166 ** @param pos Map tile-position.
167 */
MapFixWallTile(const Vec2i & pos)168 void MapFixWallTile(const Vec2i &pos)
169 {
170 // Outside of map or no wall.
171 if (!Map.Info.IsPointOnMap(pos)) {
172 return;
173 }
174 CMapField &mf = *Map.Field(pos);
175 const CTileset &tileset = *Map.Tileset;
176 const int tile = mf.getGraphicTile();
177 if (!tileset.isAWallTile(tile)) {
178 return;
179 }
180 const bool human = tileset.isARaceWallTile(tile, true);
181 const int dirFlag = GetDirectionFromSurrounding(pos, human, false);
182 const unsigned int wallTile = getWallTile(tileset, human, dirFlag, mf.Value, tile);
183
184 if (mf.getGraphicTile() != wallTile) {
185 mf.setGraphicTile(wallTile);
186 UI.Minimap.UpdateXY(pos);
187
188 if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
189 UI.Minimap.UpdateSeenXY(pos);
190 Map.MarkSeenTile(mf);
191 }
192 }
193 }
194
195 /**
196 ** Correct the surrounding real wall fields.
197 **
198 ** @param pos Map tile-position.
199 */
MapFixWallNeighbors(const Vec2i & pos)200 static void MapFixWallNeighbors(const Vec2i &pos)
201 {
202 const Vec2i offset[] = {Vec2i(1, 0), Vec2i(-1, 0), Vec2i(0, 1), Vec2i(0, -1)};
203
204 for (unsigned int i = 0; i < sizeof(offset) / sizeof(*offset); ++i) {
205 MapFixWallTile(pos + offset[i]);
206 }
207 }
208
209 /**
210 ** Remove wall from the map.
211 **
212 ** @param pos Map position.
213 **
214 ** FIXME: support more walls of different races.
215 */
RemoveWall(const Vec2i & pos)216 void CMap::RemoveWall(const Vec2i &pos)
217 {
218 CMapField &mf = *Field(pos);
219
220 mf.Value = 0;
221
222 MapFixWallTile(pos);
223 mf.Flags &= ~(MapFieldHuman | MapFieldWall | MapFieldUnpassable | MapFieldOpaque);
224 MapFixWallNeighbors(pos);
225 UI.Minimap.UpdateXY(pos);
226
227 if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
228 UI.Minimap.UpdateSeenXY(pos);
229 this->MarkSeenTile(mf);
230 }
231 }
232
233 /**
234 ** Set wall onto the map.
235 **
236 ** @param pos Map position.
237 ** @param humanwall Flag, if true set a human wall.
238 **
239 ** @todo FIXME: support for more races.
240 */
SetWall(const Vec2i & pos,bool humanwall)241 void CMap::SetWall(const Vec2i &pos, bool humanwall)
242 {
243 CMapField &mf = *Field(pos);
244
245 /// Refresh vision of nearby units in case is walls are set as opaque field
246 const bool isOpaque = FieldOfView.GetOpaqueFields() & MapFieldWall;
247 if (isOpaque) {
248 MapRefreshUnitsSight(pos, true);
249 }
250
251 if (humanwall) {
252 const int value = UnitTypeHumanWall->MapDefaultStat.Variables[HP_INDEX].Max;
253 mf.setTileIndex(*Tileset, Tileset->getHumanWallTileIndex(0), value);
254 } else {
255 const int value = UnitTypeOrcWall->MapDefaultStat.Variables[HP_INDEX].Max;
256 mf.setTileIndex(*Tileset, Tileset->getOrcWallTileIndex(0), value);
257 }
258
259 UI.Minimap.UpdateXY(pos);
260 MapFixWallTile(pos);
261 MapFixWallNeighbors(pos);
262
263 /// Refresh vision of nearby units in case is walls are set as opaque field
264 if (isOpaque) {
265 MapRefreshUnitsSight(pos);
266 }
267
268 if (mf.playerInfo.IsTeamVisible(*ThisPlayer)) {
269 UI.Minimap.UpdateSeenXY(pos);
270 this->MarkSeenTile(mf);
271 }
272 }
273
274 /**
275 ** Wall is hit with damage.
276 **
277 ** @param pos Map tile-position of wall.
278 ** @param damage Damage done to wall.
279 */
HitWall(const Vec2i & pos,unsigned damage)280 void CMap::HitWall(const Vec2i &pos, unsigned damage)
281 {
282 const unsigned v = this->Field(pos)->Value;
283
284 if (v <= damage) {
285 //RemoveWall(pos);
286 ClearTile(pos);
287 } else {
288 this->Field(pos)->Value = v - damage;
289 MapFixWallTile(pos);
290 }
291 }
292
293 //@}
294