Esempio n. 1
0
void UnitWalkBState::init()
{
	_unit = _action.actor;
	setNormalWalkSpeed();
	_pf = _parent->getPathfinding();
	_terrain = _parent->getTileEngine();
	_target = _action.target;
}
Esempio n. 2
0
/**
 * Initializes the state.
 */
void UnitWalkBState::init()
{
	_unit = _action.actor;
	_numUnitsSpotted = _unit->getUnitsSpottedThisTurn().size();
	setNormalWalkSpeed();
	_pf = _parent->getPathfinding();
	_terrain = _parent->getTileEngine();
	_target = _action.target;
	if (Options::traceAI) { Log(LOG_INFO) << "Walking from: " << _unit->getPosition() << "," << " to " << _target;}
	int dir = _pf->getStartDirection();
	if (!_action.strafe && dir != -1 && dir != _unit->getDirection())
	{
		_beforeFirstStep = true;
	}
}
Esempio n. 3
0
void UnitWalkBState::think()
{
	bool unitspotted = false;

	if (_unit->isOut())
	{
		_pf->abortPath();
		_parent->popState();
		return;
	}

	// during a walking cycle we make step sounds
	if (_unit->getStatus() == STATUS_WALKING || _unit->getStatus() == STATUS_FLYING)
	{

		if (_unit->getVisible() && _unit->getStatus() == STATUS_WALKING)
		{
			if (_unit->getUnit()->getArmor()->getSize() > 1)
			{
				// play hwp engine sound
				if (_unit->getWalkingPhase() == 0)
				{
					// tank with threads "walks"
					if (_unit->getUnit()->getArmor()->getMovementType() == MT_WALK)
						_parent->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(14)->play();
					else // hovering tank hovers
						_parent->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(40)->play();
				}
			}
			else
			{
				// play footstep sound 1
				if (_unit->getWalkingPhase() == 3)
				{
					Tile *tile = _unit->getTile();
					if (tile->getFootstepSound())
					{
						_parent->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(22 + (tile->getFootstepSound()*2))->play();
					}
				}
				// play footstep sound 2
				if (_unit->getWalkingPhase() == 7)
				{
					Tile *tile = _unit->getTile();
					if (tile->getFootstepSound())
					{
						_parent->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(23 + (tile->getFootstepSound()*2))->play();
					}
				}
			}
		}

		_unit->keepWalking(); // advances the phase

		// unit moved from one tile to the other, update the tiles
		if (_unit->getPosition() != _unit->getLastPosition())
		{
			int size = _unit->getUnit()->getArmor()->getSize() - 1;
			for (int x = size; x >= 0; x--)
			{
				for (int y = size; y >= 0; y--)
				{
					_parent->getSave()->getTile(_unit->getLastPosition() + Position(x,y,0))->setUnit(0);
				}
			}
			for (int x = size; x >= 0; x--)
			{
				for (int y = size; y >= 0; y--)
				{
					_parent->getSave()->getTile(_unit->getPosition() + Position(x,y,0))->setUnit(_unit);
				}
			}

			// if the unit changed level, camera changes level with
			_parent->getMap()->getCamera()->setViewHeight(_unit->getPosition().z);
		}

		// is the step finished?
		if (_unit->getStatus() == STATUS_STANDING)
		{
			// move our personal lighting with us
			_terrain->calculateUnitLighting();

			BattleAction action;
			
			// check for proximity grenades (1 tile around the unit in every direction) (for large units, we need to check every tile it occupies)
			int size = _unit->getUnit()->getArmor()->getSize() - 1;
			for (int x = size; x >= 0; x--)
			{
				for (int y = size; y >= 0; y--)
				{
					for (int tx = -1; tx < 2; tx++)
					{
						for (int ty = -1; ty < 2; ty++)
						{
							Tile *t = _parent->getSave()->getTile(_unit->getPosition() + Position(x,y,0) + Position(tx,ty,0));
							if (t)
							for (std::vector<BattleItem*>::iterator i = t->getInventory()->begin(); i != t->getInventory()->end(); ++i)
							{
								if ((*i)->getRules()->getBattleType() == BT_PROXIMITYGRENADE && (*i)->getExplodeTurn() > 0)
								{
									Position p;
									p.x = t->getPosition().x*16 + 8;
									p.y = t->getPosition().y*16 + 8;
									p.z = t->getPosition().z*24 + t->getTerrainLevel();
									_parent->statePushNext(new ExplosionBState(_parent, p, (*i), (*i)->getPreviousOwner()));
									t->getInventory()->erase(i);
									return;
								}
							}
						}
					}
				}
			}

			// check for reaction fire
			if (_terrain->checkReactionFire(_unit, &action))
			{
				_parent->statePushBack(new ProjectileFlyBState(_parent, action));
				// unit got fired upon - stop walking
				_pf->abortPath();
				return;
			}
		}
		else
		{
			// make sure the unit sprites are up to date
			_parent->getMap()->cacheUnit(_unit);
		}
	}

	// we are just standing around, shouldn't we be walking?
	if (_unit->getStatus() == STATUS_STANDING)
	{
		// check if we can spot new units
		unitspotted = _terrain->calculateFOV(_unit);
		if (unitspotted)
		{
			_pf->abortPath();

			// a hostile unit will aggro on the new unit if it sees one - it will not start walking
			if (_unit->getFaction() == FACTION_HOSTILE)
			{
				AggroBAIState *aggro = dynamic_cast<AggroBAIState*>(_unit->getCurrentAIState());
				if (aggro == 0)
				{
					_unit->setAIState(new AggroBAIState(_parent->getSave(), _unit));
				}
			}
			return;
		}

		if (_unit->getVisible())
		{
			setNormalWalkSpeed();
		}
		else
		{
			_parent->setStateInterval(1);
		}
		int dir = _pf->getStartDirection();
		if (dir != -1)
		{
			Position destination;
			int tu = _pf->getTUCost(_unit->getPosition(), dir, &destination, _unit); // gets tu cost, but also gets the destination position.

			if (tu > _unit->getTimeUnits() && !_parent->dontSpendTUs())
			{
				_action.result = "STR_NOT_ENOUGH_TIME_UNITS";
				_pf->abortPath();
				return;
			}

			if (_parent->checkReservedTU(_unit, tu) == false)
			{
				_pf->abortPath();
				return;
			}

			// we are looking in the wrong way, turn first
			// we are not using the turn state, because turning during walking costs no tu
			if (dir != _unit->getDirection() && dir < Pathfinding::DIR_UP)
			{
				_unit->lookAt(dir);
				return;
			}

			// now open doors (if any)
			int door = _terrain->unitOpensDoor(_unit);
			if (door == 3)
			{
				return; // don't start walking yet, wait for the ufo door to open
			}
			if (door == 0)
			{
				_parent->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(3)->play(); // normal door
			}
			if (door == 1)
			{
				_parent->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(20)->play(); // ufo door
				return; // don't start walking yet, wait for the ufo door to open
			}

			// now start moving
			dir = _pf->dequeuePath();
			if (_unit->spendTimeUnits(tu, _parent->dontSpendTUs()))
			{
				if (_unit->spendEnergy(tu, _parent->dontSpendTUs()))
				{
					_unit->startWalking(dir, destination);
				}
				else
				{
					_action.result = "STR_NOT_ENOUGH_ENERGY";
					_parent->popState();
				}
			}
			else
			{
				_action.result = "STR_NOT_ENOUGH_TIME_UNITS";
				_parent->popState();
			}
			// make sure the unit sprites are up to date
			_parent->getMap()->cacheUnit(_unit);
		}
		else
		{
			postPathProcedures();
			return;
		}
	}

	// turning during walking costs no tu
	if (_unit->getStatus() == STATUS_TURNING)
	{
		_unit->turn();
		unitspotted = _terrain->calculateFOV(_unit);
		// make sure the unit sprites are up to date
		_parent->getMap()->cacheUnit(_unit);
		if (unitspotted)
		{
			_pf->abortPath();
			return;
		}
	}
}
Esempio n. 4
0
void UnitWalkBState::think()
{
	bool unitspotted = false;

	// during a walking cycle we make step sounds
	if (_unit->getStatus() == STATUS_WALKING || _unit->getStatus() == STATUS_FLYING)
	{

		if (_unit->getVisible() && _unit->getStatus() == STATUS_WALKING)
		{
			// play footstep sound 1
			if (_unit->getWalkingPhase() == 3)
			{
				Tile *tile = _unit->getTile();
				if (tile->getFootstepSound())
				{
					_parent->getGame()->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(22 + (tile->getFootstepSound()*2))->play();
				}
			}
			// play footstep sound 2
			if (_unit->getWalkingPhase() == 7)
			{
				Tile *tile = _unit->getTile();
				if (tile->getFootstepSound())
				{
					_parent->getGame()->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(23 + (tile->getFootstepSound()*2))->play();
				}
			}
		}

		_unit->keepWalking(); // advances the phase

		// unit moved from one tile to the other, update the tiles
		if (_unit->getPosition() != _unit->getLastPosition())
		{
			_parent->getGame()->getSavedGame()->getBattleGame()->getTile(_unit->getLastPosition())->setUnit(0); //don't change these
			_parent->getGame()->getSavedGame()->getBattleGame()->getTile(_unit->getPosition())->setUnit(_unit); //don't change these
			// if the unit changed level, camera changes level with
			_parent->getMap()->getCamera()->setViewHeight(_unit->getPosition().z);
		}

		// is the step finished?
		if (_unit->getStatus() == STATUS_STANDING)
		{
			_terrain->calculateUnitLighting();
			unitspotted = _terrain->calculateFOV(_unit);
			if (unitspotted)
			{
				_pf->abortPath();
				if (_unit->getFaction() == FACTION_HOSTILE)
				{
					AggroBAIState *aggro = dynamic_cast<AggroBAIState*>(_unit->getCurrentAIState());
					if (aggro == 0)
					{
						_unit->setAIState(new AggroBAIState(_parent->getGame()->getSavedGame()->getBattleGame(), _unit));
					}
					_parent->handleAI(_unit);
				}
				return;
			}
			BattleAction action;
			if (_terrain->checkReactionFire(_unit, &action))
			{
				_parent->statePushBack(new ProjectileFlyBState(_parent, action));
				// unit got fired upon - stop walking
				_pf->abortPath();
				return;
			}
		}
		else
		{
			// make sure the unit sprites are up to date
			_parent->getMap()->cacheUnit(_unit);
		}
	}

	// we are just standing around, shouldn't we be walking?
	if (_unit->getStatus() == STATUS_STANDING)
	{
		if (_unit->getVisible())
		{
			setNormalWalkSpeed();
		}
		else
		{
			_parent->setStateInterval(1);
		}
		int dir = _pf->getStartDirection();
		if (dir != -1)
		{
			Position destination;
			int tu = _pf->getTUCost(_unit->getPosition(), dir, &destination, _unit); // gets tu cost, but also gets the destination position.

			if (tu > _unit->getTimeUnits() && !_parent->dontSpendTUs())
			{
				_result = "STR_NOT_ENOUGH_TIME_UNITS";
				_pf->abortPath();
				return;
			}

			if (_parent->checkReservedTU(_unit, tu) == false)
			{
				_pf->abortPath();
				return;
			}

			// we are looking in the wrong way, turn first
			// we are not using the turn state, because turning during walking costs no tu
			if (dir != _unit->getDirection() && dir < Pathfinding::DIR_UP)
			{
				_unit->lookAt(dir);
				return;
			}

			// now open doors (if any)
			int door = _terrain->unitOpensDoor(_unit);
			if (door == 3)
			{
				return; // don't start walking yet, wait for the ufo door to open
			}
			if (door == 0)
			{
				_parent->getGame()->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(3)->play(); // normal door
			}
			if (door == 1)
			{
				_parent->getGame()->getResourcePack()->getSoundSet("BATTLE.CAT")->getSound(20)->play(); // ufo door
				return; // don't start walking yet, wait for the ufo door to open
			}

			// now start moving
			dir = _pf->dequeuePath();
			if (_unit->spendTimeUnits(tu, _parent->dontSpendTUs()))
			{
				if (_unit->spendEnergy(tu, _parent->dontSpendTUs()))
				{
					_unit->startWalking(dir, destination);
				}
				else
				{
					_result = "STR_NOT_ENOUGH_ENERGY";
					_parent->popState();
				}
			}
			else
			{
				_result = "STR_NOT_ENOUGH_TIME_UNITS";
				_parent->popState();
			}
			// make sure the unit sprites are up to date
			_parent->getMap()->cacheUnit(_unit);
		}
		else
		{
			postPathProcedures();
			return;
		}
	}

	// turning during walking costs no tu
	if (_unit->getStatus() == STATUS_TURNING)
	{
		_unit->turn();
		unitspotted = _terrain->calculateFOV(_unit);
		// make sure the unit sprites are up to date
		_parent->getMap()->cacheUnit(_unit);
		if (unitspotted)
		{
			_pf->abortPath();
			return;
		}
	}
}
Esempio n. 5
0
/**
 * Runs state functionality every cycle.
 */
