source: tags/1.3.1/UnitProcessing.pas

Last change on this file was 442, checked in by chronos, 2 years ago
  • Modified: Code cleanup.
File size: 47.1 KB
Line 
1{$INCLUDE Switches.inc}
2unit UnitProcessing;
3
4interface
5
6uses
7 SysUtils, Protocol, Database;
8
9type
10 TMoveType = (mtInvalid, mtMove, mtCapture, mtSpyMission, mtAttack,
11 mtBombard, mtExpel);
12
13 TMoveInfo = record
14 MoveType: TMoveType;
15 Cost, ToMaster, EndHealth, Defender, Dcix, Duix, EndHealthDef: integer;
16 MountainDelay: boolean;
17 end;
18
19var
20 uixSelectedTransport: integer;
21 Worked: array [0 .. nPl - 1] of integer; { settler work statistics }
22
23 // Moving/Combat
24function HostileDamage(p, mix, Loc, MP: integer): integer;
25function CalculateMove(p, uix, ToLoc, MoveLength: integer; TestOnly: boolean;
26 var MoveInfo: TMoveInfo): integer;
27function GetBattleForecast(Loc: integer; var BattleForecast: TBattleForecast;
28 var Duix, Dcix, AStr, DStr, ABaseDamage, DBaseDamage: integer): integer;
29function LoadUnit(p, uix: integer; TestOnly: boolean): integer;
30function UnloadUnit(p, uix: integer; TestOnly: boolean): integer;
31procedure Recover(p, uix: integer);
32function GetMoveAdvice(p, uix: integer; var a: TMoveAdviceData): integer;
33function CanPlaneReturn(p, uix: integer;
34 PlaneReturnData: TPlaneReturnData): boolean;
35
36// Terrain Improvement
37function StartJob(p, uix, NewJob: integer; TestOnly: boolean): integer;
38function Work(p, uix: integer): boolean;
39function GetJobProgress(p, Loc: integer;
40 var JobProgressData: TJobProgressData): integer;
41
42// Start/End Game
43procedure InitGame;
44procedure ReleaseGame;
45
46
47implementation
48
49uses
50 IPQ;
51
52const
53 eMountains = $6000FFFF; // additional return code for server internal use
54
55 // tile control flags
56 coKnown = $02;
57 coTrue = $04;
58
59 ContraJobs: array [0 .. nJob - 1] of Set of 0 .. nJob - 1 = ([], // jNone
60 [jCity], // jRoad
61 [jCity], // jRR
62 [jCity, jTrans], // jClear
63 [jCity, jFarm, jAfforest, jMine, jBase, jFort], // jIrr
64 [jCity, jIrr, jAfforest, jMine, jBase, jFort], // jFarm
65 [jCity, jIrr, jFarm, jTrans], // jAfforest
66 [jCity, jTrans, jIrr, jFarm, jBase, jFort], // jMine
67 [jCity, jTrans], // jCanal
68 [jCity, jClear, jAfforest, jMine, jCanal], // jTrans
69 [jCity, jIrr, jFarm, jMine, jBase], // jFort
70 [jCity], // jPoll
71 [jCity, jIrr, jFarm, jMine, jFort], // jBase
72 [jCity], // jPillage
73 [jRoad .. jPillage]); // jCity
74
75type
76 TToWorkList = array [0 .. INFIN, 0 .. nJob - 1] of word;
77
78var
79 ToWork: ^TToWorkList; { work left for each tile and job }
80
81 {
82 Moving/Combat
83 ____________________________________________________________________
84 }
85function HostileDamage(p, mix, Loc, MP: integer): integer;
86var
87 Tile: integer;
88begin
89 Tile := RealMap[Loc];
90 if (RW[p].Model[mix].Domain >= dSea) or (RW[p].Model[mix].Kind = mkSettler)
91 and (RW[p].Model[mix].Speed >= 200) or
92 (Tile and (fCity or fRiver or fCanal) <> 0) or (Tile and fTerImp = tiBase)
93 or (GWonder[woGardens].EffectiveOwner = p) then
94 result := 0
95 else if (Tile and fTerrain = fDesert) and
96 (Tile and fSpecial <> fSpecial1 { Oasis } ) then
97 begin
98 assert((Tile and fTerImp <> tiIrrigation) and (Tile and fTerImp <> tiFarm));
99 result := (DesertThurst * MP - 1) div RW[p].Model[mix].Speed + 1
100 end
101 else if Tile and fTerrain = fArctic then
102 begin
103 assert((Tile and fTerImp <> tiIrrigation) and (Tile and fTerImp <> tiFarm));
104 result := (ArcticThurst * MP - 1) div RW[p].Model[mix].Speed + 1
105 end
106 else
107 result := 0
108end;
109
110function Controlled(p, Loc: integer; IsDest: boolean): integer;
111{ whether tile at Loc is in control zone of enemy unit
112 returns combination of tile control flags }
113var
114 Loc1, V8: integer;
115 Adjacent: TVicinity8Loc;
116begin
117 result := 0;
118 if IsDest and (Occupant[Loc] = p) and (ZoCMap[Loc] > 0) then
119 exit;
120 // destination tile, not controlled if already occupied
121
122 if (RealMap[Loc] and fCity = 0) or (integer(RealMap[Loc] shr 27) <> p) and
123 (ServerVersion[p] >= $000EF0) then
124 begin // not own city
125 V8_to_Loc(Loc, Adjacent);
126 for V8 := 0 to 7 do
127 begin
128 Loc1 := Adjacent[V8];
129 if (Loc1 >= 0) and (Loc1 < MapSize) and (ZoCMap[Loc1] > 0) and
130 (Occupant[Loc1] >= 0) and (Occupant[Loc1] <> p) and
131 (RW[p].Treaty[Occupant[Loc1]] < trAlliance) then
132 if ObserveLevel[Loc1] and (3 shl (p * 2)) > 0 then
133 begin // p observes tile
134 result := coKnown or coTrue;
135 exit;
136 end
137 else
138 result := coTrue; // p does not observe tile
139 end;
140 end;
141end;
142
143function GetMoveCost(p, mix, FromLoc, ToLoc, MoveLength: integer;
144 var MoveCost: integer): integer;
145// MoveLength - 2 for short move, 3 for long move
146var
147 FromTile, ToTile: integer;
148begin
149 result := eOK;
150 FromTile := RealMap[FromLoc];
151 ToTile := RealMap[ToLoc];
152 with RW[p].Model[mix] do
153 begin
154 case Domain of
155 dGround:
156 if (ToTile and fTerrain >= fGrass) then { domain ok }
157 // if (Flags and mdCivil<>0) and (ToTile and fDeadLands<>0) then result:=eEerie
158 // else
159 begin { valid move }
160 if (FromTile and (fRR or fCity) <> 0) and
161 (ToTile and (fRR or fCity) <> 0) then
162 if GWonder[woShinkansen].EffectiveOwner = p then
163 MoveCost := 0
164 else
165 MoveCost := Speed * (4 * 1311) shr 17 // move along railroad
166 else if (FromTile and (fRoad or fRR or fCity) <> 0) and
167 (ToTile and (fRoad or fRR or fCity) <> 0) or
168 (FromTile and ToTile and (fRiver or fCanal) <> 0) or
169 (Cap[mcAlpine] > 0) then
170 // move along road, river or canal
171 if Cap[mcOver] > 0 then
172 MoveCost := 40
173 else
174 MoveCost := 20
175 else if Cap[mcOver] > 0 then
176 result := eNoRoad
177 else
178 case Terrain[ToTile and fTerrain].MoveCost of
179 1:
180 MoveCost := 50; // plain terrain
181 2:
182 begin
183 assert(Speed - 150 <= 600);
184 MoveCost := 50 + (Speed - 150) * 13 shr 7; // heavy terrain
185 end;
186 3:
187 begin
188 MoveCost := Speed;
189 result := eMountains;
190 exit;
191 end;
192 end;
193 MoveCost := MoveCost * MoveLength;
194 end
195 else
196 result := eDomainMismatch;
197
198 dSea:
199 if (ToTile and (fCity or fCanal) <> 0) or (ToTile and fTerrain < fGrass)
200 then { domain ok }
201 if (ToTile and fTerrain <> fOcean) or (Cap[mcNav] > 0) then
202 MoveCost := 50 * MoveLength { valid move }
203 else
204 result := eNoNav { navigation required for open sea }
205 else
206 result := eDomainMismatch;
207
208 dAir:
209 MoveCost := 50 * MoveLength; { always valid move }
210 end;
211 end;
212end;
213
214function CalculateMove(p, uix, ToLoc, MoveLength: integer; TestOnly: boolean;
215 var MoveInfo: TMoveInfo): integer;
216var
217 uix1, p1, FromLoc, DestControlled, AStr, DStr, ABaseDamage,
218 DBaseDamage: integer;
219 PModel: ^TModel;
220 BattleForecast: TBattleForecast;
221begin
222 with RW[p], Un[uix] do
223 begin
224 PModel := @Model[mix];
225 FromLoc := Loc;
226
227 BattleForecast.pAtt := p;
228 BattleForecast.mixAtt := mix;
229 BattleForecast.HealthAtt := Health;
230 BattleForecast.ExpAtt := Exp;
231 BattleForecast.FlagsAtt := Flags;
232 BattleForecast.Movement := Movement;
233 result := GetBattleForecast(ToLoc, BattleForecast, MoveInfo.Duix,
234 MoveInfo.Dcix, AStr, DStr, ABaseDamage, DBaseDamage);
235
236 if result = eHiddenUnit then
237 if TestOnly then
238 result := eOK // behave just like unit was moving
239 else if Mode > moLoading_Fast then
240 Map[ToLoc] := Map[ToLoc] or fHiddenUnit;
241 if result = eStealthUnit then
242 if TestOnly then
243 result := eOK // behave just like unit was moving
244 else if Mode > moLoading_Fast then
245 Map[ToLoc] := Map[ToLoc] or fStealthUnit;
246 if result < rExecuted then
247 exit;
248
249 case result of
250 eOK:
251 MoveInfo.MoveType := mtMove;
252 eExpelled:
253 MoveInfo.MoveType := mtExpel;
254 else
255 MoveInfo.MoveType := mtAttack;
256 end;
257
258 if MoveInfo.MoveType = mtMove then
259 begin
260 if Mode = moPlaying then
261 begin
262 p1 := RealMap[ToLoc] shr 27;
263 if (p1 < nPl) and (p1 <> p) and
264 ((RealMap[Loc] shr 27 <> Cardinal(p1)) and (PModel.Kind <> mkDiplomat)
265 and (Treaty[p1] >= trPeace) and (Treaty[p1] < trAlliance) or
266 (RealMap[ToLoc] and fCity <> 0) and (Treaty[p1] >= trPeace)) then
267 begin
268 result := eTreaty;
269 exit;
270 end; // keep peace treaty!
271 end;
272 if (RealMap[ToLoc] and fCity <> 0) and
273 (RealMap[ToLoc] shr 27 <> Cardinal(p)) then // empty enemy city
274 if PModel.Kind = mkDiplomat then
275 begin
276 MoveInfo.MoveType := mtSpyMission;
277 end
278 else if PModel.Domain = dGround then
279 begin
280 if PModel.Flags and mdCivil <> 0 then
281 begin
282 result := eNoCapturer;
283 exit;
284 end;
285 MoveInfo.MoveType := mtCapture;
286 end
287 else
288 begin
289 if (PModel.Domain = dSea) and (PModel.Cap[mcArtillery] = 0) then
290 begin
291 result := eDomainMismatch;
292 exit;
293 end
294 else if (PModel.Attack = 0) and
295 not((PModel.Cap[mcBombs] > 0) and (Flags and unBombsLoaded <> 0))
296 then
297 begin
298 result := eNoBombarder;
299 exit;
300 end
301 else if Movement < 100 then
302 begin
303 result := eNoTime_Bombard;
304 exit;
305 end;
306 MoveInfo.MoveType := mtBombard;
307 result := eBombarded;
308 end;
309 end;
310
311 MoveInfo.MountainDelay := false;
312 if MoveInfo.MoveType in [mtAttack, mtBombard, mtExpel] then
313 begin
314 if (Master >= 0) or (PModel.Domain = dSea) and
315 (RealMap[Loc] and fTerrain >= fGrass) or (PModel.Domain = dAir) and
316 ((RealMap[Loc] and fCity <> 0) or (RealMap[Loc] and fTerImp = tiBase))
317 then
318 begin
319 result := eViolation;
320 exit
321 end;
322 if MoveInfo.MoveType = mtBombard then
323 begin
324 MoveInfo.EndHealth := Health;
325 MoveInfo.EndHealthDef := -1;
326 end
327 else
328 begin
329 MoveInfo.EndHealth := BattleForecast.EndHealthAtt;
330 MoveInfo.EndHealthDef := BattleForecast.EndHealthDef;
331 end;
332 end
333 else // if MoveInfo.MoveType in [mtMove,mtCapture,mtSpyMission] then
334 begin
335 if (Master >= 0) and (PModel.Domain < dSea) then
336 begin // transport unload
337 MoveInfo.Cost := PModel.Speed;
338 if RealMap[ToLoc] and fTerrain < fGrass then
339 result := eDomainMismatch;
340 end
341 else
342 begin
343 result := GetMoveCost(p, mix, FromLoc, ToLoc, MoveLength,
344 MoveInfo.Cost);
345 if result = eMountains then
346 begin
347 result := eOK;
348 MoveInfo.MountainDelay := true
349 end;
350 end;
351 if (result >= rExecuted) and (MoveInfo.MoveType = mtSpyMission) then
352 result := eMissionDone;
353
354 MoveInfo.ToMaster := -1;
355 if (result = eDomainMismatch) and (PModel.Domain < dSea) and
356 (PModel.Cap[mcOver] = 0) then
357 begin
358 for uix1 := 0 to nUn - 1 do
359 with Un[uix1] do // check load to transport
360 if (Loc = ToLoc) and
361 (TroopLoad < Model[mix].MTrans * Model[mix].Cap[mcSeaTrans]) then
362 begin
363 result := eLoaded;
364 MoveInfo.Cost := PModel.Speed;
365 MoveInfo.ToMaster := uix1;
366 if (uixSelectedTransport >= 0) and (uix1 = uixSelectedTransport)
367 then
368 Break;
369 end;
370 end
371 else if (PModel.Domain = dAir) and (PModel.Cap[mcAirTrans] = 0) and
372 (RealMap[ToLoc] and fCity = 0) and (RealMap[ToLoc] and fTerImp <> tiBase)
373 then
374 begin
375 for uix1 := 0 to nUn - 1 do
376 with Un[uix1] do
377 if (Loc = ToLoc) and
378 (AirLoad < Model[mix].MTrans * Model[mix].Cap[mcCarrier]) then
379 begin // load plane to ship
380 result := eLoaded;
381 MoveInfo.ToMaster := uix1;
382 if (uixSelectedTransport >= 0) and (uix1 = uixSelectedTransport)
383 then
384 Break;
385 end;
386 end;
387 if result < rExecuted then
388 exit;
389
390 if (Master < 0) and (MoveInfo.ToMaster < 0) then
391 MoveInfo.EndHealth := Health - HostileDamage(p, mix, ToLoc,
392 MoveInfo.Cost)
393 else
394 MoveInfo.EndHealth := Health;
395
396 if (Mode = moPlaying) and (PModel.Flags and mdZOC <> 0) and (Master < 0)
397 and (MoveInfo.ToMaster < 0) and (Controlled(p, FromLoc, false) >= coTrue)
398 then
399 begin
400 DestControlled := Controlled(p, ToLoc, true);
401 if DestControlled >= coTrue + coKnown then
402 begin
403 result := eZOC;
404 exit;
405 end
406 else if not TestOnly and (DestControlled >= coTrue) then
407 begin
408 result := eZOC_EnemySpotted;
409 exit;
410 end;
411 end;
412 if (Movement = 0) and (ServerVersion[p] >= $0100F1) or
413 (MoveInfo.Cost > Movement) then
414 if (Master >= 0) or (MoveInfo.ToMaster >= 0) then
415 begin
416 result := eNoTime_Load;
417 exit;
418 end
419 else
420 begin
421 result := eNoTime_Move;
422 exit;
423 end;
424 if (MoveInfo.EndHealth <= 0) or (MoveInfo.MoveType = mtSpyMission) then
425 result := result or rUnitRemoved;
426 // spy mission or victim of HostileDamage
427
428 end; // if MoveInfo.MoveType in [mtMove,mtCapture,mtSpyMission]
429
430 if MoveInfo.MoveType in [mtAttack, mtExpel] then
431 MoveInfo.Defender := Occupant[ToLoc]
432 else if RealMap[ToLoc] and fCity <> 0 then
433 begin // MoveInfo.Dcix not set yet
434 MoveInfo.Defender := RealMap[ToLoc] shr 27;
435 SearchCity(ToLoc, MoveInfo.Defender, MoveInfo.Dcix);
436 end;
437 end;
438end;
439
440function GetBattleForecast(Loc: integer; var BattleForecast: TBattleForecast;
441 var Duix, Dcix, AStr, DStr, ABaseDamage, DBaseDamage: integer): integer;
442var
443 Time, Defender, ABon, DBon, DCnt, MultiDamage: integer;
444 PModel, DModel: ^TModel;
445begin
446 with BattleForecast do
447 begin
448 Defender := Occupant[Loc];
449 if (Defender < 0) or (Defender = pAtt) then
450 begin
451 result := eOK;
452 exit;
453 end; // no attack, simple move
454
455 PModel := @RW[pAtt].Model[mixAtt];
456 Strongest(Loc, Duix, DStr, DBon, DCnt); { get defense strength and bonus }
457 if (PModel.Kind = mkDiplomat) and (RealMap[Loc] and fCity <> 0) then
458 begin // spy mission -- return as if move was possible
459 EndHealthAtt := HealthAtt;
460 EndHealthDef := RW[Defender].Un[Duix].Health;
461 result := eOK;
462 exit;
463 end;
464
465 DModel := @RW[Defender].Model[RW[Defender].Un[Duix].mix];
466 if (RealMap[Loc] and fCity = 0) and (RealMap[Loc] and fTerImp <> tiBase)
467 then
468 begin
469 if (DModel.Cap[mcSub] > 0) and (RealMap[Loc] and fTerrain < fGrass) and
470 (ObserveLevel[Loc] shr (2 * pAtt) and 3 < lObserveAll) then
471 begin
472 result := eHiddenUnit;
473 exit;
474 end; // attacking submarine not allowed
475 if (DModel.Cap[mcStealth] > 0) and
476 (ObserveLevel[Loc] shr (2 * pAtt) and 3 <> lObserveSuper) then
477 begin
478 result := eStealthUnit;
479 exit;
480 end; // attacking stealth aircraft not allowed
481 if (DModel.Domain = dAir) and (DModel.Kind <> mkSpecial_Glider) and
482 (PModel.Domain <> dAir) then
483 begin
484 result := eDomainMismatch;
485 exit;
486 end; // can't attack plane
487 end;
488 if ((PModel.Cap[mcArtillery] = 0) or ((ServerVersion[pAtt] >= $010200) and
489 (RealMap[Loc] and fTerrain < fGrass) and (DModel.Cap[mcSub] > 0)))
490 // ground units can't attack submarines
491 and ((PModel.Domain = dGround) and (RealMap[Loc] and fTerrain < fGrass) or
492 (PModel.Domain = dSea) and (RealMap[Loc] and fTerrain >= fGrass)) then
493 begin
494 result := eDomainMismatch;
495 exit;
496 end;
497 if (PModel.Attack = 0) and not((PModel.Cap[mcBombs] > 0) and
498 (FlagsAtt and unBombsLoaded <> 0) and (DModel.Domain < dAir)) then
499 begin
500 result := eInvalid;
501 exit;
502 end;
503
504 if Movement = 0 then
505 begin
506 result := eNoTime_Attack;
507 exit;
508 end;
509
510{$IFOPT O-}assert(InvalidTreatyMap = 0); {$ENDIF}
511 if RW[pAtt].Treaty[Defender] >= trPeace then
512 begin
513 if (PModel.Domain <> dAir) and (PModel.Attack > 0) and
514 (integer(RealMap[Loc] shr 27) = pAtt) then
515 if Movement >= 100 then
516 begin // expel friendly unit
517 EndHealthDef := RW[Defender].Un[Duix].Health;
518 EndHealthAtt := HealthAtt;
519 result := eExpelled
520 end
521 else
522 result := eNoTime_Expel
523 else
524 result := eTreaty;
525 exit;
526 end;
527
528 // calculate defender strength
529 if RealMap[Loc] and fCity <> 0 then
530 begin // consider city improvements
531 SearchCity(Loc, Defender, Dcix);
532 if (PModel.Domain < dSea) and (PModel.Cap[mcArtillery] = 0) and
533 ((RW[Defender].City[Dcix].Built[imWalls] = 1) or
534 (Continent[RW[Defender].City[Dcix].Loc] = GrWallContinent[Defender]))
535 then
536 inc(DBon, 8)
537 else if (PModel.Domain = dSea) and
538 (RW[Defender].City[Dcix].Built[imCoastalFort] = 1) then
539 inc(DBon, 4)
540 else if (PModel.Domain = dAir) and
541 (RW[Defender].City[Dcix].Built[imMissileBat] = 1) then
542 inc(DBon, 4);
543 if RW[Defender].City[Dcix].Built[imBunker] = 1 then
544 inc(DBon, 4)
545 end;
546 if (PModel.Domain = dAir) and (DModel.Cap[mcAirDef] > 0) then
547 inc(DBon, 4);
548 DStr := DModel.Defense * DBon * 100;
549 if (DModel.Domain = dAir) and ((RealMap[Loc] and fCity <> 0) or
550 (RealMap[Loc] and fTerImp = tiBase)) then
551 DStr := 0;
552 if (DModel.Domain = dSea) and (RealMap[Loc] and fTerrain >= fGrass) then
553 DStr := DStr shr 1;
554
555 // calculate attacker strength
556 if PModel.Cap[mcWill] > 0 then
557 Time := 100
558 else
559 begin
560 Time := Movement;
561 if Time > 100 then
562 Time := 100;
563 end;
564 ABon := 4 + ExpAtt div ExpCost;
565 AStr := PModel.Attack;
566 if (FlagsAtt and unBombsLoaded <> 0) and (DModel.Domain < dAir) then
567 // use bombs
568 AStr := AStr + PModel.Cap[mcBombs] * PModel.MStrength * 2;
569 AStr := Time * AStr * ABon;
570
571 // calculate base damage for defender
572 if DStr = 0 then
573 DBaseDamage := RW[Defender].Un[Duix].Health
574 else
575 begin
576 DBaseDamage := HealthAtt * AStr div DStr;
577 if DBaseDamage = 0 then
578 DBaseDamage := 1;
579 if DBaseDamage > RW[Defender].Un[Duix].Health then
580 DBaseDamage := RW[Defender].Un[Duix].Health;
581 end;
582
583 // calculate base damage for attacker
584 if AStr = 0 then
585 ABaseDamage := HealthAtt
586 else
587 begin
588 ABaseDamage := RW[Defender].Un[Duix].Health * DStr div AStr;
589 if ABaseDamage = 0 then
590 ABaseDamage := 1;
591 if ABaseDamage > HealthAtt then
592 ABaseDamage := HealthAtt;
593 end;
594
595 // calculate final damage for defender
596 MultiDamage := 2;
597 if (ABaseDamage = HealthAtt) and (PModel.Cap[mcFanatic] > 0) and
598 not(RW[pAtt].Government in [gRepublic, gDemocracy, gFuture]) then
599 MultiDamage := MultiDamage * 2; // fanatic attacker died
600 EndHealthDef := RW[Defender].Un[Duix].Health - MultiDamage *
601 DBaseDamage div 2;
602 if EndHealthDef < 0 then
603 EndHealthDef := 0;
604
605 // calculate final damage for attacker
606 MultiDamage := 2;
607 if DBaseDamage = RW[Defender].Un[Duix].Health then
608 begin
609 if (DModel.Cap[mcFanatic] > 0) and
610 not(RW[Defender].Government in [gRepublic, gDemocracy, gFuture]) then
611 MultiDamage := MultiDamage * 2; // fanatic defender died
612 if PModel.Cap[mcFirst] > 0 then
613 MultiDamage := MultiDamage shr 1; // first strike unit wins
614 end;
615 Time := Movement;
616 if Time > 100 then
617 Time := 100;
618 EndHealthAtt := HealthAtt - MultiDamage * ABaseDamage div 2 -
619 HostileDamage(pAtt, mixAtt, Loc, Time);
620 if EndHealthAtt < 0 then
621 EndHealthAtt := 0;
622
623 if EndHealthDef > 0 then
624 result := eLost
625 else if EndHealthAtt > 0 then
626 result := eWon
627 else
628 result := eBloody;
629 end;
630end;
631
632function LoadUnit(p, uix: integer; TestOnly: boolean): integer;
633var
634 uix1, d, Cost, ToMaster: integer;
635begin
636 result := eOK;
637 with RW[p].Un[uix] do
638 begin
639 d := RW[p].Model[mix].Domain;
640 if (Master >= 0) or (d = dSea) or
641 (RW[p].Model[mix].Cap[mcAirTrans] + RW[p].Model[mix].Cap[mcOver] > 0) then
642 result := eViolation
643 else
644 begin
645 ToMaster := -1;
646 for uix1 := 0 to RW[p].nUn - 1 do
647 if RW[p].Un[uix1].Loc = Loc then
648 with RW[p].Un[uix1], RW[p].Model[mix] do
649 if (d < dSea) and
650 (TroopLoad < MTrans * (Cap[mcSeaTrans] + Cap[mcAirTrans])) or
651 (d = dAir) and (AirLoad < MTrans * Cap[mcCarrier]) then
652 begin { load onto unit uix1 }
653 if (uixSelectedTransport < 0) or (uix1 = uixSelectedTransport)
654 then
655 begin
656 ToMaster := uix1;
657 Break;
658 end
659 else if ToMaster < 0 then
660 ToMaster := uix1;
661 end;
662 if ToMaster < 0 then
663 result := eNoLoadCapacity
664 else
665 begin
666 if d = dAir then
667 Cost := 100
668 else
669 Cost := RW[p].Model[mix].Speed;
670 if Movement < Cost then
671 result := eNoTime_Load
672 else if not TestOnly then
673 begin
674 FreeUnit(p, uix);
675 dec(Movement, Cost);
676 if d = dAir then
677 inc(RW[p].Un[ToMaster].AirLoad)
678 else
679 inc(RW[p].Un[ToMaster].TroopLoad);
680 Master := ToMaster;
681 UpdateUnitMap(Loc);
682 end;
683 end;
684 end;
685 end;
686end;
687
688function UnloadUnit(p, uix: integer; TestOnly: boolean): integer;
689var
690 Cost: integer;
691begin
692 result := eOK;
693 with RW[p].Un[uix] do
694 if Master < 0 then
695 result := eNotChanged
696 else if (RW[p].Model[mix].Domain < dSea) and
697 (RealMap[Loc] and fTerrain < fGrass) then
698 result := eDomainMismatch
699 // else if (RW[p].Model[mix].Domain<dSea)
700 // and (RW[p].Model[mix].Flags and mdCivil<>0)
701 // and (RealMap[Loc] and fDeadLands<>0) then result:=eEerie
702 else
703 begin
704 if RW[p].Model[mix].Domain = dAir then
705 Cost := 100
706 else
707 Cost := RW[p].Model[mix].Speed;
708 if Movement < Cost then
709 result := eNoTime_Load
710 else if not TestOnly then
711 begin
712 dec(Movement, Cost);
713 if RW[p].Model[mix].Domain = dAir then
714 dec(RW[p].Un[Master].AirLoad)
715 else
716 begin
717 dec(RW[p].Un[Master].TroopLoad);
718 // Movement:=0 // no more movement after unload
719 end;
720 Master := -1;
721 PlaceUnit(p, uix);
722 UpdateUnitMap(Loc);
723 end;
724 end;
725end;
726
727procedure Recover(p, uix: integer);
728var
729 cix, Recovery: integer;
730begin
731 with RW[p], Un[uix] do
732 begin
733 if (Master >= 0) and (Model[Un[Master].mix].Cap[mcSupplyShip] > 0) then
734 Recovery := FastRecovery { hospital ship }
735 else if RealMap[Loc] and fTerImp = tiBase then
736 Recovery := CityRecovery
737 else if RealMap[Loc] and fCity <> 0 then
738 begin { unit in city }
739 cix := nCity - 1;
740 while (cix >= 0) and (City[cix].Loc <> Loc) do
741 dec(cix);
742 if City[cix].Flags and chDisorder <> 0 then
743 Recovery := NoCityRecovery
744 else if (Model[mix].Domain = dGround) and
745 (City[cix].Built[imBarracks] + City[cix].Built[imElite] > 0) or
746 (Model[mix].Domain = dSea) and (City[cix].Built[imDockyard] = 1) or
747 (Model[mix].Domain = dAir) and (City[cix].Built[imAirport] = 1) then
748 Recovery := FastRecovery { city has baracks/shipyard/airport }
749 else
750 Recovery := CityRecovery
751 end
752 else if (RealMap[Loc] and fTerrain >= fGrass) and (Model[mix].Domain <> dAir)
753 then
754 Recovery := NoCityRecovery
755 else
756 Recovery := 0;
757
758 Recovery := Recovery * Movement div Model[mix].Speed;
759 { recovery depends on movement unused }
760 if Recovery > Health then
761 Recovery := Health; // health max. doubled each turn
762 if Recovery > 100 - Health then
763 Recovery := 100 - Health;
764 inc(Health, Recovery);
765 end;
766end;
767
768function GetMoveAdvice(p, uix: integer; var a: TMoveAdviceData): integer;
769const
770 // domains
771 gmaAir = 0;
772 gmaSea = 1;
773 gmaGround_NoZoC = 2;
774 gmaGround_ZoC = 3;
775 // flags
776 gmaNav = 4;
777 gmaOver = 4;
778 gmaAlpine = 8;
779var
780 i, FromLoc, EndLoc, T, T1, maxmov, initmov, Loc, Loc1, FromTile, ToTile, V8,
781 MoveInfo, HeavyCost, RailCost, MoveCost, AddDamage, MaxDamage,
782 MovementLeft: integer;
783 Map: ^TTileList;
784 Q: TIPQ;
785 Adjacent: TVicinity8Loc;
786 From: array [0 .. lxmax * lymax - 1] of integer;
787 Time: array [0 .. lxmax * lymax - 1] of integer;
788 Damage: array [0 .. lxmax * lymax - 1] of integer;
789 MountainDelay, Resistant: boolean;
790 // tt,tt0: int64;
791begin
792 // QueryPerformanceCounter(tt0);
793
794 MaxDamage := RW[p].Un[uix].Health - 1;
795 if MaxDamage > a.MaxHostile_MovementLeft then
796 if a.MaxHostile_MovementLeft >= 0 then
797 MaxDamage := a.MaxHostile_MovementLeft
798 else
799 MaxDamage := 0;
800
801 Map := @(RW[p].Map^);
802 if (a.ToLoc <> maNextCity) and ((a.ToLoc < 0) or (a.ToLoc >= MapSize)) then
803 begin
804 result := eInvalid;
805 exit;
806 end;
807 if (a.ToLoc <> maNextCity) and (Map[a.ToLoc] and fTerrain = fUNKNOWN) then
808 begin
809 result := eNoWay;
810 exit;
811 end;
812
813 with RW[p].Model[RW[p].Un[uix].mix] do
814 case Domain of
815 dGround:
816 if (a.ToLoc <> maNextCity) and (Map[a.ToLoc] and fTerrain = fOcean) then
817 begin
818 result := eDomainMismatch;
819 exit;
820 end
821 else
822 begin
823 if Flags and mdZOC <> 0 then
824 MoveInfo := gmaGround_ZoC
825 else
826 MoveInfo := gmaGround_NoZoC;
827 if Cap[mcOver] > 0 then
828 inc(MoveInfo, gmaOver);
829 if Cap[mcAlpine] > 0 then
830 inc(MoveInfo, gmaAlpine);
831 HeavyCost := 50 + (Speed - 150) * 13 shr 7;
832 if GWonder[woShinkansen].EffectiveOwner = p then
833 RailCost := 0
834 else
835 RailCost := Speed * (4 * 1311) shr 17;
836 maxmov := Speed;
837 initmov := 0;
838 Resistant := (GWonder[woGardens].EffectiveOwner = p) or
839 (Kind = mkSettler) and (Speed >= 200);
840 end;
841 dSea:
842 if (a.ToLoc <> maNextCity) and (Map[a.ToLoc] and fTerrain >= fGrass) and
843 (Map[a.ToLoc] and (fCity or fUnit or fCanal) = 0) then
844 begin
845 result := eDomainMismatch;
846 exit;
847 end
848 else
849 begin
850 MoveInfo := gmaSea;
851 if Cap[mcNav] > 0 then
852 inc(MoveInfo, gmaNav);
853 maxmov := UnitSpeed(p, RW[p].Un[uix].mix, 100);
854 initmov := maxmov - UnitSpeed(p, RW[p].Un[uix].mix,
855 RW[p].Un[uix].Health);
856 end;
857 dAir:
858 begin
859 MoveInfo := gmaAir;
860 maxmov := Speed;
861 initmov := 0;
862 end;
863 end;
864
865 FromLoc := RW[p].Un[uix].Loc;
866 FillChar(Time, SizeOf(Time), 255); { -1 }
867 Damage[FromLoc] := 0;
868 Q := TIPQ.Create(MapSize);
869 Q.Put(FromLoc, (maxmov - RW[p].Un[uix].Movement) shl 8);
870 while Q.Get(Loc, T) do
871 begin
872 Time[Loc] := T;
873 if T >= (a.MoreTurns + 1) shl 20 then
874 begin
875 Loc := -1;
876 Break;
877 end;
878 FromTile := Map[Loc];
879 if (Loc = a.ToLoc) or (a.ToLoc = maNextCity) and (FromTile and fCity <> 0)
880 then
881 Break;
882 if T and $FFF00 = $FFF00 then
883 inc(T, $100000); // indicates mountain delay
884 V8_to_Loc(Loc, Adjacent);
885 for V8 := 0 to 7 do
886 begin
887 Loc1 := Adjacent[V8];
888 if (Loc1 >= 0) and (Loc1 < MapSize) and (Time[Loc1] < 0) then
889 begin
890 ToTile := Map[Loc1];
891 if (Loc1 = a.ToLoc) and (ToTile and (fUnit or fOwned) = fUnit) and
892 not((MoveInfo and 3 = gmaSea) and (FromTile and fTerrain >= fGrass))
893 and not((MoveInfo and 3 = gmaAir) and ((FromTile and fCity <> 0) or
894 (FromTile and fTerImp = tiBase))) then
895 begin // attack position found
896 if Q.Put(Loc1, T + 1) then
897 From[Loc1] := Loc;
898 end
899 else if (ToTile and fTerrain <> fUNKNOWN) and
900 ((Loc1 = a.ToLoc) or (ToTile and (fCity or fOwned) <> fCity))
901 // don't move through enemy cities
902 and ((Loc1 = a.ToLoc) or (ToTile and (fUnit or fOwned) <> fUnit))
903 // way is blocked
904 and (ToTile and not FromTile and fPeace = 0) and
905 ((MoveInfo and 3 < gmaGround_ZoC) or (ToTile and FromTile and
906 fInEnemyZoc = 0) or (ToTile and fOwnZoCUnit <> 0) or
907 (FromTile and fCity <> 0) or (ToTile and (fCity or fOwned) = fCity or
908 fOwned)) then
909 begin
910 // calculate move cost, must be identic to GetMoveCost function
911 AddDamage := 0;
912 MountainDelay := false;
913 case MoveInfo of
914
915 gmaAir:
916 MoveCost := 50; { always valid move }
917
918 gmaSea:
919 if (ToTile and (fCity or fCanal) <> 0) or
920 (ToTile and fTerrain = fShore) then { domain ok }
921 MoveCost := 50 { valid move }
922 else
923 MoveCost := -1;
924
925 gmaSea + gmaNav:
926 if (ToTile and (fCity or fCanal) <> 0) or
927 (ToTile and fTerrain < fGrass) then { domain ok }
928 MoveCost := 50 { valid move }
929 else
930 MoveCost := -1;
931
932 else // ground unit
933 if (ToTile and fTerrain >= fGrass) then { domain ok }
934 begin { valid move }
935 if (FromTile and (fRR or fCity) <> 0) and
936 (ToTile and (fRR or fCity) <> 0) then
937 MoveCost := RailCost // move along railroad
938 else if (FromTile and (fRoad or fRR or fCity) <> 0) and
939 (ToTile and (fRoad or fRR or fCity) <> 0) or
940 (FromTile and ToTile and (fRiver or fCanal) <> 0) or
941 (MoveInfo and gmaAlpine <> 0) then
942 // move along road, river or canal
943 if MoveInfo and gmaOver <> 0 then
944 MoveCost := 40
945 else
946 MoveCost := 20
947 else if MoveInfo and gmaOver <> 0 then
948 MoveCost := -1
949 else
950 case Terrain[ToTile and fTerrain].MoveCost of
951 1:
952 MoveCost := 50; // plain terrain
953 2:
954 MoveCost := HeavyCost; // heavy terrain
955 3:
956 begin
957 MoveCost := maxmov;
958 MountainDelay := true;
959 end;
960 end;
961
962 // calculate HostileDamage
963 if not Resistant and (ToTile and fTerImp <> tiBase) then
964 if ToTile and (fTerrain or fCity or fRiver or fCanal or
965 fSpecial1 { Oasis } ) = fDesert then
966 begin
967 if V8 and 1 <> 0 then
968 AddDamage := ((DesertThurst * 3) * MoveCost - 1)
969 div maxmov + 1
970 else
971 AddDamage := ((DesertThurst * 2) * MoveCost - 1)
972 div maxmov + 1
973 end
974 else if ToTile and (fTerrain or fCity or fRiver or fCanal) = fArctic
975 then
976 begin
977 if V8 and 1 <> 0 then
978 AddDamage := ((ArcticThurst * 3) * MoveCost - 1)
979 div maxmov + 1
980 else
981 AddDamage := ((ArcticThurst * 2) * MoveCost - 1)
982 div maxmov + 1
983 end;
984 end
985 else
986 MoveCost := -1;
987
988 end;
989
990 if (MoveCost > 0) and not MountainDelay then
991 if V8 and 1 <> 0 then
992 inc(MoveCost, MoveCost * 2)
993 else
994 inc(MoveCost, MoveCost);
995
996 if (MoveInfo and 2 <> 0) // ground unit, check transport load/unload
997 and ((MoveCost < 0) and (ToTile and (fUnit or fOwned) = fUnit or
998 fOwned) // assume ship/airplane is transport -- load!
999 or (MoveCost >= 0) and (FromTile and fTerrain < fGrass)) then
1000 MoveCost := maxmov; // transport load or unload
1001
1002 if MoveCost >= 0 then
1003 begin { valid move }
1004 MovementLeft := maxmov - T shr 8 and $FFF - MoveCost;
1005 if (MovementLeft < 0) or ((MoveCost = 0) and (MovementLeft = 0))
1006 then
1007 begin // must wait for next turn
1008 // calculate HostileDamage
1009 if (MoveInfo and 2 <> 0) { ground unit }
1010 and not Resistant and (FromTile and fTerImp <> tiBase) then
1011 if FromTile and (fTerrain or fCity or fRiver or fCanal or
1012 fSpecial1 { Oasis } ) = fDesert then
1013 inc(AddDamage, (DesertThurst * (maxmov - T shr 8 and $FFF) -
1014 1) div maxmov + 1)
1015 else if FromTile and (fTerrain or fCity or fRiver or fCanal) = fArctic
1016 then
1017 inc(AddDamage, (ArcticThurst * (maxmov - T shr 8 and $FFF) -
1018 1) div maxmov + 1);
1019
1020 T1 := T and $7FF000FF + $100000 + (initmov + MoveCost) shl 8;
1021 end
1022 else
1023 T1 := T + MoveCost shl 8 + 1;
1024 if MountainDelay then
1025 T1 := T1 or $FFF00;
1026 if (Damage[Loc] + AddDamage <= MaxDamage) and (T1 and $FF < $FF)
1027 then
1028 if Q.Put(Loc1, T1) then
1029 begin
1030 From[Loc1] := Loc;
1031 Damage[Loc1] := Damage[Loc] + AddDamage;
1032 end;
1033 end;
1034 end;
1035 end;
1036 end;
1037 end;
1038 FreeAndNil(Q);
1039 if (Loc = a.ToLoc) or (a.ToLoc = maNextCity) and (Loc >= 0) and
1040 (Map[Loc] and fCity <> 0) then
1041 begin
1042 a.MoreTurns := T shr 20;
1043 EndLoc := Loc;
1044 a.nStep := 0;
1045 while Loc <> FromLoc do
1046 begin
1047 if Time[Loc] < $100000 then
1048 inc(a.nStep);
1049 Loc := From[Loc];
1050 end;
1051 Loc := EndLoc;
1052 i := a.nStep;
1053 while Loc <> FromLoc do
1054 begin
1055 if Time[Loc] < $100000 then
1056 begin
1057 dec(i);
1058 if i < 25 then
1059 begin
1060 a.dx[i] := ((Loc mod lx * 2 + Loc div lx and 1) -
1061 (From[Loc] mod lx * 2 + From[Loc] div lx and 1) + 3 * lx)
1062 mod (2 * lx) - lx;
1063 a.dy[i] := Loc div lx - From[Loc] div lx;
1064 end
1065 end;
1066 Loc := From[Loc];
1067 end;
1068 a.MaxHostile_MovementLeft := maxmov - Time[EndLoc] shr 8 and $FFF;
1069 if a.nStep > 25 then
1070 a.nStep := 25;
1071 result := eOK
1072 end
1073 else
1074 result := eNoWay;
1075
1076 // QueryPerformanceCounter(tt);{time in s is: (tt-tt0)/PerfFreq}
1077end;
1078
1079function CanPlaneReturn(p, uix: integer;
1080 PlaneReturnData: TPlaneReturnData): boolean;
1081const
1082 mfEnd = 1;
1083 mfReached = 2;
1084var
1085 uix1, T, T1, Loc, Loc1, FromTile, ToTile, V8, MoveCost, maxmov: integer;
1086 Map: ^TTileList;
1087 Q: TIPQ;
1088 Adjacent: TVicinity8Loc;
1089 MapFlags: array [0 .. lxmax * lymax - 1] of byte;
1090begin
1091 Map := @(RW[p].Map^);
1092
1093 // calculate possible return points
1094 FillChar(MapFlags, SizeOf(MapFlags), 0);
1095 if RW[p].Model[RW[p].Un[uix].mix].Kind = mkSpecial_Glider then
1096 begin
1097 for Loc := 0 to MapSize - 1 do
1098 if Map[Loc] and fTerrain >= fGrass then
1099 MapFlags[Loc] := MapFlags[Loc] or mfEnd;
1100 end
1101 else
1102 begin
1103 for Loc := 0 to MapSize - 1 do
1104 if (Map[Loc] and (fCity or fOwned) = fCity or fOwned) or
1105 (Map[Loc] and fTerImp = tiBase) and (Map[Loc] and fObserved <> 0) and
1106 (Map[Loc] and (fUnit or fOwned) <> fUnit) then
1107 MapFlags[Loc] := MapFlags[Loc] or mfEnd;
1108 if RW[p].Model[RW[p].Un[uix].mix].Cap[mcAirTrans] = 0 then
1109 // plane can land on carriers
1110 for uix1 := 0 to RW[p].nUn - 1 do
1111 with RW[p].Un[uix1], RW[p].Model[mix] do
1112 if AirLoad < MTrans * Cap[mcCarrier] then
1113 MapFlags[Loc] := MapFlags[Loc] or mfEnd;
1114 end;
1115
1116 with RW[p].Un[uix] do
1117 begin
1118 if Master >= 0 then // can return to same carrier, even if full now
1119 MapFlags[Loc] := MapFlags[Loc] or mfEnd;
1120 maxmov := RW[p].Model[mix].Speed;
1121 end;
1122
1123 result := false;
1124 Q := TIPQ.Create(MapSize);
1125 Q.Put(PlaneReturnData.Loc, (maxmov - PlaneReturnData.Movement) shl 8);
1126 while Q.Get(Loc, T) do
1127 begin
1128 MapFlags[Loc] := MapFlags[Loc] or mfReached;
1129 if T >= (PlaneReturnData.Fuel + 1) shl 20 then
1130 begin
1131 result := false;
1132 Break;
1133 end;
1134 if MapFlags[Loc] and mfEnd <> 0 then
1135 begin
1136 result := true;
1137 Break;
1138 end;
1139 FromTile := Map[Loc];
1140 V8_to_Loc(Loc, Adjacent);
1141 for V8 := 0 to 7 do
1142 begin
1143 Loc1 := Adjacent[V8];
1144 if (Loc1 >= 0) and (Loc1 < MapSize) and (MapFlags[Loc1] and mfReached = 0)
1145 then
1146 begin
1147 ToTile := Map[Loc1];
1148 if (ToTile and fTerrain <> fUNKNOWN) and
1149 (ToTile and (fCity or fOwned) <> fCity)
1150 // don't move through enemy cities
1151 and (ToTile and (fUnit or fOwned) <> fUnit) // way is blocked
1152 and (ToTile and not FromTile and fPeace = 0) then
1153 begin
1154 if V8 and 1 <> 0 then
1155 MoveCost := 150
1156 else
1157 MoveCost := 100;
1158 if MoveCost + T shr 8 and $FFF > maxmov then
1159 // must wait for next turn
1160 T1 := T and $7FF000FF + $100000 + MoveCost shl 8
1161 else
1162 T1 := T + MoveCost shl 8;
1163 Q.Put(Loc1, T1);
1164 end;
1165 end;
1166 end;
1167 end;
1168 FreeAndNil(Q);
1169end;
1170
1171{
1172 Terrain Improvement
1173 ____________________________________________________________________
1174}
1175function CalculateJobWork(p, Loc, Job: integer; var JobWork: integer): integer;
1176var
1177 TerrType: integer;
1178begin
1179 result := eOK;
1180 TerrType := RealMap[Loc] and fTerrain;
1181 with Terrain[TerrType] do
1182 case Job of
1183 jCity:
1184 if RealMap[Loc] and fCity <> 0 then
1185 result := eInvalid
1186 else if IrrEff = 0 then
1187 result := eNoCityTerrain
1188 else
1189 JobWork := CityWork;
1190 jRoad:
1191 if RealMap[Loc] and (fRoad or fRR) = 0 then
1192 begin
1193 JobWork := MoveCost * RoadWork;
1194 if RealMap[Loc] and fRiver <> 0 then
1195 if RW[p].Tech[adBridgeBuilding] >= tsApplicable then
1196 inc(JobWork, RoadBridgeWork) { across river }
1197 else
1198 result := eNoBridgeBuilding;
1199 end
1200 else
1201 result := eInvalid;
1202 jRR:
1203 if RealMap[Loc] and fRoad = 0 then
1204 result := eNoPreq
1205 else if RealMap[Loc] and fRR <> 0 then
1206 result := eInvalid
1207 else
1208 begin
1209 JobWork := MoveCost * RRWork;
1210 if RealMap[Loc] and fRiver <> 0 then
1211 inc(JobWork, RRBridgeWork); { across river }
1212 end;
1213 jClear:
1214 if (TerrType = fDesert) and (GWonder[woGardens].EffectiveOwner <> p)
1215 then
1216 result := eInvalid
1217 else if ClearTerrain >= 0 then
1218 JobWork := IrrClearWork
1219 else
1220 result := eInvalid;
1221 jIrr:
1222 begin
1223 JobWork := IrrClearWork;
1224 if (IrrEff = 0) or (RealMap[Loc] and fTerImp = tiIrrigation) or
1225 (RealMap[Loc] and fTerImp = tiFarm) then
1226 result := eInvalid;
1227 end;
1228 jFarm:
1229 if RealMap[Loc] and fTerImp <> tiIrrigation then
1230 result := eNoPreq
1231 else
1232 begin
1233 JobWork := IrrClearWork * FarmWork;
1234 if (JobWork <= 0) or (RealMap[Loc] and fTerImp = tiFarm) then
1235 result := eInvalid;
1236 end;
1237 jAfforest:
1238 if AfforestTerrain >= 0 then
1239 JobWork := MineAfforestWork
1240 else
1241 result := eInvalid;
1242 jMine:
1243 begin
1244 JobWork := MineAfforestWork;
1245 if (MineEff = 0) or (RealMap[Loc] and fTerImp = tiMine) then
1246 result := eInvalid;
1247 end;
1248 jFort:
1249 if RealMap[Loc] and fTerImp <> tiFort then
1250 JobWork := MoveCost * FortWork
1251 else
1252 result := eInvalid;
1253 jCanal:
1254 if (RealMap[Loc] and fCanal = 0) and (TerrType in TerrType_Canalable)
1255 then
1256 JobWork := CanalWork
1257 else
1258 result := eInvalid;
1259 jTrans:
1260 begin
1261 JobWork := TransWork;
1262 if JobWork <= 0 then
1263 result := eInvalid;
1264 end;
1265 jPoll:
1266 if RealMap[Loc] and fPoll <> 0 then
1267 JobWork := PollWork
1268 else
1269 result := eInvalid;
1270 jBase:
1271 if RealMap[Loc] and fTerImp <> tiBase then
1272 JobWork := MoveCost * BaseWork
1273 else
1274 result := eInvalid;
1275 jPillage:
1276 if RealMap[Loc] and (fRoad or fRR or fCanal or fTerImp) <> 0 then
1277 JobWork := PillageWork
1278 else
1279 result := eInvalid;
1280 end;
1281end;
1282
1283function StartJob(p, uix, NewJob: integer; TestOnly: boolean): integer;
1284var
1285 JobWork, Loc0, p1, uix1, TerrType: integer;
1286begin
1287{$IFOPT O-}assert(1 shl p and InvalidTreatyMap = 0); {$ENDIF}
1288 result := eOK;
1289 with RW[p].Un[uix] do
1290 begin
1291 if NewJob = Job then
1292 begin
1293 result := eNotChanged;
1294 exit;
1295 end;
1296 if NewJob = jNone then
1297 begin
1298 if not TestOnly then
1299 Job := jNone;
1300 exit;
1301 end;
1302 Loc0 := Loc;
1303 if (RealMap[Loc0] and fDeadLands <> 0) and (NewJob <> jRoad) and
1304 (NewJob <> jRR) then
1305 begin
1306 result := eDeadLands;
1307 exit;
1308 end;
1309 TerrType := RealMap[Loc0] and fTerrain;
1310 if (RealMap[Loc0] and fCity <> 0) or (TerrType < fGrass) or (Master >= 0) or
1311 not((NewJob = jPillage) and (RW[p].Model[mix].Domain = dGround) or
1312 (RW[p].Model[mix].Kind = mkSettler) or (NewJob <> jCity) and
1313 (RW[p].Model[mix].Kind = mkSlaves) and (GWonder[woPyramids].EffectiveOwner
1314 >= 0)) then
1315 begin
1316 result := eInvalid;
1317 exit;
1318 end;
1319 if (JobPreq[NewJob] <> preNone) and
1320 (RW[p].Tech[JobPreq[NewJob]] < tsApplicable) then
1321 begin
1322 result := eNoPreq;
1323 exit;
1324 end;
1325
1326 result := CalculateJobWork(p, Loc0, NewJob, JobWork);
1327 if (Mode = moPlaying) and (result = eOK) and (NewJob <> jPoll) then
1328 begin // not allowed in territory of friendly nation
1329 p1 := RealMap[Loc0] shr 27; // owner of territory
1330 if (p1 < nPl) and (p1 <> p) and (RW[p].Treaty[p1] >= trPeace) then
1331 result := eTreaty; // keep peace treaty!
1332 end;
1333 if TestOnly or (result < rExecuted) then
1334 exit;
1335
1336 if (ToWork[Loc0, NewJob] = 0) or (ToWork[Loc0, NewJob] > JobWork) then
1337 ToWork[Loc0, NewJob] := JobWork;
1338 Job := NewJob;
1339 Flags := Flags and not unFortified;
1340 for uix1 := 0 to RW[p].nUn - 1 do
1341 if (RW[p].Un[uix1].Loc = Loc) and
1342 (RW[p].Un[uix1].Job in ContraJobs[NewJob]) then
1343 RW[p].Un[uix1].Job := jNone; // stop contradictive jobs
1344 if ServerVersion[p] < $000EF0 then
1345 if Work(p, uix) then
1346 result := eJobDone;
1347 if (NewJob = jCity) and (result = eJobDone) then
1348 begin
1349 RemoveUnit_UpdateMap(p, uix);
1350 result := eCity;
1351 end
1352 else if Health <= 0 then
1353 begin // victim of HostileDamage
1354 RemoveUnit_UpdateMap(p, uix);
1355 result := result or rUnitRemoved;
1356 end;
1357 if Mode > moLoading_Fast then
1358 begin
1359 if result = eCity then
1360 begin
1361 ObserveLevel[Loc0] := ObserveLevel[Loc0] and not(3 shl (2 * p));
1362 Discover21(Loc0, p, lObserveUnhidden, true, true);
1363 // CheckContact;
1364 end;
1365 end;
1366 end;
1367end;
1368
1369function Work(p, uix: integer): boolean;
1370var
1371 uix1, j0: integer;
1372begin
1373 result := false;
1374 with RW[p].Un[uix] do
1375 if Movement >= 100 then
1376 begin
1377 assert(ToWork[Loc, Job] < $FFFF); // should have been set by StartJob
1378 if Job >= jRoad then
1379 if integer(Movement) >= integer(ToWork[Loc, Job]) then { work complete }
1380 begin
1381 result := true;
1382 if Job <> jIrr then
1383 Health := Health - HostileDamage(p, mix, Loc, ToWork[Loc, Job]);
1384 dec(Movement, ToWork[Loc, Job]);
1385 if not(Job in [jCity, jPillage, jPoll]) then
1386 inc(Worked[p], ToWork[Loc, Job]);
1387 if Job = jCity then
1388 begin // found new city
1389 FoundCity(p, Loc);
1390 inc(Founded[p]);
1391 with RW[p].City[RW[p].nCity - 1] do
1392 begin
1393 ID := p shl 12 + Founded[p] - 1;
1394 Flags := chFounded;
1395 end;
1396 if Mode = moPlaying then
1397 begin
1398 LogCheckBorders(p, RW[p].nCity - 1);
1399 RecalcPeaceMap(p);
1400 end;
1401{$IFOPT O-} if Mode < moPlaying then
1402 InvalidTreatyMap := not(1 shl p); {$ENDIF}
1403 // territory should not be considered for the rest of the command
1404 // execution, because during loading a game it's incorrect before
1405 // subsequent sIntExpandTerritory is processed
1406 RW[p].Un[uix].Health := 0; // causes unit to be removed later
1407 end
1408 else
1409 CompleteJob(p, Loc, Job);
1410 ToWork[Loc, Job] := 0;
1411 j0 := Job;
1412 for uix1 := 0 to RW[p].nUn - 1 do
1413 if (RW[p].Un[uix1].Loc = Loc) and (RW[p].Un[uix1].Job = j0) then
1414 RW[p].Un[uix1].Job := jNone
1415 end
1416 else
1417 begin
1418 dec(ToWork[Loc, Job], Movement);
1419 if not(Job in [jCity, jPillage, jPoll]) then
1420 inc(Worked[p], Movement);
1421 Health := Health - HostileDamage(p, mix, Loc, Movement);
1422 Movement := 0;
1423 end
1424 end
1425end;
1426
1427function GetJobProgress(p, Loc: integer;
1428 var JobProgressData: TJobProgressData): integer;
1429var
1430 Job, JobResult, uix: integer;
1431begin
1432 for Job := 0 to nJob - 1 do
1433 begin
1434 JobResult := CalculateJobWork(p, Loc, Job, JobProgressData[Job].Required);
1435 if JobResult = eOK then
1436 begin
1437 if ToWork[Loc, Job] = $FFFF then // not calculated yet
1438 JobProgressData[Job].Done := 0
1439 else
1440 JobProgressData[Job].Done := JobProgressData[Job].Required -
1441 ToWork[Loc, Job];
1442 end
1443 else
1444 begin
1445 JobProgressData[Job].Required := 0;
1446 JobProgressData[Job].Done := 0;
1447 end;
1448 JobProgressData[Job].NextTurnPlus := 0;
1449 end;
1450 for uix := 0 to RW[p].nUn - 1 do
1451 if (RW[p].Un[uix].Loc = Loc) and (RW[p].Un[uix].Movement >= 100) then
1452 inc(JobProgressData[RW[p].Un[uix].Job].NextTurnPlus,
1453 RW[p].Un[uix].Movement);
1454 result := eOK;
1455end;
1456
1457{
1458 Start/End Game
1459 ____________________________________________________________________
1460}
1461procedure InitGame;
1462begin
1463 GetMem(ToWork, 2 * MapSize * nJob);
1464 FillChar(ToWork^, 2 * MapSize * nJob, $FF);
1465end;
1466
1467procedure ReleaseGame;
1468begin
1469 FreeMem(ToWork);
1470end;
1471
1472end.
Note: See TracBrowser for help on using the repository browser.