/** ** 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; }
/** ** Unit attacks! ** ** if (SubAction & WEAK_TARGET) is true the goal is a weak goal. ** This means the unit AI (little AI) could choose a new better goal. ** ** @todo Lets do some tries to reach the target. ** If target place is not reachable, choose better goal to reduce ** the pathfinder load. ** ** @param unit Unit, for that the attack is handled. */ /* virtual */ void COrder_Attack::Execute(CUnit &unit) { Assert(this->HasGoal() || Map.Info.IsPointOnMap(this->goalPos)); if (unit.Wait) { unit.Wait--; return; } switch (this->State) { case 0: { // First entry // did Order change ? if (CheckForTargetInRange(unit)) { return; } // Can we already attack ? if (this->HasGoal()) { CUnit &goal = *this->GetGoal(); const int dist = goal.MapDistanceTo(unit); if (unit.Type->MinAttackRange < dist && dist <= unit.Stats->Variables[ATTACKRANGE_INDEX].Max) { const Vec2i dir = goal.tilePos + goal.Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, dir); this->State = ATTACK_TARGET; AttackTarget(unit); return; } } this->State = MOVE_TO_TARGET; // FIXME: should use a reachable place to reduce pathfinder time. } // FALL THROUGH case MOVE_TO_TARGET: case MOVE_TO_TARGET + WEAK_TARGET: if (!unit.CanMove()) { this->Finished = true; return; } MoveToTarget(unit); break; case ATTACK_TARGET: case ATTACK_TARGET + WEAK_TARGET: AttackTarget(unit); break; case WEAK_TARGET: DebugPrint("FIXME: wrong entry.\n"); break; } }
/* virtual */ void CAnimation_Rotate::Action(CUnit &unit, int &/*move*/, int /*scale*/) const { Assert(unit.Anim.Anim == this); if (!strcmp(this->rotateStr.c_str(), "target") && unit.CurrentOrder()->HasGoal()) { COrder &order = *unit.CurrentOrder(); const CUnit &target = *order.GetGoal(); if (target.Destroyed) { order.ClearGoal(); return; } const Vec2i pos = target.tilePos + target.Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, pos); } else { UnitRotate(unit, ParseAnimInt(unit, this->rotateStr.c_str())); } }
bool COrder_Still::AutoAttackStand(CUnit &unit) { //Wyrmgus start // if (unit.Type->CanAttack == false) { if (unit.CanAttack() == false) { //Wyrmgus end return false; } // Removed units can only attack in AttackRange, from bunker //Wyrmgus start //if unit is in a container which is attacking, and the container has a goal, use that goal (if possible) instead // CUnit *autoAttackUnit = AttackUnitsInRange(unit); CUnit *autoAttackUnit = unit.Container && unit.Container->CurrentAction() == UnitActionAttack && unit.Container->CurrentOrder()->HasGoal() ? unit.Container->CurrentOrder()->GetGoal() : AttackUnitsInRange(unit); //Wyrmgus end if (autoAttackUnit == nullptr) { return false; } // If unit is removed, use container's x and y const CUnit *firstContainer = unit.GetFirstContainer(); if (firstContainer->MapDistanceTo(*autoAttackUnit) > unit.GetModifiedVariable(ATTACKRANGE_INDEX)) { return false; } //Wyrmgus start // if (GameSettings.Inside && CheckObstaclesBetweenTiles(unit.tilePos, autoAttackUnit->tilePos, MapFieldRocks | MapFieldForest) == false) { if (Map.IsLayerUnderground(autoAttackUnit->MapLayer->ID) && unit.GetModifiedVariable(ATTACKRANGE_INDEX) > 1 && CheckObstaclesBetweenTiles(unit.tilePos, autoAttackUnit->tilePos, MapFieldAirUnpassable, autoAttackUnit->MapLayer->ID) == false) { //Wyrmgus end return false; } this->State = SUB_STILL_ATTACK; // Mark attacking. this->SetGoal(autoAttackUnit); //Wyrmgus start // UnitHeadingFromDeltaXY(unit, autoAttackUnit->tilePos + autoAttackUnit->Type->GetHalfTileSize() - unit.tilePos); UnitHeadingFromDeltaXY(unit, PixelSize(PixelSize(autoAttackUnit->tilePos) * Map.GetMapLayerPixelTileSize(autoAttackUnit->MapLayer->ID)) + autoAttackUnit->GetHalfTilePixelSize() - PixelSize(PixelSize(unit.tilePos) * Map.GetMapLayerPixelTileSize(autoAttackUnit->MapLayer->ID)) - unit.GetHalfTilePixelSize()); //Wyrmgus end return true; }
bool COrder_Still::AutoAttackStand(CUnit &unit) { if (unit.Type->CanAttack == false) { return false; } // Removed units can only attack in AttackRange, from bunker CUnit *autoAttackUnit = AttackUnitsInRange(unit); if (autoAttackUnit == NULL) { return false; } // If unit is removed, use containers x and y const CUnit *firstContainer = unit.Container ? unit.Container : &unit; if (firstContainer->MapDistanceTo(*autoAttackUnit) > unit.Stats->Variables[ATTACKRANGE_INDEX].Max) { return false; } if (GameSettings.Inside && CheckObstaclesBetweenTiles(unit.tilePos, autoAttackUnit->tilePos, MapFieldRocks | MapFieldForest) == false) { return false; } this->State = SUB_STILL_ATTACK; // Mark attacking. this->SetGoal(autoAttackUnit); UnitHeadingFromDeltaXY(unit, autoAttackUnit->tilePos + autoAttackUnit->Type->GetHalfTileSize() - unit.tilePos); return 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.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; } }
/** ** Unit moves! Generic function called from other actions. ** ** @param unit Pointer to unit. ** ** @return >0 remaining path length, 0 wait for path, -1 ** reached goal, -2 can't reach the goal. */ int DoActionMove(CUnit &unit) { Vec2i posd; // movement in tile. int d; Assert(unit.CanMove()); if (!unit.Moving && (unit.Type->Animations->Move != unit.Anim.CurrAnim || !unit.Anim.Wait)) { Assert(!unit.Anim.Unbreakable); // FIXME: So units flying up and down are not affected. unit.IX = 0; unit.IY = 0; UnmarkUnitFieldFlags(unit); d = NextPathElement(unit, &posd.x, &posd.y); MarkUnitFieldFlags(unit); switch (d) { case PF_UNREACHABLE: // Can't reach, stop if (unit.Player->AiEnabled) { AiCanNotMove(unit); } unit.Moving = 0; return d; case PF_REACHED: // Reached goal, stop unit.Moving = 0; return d; case PF_WAIT: // No path, wait // Reset frame to still frame while we wait // FIXME: Unit doesn't animate. unit.Frame = unit.Type->StillFrame; UnitUpdateHeading(unit); unit.Wait = 10; unit.Moving = 0; return d; default: // On the way moving unit.Moving = 1; break; } if (unit.Type->UnitType == UnitTypeNaval) { // Boat (un)docking? const CMapField &mf_cur = *Map.Field(unit.Offset); const CMapField &mf_next = *Map.Field(unit.tilePos + posd); if (mf_cur.WaterOnMap() && mf_next.CoastOnMap()) { PlayUnitSound(unit, VoiceDocking); } else if (mf_cur.CoastOnMap() && mf_next.WaterOnMap()) { PlayUnitSound(unit, VoiceDocking); // undocking } } Vec2i pos = unit.tilePos + posd; unit.MoveToXY(pos); // Remove unit from the current selection if (unit.Selected && !Map.Field(pos)->playerInfo.IsTeamVisible(*ThisPlayer)) { if (NumSelected == 1) { // Remove building cursor CancelBuildingMode(); } if (!ReplayRevealMap) { UnSelectUnit(unit); SelectionChanged(); } } unit.IX = -posd.x * PixelTileSize.x; unit.IY = -posd.y * PixelTileSize.y; unit.Frame = unit.Type->StillFrame; UnitHeadingFromDeltaXY(unit, posd); } else { posd.x = Heading2X[unit.Direction / NextDirection]; posd.y = Heading2Y[unit.Direction / NextDirection]; d = unit.pathFinderData->output.Length + 1; } unit.pathFinderData->output.Cycles++;//reset have to be manualy controled by caller. int move = UnitShowAnimationScaled(unit, unit.Type->Animations->Move, Map.Field(unit.Offset)->Cost); unit.IX += posd.x * move; unit.IY += posd.y * move; // Finished move animation, set Moving to 0 so we recalculate the path // next frame // FIXME: this is broken for subtile movement if (!unit.Anim.Unbreakable && !unit.IX && !unit.IY) { unit.Moving = 0; } return d; }
/** ** 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; } }
/** ** 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); } }
/** ** 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; } }
/** ** Auto attack nearby units if possible */ static void AutoAttack(CUnit *unit, bool stand_ground) { CUnit *temp; CUnit *goal; if (unit->Wait) { unit->Wait--; return; } // Cowards don't attack unless ordered. if (unit->Type->CanAttack && !unit->Type->Coward) { // Normal units react in reaction range. if (CanMove(unit) && !unit->Removed && !stand_ground) { if ((goal = AttackUnitsInReactRange(unit))) { // Weak goal, can choose other unit, come back after attack CommandAttack(unit, goal->X, goal->Y, NULL, FlushCommands); Assert(unit->SavedOrder.Action == UnitActionStill); Assert(!unit->SavedOrder.Goal); unit->SavedOrder.Action = UnitActionAttack; unit->SavedOrder.Range = 0; unit->SavedOrder.X = unit->X; unit->SavedOrder.Y = unit->Y; unit->SavedOrder.Goal = NoUnitP; } else { unit->Wait = 15; } // Removed units can only attack in AttackRange, from bunker } else if ((goal = AttackUnitsInRange(unit))) { temp = unit->Orders[0]->Goal; if (temp && temp->Orders[0]->Action == UnitActionDie) { temp->RefsDecrease(); unit->Orders[0]->Goal = temp = NoUnitP; } if (!unit->SubAction || temp != goal) { // New target. if (temp) { temp->RefsDecrease(); } unit->Orders[0]->Goal = goal; goal->RefsIncrease(); unit->State = 0; unit->SubAction = 1; // Mark attacking. UnitHeadingFromDeltaXY(unit, goal->X + (goal->Type->TileWidth - 1) / 2 - unit->X, goal->Y + (goal->Type->TileHeight - 1) / 2 - unit->Y); } return; } } else { unit->Wait = 15; } if (unit->SubAction) { // was attacking. if ((temp = unit->Orders[0]->Goal)) { temp->RefsDecrease(); unit->Orders[0]->Goal = NoUnitP; } unit->SubAction = unit->State = 0; // No attacking, restart } Assert(!unit->Orders[0]->Goal); }
/** ** Start harvesting the resource. ** ** @param unit Pointer to unit. ** ** @return TRUE if ready, otherwise FALSE. */ int COrder_Resource::StartGathering(CUnit &unit) { CUnit *goal; const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource]; Assert(!unit.IX); Assert(!unit.IY); //Wyrmgus start // if (resinfo.TerrainHarvester) { if (Map.Info.IsPointOnMap(this->goalPos)) { //Wyrmgus end // This shouldn't happened? #if 0 if (!Map.IsTerrainResourceOnMap(unit.Orders->goalPos, this->CurrentResource)) { DebugPrint("Wood gone, just like that?\n"); return 0; } #endif UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos); if (resinfo.WaitAtResource) { this->TimeToHarvest = std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]); } else { this->TimeToHarvest = 1; } this->DoneHarvesting = 0; if (this->CurrentResource != unit.CurrentResource) { DropResource(unit); unit.CurrentResource = this->CurrentResource; } return 1; } goal = this->GetGoal(); // Target is dead, stop getting resources. if (!goal || goal->IsVisibleAsGoal(*unit.Player) == false) { // Find an alternative, but don't look too far. this->goalPos.x = -1; this->goalPos.y = -1; if ((goal = UnitFindResource(unit, unit, 15, this->CurrentResource, unit.Player->AiEnabled))) { this->State = SUB_START_RESOURCE; this->SetGoal(goal); } else { this->ClearGoal(); this->Finished = true; } return 0; } // FIXME: 0 can happen, if to near placed by map designer. Assert(unit.MapDistanceTo(*goal) <= 1); // Update the heading of a harvesting unit to looks straight at the resource. //Wyrmgus start // UnitHeadingFromDeltaXY(unit, goal->tilePos - unit.tilePos + goal->Type->GetHalfTileSize()); UnitHeadingFromDeltaXY(unit, Vec2i(goal->tilePos.x * PixelTileSize.x, goal->tilePos.y * PixelTileSize.y) - Vec2i(unit.tilePos.x * PixelTileSize.x, unit.tilePos.y * PixelTileSize.y) + goal->Type->GetHalfTilePixelSize() - unit.Type->GetHalfTilePixelSize()); //Wyrmgus end // If resource is still under construction, wait! if ((goal->Type->MaxOnBoard && goal->Resource.Active >= goal->Type->MaxOnBoard) || goal->CurrentAction() == UnitActionBuilt) { // FIXME: Determine somehow when the resource will be free to use // FIXME: Could we somehow find another resource? Think minerals // FIXME: We should add a flag for that, and a limited range. // However the CPU usage is really low (no pathfinding stuff). unit.Wait = 10; return 0; } // Place unit inside the resource //Wyrmgus start // if (!resinfo.HarvestFromOutside) { if (!goal->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) { //Wyrmgus end if (goal->Variable[MAXHARVESTERS_INDEX].Value == 0 || goal->Variable[MAXHARVESTERS_INDEX].Value > goal->InsideCount) { this->ClearGoal(); int selected = unit.Selected; unit.Remove(goal); if (selected && !Preference.DeselectInMine) { unit.Removed = 0; SelectUnit(unit); SelectionChanged(); unit.Removed = 1; } } else if (goal->Variable[MAXHARVESTERS_INDEX].Value <= goal->InsideCount) { //Resource is full, wait unit.Wait = 10; return 0; } } if (this->CurrentResource != unit.CurrentResource) { DropResource(unit); unit.CurrentResource = this->CurrentResource; } // Activate the resource goal->Resource.Active++; if (resinfo.WaitAtResource) { //Wyrmgus start // this->TimeToHarvest = std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]); int wait_at_resource = resinfo.WaitAtResource; if (!goal->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) { wait_at_resource = resinfo.WaitAtResource * 100 / resinfo.ResourceStep; } this->TimeToHarvest = std::max<int>(1, wait_at_resource * SPEEDUP_FACTOR / (unit.Player->SpeedResourcesHarvest[resinfo.ResourceId] + goal->Variable[TIMEEFFICIENCYBONUS_INDEX].Value)); //Wyrmgus end } else { this->TimeToHarvest = 1; } this->DoneHarvesting = 0; return 1; }
/* 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; } }