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