/** ** Unit stands still or stand ground. ** ** @param unit Unit pointer for action. ** @param stand_ground true if unit is standing ground. */ void ActionStillGeneric(CUnit *unit, bool stand_ground) { // If unit is not bunkered and removed, wait if (unit->Removed && (!unit->Container || !unit->Container->Type->CanTransport || !unit->Container->Type->AttackFromTransporter || unit->Type->Missile.Missile->Class == MissileClassNone)) { // If unit is in building or transporter it is removed. return; } // Animations if (unit->SubAction) { // attacking unit in attack range. AnimateActionAttack(unit); } else { UnitShowAnimation(unit, unit->Type->Animations->Still); } if (unit->Anim.Unbreakable) { // animation can't be aborted here return; } if (AutoCast(unit) || AutoRepair(unit)) { return; } AutoAttack(unit, stand_ground); }
/* virtual */ void COrder_Still::Execute(CUnit &unit) { // If unit is not bunkered and removed, wait if (unit.Removed //Wyrmgus start // && (unit.Container == nullptr || unit.Container->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value == false)) { && (unit.Container == nullptr || !unit.Container->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value || !unit.Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value)) { // make both the unit and the transporter have the tag be necessary for the attack to be possible if (unit.Container != nullptr) { LeaveShelter(unit); // leave shelter if surrounded } //Wyrmgus end return ; } this->Finished = false; switch (this->State) { case SUB_STILL_STANDBY: //Wyrmgus start // UnitShowAnimation(unit, unit.Type->Animations->Still); if (unit.Variable[STUN_INDEX].Value == 0) { //only show the idle animation when still if the unit is not stunned UnitShowAnimation(unit, unit.GetAnimations()->Still); } if (SyncRand(100000) == 0) { PlayUnitSound(unit, VoiceIdle); } unit.StepCount = 0; //Wyrmgus end break; case SUB_STILL_ATTACK: // attacking unit in attack range. AnimateActionAttack(unit, *this); break; } if (unit.Anim.Unbreakable) { // animation can't be aborted here return; } //Wyrmgus start if (unit.Variable[STUN_INDEX].Value > 0) { //if unit is stunned, remain still return; } //Wyrmgus end this->State = SUB_STILL_STANDBY; this->Finished = (this->Action == UnitActionStill); if (this->Action == UnitActionStandGround || unit.Removed || unit.CanMove() == false) { if (unit.AutoCastSpell) { this->AutoCastStand(unit); } if (unit.IsAgressive()) { this->AutoAttackStand(unit); } } else { if (AutoCast(unit) || (unit.IsAgressive() && AutoAttack(unit)) || AutoRepair(unit) //Wyrmgus start // || MoveRandomly(unit)) { || MoveRandomly(unit) || PickUpItem(unit)) { //Wyrmgus end } } }
/* virtual */ void COrder_Still::Execute(CUnit &unit) { // If unit is not bunkered and removed, wait if (unit.Removed && (unit.Container == NULL || unit.Container->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value == false)) { return ; } this->Finished = false; switch (this->State) { case SUB_STILL_STANDBY: UnitShowAnimation(unit, unit.Type->Animations->Still); break; case SUB_STILL_ATTACK: // attacking unit in attack range. AnimateActionAttack(unit, *this); break; } if (unit.Anim.Unbreakable) { // animation can't be aborted here return; } this->State = SUB_STILL_STANDBY; this->Finished = (this->Action == UnitActionStill); if (this->Action == UnitActionStandGround || unit.Removed || unit.CanMove() == false) { if (unit.AutoCastSpell) { this->AutoCastStand(unit); } if (unit.IsAgressive()) { this->AutoAttackStand(unit); } } else { if (AutoCast(unit) || (unit.IsAgressive() && AutoAttack(unit)) || AutoRepair(unit) || MoveRandomly(unit)) { } } }
/** ** Handle attacking the target. ** ** @param unit Unit, for that the attack is handled. */ void COrder_Attack::AttackTarget(CUnit &unit) { Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos)); AnimateActionAttack(unit, *this); if (unit.Anim.Unbreakable) { return; } if (!this->HasGoal() && (this->Action == UnitActionAttackGround || Map.WallOnMap(this->goalPos))) { return; } // Target is dead ? Change order ? if (CheckForDeadGoal(unit)) { return; } CUnit *goal = this->GetGoal(); bool dead = !goal || goal->IsAlive() == false; // No target choose one. if (!goal) { goal = AttackUnitsInReactRange(unit); // No new goal, continue way to destination. if (!goal) { // Return to old task ? if (unit.RestoreOrder()) { return; } this->State = MOVE_TO_TARGET; return; } // Save current command to come back. COrder *savedOrder = COrder::NewActionAttack(unit, this->goalPos); if (unit.CanStoreOrder(savedOrder) == false) { delete savedOrder; savedOrder = NULL; } else { unit.SavedOrder = savedOrder; } this->SetGoal(goal); this->goalPos = goal->tilePos; this->MinRange = unit.Type->MinAttackRange; this->Range = unit.Stats->Variables[ATTACKRANGE_INDEX].Max; this->State |= WEAK_TARGET; // Have a weak target, try a better target. // FIXME: if out of range also try another target quick } else { if ((this->State & WEAK_TARGET)) { CUnit *newTarget = AttackUnitsInReactRange(unit); if (newTarget && ThreatCalculate(unit, *newTarget) < ThreatCalculate(unit, *goal)) { if (unit.CanStoreOrder(this)) { unit.SavedOrder = this->Clone(); } goal = newTarget; this->SetGoal(newTarget); this->goalPos = newTarget->tilePos; this->MinRange = unit.Type->MinAttackRange; this->State = MOVE_TO_TARGET; } } } // Still near to target, if not goto target. const int dist = unit.MapDistanceTo(*goal); if (dist > unit.Stats->Variables[ATTACKRANGE_INDEX].Max) { // towers don't chase after goal if (unit.CanMove()) { if (unit.CanStoreOrder(this)) { if (dead) { unit.SavedOrder = COrder::NewActionAttack(unit, this->goalPos); } else { unit.SavedOrder = this->Clone(); } } } unit.Frame = 0; this->State &= WEAK_TARGET; this->State |= MOVE_TO_TARGET; } if (dist < unit.Type->MinAttackRange) { this->State = MOVE_TO_TARGET; } // Turn always to target if (goal) { const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); } }