00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00031
00032
00033
00034
00035
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038
00039 #include "stratagus.h"
00040 #include "unittype.h"
00041 #include "animation.h"
00042 #include "player.h"
00043 #include "unit.h"
00044 #include "unit_cache.h"
00045 #include "sound.h"
00046 #include "actions.h"
00047 #include "map.h"
00048 #include "ai.h"
00049 #include "interface.h"
00050 #include "pathfinder.h"
00051 #include "construct.h"
00052
00053
00054
00055
00056
00061 static int GetProductionCost(CUnitType *type)
00062 {
00063 int *costs = type->ProductionCosts;
00064 return costs[EnergyCost] ? costs[EnergyCost] : costs[MagmaCost];
00065 }
00066
00072 static void UpdateConstructionFrame(CUnit *unit)
00073 {
00074 CConstructionFrame *cframe;
00075 CConstructionFrame *tmp;
00076 int percent = unit->Data.Built.Progress * 100 / GetProductionCost(unit->Type);
00077
00078
00079 cframe = tmp = unit->Type->Construction->Frames;
00080 while (tmp) {
00081 if (percent < tmp->Percent) {
00082 break;
00083 }
00084 cframe = tmp;
00085 tmp = tmp->Next;
00086 }
00087
00088
00089 if (cframe != unit->Data.Built.Frame) {
00090 unit->Data.Built.Frame = cframe;
00091 if (unit->Frame < 0) {
00092 unit->Frame = -cframe->Frame - 1;
00093 } else {
00094 unit->Frame = cframe->Frame;
00095 }
00096 }
00097 }
00098
00104 static void MoveToLocation(CUnit *unit)
00105 {
00106
00107 if (!unit->SubAction) {
00108 unit->SubAction = 1;
00109 NewResetPath(unit);
00110 }
00111
00112 if (unit->Wait) {
00113
00114 unit->Wait--;
00115 return;
00116 }
00117
00118 switch (DoActionMove(unit)) {
00119 case PF_UNREACHABLE:
00120
00121
00122
00123 if (unit->SubAction++ < 10) {
00124
00125
00126 unit->Wait = 10;
00127 return;
00128 }
00129
00130 unit->Player->Notify(NotifyYellow, unit->X, unit->Y,
00131 _("You cannot reach building place"));
00132 if (unit->Player->AiEnabled) {
00133 AiCanNotReach(unit, unit->Orders[0]->Type);
00134 }
00135
00136 unit->ClearAction();
00137 return;
00138
00139 case PF_REACHED:
00140 unit->SubAction = 20;
00141 return;
00142
00143 default:
00144
00145 return;
00146 }
00147 }
00148
00152 static CUnit *CheckAlreadyBuilding(CUnit *unit, CUnitType *type, int x, int y)
00153 {
00154 CUnit *table[UnitMax];
00155 int n;
00156
00157 n = UnitCache.Select(x, y, table, UnitMax);
00158 for (int i = 0; i < n; ++i) {
00159 if (!table[i]->Destroyed && table[i]->Type == type &&
00160 (table[i]->Player == unit->Player || unit->IsAllied(table[i]))) {
00161 return table[i];
00162 }
00163 }
00164 return NULL;
00165 }
00166
00172 static CUnit *CheckCanBuild(CUnit *unit)
00173 {
00174 int x;
00175 int y;
00176 CUnitType *type;
00177 CUnit *ontop;
00178
00179 if (unit->Wait) {
00180
00181 unit->Wait--;
00182 return NULL;
00183 }
00184
00185 x = unit->Orders[0]->X;
00186 y = unit->Orders[0]->Y;
00187 type = unit->Orders[0]->Type;
00188
00189
00190
00191
00192 if ((ontop = CanBuildUnitType(unit, type, x, y, 1)) == NULL) {
00193 if ((ontop = CheckAlreadyBuilding(unit, type, x, y)) != NULL) {
00194 unit->Orders[0]->Init();
00195 unit->Orders[0]->Action = UnitActionRepair;
00196 unit->Orders[0]->Goal = ontop;
00197 unit->Orders[0]->Goal->RefsIncrease();
00198 unit->Orders[0]->Range = unit->Type->RepairRange;
00199 unit->SubAction = 0;
00200 return NULL;
00201 }
00202
00203
00204
00205
00206 if (unit->SubAction++ < 30) {
00207
00208
00209 unit->Wait = 10;
00210 return NULL;
00211 }
00212
00213 unit->Player->Notify(NotifyYellow, unit->X, unit->Y,
00214 _("You cannot build %s here"), type->Name.c_str());
00215 if (unit->Player->AiEnabled) {
00216 AiCanNotBuild(unit, type);
00217 }
00218
00219 unit->ClearAction();
00220 return NULL;
00221 }
00222
00223
00224
00225
00226 if (unit->Player->CheckLimits(type) < 0) {
00227 unit->Player->Notify(NotifyYellow, unit->X, unit->Y,
00228 _("Can't build more units %s"), type->Name.c_str());
00229 if (unit->Player->AiEnabled) {
00230 AiCanNotBuild(unit, type);
00231 }
00232
00233 unit->ClearAction();
00234 return NULL;
00235 }
00236
00237 return ontop;
00238 }
00239
00243 static void StartBuilding(CUnit *unit, CUnit *ontop)
00244 {
00245 int x;
00246 int y;
00247 CUnitType *type;
00248 CUnit *build;
00249
00250 x = unit->Orders[0]->X;
00251 y = unit->Orders[0]->Y;
00252 type = unit->Orders[0]->Type;
00253
00254 build = MakeUnit(type, unit->Player);
00255
00256
00257 if (build == NoUnitP) {
00258
00259 unit->Player->Notify(NotifyYellow, unit->X, unit->Y,
00260 _("Unable to create building %s"), type->Name.c_str());
00261 if (unit->Player->AiEnabled) {
00262 AiCanNotBuild(unit, type);
00263 }
00264
00265 unit->ClearAction();
00266 return;
00267 }
00268
00269 if (!type->BuilderOutside) {
00270
00271 int costs[MaxCosts];
00272 CalculateRequestedAmount(build->Type, build->Type->ProductionCosts, costs);
00273 build->Player->AddToUnitsConsumingResources(build, costs);
00274 }
00275
00276 build->Constructed = 1;
00277 build->CurrentSightRange = 0;
00278
00279
00280 if (ontop != unit) {
00281 CBuildRestrictionOnTop *b;
00282
00283 build->ProductionEfficiency = ontop->Type->ProductionEfficiency;
00284 b = static_cast<CBuildRestrictionOnTop *>(OnTopDetails(build, ontop->Type));
00285 Assert(b);
00286 if (b->ReplaceOnBuild) {
00287
00288 memcpy(build->ResourcesHeld, ontop->ResourcesHeld, sizeof(build->ResourcesHeld));
00289 ontop->Remove(NULL);
00290 UnitLost(ontop);
00291 UnitClearOrders(ontop);
00292 ontop->Release();
00293 }
00294 }
00295
00296
00297 build->Orders[0]->Action = UnitActionBuilt;
00298
00299
00300 build->Place(x, y);
00301 if (!type->BuilderOutside) {
00302 build->CurrentSightRange = 1;
00303 }
00304
00305
00306 build->Player->UnitTypesCount[type->Slot]--;
00307
00308
00309 build->Data.Built.Progress = 0;
00310 build->Variable[HP_INDEX].Value = 1;
00311 UpdateConstructionFrame(build);
00312
00313
00314 if (!type->BuilderOutside) {
00315
00316 build->Data.Built.Worker = unit;
00317
00318 build->CurrentSightRange = 1;
00319 unit->Remove(build);
00320 build->CurrentSightRange = 0;
00321 unit->X = x;
00322 unit->Y = y;
00323 unit->ClearAction();
00324 unit->Orders[0]->Goal = NULL;
00325 } else {
00326
00327 unit->Orders[0]->Action = UnitActionRepair;
00328 unit->Orders[0]->Goal = build;
00329 unit->Orders[0]->X = unit->Orders[0]->Y = -1;
00330 unit->Orders[0]->Range = unit->Type->RepairRange;
00331 unit->SubAction = 0;
00332 unit->Direction = DirectionToHeading(x - unit->X, y - unit->Y);
00333 UnitUpdateHeading(unit);
00334 build->RefsIncrease();
00335
00336 MapMarkUnitSight(build);
00337 }
00338 UpdateConstructionFrame(build);
00339 }
00340
00346 void HandleActionBuild(CUnit *unit)
00347 {
00348 CUnit *ontop;
00349
00350 if (unit->SubAction <= 10) {
00351 MoveToLocation(unit);
00352 }
00353 if (20 <= unit->SubAction && unit->SubAction <= 30) {
00354 if ((ontop = CheckCanBuild(unit))) {
00355 StartBuilding(unit, ontop);
00356 }
00357 }
00358 }
00359
00360
00366 void HandleActionBuilt(CUnit *unit)
00367 {
00368 CUnit *worker;
00369 int pcost = GetProductionCost(unit->Type);
00370
00371
00372 int hp = (unit->Data.Built.Progress * unit->Variable[HP_INDEX].Max) / pcost - unit->Variable[HP_INDEX].Value;
00373
00374 if (!unit->Type->BuilderOutside) {
00375
00376 int *costs = unit->Player->UnitsConsumingResourcesActual[unit];
00377 int cost = costs[EnergyCost] ? costs[EnergyCost] : costs[MagmaCost];
00378 unit->Data.Built.Progress += cost * SpeedBuild;
00379 if (unit->Data.Built.Progress > pcost) {
00380 unit->Data.Built.Progress = pcost;
00381 }
00382
00383
00384 unit->Variable[HP_INDEX].Value = (unit->Data.Built.Progress * unit->Variable[HP_INDEX].Max) / pcost - hp;
00385 if (unit->Variable[HP_INDEX].Value > unit->Stats->Variables[HP_INDEX].Max) {
00386 unit->Variable[HP_INDEX].Value = unit->Stats->Variables[HP_INDEX].Max;
00387 }
00388 }
00389
00390
00391
00392
00393 if (unit->Data.Built.Cancel || unit->Data.Built.Progress < 0) {
00394 DebugPrint("%s canceled.\n" _C_ unit->Type->Name.c_str());
00395
00396 if ((worker = unit->Data.Built.Worker)) {
00397 worker->ClearAction();
00398 unit->Data.Built.Worker = NoUnitP;
00399
00400 unit->CurrentSightRange = 1;
00401 DropOutOnSide(worker, LookingW, unit->Type->TileWidth, unit->Type->TileHeight);
00402 unit->CurrentSightRange = 0;
00403 }
00404
00405 if (!unit->Type->BuilderOutside) {
00406 unit->Player->RemoveFromUnitsConsumingResources(unit);
00407 }
00408
00409
00410 LetUnitDie(unit);
00411 return;
00412 }
00413
00414
00415
00416
00417 if (unit->Data.Built.Progress >= pcost ||
00418 unit->Variable[HP_INDEX].Value >= unit->Stats->Variables[HP_INDEX].Max) {
00419 DebugPrint("Building ready.\n");
00420 if (unit->Variable[HP_INDEX].Value > unit->Stats->Variables[HP_INDEX].Max) {
00421 unit->Variable[HP_INDEX].Value = unit->Stats->Variables[HP_INDEX].Max;
00422 }
00423 unit->ClearAction();
00424
00425 unit->Player->UnitTypesCount[unit->Type->Slot]++;
00426 unit->Constructed = 0;
00427 if (unit->Frame < 0) {
00428 unit->Frame = -unit->Type->StillFrame - 1;
00429 } else {
00430 unit->Frame = unit->Type->StillFrame;
00431 }
00432
00433 if ((worker = unit->Data.Built.Worker)) {
00434
00435 if (unit->Type->BuilderLost) {
00436
00437 LetUnitDie(worker);
00438
00439 } else {
00440 worker->ClearAction();
00441
00442 unit->CurrentSightRange = 1;
00443 DropOutOnSide(worker, LookingW, unit->Type->TileWidth, unit->Type->TileHeight);
00444
00445
00446
00447 if (worker->Type->Harvester) {
00448 CommandResource(worker, unit, 0);
00449 }
00450 }
00451 }
00452
00453 unit->Player->Notify(NotifyGreen, unit->X, unit->Y,
00454 _("New %s done"), unit->Type->Name.c_str());
00455 if (unit->Player == ThisPlayer) {
00456 if (unit->Type->Sound.Ready.Sound) {
00457 PlayUnitSound(unit, VoiceReady);
00458 } else if (worker) {
00459 PlayUnitSound(worker, VoiceWorkCompleted);
00460 } else {
00461 PlayUnitSound(unit, VoiceBuilding);
00462 }
00463 }
00464 if (unit->Player->AiEnabled) {
00465 AiWorkComplete(worker, unit);
00466 }
00467
00468 UpdateForNewUnit(unit, 0);
00469 if (!unit->Type->BuilderOutside) {
00470 unit->Player->RemoveFromUnitsConsumingResources(unit);
00471 }
00472
00473
00474 if (unit->Type->NumDirections > 1) {
00475 unit->Direction = (MyRand() >> 8) & 0xFF;
00476 UnitUpdateHeading(unit);
00477 }
00478
00479 if (IsOnlySelected(unit) || unit->Player == ThisPlayer) {
00480 SelectedUnitChanged();
00481 }
00482 unit->CurrentSightRange = unit->Stats->Variables[SIGHTRANGE_INDEX].Max;
00483 MapMarkUnitSight(unit);
00484 return;
00485 }
00486
00487 UpdateConstructionFrame(unit);
00488 }
00489