source: tags/1.2.0/UnitProcessing.pas

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