/** * Updates all soldier stats when the soldier changes. */ void InventoryState::init() { State::init(); BattleUnit *unit = _battleGame->getSelectedUnit(); // no selected unit, close inventory if (unit == 0) { btnOkClick(0); return; } // skip to the first unit with inventory if (!unit->hasInventory()) { if (_parent) { _parent->selectNextPlayerUnit(false, false, true); } else { _battleGame->selectNextPlayerUnit(false, false, true); } // no available unit, close inventory if (_battleGame->getSelectedUnit() == 0 || !_battleGame->getSelectedUnit()->hasInventory()) { // starting a mission with just vehicles btnOkClick(0); return; } else { unit = _battleGame->getSelectedUnit(); } } unit->setCache(0); _soldier->clear(); _rank->clear(); _txtName->setBig(); _txtName->setText(unit->getName(_game->getLanguage())); _inv->setSelectedUnit(unit); Soldier *s = unit->getGeoscapeSoldier(); if (s) { SurfaceSet *texture = _game->getMod()->getSurfaceSet("SMOKE.PCK"); texture->getFrame(20 + s->getRank())->setX(0); texture->getFrame(20 + s->getRank())->setY(0); texture->getFrame(20 + s->getRank())->blit(_rank); std::string look = s->getArmor()->getSpriteInventory(); if (s->getGender() == GENDER_MALE) look += "M"; else look += "F"; if (s->getLook() == LOOK_BLONDE) look += "0"; if (s->getLook() == LOOK_BROWNHAIR) look += "1"; if (s->getLook() == LOOK_ORIENTAL) look += "2"; if (s->getLook() == LOOK_AFRICAN) look += "3"; look += ".SPK"; const std::set<std::string> &ufographContents = FileMap::getVFolderContents("UFOGRAPH"); std::string lcaseLook = look; std::transform(lcaseLook.begin(), lcaseLook.end(), lcaseLook.begin(), tolower); if (ufographContents.find("lcaseLook") == ufographContents.end() && !_game->getMod()->getSurface(look)) { look = s->getArmor()->getSpriteInventory() + ".SPK"; } _game->getMod()->getSurface(look)->blit(_soldier); } else { Surface *armorSurface = _game->getMod()->getSurface(unit->getArmor()->getSpriteInventory()); if (armorSurface) { armorSurface->blit(_soldier); } } updateStats(); _refreshMouse(); }
/** * Calculates the effects of the explosion. */ void ExplosionBState::explode() { bool terrainExplosion = false; SavedBattleGame *save = _parent->getSave(); // after the animation is done, the real explosion/hit takes place if (_item) { if (!_unit && _item->getPreviousOwner()) { _unit = _item->getPreviousOwner(); } if (_areaOfEffect) { save->getTileEngine()->explode(_center, _power, _item->getRules()->getDamageType(), _item->getRules()->getExplosionRadius(), _unit); } else { BattleUnit *victim = save->getTileEngine()->hit(_center, _power, _item->getRules()->getDamageType(), _unit); // check if this unit turns others into zombies if (!_unit->getZombieUnit().empty() && victim && victim->getArmor()->getSize() == 1 && victim->getSpawnUnit().empty() && victim->getOriginalFaction() != FACTION_HOSTILE) { // converts the victim to a zombie on death victim->setSpecialAbility(SPECAB_RESPAWN); victim->setSpawnUnit(_unit->getZombieUnit()); } } } if (_tile) { save->getTileEngine()->explode(_center, _power, DT_HE, _power/10); terrainExplosion = true; } if (!_tile && !_item) { // explosion not caused by terrain or an item, must be by a unit (cyberdisc) save->getTileEngine()->explode(_center, _power, DT_HE, 6); terrainExplosion = true; } // now check for new casualties _parent->checkForCasualties(_item, _unit, false, terrainExplosion); // if this explosion was caused by a unit shooting, now it's the time to put the gun down if (_unit && !_unit->isOut() && _lowerWeapon) { _unit->aim(false); } _parent->getMap()->cacheUnits(); _parent->popState(); // check for terrain explosions Tile *t = save->getTileEngine()->checkForTerrainExplosions(); if (t) { Position p = Position(t->getPosition().x * 16, t->getPosition().y * 16, t->getPosition().z * 24); _parent->statePushFront(new ExplosionBState(_parent, p, 0, _unit, t)); } if (_item && (_item->getRules()->getBattleType() == BT_GRENADE || _item->getRules()->getBattleType() == BT_PROXIMITYGRENADE)) { for (std::vector<BattleItem*>::iterator j = _parent->getSave()->getItems()->begin(); j != _parent->getSave()->getItems()->end(); ++j) { if (_item->getId() == (*j)->getId()) { delete *j; _parent->getSave()->getItems()->erase(j); break; } } } }
/** * Calculates the effects of the explosion. */ void ExplosionBState::explode() { bool terrainExplosion = false; SavedBattleGame *save = _parent->getSave(); // after the animation is done, the real explosion/hit takes place if (_item) { if (!_unit && _item->getPreviousOwner()) { _unit = _item->getPreviousOwner(); } BattleUnit *victim = 0; if (_areaOfEffect) { save->getTileEngine()->explode(_center, _power, _item->getRules()->getDamageType(), _item->getRules()->getExplosionRadius(), _unit); } else if (!_cosmetic) { ItemDamageType type = _item->getRules()->getDamageType(); victim = save->getTileEngine()->hit(_center, _power, type, _unit); } // check if this unit turns others into zombies if (!_item->getRules()->getZombieUnit().empty() && victim && victim->getArmor()->getSize() == 1 && (victim->getGeoscapeSoldier() || victim->getUnitRules()->getRace() == "STR_CIVILIAN") && victim->getSpawnUnit().empty()) { // converts the victim to a zombie on death victim->setRespawn(true); victim->setSpawnUnit(_item->getRules()->getZombieUnit()); } } if (_tile) { ItemDamageType DT; switch (_tile->getExplosiveType()) { case 0: DT = DT_HE; break; case 5: DT = DT_IN; break; case 6: DT = DT_STUN; break; default: DT = DT_SMOKE; break; } if (DT != DT_HE) { _tile->setExplosive(0,0,true); } save->getTileEngine()->explode(_center, _power, DT, _power/10); terrainExplosion = true; } if (!_tile && !_item) { int radius = 6; // explosion not caused by terrain or an item, must be by a unit (cyberdisc) if (_unit && (_unit->getSpecialAbility() == SPECAB_EXPLODEONDEATH || _unit->getSpecialAbility() == SPECAB_BURN_AND_EXPLODE)) { radius = _parent->getMod()->getItem(_unit->getArmor()->getCorpseGeoscape())->getExplosionRadius(); } save->getTileEngine()->explode(_center, _power, DT_HE, radius); terrainExplosion = true; } if (!_cosmetic) { // now check for new casualties _parent->checkForCasualties(_item, _unit, false, terrainExplosion); } // if this explosion was caused by a unit shooting, now it's the time to put the gun down if (_unit && !_unit->isOut() && _lowerWeapon) { _unit->aim(false); _unit->setCache(0); } _parent->getMap()->cacheUnits(); _parent->popState(); // check for terrain explosions Tile *t = save->getTileEngine()->checkForTerrainExplosions(); if (t) { Position p = Position(t->getPosition().x * 16, t->getPosition().y * 16, t->getPosition().z * 24); p += Position(8,8,0); _parent->statePushFront(new ExplosionBState(_parent, p, 0, _unit, t)); } if (_item && (_item->getRules()->getBattleType() == BT_GRENADE || _item->getRules()->getBattleType() == BT_PROXIMITYGRENADE)) { for (std::vector<BattleItem*>::iterator j = _parent->getSave()->getItems()->begin(); j != _parent->getSave()->getItems()->end(); ++j) { if (_item->getId() == (*j)->getId()) { _parent->getSave()->removeItem(_item); break; } } } }
/** * Updates all soldier stats when the soldier changes. */ void InventoryState::init() { BattleUnit *unit = _battleGame->getSelectedUnit(); // no selected unit, close inventory if (unit == 0) { btnOkClick(0); return; } // skip to the first unit with inventory if (!unit->hasInventory()) { if (_parent) { _parent->selectNextPlayerUnit(false, false, true); } else { _battleGame->selectNextPlayerUnit(false, false, true); } // no available unit, close inventory if (_battleGame->getSelectedUnit() == 0) { // starting a mission with just vehicles btnOkClick(0); return; } else { unit = _battleGame->getSelectedUnit(); } } if (_parent) _parent->getMap()->getCamera()->centerOnPosition(unit->getPosition()); unit->setCache(0); _soldier->clear(); _btnRank->clear(); _txtName->setBig(); _txtName->setText(unit->getName(_game->getLanguage())); _inv->setSelectedUnit(unit); Soldier *s = _game->getSavedGame()->getSoldier(unit->getId()); if (s) { SurfaceSet *texture = _game->getResourcePack()->getSurfaceSet("BASEBITS.PCK"); texture->getFrame(s->getRankSprite())->setX(0); texture->getFrame(s->getRankSprite())->setY(0); texture->getFrame(s->getRankSprite())->blit(_btnRank); std::string look = s->getArmor()->getSpriteInventory(); if (s->getGender() == GENDER_MALE) look += "M"; else look += "F"; if (s->getLook() == LOOK_BLONDE) look += "0"; if (s->getLook() == LOOK_BROWNHAIR) look += "1"; if (s->getLook() == LOOK_ORIENTAL) look += "2"; if (s->getLook() == LOOK_AFRICAN) look += "3"; look += ".SPK"; if (!CrossPlatform::fileExists(CrossPlatform::getDataFile("UFOGRAPH/" + look)) && !_game->getResourcePack()->getSurface(look)) { look = s->getArmor()->getSpriteInventory() + ".SPK"; } _game->getResourcePack()->getSurface(look)->blit(_soldier); } else { Surface *armorSurface = _game->getResourcePack()->getSurface(unit->getArmor()->getSpriteInventory()); if (armorSurface) { armorSurface->blit(_soldier); } } if (_showMoreStatsInInventoryView && !_tu) { std::wstringstream ss2; ss2 << tr("STR_FACCURACY") << L'\x01' << (int)(unit->getStats()->firing * unit->getAccuracyModifier()); _txtFAcc->setText(ss2.str()); std::wstringstream ss3; ss3 << tr("STR_REACT") << L'\x01' << unit->getStats()->reactions; _txtReact->setText(ss3.str()); if (unit->getStats()->psiSkill > 0) { std::wstringstream ss4; ss4 << tr("STR_PSKILL") << L'\x01' << unit->getStats()->psiSkill; _txtPSkill->setText(ss4.str()); std::wstringstream ss5; ss5 << tr("STR_PSTRENGTH") << L'\x01' << unit->getStats()->psiStrength; _txtPStr->setText(ss5.str()); } else { _txtPSkill->setText(L""); _txtPStr->setText(L""); } } updateStats(); }
/** * Updates all soldier stats when the soldier changes. */ void InventoryState::init() { State::init(); BattleUnit *unit = _battleGame->getSelectedUnit(); // no selected unit, close inventory if (unit == 0) { btnOkClick(0); return; } // skip to the first unit with inventory if (!unit->hasInventory()) { if (_parent) { _parent->selectNextPlayerUnit(false, false, true); } else { _battleGame->selectNextPlayerUnit(false, false, true); } // no available unit, close inventory if (_battleGame->getSelectedUnit() == 0 || !_battleGame->getSelectedUnit()->hasInventory()) { // starting a mission with just vehicles btnOkClick(0); return; } else { unit = _battleGame->getSelectedUnit(); } } if (_parent) _parent->getMap()->getCamera()->centerOnPosition(unit->getPosition(), false); unit->setCache(0); _soldier->clear(); _btnRank->clear(); _txtName->setBig(); _txtName->setText(unit->getName(_game->getLanguage())); _inv->setSelectedUnit(unit); Soldier *s = _game->getSavedGame()->getSoldier(unit->getId()); if (s) { SurfaceSet *texture = _game->getResourcePack()->getSurfaceSet("BASEBITS.PCK"); texture->getFrame(s->getRankSprite())->setX(0); texture->getFrame(s->getRankSprite())->setY(0); texture->getFrame(s->getRankSprite())->blit(_btnRank); std::string look = s->getArmor()->getSpriteInventory(); if (s->getGender() == GENDER_MALE) look += "M"; else look += "F"; if (s->getLook() == LOOK_BLONDE) look += "0"; if (s->getLook() == LOOK_BROWNHAIR) look += "1"; if (s->getLook() == LOOK_ORIENTAL) look += "2"; if (s->getLook() == LOOK_AFRICAN) look += "3"; look += ".SPK"; if (!CrossPlatform::fileExists(CrossPlatform::getDataFile("UFOGRAPH/" + look)) && !_game->getResourcePack()->getSurface(look)) { look = s->getArmor()->getSpriteInventory() + ".SPK"; } _game->getResourcePack()->getSurface(look)->blit(_soldier); } else { Surface *armorSurface = _game->getResourcePack()->getSurface(unit->getArmor()->getSpriteInventory()); if (armorSurface) { armorSurface->blit(_soldier); } } updateStats(); }
/** * calculateTrajectory. * @return the objectnumber(0-3) or unit(4) or out of map (5) or -1(no line of fire) */ int Projectile::calculateTrajectory(double accuracy) { Position originVoxel, targetVoxel; Tile *targetTile = 0; int direction; int dirYshift[24] = {1, 3, 9, 15, 15, 13, 7, 1, 1, 1, 7, 13, 15, 15, 9, 3, 1, 2, 8, 14, 15, 14, 8, 2}; int dirXshift[24] = {9, 15, 15, 13, 8, 1, 1, 3, 7, 13, 15, 15, 9, 3, 1, 1, 8, 14, 15, 14, 8, 2, 1, 2}; int offset = 0; originVoxel = Position(_origin.x*16, _origin.y*16, _origin.z*24); BattleUnit *bu = _action.actor; if (bu->getArmor()->getSize() > 1) { offset = 16; } else if(_action.weapon == _action.weapon->getOwner()->getItem("STR_LEFT_HAND") && !_action.weapon->getRules()->isTwoHanded()) { offset = 8; } // take into account soldier height and terrain level if the projectile is launched from a soldier if (_action.actor->getPosition() == _origin) { // calculate offset of the starting point of the projectile originVoxel.z += -_save->getTile(_origin)->getTerrainLevel(); originVoxel.z += bu->getHeight() + bu->getFloatHeight(); originVoxel.z -= 4; if (originVoxel.z >= (_origin.z + 1)*24) { _origin.z++; } direction = bu->getDirection(); if (bu->getTurretType() != -1) direction = bu->getTurretDirection(); originVoxel.x += dirXshift[direction+offset]*bu->getArmor()->getSize(); originVoxel.y += dirYshift[direction+offset]*bu->getArmor()->getSize(); } else { // don't take into account soldier height and terrain level if the projectile is not launched from a soldier(from a waypoint) originVoxel.x += 8; originVoxel.y += 8; originVoxel.z += 12; } if (_action.type == BA_LAUNCH || (SDL_GetModState() & KMOD_CTRL) != 0) { // target nothing, targets the middle of the tile targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24 + 12); } else { // determine the target voxel. // aim at the center of the unit, the object, the walls or the floor (in that priority) // if there is no LOF to the center, try elsewhere (more outward). // Store this target voxel. targetTile = _save->getTile(_action.target); Position hitPos; int test = -1; if (targetTile->getUnit() != 0) { if (_origin == _action.target || targetTile->getUnit() == _action.actor) { // don't shoot at yourself but shoot at the floor targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24); } else { _save->getTileEngine()->canTargetUnit(&originVoxel, targetTile, &targetVoxel, bu); } } else if (targetTile->getMapData(MapData::O_OBJECT) != 0) { if (!_save->getTileEngine()->canTargetTile(&originVoxel, targetTile, MapData::O_OBJECT, &targetVoxel, bu)) { targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24 + 10); } } else if (targetTile->getMapData(MapData::O_NORTHWALL) != 0) { if (!_save->getTileEngine()->canTargetTile(&originVoxel, targetTile, MapData::O_NORTHWALL, &targetVoxel, bu)) { targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16, _action.target.z*24 + 9); } } else if (targetTile->getMapData(MapData::O_WESTWALL) != 0) { if (!_save->getTileEngine()->canTargetTile(&originVoxel, targetTile, MapData::O_WESTWALL, &targetVoxel, bu)) { targetVoxel = Position(_action.target.x*16, _action.target.y*16 + 8, _action.target.z*24 + 9); } } else if (targetTile->getMapData(MapData::O_FLOOR) != 0) { if (!_save->getTileEngine()->canTargetTile(&originVoxel, targetTile, MapData::O_FLOOR, &targetVoxel, bu)) { targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24); } } else { // target nothing, targets the middle of the tile targetVoxel = Position(_action.target.x*16 + 8, _action.target.y*16 + 8, _action.target.z*24 + 10); } test = _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, false, &_trajectory, bu); if (test == 4 && !_trajectory.empty()) { hitPos = Position(_trajectory.at(0).x/16, _trajectory.at(0).y/16, _trajectory.at(0).z/24); if (_save->getTile(hitPos) && _save->getTile(hitPos)->getUnit() == 0) //no unit? must be lower { hitPos = Position(hitPos.x, hitPos.y, hitPos.z-1); } } if (test != -1 && !_trajectory.empty() && _action.actor->getFaction() == FACTION_PLAYER && _action.autoShotCounter == 1) { //skip already estimated hitPos if (test != 4) { hitPos = Position(_trajectory.at(0).x/16, _trajectory.at(0).y/16, _trajectory.at(0).z/24); } if (hitPos != _action.target && _action.result == "") { if (test == 2) { if (hitPos.y - 1 == _action.target.y) { _trajectory.clear(); return _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, true, &_trajectory, bu); } } if (test == 1) { if (hitPos.x - 1 == _action.target.x) { _trajectory.clear(); return _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, true, &_trajectory, bu); } } _trajectory.clear(); return -1; } } _trajectory.clear(); } // apply some accuracy modifiers (todo: calculate this) // This will results in a new target voxel if (_action.type != BA_LAUNCH) applyAccuracy(originVoxel, &targetVoxel, accuracy, false, targetTile); // finally do a line calculation and store this trajectory. return _save->getTileEngine()->calculateLine(originVoxel, targetVoxel, true, &_trajectory, bu); }
/** * Runs state functionality every cycle. * Progresses the fall, updates the battlescape, ... */ void UnitFallBState::think() { for (std::list<BattleUnit*>::iterator unit = _parent->getSave()->getFallingUnits()->begin(); unit != _parent->getSave()->getFallingUnits()->end();) { if ((*unit)->getStatus() == STATUS_TURNING) { (*unit)->abortTurn(); } bool largeCheck = true; bool falling = true; int size = (*unit)->getArmor()->getSize() - 1; if ((*unit)->getHealth() == 0 || (*unit)->getStunlevel() >= (*unit)->getHealth()) { unit = _parent->getSave()->getFallingUnits()->erase(unit); continue; } bool onScreen = ((*unit)->getVisible() && _parent->getMap()->getCamera()->isOnScreen((*unit)->getPosition(), true, size, false)); Tile *tileBelow = _parent->getSave()->getTile((*unit)->getPosition() + Position(0,0,-1)); 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; } } } if ((*unit)->getStatus() == STATUS_WALKING || (*unit)->getStatus() == STATUS_FLYING) { (*unit)->keepWalking(tileBelow, true); // advances the phase _parent->getMap()->cacheUnit(*unit); // make sure the unit sprites are up to date if ((*unit)->getPosition() != (*unit)->getLastPosition()) { // Reset tiles moved from. for (int x = size; x >= 0; x--) { for (int y = size; y >= 0; y--) { // A falling unit might have already taken up this position so check that this unit is still here. if (_parent->getSave()->getTile((*unit)->getLastPosition() + Position(x,y,0))->getUnit() == (*unit)) { _parent->getSave()->getTile((*unit)->getLastPosition() + Position(x,y,0))->setUnit(0); } } } // Update tiles moved to. 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))); } } } ++unit; continue; } falling = largeCheck && (*unit)->getPosition().z != 0 && (*unit)->getTile()->hasNoFloor(tileBelow) && (*unit)->getMovementType() != MT_FLY && (*unit)->getWalkingPhase() == 0; if (falling) { // Tile(s) unit is falling into. for (int x = (*unit)->getArmor()->getSize() - 1; x >= 0; --x) { for (int y = (*unit)->getArmor()->getSize() - 1; y >= 0; --y) { Tile *tileTarget = _parent->getSave()->getTile((*unit)->getPosition() + Position(x,y,-1)); tilesToFallInto.push_back(tileTarget); } } // Check each tile for units that need moving out of the way. for (std::vector<Tile*>::iterator i = tilesToFallInto.begin(); i < tilesToFallInto.end(); ++i) { BattleUnit *unitBelow = (*i)->getUnit(); if (unitBelow && (*unit) != unitBelow // falling units do not fall on themselves && !(std::find(unitsToMove.begin(), unitsToMove.end(), unitBelow) != unitsToMove.end())) // already added { unitsToMove.push_back(unitBelow); } } } falling = largeCheck && (*unit)->getPosition().z != 0 && (*unit)->getTile()->hasNoFloor(tileBelow) && (*unit)->getMovementType() != MT_FLY && (*unit)->getWalkingPhase() == 0; // Find somewhere to move the unit(s) endanger of being squashed. if (!unitsToMove.empty()) { std::vector<Tile*> escapeTiles; for (std::vector<BattleUnit*>::iterator ub = unitsToMove.begin(); ub < unitsToMove.end(); ) { BattleUnit *unitBelow = (*ub); bool escapeFound = false; // We need to move all sections of the unit out of the way. std::vector<Position> bodySections; for (int x = unitBelow->getArmor()->getSize() - 1; x >= 0; --x) { for (int y = unitBelow->getArmor()->getSize() - 1; y >= 0; --y) { Position bs = unitBelow->getPosition() + Position(x, y, 0); bodySections.push_back(bs); } } // Check in each compass direction. for (int dir = 0; dir < Pathfinding::DIR_UP && !escapeFound; dir++) { Position offset; Pathfinding::directionToVector(dir, &offset); for (std::vector<Position>::iterator bs = bodySections.begin(); bs < bodySections.end(); ) { Position originalPosition = (*bs); Position endPosition = originalPosition + offset; Tile *t = _parent->getSave()->getTile(endPosition); Tile *bt = _parent->getSave()->getTile(endPosition + Position(0,0,-1)); bool aboutToBeOccupiedFromAbove = t && std::find(tilesToFallInto.begin(), tilesToFallInto.end(), t) != tilesToFallInto.end(); bool alreadyTaken = t && std::find(escapeTiles.begin(), escapeTiles.end(), t) != escapeTiles.end(); bool alreadyOccupied = t && t->getUnit() && (t->getUnit() != unitBelow); bool movementBlocked = _parent->getSave()->getPathfinding()->getTUCost(originalPosition, dir, &endPosition, *ub, 0, false) == 255; bool hasFloor = t && !t->hasNoFloor(bt); bool unitCanFly = unitBelow->getMovementType() == MT_FLY; bool canMoveToTile = t && !alreadyOccupied && !alreadyTaken && !aboutToBeOccupiedFromAbove && !movementBlocked && (hasFloor || unitCanFly); if (canMoveToTile) { // Check next section of the unit. ++bs; } else { // Try next direction. break; } // If all sections of the fallen onto unit can be moved, then we move it. if (bs == bodySections.end()) { if (_parent->getSave()->addFallingUnit(unitBelow)) { escapeFound = true; // Now ensure no other unit escapes to here too. for (int x = unitBelow->getArmor()->getSize() - 1; x >= 0; --x) { for (int y = unitBelow->getArmor()->getSize() - 1; y >= 0; --y) { Tile *et = _parent->getSave()->getTile(t->getPosition() + Position(x,y,0)); escapeTiles.push_back(et); } } Tile *bu = _parent->getSave()->getTile(originalPosition + Position(0,0,-1)); unitBelow->startWalking(dir, unitBelow->getPosition() + offset, bu, onScreen); ub = unitsToMove.erase(ub); } } } } if (!escapeFound) { unitBelow->knockOut(_parent); ub = unitsToMove.erase(ub); } } _parent->checkForCasualties(0,*unit); } // we are just standing around, we are done falling. if ((*unit)->getStatus() == STATUS_STANDING) { if (falling) { Position destination = (*unit)->getPosition() + Position(0,0,-1); Tile *tileDest = _parent->getSave()->getTile(destination); (*unit)->startWalking(Pathfinding::DIR_DOWN, destination, tileDest, onScreen); (*unit)->setCache(0); _parent->getMap()->cacheUnit(*unit); ++unit; } else { // if the unit burns floortiles, burn floortiles if ((*unit)->getSpecialAbility() == SPECAB_BURNFLOOR || (*unit)->getSpecialAbility() == SPECAB_BURN_AND_EXPLODE) { (*unit)->getTile()->ignite(1); Position groundVoxel = ((*unit)->getPosition() * Position(16,16,24)) + Position(8,8,-((*unit)->getTile()->getTerrainLevel())); _parent->getTileEngine()->hit(groundVoxel, (*unit)->getBaseStats()->strength, DT_IN, (*unit)); if ((*unit)->getStatus() != STATUS_STANDING) // ie: we burned a hole in the floor and fell through it { _parent->getPathfinding()->abortPath(); } } // move our personal lighting with us _terrain->calculateUnitLighting(); _parent->getMap()->cacheUnit(*unit); (*unit)->setCache(0); _terrain->calculateFOV(*unit); _parent->checkForProximityGrenades(*unit); if ((*unit)->getStatus() == STATUS_STANDING) { if (_parent->getTileEngine()->checkReactionFire(*unit)) _parent->getPathfinding()->abortPath(); unit = _parent->getSave()->getFallingUnits()->erase(unit); } } } else { ++unit; } } if (_parent->getSave()->getFallingUnits()->empty()) { tilesToFallInto.clear(); unitsToMove.clear(); _parent->popState(); return; } }
/** * Calculates the effects of the explosion. */ void ExplosionBState::explode() { bool terrainExplosion = false; SavedBattleGame *save = _parent->getSave(); // last minute adjustment: determine if we actually if (_hit) { if (_unit && !_unit->isOut()) { _unit->aim(false); } if (_power <= 0) { _parent->popState(); return; } int sound = _item->getRules()->getMeleeHitSound(); if (!_pistolWhip) { // melee weapon with ammo BattleItem *ammo = _item->getAmmoItem(); if (ammo) { optValue(sound, ammo->getRules()->getMeleeHitSound()); } } _parent->playSound(sound, _action.target); } // after the animation is done, the real explosion/hit takes place if (_item) { if (!_unit && _item->getPreviousOwner()) { _unit = _item->getPreviousOwner(); } } bool range = !(_hit || (_item && _item->getRules()->getBattleType() == BT_PSIAMP)); if (_areaOfEffect) { save->getTileEngine()->explode(_center, _power, _damageType, _radius, _unit, _item, range); } else { BattleUnit *victim = save->getTileEngine()->hit(_center, _power, _damageType, _unit, _item, range); // check if this unit turns others into zombies if (!_item->getRules()->getZombieUnit().empty() && RNG::percent(_item->getRules()->getSpecialChance()) && victim && victim->getArmor()->getZombiImmune() == false && victim->getSpawnUnit().empty() && victim->getOriginalFaction() != FACTION_HOSTILE) { // converts the victim to a zombie on death victim->setRespawn(true); victim->setSpawnUnit(_item->getRules()->getZombieUnit()); } } if (_tile) { terrainExplosion = true; } if (!_tile && !_item) { terrainExplosion = true; } // now check for new casualties _parent->checkForCasualties(_item ? _damageType : 0, _item, _unit, false, terrainExplosion); // revive units if damage could give hp or reduce stun _parent->getSave()->reviveUnconsciousUnits(true); // if this explosion was caused by a unit shooting, now it's the time to put the gun down if (_unit && !_unit->isOut() && _lowerWeapon) { _unit->aim(false); } if (_item && (_item->getRules()->getBattleType() == BT_GRENADE || _item->getRules()->getBattleType() == BT_PROXIMITYGRENADE)) { _parent->getSave()->removeItem(_item); } _parent->popState(); // check for terrain explosions Tile *t = save->getTileEngine()->checkForTerrainExplosions(); if (t) { Position p = t->getPosition().toVexel(); p += Position(8,8,0); _parent->statePushFront(new ExplosionBState(_parent, p, BA_NONE, 0, _unit, t)); } }
/** * Calculates the effects of the explosion. */ void ExplosionBState::explode() { bool terrainExplosion = false; SavedBattleGame *save = _parent->getSave(); // last minute adjustment: determine if we actually if (_hit) { save->getBattleGame()->getCurrentAction()->type = BA_NONE; BattleUnit *targetUnit = save->getTile(_center / Position(16, 16, 24))->getUnit(); if (_unit && !_unit->isOut()) { _unit->aim(false); _unit->setCache(0); } if (!RNG::percent(_unit->getFiringAccuracy(BA_HIT, _item))) { _parent->getMap()->cacheUnits(); _parent->popState(); return; } else if (targetUnit && targetUnit->getOriginalFaction() == FACTION_HOSTILE && _unit->getOriginalFaction() == FACTION_PLAYER) { _unit->addMeleeExp(); } if (_item->getRules()->getMeleeHitSound() != -1) { _parent->getResourcePack()->getSoundByDepth(_parent->getDepth(), _item->getRules()->getMeleeHitSound())->play(); } } // after the animation is done, the real explosion/hit takes place if (_item) { if (!_unit && _item->getPreviousOwner()) { _unit = _item->getPreviousOwner(); } if (_areaOfEffect) { save->getTileEngine()->explode(_center, _power, _item->getRules()->getDamageType(), _item->getRules()->getExplosionRadius(), _unit); } else { ItemDamageType type = _item->getRules()->getDamageType(); if (_pistolWhip) { type = DT_STUN; } BattleUnit *victim = save->getTileEngine()->hit(_center, _power, type, _unit); // check if this unit turns others into zombies if (!_item->getRules()->getZombieUnit().empty() && victim && victim->getArmor()->getSize() == 1 && victim->getSpawnUnit().empty() && victim->getOriginalFaction() != FACTION_HOSTILE) { // converts the victim to a zombie on death victim->setSpecialAbility(SPECAB_RESPAWN); victim->setSpawnUnit(_item->getRules()->getZombieUnit()); } } } if (_tile) { save->getTileEngine()->explode(_center, _power, DT_HE, _power/10); terrainExplosion = true; } if (!_tile && !_item) { int radius = 6; // explosion not caused by terrain or an item, must be by a unit (cyberdisc) if (_unit && _unit->getSpecialAbility() == SPECAB_EXPLODEONDEATH) { radius = _parent->getRuleset()->getItem(_unit->getArmor()->getCorpseGeoscape())->getExplosionRadius(); } save->getTileEngine()->explode(_center, _power, DT_HE, radius); terrainExplosion = true; } // now check for new casualties _parent->checkForCasualties(_item, _unit, false, terrainExplosion); // if this explosion was caused by a unit shooting, now it's the time to put the gun down if (_unit && !_unit->isOut() && _lowerWeapon) { _unit->aim(false); _unit->setCache(0); } _parent->getMap()->cacheUnits(); _parent->popState(); // check for terrain explosions Tile *t = save->getTileEngine()->checkForTerrainExplosions(); if (t) { Position p = Position(t->getPosition().x * 16, t->getPosition().y * 16, t->getPosition().z * 24); p += Position(8,8,0); _parent->statePushFront(new ExplosionBState(_parent, p, 0, _unit, t)); } if (_item && (_item->getRules()->getBattleType() == BT_GRENADE || _item->getRules()->getBattleType() == BT_PROXIMITYGRENADE)) { for (std::vector<BattleItem*>::iterator j = _parent->getSave()->getItems()->begin(); j != _parent->getSave()->getItems()->end(); ++j) { if (_item->getId() == (*j)->getId()) { delete *j; _parent->getSave()->getItems()->erase(j); break; } } } }
/** * Calculates the effects of the explosion. */ void ExplosionBState::explode() { bool terrainExplosion = false; SavedBattleGame *save = _parent->getSave(); // last minute adjustment: determine if we actually if (_hit) { if (_attack.attacker && !_attack.attacker->isOut()) { _attack.attacker->aim(false); } if (_power <= 0) { _parent->popState(); return; } int sound = _attack.weapon_item->getRules()->getMeleeHitSound(); if (_attack.weapon_item != _attack.damage_item) { // melee weapon with ammo optValue(sound, _attack.damage_item->getRules()->getMeleeHitSound()); } _parent->playSound(sound, _center.toTile()); } bool range = !(_hit || (_attack.weapon_item && _attack.weapon_item->getRules()->getBattleType() == BT_PSIAMP)); // after the animation is done, the real explosion/hit takes place /*if (_item) { if (!_unit && _item->getPreviousOwner()) { _unit = _item->getPreviousOwner(); }*/ if (_areaOfEffect) { save->getTileEngine()->explode(_attack, _center, _power, _damageType, _radius, range); } else //if (!_cosmetic) { if (_damageType->ResistType == DT_TELEPORT) { Tile *destination = _tile ? _tile : save->getTile(Position(_center.x / 16, _center.y / 16, _center.z / 24)); if (_attack.attacker && destination && !destination->getUnit()) { save->getTile(_attack.attacker->getPosition())->setUnit(0); const Position& oldPosition = _attack.attacker->getPosition(); _attack.attacker->setPosition(destination->getPosition()); destination->setUnit(_attack.attacker); save->getTileEngine()->calculateFOV(oldPosition); save->getTileEngine()->calculateFOV(destination->getPosition()); } } else { BattleUnit *victim = save->getTileEngine()->hit(_attack, _center, _power, _action.type, _damageType, range); const RuleItem *hitItem = _attack.damage_item->getRules(); // check if this unit turns others into zombies if (!hitItem->getZombieUnit().empty() && RNG::percent(hitItem->getSpecialChance()) && victim && victim->getArmor()->getZombiImmune() == false && victim->getSpawnUnit().empty() && victim->getOriginalFaction() != FACTION_HOSTILE) { // converts the victim to a zombie on death victim->setRespawn(true); victim->setSpawnUnit(hitItem->getZombieUnit()); } } } //} if (_tile) { /*ItemDamageType DT; switch (_tile->getExplosiveType()) { case 0: DT = DT_HE; break; case 5: DT = DT_IN; break; case 6: DT = DT_STUN; break; default: DT = DT_SMOKE; break; } if (DT != DT_HE) { _tile->setExplosive(0, 0, true); } save->getTileEngine()->explode(_center, _power, _parent->getCurrentAction()->type, DT, _power / 10, 0);*/ terrainExplosion = true; } if (!_tile && !_attack.damage_item) { /*int radius = 6; // explosion not caused by terrain or an item, must be by a unit (cyberdisc) if (_unit && (_unit->getSpecialAbility() == SPECAB_EXPLODEONDEATH || _unit->getSpecialAbility() == SPECAB_BURN_AND_EXPLODE)) { radius = _parent->getMod()->getItem(_unit->getArmor()->getCorpseGeoscape(), true)->getExplosionRadius(); } save->getTileEngine()->explode(_center, _power, _parent->getCurrentAction()->type, DT_HE, radius, 0);*/ terrainExplosion = true; } // now check for new casualties _parent->checkForCasualties(_damageType, _attack, false, terrainExplosion, _subState); // revive units if damage could give hp or reduce stun _parent->getSave()->reviveUnconsciousUnits(true); // if this explosion was caused by a unit shooting, now it's the time to put the gun down if (_attack.attacker && !_attack.attacker->isOut() && _lowerWeapon) { _attack.attacker->aim(false); } if (_attack.damage_item && (_attack.damage_item->getRules()->getBattleType() == BT_GRENADE || _attack.damage_item->getRules()->getBattleType() == BT_PROXIMITYGRENADE)) { _parent->getSave()->removeItem(_attack.damage_item); } //_finished = true; //if(!_subState) _parent->popState(); // check for terrain explosions Tile *t = save->getTileEngine()->checkForTerrainExplosions(); if (t) { Position p = t->getPosition().toVexel(); p += Position(8, 8, 0); { //_parent->statePushFront(new ExplosionBState(_parent, p, BattleActionAttack{ BA_NONE, _attack.attacker, }, t)); if (!_subState) { _parent->statePushFront(new ExplosionBState(_parent, p, BattleActionAttack{ BA_NONE, _attack.attacker, }, t)); } else { _terrainExplosion = new ExplosionBState(_parent, p, BattleActionAttack{ BA_NONE, _attack.attacker, }, t, false, false, true); } } } }