/** ** Enter the transporter. ** ** @param unit Pointer to unit. */ static void EnterTransporter(CUnit &unit, COrder_Board &order) { CUnit *transporter = order.GetGoal(); Assert(transporter != NULL); if (!transporter->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Transporter gone\n"); return; } if (transporter->BoardCount < transporter->Type->MaxOnBoard) { // Place the unit inside the transporter. unit.Remove(transporter); transporter->BoardCount++; unit.Boarded = 1; if (!unit.Player->AiEnabled) { // Don't make anything funny after going out of the transporter. CommandStopUnit(unit); } if (IsOnlySelected(*transporter)) { SelectedUnitChanged(); } return; } DebugPrint("No free slot in transporter\n"); }
/** ** Remove unit from the map. ** ** @param l Lua state. ** ** @return Returns 1. */ static int CclRemoveUnit(lua_State *l) { LuaCheckArgs(l, 1); lua_pushvalue(l, 1); CUnit *unit = CclGetUnit(l); lua_pop(l, 1); unit->Remove(NULL); lua_pushvalue(l, 1); return 1; }
/* virtual */ void COrder_Die::Execute(CUnit &unit) { // Show death animation if (AnimateActionDie(unit) == false) { // some units has no death animation unit.Anim.Unbreakable = 0; } if (unit.Anim.Unbreakable) { return ; } const CUnitType &type = *unit.Type; // Die sequence terminated, generate corpse. if (type.CorpseType == NULL) { unit.Remove(NULL); unit.Release(); return ; } const CUnitType &corpseType = *type.CorpseType; Assert(type.TileWidth >= corpseType.TileWidth && type.TileHeight >= corpseType.TileHeight); // Update sight for new corpse // We have to unmark BEFORE changing the type. // Always do that, since types can have different vision properties. unit.Remove(NULL); unit.Type = &corpseType; unit.Stats = &corpseType.Stats[unit.Player->Index]; UpdateUnitSightRange(unit); //Wyrmgus start // unit.Place(unit.tilePos); unit.Place(unit.tilePos, unit.MapLayer); //Wyrmgus end unit.Frame = 0; UnitUpdateHeading(unit); AnimateActionDie(unit); // with new corpse. }
/** ** Find the closest available drop zone for a transporter. ** Fail if transporter don't transport any unit.. ** ** @param transporter the transporter ** @param startPos start location for the search ** @param maxRange The maximum distance from initial position to search... ** @param resPos drop zone position ** ** @return 1 if a location was found, 0 otherwise */ static int ClosestFreeDropZone(CUnit &transporter, const Vec2i &startPos, int maxRange, Vec2i *resPos) { // Check there are units onboard if (!transporter.UnitInside) { return 0; } const bool isTransporterRemoved = transporter.Removed; const bool selected = transporter.Selected; if (!isTransporterRemoved) { // Remove transporter to avoid "collision" with itself. transporter.Remove(NULL); } const bool res = ClosestFreeDropZone_internal(transporter, startPos, maxRange, resPos); if (!isTransporterRemoved) { transporter.Place(transporter.tilePos); if (selected) { SelectUnit(transporter); SelectionChanged(); } } return res; }
bool COrder_Build::StartBuilding(CUnit &unit, CUnit &ontop) { const CUnitType &type = this->GetUnitType(); unit.Player->SubUnitType(type); CUnit *build = MakeUnit(const_cast<CUnitType &>(type), unit.Player); // If unable to make unit, stop, and report message if (build == NULL) { // FIXME: Should we retry this? unit.Player->Notify(NotifyYellow, unit.tilePos, _("Unable to create building %s"), type.Name.c_str()); if (unit.Player->AiEnabled) { AiCanNotBuild(unit, type); } return false; } build->Constructed = 1; build->CurrentSightRange = 0; // Building on top of something, may remove what is beneath it if (&ontop != &unit) { CBuildRestrictionOnTop *b; b = static_cast<CBuildRestrictionOnTop *>(OnTopDetails(*build, ontop.Type)); Assert(b); if (b->ReplaceOnBuild) { build->ResourcesHeld = ontop.ResourcesHeld; // We capture the value of what is beneath. build->Variable[GIVERESOURCE_INDEX].Value = ontop.Variable[GIVERESOURCE_INDEX].Value; build->Variable[GIVERESOURCE_INDEX].Max = ontop.Variable[GIVERESOURCE_INDEX].Max; build->Variable[GIVERESOURCE_INDEX].Enable = ontop.Variable[GIVERESOURCE_INDEX].Enable; ontop.Remove(NULL); // Destroy building beneath UnitLost(ontop); UnitClearOrders(ontop); ontop.Release(); } } // Must set action before placing, otherwise it will incorrectly mark radar delete build->CurrentOrder(); build->Orders[0] = COrder::NewActionBuilt(unit, *build); UpdateUnitSightRange(*build); // Must place after previous for map flags build->Place(this->goalPos); // HACK: the building is not ready yet build->Player->UnitTypesCount[type.Slot]--; if (build->Active) { build->Player->UnitTypesAiActiveCount[type.Slot]--; } // We need somebody to work on it. if (!type.BoolFlag[BUILDEROUTSIDE_INDEX].value) { UnitShowAnimation(unit, unit.Type->Animations->Still); unit.Remove(build); this->State = State_BuildFromInside; if (unit.Selected) { SelectedUnitChanged(); } } else { this->State = State_BuildFromOutside; this->BuildingUnit = build; unit.Direction = DirectionToHeading(build->tilePos - unit.tilePos); UnitUpdateHeading(unit); } return true; }
//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; }
//Wyrmgus start //static int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) //Wyrmgus end { const CUnitType &oldtype = *unit.Type; if (&oldtype == &newtype) { // nothing to do return 1; } const Vec2i pos = unit.tilePos + oldtype.GetHalfTileSize() - newtype.GetHalfTileSize(); CUnit *container = unit.Container; //Wyrmgus start /* if (container) { MapUnmarkUnitSight(unit); } else { SaveSelection(); unit.Remove(NULL); if (!UnitTypeCanBeAt(newtype, pos)) { unit.Place(unit.tilePos); RestoreSelection(); // FIXME unit is not modified, try later ? return 0; } } */ if (!SaveGameLoading) { if (container) { MapUnmarkUnitSight(unit); } else { SaveSelection(); unit.Remove(NULL); if (!UnitTypeCanBeAt(newtype, pos)) { unit.Place(unit.tilePos); RestoreSelection(); // FIXME unit is not modified, try later ? return 0; } } } //Wyrmgus end CPlayer &player = *unit.Player; player.UnitTypesCount[oldtype.Slot]--; player.UnitTypesCount[newtype.Slot]++; if (unit.Active) { player.UnitTypesAiActiveCount[oldtype.Slot]--; player.UnitTypesAiActiveCount[newtype.Slot]++; } //Wyrmgus start if (unit.Character == NULL) { player.UnitTypesNonHeroCount[oldtype.Slot]--; player.UnitTypesNonHeroCount[newtype.Slot]++; } //Wyrmgus end player.Demand += newtype.Stats[player.Index].Variables[DEMAND_INDEX].Value - oldtype.Stats[player.Index].Variables[DEMAND_INDEX].Value; player.Supply += newtype.Stats[player.Index].Variables[SUPPLY_INDEX].Value - oldtype.Stats[player.Index].Variables[SUPPLY_INDEX].Value; // Change resource limit for (int i = 0; i < MaxCosts; ++i) { if (player.MaxResources[i] != -1) { player.MaxResources[i] += newtype.Stats[player.Index].Storing[i] - oldtype.Stats[player.Index].Storing[i]; player.SetResource(i, player.StoredResources[i], STORE_BUILDING); } } // adjust Variables with percent. const CUnitStats &newstats = newtype.Stats[player.Index]; //Wyrmgus start const CUnitStats &oldstats = oldtype.Stats[player.Index]; //Wyrmgus end for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) { //Wyrmgus start /* if (unit.Variable[i].Max && unit.Variable[i].Value) { unit.Variable[i].Value = newstats.Variables[i].Max * unit.Variable[i].Value / unit.Variable[i].Max; } else { unit.Variable[i].Value = newstats.Variables[i].Value; } if (i == KILL_INDEX || i == XP_INDEX) { unit.Variable[i].Value = unit.Variable[i].Max; } else { unit.Variable[i].Max = newstats.Variables[i].Max; unit.Variable[i].Increase = newstats.Variables[i].Increase; unit.Variable[i].Enable = newstats.Variables[i].Enable; } */ if (i == LEVEL_INDEX) { //if the unit's level changed in accordance to the difference between the levels of the two unit types, then its level change would be duplicated when leveling up, so let's skip the level variable here continue; } if (unit.Variable[i].Max && unit.Variable[i].Value) { unit.Variable[i].Value += newstats.Variables[i].Max - oldstats.Variables[i].Max; } else { unit.Variable[i].Value += newstats.Variables[i].Value - oldstats.Variables[i].Value; } if (i == KILL_INDEX || i == XP_INDEX) { unit.Variable[i].Value = unit.Variable[i].Max; } else { unit.Variable[i].Max += newstats.Variables[i].Max - oldstats.Variables[i].Max; unit.Variable[i].Increase += newstats.Variables[i].Increase - oldstats.Variables[i].Increase; unit.Variable[i].Enable = newstats.Variables[i].Enable; } //Wyrmgus end } //Wyrmgus start for (size_t i = 0; i < UnitTypes.size(); ++i) { if (newstats.UnitStock[i] != oldstats.UnitStock[i]) { unit.UnitStock[i] += newstats.UnitStock[i] - oldstats.UnitStock[i]; unit.UnitStock[i] = std::max(unit.UnitStock[i], 0); } } //Wyrmgus end //Wyrmgus start //change variation if upgrading (new unit type may have different variations) unit.ChooseVariation(&newtype); for (int i = 0; i < MaxImageLayers; ++i) { unit.ChooseVariation(&newtype, false, i); } //Wyrmgus end unit.Type = const_cast<CUnitType *>(&newtype); unit.Stats = &unit.Type->Stats[player.Index]; //Wyrmgus start //deequip the current equipment if they are incompatible with the new unit type for (int i = 0; i < MaxItemSlots; ++i) { for (size_t j = 0; j < unit.EquippedItems[i].size(); ++j) { if (!unit.CanEquipItemClass(unit.EquippedItems[i][j]->Type->ItemClass)) { unit.DeequipItem(*unit.EquippedItems[i][j]); } } } //Wyrmgus end //Wyrmgus start //change personal name if new unit type's civilization is different from old unit type's civilization if (unit.Character == NULL && !oldtype.Civilization.empty() && !newtype.Civilization.empty() && oldtype.Civilization != newtype.Civilization) { unit.UpdatePersonalName(); } //Wyrmgus end if (newtype.CanCastSpell && !unit.AutoCastSpell) { unit.AutoCastSpell = new char[SpellTypeTable.size()]; unit.SpellCoolDownTimers = new int[SpellTypeTable.size()]; memset(unit.AutoCastSpell, 0, SpellTypeTable.size() * sizeof(char)); memset(unit.SpellCoolDownTimers, 0, SpellTypeTable.size() * sizeof(int)); } UpdateForNewUnit(unit, 1); //Wyrmgus start /* // Update Possible sight range change UpdateUnitSightRange(unit); if (!container) { unit.Place(pos); RestoreSelection(); } else { MapMarkUnitSight(unit); } */ if (!SaveGameLoading) { // Update Possible sight range change UpdateUnitSightRange(unit); if (!container) { unit.Place(pos); RestoreSelection(); } else { MapMarkUnitSight(unit); //Wyrmgus start //if unit has a container, update the container's attack range, as the unit's range may have been changed with the upgrade container->UpdateContainerAttackRange(); //Wyrmgus end } } //Wyrmgus end //Wyrmgus start //update the unit's XP required, as its level or points may have changed unit.UpdateXPRequired(); //Wyrmgus end //Wyrmgus start /* // // Update possible changed buttons. // if (IsOnlySelected(unit) || &player == ThisPlayer) { // could affect the buttons of any selected unit SelectedUnitChanged(); } */ if (!SaveGameLoading) { // // Update possible changed buttons. // if (IsOnlySelected(unit) || &player == ThisPlayer) { // could affect the buttons of any selected unit SelectedUnitChanged(); } } //Wyrmgus end return 1; }
/* 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; } } } }
/** ** 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; }
/** ** 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; }
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; }
/** ** Transform a unit in another. ** ** @param unit unit to transform. ** @param newtype new type of the unit. ** ** @return 0 on error, 1 if nothing happens, 2 else. */ static int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) { const CUnitType &oldtype = *unit.Type; if (&oldtype == &newtype) { // nothing to do return 1; } const Vec2i pos = unit.tilePos + oldtype.GetHalfTileSize() - newtype.GetHalfTileSize(); CUnit *container = unit.Container; if (container) { MapUnmarkUnitSight(unit); } else { SaveSelection(); unit.Remove(NULL); if (!UnitTypeCanBeAt(newtype, pos)) { unit.Place(unit.tilePos); RestoreSelection(); // FIXME unit is not modified, try later ? return 0; } } CPlayer &player = *unit.Player; player.UnitTypesCount[oldtype.Slot]--; player.UnitTypesCount[newtype.Slot]++; player.Demand += newtype.Demand - oldtype.Demand; player.Supply += newtype.Supply - oldtype.Supply; // Change resource limit for (int i = 0; i < MaxCosts; ++i) { if (player.MaxResources[i] != -1) { player.MaxResources[i] += newtype.Stats[player.Index].Storing[i] - oldtype.Stats[player.Index].Storing[i]; player.SetResource(i, player.StoredResources[i], STORE_BUILDING); } } // adjust Variables with percent. const CUnitStats &newstats = newtype.Stats[player.Index]; for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) { if (unit.Variable[i].Max && unit.Variable[i].Value) { unit.Variable[i].Value = newstats.Variables[i].Max * unit.Variable[i].Value / unit.Variable[i].Max; } else { unit.Variable[i].Value = newstats.Variables[i].Value; } unit.Variable[i].Max = newstats.Variables[i].Max; unit.Variable[i].Increase = newstats.Variables[i].Increase; unit.Variable[i].Enable = newstats.Variables[i].Enable; } unit.Type = const_cast<CUnitType *>(&newtype); unit.Stats = &unit.Type->Stats[player.Index]; if (newtype.CanCastSpell && !unit.AutoCastSpell) { unit.AutoCastSpell = new char[SpellTypeTable.size()]; memset(unit.AutoCastSpell, 0, SpellTypeTable.size() * sizeof(char)); } UpdateForNewUnit(unit, 1); // Update Possible sight range change UpdateUnitSightRange(unit); if (!container) { unit.Place(pos); RestoreSelection(); } else { MapMarkUnitSight(unit); } // // Update possible changed buttons. // if (IsOnlySelected(unit) || &player == ThisPlayer) { // could affect the buttons of any selected unit SelectedUnitChanged(); } return 1; }
/** ** Cast capture. ** ** @param caster Unit that casts the spell ** @param spell Spell-type pointer ** @param target Target unit that spell is addressed to ** @param goalPos coord of target spot when/if target does not exist ** ** @return =!0 if spell should be repeated, 0 if not */ /* virtual */ int Spell_Capture::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &/*goalPos*/) { if (!target || caster.Player == target->Player) { return 0; } if (this->DamagePercent) { if ((100 * target->Variable[HP_INDEX].Value) / target->Variable[HP_INDEX].Max > this->DamagePercent && target->Variable[HP_INDEX].Value > this->Damage) { HitUnit(&caster, *target, this->Damage); if (this->SacrificeEnable) { // No corpse. caster.Remove(NULL); caster.Release(); } return 1; } } caster.Player->Score += target->Variable[POINTS_INDEX].Value; if (caster.IsEnemy(*target)) { if (target->Type->Building) { caster.Player->TotalRazings++; } else { caster.Player->TotalKills++; } //Wyrmgus start caster.Player->UnitTypeKills[target->Type->Slot]++; /* if (UseHPForXp) { caster.Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value; } else { caster.Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value; } caster.Variable[XP_INDEX].Value = caster.Variable[XP_INDEX].Max; */ //distribute experience between nearby units belonging to the same player if (!target->Type->BoolFlag[BUILDING_INDEX].value) { std::vector<CUnit *> table; SelectAroundUnit(caster, 6, table, MakeAndPredicate(HasSamePlayerAs(*caster.Player), IsNotBuildingType())); if (UseHPForXp) { caster.Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value / (table.size() + 1); } else { caster.Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value / (table.size() + 1); } caster.Variable[XP_INDEX].Value = caster.Variable[XP_INDEX].Max; caster.XPChanged(); for (size_t i = 0; i != table.size(); ++i) { if (UseHPForXp) { table[i]->Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value / (table.size() + 1); } else { table[i]->Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value / (table.size() + 1); } table[i]->Variable[XP_INDEX].Value = table[i]->Variable[XP_INDEX].Max; table[i]->XPChanged(); } } //Wyrmgus end caster.Variable[KILL_INDEX].Value++; caster.Variable[KILL_INDEX].Max++; caster.Variable[KILL_INDEX].Enable = 1; } target->ChangeOwner(*caster.Player); UnitClearOrders(*target); 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); } } if (this->SacrificeEnable) { // No corpse. caster.Remove(NULL); caster.Release(); } else { caster.Variable[MANA_INDEX].Value -= spell.ManaCost; } return 0; }