source: trunk/UnitProcessing.pas

Last change on this file was 663, checked in by chronos, 8 hours ago
  • Fixed: Fixed unit move style hostile calculation in StdAI. Engineers don't take hostile damage on hostile terrain.
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 else MoveCost := -1;
985 end;
986
987 if (MoveCost > 0) and not MountainDelay then
988 if V8 and 1 <> 0 then
989 Inc(MoveCost, MoveCost * 2)
990 else
991 Inc(MoveCost, MoveCost);
992
993 if (MoveInfo and 2 <> 0) // ground unit, check transport load/unload
994 and ((MoveCost < 0) and (ToTile and (fUnit or fOwned) = fUnit or
995 fOwned) // assume ship/airplane is transport -- load!
996 or (MoveCost >= 0) and (FromTile and fTerrain < fGrass)) then
997 MoveCost := MaxMov; // transport load or unload
998
999 if MoveCost >= 0 then
1000 begin { valid move }
1001 MovementLeft := MaxMov - T shr 8 and $FFF - MoveCost;
1002 if (MovementLeft < 0) or ((MoveCost = 0) and (MovementLeft = 0))
1003 then
1004 begin // must wait for next turn
1005 // calculate HostileDamage
1006 if (MoveInfo and 2 <> 0) { ground unit }
1007 and not Resistant and (FromTile and fTerImp <> tiBase) then
1008 if FromTile and (fTerrain or fCity or fRiver or fCanal or
1009 fSpecial1 { Oasis } ) = fDesert then
1010 Inc(AddDamage, (DesertThurst * (MaxMov - T shr 8 and $FFF) -
1011 1) div MaxMov + 1)
1012 else if FromTile and (fTerrain or fCity or fRiver or fCanal) = fArctic
1013 then
1014 Inc(AddDamage, (ArcticThurst * (MaxMov - T shr 8 and $FFF) -
1015 1) div MaxMov + 1);
1016
1017 T1 := T and $7FF000FF + $100000 + (InitMov + MoveCost) shl 8;
1018 end
1019 else
1020 T1 := T + MoveCost shl 8 + 1;
1021 if MountainDelay then
1022 T1 := T1 or $FFF00;
1023 if (Damage[Loc] + AddDamage <= MaxDamage) and (T1 and $FF < $FF)
1024 then
1025 if Q.Put(Loc1, T1) then
1026 begin
1027 From[Loc1] := Loc;
1028 Damage[Loc1] := Damage[Loc] + AddDamage;
1029 end;
1030 end;
1031 end;
1032 end;
1033 end;
1034 end;
1035 FreeAndNil(Q);
1036 if (Loc = A.ToLoc) or (A.ToLoc = maNextCity) and (Loc >= 0) and
1037 (Map[Loc] and fCity <> 0) then
1038 begin
1039 A.MoreTurns := T shr 20;
1040 EndLoc := Loc;
1041 A.nStep := 0;
1042 while Loc <> FromLoc do
1043 begin
1044 if Time[Loc] < $100000 then
1045 Inc(A.nStep);
1046 Loc := From[Loc];
1047 end;
1048 Loc := EndLoc;
1049 I := A.nStep;
1050 while Loc <> FromLoc do
1051 begin
1052 if Time[Loc] < $100000 then
1053 begin
1054 Dec(I);
1055 if I < 25 then
1056 begin
1057 A.dx[I] := ((Loc mod lx * 2 + Loc div lx and 1) -
1058 (From[Loc] mod lx * 2 + From[Loc] div lx and 1) + 3 * lx)
1059 mod (2 * lx) - lx;
1060 A.dy[I] := Loc div lx - From[Loc] div lx;
1061 end;
1062 end;
1063 Loc := From[Loc];
1064 end;
1065 A.MaxHostile_MovementLeft := MaxMov - Time[EndLoc] shr 8 and $FFF;
1066 if A.nStep > 25 then
1067 A.nStep := 25;
1068 Result := eOK;
1069 end
1070 else
1071 Result := eNoWay;
1072
1073 // QueryPerformanceCounter(tt);{time in s is: (tt-tt0)/PerfFreq}
1074end;
1075
1076function CanPlaneReturn(P, uix: Integer;
1077 PlaneReturnData: TPlaneReturnData): Boolean;
1078const
1079 mfEnd = 1;
1080 mfReached = 2;
1081var
1082 uix1, T, T1, Loc, Loc1, FromTile, ToTile, V8, MoveCost, maxmov: Integer;
1083 Map: ^TTileList;
1084 Q: TIPQ;
1085 Adjacent: TVicinity8Loc;
1086 MapFlags: array [0 .. lxmax * lymax - 1] of Byte;
1087begin
1088 Map := @(RW[P].Map^);
1089
1090 // calculate possible return points
1091 FillChar(MapFlags, SizeOf(MapFlags), 0);
1092 if RW[P].Model[RW[P].Un[uix].mix].Kind = mkSpecial_Glider then
1093 begin
1094 for Loc := 0 to MapSize - 1 do
1095 if Map[Loc] and fTerrain >= fGrass then
1096 MapFlags[Loc] := MapFlags[Loc] or mfEnd;
1097 end
1098 else
1099 begin
1100 for Loc := 0 to MapSize - 1 do
1101 if (Map[Loc] and (fCity or fOwned) = fCity or fOwned) or
1102 (Map[Loc] and fTerImp = tiBase) and (Map[Loc] and fObserved <> 0) and
1103 (Map[Loc] and (fUnit or fOwned) <> fUnit) then
1104 MapFlags[Loc] := MapFlags[Loc] or mfEnd;
1105 if RW[P].Model[RW[P].Un[uix].mix].Cap[mcAirTrans] = 0 then
1106 // plane can land on carriers
1107 for uix1 := 0 to RW[P].nUn - 1 do
1108 with RW[P].Un[uix1], RW[P].Model[mix] do
1109 if AirLoad < MTrans * Cap[mcCarrier] then
1110 MapFlags[Loc] := MapFlags[Loc] or mfEnd;
1111 end;
1112
1113 with RW[P].Un[uix] do
1114 begin
1115 if Master >= 0 then // can return to same carrier, even if full now
1116 MapFlags[Loc] := MapFlags[Loc] or mfEnd;
1117 maxmov := RW[P].Model[mix].Speed;
1118 end;
1119
1120 Result := False;
1121 Q := TIPQ.Create(MapSize);
1122 Q.Put(PlaneReturnData.Loc, (maxmov - PlaneReturnData.Movement) shl 8);
1123 while Q.Get(Loc, T) do
1124 begin
1125 MapFlags[Loc] := MapFlags[Loc] or mfReached;
1126 if T >= (PlaneReturnData.Fuel + 1) shl 20 then
1127 begin
1128 Result := False;
1129 Break;
1130 end;
1131 if MapFlags[Loc] and mfEnd <> 0 then
1132 begin
1133 Result := True;
1134 Break;
1135 end;
1136 FromTile := Map[Loc];
1137 V8_to_Loc(Loc, Adjacent);
1138 for V8 := 0 to 7 do
1139 begin
1140 Loc1 := Adjacent[V8];
1141 if (Loc1 >= 0) and (Loc1 < MapSize) and (MapFlags[Loc1] and mfReached = 0)
1142 then
1143 begin
1144 ToTile := Map[Loc1];
1145 if (ToTile and fTerrain <> fUNKNOWN) and
1146 (ToTile and (fCity or fOwned) <> fCity)
1147 // don't move through enemy cities
1148 and (ToTile and (fUnit or fOwned) <> fUnit) // way is blocked
1149 and (ToTile and not FromTile and fPeace = 0) then
1150 begin
1151 if V8 and 1 <> 0 then
1152 MoveCost := 150
1153 else
1154 MoveCost := 100;
1155 if MoveCost + T shr 8 and $FFF > maxmov then
1156 // must wait for next turn
1157 T1 := T and $7FF000FF + $100000 + MoveCost shl 8
1158 else
1159 T1 := T + MoveCost shl 8;
1160 Q.Put(Loc1, T1);
1161 end;
1162 end;
1163 end;
1164 end;
1165 FreeAndNil(Q);
1166end;
1167
1168{
1169 Terrain Improvement
1170 ____________________________________________________________________
1171}
1172function CalculateJobWork(P, Loc, Job: Integer; var JobWork: Integer): Integer;
1173var
1174 TerrType: Integer;
1175begin
1176 Result := eOK;
1177 TerrType := RealMap[Loc] and fTerrain;
1178 with Terrain[TerrType] do
1179 case Job of
1180 jCity:
1181 if RealMap[Loc] and fCity <> 0 then
1182 Result := eInvalid
1183 else if IrrEff = 0 then
1184 Result := eNoCityTerrain
1185 else
1186 JobWork := CityWork;
1187 jRoad:
1188 if RealMap[Loc] and (fRoad or fRR) = 0 then
1189 begin
1190 JobWork := MoveCost * RoadWork;
1191 if RealMap[Loc] and fRiver <> 0 then
1192 if RW[P].Tech[adBridgeBuilding] >= tsApplicable then
1193 Inc(JobWork, RoadBridgeWork) { across river }
1194 else
1195 Result := eNoBridgeBuilding;
1196 end
1197 else
1198 Result := eInvalid;
1199 jRR:
1200 if RealMap[Loc] and fRoad = 0 then
1201 Result := eNoPreq
1202 else if RealMap[Loc] and fRR <> 0 then
1203 Result := eInvalid
1204 else
1205 begin
1206 JobWork := MoveCost * RRWork;
1207 if RealMap[Loc] and fRiver <> 0 then
1208 Inc(JobWork, RRBridgeWork); { across river }
1209 end;
1210 jClear:
1211 if (TerrType = fDesert) and (GWonder[woGardens].EffectiveOwner <> P)
1212 then
1213 Result := eInvalid
1214 else if ClearTerrain >= 0 then
1215 JobWork := IrrClearWork
1216 else
1217 Result := eInvalid;
1218 jIrr:
1219 begin
1220 JobWork := IrrClearWork;
1221 if (IrrEff = 0) or (RealMap[Loc] and fTerImp = tiIrrigation) or
1222 (RealMap[Loc] and fTerImp = tiFarm) then
1223 Result := eInvalid;
1224 end;
1225 jFarm:
1226 if RealMap[Loc] and fTerImp <> tiIrrigation then
1227 Result := eNoPreq
1228 else
1229 begin
1230 JobWork := IrrClearWork * FarmWork;
1231 if (JobWork <= 0) or (RealMap[Loc] and fTerImp = tiFarm) then
1232 Result := eInvalid;
1233 end;
1234 jAfforest:
1235 if AfforestTerrain >= 0 then
1236 JobWork := MineAfforestWork
1237 else
1238 Result := eInvalid;
1239 jMine:
1240 begin
1241 JobWork := MineAfforestWork;
1242 if (MineEff = 0) or (RealMap[Loc] and fTerImp = tiMine) then
1243 Result := eInvalid;
1244 end;
1245 jFort:
1246 if RealMap[Loc] and fTerImp <> tiFort then
1247 JobWork := MoveCost * FortWork
1248 else
1249 Result := eInvalid;
1250 jCanal:
1251 if (RealMap[Loc] and fCanal = 0) and (TerrType in TerrType_Canalable)
1252 then
1253 JobWork := CanalWork
1254 else
1255 Result := eInvalid;
1256 jTrans:
1257 begin
1258 JobWork := TransWork;
1259 if JobWork <= 0 then
1260 Result := eInvalid;
1261 end;
1262 jPoll:
1263 if RealMap[Loc] and fPoll <> 0 then
1264 JobWork := PollWork
1265 else
1266 Result := eInvalid;
1267 jBase:
1268 if RealMap[Loc] and fTerImp <> tiBase then
1269 JobWork := MoveCost * BaseWork
1270 else
1271 Result := eInvalid;
1272 jPillage:
1273 if RealMap[Loc] and (fRoad or fRR or fCanal or fTerImp) <> 0 then
1274 JobWork := PillageWork
1275 else
1276 Result := eInvalid;
1277 end;
1278end;
1279
1280function StartJob(P, uix, NewJob: Integer; TestOnly: Boolean): Integer;
1281var
1282 JobWork, Loc0, p1, uix1, TerrType: Integer;
1283begin
1284{$IFOPT O-}Assert(1 shl P and InvalidTreatyMap = 0); {$ENDIF}
1285 Result := eOK;
1286 with RW[P].Un[uix] do
1287 begin
1288 if NewJob = Job then
1289 begin
1290 Result := eNotChanged;
1291 Exit;
1292 end;
1293 if NewJob = jNone then
1294 begin
1295 if not TestOnly then
1296 Job := jNone;
1297 Exit;
1298 end;
1299 Loc0 := Loc;
1300 if (RealMap[Loc0] and fDeadLands <> 0) and (NewJob <> jRoad) and
1301 (NewJob <> jRR) then
1302 begin
1303 Result := eDeadLands;
1304 Exit;
1305 end;
1306 TerrType := RealMap[Loc0] and fTerrain;
1307 if (RealMap[Loc0] and fCity <> 0) or (TerrType < fGrass) or (Master >= 0) or
1308 not ((NewJob = jPillage) and (RW[P].Model[mix].Domain = dGround) or
1309 (RW[P].Model[mix].Kind = mkSettler) or (NewJob <> jCity) and
1310 (RW[P].Model[mix].Kind = mkSlaves) and (GWonder[woPyramids].EffectiveOwner
1311 >= 0)) then
1312 begin
1313 Result := eInvalid;
1314 Exit;
1315 end;
1316 if (JobPreq[NewJob] <> preNone) and
1317 (RW[P].Tech[JobPreq[NewJob]] < tsApplicable) then
1318 begin
1319 Result := eNoPreq;
1320 Exit;
1321 end;
1322
1323 Result := CalculateJobWork(P, Loc0, NewJob, JobWork);
1324 if (Mode = moPlaying) and (Result = eOK) and (NewJob <> jPoll) then
1325 begin // not allowed in territory of friendly nation
1326 p1 := RealMap[Loc0] shr 27; // owner of territory
1327 if (p1 < nPl) and (p1 <> P) and (RW[P].Treaty[p1] >= trPeace) then
1328 Result := eTreaty; // keep peace treaty!
1329 end;
1330 if TestOnly or (Result < rExecuted) then
1331 Exit;
1332
1333 if (ToWork[Loc0, NewJob] = 0) or (ToWork[Loc0, NewJob] > JobWork) then
1334 ToWork[Loc0, NewJob] := JobWork;
1335 Job := NewJob;
1336 Flags := Flags and not unFortified;
1337 for uix1 := 0 to RW[P].nUn - 1 do
1338 if (RW[P].Un[uix1].Loc = Loc) and
1339 (RW[P].Un[uix1].Job in ContraJobs[NewJob]) then
1340 RW[P].Un[uix1].Job := jNone; // stop contradictive jobs
1341 if ServerVersion[P] < $000EF0 then
1342 if Work(P, uix) then
1343 Result := eJobDone;
1344 if (NewJob = jCity) and (Result = eJobDone) then
1345 begin
1346 RemoveUnit_UpdateMap(P, uix);
1347 Result := eCity;
1348 end
1349 else if Health <= 0 then
1350 begin // victim of HostileDamage
1351 RemoveUnit_UpdateMap(P, uix);
1352 Result := Result or rUnitRemoved;
1353 end;
1354 if Mode > moLoading_Fast then
1355 begin
1356 if Result = eCity then
1357 begin
1358 ObserveLevel[Loc0] := ObserveLevel[Loc0] and not (3 shl (2 * P));
1359 Discover21(Loc0, P, lObserveUnhidden, True, True);
1360 // CheckContact;
1361 end;
1362 end;
1363 end;
1364end;
1365
1366function Work(P, uix: Integer): Boolean;
1367var
1368 uix1, j0: Integer;
1369begin
1370 Result := False;
1371 with RW[P].Un[uix] do
1372 if Movement >= 100 then
1373 begin
1374 Assert(ToWork[Loc, Job] < $FFFF); // should have been set by StartJob
1375 if Job >= jRoad then
1376 if Integer(Movement) >= Integer(ToWork[Loc, Job]) then { work complete }
1377 begin
1378 Result := True;
1379 if Job <> jIrr then
1380 Health := Health - HostileDamage(P, mix, Loc, ToWork[Loc, Job]);
1381 Dec(Movement, ToWork[Loc, Job]);
1382 if not (Job in [jCity, jPillage, jPoll]) then
1383 Inc(Worked[P], ToWork[Loc, Job]);
1384 if Job = jCity then
1385 begin // found new city
1386 FoundCity(P, Loc);
1387 Inc(Founded[P]);
1388 with RW[P].City[RW[P].nCity - 1] do
1389 begin
1390 ID := P shl 12 + Founded[P] - 1;
1391 Flags := chFounded;
1392 end;
1393 if Mode = moPlaying then
1394 begin
1395 LogCheckBorders(P, RW[P].nCity - 1);
1396 RecalcPeaceMap(P);
1397 end;
1398{$IFOPT O-} if Mode < moPlaying then
1399 InvalidTreatyMap := not (1 shl P); {$ENDIF}
1400 // territory should not be considered for the rest of the command
1401 // execution, because during loading a game it's incorrect before
1402 // subsequent sIntExpandTerritory is processed
1403 RW[P].Un[uix].Health := 0; // causes unit to be removed later
1404 end
1405 else
1406 CompleteJob(P, Loc, Job);
1407 ToWork[Loc, Job] := 0;
1408 j0 := Job;
1409 for uix1 := 0 to RW[P].nUn - 1 do
1410 if (RW[P].Un[uix1].Loc = Loc) and (RW[P].Un[uix1].Job = j0) then
1411 RW[P].Un[uix1].Job := jNone;
1412 end
1413 else
1414 begin
1415 Dec(ToWork[Loc, Job], Movement);
1416 if not (Job in [jCity, jPillage, jPoll]) then
1417 Inc(Worked[P], Movement);
1418 Health := Health - HostileDamage(P, mix, Loc, Movement);
1419 Movement := 0;
1420 end;
1421 end;
1422end;
1423
1424function GetJobProgress(P, Loc: Integer;
1425 var JobProgressData: TJobProgressData): Integer;
1426var
1427 Job, JobResult, uix: Integer;
1428begin
1429 for Job := 0 to nJob - 1 do
1430 begin
1431 JobResult := CalculateJobWork(P, Loc, Job, JobProgressData[Job].Required);
1432 if JobResult = eOK then
1433 begin
1434 if ToWork[Loc, Job] = $FFFF then // not calculated yet
1435 JobProgressData[Job].Done := 0
1436 else
1437 JobProgressData[Job].Done := JobProgressData[Job].Required -
1438 ToWork[Loc, Job];
1439 end
1440 else
1441 begin
1442 JobProgressData[Job].Required := 0;
1443 JobProgressData[Job].Done := 0;
1444 end;
1445 JobProgressData[Job].NextTurnPlus := 0;
1446 end;
1447 for uix := 0 to RW[P].nUn - 1 do
1448 if (RW[P].Un[uix].Loc = Loc) and (RW[P].Un[uix].Movement >= 100) then
1449 Inc(JobProgressData[RW[P].Un[uix].Job].NextTurnPlus,
1450 RW[P].Un[uix].Movement);
1451 Result := eOK;
1452end;
1453
1454{
1455 Start/End Game
1456 ____________________________________________________________________
1457}
1458procedure InitGame;
1459begin
1460 GetMem(ToWork, 2 * MapSize * nJob);
1461 FillChar(ToWork^, 2 * MapSize * nJob, $FF);
1462end;
1463
1464procedure ReleaseGame;
1465begin
1466 FreeMem(ToWork);
1467end;
1468
1469end.
Note: See TracBrowser for help on using the repository browser.