void UnitWalkBState::think()
{
	bool unitSpotted = false;
	int size = _unit->getArmor()->getSize() - 1;
	bool onScreen = (_unit->getVisible() && _parent->getMap()->getCamera()->isOnScreen(_unit->getPosition(), true, size, false));
	if (_unit->isKneeled())
	{
		if (_parent->kneel(_unit))
		{
			_unit->setCache(0);
			_terrain->calculateFOV(_unit);
			_parent->getMap()->cacheUnit(_unit);
			return;
		}
		else
		{
			_action.result = "STR_NOT_ENOUGH_TIME_UNITS";
			_pf->abortPath();
			_parent->popState();
			return;
		}
	}
	Tile *tileBelow = _parent->getSave()->getTile(_unit->getPosition() + Position(0,0,-1));

	if (_unit->isOut())
	{
		_pf->abortPath();
		_parent->popState();
		return;
	}

	if (_unit->getStatus() == STATUS_WALKING || _unit->getStatus() == STATUS_FLYING)
	{
		if ((_parent->getSave()->getTile(_unit->getDestination())->getUnit() == 0) || // next tile must be not occupied
			(_parent->getSave()->getTile(_unit->getDestination())->getUnit() == _unit))
		{
			bool onScreenBoundary = (_unit->getVisible() && _parent->getMap()->getCamera()->isOnScreen(_unit->getPosition(), true, size, true));
			_unit->keepWalking(tileBelow, onScreenBoundary); // advances the phase
			playMovementSound();
		}
		else if (!_falling)
		{
			_unit->lookAt(_unit->getDestination(), (_unit->getTurretType() != -1));	// turn to undiscovered unit
			_pf->abortPath();
		}

		// unit moved from one tile to the other, update the tiles
		if (_unit->getPosition() != _unit->getLastPosition())
		{
			bool largeCheck = true;
			for (int x = size; x >= 0; x--)
			{
				for (int y = size; y >= 0; y--)
				{
					Tile *otherTileBelow = _parent->getSave()->getTile(_unit->getPosition() + Position(x,y,-1));
					if (!_parent->getSave()->getTile(_unit->getPosition() + Position(x,y,0))->hasNoFloor(otherTileBelow) || _unit->getMovementType() == MT_FLY)
						largeCheck = false;
					_parent->getSave()->getTile(_unit->getLastPosition() + Position(x,y,0))->setUnit(0);
				}
			}
			for (int x = size; x >= 0; x--)
			{
				for (int y = size; y >= 0; y--)
				{
					_parent->getSave()->getTile(_unit->getPosition() + Position(x,y,0))->setUnit(_unit, _parent->getSave()->getTile(_unit->getPosition() + Position(x,y,-1)));
				}
			}
			_falling = largeCheck && _unit->getPosition().z != 0 && _unit->getTile()->hasNoFloor(tileBelow) && _unit->getMovementType() != MT_FLY && _unit->getWalkingPhase() == 0;

			if (_falling)
			{
				for (int x = size; x >= 0; --x)
				{
					for (int y = size; y >= 0; --y)
					{
						Tile *otherTileBelow = _parent->getSave()->getTile(_unit->getPosition() + Position(x,y,-1));
						if (otherTileBelow && otherTileBelow->getUnit())
						{
							_falling = false;
							_pf->dequeuePath();
							_parent->getSave()->addFallingUnit(_unit);
							_parent->statePushFront(new UnitFallBState(_parent));
							return;
						}
					}
				}
			}

			if (!_parent->getMap()->getCamera()->isOnScreen(_unit->getPosition(), true, size, false) && _unit->getFaction() != FACTION_PLAYER && _unit->getVisible())
				_parent->getMap()->getCamera()->centerOnPosition(_unit->getPosition());
			// if the unit changed level, camera changes level with
			_parent->getMap()->getCamera()->setViewLevel(_unit->getPosition().z);
		}

		// is the step finished?
		if (_unit->getStatus() == STATUS_STANDING)
		{
			// update the TU display
			_parent->getSave()->getBattleState()->updateSoldierInfo();
			// if the unit burns floortiles, burn floortiles as long as we're not falling
			if (!_falling && (_unit->getSpecialAbility() == SPECAB_BURNFLOOR || _unit->getSpecialAbility() == SPECAB_BURN_AND_EXPLODE))
			{
				_unit->getTile()->ignite(1);
				Position posHere = _unit->getPosition();
				Position voxelHere = (posHere * Position(16,16,24)) + Position(8,8,-(_unit->getTile()->getTerrainLevel()));
				_parent->getTileEngine()->hit(voxelHere, _unit->getBaseStats()->strength, DT_IN, _unit);
				
				if (_unit->getStatus() != STATUS_STANDING) // ie: we burned a hole in the floor and fell through it
				{
					_pf->abortPath();
					return;
				}
			}

			// move our personal lighting with us
			_terrain->calculateUnitLighting();
			if (_unit->getFaction() != FACTION_PLAYER)
			{
				_unit->setVisible(false);
			}
			_terrain->calculateFOV(_unit->getPosition());
			unitSpotted = (!_falling && !_action.desperate && _parent->getPanicHandled() && _numUnitsSpotted != _unit->getUnitsSpottedThisTurn().size());

			if (_parent->checkForProximityGrenades(_unit))
			{
				_parent->popState();
				return;
			}
			if (unitSpotted)
			{
				_unit->setCache(0);
				_parent->getMap()->cacheUnit(_unit);
				_pf->abortPath();
				_parent->popState();
				return;
			}
			// check for reaction fire
			if (!_falling)
			{
				if (_terrain->checkReactionFire(_unit))
				{
					// unit got fired upon - stop walking
					_unit->setCache(0);
					_parent->getMap()->cacheUnit(_unit);
					_pf->abortPath();
					_parent->popState();
					return;
				}
			}
		}
		else if (onScreen)
		{
			// make sure the unit sprites are up to date
			if (_pf->getStrafeMove())
			{
				// This is where we fake out the strafe movement direction so the unit "moonwalks"
				int dirTemp = _unit->getDirection();
				_unit->setDirection(_unit->getFaceDirection());
				_parent->getMap()->cacheUnit(_unit);
				_unit->setDirection(dirTemp);
			}
			else
			{
				_parent->getMap()->cacheUnit(_unit);
			}
		}
	}

	// we are just standing around, shouldn't we be walking?
	if (_unit->getStatus() == STATUS_STANDING || _unit->getStatus() == STATUS_PANICKING)
	{
		// check if we did spot new units
		if (unitSpotted && !_action.desperate && _unit->getCharging() == 0 && !_falling)
		{
			if (Options::traceAI) { Log(LOG_INFO) << "Uh-oh! Company!"; }
			_unit->setHiding(false); // clearly we're not hidden now
			_parent->getMap()->cacheUnit(_unit);
			postPathProcedures();
			return;
		}

		if (onScreen || _parent->getSave()->getDebugMode())
		{
			setNormalWalkSpeed();
		}
		else
		{
			_parent->setStateInterval(0);
		}
		int dir = _pf->getStartDirection();
		if (_falling)
		{
			dir = Pathfinding::DIR_DOWN;
		}

		if (dir != -1)
		{
			if (_pf->getStrafeMove())
			{
				_unit->setFaceDirection(_unit->getDirection());
			}

			Position destination;
			int tu = _pf->getTUCost(_unit->getPosition(), dir, &destination, _unit, 0, false); // gets tu cost, but also gets the destination position.
			if (_unit->getFaction() != FACTION_PLAYER &&
				_unit->getSpecialAbility() < SPECAB_BURNFLOOR &&
				_parent->getSave()->getTile(destination) &&
				_parent->getSave()->getTile(destination)->getFire() > 0)
			{
				tu -= 32; // we artificially inflate the TU cost by 32 points in getTUCost under these conditions, so we have to deflate it here.
			}
			if (_falling)
			{
				tu = 0;
			}
			int energy = tu;
			if (dir >= Pathfinding::DIR_UP)
			{
				energy = 0;
			}
			else if (_action.run)
			{
				tu *= 0.75;
				energy *= 1.5;
			}
			if (tu > _unit->getTimeUnits())
			{
				if (_parent->getPanicHandled() && tu < 255)
				{
					_action.result = "STR_NOT_ENOUGH_TIME_UNITS";
				}
				_pf->abortPath();
				_unit->setCache(0);
				_parent->getMap()->cacheUnit(_unit);
				_parent->popState();
				return;
			}

			if (energy / 2 > _unit->getEnergy())
			{
				if (_parent->getPanicHandled())
				{
					_action.result = "STR_NOT_ENOUGH_ENERGY";
				}
				_pf->abortPath();
				_unit->setCache(0);
				_parent->getMap()->cacheUnit(_unit);
				_parent->popState();
				return;
			}

			if (_parent->getPanicHandled() && _parent->checkReservedTU(_unit, tu) == false)
			{
				_pf->abortPath();
				_unit->setCache(0);
				_parent->getMap()->cacheUnit(_unit);
				return;
			}

			// we are looking in the wrong way, turn first (unless strafing)
			// we are not using the turn state, because turning during walking costs no tu
			if (dir != _unit->getDirection() && dir < Pathfinding::DIR_UP && !_pf->getStrafeMove())
			{
				_unit->lookAt(dir);
				_unit->setCache(0);
				_parent->getMap()->cacheUnit(_unit);
				return;
			}

			// now open doors (if any)
			if (dir < Pathfinding::DIR_UP)
			{
				int door = _terrain->unitOpensDoor(_unit, false, dir);
				if (door == 3)
				{
					return; // don't start walking yet, wait for the ufo door to open
				}
				if (door == 0)
				{
					_parent->getMod()->getSoundByDepth(_parent->getDepth(), Mod::DOOR_OPEN)->play(-1, _parent->getMap()->getSoundAngle(_unit->getPosition())); // normal door
				}
				if (door == 1)
				{
					_parent->getMod()->getSoundByDepth(_parent->getDepth(), Mod::SLIDING_DOOR_OPEN)->play(-1, _parent->getMap()->getSoundAngle(_unit->getPosition())); // ufo door
					return; // don't start walking yet, wait for the ufo door to open
				}
			}
			for (int x = size; x >= 0; --x)
			{
				for (int y = size; y >= 0; --y)
				{
					BattleUnit* unitInMyWay = _parent->getSave()->getTile(destination + Position(x,y,0))->getUnit();
					BattleUnit* unitBelowMyWay = 0;
					Tile* belowDest = _parent->getSave()->getTile(destination + Position(x,y,-1));
					if (belowDest)
					{
						unitBelowMyWay = belowDest->getUnit();
					}
					// can't walk into units in this tile, or on top of other units sticking their head into this tile
					if (!_falling &&
						((unitInMyWay && unitInMyWay != _unit)
						|| (belowDest && unitBelowMyWay && unitBelowMyWay != _unit &&
						(-belowDest->getTerrainLevel() + unitBelowMyWay->getFloatHeight() + unitBelowMyWay->getHeight())
						>= 28)))  // 4+ voxels poking into the tile above, we don't kick people in the head here at XCom.
					{
						_action.TU = 0;
						_pf->abortPath();
						_unit->setCache(0);
						_parent->getMap()->cacheUnit(_unit);
						_parent->popState();
						return;
					}
				}
			}
			// now start moving
			dir = _pf->dequeuePath();
			if (_falling)
			{
				dir = Pathfinding::DIR_DOWN;
			}

			if (_unit->spendTimeUnits(tu))
			{
				if (_unit->spendEnergy(energy))
				{
					Tile *tileBelow = _parent->getSave()->getTile(_unit->getPosition() + Position(0,0,-1));
					_unit->startWalking(dir, destination, tileBelow, onScreen);
					_beforeFirstStep = false;
				}
			}
			// make sure the unit sprites are up to date
			if (onScreen)
			{
				if (_pf->getStrafeMove())
				{
					// This is where we fake out the strafe movement direction so the unit "moonwalks"
					int dirTemp = _unit->getDirection();
					_unit->setDirection(_unit->getFaceDirection());
					_parent->getMap()->cacheUnit(_unit);
					_unit->setDirection(dirTemp);
				}
				else
				{
					_parent->getMap()->cacheUnit(_unit);
				}
			}
		}
		else
		{
			postPathProcedures();
			return;
		}
	}

	// turning during walking costs no tu
	if (_unit->getStatus() == STATUS_TURNING)
	{
		// except before the first step.
		if (_beforeFirstStep)
		{
			_preMovementCost++;
		}

		_unit->turn();

		// calculateFOV is unreliable for setting the unitSpotted bool, as it can be called from various other places
		// in the code, ie: doors opening, and this messes up the result.
		_terrain->calculateFOV(_unit);
		unitSpotted = (!_falling && !_action.desperate && _parent->getPanicHandled() && _numUnitsSpotted != _unit->getUnitsSpottedThisTurn().size());

		// make sure the unit sprites are up to date
		_unit->setCache(0);
		_parent->getMap()->cacheUnit(_unit);

		if (unitSpotted && !_action.desperate && !_unit->getCharging() && !_falling)
		{
			if (_beforeFirstStep)
			{
				_unit->spendTimeUnits(_preMovementCost);
			}
			if (Options::traceAI) { Log(LOG_INFO) << "Egads! A turn reveals new units! I must pause!"; }
			_unit->setHiding(false); // not hidden, are we...
			_pf->abortPath();
			_unit->abortTurn(); //revert to a standing state.
			_unit->setCache(0);
			_parent->getMap()->cacheUnit(_unit);
			_parent->popState();
		}
	}
}
Esempio n. 6
0
void UnitWalkBState::think()
{
	bool unitspotted = false;
	bool onScreen = (_unit->getVisible() && _parent->getMap()->getCamera()->isOnScreen(_unit->getPosition()));
	Tile *tileBelow = _parent->getSave()->getTile(_unit->getPosition() + Position(0,0,-1));

	if (_unit->isOut())
	{
		_pf->abortPath();
		_parent->popState();
		return;
	}

	if (_unit->getStatus() == STATUS_WALKING || _unit->getStatus() == STATUS_FLYING)
	{
		if ((_parent->getSave()->getTile(_unit->getDestination())->getUnit() == 0) || // next tile must be not occupied
			(_parent->getSave()->getTile(_unit->getDestination())->getUnit() == _unit))
		{
			playMovementSound();
			_unit->keepWalking(tileBelow, onScreen); // advances the phase
		}
		else if (!_falling)
		{
			_unit->lookAt(_unit->getDestination(), true);	// turn to undiscovered unit
			_pf->abortPath();
		}

		// unit moved from one tile to the other, update the tiles
		if (_unit->getPosition() != _unit->getLastPosition())
		{	
			int size = _unit->getArmor()->getSize() - 1;
			bool largeCheck = true;
			for (int x = size; x >= 0; x--)
			{
				for (int y = size; y >= 0; y--)
				{
					Tile *otherTileBelow = _parent->getSave()->getTile(_unit->getPosition() + Position(x,y,-1));
					if (!_parent->getSave()->getTile(_unit->getPosition() + Position(x,y,0))->hasNoFloor(otherTileBelow) || _unit->getArmor()->getMovementType() == MT_FLY)
						largeCheck = false;
					_parent->getSave()->getTile(_unit->getLastPosition() + Position(x,y,0))->setUnit(0);
				}
			}
			for (int x = size; x >= 0; x--)
			{
				for (int y = size; y >= 0; y--)
				{
					_parent->getSave()->getTile(_unit->getPosition() + Position(x,y,0))->setUnit(_unit, _parent->getSave()->getTile(_unit->getPosition() + Position(x,y,-1)));
				}
			}
			_falling = largeCheck && _unit->getPosition().z != 0 && _unit->getTile()->hasNoFloor(tileBelow) && _unit->getArmor()->getMovementType() != MT_FLY && _unit->getWalkingPhase() == 0;
			
			if (_falling)
			{
				for (int x = _unit->getArmor()->getSize() - 1; x >= 0; --x)
				{
					for (int y = _unit->getArmor()->getSize() - 1; y >= 0; --y)
					{
						Tile *otherTileBelow = _parent->getSave()->getTile(_unit->getPosition() + Position(x,y,-1));
						if (otherTileBelow && otherTileBelow->getUnit())
						{
							Position originalPosition(otherTileBelow->getUnit()->getPosition());
							for (int dir = 0; dir < Pathfinding::DIR_UP; dir++)
							{
								Position offset;
								Pathfinding::directionToVector(dir, &offset);
								Tile *t = _parent->getSave()->getTile(originalPosition + offset);
								Tile *bt = _parent->getSave()->getTile(originalPosition + offset + Position(0,0,-1));
								Tile *bu = _parent->getSave()->getTile(originalPosition + Position(0,0,-1));
								if (t && !_parent->getPathfinding()->isBlocked(otherTileBelow, t, dir, 0) && t->getUnit() == 0 && (!t->hasNoFloor(bt) || otherTileBelow->getUnit()->getArmor()->getMovementType() == MT_FLY))
								{
									_falling = false;
									_pf->dequeuePath();
									otherTileBelow->getUnit()->startWalking(dir, t->getPosition(), t, bu, bt, onScreen);
									_parent->getSave()->addFallingUnit(otherTileBelow->getUnit());
									_parent->getSave()->addFallingUnit(_unit);
									_parent->statePushFront(new UnitFallBState(_parent));
									return;
								}
							}
						}
					}
				}
			}

			if (!_parent->getMap()->getCamera()->isOnScreen(_unit->getPosition()) && _unit->getFaction() != FACTION_PLAYER && _unit->getVisible())
				_parent->getMap()->getCamera()->centerOnPosition(_unit->getPosition());
			// if the unit changed level, camera changes level with
			_parent->getMap()->getCamera()->setViewLevel(_unit->getPosition().z);
		}

		// is the step finished?
		if (_unit->getStatus() == STATUS_STANDING)
		{
			// if the unit burns floortiles, burn floortiles
			if (_unit->getSpecialAbility() == SPECAB_BURNFLOOR)
			{
				_unit->getTile()->destroy(MapData::O_FLOOR);
			}

			// move our personal lighting with us
			_terrain->calculateUnitLighting();
			unitspotted = _terrain->calculateFOV(_unit);

			BattleAction action;
			
			// check for proximity grenades (1 tile around the unit in every direction) (for large units, we need to check every tile it occupies)
			int size = _unit->getArmor()->getSize() - 1;
			for (int x = size; x >= 0; x--)
			{
				for (int y = size; y >= 0; y--)
				{
					for (int tx = -1; tx < 2; tx++)
					{
						for (int ty = -1; ty < 2; ty++)
						{
							Tile *t = _parent->getSave()->getTile(_unit->getPosition() + Position(x,y,0) + Position(tx,ty,0));
							if (t)
							for (std::vector<BattleItem*>::iterator i = t->getInventory()->begin(); i != t->getInventory()->end(); ++i)
							{
								if ((*i)->getRules()->getBattleType() == BT_PROXIMITYGRENADE && (*i)->getExplodeTurn() > 0)
								{
									Position p;
									p.x = t->getPosition().x*16 + 8;
									p.y = t->getPosition().y*16 + 8;
									p.z = t->getPosition().z*24 + t->getTerrainLevel();
									_parent->statePushNext(new ExplosionBState(_parent, p, (*i), (*i)->getPreviousOwner()));
									t->getInventory()->erase(i);
									return;
								}
							}
						}
					}
				}
			}

			// check for reaction fire
			if (!_falling && !_action.reckless && _terrain->checkReactionFire(_unit, &action))
			{
				postPathProcedures();
				action.cameraPosition = _parent->getMap()->getCamera()->getMapOffset();
				_parent->statePushBack(new ProjectileFlyBState(_parent, action));
				// unit got fired upon - stop walking
				_pf->abortPath();
				return;
			}
		}
		else if (onScreen)
		{
			// make sure the unit sprites are up to date
			if (_pf->getStrafeMove())
			{
				// This is where we fake out the strafe movement direction so the unit "moonwalks"
				int dirTemp = _unit->getDirection();
				_unit->setDirection(_unit->getFaceDirection());
				_parent->getMap()->cacheUnit(_unit);
				_unit->setDirection(dirTemp);
			}
			else
			{
				_parent->getMap()->cacheUnit(_unit);
			}
		}
	}

	// we are just standing around, shouldn't we be walking?
	if (_unit->getStatus() == STATUS_STANDING || _unit->getStatus() == STATUS_PANICKING)
	{
		// check if we did spot new units
		if (unitspotted && !_action.desperate && _unit->getCharging() == 0 && !_falling)
		{
			_parent->getMap()->cacheUnit(_unit);
			_pf->abortPath();
			return;
		}

		if (onScreen || _parent->getSave()->getDebugMode())
		{
			setNormalWalkSpeed();
		}
		else
		{
			_parent->setStateInterval(0);
		}
		int dir = _pf->getStartDirection();
		if (_falling)
		{
			dir = Pathfinding::DIR_DOWN;
		}

		if (dir != -1)
		{
			if (_pf->getStrafeMove())
			{
				_unit->setFaceDirection(_unit->getDirection());
			}

			Position destination;
			int tu = _pf->getTUCost(_unit->getPosition(), dir, &destination, _unit, 0); // gets tu cost, but also gets the destination position.
			if (_falling)
			{
				tu = 0;
			}
			int energy = tu;
			if (_action.run)
			{
				tu *= 0.75;
				energy *= 1.5;
			}

			if (tu > _unit->getTimeUnits())
			{
				_action.result = "STR_NOT_ENOUGH_TIME_UNITS";
				_pf->abortPath();
				_parent->getMap()->cacheUnit(_unit);
				return;
			}

			if (_parent->checkReservedTU(_unit, tu) == false)
			{
				_pf->abortPath();
				_parent->getMap()->cacheUnit(_unit);
				return;
			}

			// we are looking in the wrong way, turn first (unless strafing)
			// we are not using the turn state, because turning during walking costs no tu
			if (dir != _unit->getDirection() && dir < Pathfinding::DIR_UP && !_pf->getStrafeMove())
			{
				_unit->lookAt(dir, true);
				return;
			}

			// now open doors (if any)
			int door = _terrain->unitOpensDoor(_unit);
			if (door == 3)
			{
				return; // don't start walking yet, wait for the ufo door to open
			}
			if (door == 0)
			{
				_parent->getResourcePack()->getSound("BATTLE.CAT", 3)->play(); // normal door
			}
			if (door == 1)
			{
				_parent->getResourcePack()->getSound("BATTLE.CAT", 20)->play(); // ufo door
				return; // don't start walking yet, wait for the ufo door to open
			}

			// now start moving
			dir = _pf->dequeuePath();
			if (_falling)
			{
				dir = Pathfinding::DIR_DOWN;
			}

			if (_unit->spendTimeUnits(tu))
			{
				if (_unit->spendEnergy(energy))
				{
					Tile *tileBelow = _parent->getSave()->getTile(_unit->getPosition() + Position(0,0,-1));
					Tile *tileBelowDestination = _parent->getSave()->getTile(destination + Position(0,0,-1));
					_unit->startWalking(dir, destination, _parent->getSave()->getTile(destination), tileBelow, tileBelowDestination, onScreen);
				}
				else
				{
					_action.result = "STR_NOT_ENOUGH_ENERGY";
					_parent->getMap()->cacheUnit(_unit);
					_parent->popState();
				}
			}
			else
			{
				_action.result = "STR_NOT_ENOUGH_TIME_UNITS";
				_parent->getMap()->cacheUnit(_unit);
				_parent->popState();
			}
			// make sure the unit sprites are up to date
			if (onScreen)
			{
				if (_pf->getStrafeMove())
				{
					// This is where we fake out the strafe movement direction so the unit "moonwalks"
					int dirTemp = _unit->getDirection();
					_unit->setDirection(_unit->getFaceDirection());
					_unit->setDirection(dirTemp);
				}
				_parent->getMap()->cacheUnit(_unit);
			}
		}
		else
		{
			postPathProcedures();
			return;
		}
	}

	// turning during walking costs no tu
	if (_unit->getStatus() == STATUS_TURNING)
	{
		if (_action.strafe && _unit->getTurretType() > -1)
		{
			_unit->turn(true);
		}
		else
		{
			_unit->turn();
		}

		unitspotted = _terrain->calculateFOV(_unit);

		// make sure the unit sprites are up to date
		if (onScreen)
			_parent->getMap()->cacheUnit(_unit);
		if (unitspotted && !_action.desperate && _unit->getStatus() != STATUS_PANICKING && _unit->getCharging() == 0 && !_falling)
		{
			_pf->abortPath();
			_parent->getMap()->cacheUnit(_unit);
			return;
		}
	}
}