1 /*
2  * Copyright 2010-2014 OpenXcom Developers.
3  *
4  * This file is part of OpenXcom.
5  *
6  * OpenXcom is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * OpenXcom is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "Tile.h"
20 #include "../Ruleset/MapData.h"
21 #include "../Ruleset/MapDataSet.h"
22 #include "../Engine/SurfaceSet.h"
23 #include "../Engine/Surface.h"
24 #include "../Engine/RNG.h"
25 #include "../Engine/Exception.h"
26 #include "BattleUnit.h"
27 #include "BattleItem.h"
28 #include "../Ruleset/RuleItem.h"
29 #include "../Ruleset/Armor.h"
30 #include "SerializationHelper.h"
31 
32 namespace OpenXcom
33 {
34 
35 /// How many bytes various fields use in a serialized tile. See header.
36 Tile::SerializationKey Tile::serializationKey =
37 {4, // index
38  2, // _mapDataSetID, four of these
39  2, // _mapDataID, four of these
40  1, // _fire
41  1, // _smoke
42  1,	// one 8-bit bool field
43  4 + 2*4 + 2*4 + 1 + 1 + 1 // total bytes to save one tile
44 };
45 
46 /**
47 * constructor
48 * @param pos Position.
49 */
Tile(const Position & pos)50 Tile::Tile(const Position& pos): _smoke(0), _fire(0), _explosive(0), _pos(pos), _unit(0), _animationOffset(0), _markerColor(0), _visible(false), _preview(-1), _TUMarker(0), _overlaps(0), _danger(false)
51 {
52 	for (int i = 0; i < 4; ++i)
53 	{
54 		_objects[i] = 0;
55 		_mapDataID[i] = -1;
56 		_mapDataSetID[i] = -1;
57 		_currentFrame[i] = 0;
58 	}
59 	for (int layer = 0; layer < LIGHTLAYERS; layer++)
60 	{
61 		_light[layer] = 0;
62 		_lastLight[layer] = -1;
63 	}
64 	for (int i = 0; i < 3; ++i)
65 	{
66 		_discovered[i] = false;
67 	}
68 }
69 
70 /**
71  * destructor
72  */
~Tile()73 Tile::~Tile()
74 {
75 	_inventory.clear();
76 }
77 
78 /**
79  * Load the tile from a YAML node.
80  * @param node YAML node.
81  */
load(const YAML::Node & node)82 void Tile::load(const YAML::Node &node)
83 {
84 	//_position = node["position"].as<Position>(_position);
85 	for (int i = 0; i < 4; i++)
86 	{
87 		_mapDataID[i] = node["mapDataID"][i].as<int>(_mapDataID[i]);
88 		_mapDataSetID[i] = node["mapDataSetID"][i].as<int>(_mapDataSetID[i]);
89 	}
90 	_fire = node["fire"].as<int>(_fire);
91 	_smoke = node["smoke"].as<int>(_smoke);
92 	for (int i = 0; i < 3; i++)
93 	{
94 		_discovered[i] = node["discovered"][i].as<bool>();
95 	}
96 	if (node["openDoorWest"])
97 	{
98 		_currentFrame[1] = 7;
99 	}
100 	if (node["openDoorNorth"])
101 	{
102 		_currentFrame[2] = 7;
103 	}
104 }
105 
106 /**
107  * Load the tile from binary.
108  * @param buffer Pointer to buffer.
109  * @param serKey Serialization key.
110  */
loadBinary(Uint8 * buffer,Tile::SerializationKey & serKey)111 void Tile::loadBinary(Uint8 *buffer, Tile::SerializationKey& serKey)
112 {
113 	_mapDataID[0] = unserializeInt(&buffer, serKey._mapDataID);
114 	_mapDataID[1] = unserializeInt(&buffer, serKey._mapDataID);
115 	_mapDataID[2] = unserializeInt(&buffer, serKey._mapDataID);
116 	_mapDataID[3] = unserializeInt(&buffer, serKey._mapDataID);
117 	_mapDataSetID[0] = unserializeInt(&buffer, serKey._mapDataSetID);
118 	_mapDataSetID[1] = unserializeInt(&buffer, serKey._mapDataSetID);
119 	_mapDataSetID[2] = unserializeInt(&buffer, serKey._mapDataSetID);
120 	_mapDataSetID[3] = unserializeInt(&buffer, serKey._mapDataSetID);
121 
122 	_smoke = unserializeInt(&buffer, serKey._smoke);
123 	_fire = unserializeInt(&buffer, serKey._fire);
124 
125     Uint8 boolFields = unserializeInt(&buffer, serKey.boolFields);
126 	_discovered[0] = (boolFields & 1) ? true : false;
127 	_discovered[1] = (boolFields & 2) ? true : false;
128 	_discovered[2] = (boolFields & 4) ? true : false;
129 	_currentFrame[1] = (boolFields & 8) ? 7 : 0;
130 	_currentFrame[2] = (boolFields & 0x10) ? 7 : 0;
131 }
132 
133 
134 /**
135  * Saves the tile to a YAML node.
136  * @return YAML node.
137  */
save() const138 YAML::Node Tile::save() const
139 {
140 	YAML::Node node;
141 	node["position"] = _pos;
142 	for (int i = 0; i < 4; i++)
143 	{
144 		node["mapDataID"].push_back(_mapDataID[i]);
145 		node["mapDataSetID"].push_back(_mapDataSetID[i]);
146 	}
147 	if (_smoke)
148 		node["smoke"] = _smoke;
149 	if (_fire)
150 		node["fire"] = _fire;
151 	if (_discovered[0] || _discovered[1] || _discovered[2])
152 	{
153 		for (int i = 0; i < 3; i++)
154 		{
155 			node["discovered"].push_back(_discovered[i]);
156 		}
157 	}
158 	if (isUfoDoorOpen(1))
159 	{
160 		node["openDoorWest"] = true;
161 	}
162 	if (isUfoDoorOpen(2))
163 	{
164 		node["openDoorNorth"] = true;
165 	}
166 	return node;
167 }
168 
169 /**
170  * Saves the tile to binary.
171  * @param buffer pointer to buffer.
172  */
saveBinary(Uint8 ** buffer) const173 void Tile::saveBinary(Uint8** buffer) const
174 {
175 	serializeInt(buffer, serializationKey._mapDataID, _mapDataID[0]);
176 	serializeInt(buffer, serializationKey._mapDataID, _mapDataID[1]);
177 	serializeInt(buffer, serializationKey._mapDataID, _mapDataID[2]);
178 	serializeInt(buffer, serializationKey._mapDataID, _mapDataID[3]);
179 	serializeInt(buffer, serializationKey._mapDataSetID, _mapDataSetID[0]);
180 	serializeInt(buffer, serializationKey._mapDataSetID, _mapDataSetID[1]);
181 	serializeInt(buffer, serializationKey._mapDataSetID, _mapDataSetID[2]);
182 	serializeInt(buffer, serializationKey._mapDataSetID, _mapDataSetID[3]);
183 
184 	serializeInt(buffer, serializationKey._smoke, _smoke);
185 	serializeInt(buffer, serializationKey._fire, _fire);
186 
187 	Uint8 boolFields = (_discovered[0]?1:0) + (_discovered[1]?2:0) + (_discovered[2]?4:0);
188 	boolFields |= isUfoDoorOpen(1) ? 8 : 0; // west
189 	boolFields |= isUfoDoorOpen(2) ? 0x10 : 0; // north?
190 	serializeInt(buffer, serializationKey.boolFields, boolFields);
191 }
192 
193 /**
194  * Set the MapData references of part 0 to 3.
195  * @param dat pointer to the data object
196  * @param mapDataID
197  * @param mapDataSetID
198  * @param part the part number
199  */
setMapData(MapData * dat,int mapDataID,int mapDataSetID,int part)200 void Tile::setMapData(MapData *dat, int mapDataID, int mapDataSetID, int part)
201 {
202 	_objects[part] = dat;
203 	_mapDataID[part] = mapDataID;
204 	_mapDataSetID[part] = mapDataSetID;
205 }
206 
207 /**
208  * get the MapData references of part 0 to 3.
209  * @param mapDataID
210  * @param mapDataSetID
211  * @param part the part number
212  * @return the object ID
213  */
getMapData(int * mapDataID,int * mapDataSetID,int part) const214 void Tile::getMapData(int *mapDataID, int *mapDataSetID, int part) const
215 {
216 	*mapDataID = _mapDataID[part];
217 	*mapDataSetID = _mapDataSetID[part];
218 }
219 
220 /**
221  * Gets whether this tile has no objects. Note that we can have a unit or smoke on this tile.
222  * @return bool True if there is nothing but air on this tile.
223  */
isVoid() const224 bool Tile::isVoid() const
225 {
226 	return _objects[0] == 0 && _objects[1] == 0 && _objects[2] == 0 && _objects[3] == 0 && _smoke == 0 && _inventory.empty();
227 }
228 
229 /**
230  * Get the TU cost to walk over a certain part of the tile.
231  * @param part
232  * @param movementType
233  * @return TU cost
234  */
getTUCost(int part,MovementType movementType) const235 int Tile::getTUCost(int part, MovementType movementType) const
236 {
237 	if (_objects[part])
238 	{
239 		if (_objects[part]->isUFODoor() && _currentFrame[part] == 7)
240 			return 0;
241 		if (_objects[part]->getBigWall() >= 4)
242 			return 0;
243 		return _objects[part]->getTUCost(movementType);
244 	}
245 	else
246 		return 0;
247 }
248 
249 /**
250  * Whether this tile has a floor or not. If no object defined as floor, it has no floor.
251  * @param tileBelow
252  * @return bool
253  */
hasNoFloor(Tile * tileBelow) const254 bool Tile::hasNoFloor(Tile *tileBelow) const
255 {
256 	if (tileBelow != 0 && tileBelow->getTerrainLevel() == -24)
257 		return false;
258 	if (_objects[MapData::O_FLOOR])
259 		return _objects[MapData::O_FLOOR]->isNoFloor();
260 	else
261 		return true;
262 }
263 
264 /**
265  * Whether this tile has a big wall.
266  * @return bool
267  */
isBigWall() const268 bool Tile::isBigWall() const
269 {
270 	if (_objects[MapData::O_OBJECT])
271 		return (_objects[MapData::O_OBJECT]->getBigWall() != 0);
272 	else
273 		return false;
274 }
275 
276 /**
277  * If an object stand on this tile, this returns how high the unit is it standing.
278  * @return the level in pixels
279  */
getTerrainLevel() const280 int Tile::getTerrainLevel() const
281 {
282 	int level = 0;
283 
284 	if (_objects[MapData::O_FLOOR])
285 		level = _objects[MapData::O_FLOOR]->getTerrainLevel();
286 	if (_objects[MapData::O_OBJECT])
287 		level += _objects[MapData::O_OBJECT]->getTerrainLevel();
288 
289 	return level;
290 }
291 
292 /**
293  * Gets the tile's footstep sound.
294  * @param tileBelow
295  * @return sound ID
296  */
getFootstepSound(Tile * tileBelow) const297 int Tile::getFootstepSound(Tile *tileBelow) const
298 {
299 	int sound = 0;
300 
301 	if (_objects[MapData::O_FLOOR])
302 		sound = _objects[MapData::O_FLOOR]->getFootstepSound();
303 	if (_objects[MapData::O_OBJECT] && _objects[MapData::O_OBJECT]->getBigWall() == 0)
304 		sound = _objects[MapData::O_OBJECT]->getFootstepSound();
305 	if (!_objects[MapData::O_FLOOR] && !_objects[MapData::O_OBJECT] && tileBelow != 0 && tileBelow->getTerrainLevel() == -24)
306 		sound = tileBelow->getMapData(MapData::O_OBJECT)->getFootstepSound();
307 
308 	return sound;
309 }
310 
311 
312 /**
313  * Open a door on this tile.
314  * @param part
315  * @param unit
316  * @param reserve
317  * @return a value: 0(normal door), 1(ufo door) or -1 if no door opened or 3 if ufo door(=animated) is still opening 4 if not enough TUs
318  */
openDoor(int part,BattleUnit * unit,BattleActionType reserve)319 int Tile::openDoor(int part, BattleUnit *unit, BattleActionType reserve)
320 {
321 	if (!_objects[part]) return -1;
322 
323 	if (_objects[part]->isDoor())
324 	{
325 		if (unit && unit->getTimeUnits() < _objects[part]->getTUCost(unit->getArmor()->getMovementType()) + unit->getActionTUs(reserve, unit->getMainHandWeapon(false)))
326 			return 4;
327 		if (_unit && _unit != unit && _unit->getPosition() != getPosition())
328 			return -1;
329 		setMapData(_objects[part]->getDataset()->getObjects()->at(_objects[part]->getAltMCD()), _objects[part]->getAltMCD(), _mapDataSetID[part],
330 				   _objects[part]->getDataset()->getObjects()->at(_objects[part]->getAltMCD())->getObjectType());
331 		setMapData(0, -1, -1, part);
332 		return 0;
333 	}
334 	if (_objects[part]->isUFODoor() && _currentFrame[part] == 0) // ufo door part 0 - door is closed
335 	{
336 		if (unit &&	unit->getTimeUnits() < _objects[part]->getTUCost(unit->getArmor()->getMovementType()) + unit->getActionTUs(reserve, unit->getMainHandWeapon(false)))
337 			return 4;
338 		_currentFrame[part] = 1; // start opening door
339 		return 1;
340 	}
341 	if (_objects[part]->isUFODoor() && _currentFrame[part] != 7) // ufo door != part 7 - door is still opening
342 	{
343 		return 3;
344 	}
345 	return -1;
346 }
347 
closeUfoDoor()348 int Tile::closeUfoDoor()
349 {
350 	int retval = 0;
351 
352 	for (int part = 0; part < 4; part++)
353 	{
354 		if (isUfoDoorOpen(part))
355 		{
356 			_currentFrame[part] = 0;
357 			retval = 1;
358 		}
359 	}
360 
361 	return retval;
362 }
363 
364 /**
365  * Sets the tile's cache flag. - TODO: set this for each object separately?
366  * @param flag true/false
367  * @param part 0-2 westwall/northwall/content+floor
368  */
setDiscovered(bool flag,int part)369 void Tile::setDiscovered(bool flag, int part)
370 {
371 	if (_discovered[part] != flag)
372 	{
373 		_discovered[part] = flag;
374 		if (part == 2 && flag == true)
375 		{
376 			_discovered[0] = true;
377 			_discovered[1] = true;
378 		}
379 		// if light on tile changes, units and objects on it change light too
380 		if (_unit != 0)
381 		{
382 			_unit->setCache(0);
383 		}
384 	}
385 }
386 
387 /**
388  * Get the black fog of war state of this tile.
389  * @param part 0-2 westwall/northwall/content+floor
390  * @return bool True = discovered the tile.
391  */
isDiscovered(int part) const392 bool Tile::isDiscovered(int part) const
393 {
394 	return _discovered[part];
395 }
396 
397 
398 /**
399  * Reset the light amount on the tile. This is done before a light level recalculation.
400  * @param layer Light is separated in 3 layers: Ambient, Static and Dynamic.
401  */
resetLight(int layer)402 void Tile::resetLight(int layer)
403 {
404 	_light[layer] = 0;
405 	_lastLight[layer] = _light[layer];
406 }
407 
408 /**
409  * Add the light amount on the tile. Only add light if the current light is lower.
410  * @param light Amount of light to add.
411  * @param layer Light is separated in 3 layers: Ambient, Static and Dynamic.
412  */
addLight(int light,int layer)413 void Tile::addLight(int light, int layer)
414 {
415 	if (_light[layer] < light)
416 		_light[layer] = light;
417 }
418 
419 /**
420  * Gets the tile's shade amount 0-15. It returns the brightest of all light layers.
421  * Shade level is the inverse of light level. So a maximum amount of light (15) returns shade level 0.
422  * @return shade
423  */
getShade() const424 int Tile::getShade() const
425 {
426 	int light = 0;
427 
428 	for (int layer = 0; layer < LIGHTLAYERS; layer++)
429 	{
430 		if (_light[layer] > light)
431 			light = _light[layer];
432 	}
433 
434 	return 15 - light;
435 }
436 
437 /**
438  * Destroy a part on this tile. We first remove the old object, then replace it with the destroyed one.
439  * This is because the object type of the old and new one are not necessarily the same.
440  * If the destroyed part is an explosive, set the tile's explosive value, which will trigger a chained explosion.
441  * @param part
442  * @return bool Return true objective was destroyed
443  */
destroy(int part)444 bool Tile::destroy(int part)
445 {
446 	bool _objective = false;
447 	if (_objects[part])
448 	{
449 		if (_objects[part]->isGravLift())
450 			return false;
451 		_objective = _objects[part]->getSpecialType() == MUST_DESTROY;
452 		MapData *originalPart = _objects[part];
453 		int originalMapDataSetID = _mapDataSetID[part];
454 		setMapData(0, -1, -1, part);
455 		if (originalPart->getDieMCD())
456 		{
457 			MapData *dead = originalPart->getDataset()->getObjects()->at(originalPart->getDieMCD());
458 			setMapData(dead, originalPart->getDieMCD(), originalMapDataSetID, dead->getObjectType());
459 		}
460 		if (originalPart->getExplosive())
461 		{
462 			setExplosive(originalPart->getExplosive());
463 		}
464 	}
465 	/* check if the floor on the lowest level is gone */
466 	if (part == MapData::O_FLOOR && getPosition().z == 0 && _objects[MapData::O_FLOOR] == 0)
467 	{
468 		/* replace with scorched earth */
469 		setMapData(MapDataSet::getScorchedEarthTile(), 1, 0, MapData::O_FLOOR);
470 	}
471 	return _objective;
472 }
473 
474 /**
475  * damage terrain - check against armor
476  * @param part Part to check.
477  * @param power Power of the damage.
478  * @return bool Return true objective was destroyed
479  */
damage(int part,int power)480 bool Tile::damage(int part, int power)
481 {
482 	bool objective = false;
483 	if (power >= _objects[part]->getArmor())
484 		objective = destroy(part);
485 	return objective;
486 }
487 
488 /**
489  * Set a "virtual" explosive on this tile. We mark a tile this way to detonate it later.
490  * We do it this way, because the same tile can be visited multiple times by an "explosion ray".
491  * The explosive power on the tile is some kind of moving MAXIMUM of the explosive rays that passes it.
492  * @param power Power of the damage.
493  * @param force Force damage.
494  */
setExplosive(int power,bool force)495 void Tile::setExplosive(int power, bool force)
496 {
497 	if (force || _explosive < power)
498 	{
499 		_explosive = power;
500 	}
501 }
502 
503 /**
504  * Get explosive on this tile.
505  * @return explosive
506  */
getExplosive() const507 int Tile::getExplosive() const
508 {
509 	return _explosive;
510 }
511 
512 /*
513  * Flammability of a tile is the lowest flammability of it's objects.
514  * @return Flammability : the lower the value, the higher the chance the tile/object catches fire.
515  */
getFlammability() const516 int Tile::getFlammability() const
517 {
518 	int flam = 255;
519 
520 	if (_objects[3])
521 	{
522 		flam = _objects[3]->getFlammable();
523 	}
524 	else if (_objects[0])
525 	{
526 		flam = _objects[0]->getFlammable();
527 	}
528 
529 	return flam;
530 }
531 
532 /*
533  * Fuel of a tile is the lowest flammability of its objects.
534  * @return how long to burn.
535  */
getFuel() const536 int Tile::getFuel() const
537 {
538 	int fuel = 0;
539 
540 	if (_objects[3])
541 	{
542 		fuel = _objects[3]->getFuel();
543 	}
544 	else if (_objects[0])
545 	{
546 		fuel = _objects[0]->getFuel();
547 	}
548 
549 	return fuel;
550 }
551 /*
552  * Ignite starts fire on a tile, it will burn <fuel> rounds. Fuel of a tile is the highest fuel of its objects.
553  * NOT the sum of the fuel of the objects!
554  */
ignite(int power)555 void Tile::ignite(int power)
556 {
557 	if (getFlammability() != 255)
558 	{
559 		power = power - (getFlammability() / 10) + 15;
560 		if (power < 0)
561 		{
562 			power = 0;
563 		}
564 		if (RNG::percent(power))
565 		{
566 			if (_fire == 0)
567 			{
568 				_smoke = 15 - std::max(1, std::min((getFlammability() / 10), 12));
569 				_overlaps = 1;
570 				_fire = getFuel() + 1;
571 				_animationOffset = RNG::generate(0,3);
572 			}
573 		}
574 	}
575 }
576 
577 /**
578  * Animate the tile. This means to advance the current frame for every object.
579  * Ufo doors are a bit special, they animated only when triggered.
580  * When ufo doors are on frame 0(closed) or frame 7(open) they are not animated further.
581  */
animate()582 void Tile::animate()
583 {
584 	int newframe;
585 	for (int i=0; i < 4; ++i)
586 	{
587 		if (_objects[i])
588 		{
589 			if (_objects[i]->isUFODoor() && (_currentFrame[i] == 0 || _currentFrame[i] == 7)) // ufo door is static
590 			{
591 				continue;
592 			}
593 			newframe = _currentFrame[i] + 1;
594 			if (_objects[i]->isUFODoor() && _objects[i]->getSpecialType() == START_POINT && newframe == 3)
595 			{
596 				newframe = 7;
597 			}
598 			if (newframe == 8)
599 			{
600 				newframe = 0;
601 			}
602 			_currentFrame[i] = newframe;
603 		}
604 	}
605 }
606 
607 /**
608  * Get the sprite of a certain part of the tile.
609  * @param part
610  * @return Pointer to the sprite.
611  */
getSprite(int part) const612 Surface *Tile::getSprite(int part) const
613 {
614 	if (_objects[part] == 0)
615 		return 0;
616 
617 	return _objects[part]->getDataset()->getSurfaceset()->getFrame(_objects[part]->getSprite(_currentFrame[part]));
618 }
619 
620 /**
621  * Set a unit on this tile.
622  * @param unit
623  * @param tileBelow
624  */
setUnit(BattleUnit * unit,Tile * tileBelow)625 void Tile::setUnit(BattleUnit *unit, Tile *tileBelow)
626 {
627 	if (unit != 0)
628 	{
629 		unit->setTile(this, tileBelow);
630 	}
631 	_unit = unit;
632 }
633 
634 /**
635  * Set the amount of turns this tile is on fire. 0 = no fire.
636  * @param fire : amount of turns this tile is on fire.
637  */
setFire(int fire)638 void Tile::setFire(int fire)
639 {
640 	_fire = fire;
641 	_animationOffset = RNG::generate(0,3);
642 }
643 
644 /**
645  * Get the amount of turns this tile is on fire. 0 = no fire.
646  * @return fire : amount of turns this tile is on fire.
647  */
getFire() const648 int Tile::getFire() const
649 {
650 	return _fire;
651 }
652 
653 /**
654  * Set the amount of turns this tile is smoking. 0 = no smoke.
655  * @param smoke : amount of turns this tile is smoking.
656  */
addSmoke(int smoke)657 void Tile::addSmoke(int smoke)
658 {
659 	if (_fire == 0)
660 	{
661 		if (_overlaps == 0)
662 		{
663 			_smoke = std::max(1, std::min(_smoke + smoke, 15));
664 		}
665 		else
666 		{
667 			_smoke += smoke;
668 		}
669 		_animationOffset = RNG::generate(0,3);
670 		addOverlap();
671 	}
672 }
673 
674 /**
675  * Set the amount of turns this tile is smoking. 0 = no smoke.
676  * @param smoke : amount of turns this tile is smoking.
677  */
setSmoke(int smoke)678 void Tile::setSmoke(int smoke)
679 {
680 	_smoke = smoke;
681 	_animationOffset = RNG::generate(0,3);
682 }
683 
684 
685 /**
686  * Get the amount of turns this tile is smoking. 0 = no smoke.
687  * @return smoke : amount of turns this tile is smoking.
688  */
getSmoke() const689 int Tile::getSmoke() const
690 {
691 	return _smoke;
692 }
693 
694 /**
695  * Get the number of frames the fire or smoke animation is off-sync.
696  * To void fire and smoke animations of different tiles moving nice in sync - it looks fake.
697  * @return offset
698  */
getAnimationOffset() const699 int Tile::getAnimationOffset() const
700 {
701 	return _animationOffset;
702 }
703 
704 /**
705  * Add an item on the tile.
706  * @param item
707  * @param ground
708  */
addItem(BattleItem * item,RuleInventory * ground)709 void Tile::addItem(BattleItem *item, RuleInventory *ground)
710 {
711 	item->setSlot(ground);
712 	_inventory.push_back(item);
713 	item->setTile(this);
714 }
715 
716 /**
717  * Remove an item from the tile.
718  * @param item
719  */
removeItem(BattleItem * item)720 void Tile::removeItem(BattleItem *item)
721 {
722 	for (std::vector<BattleItem*>::iterator i = _inventory.begin(); i != _inventory.end(); ++i)
723 	{
724 		if ((*i) == item)
725 		{
726 			_inventory.erase(i);
727 			break;
728 		}
729 	}
730 	item->setTile(0);
731 }
732 
733 /**
734  * Get the topmost item sprite to draw on the battlescape.
735  * @return item sprite ID in floorob, or -1 when no item
736  */
getTopItemSprite()737 int Tile::getTopItemSprite()
738 {
739 	int biggestWeight = -1;
740 	int biggestItem = -1;
741 	for (std::vector<BattleItem*>::iterator i = _inventory.begin(); i != _inventory.end(); ++i)
742 	{
743 		if ((*i)->getRules()->getWeight() > biggestWeight)
744 		{
745 			biggestWeight = (*i)->getRules()->getWeight();
746 			biggestItem = (*i)->getRules()->getFloorSprite();
747 		}
748 	}
749 	return biggestItem;
750 }
751 
752 /**
753  * New turn preparations.
754  * average out any smoke added by the number of overlaps.
755  * apply fire/smoke damage to units as applicable.
756  */
prepareNewTurn()757 void Tile::prepareNewTurn()
758 {
759 	// we've recieved new smoke in this turn, but we're not on fire, average out the smoke.
760 	if ( _overlaps != 0 && _smoke != 0 && _fire == 0)
761 	{
762 		_smoke = std::max(0, std::min((_smoke / _overlaps)- 1, 15));
763 	}
764 	// if we still have smoke/fire
765 	if (_smoke)
766 	{
767 		if (_unit && !_unit->isOut())
768 		{
769 			if (_fire)
770 			{
771 				// this is how we avoid hitting the same unit multiple times.
772 				if (_unit->getArmor()->getSize() == 1 || !_unit->tookFireDamage())
773 				{
774 					_unit->toggleFireDamage();
775 					// _smoke becomes our damage value
776 					_unit->damage(Position(0, 0, 0), _smoke, DT_IN, true);
777 					// try to set the unit on fire.
778 					if (RNG::percent(40 * _unit->getArmor()->getDamageModifier(DT_IN)))
779 					{
780 						int burnTime = RNG::generate(0, int(5 * _unit->getArmor()->getDamageModifier(DT_IN)));
781 						if (_unit->getFire() < burnTime)
782 						{
783 							_unit->setFire(burnTime);
784 						}
785 					}
786 				}
787 			}
788 			// no fire: must be smoke
789 			else
790 			{
791 				// aliens don't breathe
792 				if (_unit->getOriginalFaction() != FACTION_HOSTILE)
793 				{
794 					// try to knock this guy out.
795 					if (_unit->getArmor()->getDamageModifier(DT_SMOKE) > 0.0 && _unit->getArmor()->getSize() == 1)
796 					{
797 						_unit->damage(Position(0,0,0), (_smoke / 4) + 1, DT_SMOKE, true);
798 					}
799 				}
800 			}
801 		}
802 	}
803 	_overlaps = 0;
804 	_danger = false;
805 }
806 
807 /**
808  * Get the inventory on this tile.
809  * @return pointer to a vector of battleitems.
810  */
getInventory()811 std::vector<BattleItem *> *Tile::getInventory()
812 {
813 	return &_inventory;
814 }
815 
816 
817 /**
818  * Set the marker color on this tile.
819  * @param color
820  */
setMarkerColor(int color)821 void Tile::setMarkerColor(int color)
822 {
823 	_markerColor = color;
824 }
825 
826 /**
827  * Get the marker color on this tile.
828  * @return color
829  */
getMarkerColor()830 int Tile::getMarkerColor()
831 {
832 	return _markerColor;
833 }
834 
835 /**
836  * Set the tile visible flag.
837  * @param visibility
838  */
setVisible(int visibility)839 void Tile::setVisible(int visibility)
840 {
841 	_visible += visibility;
842 }
843 
844 /**
845  * Get the tile visible flag.
846  * @return visibility
847  */
getVisible()848 int Tile::getVisible()
849 {
850 	return _visible;
851 }
852 
853 /**
854  * set the direction used for path previewing.
855  * @param dir
856  */
setPreview(int dir)857 void Tile::setPreview(int dir)
858 {
859 	_preview = dir;
860 }
861 
862 /**
863  * retrieve the direction stored by the pathfinding.
864  * @return preview
865  */
getPreview() const866 int Tile::getPreview() const
867 {
868 	return _preview;
869 }
870 
871 /**
872  * set the number to be displayed for pathfinding preview.
873  * @param tu
874  */
setTUMarker(int tu)875 void Tile::setTUMarker(int tu)
876 {
877 	_TUMarker = tu;
878 }
879 
880 /**
881  * get the number to be displayed for pathfinding preview.
882  * @return marker
883  */
getTUMarker() const884 int Tile::getTUMarker() const
885 {
886 	return _TUMarker;
887 }
888 
889 /**
890  * get the overlap value of this tile.
891  * @return overlap
892  */
getOverlaps() const893 int Tile::getOverlaps() const
894 {
895 	return _overlaps;
896 }
897 
898 /**
899  * increment the overlap value on this tile.
900  */
addOverlap()901 void Tile::addOverlap()
902 {
903 	++_overlaps;
904 }
905 
906 /**
907  * set the danger flag on this tile.
908  */
setDangerous()909 void Tile::setDangerous()
910 {
911 	_danger = true;
912 }
913 
914 /**
915  * get the danger flag on this tile.
916  * @return the danger flag for this tile.
917  */
getDangerous()918 bool Tile::getDangerous()
919 {
920 	return _danger;
921 }
922 
923 }
924