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); }
/** ** 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()); }
/** ** 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; } }
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; }