/** ** Auto attack nearby units if possible */ bool AutoAttack(CUnit &unit) { if (unit.Type->CanAttack == false) { return false; } // Normal units react in reaction range. CUnit *goal = AttackUnitsInReactRange(unit); if (goal == NULL) { return false; } COrder *savedOrder = NULL; if (unit.CurrentAction() == UnitActionStill) { savedOrder = COrder::NewActionAttack(unit, unit.tilePos); } else if (unit.CanStoreOrder(unit.CurrentOrder())) { savedOrder = unit.CurrentOrder()->Clone(); } // Weak goal, can choose other unit, come back after attack CommandAttack(unit, goal->tilePos, NULL, FlushCommands); if (savedOrder != NULL) { unit.SavedOrder = savedOrder; } return true; }
/** ** @brief Auto attack nearby units if possible */ bool AutoAttack(CUnit &unit) { //Wyrmgus start // if (unit.Type->CanAttack == false) { if (unit.CanAttack() == false) { //Wyrmgus end return false; } // Normal units react in reaction range. CUnit *goal = AttackUnitsInReactRange(unit); if (goal == nullptr) { return false; } COrder *savedOrder = nullptr; if (unit.CurrentAction() == UnitActionStill) { //Wyrmgus start // savedOrder = COrder::NewActionAttack(unit, unit.tilePos); savedOrder = COrder::NewActionAttack(unit, unit.tilePos, unit.MapLayer->ID); //Wyrmgus end } else if (unit.CanStoreOrder(unit.CurrentOrder())) { savedOrder = unit.CurrentOrder()->Clone(); } // Weak goal, can choose other unit, come back after attack CommandAttack(unit, goal->tilePos, nullptr, FlushCommands, goal->MapLayer->ID); if (savedOrder != nullptr) { unit.SavedOrder = savedOrder; } return true; }
/** ** Auto repair a unit if possible ** ** @return true if the unit is repairing, false otherwise */ bool AutoRepair(CUnit &unit) { const int repairRange = unit.Type->DefaultStat.Variables[AUTOREPAIRRANGE_INDEX].Value; if (unit.AutoRepair == false || repairRange == 0) { return false; } CUnit *repairedUnit = UnitToRepairInRange(unit, repairRange); if (repairedUnit == NULL) { return false; } const Vec2i invalidPos(-1, -1); COrder *savedOrder = NULL; if (unit.CanStoreOrder(unit.CurrentOrder())) { savedOrder = unit.CurrentOrder()->Clone(); } //Command* will clear unit.SavedOrder CommandRepair(unit, invalidPos, repairedUnit, FlushCommands); if (savedOrder != NULL) { unit.SavedOrder = savedOrder; } return true; }
/** ** Auto repair a unit if possible ** ** @return true if the unit is repairing, false otherwise */ bool AutoRepair(CUnit &unit) { //Wyrmgus start // const int repairRange = unit.Type->DefaultStat.Variables[AUTOREPAIRRANGE_INDEX].Value; const int repairRange = unit.Variable[AUTOREPAIRRANGE_INDEX].Value; //Wyrmgus end if (unit.AutoRepair == false || repairRange == 0) { return false; } CUnit *repairedUnit = UnitToRepairInRange(unit, repairRange); if (repairedUnit == nullptr) { return false; } const Vec2i invalidPos(-1, -1); COrder *savedOrder = nullptr; if (unit.CanStoreOrder(unit.CurrentOrder())) { savedOrder = unit.CurrentOrder()->Clone(); } //Command* will clear unit.SavedOrder CommandRepair(unit, invalidPos, repairedUnit, FlushCommands, repairedUnit->MapLayer->ID); if (savedOrder != nullptr) { unit.SavedOrder = savedOrder; } return true; }
/** ** Check if the spell can be auto cast and cast it. ** ** @param caster Unit who can cast the spell. ** @param spell Spell-type pointer. ** ** @return 1 if spell is casted, 0 if not. */ int AutoCastSpell(CUnit &caster, const SpellType &spell) { // Check for mana and cooldown time, trivial optimization. if (!SpellIsAvailable(*caster.Player, spell.Slot) || caster.Variable[MANA_INDEX].Value < spell.ManaCost || caster.SpellCoolDownTimers[spell.Slot]) { return 0; } Target *target = SelectTargetUnitsOfAutoCast(caster, spell); if (target == NULL) { return 0; } else { // Save previous order COrder *savedOrder = NULL; if (caster.CurrentAction() != UnitActionStill && caster.CanStoreOrder(caster.CurrentOrder())) { savedOrder = caster.CurrentOrder()->Clone(); } // Must move before ? CommandSpellCast(caster, target->targetPos, target->Unit, spell, FlushCommands); delete target; if (savedOrder != NULL) { caster.SavedOrder = savedOrder; } } return 1; }
/** ** Repair a unit. ** ** @param unit unit repairing ** @param goal unit being repaired ** ** @return true when action is finished/canceled. */ bool COrder_Repair::RepairUnit(const CUnit &unit, CUnit &goal) { CPlayer &player = *unit.Player; if (goal.CurrentAction() == UnitActionBuilt) { COrder_Built &order = *static_cast<COrder_Built *>(goal.CurrentOrder()); order.ProgressHp(goal, 100 * this->RepairCycle); this->RepairCycle = 0; if (ResourcesMultiBuildersMultiplier && SubRepairCosts(unit, player, goal)) { return true; } return false; } if (goal.Variable[HP_INDEX].Value >= goal.Variable[HP_INDEX].Max) { return true; } Assert(goal.Stats->Variables[HP_INDEX].Max); if (SubRepairCosts(unit, player, goal)) { return true; } goal.Variable[HP_INDEX].Value += goal.Type->RepairHP; if (goal.Variable[HP_INDEX].Value >= goal.Variable[HP_INDEX].Max) { goal.Variable[HP_INDEX].Value = goal.Variable[HP_INDEX].Max; return true; } return false; }
/** ** Show Map Location ** ** @param l Lua state. */ static int CclShowMapLocation(lua_State *l) { // Put a unit on map, use its properties, except for // what is listed below LuaCheckArgs(l, 4); const char *unitname = LuaToString(l, 5); CUnitType* unitType = UnitTypeByIdent(unitname); if (!unitType) { DebugPrint("Unable to find UnitType '%s'" _C_ unitname); return 0; } CUnit *target = MakeUnit(*unitType, ThisPlayer); if (target != NoUnitP) { target->CurrentOrder()->Action = UnitActionStill; target->Variable[HP_INDEX].Value = 0; target->tilePos.x = LuaToNumber(l, 1); target->tilePos.y = LuaToNumber(l, 2); target->TTL = GameCycle + LuaToNumber(l, 4); target->CurrentSightRange = LuaToNumber(l, 3); MapMarkUnitSight(*target); } else { DebugPrint("Unable to allocate Unit"); } return 0; }
/* virtual */ void COrder_Train::UpdateUnitVariables(CUnit &unit) const { Assert(unit.CurrentOrder() == this); unit.Variable[TRAINING_INDEX].Value = this->Ticks; unit.Variable[TRAINING_INDEX].Max = this->Type->Stats[unit.Player->Index].Costs[TimeCost]; }
/* 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())); } }
/** ** Cancel the building construction, or kill a unit. ** ** @param unit pointer to unit. */ void CommandDismiss(CUnit &unit) { // Check if building is still under construction? (NETWORK!) if (unit.CurrentAction() == UnitActionBuilt) { unit.CurrentOrder()->Cancel(unit); } else { DebugPrint("Suicide unit ... \n"); LetUnitDie(unit, true); } ClearSavedAction(unit); }
/* virtual */ void COrder_Built::UpdateUnitVariables(CUnit &unit) const { Assert(unit.CurrentOrder() == this); unit.Variable[BUILD_INDEX].Value = this->ProgressCounter; unit.Variable[BUILD_INDEX].Max = unit.Type->Stats[unit.Player->Index].Costs[TimeCost] * 600; // This should happen when building unit with several peons // Maybe also with only one. // FIXME : Should be better to fix it in action_{build,repair}.c ? unit.Variable[BUILD_INDEX].Value = std::min(unit.Variable[BUILD_INDEX].Max, unit.Variable[BUILD_INDEX].Value); }
/** ** Cancel Building researching. ** ** @param unit Pointer to unit. */ void CommandCancelResearch(CUnit &unit) { // Check if unit is still researching? (NETWORK!) if (unit.CurrentAction() == UnitActionResearch) { unit.CurrentOrder()->Cancel(unit); RemoveOrder(unit, 0); if (!Selected.empty()) { SelectedUnitChanged(); } } ClearSavedAction(unit); }
/** ** Cancel building upgrading to. ** ** @param unit pointer to unit. */ void CommandCancelUpgradeTo(CUnit &unit) { // Check if unit is still upgrading? (NETWORK!) if (unit.CurrentAction() == UnitActionUpgradeTo) { unit.CurrentOrder()->Cancel(unit); RemoveOrder(unit, 0); if (Selected) { SelectedUnitChanged(); } } ClearSavedAction(unit); }
static bool IsReadyToRepair(const CUnit &unit) { if (unit.IsIdle()) { return true; } else if (unit.Orders.size() == 1 && unit.CurrentAction() == UnitActionResource) { COrder_Resource &order = *static_cast<COrder_Resource *>(unit.CurrentOrder()); if (order.IsGatheringStarted() == false) { return true; } } return false; }
static bool DrawUnitInfo_single_selection(const CUnit &unit) { switch (unit.CurrentAction()) { case UnitActionTrain: { // Building training units. DrawUnitInfo_Training(unit); return true; } case UnitActionUpgradeTo: { // Building upgrading to better type. if (UI.UpgradingButton) { const COrder_UpgradeTo &order = *static_cast<COrder_UpgradeTo *>(unit.CurrentOrder()); CIcon &icon = *order.GetUnitType().Icon.Icon; unsigned int flag = (ButtonAreaUnderCursor == ButtonAreaUpgrading && ButtonUnderCursor == 0) ? (IconActive | (MouseButtons & LeftButton)) : 0; const PixelPos pos(UI.UpgradingButton->X, UI.UpgradingButton->Y); icon.DrawUnitIcon(*UI.UpgradingButton->Style, flag, pos, "", unit.RescuedFrom ? GameSettings.Presets[unit.RescuedFrom->Index].PlayerColor : GameSettings.Presets[unit.Player->Index].PlayerColor); } return true; } case UnitActionResearch: { // Building research new technology. if (UI.ResearchingButton) { COrder_Research &order = *static_cast<COrder_Research *>(unit.CurrentOrder()); CIcon &icon = *order.GetUpgrade().Icon; int flag = (ButtonAreaUnderCursor == ButtonAreaResearching && ButtonUnderCursor == 0) ? (IconActive | (MouseButtons & LeftButton)) : 0; PixelPos pos(UI.ResearchingButton->X, UI.ResearchingButton->Y); icon.DrawUnitIcon(*UI.ResearchingButton->Style, flag, pos, "", unit.RescuedFrom ? GameSettings.Presets[unit.RescuedFrom->Index].PlayerColor : GameSettings.Presets[unit.Player->Index].PlayerColor); } return true; } default: return false; } }
static void DrawUnitInfo_Training(const CUnit &unit) { if (unit.Orders.size() == 1 || unit.Orders[1]->Action != UnitActionTrain) { if (!UI.SingleTrainingText.empty()) { CLabel label(*UI.SingleTrainingFont); label.Draw(UI.SingleTrainingTextX, UI.SingleTrainingTextY, UI.SingleTrainingText); } if (UI.SingleTrainingButton) { const COrder_Train &order = *static_cast<COrder_Train *>(unit.CurrentOrder()); CIcon &icon = *order.GetUnitType().Icon.Icon; //Wyrmgus start // const unsigned int flags = (ButtonAreaUnderCursor == ButtonAreaTraining && ButtonUnderCursor == 0) ? unsigned int flags = (ButtonAreaUnderCursor == ButtonAreaTraining && ButtonUnderCursor == 0) ? //Wyrmgus end (IconActive | (MouseButtons & LeftButton)) : 0; //Wyrmgus start flags |= IconCommandButton; //Wyrmgus end const PixelPos pos(UI.SingleTrainingButton->X, UI.SingleTrainingButton->Y); icon.DrawUnitIcon(*UI.SingleTrainingButton->Style, flags, pos, "", unit.RescuedFrom ? unit.RescuedFrom->Index : unit.Player->Index); } } else { if (!UI.TrainingText.empty()) { CLabel label(*UI.TrainingFont); label.Draw(UI.TrainingTextX, UI.TrainingTextY, UI.TrainingText); } if (!UI.TrainingButtons.empty()) { for (size_t i = 0; i < unit.Orders.size() && i < UI.TrainingButtons.size(); ++i) { if (unit.Orders[i]->Action == UnitActionTrain) { const COrder_Train &order = *static_cast<COrder_Train *>(unit.Orders[i]); CIcon &icon = *order.GetUnitType().Icon.Icon; //Wyrmgus start // const int flag = (ButtonAreaUnderCursor == ButtonAreaTraining int flag = (ButtonAreaUnderCursor == ButtonAreaTraining //Wyrmgus end && static_cast<size_t>(ButtonUnderCursor) == i) ? (IconActive | (MouseButtons & LeftButton)) : 0; const PixelPos pos(UI.TrainingButtons[i].X, UI.TrainingButtons[i].Y); //Wyrmgus start flag |= IconCommandButton; //Wyrmgus end icon.DrawUnitIcon(*UI.TrainingButtons[i].Style, flag, pos, "", unit.RescuedFrom ? unit.RescuedFrom->Index : unit.Player->Index); } } } } }
int GetNumWaitingWorkers(const CUnit &mine) { int ret = 0; CUnit *worker = mine.Resource.Workers; for (int i = 0; NULL != worker; worker = worker->NextWorker, ++i) { Assert(worker->CurrentAction() == UnitActionResource); COrder_Resource &order = *static_cast<COrder_Resource *>(worker->CurrentOrder()); if (order.IsGatheringWaiting()) { ret++; } Assert(i <= mine.Resource.Assigned); } return ret; }
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); }
/** ** Make one or more unit leave the transporter. ** ** @return false if action should continue */ bool COrder_Unload::LeaveTransporter(CUnit &transporter) { int stillonboard = 0; // Goal is the specific unit unit that you want to unload. // This can be NULL, in case you want to unload everything. if (this->HasGoal()) { CUnit &goal = *this->GetGoal(); if (goal.Destroyed) { DebugPrint("destroyed unit unloading?\n"); this->ClearGoal(); return true; } transporter.CurrentOrder()->ClearGoal(); // Try to unload the unit. If it doesn't work there is no problem. if (UnloadUnit(transporter, goal)) { this->ClearGoal(); } else { ++stillonboard; } } else { // Unload all units. CUnit *goal = transporter.UnitInside; for (int i = transporter.InsideCount; i; --i, goal = goal->NextContained) { if (goal->Boarded) { if (!UnloadUnit(transporter, *goal)) { ++stillonboard; } } } } if (IsOnlySelected(transporter)) { SelectedUnitChanged(); } // We still have some units to unload, find a piece of free coast. if (stillonboard) { // We tell it to unload at it's current position. This can't be done, // so it will search for a piece of free coast nearby. this->State = 0; return false; } else { return true; } }
void COrder_Built::Boost(CUnit &building, int amount, int varIndex) const { Assert(building.CurrentOrder() == this); const int costs = building.Stats->Costs[TimeCost] * 600; const int progress = this->ProgressCounter; const int newProgress = progress + std::max(1, amount * building.Player->SpeedBuild / SPEEDUP_FACTOR); const int maxValue = building.Variable[varIndex].Max; int ¤tValue = building.Variable[varIndex].Value; // damageValue is the current damage taken by the unit. const int damageValue = (progress * maxValue) / costs - currentValue; // Keep the same level of damage while increasing Value. currentValue = (newProgress * maxValue) / costs - damageValue; currentValue = std::min(currentValue, maxValue); }
/** ** Cancel the training of an unit. ** ** @param unit pointer to unit. ** @param slot slot number to cancel. ** @param type Unit-type to cancel. */ void CommandCancelTraining(CUnit &unit, int slot, const CUnitType *type) { DebugPrint("Cancel %d type: %s\n" _C_ slot _C_ type ? type->Ident.c_str() : "-any-"); ClearSavedAction(unit); // Check if unit is still training 'slot'? (NETWORK!) if (slot == -1) { // Cancel All training while (unit.CurrentAction() == UnitActionTrain) { unit.CurrentOrder()->Cancel(unit); RemoveOrder(unit, 0); } if (unit.Player == ThisPlayer && unit.Selected) { SelectedUnitChanged(); } } else if (unit.Orders.size() <= static_cast<size_t>(slot)) { // Order has moved return; } else if (unit.Orders[slot]->Action != UnitActionTrain) { // Order has moved, we are not training return; } else if (unit.Orders[slot]->Action == UnitActionTrain) { COrder_Train &order = *static_cast<COrder_Train *>(unit.Orders[slot]); // Still training this order, same unit? if (type && &order.GetUnitType() != type) { // Different unit being trained return; } order.Cancel(unit); RemoveOrder(unit, slot); // Update interface. if (unit.Player == ThisPlayer && unit.Selected) { SelectedUnitChanged(); } } }
/** ** Unit upgrades unit! ** ** @param unit Pointer to unit. */ void HandleActionUpgradeTo(CUnit &unit) { if (!unit.SubAction) { // first entry unit.Data.UpgradeTo.Ticks = 0; unit.SubAction = 1; } unit.Type->Animations->Upgrade ? UnitShowAnimation(unit, unit.Type->Animations->Upgrade) : UnitShowAnimation(unit, unit.Type->Animations->Still); if (unit.Wait) { unit.Wait--; return; } CPlayer *player = unit.Player; CUnitType &newtype = *unit.CurrentOrder()->Arg1.Type; const CUnitStats *newstats = &newtype.Stats[player->Index]; // FIXME: Should count down here unit.Data.UpgradeTo.Ticks += SpeedUpgrade; if (unit.Data.UpgradeTo.Ticks < newstats->Costs[TimeCost]) { unit.Wait = CYCLES_PER_SECOND / 6; return; } unit.ClearAction(); unit.State = 0; if (TransformUnitIntoType(unit, newtype) == 0) { player->Notify(NotifyGreen, unit.tilePos.x, unit.tilePos.y, _("Upgrade to %s canceled"), newtype.Name.c_str()); return ; } // Warn AI. if (player->AiEnabled) { AiUpgradeToComplete(unit, newtype); } player->Notify(NotifyGreen, unit.tilePos.x, unit.tilePos.y, _("Upgrade to %s complete"), unit.Type->Name.c_str()); }
/** ** Called if a unit is killed. ** ** @param unit Pointer to unit. */ void AiUnitKilled(CUnit &unit) { DebugPrint("%d: %d(%s) killed\n" _C_ unit.Player->Index _C_ UnitNumber(unit) _C_ unit.Type->Ident.c_str()); Assert(unit.Player->Type != PlayerPerson); if (unit.GroupId) { AiForce &force = unit.Player->Ai->Force[unit.GroupId - 1]; force.Remove(unit); if (force.Size() == 0) { force.Attacking = false; if (!force.Defending && force.State > 0) { DebugPrint("%d: Attack force #%lu was destroyed, giving up\n" _C_ unit.Player->Index _C_(long unsigned int)(&force - & (unit.Player->Ai->Force[0]))); force.Reset(true); } } } unit.CurrentOrder()->AiUnitKilled(unit); }
/* virtual */ bool COrder_Attack::OnAiHitUnit(CUnit &unit, CUnit *attacker, int /*damage*/) { CUnit *goal = this->GetGoal(); if (goal) { if (goal->IsAlive() == false) { this->ClearGoal(); this->goalPos = goal->tilePos; return false; } if (goal == attacker) { return true; } if (goal->CurrentAction() == UnitActionAttack) { const COrder_Attack &order = *static_cast<COrder_Attack *>(goal->CurrentOrder()); if (order.GetGoal() == &unit) { //we already fight with one of attackers; return true; } } } return false; }
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; }
/** ** Repair a unit. ** ** @param unit unit repairing ** @param goal unit being repaired */ static void RepairUnit(CUnit &unit, CUnit &goal) { CPlayer *player; int animlength; int hp; char buf[100]; player = unit.Player; if (goal.CurrentAction() != UnitActionBuilt) { // // Calculate the repair costs. // Assert(goal.Stats->Variables[HP_INDEX].Max); // // Check if enough resources are available // for (int i = 1; i < MaxCosts; ++i) { if (player->Resources[i] < goal.Type->RepairCosts[i]) { snprintf(buf, 100, _("We need more %s for repair!"), DefaultResourceNames[i].c_str()); player->Notify(NotifyYellow, unit.tilePos.x, unit.tilePos.y, buf); if (player->AiEnabled) { // FIXME: call back to AI? unit.CurrentOrder()->ClearGoal(); if (!unit.RestoreOrder()) { unit.ClearAction(); unit.State = 0; } } // FIXME: We shouldn't animate if no resources are available. return; } } // // Subtract the resources // player->SubCosts(goal.Type->RepairCosts); goal.Variable[HP_INDEX].Value += goal.Type->RepairHP; if (goal.Variable[HP_INDEX].Value > goal.Variable[HP_INDEX].Max) { goal.Variable[HP_INDEX].Value = goal.Variable[HP_INDEX].Max; } } else { int costs = goal.Stats->Costs[TimeCost] * 600; // hp is the current damage taken by the unit. hp = (goal.Data.Built.Progress * goal.Variable[HP_INDEX].Max) / costs - goal.Variable[HP_INDEX].Value; // // Calculate the length of the attack (repair) anim. // animlength = unit.Data.Repair.Cycles; unit.Data.Repair.Cycles = 0; // FIXME: implement this below: //unit.Data.Built.Worker->Type->BuilderSpeedFactor; goal.Data.Built.Progress += 100 * animlength * SpeedBuild; // Keep the same level of damage while increasing HP. goal.Variable[HP_INDEX].Value = (goal.Data.Built.Progress * goal.Stats->Variables[HP_INDEX].Max) / costs - hp; if (goal.Variable[HP_INDEX].Value > goal.Variable[HP_INDEX].Max) { goal.Variable[HP_INDEX].Value = goal.Variable[HP_INDEX].Max; } } }
/** ** 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; } }
void AiNewDepotRequest(CUnit &worker) { /* DebugPrint("%d: Worker %d report: Resource [%d] too far from depot, returning time [%d].\n" _C_ worker->Player->Index _C_ worker->Slot _C_ worker->CurrentResource _C_ worker->Data.Move.Cycles ); */ Vec2i pos = {-1, -1}; ResourceInfo *resinfo = worker.Type->ResInfo[worker.CurrentResource]; if (resinfo->TerrainHarvester) { pos = worker.CurrentOrder()->Arg1.Resource.Pos; } else { CUnit *mine = worker.CurrentOrder()->Arg1.Resource.Mine; if (mine) { pos = mine->tilePos; } } if (pos.x != -1 && NULL != FindDepositNearLoc(worker.Player, pos.x, pos.y, 10, worker.CurrentResource)) { /* * New Depot has just be finished and worker just return to old depot * (far away) from new Deopt. */ return; } CUnitType *best_type = NULL; int best_cost = 0; //int best_mask = 0; // Count the already made build requests. int counter[UnitTypeMax]; AiGetBuildRequestsCount(worker.Player->Ai, counter); const int n = AiHelpers.Depots[worker.CurrentResource - 1].size(); for (int i = 0; i < n; ++i) { CUnitType &type = *AiHelpers.Depots[worker.CurrentResource - 1][i]; if (counter[type.Slot]) { // Already ordered. return; } if (!AiRequestedTypeAllowed(worker.Player, type)) { continue; } // Check if resources available. //int needmask = AiCheckUnitTypeCosts(type); int cost = 0; for (int c = 1; c < MaxCosts; ++c) { cost += type.Stats[worker.Player->Index].Costs[c]; } if (best_type == NULL || (cost < best_cost)) { best_type = &type; best_cost = cost; //best_mask = needmask; } } if (best_type) { //if(!best_mask) { AiBuildQueue queue; queue.Type = best_type; queue.Want = 1; queue.Made = 0; queue.X = pos.x; queue.Y = pos.y; worker.Player->Ai->UnitTypeBuilt.push_back(queue); DebugPrint("%d: Worker %d report: Requesting new depot near [%d,%d].\n" _C_ worker.Player->Index _C_ worker.Slot _C_ queue.X _C_ queue.Y ); /* } else { AiPlayer->NeededMask |= best_mask; } */ } }
/** ** Try to move a unit that's in the way */ static void AiMoveUnitInTheWay(CUnit &unit) { static Vec2i dirs[8] = {Vec2i(-1, -1), Vec2i(-1, 0), Vec2i(-1, 1), Vec2i(0, 1), Vec2i(1, 1), Vec2i(1, 0), Vec2i(1, -1), Vec2i(0, -1)}; CUnit *movableunits[16]; Vec2i movablepos[16]; int movablenb; AiPlayer = unit.Player->Ai; // No more than 1 move per 10 cycle ( avoid stressing the pathfinder ) if (GameCycle <= AiPlayer->LastCanNotMoveGameCycle + 10) { return; } const CUnitType &unittype = *unit.Type; const Vec2i u0 = unit.tilePos; const Vec2i u1(u0.x + unittype.TileWidth - 1, u0.y + unittype.TileHeight - 1); movablenb = 0; // Try to make some unit moves around it for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) { CUnit &blocker = **it; if (blocker.IsUnusable()) { continue; } if (!blocker.CanMove() || blocker.Moving) { continue; } if (blocker.Player != unit.Player && blocker.Player->IsAllied(*unit.Player) == false) { continue; } const CUnitType &blockertype = *blocker.Type; if (blockertype.UnitType != unittype.UnitType) { continue; } const Vec2i b0 = blocker.tilePos; const Vec2i b1(b0.x + blockertype.TileWidth - 1, b0.y + blockertype.TileHeight - 1); if (&unit == &blocker) { continue; } // Check for collision if (unit.MapDistanceTo(blocker) >= unit.Type->TileWidth + 1) { continue; } // Move blocker in a rand dir int r = SyncRand() & 7; int trycount = 8; while (trycount > 0) { r = (r + 1) & 7; --trycount; const Vec2i pos = blocker.tilePos + blocker.Type->TileWidth * dirs[r]; // Out of the map => no ! if (!Map.Info.IsPointOnMap(pos)) { continue; } // move to blocker ? => no ! if (pos == u0) { continue; } if (Map.Field(pos)->UnitCache.size() > 0) { continue; } movableunits[movablenb] = &blocker; movablepos[movablenb] = pos; ++movablenb; trycount = 0; } if (movablenb >= 16) { break; } } // Don't move more than 1 unit. if (movablenb) { const int index = SyncRand() % movablenb; COrder *savedOrder = NULL; if (movableunits[index]->IsIdle() == false) { if (unit.CanStoreOrder(unit.CurrentOrder())) { savedOrder = unit.CurrentOrder()->Clone(); } } CommandMove(*movableunits[index], movablepos[index], FlushCommands); if (savedOrder != NULL) { unit.SavedOrder = savedOrder; } AiPlayer->LastCanNotMoveGameCycle = GameCycle; } }
int ParseAnimInt(const CUnit &unit, const char *parseint) { char s[100]; const CUnit *goal = &unit; if (!strlen(parseint)) { return 0; } strcpy(s, parseint); char *cur = &s[2]; if (s[0] == 'v' || s[0] == 't') { //unit variable detected if (s[0] == 't') { if (unit.CurrentOrder()->HasGoal()) { goal = unit.CurrentOrder()->GetGoal(); } else { return 0; } } char *next = strchr(cur, '.'); if (next == NULL) { fprintf(stderr, "Need also specify the variable '%s' tag \n", cur); ExitFatal(1); } else { *next = '\0'; } const int index = UnitTypeVar.VariableNameLookup[cur];// User variables if (index == -1) { if (!strcmp(cur, "ResourcesHeld")) { return goal->ResourcesHeld; } else if (!strcmp(cur, "ResourceActive")) { return goal->Resource.Active; } else if (!strcmp(cur, "_Distance")) { return unit.MapDistanceTo(*goal); } fprintf(stderr, "Bad variable name '%s'\n", cur); ExitFatal(1); } if (!strcmp(next + 1, "Value")) { return goal->Variable[index].Value; } else if (!strcmp(next + 1, "Max")) { return goal->Variable[index].Max; } else if (!strcmp(next + 1, "Increase")) { return goal->Variable[index].Increase; } else if (!strcmp(next + 1, "Enable")) { return goal->Variable[index].Enable; } else if (!strcmp(next + 1, "Percent")) { return goal->Variable[index].Value * 100 / goal->Variable[index].Max; } return 0; } else if (s[0] == 'b' || s[0] == 'g') { //unit bool flag detected if (s[0] == 'g') { if (unit.CurrentOrder()->HasGoal()) { goal = unit.CurrentOrder()->GetGoal(); } else { return 0; } } const int index = UnitTypeVar.BoolFlagNameLookup[cur];// User bool flags if (index == -1) { fprintf(stderr, "Bad bool-flag name '%s'\n", cur); ExitFatal(1); } return goal->Type->BoolFlag[index].value; } else if (s[0] == 's') { //spell type detected Assert(goal->CurrentAction() == UnitActionSpellCast); const COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(goal->CurrentOrder()); const SpellType &spell = order.GetSpell(); if (!strcmp(spell.Ident.c_str(), cur)) { return 1; } return 0; } else if (s[0] == 'S') { // check if autocast for this spell available const SpellType *spell = SpellTypeByIdent(cur); if (!spell) { fprintf(stderr, "Invalid spell: '%s'\n", cur); ExitFatal(1); } if (unit.AutoCastSpell[spell->Slot]) { return 1; } return 0; } else if (s[0] == 'p') { //player variable detected char *next; if (*cur == '(') { ++cur; char *end = strchr(cur, ')'); if (end == NULL) { fprintf(stderr, "ParseAnimInt: expected ')'\n"); ExitFatal(1); } *end = '\0'; next = end + 1; } else { next = strchr(cur, '.'); } if (next == NULL) { fprintf(stderr, "Need also specify the %s player's property\n", cur); ExitFatal(1); } else { *next = '\0'; } char *arg = strchr(next + 1, '.'); if (arg != NULL) { *arg = '\0'; } return GetPlayerData(ParseAnimPlayer(unit, cur), next + 1, arg + 1); } else if (s[0] == 'r') { //random value char *next = strchr(cur, '.'); if (next == NULL) { return SyncRand(atoi(cur) + 1); } else { *next = '\0'; const int min = atoi(cur); return min + SyncRand(atoi(next + 1) - min + 1); } } else if (s[0] == 'l') { //player number return ParseAnimPlayer(unit, cur); } // Check if we trying to parse a number Assert(isdigit(s[0]) || s[0] == '-'); return atoi(parseint); }