/** * Animates the projectile (move to the next point in it's trajectory). * If the animation is finished the projectile sprite is removed from the map. * And this state is finished. */ void ProjectileFlyBState::think() { /* TODO refactoring : store the projectile in this state, instead of getting it from the map each time? */ if (_parent->getMap()->getProjectile() == 0) { if (_action.type == BA_AUTOSHOT && _action.autoShotCounter < 3 && !_action.actor->isOut() && _ammo->getAmmoQuantity() != 0) { createNewProjectile(); } else { if (_action.cameraPosition.z != -1) { _parent->getMap()->getCamera()->setMapOffset(_action.cameraPosition); } if (_action.type != BA_PANIC && _action.type != BA_MINDCONTROL) { _parent->getTileEngine()->checkReactionFire(_unit); } _unit->abortTurn(); _parent->popState(); } } else { if(!_parent->getMap()->getProjectile()->move()) { // impact ! if (_action.type == BA_THROW) { Position pos = _parent->getMap()->getProjectile()->getPosition(-1); pos.x /= 16; pos.y /= 16; pos.z /= 24; BattleItem *item = _parent->getMap()->getProjectile()->getItem(); _parent->getResourcePack()->getSound("BATTLE.CAT", 38)->play(); if (Options::getBool("battleInstantGrenade") && item->getRules()->getBattleType() == BT_GRENADE && item->getExplodeTurn() != 0 && item->getExplodeTurn() <= _parent->getSave()->getTurn()) { // it's a hot grenade to explode immediately _parent->statePushFront(new ExplosionBState(_parent, _parent->getMap()->getProjectile()->getPosition(-1), item, _action.actor)); } else { _parent->dropItem(pos, item); } } else if (_action.type == BA_LAUNCH && _action.waypoints.size() > 1 && _projectileImpact == -1) { _origin = _action.waypoints.front(); _action.waypoints.pop_front(); _action.target = _action.waypoints.front(); // launch the next projectile in the waypoint cascade _parent->statePushBack(new ProjectileFlyBState(_parent, _action, _origin)); } else { if (_ammo && _action.type == BA_LAUNCH && _ammo->spendBullet() == false) { _parent->getSave()->removeItem(_ammo); _action.weapon->setAmmoItem(0); } if (_projectileImpact != 5) // out of map { int offset = 0; // explosions impact not inside the voxel but two steps back (projectiles generally move 2 voxels at a time) if (_ammo && ( _ammo->getRules()->getDamageType() == DT_HE || _ammo->getRules()->getDamageType() == DT_IN)) { offset = -2; } _parent->statePushFront(new ExplosionBState(_parent, _parent->getMap()->getProjectile()->getPosition(offset), _ammo, _action.actor, 0, (_action.type != BA_AUTOSHOT || _action.autoShotCounter == 3|| !_action.weapon->getAmmoItem()))); if (_projectileImpact == 4) { BattleUnit *victim = _parent->getSave()->getTile(_parent->getMap()->getProjectile()->getPosition(offset) / Position(16,16,24))->getUnit(); if (victim && !victim->isOut() && victim->getFaction() == FACTION_HOSTILE) { AggroBAIState *aggro = dynamic_cast<AggroBAIState*>(victim->getCurrentAIState()); if (aggro == 0) { aggro = new AggroBAIState(_parent->getSave(), victim); victim->setAIState(aggro); } aggro->setAggroTarget(_action.actor); } } } else if (_action.type != BA_AUTOSHOT || _action.autoShotCounter == 3 || !_action.weapon->getAmmoItem()) { _unit->aim(false); _parent->getMap()->cacheUnits(); } } delete _parent->getMap()->getProjectile(); _parent->getMap()->setProjectile(0); } } }
/** * Processes any clicks on the map to * command units. * @param action Pointer to an action. */ void BattlescapeState::mapClick(Action *action) { // right-click aborts walking state if (action->getDetails()->button.button == SDL_BUTTON_RIGHT) { if (_popup) { hidePopup(); return; } if (_states.empty()) { if (_targeting) { _targeting = false; _map->setCursorType(CT_NORMAL); _game->getCursor()->setVisible(true); _selectedAction = BA_NONE; return; } } else { _states.front()->cancel(); return; } } // don't handle mouseclicks below 140, because they are in the buttons area (it overlaps with map surface) if (action->getYMouse() / action->getYScale() > BUTTONS_AREA) return; // don't accept leftclicks if there is no cursor or there is an action busy if (_map->getCursorType() == CT_NONE || !_states.empty()) return; Position pos; _map->getSelectorPosition(&pos); if (action->getDetails()->button.button == SDL_BUTTON_LEFT) { if (_targeting && _battleGame->getSelectedUnit()) { // -= fire weapon =- _target = pos; _map->setCursorType(CT_NONE); _game->getCursor()->setVisible(false); statePushBack(new UnitTurnBState(this)); statePushBack(new ProjectileFlyBState(this)); } else { BattleUnit *unit = _battleGame->selectUnit(pos); if (unit && !unit->isOut()) { // -= select unit =- if (unit->getFaction() == _battleGame->getSide()) { _battleGame->setSelectedUnit(unit); updateSoldierInfo(unit); } } else if (_battleGame->getSelectedUnit()) { // -= start walking =- _target = pos; _map->setCursorType(CT_NONE); _game->getCursor()->setVisible(false); statePushBack(new UnitWalkBState(this)); } } } else if (action->getDetails()->button.button == SDL_BUTTON_RIGHT && _battleGame->getSelectedUnit()) { // -= turn to or open door =- _target = pos; statePushBack(new UnitTurnBState(this)); } }