static void CancelBuilt(COrder_Built &order, CUnit &unit) { Assert(unit.CurrentOrder() == &order); CUnit *worker = order.GetWorkerPtr(); // Drop out unit if (worker != NULL) { worker->ClearAction(); DropOutOnSide(*worker, LookingW, &unit); } // Player gets back 75% of the original cost for a building. unit.Player->AddCostsFactor(unit.Stats->Costs, CancelBuildingCostsFactor); // Cancel building LetUnitDie(unit); }
/** ** Create a unit and place it on the map ** ** @param l Lua state. ** ** @return Returns the slot number of the made unit. */ static int CclCreateUnit(lua_State *l) { LuaCheckArgs(l, 3); lua_pushvalue(l, 1); CUnitType *unittype = CclGetUnitType(l); if (unittype == NULL) { LuaError(l, "Bad unittype"); } lua_pop(l, 1); Vec2i ipos; CclGetPos(l, &ipos.x, &ipos.y, 3); lua_pushvalue(l, 2); const int playerno = TriggerGetPlayer(l); lua_pop(l, 1); if (playerno == -1) { printf("CreateUnit: You cannot use \"any\" in create-unit, specify a player\n"); LuaError(l, "bad player"); return 0; } if (Players[playerno].Type == PlayerNobody) { printf("CreateUnit: player %d does not exist\n", playerno); LuaError(l, "bad player"); return 0; } CUnit *unit = MakeUnit(*unittype, &Players[playerno]); if (unit == NULL) { DebugPrint("Unable to allocate unit"); return 0; } else { if (UnitCanBeAt(*unit, ipos) || (unit->Type->Building && CanBuildUnitType(NULL, *unit->Type, ipos, 0))) { unit->Place(ipos); } else { const int heading = SyncRand() % 256; unit->tilePos = ipos; DropOutOnSide(*unit, heading, NULL); } UpdateForNewUnit(*unit, 0); lua_pushnumber(l, UnitNumber(*unit)); return 1; } }
/** ** Move a unit on map. ** ** @param l Lua state. ** ** @return Returns the slot number of the made placed. */ static int CclMoveUnit(lua_State *l) { LuaCheckArgs(l, 2); lua_pushvalue(l, 1); CUnit *unit = CclGetUnit(l); lua_pop(l, 1); Vec2i ipos; CclGetPos(l, &ipos.x, &ipos.y, 2); if (UnitCanBeAt(*unit, ipos)) { unit->Place(ipos); } else { const int heading = SyncRand() % 256; unit->tilePos = ipos; DropOutOnSide(*unit, heading, NULL); } lua_pushvalue(l, 1); return 1; }
/** ** Unit trains unit! ** ** @param unit Unit that trains. */ global void HandleActionTrain(Unit* unit) { Unit* nunit; UnitType* type; Player* player; #if 0 // JOHNS: should be checked by the user-interface if( &Players[unit->Player]==ThisPlayer ) { // FIXME: If so used you get millions of messages. if( ThisPlayer->Food<=ThisPlayer->Units && unit->Command.Data.Train.Ticks ) { SetMessage( "You need more farms!" ); } else { AiNeedMoreFarms(unit); } } #endif player=unit->Player; unit->Command.Data.Train.Ticks+=SpeedTrain; // FIXME: Should count down if( unit->Command.Data.Train.Ticks >=unit->Command.Data.Train.What[0] ->Stats[player->Player].Costs[TimeCost] ) { // // Check if enough food available. // if( player->Food<=player->NumUnits ) { // FIXME: GameMessage if( player==ThisPlayer ) { // FIXME: PlayVoice :), see task.txt SetMessage("You need more farms!"); } else { // FIXME: Callback for AI! // AiNeedMoreFarms(unit); } unit->Command.Data.Train.Ticks-=SpeedTrain; unit->Reset=1; unit->Wait=FRAMES_PER_SECOND/6; return; } nunit=MakeUnit(&UnitTypes[unit->Command.Data.Train.What[0]->Type] ,player); nunit->X=unit->X; nunit->Y=unit->Y; type=unit->Type; DropOutOnSide(nunit,HeadingW,type->TileWidth,type->TileHeight); // FIXME: GameMessage if( player==ThisPlayer ) { SetMessage("Training complete"); PlayUnitSound(nunit,VoiceReady); } else { AiTrainingComplete(unit,nunit); } unit->Reset=1; unit->Wait=1; if ( --unit->Command.Data.Train.Count ) { int z; for( z = 0; z < MAX_UNIT_TRAIN-1; z++ ) { unit->Command.Data.Train.What[z] = unit->Command.Data.Train.What[z+1]; } unit->Command.Data.Train.Ticks=0; } else { unit->Command.Action=UnitActionStill; } nunit->Command=unit->PendCommand; if( IsSelected(unit) ) { UpdateBottomPanel(); MustRedraw|=RedrawPanels; } return; } if( IsSelected(unit) ) { MustRedraw|=RedrawTopPanel; } unit->Reset=1; unit->Wait=FRAMES_PER_SECOND/6; }
//Wyrmgus start ///* virtual */ int Spell_Summon::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos) /* virtual */ int Spell_Summon::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos, int z) //Wyrmgus end { Vec2i pos = goalPos; bool cansummon; CUnitType &unittype = *this->UnitType; int ttl = this->TTL; if (this->RequireCorpse) { const Vec2i offset(1, 1); const Vec2i minPos = pos - offset; const Vec2i maxPos = pos + offset; //Wyrmgus start // CUnit *unit = FindUnit_If(minPos, maxPos, IsDyingAndNotABuilding()); CUnit *unit = FindUnit_If(minPos, maxPos, z, IsDyingAndNotABuilding()); //Wyrmgus end cansummon = false; if (unit != NULL) { // Found a corpse. eliminate it and proceed to summoning. pos = unit->tilePos; //Wyrmgus start z = unit->MapLayer; //Wyrmgus end unit->Remove(NULL); unit->Release(); cansummon = true; } } else { cansummon = true; } if (cansummon) { //Wyrmgus start // DebugPrint("Summoning a %s\n" _C_ unittype.Name.c_str()); DebugPrint("Summoning a %s\n" _C_ unittype.GetDefaultName(*caster.Player).c_str()); //Wyrmgus end // // Create units. // FIXME: do summoned units count on food? // target = MakeUnit(unittype, caster.Player); if (target != NULL) { target->tilePos = pos; //Wyrmgus start target->MapLayer = z; //Wyrmgus end DropOutOnSide(*target, LookingW, NULL); // To avoid defending summoned unit for AI target->Summoned = 1; // // set life span. ttl=0 results in a permanent unit. // if (ttl) { target->TTL = GameCycle + ttl; } // Insert summoned unit to AI force so it will help them in battle if (this->JoinToAiForce && caster.Player->AiEnabled) { int force = caster.Player->Ai->Force.GetForce(caster); if (force != -1) { caster.Player->Ai->Force[force].Insert(*target); target->GroupId = caster.GroupId; CommandDefend(*target, caster, FlushCommands); } } caster.Variable[MANA_INDEX].Value -= spell.ManaCost; } else { DebugPrint("Unable to allocate Unit"); } return 1; } return 0; }
/* virtual */ void COrder_Train::Execute(CUnit &unit) { AnimateActionTrain(unit); if (unit.Wait) { unit.Wait--; return ; } CPlayer &player = *unit.Player; const CUnitType &nType = *this->Type; const int cost = nType.Stats[player.Index].Costs[TimeCost]; this->Ticks += std::max(1, player.SpeedTrain / SPEEDUP_FACTOR); if (this->Ticks < cost) { unit.Wait = CYCLES_PER_SECOND / 6; return ; } this->Ticks = std::min(this->Ticks, cost); // Check if enough supply available. const int food = player.CheckLimits(nType); if (food < 0) { if (food == -3 && unit.Player->AiEnabled) { AiNeedMoreSupply(*unit.Player); } unit.Wait = CYCLES_PER_SECOND / 6; return ; } CUnit *newUnit = MakeUnit(nType, &player); if (newUnit == NULL) { // No more memory :/ player.Notify(NotifyYellow, unit.tilePos, _("Unable to train %s"), nType.Name.c_str()); unit.Wait = CYCLES_PER_SECOND / 6; return ; } // New unit might supply food UpdateForNewUnit(*newUnit, 0); // Set life span if (unit.Type->DecayRate) { newUnit->TTL = GameCycle + unit.Type->DecayRate * 6 * CYCLES_PER_SECOND; } /* Auto Group Add */ if (!unit.Player->AiEnabled && unit.GroupId) { int num = 0; while (!(unit.GroupId & (1 << num))) { ++num; } AddToGroup(&newUnit, 1, num); } DropOutOnSide(*newUnit, LookingW, &unit); player.Notify(NotifyGreen, newUnit->tilePos, _("New %s ready"), nType.Name.c_str()); if (&player == ThisPlayer) { PlayUnitSound(*newUnit, VoiceReady); } if (unit.Player->AiEnabled) { AiTrainingComplete(unit, *newUnit); } if (unit.NewOrder && unit.NewOrder->HasGoal() && unit.NewOrder->GetGoal()->Destroyed) { delete unit.NewOrder; unit.NewOrder = NULL; } if (CanHandleOrder(*newUnit, unit.NewOrder) == true) { delete newUnit->Orders[0]; newUnit->Orders[0] = unit.NewOrder->Clone(); } else { #if 0 // Tell the unit to rigth-click ? #endif } this->Finished = true; if (IsOnlySelected(unit)) { UI.ButtonPanel.Update(); } }
/* 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; } } } }
/** ** Stop gathering from the resource, go home. ** ** @param unit Poiner to unit. ** ** @return TRUE if ready, otherwise FALSE. */ int COrder_Resource::StopGathering(CUnit &unit) { CUnit *source = 0; const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource]; //Wyrmgus start // if (!resinfo.TerrainHarvester) { if (!Map.Info.IsPointOnMap(this->goalPos)) { //Wyrmgus end //Wyrmgus start // if (resinfo.HarvestFromOutside) { if (this->GetGoal() && this->GetGoal()->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) { //Wyrmgus end source = this->GetGoal(); this->ClearGoal(); } else { source = unit.Container; } source->Resource.Active--; Assert(source->Resource.Active >= 0); //Store resource position. this->Resource.Mine = source; if (Preference.MineNotifications && unit.Player->Index == ThisPlayer->Index && source->IsAlive() && !source->MineLow && source->ResourcesHeld * 100 / source->Variable[GIVERESOURCE_INDEX].Max <= 10 //Wyrmgus start // && source->Variable[GIVERESOURCE_INDEX].Max > DefaultIncomes[this->CurrentResource]) { && source->Variable[GIVERESOURCE_INDEX].Max > (DefaultIncomes[this->CurrentResource] * 10)) { //Wyrmgus end //Wyrmgus start // unit.Player->Notify(NotifyYellow, source->tilePos, _("%s is running low!"), source->Type->Name.c_str()); unit.Player->Notify(NotifyYellow, source->tilePos, _("Our %s is nearing depletion!"), source->Type->Name.c_str()); //Wyrmgus end source->MineLow = 1; } if (source->Type->MaxOnBoard) { int count = 0; CUnit *worker = source->Resource.Workers; CUnit *next = NULL; for (; NULL != worker; worker = worker->NextWorker) { Assert(worker->CurrentAction() == UnitActionResource); COrder_Resource &order = *static_cast<COrder_Resource *>(worker->CurrentOrder()); if (worker != &unit && order.IsGatheringWaiting()) { count++; if (next) { if (next->Wait > worker->Wait) { next = worker; } } else { next = worker; } } } if (next) { if (!unit.Player->AiEnabled) { DebugPrint("%d: Worker %d report: Unfreez resource gathering of %d <Wait %d> on %d [Assigned: %d Waiting %d].\n" _C_ unit.Player->Index _C_ UnitNumber(unit) _C_ UnitNumber(*next) _C_ next->Wait _C_ UnitNumber(*source) _C_ source->Resource.Assigned _C_ count); } next->Wait = 0; //source->Data.Resource.Waiting = count - 1; //Assert(source->Data.Resource.Assigned >= source->Data.Resource.Waiting); //StartGathering(next); } } } else { // Store resource position. this->Resource.Pos = unit.tilePos; Assert(this->Resource.Mine == NULL); } #ifdef DEBUG if (!unit.ResourcesHeld) { DebugPrint("Unit %d is empty???\n" _C_ UnitNumber(unit)); } #endif // Find and send to resource deposit. CUnit *depot = FindDeposit(unit, 1000, unit.CurrentResource); if (!depot || !unit.ResourcesHeld || this->Finished) { //Wyrmgus start // if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) { if (!((source && source->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) || Map.Info.IsPointOnMap(this->goalPos))) { //Wyrmgus end Assert(unit.Container); DropOutOnSide(unit, LookingW, source); } CUnit *mine = this->Resource.Mine; if (mine) { unit.DeAssignWorkerFromMine(*mine); this->Resource.Mine = NULL; } DebugPrint("%d: Worker %d report: Can't find a resource [%d] deposit.\n" _C_ unit.Player->Index _C_ UnitNumber(unit) _C_ unit.CurrentResource); this->Finished = true; return 0; } else { //Wyrmgus start // if (!(resinfo.HarvestFromOutside || resinfo.TerrainHarvester)) { if (!((source && source->Type->BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) || Map.Info.IsPointOnMap(this->goalPos))) { //Wyrmgus end Assert(unit.Container); DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), source); } UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT); } if (IsOnlySelected(unit)) { SelectedUnitChanged(); } #if 1 return 1; #endif }
/** ** Find something else to do when the resource is exhausted. ** This is called from GatherResource when the resource is empty. ** ** @param unit pointer to harvester unit. ** @param source pointer to resource unit. */ void COrder_Resource::LoseResource(CUnit &unit, CUnit &source) { CUnit *depot; const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource]; //Wyrmgus start const CUnitType &source_type = *source.Type; // Assert((unit.Container == &source && !resinfo.HarvestFromOutside) // || (!unit.Container && resinfo.HarvestFromOutside)); Assert((unit.Container == &source && !source_type.BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) || (!unit.Container && source_type.BoolFlag[HARVESTFROMOUTSIDE_INDEX].value)); //Wyrmgus end //Wyrmgus start // if (resinfo.HarvestFromOutside) { if (source_type.BoolFlag[HARVESTFROMOUTSIDE_INDEX].value) { //Wyrmgus end this->ClearGoal(); --source.Resource.Active; } // Continue to harvest if we aren't fully loaded //Wyrmgus start // if (resinfo.HarvestFromOutside && unit.ResourcesHeld < resinfo.ResourceCapacity) { if (source_type.BoolFlag[HARVESTFROMOUTSIDE_INDEX].value && unit.ResourcesHeld < resinfo.ResourceCapacity) { //Wyrmgus end CUnit *goal = UnitFindResource(unit, unit, 15, this->CurrentResource, 1); if (goal) { this->goalPos.x = -1; this->goalPos.y = -1; this->State = SUB_START_RESOURCE; this->SetGoal(goal); return; } } // If we are fully loaded first search for a depot. if (unit.ResourcesHeld && (depot = FindDeposit(unit, 1000, unit.CurrentResource))) { if (unit.Container) { DropOutNearest(unit, depot->tilePos + depot->Type->GetHalfTileSize(), &source); } // Remember where it mined, so it can look around for another resource. // //FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //unit.CurrentOrder()->Arg1.ResourcePos = (unit.X << 16) | unit.Y; this->DoneHarvesting = true; UnitGotoGoal(unit, depot, SUB_MOVE_TO_DEPOT); DebugPrint("%d: Worker %d report: Resource is exhausted, Going to depot\n" _C_ unit.Player->Index _C_ UnitNumber(unit)); return; } // No depot found, or harvester empty // Dump the unit outside and look for something to do. if (unit.Container) { //Wyrmgus start // Assert(!resinfo.HarvestFromOutside); Assert(!source_type.BoolFlag[HARVESTFROMOUTSIDE_INDEX].value); //Wyrmgus end DropOutOnSide(unit, LookingW, &source); } this->goalPos.x = -1; this->goalPos.y = -1; //use depot as goal depot = UnitFindResource(unit, unit, 15, this->CurrentResource, unit.Player->AiEnabled); if (depot) { DebugPrint("%d: Worker %d report: Resource is exhausted, Found another resource.\n" _C_ unit.Player->Index _C_ UnitNumber(unit)); this->State = SUB_START_RESOURCE; this->SetGoal(depot); } else { DebugPrint("%d: Worker %d report: Resource is exhausted, Just sits around confused.\n" _C_ unit.Player->Index _C_ UnitNumber(unit)); this->Finished = true; } }
/** ** Wait in depot, for the resources stored. ** ** @param unit Pointer to unit. ** ** @return TRUE if ready, otherwise FALSE. */ bool COrder_Resource::WaitInDepot(CUnit &unit) { const ResourceInfo &resinfo = *unit.Type->ResInfo[this->CurrentResource]; const CUnit *depot = ResourceDepositOnMap(unit.tilePos, resinfo.ResourceId); //Assert(depot); // Range hardcoded. don't stray too far though //Wyrmgus start // if (resinfo.TerrainHarvester) { if (!this->Resource.Mine) { //Wyrmgus end Vec2i pos = this->Resource.Pos; //Wyrmgus start // if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, 10, *unit.Player, pos, &pos)) { if ((this->CurrentResource == WoodCost && FindTerrainType(unit.Type->MovementMask, MapFieldForest, 10, *unit.Player, pos, &pos)) || (this->CurrentResource == StoneCost && FindTerrainType(unit.Type->MovementMask, MapFieldRocks, 10, *unit.Player, pos, &pos))) { //Wyrmgus end if (depot) { DropOutNearest(unit, pos, depot); } this->goalPos = pos; //Wyrmgus start if (this->CurrentResource == WoodCost) { //tree tiles can regrow, so we need to check if any have regrown closer to the worker Vec2i forestPos; int max_forest_range = std::max<int>(abs(unit.tilePos.x - this->goalPos.x), abs(unit.tilePos.y - this->goalPos.y)); if (FindTerrainType(unit.Type->MovementMask, MapFieldForest, max_forest_range, *unit.Player, unit.tilePos, &forestPos)) { if (PlaceReachable(unit, forestPos, 1, 1, 0, 1, max_forest_range * 4)) { this->goalPos = forestPos; } } } //Wyrmgus end } else { if (depot) { DropOutOnSide(unit, LookingW, depot); } this->Finished = true; return false; } } else { const unsigned int tooManyWorkers = 15; CUnit *mine = this->Resource.Mine; const int range = 15; CUnit *newdepot = NULL; CUnit *goal = NULL; const bool longWay = unit.pathFinderData->output.Cycles > 500; //Wyrmgus start // if (unit.Player->AiEnabled && AiPlayer && AiPlayer->BuildDepots) { if (depot && unit.Player->AiEnabled && AiPlayer && AiPlayer->BuildDepots) { //check if the depot is valid //Wyrmgus end // If the depot is overused, we need first to try to switch into another depot // Use depot's ref counter for that if (longWay || !mine || (depot->Refs > tooManyWorkers)) { newdepot = AiGetSuitableDepot(unit, *depot, &goal); if (newdepot == NULL && longWay) { // We need a new depot AiNewDepotRequest(unit); } } } // If goal is not NULL, then we got it in AiGetSuitableDepot if (!goal) { goal = UnitFindResource(unit, newdepot ? *newdepot : (mine ? *mine : unit), mine ? range : 1000, this->CurrentResource, unit.Player->AiEnabled, newdepot ? newdepot : depot); } if (goal) { if (depot) { DropOutNearest(unit, goal->tilePos + goal->Type->GetHalfTileSize(), depot); } if (goal != mine) { if (mine) { unit.DeAssignWorkerFromMine(*mine); } unit.AssignWorkerToMine(*goal); this->Resource.Mine = goal; } this->SetGoal(goal); this->goalPos.x = this->goalPos.y = -1; } else { #ifdef DEBUG const Vec2i &pos = mine ? mine->tilePos : unit.tilePos; DebugPrint("%d: Worker %d report: [%d,%d] Resource gone near [%d,%d] in range %d. Sit and play dumb.\n" _C_ unit.Player->Index _C_ UnitNumber(unit) _C_ unit.tilePos.x _C_ unit.tilePos.y _C_ pos.x _C_ pos.y _C_ range); #endif // DEBUG if (depot) { DropOutOnSide(unit, LookingW, depot); } if (mine) { unit.DeAssignWorkerFromMine(*mine); this->Resource.Mine = NULL; } this->Finished = true; return false; } } return true; }
static void Finish(COrder_Built &order, CUnit &unit) { const CUnitType &type = *unit.Type; CPlayer &player = *unit.Player; DebugPrint("%d: Building %s(%s) ready.\n" _C_ player.Index _C_ type.Ident.c_str() _C_ type.Name.c_str()); // HACK: the building is ready now player.UnitTypesCount[type.Slot]++; if (unit.Active) { player.UnitTypesAiActiveCount[type.Slot]++; } unit.Constructed = 0; if (unit.Frame < 0) { unit.Frame = -1; } else { unit.Frame = 0; } CUnit *worker = order.GetWorkerPtr(); if (worker != NULL) { if (type.BoolFlag[BUILDERLOST_INDEX].value) { // Bye bye worker. LetUnitDie(*worker); worker = NULL; } else { // Drop out the worker. worker->ClearAction(); DropOutOnSide(*worker, LookingW, &unit); // If we can harvest from the new building, do it. if (worker->Type->ResInfo[type.GivesResource]) { CommandResource(*worker, unit, 0); } // If we can reurn goods to a new depot, do it. if (worker->CurrentResource && worker->ResourcesHeld > 0 && type.CanStore[worker->CurrentResource]) { CommandReturnGoods(*worker, &unit, 0); } } } if (type.GivesResource && type.StartingResources != 0) { // Has StartingResources, Use those unit.ResourcesHeld = type.StartingResources; } player.Notify(NotifyGreen, unit.tilePos, _("New %s done"), type.Name.c_str()); if (&player == ThisPlayer) { if (type.MapSound.Ready.Sound) { PlayUnitSound(unit, VoiceReady); } else if (worker) { PlayUnitSound(*worker, VoiceWorkCompleted); } else { PlayUnitSound(unit, VoiceBuilding); } } if (player.AiEnabled) { /* Worker can be NULL */ AiWorkComplete(worker, unit); } // FIXME: Vladi: this is just a hack to test wall fixing, // FIXME: also not sure if the right place... // FIXME: Johns: hardcoded unit-type wall / more races! if (&type == UnitTypeOrcWall || &type == UnitTypeHumanWall) { Map.SetWall(unit.tilePos, &type == UnitTypeHumanWall); unit.Remove(NULL); UnitLost(unit); UnitClearOrders(unit); unit.Release(); return ; } UpdateForNewUnit(unit, 0); // Set the direction of the building if it supports them if (type.NumDirections > 1 && type.BoolFlag[NORANDOMPLACING_INDEX].value == false) { if (type.BoolFlag[WALL_INDEX].value) { // Special logic for walls CorrectWallDirections(unit); CorrectWallNeighBours(unit); } else { unit.Direction = (MyRand() >> 8) & 0xFF; // random heading } UnitUpdateHeading(unit); } if (IsOnlySelected(unit) || &player == ThisPlayer) { SelectedUnitChanged(); } MapUnmarkUnitSight(unit); unit.CurrentSightRange = unit.Stats->Variables[SIGHTRANGE_INDEX].Max; MapMarkUnitSight(unit); order.Finished = true; }