/* virtual */ void COrder_Move::Execute(CUnit &unit) { Assert(unit.CanMove()); if (unit.Wait) { if (!unit.Waiting) { unit.Waiting = 1; unit.WaitBackup = unit.Anim; } UnitShowAnimation(unit, unit.Type->Animations->Still); unit.Wait--; return; } if (unit.Waiting) { unit.Anim = unit.WaitBackup; unit.Waiting = 0; } // FIXME: (mr-russ) Make a reachable goal here with GoalReachable ... switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: // Some tries to reach the goal this->Range++; break; case PF_REACHED: this->Finished = true; break; default: break; } }
/** ** Handle moving to the target. ** ** @param unit Unit, for that the spell cast is handled. */ bool COrder_SpellCast::SpellMoveToTarget(CUnit &unit) { // Unit can't move int err = 1; if (unit.CanMove()) { err = DoActionMove(unit); if (unit.Anim.Unbreakable) { return false; } } // when reached DoActionMove changes unit action // FIXME: use return codes from pathfinder CUnit *goal = this->GetGoal(); if (goal && unit.MapDistanceTo(*goal) <= this->Range) { // there is goal and it is in range UnitHeadingFromDeltaXY(unit, goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos); this->State++; // cast the spell return false; } else if (!goal && unit.MapDistanceTo(this->goalPos) <= this->Range) { // there is no goal and target spot is in range UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos); this->State++; // cast the spell return false; } else if (err == PF_UNREACHABLE || !unit.CanMove()) { // goal/spot unreachable and out of range -- give up return true; } return false; }
/** ** Move to build location ** ** @param unit Unit to move */ bool COrder_Build::MoveToLocation(CUnit &unit) { // First entry if (this->State == 0) { unit.pathFinderData->output.Cycles = 0; //moving counter this->State = 1; } switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: { // Some tries to reach the goal if (this->State++ < 10) { // To keep the load low, retry each 1/4 second. // NOTE: we can already inform the AI about this problem? unit.Wait = CYCLES_PER_SECOND / 4; return false; } unit.Player->Notify(NotifyYellow, unit.tilePos, "%s", _("You cannot reach building place")); if (unit.Player->AiEnabled) { AiCanNotReach(unit, this->GetUnitType()); } return true; } case PF_REACHED: this->State = State_NearOfLocation; return false; default: // Moving... return false; } }
/** ** Move to transporter. ** ** @param unit Pointer to unit, moving to transporter. ** ** @return >0 remaining path length, 0 wait for path, -1 ** reached goal, -2 can't reach the goal. */ local int MoveToTransporter(Unit* unit) { int i; i=DoActionMove(unit); // New code has this as default. DebugCheck( unit->Orders[0].Action!=UnitActionBoard ); return i; }
/** ** Move to transporter. ** ** @param unit Pointer to unit, moving to transporter. ** ** @return >0 remaining path length, 0 wait for path, -1 ** reached goal, -2 can't reach the goal. */ int COrder_Board::MoveToTransporter(CUnit &unit) { const Vec2i oldPos = unit.tilePos; const int res = DoActionMove(unit); // We have to reset a lot, or else they will circle each other and stuff. if (oldPos != unit.tilePos) { this->Range = 1; } return res; }
/** ** Move to dropzone. ** ** @param unit Pointer to unit. ** ** @return -1 if unreachable, True if reached, False otherwise. */ static int MoveToDropZone(CUnit &unit) { switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: return PF_UNREACHABLE; case PF_REACHED: break; default: return 0; } Assert(unit.CurrentAction() == UnitActionUnload); return 1; }
/** ** Move to dropzone. ** ** @param unit Pointer to unit. ** ** @return -1 if unreachable, True if reached, False otherwise. */ static int MoveToDropZone(CUnit *unit) { switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: return -1; case PF_REACHED: break; default: return 0; } Assert(unit->Orders[0]->Action == UnitActionUnload); return 1; }
/** ** Move to transporter. ** ** @param unit Pointer to unit, moving to transporter. ** ** @return >0 remaining path length, 0 wait for path, -1 ** reached goal, -2 can't reach the goal. */ static int MoveToTransporter(CUnit *unit) { int i; int x; int y; x = unit->X; y = unit->Y; i = DoActionMove(unit); // We have to reset a lot, or else they will circle each other and stuff. if (x != unit->X || y != unit->Y) { unit->Orders[0]->Range = 1; NewResetPath(unit); } // New code has this as default. Assert(unit->Orders[0]->Action == UnitActionBoard); return i; }
/** ** Move unit to unit resource. ** ** @return 1 if reached, -1 if unreacheable, 0 if on the way. */ int COrder_Resource::MoveToResource_Unit(CUnit &unit) { const CUnit *goal = this->GetGoal(); Assert(goal); switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: //Wyrmgus start //if is unreachable and is on a raft, see if the raft can move closer if ((Map.Field(unit.tilePos)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands); } return 0; } } } //Wyrmgus end return -1; case PF_REACHED: break; case PF_WAIT: if (unit.Player->AiEnabled) { this->Range++; if (this->Range >= 5) { this->Range = 0; AiCanNotMove(unit); } } default: // Goal gone or something. if (unit.Anim.Unbreakable || goal->IsVisibleAsGoal(*unit.Player)) { return 0; } break; } return 1; }
/** ** Move to coast. ** ** @param unit Pointer to unit. ** @return -1 if unreachable, True if reached, False otherwise. */ local int MoveToCoast(Unit* unit) { DebugLevel3Fn("%p\n" _C_ unit->Orders[0].Goal); switch( DoActionMove(unit) ) { // reached end-point? case PF_UNREACHABLE: DebugLevel2Fn("COAST NOT REACHED\n"); return -1; case PF_REACHED: break; default: return 0; } IfDebug( if( !CoastOnMap(unit->X,unit->Y) ) { DebugLevel2Fn("COAST NOT REACHED\n"); return -1; } )
/* virtual */ void COrder_Move::Execute(CUnit &unit) { Assert(unit.CanMove()); if (unit.Wait) { unit.Wait--; return ; } // FIXME: (mr-russ) Make a reachable goal here with GoalReachable ... switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: // Some tries to reach the goal this->Range++; break; case PF_REACHED: this->Finished = true; break; default: break; } }
/** ** Controls moving a unit to its target when attacking ** ** @param unit Unit that is attacking and moving */ void COrder_Attack::MoveToTarget(CUnit &unit) { Assert(!unit.Type->Vanishes && !unit.Destroyed && !unit.Removed); Assert(unit.CurrentOrder() == this); Assert(unit.CanMove()); Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos)); int err = DoActionMove(unit); if (unit.Anim.Unbreakable) { return; } // Look if we have reached the target. if (err == 0 && !this->HasGoal()) { // Check if we're in range when attacking a location and we are waiting if (unit.MapDistanceTo(this->goalPos) <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) { err = PF_REACHED; } } if (err >= 0) { if (CheckForTargetInRange(unit)) { return; } return; } if (err == PF_REACHED) { CUnit *goal = this->GetGoal(); // Have reached target? FIXME: could use the new return code? if (goal && unit.MapDistanceTo(*goal) <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) { // Reached another unit, now attacking it const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); this->State++; return; } // Attacking wall or ground. if (((goal && goal->Type && goal->Type->Wall) || (!goal && (Map.WallOnMap(this->goalPos) || this->Action == UnitActionAttackGround))) && unit.MapDistanceTo(this->goalPos) <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) { // Reached wall or ground, now attacking it UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos); this->State &= WEAK_TARGET; this->State |= ATTACK_TARGET; return; } } // Unreachable. if (err == PF_UNREACHABLE) { if (!this->HasGoal()) { // When attack-moving we have to allow a bigger range this->Range++; unit.Wait = 5; return; } else { this->ClearGoal(); } } // Return to old task? if (!unit.RestoreOrder()) { this->Finished = true; } }
/** ** Unit repairs ** ** @param unit Unit, for that the attack is handled. */ void HandleActionRepair(CUnit *unit) { CUnit *goal; int err; switch (unit->SubAction) { case 0: NewResetPath(unit); unit->SubAction = 1; // FALL THROUGH // // Move near to target. // case 1: // FIXME: RESET FIRST!! Why? We move first and than check if // something is in sight. err = DoActionMove(unit); if (!unit->Anim.Unbreakable) { // // No goal: if meeting damaged building repair it. // goal = unit->Orders[0]->Goal; // // Target is dead, choose new one. // // Check if goal is correct unit. if (goal) { if (!goal->IsVisibleAsGoal(unit->Player)) { DebugPrint("repair target gone.\n"); unit->Orders[0]->X = goal->X; unit->Orders[0]->Y = goal->Y; goal->RefsDecrease(); // FIXME: should I clear this here? unit->Orders[0]->Goal = goal = NULL; NewResetPath(unit); } } else if (unit->Player->AiEnabled) { // Ai players workers should stop if target is killed err = -1; } // // Have reached target? FIXME: could use return value // if (goal && MapDistanceBetweenUnits(unit, goal) <= unit->Type->RepairRange && goal->Variable[HP_INDEX].Value < goal->Variable[HP_INDEX].Max) { unit->State = 0; unit->SubAction = 2; unit->Data.Repair.Cycles = 0; UnitHeadingFromDeltaXY(unit, goal->X + (goal->Type->TileWidth - 1) / 2 - unit->X, goal->Y + (goal->Type->TileHeight - 1) / 2 - unit->Y); } else if (err < 0) { if (goal) { // release reference goal->RefsDecrease(); unit->Orders[0]->Goal = NoUnitP; } unit->Orders[0]->Action = UnitActionStill; unit->State = unit->SubAction = 0; if (unit->Selected) { // update display for new action SelectedUnitChanged(); } return; } // FIXME: Should be it already? Assert(unit->Orders[0]->Action == UnitActionRepair); } break; // // Repair the target. // case 2: AnimateActionRepair(unit); unit->Data.Repair.Cycles++; if (!unit->Anim.Unbreakable) { goal = unit->Orders[0]->Goal; // // Target is dead, choose new one. // // Check if goal is correct unit. // FIXME: should I do a function for this? if (goal) { if (!goal->IsVisibleAsGoal(unit->Player)) { DebugPrint("repair goal is gone\n"); unit->Orders[0]->X = goal->X; unit->Orders[0]->Y = goal->Y; goal->RefsDecrease(); // FIXME: should I clear this here? unit->Orders[0]->Goal = goal = NULL; NewResetPath(unit); } } if (goal && MapDistanceBetweenUnits(unit, goal) <= unit->Type->RepairRange) { RepairUnit(unit, goal); goal = unit->Orders[0]->Goal; } else if (goal && MapDistanceBetweenUnits(unit, goal) > unit->Type->RepairRange) { // If goal has move, chase after it unit->State = 0; unit->SubAction = 0; } // // Target is fine, choose new one. // if (!goal || goal->Variable[HP_INDEX].Value >= goal->Variable[HP_INDEX].Max) { if (goal) { // release reference goal->RefsDecrease(); unit->Orders[0]->Goal = NULL; } unit->Orders[0]->Action = UnitActionStill; unit->SubAction = unit->State = 0; if (unit->Selected) { // update display for new action SelectedUnitChanged(); } return; } // FIXME: automatic repair } break; } }
/* virtual */ void COrder_Defend::Execute(CUnit &unit) { if (unit.Wait) { if (!unit.Waiting) { unit.Waiting = 1; unit.WaitBackup = unit.Anim; } //Wyrmgus start // UnitShowAnimation(unit, unit.Type->Animations->Still); VariationInfo *varinfo = unit.Type->VarInfo[unit.Variation]; if (varinfo && varinfo->Animations && varinfo->Animations->Still) { UnitShowAnimation(unit, varinfo->Animations->Still); } else { UnitShowAnimation(unit, unit.Type->Animations->Still); } //Wyrmgus end unit.Wait--; return; } if (unit.Waiting) { unit.Anim = unit.WaitBackup; unit.Waiting = 0; } CUnit *goal = this->GetGoal(); if (this->State == State_Init) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { this->Finished = true; return; } this->State = State_MovingToTarget; } else if (this->State == State_Defending) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { this->Finished = true; return; } } if (!unit.Anim.Unbreakable) { if (AutoCast(unit) || AutoAttack(unit) || AutoRepair(unit)) { return; } } switch (DoActionMove(unit)) { case PF_UNREACHABLE: // Some tries to reach the goal this->Range++; break; case PF_REACHED: { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { // goal has died this->Finished = true; return; } // Now defend the goal this->goalPos = goal->tilePos; this->State = State_Defending; } default: break; } // Target destroyed? if (goal && !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize(); this->ClearGoal(); goal = NULL; if (this->State == State_Defending) { this->Finished = true; return; } } }
/** ** Unit Demolishs ** ** @param unit Unit, for that the demolish is handled. */ global void HandleActionDemolish(Unit* unit) { Unit* table[UnitMax]; int i; int n; int xmin, ymin, xmax, ymax; int ix, iy; Unit* goal; int err; DebugLevel3Fn("Demolish %d\n" _C_ UnitNumber(unit)); switch( unit->SubAction ) { // // Move near to target. // case 0: // first entry. NewResetPath(unit); unit->SubAction=1; // FALL THROUGH case 1: // FIXME: reset first!! why? (johns) err=DoActionMove(unit); if( unit->Reset ) { goal=unit->Orders[0].Goal; // // Target is dead, stop demolish. // FIXME: what should I do, go back or explode on place? // if( goal ) { if( goal->Destroyed ) { DebugLevel0Fn("Destroyed unit\n"); RefsDebugCheck( !goal->Refs ); if( !--goal->Refs ) { ReleaseUnit(goal); } // FIXME: perhaps I should choose an alternative unit->Orders[0].Goal=NoUnitP; unit->Orders[0].Action=UnitActionStill; unit->SubAction=0; return; } else if( goal->Removed || !goal->HP || goal->Orders[0].Action==UnitActionDie ) { RefsDebugCheck( !goal->Refs ); --goal->Refs; RefsDebugCheck( !goal->Refs ); unit->Orders[0].Goal=NoUnitP; // FIXME: perhaps I should choose an alternative unit->Orders[0].Action=UnitActionStill; unit->SubAction=0; return; } } // // Have reached target? FIXME: could use pathfinder result? // if( goal ) { if( MapDistanceToUnit(unit->X,unit->Y,goal)<=1 ) { unit->State=0; unit->SubAction=2; } } else if( MapDistance(unit->X,unit->Y ,unit->Orders[0].X,unit->Orders[0].Y)<=1 ) { unit->State=0; unit->SubAction=2; } else if( err==PF_UNREACHABLE ) { unit->Orders[0].Action=UnitActionStill; return; } DebugCheck( unit->Orders[0].Action!=UnitActionDemolish ); } break; // // Demolish the target. // case 2: goal=unit->Orders[0].Goal; if( goal ) { RefsDebugCheck( !goal->Refs ); --goal->Refs; RefsDebugCheck( !goal->Refs ); unit->Orders[0].Goal=NoUnitP; } xmin = unit->X - 2; ymin = unit->Y - 2; xmax = unit->X + 2; ymax = unit->Y + 2; if (xmin<0) xmin=0; if (xmax > TheMap.Width-1) xmax = TheMap.Width-1; if (ymin<0) ymin=0; if (ymax > TheMap.Height-1) ymax = TheMap.Height-1; // FIXME: Must play explosion sound // FIXME: Currently we take the X fields, the original only the O // XXXXX ..O.. // XXXXX .OOO. // XX.XX OO.OO // XXXXX .OOO. // XXXXX ..O.. // // // Effect of the explosion on units. // n=SelectUnits(xmin,ymin, xmax, ymax,table); for( i=0; i<n; ++i ) { if( table[i]->Type->UnitType!=UnitTypeFly && table[i]->HP && table[i] != unit ) { // Don't hit flying units! HitUnit(unit,table[i],DEMOLISH_DAMAGE); } } // // Terrain effect of the explosion // for( ix=xmin; ix<=xmax; ix++ ) { for( iy=ymin; iy<=ymax; iy++ ) { n=TheMap.Fields[ix+iy*TheMap.Width].Flags; if( n&MapFieldWall ) { MapRemoveWall(ix,iy); } else if( n&MapFieldRocks ) { MapRemoveRock(ix,iy); } else if( n&MapFieldForest ) { MapRemoveWood(ix,iy); } } } LetUnitDie(unit); #ifdef HIERARCHIC_PATHFINDER PfHierMapChangedCallback (xmin, ymin, xmax, ymax); #endif break; } }
/** ** Move to resource depot ** ** @param unit Pointer to unit. ** ** @return TRUE if reached, otherwise FALSE. */ int COrder_Resource::MoveToDepot(CUnit &unit) { const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource]; CUnit &goal = *this->GetGoal(); CPlayer &player = *unit.Player; Assert(&goal); switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: //Wyrmgus start //if is unreachable and is on a raft, see if the raft can move closer if ((Map.Field(unit.tilePos)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands); } return 0; } } } //Wyrmgus end return -1; case PF_REACHED: break; case PF_WAIT: if (unit.Player->AiEnabled) { this->Range++; if (this->Range >= 5) { this->Range = 0; AiCanNotMove(unit); } } default: if (unit.Anim.Unbreakable || goal.IsVisibleAsGoal(player)) { return 0; } break; } // // Target is dead, stop getting resources. // if (!goal.IsVisibleAsGoal(player)) { DebugPrint("%d: Worker %d report: Destroyed depot\n" _C_ player.Index _C_ UnitNumber(unit)); unit.CurrentOrder()->ClearGoal(); CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource); if (depot) { UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT); DebugPrint("%d: Worker %d report: Going to new deposit.\n" _C_ player.Index _C_ UnitNumber(unit)); } else { DebugPrint("%d: Worker %d report: Can't find a new resource deposit.\n" _C_ player.Index _C_ UnitNumber(unit)); // FIXME: perhaps we should choose an alternative this->Finished = true; } return 0; } // If resource depot is still under construction, wait! if (goal.CurrentAction() == UnitActionBuilt) { unit.Wait = 10; return 0; } this->ClearGoal(); unit.Wait = resinfo.WaitAtDepot; // Place unit inside the depot if (unit.Wait) { int selected = unit.Selected; unit.Remove(&goal); if (selected && !Preference.DeselectInMine) { unit.Removed = 0; SelectUnit(unit); SelectionChanged(); unit.Removed = 1; } unit.Anim.CurrAnim = NULL; } // Update resource. const int rindex = resinfo.FinalResource; //Wyrmgus start // player.ChangeResource(rindex, (unit.ResourcesHeld * player.Incomes[rindex]) / 100, true); // player.TotalResources[rindex] += (unit.ResourcesHeld * player.Incomes[rindex]) / 100; player.ChangeResource(rindex, (unit.ResourcesHeld * resinfo.FinalResourceConversionRate / 100 * player.Incomes[rindex]) / 100, true); player.TotalResources[rindex] += (unit.ResourcesHeld * resinfo.FinalResourceConversionRate / 100 * player.Incomes[rindex]) / 100; //Wyrmgus end unit.ResourcesHeld = 0; unit.CurrentResource = 0; if (unit.Wait) { //Wyrmgus start // unit.Wait /= std::max(1, unit.Player->SpeedResourcesReturn[resinfo.ResourceId] / SPEEDUP_FACTOR); unit.Wait /= std::max(1, (unit.Player->SpeedResourcesReturn[resinfo.ResourceId] + goal.Variable[TIMEEFFICIENCYBONUS_INDEX].Value) / SPEEDUP_FACTOR); //Wyrmgus end if (unit.Wait) { unit.Wait--; } } return 1; }
/** ** Unit repairs ** ** @param unit Unit, for that the attack is handled. */ void HandleActionRepair(CUnit &unit) { CUnit *goal; int err; switch (unit.SubAction) { case 0: NewResetPath(unit); unit.SubAction = 1; // FALL THROUGH // // Move near to target. // case 1: // FIXME: RESET FIRST!! Why? We move first and than check if // something is in sight. err = DoActionMove(unit); if (!unit.Anim.Unbreakable) { // // No goal: if meeting damaged building repair it. // goal = unit.CurrentOrder()->GetGoal(); // // Target is dead, choose new one. // // Check if goal is correct unit. if (goal) { if (!goal->IsVisibleAsGoal(unit.Player)) { DebugPrint("repair target gone.\n"); unit.CurrentOrder()->goalPos = goal->tilePos; // FIXME: should I clear this here? unit.CurrentOrder()->ClearGoal(); goal = NULL; NewResetPath(unit); } } else if (unit.Player->AiEnabled) { // Ai players workers should stop if target is killed err = -1; } // // Have reached target? FIXME: could use return value // if (goal && unit.MapDistanceTo(*goal) <= unit.Type->RepairRange && goal->Variable[HP_INDEX].Value < goal->Variable[HP_INDEX].Max) { unit.State = 0; unit.SubAction = 2; unit.Data.Repair.Cycles = 0; const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); } else if (err < 0) { unit.CurrentOrder()->ClearGoal(); if (!unit.RestoreOrder()) { unit.ClearAction(); unit.State = 0; } return; } // FIXME: Should be it already? Assert(unit.CurrentAction() == UnitActionRepair); } break; // // Repair the target. // case 2: AnimateActionRepair(unit); unit.Data.Repair.Cycles++; if (!unit.Anim.Unbreakable) { goal = unit.CurrentOrder()->GetGoal(); // // Target is dead, choose new one. // // Check if goal is correct unit. // FIXME: should I do a function for this? if (goal) { if (!goal->IsVisibleAsGoal(unit.Player)) { DebugPrint("repair goal is gone\n"); unit.CurrentOrder()->goalPos = goal->tilePos; // FIXME: should I clear this here? unit.CurrentOrder()->ClearGoal(); goal = NULL; NewResetPath(unit); } else { int dist = unit.MapDistanceTo(*goal); if (dist <= unit.Type->RepairRange) { RepairUnit(unit, *goal); goal = unit.CurrentOrder()->GetGoal(); } else if (dist > unit.Type->RepairRange) { // If goal has move, chase after it unit.State = 0; unit.SubAction = 0; } } } // // Target is fine, choose new one. // if (!goal || goal->Variable[HP_INDEX].Value >= goal->Variable[HP_INDEX].Max) { unit.CurrentOrder()->ClearGoal(); if (!unit.RestoreOrder()) { unit.ClearAction(); unit.State = 0; } return; } // FIXME: automatic repair } break; } }
/* virtual */ void COrder_Defend::Execute(CUnit &unit) { if (unit.Wait) { if (!unit.Waiting) { unit.Waiting = 1; unit.WaitBackup = unit.Anim; } //Wyrmgus start // UnitShowAnimation(unit, unit.Type->Animations->Still); UnitShowAnimation(unit, unit.GetAnimations()->Still); //Wyrmgus end unit.Wait--; return; } if (unit.Waiting) { unit.Anim = unit.WaitBackup; unit.Waiting = 0; } CUnit *goal = this->GetGoal(); if (this->State == State_Init) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { this->Finished = true; return; } this->State = State_MovingToTarget; } else if (this->State == State_Defending) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { this->Finished = true; return; } } if (!unit.Anim.Unbreakable) { if (AutoCast(unit) || AutoAttack(unit) || AutoRepair(unit)) { return; } } switch (DoActionMove(unit)) { case PF_UNREACHABLE: //Wyrmgus start //if is unreachable and is on a raft, see if the raft can move closer to the enemy if ((Map.Field(unit.tilePos)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands); } return; } } } //Wyrmgus end // Some tries to reach the goal this->Range++; break; case PF_REACHED: { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { // goal has died this->Finished = true; return; } // Now defend the goal this->goalPos = goal->tilePos; this->State = State_Defending; } default: break; } // Target destroyed? if (goal && !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize(); this->ClearGoal(); goal = NULL; if (this->State == State_Defending) { this->Finished = true; return; } } }
/** ** Unit Patrol: ** The unit patrols between two points. ** Any enemy unit in reaction range is attacked. ** @todo FIXME: ** Should do some tries to reach the end-points. ** Should support patrol between more points! ** Patrol between units. ** ** @param unit Patroling unit pointer. */ void HandleActionPatrol(CUnit *unit) { if (unit->Wait) { unit->Wait--; return; } if (!unit->SubAction) { // first entry. NewResetPath(unit); unit->SubAction = 1; } switch (DoActionMove(unit)) { case PF_FAILED: unit->SubAction = 1; break; case PF_UNREACHABLE: // Increase range and try again unit->SubAction = 1; if (unit->Orders[0]->Range <= Map.Info.MapWidth || unit->Orders[0]->Range <= Map.Info.MapHeight) { unit->Orders[0]->Range++; break; } // FALL THROUGH case PF_REACHED: unit->SubAction = 1; unit->Orders[0]->Range = 0; SwapPatrolPoints(unit); break; case PF_WAIT: // Wait for a while then give up unit->SubAction++; if (unit->SubAction == 5) { unit->SubAction = 1; unit->Orders[0]->Range = 0; SwapPatrolPoints(unit); } break; default: // moving unit->SubAction = 1; break; } if (!unit->Anim.Unbreakable) { // // Attack any enemy in reaction range. // If don't set the goal, the unit can then choose a // better goal if moving nearer to enemy. // if (unit->Type->CanAttack) { const CUnit *goal = AttackUnitsInReactRange(unit); if (goal) { DebugPrint("Patrol attack %d\n" _C_ UnitNumber(goal)); CommandAttack(unit, goal->X, goal->Y, NULL, FlushCommands); // Save current command to come back. unit->SavedOrder = *unit->Orders[0]; unit->Orders[0]->Action = UnitActionStill; unit->Orders[0]->Goal = NoUnitP; unit->SubAction = 0; } } } }
/** ** Move unit to terrain. ** ** @return 1 if reached, -1 if unreacheable, 0 if on the way. */ int COrder_Resource::MoveToResource_Terrain(CUnit &unit) { Vec2i pos = this->goalPos; // Wood gone, look somewhere else. if ((Map.Info.IsPointOnMap(pos) == false || Map.Field(pos)->IsTerrainResourceOnMap(CurrentResource) == false) && (!unit.IX) && (!unit.IY)) { //Wyrmgus start // if (!FindTerrainType(unit.Type->MovementMask, MapFieldForest, 16, *unit.Player, this->goalPos, &pos)) { if ((this->CurrentResource == WoodCost && !FindTerrainType(unit.Type->MovementMask, MapFieldForest, 16, *unit.Player, this->goalPos, &pos)) || (this->CurrentResource == StoneCost && !FindTerrainType(unit.Type->MovementMask, MapFieldRocks, 16, *unit.Player, this->goalPos, &pos))) { //Wyrmgus end // no wood in range return -1; } else { this->goalPos = pos; } } switch (DoActionMove(unit)) { case PF_UNREACHABLE: //Wyrmgus start //if is unreachable and is on a raft, see if the raft can move closer if ((Map.Field(unit.tilePos)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands); } return 0; } } } //Wyrmgus end unit.Wait = 10; if (unit.Player->AiEnabled) { this->Range++; if (this->Range >= 5) { this->Range = 0; AiCanNotMove(unit); } } //Wyrmgus start // if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 9999, *unit.Player, unit.tilePos, &pos)) { if ((this->CurrentResource == WoodCost && FindTerrainType(unit.Type->MovementMask, MapFieldForest, 9999, *unit.Player, unit.tilePos, &pos)) || (this->CurrentResource == StoneCost && FindTerrainType(unit.Type->MovementMask, MapFieldRocks, 9999, *unit.Player, unit.tilePos, &pos))) { //Wyrmgus end this->goalPos = pos; DebugPrint("Found a better place to harvest %d,%d\n" _C_ pos.x _C_ pos.y); // FIXME: can't this overflow? It really shouldn't, since // x and y are really supossed to be reachable, checked thorugh a flood fill. // I don't know, sometimes stuff happens. return 0; } return -1; case PF_REACHED: return 1; case PF_WAIT: if (unit.Player->AiEnabled) { this->Range++; if (this->Range >= 5) { this->Range = 0; AiCanNotMove(unit); } } default: return 0; } }
/* virtual */ void COrder_Follow::Execute(CUnit &unit) { if (unit.Wait) { if (!unit.Waiting) { unit.Waiting = 1; unit.WaitBackup = unit.Anim; } //Wyrmgus start // UnitShowAnimation(unit, unit.Type->Animations->Still); UnitShowAnimation(unit, unit.GetAnimations()->Still); //Wyrmgus end unit.Wait--; return; } if (unit.Waiting) { unit.Anim = unit.WaitBackup; unit.Waiting = 0; } CUnit *goal = this->GetGoal(); // Reached target if (this->State == State_TargetReached) { if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); this->Finished = true; return ; } // Don't follow after immobile units if (goal && goal->CanMove() == false) { this->Finished = true; return; } //Wyrmgus start // if (goal->tilePos == this->goalPos) { if (goal->tilePos == this->goalPos && goal->MapLayer == this->MapLayer) { //Wyrmgus end // Move to the next order if (unit.Orders.size() > 1) { this->Finished = true; return ; } unit.Wait = 10; if (this->Range > 1) { this->Range = 1; this->State = State_Init; } return ; } this->State = State_Init; } if (this->State == State_Init) { // first entry this->State = State_Initialized; } switch (DoActionMove(unit)) { // reached end-point? case PF_UNREACHABLE: //Wyrmgus start if ((Map.Field(unit.tilePos, unit.MapLayer)->Flags & MapFieldBridge) && !unit.Type->BoolFlag[BRIDGE_INDEX].value && unit.Type->UnitType == UnitTypeLand) { std::vector<CUnit *> table; Select(unit.tilePos, unit.tilePos, table, unit.MapLayer); for (size_t i = 0; i != table.size(); ++i) { if (!table[i]->Removed && table[i]->Type->BoolFlag[BRIDGE_INDEX].value && table[i]->CanMove()) { if (table[i]->CurrentAction() == UnitActionStill) { CommandStopUnit(*table[i]); CommandMove(*table[i], this->HasGoal() ? this->GetGoal()->tilePos : this->goalPos, FlushCommands, this->HasGoal() ? this->GetGoal()->MapLayer : this->MapLayer); } return; } } } //Wyrmgus end // Some tries to reach the goal this->Range++; break; case PF_REACHED: { if (!goal) { // goal has died this->Finished = true; return ; } // Handle Teleporter Units // FIXME: BAD HACK // goal shouldn't be busy and portal should be alive if (goal->Type->BoolFlag[TELEPORTER_INDEX].value && goal->Goal && goal->Goal->IsAlive() && unit.MapDistanceTo(*goal) <= 1) { if (!goal->IsIdle()) { // wait unit.Wait = 10; return; } // Check if we have enough mana if (goal->Goal->Type->TeleportCost > goal->Variable[MANA_INDEX].Value) { this->Finished = true; return; } else { goal->Variable[MANA_INDEX].Value -= goal->Goal->Type->TeleportCost; } // Everything is OK, now teleport the unit unit.Remove(NULL); if (goal->Type->TeleportEffectIn) { goal->Type->TeleportEffectIn->pushPreamble(); goal->Type->TeleportEffectIn->pushInteger(UnitNumber(unit)); goal->Type->TeleportEffectIn->pushInteger(UnitNumber(*goal)); goal->Type->TeleportEffectIn->pushInteger(unit.GetMapPixelPosCenter().x); goal->Type->TeleportEffectIn->pushInteger(unit.GetMapPixelPosCenter().y); goal->Type->TeleportEffectIn->run(); } unit.tilePos = goal->Goal->tilePos; //Wyrmgus start unit.MapLayer = goal->Goal->MapLayer; //Wyrmgus end DropOutOnSide(unit, unit.Direction, NULL); // FIXME: we must check if the units supports the new order. CUnit &dest = *goal->Goal; if (dest.Type->TeleportEffectOut) { dest.Type->TeleportEffectOut->pushPreamble(); dest.Type->TeleportEffectOut->pushInteger(UnitNumber(unit)); dest.Type->TeleportEffectOut->pushInteger(UnitNumber(dest)); dest.Type->TeleportEffectOut->pushInteger(unit.GetMapPixelPosCenter().x); dest.Type->TeleportEffectOut->pushInteger(unit.GetMapPixelPosCenter().y); dest.Type->TeleportEffectOut->run(); } if (dest.NewOrder == NULL || (dest.NewOrder->Action == UnitActionResource && !unit.Type->BoolFlag[HARVESTER_INDEX].value) //Wyrmgus start // || (dest.NewOrder->Action == UnitActionAttack && !unit.Type->CanAttack) || (dest.NewOrder->Action == UnitActionAttack && !unit.CanAttack(true)) //Wyrmgus end || (dest.NewOrder->Action == UnitActionBoard && unit.Type->UnitType != UnitTypeLand)) { this->Finished = true; return ; } else { if (dest.NewOrder->HasGoal()) { if (dest.NewOrder->GetGoal()->Destroyed) { delete dest.NewOrder; dest.NewOrder = NULL; this->Finished = true; return ; } unit.Orders.insert(unit.Orders.begin() + 1, dest.NewOrder->Clone()); this->Finished = true; return ; } } } this->goalPos = goal->tilePos; //Wyrmgus start this->MapLayer = goal->MapLayer; //Wyrmgus end this->State = State_TargetReached; } // FALL THROUGH default: break; } // Target destroyed? if (goal && !goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Goal gone\n"); this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize(); //Wyrmgus start this->MapLayer = goal->MapLayer; //Wyrmgus end this->ClearGoal(); goal = NULL; } if (unit.Anim.Unbreakable) { return ; } // If our leader is dead or stops or attacks: // Attack any enemy in reaction range. // If don't set the goal, the unit can than choose a // better goal if moving nearer to enemy. //Wyrmgus start // if (unit.Type->CanAttack if (unit.CanAttack() //Wyrmgus end && (!goal || goal->CurrentAction() == UnitActionAttack || goal->CurrentAction() == UnitActionStill)) { CUnit *target = AttackUnitsInReactRange(unit); if (target) { // Save current command to come back. COrder *savedOrder = NULL; if (unit.CanStoreOrder(unit.CurrentOrder())) { savedOrder = this->Clone(); } this->Finished = true; //Wyrmgus start // unit.Orders.insert(unit.Orders.begin() + 1, COrder::NewActionAttack(unit, target->tilePos)); unit.Orders.insert(unit.Orders.begin() + 1, COrder::NewActionAttack(unit, target->tilePos, target->MapLayer)); //Wyrmgus end if (savedOrder != NULL) { unit.SavedOrder = savedOrder; } } } }
/* virtual */ void COrder_Repair::Execute(CUnit &unit) { Assert(this->ReparableTarget == this->GetGoal()); switch (this->State) { case 0: this->State = 1; // FALL THROUGH case 1: { // Move near to target. // FIXME: RESET FIRST!! Why? We move first and than check if // something is in sight. int err = DoActionMove(unit); if (!unit.Anim.Unbreakable) { // No goal: if meeting damaged building repair it. CUnit *goal = this->GetGoal(); if (goal) { if (!goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("repair target gone.\n"); this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize(); ReparableTarget = NULL; this->ClearGoal(); goal = NULL; } } else if (unit.Player->AiEnabled) { // Ai players workers should stop if target is killed err = -1; } // Have reached target? FIXME: could use return value if (goal && unit.MapDistanceTo(*goal) <= unit.Type->RepairRange && goal->Variable[HP_INDEX].Value < goal->Variable[HP_INDEX].Max) { this->State = 2; this->RepairCycle = 0; const Vec2i dir = goal->tilePos + goal->Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); } else if (err < 0) { this->Finished = true; return ; } } break; } case 2: {// Repair the target. AnimateActionRepair(unit); this->RepairCycle++; if (unit.Anim.Unbreakable) { return ; } CUnit *goal = this->GetGoal(); if (goal) { if (!goal->IsVisibleAsGoal(*unit.Player)) { DebugPrint("repair goal is gone\n"); this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize(); // FIXME: should I clear this here? this->ClearGoal(); ReparableTarget = NULL; goal = NULL; } else { const int dist = unit.MapDistanceTo(*goal); if (dist <= unit.Type->RepairRange) { if (RepairUnit(unit, *goal)) { this->Finished = true; return ; } } else if (dist > unit.Type->RepairRange) { // If goal has move, chase after it this->State = 0; } } } // Target is fine, choose new one. if (!goal || goal->Variable[HP_INDEX].Value >= goal->Variable[HP_INDEX].Max) { this->Finished = true; return ; } // FIXME: automatic repair } break; } }