RE: I believe this game will never happen
Posted: Wed Jun 12, 2013 4:51 pm
[quote]ORIGINAL: Extraneous
[quote]ORIGINAL: Shannon V. OKeets
My 10 years of playing WIF over the board yielded "very little knowledge"????[:D]
[/quote]
And here I thought when we started you were new at WiF.
[quote]ORIGINAL: Centuur
I think that a sane programmer who didn't play WiF over the board wouldn't start coding it... He would read the rulebook and throw the whole thing directly into a wastebucket. Far to difficult... That's why I love the fact that there is a madman here who played the game and is a skilled games programmer too...
[/quote]
Evidently you wouldn't pass the aptitude test for programming (yes there was/is one).
I find it laughable the way you all assume programming is such a hard thing to do.
Here is how it works in the real world.
A Systems analyst is given the project and meets with the user to define the specifications of the project
The Systems analyst then assigns Programmers from their group deadlines to write programs (executables, modules, or etc.) for the project.
The Programmers are to code and desk check their programs as completely as possible before submitting their programs to the test group.
The test group submits reports on errors found in the programs to the Systems analyst.
The Systems analyst returns the error report to the correct programmer for corrections.
The Systems analyst is responsible for seeing that all programmers provide documentation of their work, that the programmers are notified of changes from the user, and for the final presentation of the finished product.
Simple huh.[:D]
(Substitute: Developer for Systems analyst, game for project, rules for user, beta testers for test group and you have an Idea how a system design works.)
[/quote]
You keep assuming you know more about a topic because you've read something about it. People who work in the field have several orders of magnitude more knowledge than someone who has merely read up on a subject.
Here is some "simple programming" for you to critique. This is an excerpt of ~4000 lines of code from a module of 11,000 lines. There are ~250 modules in MWIF, totaling over 400,000 lines of code.
The following code (which references hundreds of code segments in other modules) is to determine if a stack of units that the player has picked up can move to the hex over which the player has moved the cursor. This executes in real time as the player moves the mouse over the map. For each hex the cursor passes over, a message is shown on the Main form telling the player whether the move is legal - and if not, why not. The cursor itself is also updated to either a target icon (ok move) or an X (move illegal).
By the way, the above paragraph IS the program specification for this function. It might also include a reference to the Rules as Written document for determining whether a stack of units can move to a hex. And RAW is perfectly clear in all particulars.[;)]
EDITED: For grammar and to add the last paragraph above.
---
// ****************************************************************************
function TMovingStack.CanMoveTo(const MULF: TUnitLocationFull;
var Disrupt: Boolean;
const CheckIfValid: Boolean = True;
const CheckShift: Boolean = True): TMoveResult;
var
U: TUnit;
FirstMU: TUnit; // First Moving Unit in Self.
C: TMajorCountry;
HexMP: TMajorCountry;
MPow: TMajorCountry;
Index: Longint;
RR: TRailHexData;
Old: TUnitLocationFull;
HS: THexsideRange;
MapHex: TMapStack;
SAStack: TUnitStack;
RefC: Integer; // Reference hex for sea area.
RefR: Integer;
Good: Boolean;
HasAir: Boolean;
Res: TMoveResult;
AHR: TAirHexRecord;
LHR: TLandHexRecord;
HR: TNavalHexRecord;
SectionSet: set of TSectionRange;
HQAdjacent: Boolean;
HQSameHex: Boolean;
MC: TGovernedArea;
HexHomeC: TMinorCountry;
MajC: TMajorCountry;
HexC: TMajorCountry;
HexMinC: TMinorCountry;
AirMaxRange: Integer;
ADistance: Integer;
HDistance: Integer;
CR: TLandCombatHex;
CR2: TLandCombatHex;
SameHex: Boolean;
MoveToSameHexNotOk: Boolean;
NavalUnitCounts: array [TMajorCountries] of Word;
Bombers: array [TMajorCountries] of Word;
LandUnits: array [TMajorCountries] of Word;
AirUnits: array [TMajorCountries] of Word;
MPI: TMajorCountries;
CCCount: Integer;
CCIndex: Integer; // Index for looking for Communist Chinese units.
CCUnit: TUnit;
CCFound: Boolean;
Coop: Boolean;
RebaseRangeMS: Integer;
// DistToHex: Integer;
CheckExtended: Boolean;
DoubleRange: Integer;
Shift: TShiftState;
MaxRange: Integer;
EastPol: Boolean;
BalticStat: Boolean;
F1stMovSUIndx2: Integer;
F1stMovSUni2: TUnit;
FoundMovSUni2: TUnit;
F1stMovSUIndx4: Integer;
F1stMovSUni4: TNavalUnit;
FoundMovSUni4: TNavalUnit;
CantTravel: Boolean;
Cargo: TUnit;
First: TUnit;
Second: TUnit;
procedure CheckUsingSetupTray;
begin
// ****************************************************************************
// Check for placing units on the map from the setup tray.
// ****************************************************************************
MapHex := MapStacks[MULF.Column, MULF.Row]; // Map stack of destination.
Result := CanSetup(MULF.Column, MULF.Row); // CanMoveTo.
if Result = mvOK then Result := CanStack(MapHex); // Setup stacking limits.
if (Result = mvStackingAir) and
(not MULF.AtSea) and
(UnitCount(UFilterCarrierAirUnit) = 1) then
begin
UFUnit := FindUnit(UFilterCarrierAirUnit);
UFPhase := Game.Phase;
UFSubPhase := aspNone;
if MapHex.HasUnit(UFilterCarrierCanLoadPlane) then Result := mvOK;
end;
end; // End of CheckUsingSetupTray.
function MovingNeutral: Boolean;
begin
// ****************************************************************************
// Check for neutral country trying to move.
// ****************************************************************************
Result := False;
F1stMovSUIndx2 := 0;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if F1stMovSUni2.NeutralProhibitedHex(MULF.Column, MULF.Row) then
begin
CanMoveTo := mvNeutralMove;
Result := True;
Exit;
end;
Inc(F1stMovSUIndx2);
end;
end;
function FTCFails: Boolean;
// ****************************************************************************
// Under very special circumstances, check for foreign troop commitment.
// ****************************************************************************
function NotEnoughMPOrRange: Boolean;
var
Index: Integer;
HRMP: TnavalHexRecord;
begin // Test if range and movement points permit a naval move.
Result :=
(not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index, HRMP)) or
(MaxNavalRange - HRMP.AvoidRange <= 0) or
(MaxNavalMovement - HRMP.AvoidMP <= 0);
end;
begin // FTCFails.
Result := False;
if (HexC <> nil) and // Hex must have an owner.
(FirstMU.Side = HexC.Side) and // Moving unit & hex on same side.
(HexMP <> nil) and
(HexMP.ID = HexC.ID) and
((not CheckShift) or
(not (ssCtrl in Shift)) or
(Game.Phase <> pNavalMovement) or
LoadedInPort or
NotEnoughMPOrRange) and // Use NavalHexList to check MPs and range.
(not CanMoveToCountry(Map.HexFTCCountry[MULF.Column, MULF.Row])) then
begin // 'The foreign troop commitment limit for %s will not be satisfied.'
(*
ShowMessageOK('[TMovingStack.CanMoveTo] FTCFails' +
'. HexMP = ' + HexMP.Name +
'. FirstMU''s country = ' +
Countries[FirstMU.Country].Name );
*)
CanMoveTo := mvForeignCommitment;
Result := True;
end;
end; // End of FTCFails.
function InvalidNavalGroup: Boolean;
begin
Result := False;
SectionSet := [];
if HasSurfaceAndSubs then
begin
CanMoveTo := mvSurfaceSubs; // Cannot move surface naval & subs together.
Result := True;
Exit;
end
else
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if (SectionSet <> []) and (not(F1stMovSUni2.Section in SectionSet)) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end
else
Include(SectionSet, F1stMovSUni2.Section);
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 <> nil then
begin // Units can't start in different sections.
CanMoveTo := mvNotAllSameSection;
Result := True;
Exit;
end;
end;
end; // End of InvalidNavalGroup.
function IllegalPartisanMove: Boolean;
begin
Result := False;
F1stMovSUIndx2 := 0;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if (F1stMovSUni2.UnitType = utPartisan) and
(MULF.AtSea or (Map.HexHomeCountry[MULF.Column,
MULF.Row] <> UnitHomeCountry(F1stMovSUni2))) then
begin
CanMoveTo := mvPartisan;
Result := True; // True means the the partisan was moved illegally.
Exit;
end;
Inc(F1stMovSUIndx2);
end;
end; // End of IllegalPartisanMove.
function IllegalWarlordMove: Boolean;
function CheckWarlord(var U: TUnit): Boolean;
var
Hx: TSmallPoint;
begin // True means the the warlord was moved illegally.
Result := (U.UnitType = utWarlord);
if Result then
begin
Hx := SmallPoint(TLandUnit(U).CityColumn, TLandUnit(U).CityRow);
Result := MULF.AtSea or (TGameMapArea.HexDistance(MULF, Hx) > 6);
end;
end;
begin // IllegalWarlordMove.
Result := False;
F1stMovSUIndx2 := 0;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if CheckWarlord(F1stMovSUni2) then
begin
CanMoveTo := mvWarlordRange; // Moving a warlord unit illegally.
Result := True;
Exit;
end;
Inc(F1stMovSUIndx2);
end;
end; // End of IllegalWarlordMove.
procedure ComputeUnits;
var
MStckIndx: Integer;
MStckUni: TUnit;
// ****************************************************************************
// Count the number of limited moves needed to move the stack.
// ****************************************************************************
var
MCCounter: TMajorCountries;
ConvoyPts: array [TMajorCountries] of Word; // Convoys are worth 1/2.
procedure CountUnit(var U: TUnit);
var
MCi: TMajorCountries;
begin
MCi := UnitControllingMajorCountry(U).Value;
if UFilterStackingNonConvoyNavalUnit(U) then
Inc(NavalUnitCounts[MCi])
else if UFilterConvoyUnit(U) then
Inc(ConvoyPts[MCi], TNavalUnit(U).Convoy)
else if UFilterAirUnit(U) then
begin
Inc(AirUnits[MCi]);
if UFilterBomber(U) then
Inc(Bombers[MCi]);
end
else if UFilterLandUnit(U) and (not U.LandMovesForFree) then
Inc(LandUnits[MCi]);
end;
begin // ComputeUnits.
FillChar(NavalUnitCounts, SizeOf(NavalUnitCounts), 0); // Naval unit moves.
FillChar(ConvoyPts, SizeOf(ConvoyPts), 0); // Convoys = 1/2.
FillChar(Bombers, SizeOf(Bombers), 0); // Bombing missions.
FillChar(LandUnits, SizeOf(LandUnits), 0); // Land unit moves.
FillChar(AirUnits, SizeOf(LandUnits), 0); // Air unit moves.
MStckIndx := 0;
while MStckIndx < Count do
begin
MStckUni := TUnit(Item[MStckIndx]);
CountUnit(MStckUni);
Inc(MStckIndx);
end;
// ****************************************************************************
// For every 2 convoys add 1 naval move.
// ****************************************************************************
for MCCounter := Low(TMajorCountries) to High(TMajorCountries) do
Inc(NavalUnitCounts[MCCounter], Succ(ConvoyPts[MCCounter]) div 2);
end; // End of ComputeUnits.
function RestrictedReinforcement: Boolean;
// ****************************************************************************
// Check if the player is trying to move units to reinforce an area in the
// Pacific in violation of the US Entry options/restrictions.
// ****************************************************************************
function CheckReinforcing(var U: TUnit): Boolean;
var
Loc: TSetupLocation;
begin
Result := True; // Default is violation occurs.
// ****************************************************************************
// US Entry option #26, US relocates fleet to Pearl Harbor.
// Only US naval transports and convoys can base at Honolulu or Pago Pago unless
// option #26 has been chosen.
// ****************************************************************************
if (EqualSmallPt(MULF.Hex, Honolulu) or
EqualSmallPt(MULF.Hex, PagoPago)) and
(not USEntry.OptionChosen(useRelocate)) and
UFilterUSNotPearlHarbor(U) then
begin
CanMoveTo := mvUSPearlHarbor; // Assign result for function CanMoveTo.
Exit;
end;
// ****************************************************************************
// Allied land and aircraft units are prohibited from entering certain areas of
// the map until US entry options have been taken, or the Axis has moved there.
// ****************************************************************************
if UFilterAlliedAirNotOnCarrierOrLandUnit(U) then
begin
// ****************************************************************************
// US Entry option #43, CW reinforces the NEI.
// Only NEI land and air units can enter NEI until (1) the Comonwealth and Japan
// are at war, or (2) option #43 has been selected, or (3) an axis land unit has
// entered NEI. All 3 of these are kept track of with CWCanReinforceNEI.
// ****************************************************************************
if (MC = NEI) and // MC is the country of the hex under the cursor.
(U.SourceCountry <> NEI.ID) and (not CWCanReinforceNEI) then
begin
CanMoveTo := mvCWNEI; // Assign result for function CanMoveTo.
Exit;
end;
// ****************************************************************************
// US Entry option #36, CW reinforces the Pacific.
// Allied land and air units can not enter Hong Kong or any Commonwealth
// controlled territory in the Pacific until (1) The Comonwealth and Japan are
// at war, (2) option #36 has been selected, or (3) an Axis land unit has
// entered Hong Kong or a Commonwealth controlled territory in the Pacific. All
// 3 of these are kept track of with CWCanReinforcePacific.
// ****************************************************************************
if ((MC = HongKong) or
((MC.ControllingMajorPower = Commonwealth) and
(MC.ClassType = TGovernedArea) and
MC.Pacific)) and
(not CWCanReinforcePacific) then
begin
CanMoveTo := mvCWPacific; // Assign result for function CanMoveTo.
Exit;
end;
// ****************************************************************************
// US Entry option #40, USA reinforces Guam.
// USA land and air units can not enter Guam until (1) option #40 has been
// selected or (2) an Axis land unit has entered Guam or the Marshalls. Both of
// these are kept track of with USCanReinforceGuam.
// ****************************************************************************
if EqualSmallPt(MULF.Hex, Guam) and (not USCanReinforceGuam) then
begin
CanMoveTo := mvUSGuam; // Assign result for function CanMoveTo.
Exit;
end;
// ****************************************************************************
// US Entry option #41, US reinforces the Philippines.
// USA land and air units can not enter the Philippines until (1) option #40 has
// been selected or (2) an Axis land unit has entered the Philippines. Both of
// these are kept track of with USCanReinforcePhilippines.
// In some scenarios the US starts with units in the Philippines. An exception
// is made to permit those units to relocate within the Philippines.
// Territorial units are not subject to this rule.
// ****************************************************************************
if (MC = Philippines) and
(U.SourceCountry <> Philippines.ID) and
(not USCanReinforcePhilippines) and
((not PickUpPoint.OnMap) or
(Map.HexCountry[PickUpPoint.Column, PickUpPoint.Row] <>
Philippines)) then
begin
// ****************************************************************************
// Unit's SetupGroupIndex - 1 is the index into SULocations.
// ****************************************************************************
if (Game.Phase = pSetup) and EqualSmallPt(MULF.Hex, Manila) then
begin
Loc := SULocations[U.SetupGroupIndex - 1];
if String(Loc.CityName) <> rsManila then
begin
CanMoveTo := mvUSPhilippines; // Result for function CanMoveTo.
Exit;
end;
end
else
begin
CanMoveTo := mvUSPhilippines; // Result for function CanMoveTo.
Exit;
end;
end;
end;
Result := False; // No violation occurred.
end;
begin // RestrictedReinforcement.
Result := False;
// ****************************************************************************
// Restrictions on setting up do not apply to later scenarios.
// ****************************************************************************
if CurrScenario in [scGuadalCanal, scBruteForce, scDarkness,
scDeclineAndFall] then
Exit;
if (MC <> nil) and (not HasAllUnit(UFilterPartisan)) then
begin
F1stMovSUIndx2 := 0;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if CheckReinforcing(F1stMovSUni2) then
begin
Result := True; // Move will violate restricted reinforcement.
Exit;
end;
Inc(F1stMovSUIndx2);
end;
end;
end; // End of RestrictedReinforcement.
function InsufficientAirMissions: Boolean;
var
MPIndex: TMajorCountries;
begin
Result := False;
// ****************************************************************************
// Check for exceeding air mission limits.
// ****************************************************************************
if HasAir then
begin
// ****************************************************************************
// Compare the number of bombers in the moving stack to the air mission limits
// for the owning major power.
// ****************************************************************************
for MPIndex := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPIndex];
if MPow.LegalCountry and
(MPow.CurrLimits.AirMissions <> aUnlimited) and
(Bombers[MPIndex] > MPow.CurrLimits.AirMissions) then
begin
CanMoveTo := mvAirMissions;
Result := True;
Exit;
end;
end;
end;
end; // End of InsufficientAirMissions.
function NoCooperationWithTarget: Boolean;
// ****************************************************************************
// This routine returns True if there is no cooperation or if the target is out
// of range. Otherwise it sets Good depending on the target hex's terrain.
// ****************************************************************************
function CheckForNoBombers: Boolean;
var
NoNeedToCheck: Boolean;
// ****************************************************************************
// This routine returns True if there no bombers in the target hex and a fighter
// is attempting to fly to the hex.
// ****************************************************************************
begin
// ****************************************************************************
// There is no need to check for bombers during:
// AspCAP,
// during any subphase except AspFlyA of non-GroundSupport air missions,
// during any subphase except AspFlyA and AspFlyD of Ground Support missions.
// ****************************************************************************
NoNeedToCheck := (Game.AirSubPhase = AspCAP) or
((Game.Phase <> pGroundSupport) and
(Game.AirSubPhase <> AspFlyA)) or
((Game.Phase = pGroundSupport) and
(not (Game.AirSubPhase in [AspFlyA, AspFlyD])));
if NoNeedToCheck then
begin
Result := False;
Exit;
end;
// ****************************************************************************
// Check for fighter flying day missions.
// ****************************************************************************
Result := HasUnit(UFilterFighterDayMission) and
(not HasUnit(UFilterBomberDayMission)) and // No bomber in stack.
(((Game.AirSubPhase = AspFlyA) and // No bomber in hex.
(not MapHex.HasUnit(UFilterBomberFlyingDayMission))) or
((Game.AirSubPhase = AspFlyD) and
(not OtherStackFilter(MapHex,
UFilterFriendlyBomberFlyingDayMission))
and
((not OtherStackFilter(MapHex,
UFilterEnemyFlyingDayMission)) or
(Self.AirDistanceFromTo(PickUpPoint, MULF.Hex, MaxRange) >
MaxRange))));
if Result then Exit;
// ****************************************************************************
// Check for bombers flying night missions.
// ****************************************************************************
Result := HasUnit(UFilterFighterNightMission) and
(not HasUnit(UFilterBomberNightMission)) and // No bomber in stack.
(((Game.AirSubPhase = AspFlyA) and // No bomber in hex.
(not MapHex.HasUnit(UFilterBomberFlyingNightMission))) or
((Game.AirSubPhase = AspFlyD) and
(not OtherStackFilter(MapHex,
UFilterFriendlyBomberFlyingNightMission))
and
((not OtherStackFilter(MapHex,
UFilterEnemyFlyingNightMission)) or
(Self.AirDistanceFromTo(PickUpPoint, MULF.Hex, MaxRange) >
Values.ARngMax))));
end;
begin
// ****************************************************************************
// NoCooperationWithTarget.
// Set cooperation with target units during ground strikes and ground support.
// Check artillery too if ground strike or ground support phase.
// ****************************************************************************
if HasAir or (Game.Phase in [pGroundSupport, pGroundStrike]) then
begin
if Game.Phase in [pGroundSupport, pGroundStrike] then
begin
if Side = Game.PhasingSide then // Non-phasing side is ok.
begin
LandCombatHexes.Search(MULF.Hex, Index, CR); // Land combat hex.
if CR = nil then Coop := CooperatesFlying(MapHex)
else Coop := CooperatesNotFlying(CR.CombatHexUnits) and
CooperatesFlying(MapHex);
end // Non-phasing side in ground support and ground strike.
else Coop := Cooperates(MapHex);
end
else Coop := CooperatesFlying(MapHex);
end
else Coop := True;
// ****************************************************************************
// Set MaxRange for ground support to ???
// ****************************************************************************
if Game.Phase = pGroundSupport then MaxRange := (Values.ARngMin + 1) div 2
else MaxRange := 0;
// ****************************************************************************
// Check that bombers (and artillery) correctly cooperate and are within range.
// ****************************************************************************
Result := False;
if (not Coop) or (not Self.Cooperates(Self)) then
begin
CanMoveTo := mvStackingNoCoop; // NoCooperationWithTarget.
Result := True;
end
else if CheckForNoBombers then
begin // No bombers when one must be present.
CanMoveTo := mvAirNoBombers;
Result := True;
end
else
begin
// ****************************************************************************
// Determine terrain conditions in target hex.
// ****************************************************************************
case Map.WeatherTerrain[MULF.Column, MULF.Row] of
teSea: Good := False;
teLake: Good := HasAir;
else Good := True;
end;
end; // End of non-cooperation.
end; // End of NoCooperationWithTarget.
function UnableToFlyToHex: Boolean;
begin // Called for each of the 8 air mission phases.
Result := False;
if HasAir then
begin
if (ADistance > AirMaxRange) and
// ****************************************************************************
// More severe restrictions than simply being within range apply during port
// attacks by air units that started at sea. They must be in a sea area that is
// adjacent to the port.
// ****************************************************************************
((Game.Phase <> pPortAttack) or
(not StartedAtSea) or
(ADistance > 1) or
(Map.Port[MULF.Column, MULF.Row] = ptNone)) then
begin
CanMoveTo := mvOutOfRange; // 8 air mission phases.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: UnableToFlyToHex ' +
'ADistance = ' + IntToStr(ADistance) +
'. AirMaxRange = ' + IntToStr(AirMaxRange));
*)
Result := True;
end
else if not (Game.AirSubPhase in [aspReturnA, aspReturnD]) then
begin
// ****************************************************************************
// Check for permission to fly given bad weather in the destination hex.
// ****************************************************************************
if Map.HexWeather[MULF.Column, MULF.Row] in NoAirFactorsWeather then
begin
CanMoveTo := mvAirWeather;
Result := True;
end
// ****************************************************************************
// Record aircraft that are flying at extended range.
// ****************************************************************************
else if ((Game.Phase <> pPortAttack) or
(not StartedAtSea) or
(ADistance > 1)) and
((ADistance > AirNormalRange) or
HasUnit(UFilterForceExtendedUnit)) then
begin
CanMoveTo := mvDisruptedExt;
end;
end;
end; // End of HasAir.
end; // End of UnableToFlyToHex.
function IsInTargetRange(var TargetRangeUnit: TUnit): Boolean;
begin // TargetRangeUnit is a unit in the moving stack.
Result := UnitInTargetRange(TargetRangeUnit, Game.AirSubPhase, MULF.Hex);
end;
function CantInterceptA: Boolean;
begin
Result := False;
Good := OtherStackFilter(MapHex, UFilterSelectedEnemyAirUnitMissionTime);
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: AspInterceptA ' +
'. Good = ' + BoolToStr(Good));
*)
if Good then
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if IsInTargetRange(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 = nil then
begin
CanMoveTo := mvNoTarget;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: AspInterceptA ' +
'. Failed target in range.');
*)
Exit;
end;
end;
if not Cooperates(MapHex) then // CantInterceptA.
begin
CanMoveTo := mvStackingNoCoop;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: AspInterceptA ' +
'. Failed Cooperates.');
*)
end;
end; // End of CantInterceptA.
function CantInterceptD: Boolean;
begin
Result := False;
Good := OtherStackFilter(MapHex, UFilterSelectedEnemyAirUnitMissionTime);
if Good then
begin
if (Game.Phase = pStrategicBombardment) and
MapHex.UnitsSurpriseHex then // Air interception.
begin
CanMoveTo := mvAirSurprisedHex;
Result := True;
Exit;
end
else if MapHex.SurprisedStack(Self, Game.NonPhasingSide, UFilterFlying,
UFilterAirUnit) or
((Game.Phase in [pPortAttack, pCarpetBombing, pGroundStrike,
pGroundSupport]) and
MapHex.SurprisedStack(MapHex, Game.NonPhasingSide,
UFilterFlying)) then
begin
CanMoveTo := mvAirSurprisedIntercept;
Result := True;
Exit;
end
else
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if IsInTargetRange(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 = nil then
begin
CanMoveTo := mvNoTarget;
Result := True;
Exit;
end;
end;
if not Cooperates(MapHex) then // CantInterceptD.
begin
CanMoveTo := mvStackingNoCoop;
Result := True;
end;
end;
end; // End of CantInterceptD.
function CantReturnAD: Boolean;
var
MovRes: TMoveResult;
begin // Air units only.
Result := False;
// ****************************************************************************
// When OptRules.Internment is active, air units belonging to minor countries
// can fly into neutral minor countries to be interned. This can happen at any
// time that the air unit can fly (e.g., return to base, forced rebase, etc.).
// The following conditionals permit these moves.
// ****************************************************************************
Good := OptRules.Internment and
(FirstMU is TAirUnit) and
(UnitHomeCountryCommonwealth(FirstMU).ClassType = TMinorCountry) and
MULF.OnLand and
(Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.ClassType =
TMinorCountry) and
(not Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.Conquered)
and
Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.Neutral;
if not Good then
begin
Good := FriendlyHex(MULF.Column, MULF.Row);
if not Good then
begin
CanMoveTo := mvNotAFriendlyHex;
Result := True;
end
else
begin
if EnemyStack(MapHex) then
begin
CanMoveTo := mvEnemyUnit;
Result := True;
end
else
begin
MovRes := CanStack(MapHex, False, nil, False, True); // Air units only.
CanMoveTo := MovRes;
Result := MovRes <> mvOK;
end;
end;
end;
end; // End of CantReturnAD.
function InvalidNavalMove: Boolean;
var
MovRes: TMoveResult;
begin
Result := False;
// ****************************************************************************
// Test stacking limits for moving naval units.
// ****************************************************************************
// if Game.DigressionInProgress or
// (Game.Phase in [pReturnToBaseA, pReturnToBaseD]) then
// begin
MovRes := CanStack(MapHex);
if MovRes <> mvOK then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = MovRes.');
*)
if MULF.OnLand and (Map.Port[MULF.Column, MULF.Row] = ptNone) then
begin
CanMoveTo := mvNoPort;
Result := True;
Exit;
end
else
begin
if MULF.OnLand and
(Map.Port[MULF.Column, MULF.Row] <> ptNone) and
(MovRes in [mvStackingLand, mvStackingAir, mvStackingFlyingBoat,
mvStackingNaval]) then
begin // Nothing needs to be done here?
end
else
begin
CanMoveTo := MovRes;
Result := True;
Exit;
end;
end;
end;
// ****************************************************************************
// Test moving naval units during Vichy formation.
// In the Vichy subphases vspMoveFrenchAtSea, vspMoveFrenchNavalAxis, and
// vspMoveFrenchNavalAllied NearHexes stores the legal hex destinations.
// ****************************************************************************
if (Game.Phase = pVichy) and
(Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied]) then
begin
if NearHexes.Empty then
begin
CanMoveTo := mvNoAbortPort;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove NearHexes ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNoAbortPort.');
*)
end
else if not NearHexes.Search(MULF.Column, MULF.Row) then
begin
CanMoveTo := mvNotClosest;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNotClosest.');
*)
end;
end
// ****************************************************************************
// Test whether the hex is a valid destination or waypoint. This sets HR.
// ****************************************************************************
else if not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index, HR) then
begin
if MULF.AtSea and
(CurrScenario <> scGuadalCanal) and
(not SeaAreas[MULF.SeaAreaID].LegalSeaArea) then
begin // Barbarossa and half map scenarios only.
CanMoveTo := mvIllegalSeaArea;
Result := True;
end
else if StartedAtSea and
MULF.AtSea and
((Old.SeaAreaID <> MULF.SeaAreaID) or
// ****************************************************************************
// A stack can't move into the sea area where it started if interception is
// possible. So, if you leave a sea area (S) and then return to S, and enemy
// units can intercept the moving stack in S, then S is not a valid destination.
// ****************************************************************************
(HR.DirectMP > 0)) then
begin
CanMoveTo := mvNoSeaArea;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNoSeaArea.');
*)
end
else if Game.DigressionInProgress and
(Game.CurrentDigression = digOverrun) then
begin
CanMoveTo := mvMoveToPortWithinRange;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvMoveToPortWithinRange.');
*)
end
else if Game.Phase = pNavalMovement then
begin
CanMoveTo := mvMP;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvMP' +
'. NavalHexList count = ' + IntToStr(NavalHexList.Count));
*)
end
else if NavalHexList.Empty then
begin
CanMoveTo := mvNoAbortPort;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove NavalHexList ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNoAbortPort.');
*)
end
else
begin
CanMoveTo := mvMoveToPort;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvMoveToPort.');
*)
end;
end
else
// ****************************************************************************
// Even though the sea area in is NavalHexList, you may only be allowed to move
// there if using control-left-click. It cannot be a final stopping place.
// ****************************************************************************
begin
if MULF.AtSea and
(Shift * [ssCtrl] = []) and // Not Ctrl-Left-Shift.
((Game.DigressionInProgress and
(not HR.EnemyZOC)) or // Must end in a port.
// ****************************************************************************
// A stack can only end its move in the sea area where it started if it hasn't
// moved.
// ****************************************************************************
(StartedAtSea and
((Old.SeaAreaID <> MULF.SeaAreaID) or
((Old.SeaAreaID = MULF.SeaAreaID) and
(MPUsedSoFar > 0))))) then
begin
CanMoveTo := mvPassThruSeaArea;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvPassThruSeaArea.' +
'. Old sea area = ' + SeaAreas[Old.SeaAreaID].Name +
'. New sea area = ' + SeaAreas[MULF.SeaAreaID].Name +
'. DirectMP = ' + IntToStr(HR.DirectMP));
*)
end;
end;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. About to call CheckIfValid.');
*)
// ****************************************************************************
// Not Result means the move is ok to make.
// ****************************************************************************
if (not Result) and
CheckIfValid and
(not HR.ValidMove) and
(not (Game.VichySubPhase in [vspMoveFrenchAtSea, vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied])) then
begin
CanMoveTo := mvMoveThroughEnemySeaArea;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvMoveThroughEnemySeaArea.');
*)
end;
// end;
end; // End of InvalidNavalMove.
function InvalidNavalAirNavalMission: Boolean;
begin
Result := False;
if HasUnit(UFilterNightMission) then
begin
CanMoveTo := mvNightNavalAir; // Naval Air night missions not permitted.
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalAirNavalMission ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNightNavalAir.');
*)
end
else
begin
if (Map.HexWeather[MULF.Column, MULF.Row] in NoAirFactorsWeather) then
begin
CanMoveTo := mvAirWeather; // Flights not permitted into bad weather.
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalAirNavalMission ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvAirWeather.');
*)
end;
end;
end;
function InvalidNavalAirSupport: Boolean;
begin
Result := False;
// ****************************************************************************
// Check for trying to move the stack to a hex not in the sea area where the
// current naval combat is occurring.
// ****************************************************************************
if (not MULF.AtSea) or
(MULF.SeaAreaID <> Game.CurrNavalCombat.NCHSeaArea.ID) then
begin
CanMoveTo := mvCombatSeaArea;
Result := True;
end;
end;
(*
if ((Game.Phase in [pReturnToBaseA, pReturnToBaseD]) or
Game.NavalCombatAbortDigress) or
(P in [pNavalAir, pNavalCombatA, pNavalCombatD]) then
end;
*)
function InvalidDestination(const CanUseDoubleRange: Boolean = True): Boolean;
var // Only air units. Not used for the 8 air missions or air rebases.
MovRes: TMoveResult;
MaximumRange: Integer;
begin
Result := False;
if not (FirstMU is TAirUnit) then Exit;
if MULF.AtSea then
begin // Moving to a sea area.
UFUnit := FirstMU;
if StartedAtSea then
begin
if (Game.Phase in ReturnToBasePhases) or
Game.NavalCombatAbortDigress or
(MULF.SeaAreaID <> PickUpPoint.SeaAreaID) then
begin
CanMoveTo := mvNavalAirOtherSeaArea;
Result := True; // Cannot fly Naval Air into a different sea area.
end
else
begin
// ****************************************************************************
// rsNavalAirSection0 = 'You can only fly to the same sea area if you can ' +
// 'move to a lower section'. This is not possible from section zero.
// ****************************************************************************
if FirstMU.Section = 0 then
begin
CanMoveTo := mvNavalAirSection0;
Result := True;
end;
end;
end;
end
else
begin // Moving to a land hex.
C := TMajorCountry(Countries[FirstMU.ControllingMajorCountry]);
if ((Game.Phase <> pVichy) or
(not (Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied]))) and
((HexC <> nil) and
(HexC.Side = OtherSide(C.Side))) then
begin
CanMoveTo := mvHexControl;
// ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidDestination ');
Result := True;
end
else if EnemyStack(MapHex) then
begin
CanMoveTo := mvEnemyUnit;
Result := True;
end;
end; // End of moving to a land hex.
if not Result then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: Range check.');
// ****************************************************************************
// If the air unit is at sea flying at extended range, then it can use extended
// range to return to base.
// ****************************************************************************
if CanUseDoubleRange or (TAirUnit(FirstMU).FlyingExtended) then
MaximumRange := AirMaxRange * 2
else
MaximumRange := AirMaxRange;
if Map.AirDistance(Game.Phase, FirstMU, PickUpPoint, MULF.Hex,
UnitHomeCountryCommonwealth(FirstMU),
MaximumRange) +
(Ord(Game.SectionRangePhase or
((Game.DigressionInProgress) and
(Game.CurrentDigression = digReturnToBase))) *
SeaAreaRange[FirstMU.Section]) >
MaximumRange then
begin
CanMoveTo := mvOutOfRange; // Not used for the air missions or rebases.
// ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidDestination ' +
// 'MaximumRange = ' + IntToStr(MaximumRange));
Result := True;
end;
end;
if not Result then
begin
MovRes := CanStack(MapHex);
if MovRes <> mvOK then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidDestination ' +
// 'Can not stack in hex.');
CanMoveTo := MovRes;
Result := True;
end;
end;
end;
function NotLoaded(var U: TUnit): Boolean;
var
AU: TAirUnit;
begin
AU := TAirUnit(U);
Result := (U is TAirUnit) and AU.Bomber and (AU.TransportingTotal = 0);
end;
function NotSupplyLoaded(var U: TUnit): Boolean;
var
AU: TAirUnit;
begin
Result := False;
AU := TAirUnit(U);
if (U is TAirUnit) and
AU.Bomber and
(AU.AirTransport = atrNormal) and
(AU.TransportingTotal = 1) and
(AU.TransLink[1].UnitType in LargeAirTransportSet) then
begin // LargeAirTransportSet = [utArmoredParatroop, utSupply].
Cargo := AU.TransLink[1]; // AU's cargo.
// ShowMessageOK('[TMovingStack.CanMoveTo] NotSupplyLoaded with Cargo = ' +
// Cargo.ViewName);
First := Cargo.TransportedByWhom; // First transport carrying Cargo.
Second := Cargo.TransLink[2]; // Cargo stores 2nd transporting unit.
Result := (First = nil) or
(Second = nil) or
(IndexOf(Second) = tbNone) or
(IndexOf(First) = tbNone);
end;
end;
function IsAirLandingUnit(var U: TUnit): Boolean;
begin
Result := (U.UnitType = utAirLanding) and U.AboardTransport;
end;
function IsParatroopUnit(var PU: TUnit): Boolean;
begin
Result := (PU.UnitType = utParatroop)
and PU.AboardTransport and PU.Cooperates(U);
end;
function NoAttack(var U: TUnit): Boolean;
var
CR3: TLandCombatHex;
UIndx: Integer;
UInStack: TUnit;
MPCont: TMajorCountry;
begin
if U.Country = CommunistChina.ID then
begin
if CCLimitsCurr.LandAttacks = aUnlimited then
begin
Result := False; // Unlimited attacks permitted.
Exit;
end;
if LandCombatHexes.Search(MULF.Hex, Index, CR3) then
begin
for UIndx := 0 to CR3.CombatHexUnits.Count - 1 do
begin
UInStack := CR3.CombatHexUnits[UIndx];
if UInStack.Country = CommunistChina.ID then
begin
Result := False; // Another CC unit added to existing attack.
Exit;
end;
end;
Result := CCLimitsCurr.LandAttacks = 0; // New attack needed.
end
else
Result := CCLimitsCurr.LandAttacks = 0; // New attack needed.
end
else
begin // Not a Communist Chinese unit.
MPCont := UnitControllingMajorCountry(U);
if MPCont.CurrLimits.LandAttacks = aUnlimited then
begin
Result := False; // Unlimited attacks permitted.
Exit;
end;
if LandCombatHexes.Search(MULF.Hex, Index, CR3) and
(MPCont.Value in CR3.AttLandMPs) then
Result := False// Another MPCont unit added to existing attack.
else
Result := MPCont.CurrLimits.LandAttacks = 0; // New attack needed.
end;
end;
function NoSB(var U: TNavalUnit): Boolean;
begin // SB = Shore Bombardment.
Result := not U.CanShoreBombardHex(MULF.Column, MULF.Row,
Game.Phase = pShoreBombardmentA);
end;
function NoSBFactors(var U: TNavalUnit): Boolean;
begin // SB = Shore Bombardment.
Result := U.BombardmentHex[MULF.Column, MULF.Row] = 0;
end;
function AnyWillBeDisrupted: Boolean;
var
F1stMovSUIndx: Integer;
F1stMovSUni: TLandUnit;
FoundMovSUni: TLandUnit;
begin
F1stMovSUIndx := 0;
FoundMovSUni := nil;
while F1stMovSUIndx < Count do
begin
F1stMovSUni := TLandUnit(Item[F1stMovSUIndx]);
if F1stMovSUni.TerrainMP[MULF.Column, MULF.Row] >
F1stMovSUni.CurrMove then
begin
FoundMovSUni := F1stMovSUni;
Break;
end;
Inc(F1stMovSUIndx);
end;
Result := FoundMovSUni <> nil;
end;
function DoesntCooperatesWithHex(const SelfU: TUnit): Boolean;
begin
(*
if HexMinC = nil then
MainForm.DebugPanel.Caption :=
'[TMovingStack.CanMoveTo]: DoesntCooperatesWithHex ' +
HexName(MULF.Hex) + '. HexMinC is nil.'
else
MainForm.DebugPanel.Caption :=
'[TMovingStack.CanMoveTo]: DoesntCooperatesWithHex ' +
HexName(MULF.Hex) + '. ' + HexMinC.Name;
*)
Result := (HexMinC <> nil) and (not HexMinC.CooperatesWith(SelfU.Country));
end;
function DoesCooperate(var U: TUnit): Boolean;
var // This routine is only called when flying CAP.
F1stMovSUIndx: Integer;
F1stMovSUni: TUnit;
FoundMovSUni: TUnit;
// ****************************************************************************
// Returns True if U cooperates with any units in Self.
// ****************************************************************************
function CooperatesWithUnit(var SelfU: TUnit): Boolean;
var
MinC2: TMinorCountry;
begin
MinC2 := Countries[SelfU.Country].HomeCountryCommonwealth;
Result := MinC2.CooperatesWith(U.Country);
end;
begin // DoesCooperate.
Result := Game.NonPhasingSide = U.Side; // Only check units on non-phasing side.
if Result then
begin
F1stMovSUIndx := 0;
FoundMovSUni := nil;
while F1stMovSUIndx < Count do
begin
F1stMovSUni := TUnit(Item[F1stMovSUIndx]);
if CooperatesWithUnit(F1stMovSUni) then
begin
FoundMovSUni := F1stMovSUni;
Break;
end;
Inc(F1stMovSUIndx);
end;
Result := FoundMovSUni <> nil;
end;
end;
begin
// ****************************************************************************
// TMovingStack.CanMoveTo.
// ****************************************************************************
Disrupt := False; // Initialize var parameter.
// ****************************************************************************
// No units may enter the Qattara Depression.
// ****************************************************************************
if Map.Terrain[MULF.Column, MULF.Row] = teQattaraDepression then
begin
Result := mvQattara;
Exit;
end;
// ****************************************************************************
// Switch the meaning of Left-Control-Click and Left-Click.
// ****************************************************************************
if CheckShift then
begin
Shift := CurrentShiftState;
if MoveCtrlKey then
begin
if Shift * [ssLeft, ssCtrl] = [ssLeft, ssCtrl] then Exclude(Shift, ssLeft)
else if ssLeft in Shift then Include(Shift, ssCtrl);
end;
end;
// ****************************************************************************
// Perform distance calculations which may be needed later in several places.
// HDistance is the Hex distance from PickUpPoint to MULF.
// ADistance is the Air distance from PickUpPoint to MULF (needs AirMaxRange).
// First check if the PickupPoint is on the map.
// ****************************************************************************
if PickUpPoint.OnMap then
begin
if (Game.Phase = pAirRebase) or
(Game.DigressionInProgress and
(Game.CurrentDigression = digOverrun)) then
AirMaxRange := TAirUnit(FirstMovingUnit).RebaseRange(CheckExtended,
DoubleRange)
else
AirMaxRange := AirRangeOfStack; // Minimum range of all units in stack.
// ShowMessageOK('[TMovingStack.CanMoveTo]: A ' +
// 'AirMaxRange = ' + IntToStr(AirMaxRange));
ADistance := AirDistanceFromTo(PickUpPoint, MULF.Hex, AirMaxRange);
HDistance := TGameMapArea.HexDistance(PickUpPoint, MULF.Hex);
end
else
begin // The next 3 lines of code are for the compiler.
AirMaxRange := 255;
ADistance := 0;
HDistance := 0;
end;
// ****************************************************************************
// Begin validation of move.
// ****************************************************************************
MapHex := MapStacks[MULF.Column, MULF.Row]; // Map stack at destination.
FirstMU := FirstMovingUnit;
if MULF.AtSea then
begin
SAStack := SeaAreas[MULF.SeaAreaID].SeaStack;
if FirstMU.Side = sdAxis then
begin
RefC := SeaAreas[MULF.SeaAreaID].AxisBoxZero.X;
RefR := SeaAreas[MULF.SeaAreaID].AxisBoxZero.Y;
end
else
begin
RefC := SeaAreas[MULF.SeaAreaID].AlliedBoxZero.X;
RefR := SeaAreas[MULF.SeaAreaID].AlliedBoxZero.Y;
end;
end
else
begin
SAStack := nil;
RefC := 0; // Not used.
RefR := 0;
end;
Old := PickUpPoint;
SameHex := ExactSameLocation(PickUpPoint, MULF);
Result := mvOK; // Default is that it's an ok move.
// ****************************************************************************
// Check for trying to move to the starting location for the moving stack, which
// is ok most of the time, but may be illegal in cases listed below.
// ****************************************************************************
MoveToSameHexNotOk := (Game.DigressionInProgress and
// ****************************************************************************
// May not relocate or abort to the same hex.
// ****************************************************************************
(Game.CurrentDigression = digRelocate)) or
Game.NavalCombatAbortDigress or
// ****************************************************************************
// May not fly some air phases/subphases to same hex.
// ****************************************************************************
((Game.Phase in [pPortAttack, pStrategicBombardment,
pCarpetBombing, pGroundStrike,
pParadrop]) and
(Game.AirSubPhase in [AspFlyA, AspInterceptA,
AspAntiAirA, AspReturnA])) or
// ****************************************************************************
// Air units can never fly air missions to all sea hexes.
// ****************************************************************************
((Game.Phase in AirPhases) and
MULF.AtSea);
// ****************************************************************************
// If trying to move to the same hex and that is ok, then we are done.
// ****************************************************************************
if SameHex and (not MoveToSameHexNotOk) then Exit; // Note negative.
// ****************************************************************************
// MC is the governed area that geographically owns the destination hex.
// HexHomeC converts subcountries (e.g., Transylvania) to its parent country.
// HexC is the major power that controls the destination hex.
// We may want to know the minor country that controls the hex (HexMinC).
// ****************************************************************************
MC := Map.HexCountry[MULF.Column, MULF.Row];
HexHomeC := Map.HexHomeCountry[MULF.Column, MULF.Row];
HexC := Map.HexControlMajorCountry[MULF.Column, MULF.Row];
HexMinC := Map.HexControlHex[MULF.Hex];
// ****************************************************************************
// HexMP is the major power that controls the country (governed area) that
// geographically owns the destination hex. HexMP does not necessarily = HexC.
// ****************************************************************************
if MC = nil then HexMP := nil
else HexMP := MC.ControllingMajorPower;
// ****************************************************************************
// Check for legal country (in the game).
// ****************************************************************************
if (MC <> nil) and (not (MC.ID in LegalCountries)) then
begin
Result := mvIllegalCountry;
Exit;
end;
// ****************************************************************************
// Check for neutral country trying to move. If MovingNeutral returns True,
// then the Result = mvNeutralMove.
// ****************************************************************************
if (MC <> nil) and (not Game.InSettingUpPhase) and MovingNeutral then Exit;
// ShowMessageOK('[TMovingStack.CanMoveTo]: mvNeutral check point A.');
// ****************************************************************************
// Check for moving into a neutral country.
// ****************************************************************************
if (MC <> nil) and
(not Game.InSettingUpPhase) and
(not MULF.AtSea) and
NeutralHex(MULF.Hex) and
((Map.HexControlMajorCountry[MULF.Column, MULF.Row] = nil) or
(Map.HexControlMajorCountry[MULF.Column, MULF.Row].ID <>
Self.StackControllingMP(False, True))) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: mvNeutral check point B.');
EastPol := MC.ID = EasternPoland.ID;
BalticStat := ((MC.ID = Estonia.ID) and
(Estonia.NeverInWar)) or
((MC.ID = Latvia.ID) and
(Latvia.NeverInWar)) or
((MC.ID = Lithuania.ID) and
(Lithuania.NeverInWar));
// ****************************************************************************
// Move is ok if the USSR is occupying Eastern Poland or the Baltic States.
// ****************************************************************************
if (not ((FirstMU.Country in [USSR.ID, Siberia.ID]) and
EastPol and
(not EasternPolandOccupied) and
(not Poland.Conquered))) and
(not ((FirstMU.Country in [USSR.ID, Siberia.ID]) and
EasternPolandOccupied and
BalticStat and
(not BalticStatesOccupied))) and
// ****************************************************************************
// When OptRules.Internment is active, air units belonging to minor countries
// can fly into neutral minor countries to be interned. This can happen at any
// time that the air unit can fly (e.g., return to base, forced rebase, etc.).
// The following conditionals permit these moves.
// ****************************************************************************
(not (OptRules.Internment and
(FirstMU is TAirUnit) and
(UnitHomeCountryCommonwealth(FirstMU).ClassType = TMinorCountry) and
(MC.HomeCountry.ClassType = TMinorCountry) and
MULF.OnLand and
(Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.ClassType =
TMinorCountry) and
(not Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.Conquered)
and
Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.Neutral)) and
// ****************************************************************************
// Move is ok to enter a neutral (e.g., Vichy France) when returning French air
// and naval units to base, or rebasing French naval units outside of French
// possessions, or moving French units back into French territories.
// ****************************************************************************
(not ((Game.Phase = pVichy) and
(Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchLandAirAxis,
vspMoveFrenchLandAirAllied,
vspMoveFrenchNavalAllied]))) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: mvNeutral check point C.');
Result := mvNeutral;
Exit;
end;
end; // End of checking for neutral country.
// ****************************************************************************
// RAC 17.4: Vichy units may only enter a hex outside Vichy France is it is
// controlled by an enemy major power.
//
// Check for moving Vichy units into hexes controlled by neither Vichy nor a
// major power at war with Vichy.
// ****************************************************************************
if (MC <> nil) and
(not MULF.AtSea) and
(FirstMU.Country = VichyFrance.ID) and
(HexC <> VichyFrance) and
(HexC <> nil) and
(HexC.Relations[VichyFrance.ID] <> crWar) then
begin
Result := mvVichyOutsideVichy;
Exit;
end;
// ++++++++++++++++++++++++++
// Process digressions first.
// ++++++++++++++++++++++++++
// ****************************************************************************
if Game.DigressionInProgress then
begin
case Game.CurrentDigression of
// ****************************************************************************
// Moving units on the map does not occur.
// ****************************************************************************
digNavalInterception, digFixOverstacking, digCollapseVichy: ;
// ****************************************************************************
// Check for relocating units to the nearest legal hex.
// ****************************************************************************
digRelocate:
begin
if NearHexes.Empty then
Result := mvNoRelocateHex
else if not NearHexes.Search(MULF.Column, MULF.Row) then
Result := mvNotClosest
else if (Game.Phase = pVichy) and
(Game.VichySubPhase in [vspMoveFrenchLandAirAxis,
vspMoveFrenchLandAirAllied]) then
Exit
else if FTCFails or RestrictedReinforcement then
Exit
else if MULF.OnLand and EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl; // digRelocate.
// ShowMessageOK('[TMovingStack.CanMoveTo]: digRelocate.');
end;
end;
// ****************************************************************************
// Check for rebasing units due to overrun or return to base digression.
// ****************************************************************************
digOverrun, digReturnToBase:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: digOverrun, digReturnToBase.');
if FTCFails or RestrictedReinforcement then Exit;
// ****************************************************************************
// Check naval group for illegal combintations.
// ****************************************************************************
if (FirstMU is TNavalUnit) and
(InvalidNavalGroup or
InvalidNavalMove) then
Exit;
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] digOverrun, digReturnToBase ' +
// 'FTC failure.');
if CantTravel then Result := mvMinorLimit // digOverrun, digReturnToBase.
else Result := mvForeignCommitment;
Exit;
end;
if ((Game.Phase <> pVichy) or
(not (Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied]))) and
InvalidDestination(Game.CurrentDigression = digOverrun) then
Exit;
if MULF.AtSea then
begin // Moving to a sea area.
UFUnit := FirstMU;
if FirstMU.UnitType = utCarrierAir then
begin
if not MapHex.HasUnit(UFilterCarrierCanLoadPlane) then
Result := mvNoCarrier; // No carrier available in sea area.
end
else if FirstMU is TAirUnit then
Result := mvLandAirInSeaArea // Only carrier air units RTB at sea.
else if (FirstMU is TNavalUnit) and
(not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index,
HR)) then
begin
if FirstMU.UnitType <> utCarrierAir then
Result := mvLandAirInSeaArea
// Only carrier air units RTB at sea.
else if not MapHex.HasUnit(UFilterCarrierCanLoadPlane) then
Result := mvNoCarrier; // No carrier available in sea area.
end;
end
else
// ****************************************************************************
// One possibility is that naval units have a viable return to base hex but in
// order to reach it, they have to pass through a sea area where they can be
// intercepted by enemy units. In that case, they are allowed to enter the sea
// area.
// ****************************************************************************
begin // Moving to a land hex.
if (Game.Phase = pVichy) and
(Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied]) then
begin // Returning French units to base during Vichy declaration.
if NearHexes.Empty then
begin
if Game.VichySubPhase = vspMoveFrenchAtSea then
Result := mvNoRelocateHex
else
Result := mvNoAbortPort;
end
else if not NearHexes.Search(MULF.Column, MULF.Row) then
Result := mvOutOfRange // French returning from sea.
else
begin
(*
if Game.VichySubPhase = vspMoveFrenchAtSea then
MainForm.DebugPanel.Caption :=
'[TMovingStack.CanMoveTo]: Ok to move to ' +
HexName(MULF.Hex);
*)
end;
end
else if EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl; // digOverrun, digReturnToBase.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: digOverrun, ' +
' digReturnToBase EnemyStackOrHex');
*)
end
else
begin
C := TMajorCountry(Countries[FirstMU.ControllingMajorCountry]);
if (HexC = nil) or (HexC.Side <> C.Side) then
begin
Result := mvHexControl; // digOverrun, digReturnToBase.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: digOverrun, ' +
' digReturnToBase Enemy controlled hex.');
*)
end
else if EnemyStack(MapHex) then
Result := mvEnemyUnit; // An enemy unit is in the hex.
end;
end;
if Result = mvOK then
begin // Check if the destination is in the predetermined list.
if (Game.Phase <> pVichy) or
(not (Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied])) then
begin
if (Game.CurrentDigression = digOverrun) or
(FirstMU is TNavalUnit) then
begin
if (FirstMU is TNavalUnit) and
(not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index,
HR)) then
Result := mvOutOfRange // Overrun naval units.
else if (FirstMU is TAirUnit) and
(not AirHexList.Search(MULF.RefCol, MULF.RefRow, Index,
AHR)) then
begin
if ADistance > AirMaxRange then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: Air unit, ' +
FirstMU.ViewName +
', in digOverrun. ADistance = ' +
IntToStr(ADistance) +
' is greater than AirMaxRange = ' +
IntToStr(AirMaxRange));
*)
Result := mvOutOfRange; // Air unit; digOverrun.
end
else
begin
Good := FriendlyHex(MULF.Column, MULF.Row);
if Good then
begin
if EnemyStack(MapHex) then Result := mvEnemyUnit
else Result := CanStack(MapHex, False, nil, False, True);
end;
end;
end;
end
else
begin // Air unit returning to base due to abort result/choice.
if ADistance > AirMaxRange then
Result := mvOutOfRange // // Air unit; digReturnToBase.
else
begin
Good := FriendlyHex(MULF.Column, MULF.Row);
if Good then
begin
if EnemyStack(MapHex) then Result := mvEnemyUnit
else Result := CanStack(MapHex, False, nil, False, True);
end;
end;
end;
end;
end;
// ****************************************************************************
// Check if move is within legal stacking limits.
// ****************************************************************************
// if Result = mvOK then Result := CanStack(MapHex);
end; // End of digOverrun, digReturnToBase.
digNavalCombatAbort:
begin
if HasAirAndNaval then
Result := mvAbortAirNaval// Cannot move air & naval together.
else if MULF.OnLand and EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl; // digNavalCombatAbort.
// ShowMessageOK('[TMovingStack.CanMoveTo]: digNavalCombatAbort ');
end
else if FTCFails or RestrictedReinforcement then
Exit
// ****************************************************************************
// Check naval group for illegal combintations.
// ****************************************************************************
else if (FirstMU is TNavalUnit) and
(InvalidNavalGroup or
InvalidNavalMove) then
Exit// Trying to move to an all-sea hex.
else if InvalidDestination then Exit;
if MULF.AtSea then
begin // Moving to a sea area.
UFUnit := FirstMU;
// ****************************************************************************
// One possibility is that naval units do have a viable return to base hex but
// in order to reach it, they have to pass through a sea area where they can be
// intercepted by enemy units. In that case, they are allowed to enter the sea
// area.
// ****************************************************************************
if (not (FirstMU is TNavalUnit)) or
(not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index, HR)) then
begin
if FirstMU.UnitType <> utCarrierAir then
Result := mvLandAirInSeaArea
// Only carrier air units RTB at sea.
else if not MapHex.HasUnit(UFilterCarrierCanLoadPlane) then
Result := mvNoCarrier; // No carrier available in sea area.
end;
end
else
begin // Moving to a land hex.
if EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl; // digNavalCombatAbort.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: digNavalCombatAbort ' +
' EnemyStackOrHex');
*)
end
else
begin
C := TMajorCountry(Countries[FirstMU.ControllingMajorCountry]);
if (HexC = nil) or (HexC.Side <> C.Side) then
begin
Result := mvHexControl; // digNavalCombatAbort.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: digNavalCombatAbort ' +
' Enemy controlled hex.');
*)
end
else if EnemyStack(MapHex) then
Result := mvEnemyUnit; // An enemy unit is in the hex.
end;
end;
end; // End of digNavalCombatAbort.
end; // End of case Game.CurrenntDigression.
Exit; // Exit after every digression.
end
else
// ****************************************************************************
// +++++++++++++++++++++++++++++
// Process by phase of the game.
// +++++++++++++++++++++++++++++
// ****************************************************************************
begin
case Game.Phase of
// ****************************************************************************
// In many phases, units can not be 'moved'. Those are listed first since they
// should never be encountered by this routine.
// ****************************************************************************
pLending, pInitiative, pWeather, pChooseAction, pIgnoreNotional,
pEmergencyHQSupply, pHQReorganization, pTRSSupply, pEndOfAction,
pEntry, pUSEntry, pProdPlanningPrelim, pStayAtSeaA, pStayAtSeaD,
pUseOil, pFinalReorganization, pBreakDown, pProdPlanningFinal,
pSearchAndSeizure, pScrapDestroyed, pNavalRepair, pProduction,
pReform, pIntelligence, pUkraine, pConquest, pMutualPeace,
pLiberation, pSurrender, pMinorSupport, pFactoryDestruction,
pVictory, pGameEnd, pQuit, pNone:
Exit;
pSetup:
begin
if RestrictedReinforcement then Exit;
if MULF.OnLand and EnemyStackOrHex(MapHex) and
(not SettingUpPartisans) then
Result := mvHexControl
else
CheckUsingSetupTray;
end;
pReinforcement:
begin
if Game.Phase_Reinforce.CurrentSubPhase[Game.LocalDeciderMP] =
RspPlaceUnits then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pReinforcement ' +
// 'Result at A = ' + IntToStr(Ord(Result)));
if MULF.OnLand and EnemyStackOrHex(MapHex) then
Result := mvHexControl
else if RestrictedReinforcement then Exit;
// ****************************************************************************
// Check for placing reinforcement units on the map from the setup tray.
// ****************************************************************************
MapHex := MapStacks[MULF.Column, MULF.Row]; // Destination MapStack.
Result := CanSetup(MULF.Column, MULF.Row); // CanMoveTo.
(*
if not (Result in [mvOK, mvWarlordHex]) then
ShowMessageOK('[TMovingStack.CanMoveTo]: pReinforcement ' +
'Result at B = ' + IntToStr(Ord(Result)));
*)
if Result = mvOK then
Result := CanStack(MapHex); // MapStack stacking limits for setup phases.
(*
if not (Result in [mvOK, mvWarlordHex]) then
ShowMessageOK('[TMovingStack.CanMoveTo]: pReinforcement ' +
'Result at C = ' + IntToStr(Ord(Result)));
*)
if (Result = mvStackingAir) and
(not MULF.AtSea) and
(UnitCount(UFilterCarrierAirUnit) = 1) then
begin
UFUnit := FindUnit(UFilterCarrierAirUnit);
UFPhase := Game.Phase;
UFSubPhase := aspNone;
if MapHex.HasUnit(UFilterCarrierCanLoadPlane) then
Result := mvOK;
end;
end;
end; // End of pReinforcement.
pDeclareWar:
begin
CheckUsingSetupTray;
end;
pPortAttack:
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if RestrictedReinforcement or UnableToFlyToHex then Exit;
// ****************************************************************************
// Check for attempting to port attack at night - which is forbidden.
// ****************************************************************************
if HasUnit(UFilterNightMission) then
begin
Result := mvNightPortAttack; // No night missions for port attacks.
Exit;
end;
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
AspCAP:
begin
UFSide := Game.PhasingSide;
Good := MapHex.HasUnit(UFilterPortAttackTargetCooperates);
end;
AspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
if Good then Good := MapHex.HasUnit(UFilterPortAttackTargetEnemy);
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end;
if not Good then Result := mvNoTarget;
end; // End of pPortAttack.
pNavalAir: // CanMoveTo.
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
if RestrictedReinforcement or InvalidNavalAirNavalMission then Exit;
if (MULF.OnLand and
PickUpPoint.OnLand) or
(MULF.AtSea and
PickUpPoint.AtSea and
(MULF.SeaAreaID <> PickUpPoint.SeaAreaID)) then
begin
Result := mvNavalAirNotSeaArea;
Exit;
end;
if InvalidDestination(False) then Exit;
// ****************************************************************************
// Check for available air missions for all air units in the moving stack. Even
// fighters count during the Naval Air phase.
// ****************************************************************************
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.AirMissions <> aUnlimited) and
(AirUnits[MPI] > MPow.CurrLimits.AirMissions) then
begin
Result := mvAirMissions;
Break;
end;
end;
end; // End of pNavalAir.
pNavalMovement: // CanMoveTo.
begin
if FTCFails or
InvalidNavalGroup or
RestrictedReinforcement or
InvalidNavalMove then // Trying to move to an all-sea hex.
Exit;
// ShowMessageOK('[TMovingStack.CanMoveTo] pNavalMovement A.');
ComputeUnits; // Count the # of limited activities needed to move stack.
// ShowMessageOK('[TMovingStack.CanMoveTo] pNavalMovement B.');
if MULF.OnLand and EnemyStackOrHex(MapHex) then Result := mvHexControl
// ****************************************************************************
// Moving into a sea area in order to fight through, does not count as a naval
// move.
// ****************************************************************************
else if (not SameHex) and (not Game.NavalInterceptionDigression) then
begin // Check for exceeding naval move limits.
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
MajC := UnitControllingMajorCountry(FirstMU);
if MajC = VichyFrance then MPow := Game.VichyFranceController
else MPow := MajC;
// ****************************************************************************
// This routine is for neutral major powers only.
// ****************************************************************************
if MPow.LegalCountry and
(MPow.CurrLimits.NavalMoves <> aUnlimited) and // CanMoveTo.
MajC.NeutralNavalMoves and // 1 move/unit.
(NavalUnitCounts[MPI] > MPow.CurrLimits.NavalMoves) then
begin
Result := mvNavalMoves; // Insufficient naval moves for neutral.
Break;
end;
end;
end;
// ShowMessageOK('[TMovingStack.CanMoveTo] pNavalMovement C.');
end;
pNavalCombatA, pNavalCombatD: // CanMoveTo.
begin
if Game.NCSubPhase in [NCspNavalAirSupportA, NCspNavalAirSupportD] then
begin
if InvalidNavalAirSupport or InvalidDestination(False) then Exit;
end;
end;
pStrategicBombardment: // CanMoveTo.
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
// ShowMessageOK('[TMovingStack.CanMoveTo]: pStrategicBombardment A.');
if UnableToFlyToHex then Exit;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pStrategicBombardment B.');
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
// ****************************************************************************
// Set UFSide to the enemy side except during a CAP subphase.
// ****************************************************************************
case Game.AirSubPhase of
aspCAP:
begin
UFSide := Game.PhasingSide;
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if DoesntCooperatesWithHex(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if Good then
Good := (FoundMovSUni2 = nil) and
((Map.FactoryList.FactoryTargetCount[MULF.Column,
MULF.Row] > 0) or
(Map.OilTargetCount[MULF.Column, MULF.Row] > 0) or
(Map.SavedBuildTargetCount[MULF.Column, MULF.Row] > 0) or
(Map.SavedOilTargetCount[MULF.Column, MULF.Row] > 0) or
MapHex.HasUnit(UFilterSynthOilUnitNotLost));
end;
aspFlyA:
begin
UFSide := Game.NonPhasingSide;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pStrategicBombardment ' +
// 'AspFlyA C.');
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pStrategicBombardment ' +
// 'AspFlyA D.');
if Good then
Good := EnemyHex(MULF.Column, MULF.Row) and
((Map.FactoryList.FactoryTargetCount[MULF.Column,
MULF.Row] > 0) or
(Map.OilTargetCount[MULF.Column, MULF.Row] > 0) or
(Map.SavedBuildTargetCount[MULF.Column, MULF.Row] > 0) or
(Map.SavedOilTargetCount[MULF.Column, MULF.Row] > 0) or
MapHex.HasUnit(UFilterSynthOilUnitNotLost));
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
aspReturnA, aspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pStrategicBombardment.
pCarpetBombing:
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit;
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
AspCAP:
begin
UFSide := Game.PhasingSide;
Good := MapHex.HasUnit(UFilterCarpetBombingTargetCooperates);
end;
AspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
// ****************************************************************************
// It is possible to carpetbomb a unit on the other side even if you are not at
// war with its controlling major power, if it is in a hex controlled by a
// country with which you are at war.
// ****************************************************************************
UFSide := Game.NonPhasingSide; // Has to be reset.
if Good then
Good := (EnemyHex(MULF.Column, MULF.Row) and
MapHex.HasUnit(UFilterCarpetBombingTargetSide)) or
MapHex.HasUnit(UFilterCarpetBombingTargetEnemy);
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pCarpetBombing.
pGroundStrike:
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit; // Only air units are checked.
// ****************************************************************************
// Set Good which indicates whether the unit can ground strike depending on the
// target units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
aspCAP:
begin
UFSide := Game.PhasingSide;
Good := LandCombatHexes.Search(MULF.Hex, Index, CR) and
FriendlyStack(MapHex) and
MapHex.HasUnit(UFilterGroundStrikeTargetCooperates);
// ****************************************************************************
// Check to see if UTarget is surprised. If it is, it cannot fly CAP.
// ****************************************************************************
// (not UTarget.SurprisedBy(UAttacker)) then
end;
aspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
// ****************************************************************************
// It is possible to ground strike a unit on the other side even if you are not
// at war with its controlling major power, if it is in a hex controlled by a
// country with which you are at war.
// ****************************************************************************
UFSide := Game.NonPhasingSide; // Has to be reset.
if Good then
Good := (EnemyHex(MULF.Column, MULF.Row) and
MapHex.HasUnit(UFilterGroundStrikeTargetSide)) or
MapHex.HasUnit(UFilterGroundStrikeTargetEnemy);
(*
if EnemyHex(MULF.Column, MULF.Row) and
(not MapHex.HasUnit(UFilterGroundStrikeTargetSide)) then
begin
ShowMessageOK('[TMovingStack.CanMoveTo]: pGroundStrike ' +
'AspFlyA UFilterGroundStrikeTargetSide ' +
' failed.');
end
else if not Good then
ShowMessageOK('[TMovingStack.CanMoveTo]: pGroundStrike ' +
'AspFlyA UFilterGroundStrikeTargetEnemy ' +
' failed.');
*)
// ****************************************************************************
// This final check is for artillery units that are bombarding.
// ****************************************************************************
if Good and (not HasAir) then
begin
UFSide := Game.NonPhasingSide; // Has to be reset.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pGroundStrike ' +
'AspFlyA HDistance = ' + IntToStr(HDistance));
*)
if (HDistance > 1) or
(EnemyHex(MULF.Column, MULF.Row) and
(not MapHex.HasUnit(UFilterGroundStrikeTargetSide))) or
((not EnemyHex(MULF.Column, MULF.Row)) and
(not MapHex.HasUnit(UFilterGroundStrikeTargetEnemy))) then
begin
Result := mvBombardAdjacent;
end
else if Map.HexWeather[MULF.Column, MULF.Row] in
NoAirFactorsWeather then
Result := mvBombardWeather
else if Map.HexsideTerrain[PickUpPoint.Column, PickUpPoint.Row,
THexArea.ConnectingHexsideRange(PickUpPoint.Hex,
MULF.Hex), hsAlpine] then
Result := mvBombardAlpine;
end;
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pGroundStrike.
// ****************************************************************************
// Check rail movement.
// ****************************************************************************
pRailMovement: // TMovingStack.CanMoveTo.
begin
if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement then
Exit;
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] pRailMovement ' +
// 'FTC failure.');
if CantTravel then Result := mvMinorLimit // pRailMovement.
else Result := mvForeignCommitment;
Exit;
end;
ComputeUnits; // Count the # of limited activities needed to move stack.
RR := Map.RailHexes.Search(MULF.Column, MULF.Row);
if EnemyStackOrHex(MapHex) then Result := mvHexControl
else if (RR = nil) or (not RR.RInclude) then Result := mvNoRailMove
else
begin
if (FirstMU.UnitType = utFactory) and
((Map.City[MULF.Column, MULF.Row] = cyNone) or
(Map.HexHomeCountryCommonwealth[MULF.Column, MULF.Row] <>
UnitControllingMajorCountry(FirstMU))) then
Result := mvNoHomeCity
// ****************************************************************************
// There is a limit of 2 non-red factories and 3 total factories in a hex.
// ****************************************************************************
else if (FirstMU.UnitType = utFactory) and
Map.NoRoomForFactory(MULF.Column, MULF.Row) then
Result := mvCityCapacityFactory
// ****************************************************************************
// HQ units and Railway guns can move to any rail hex. The hex does not have to
// be a station.
// ****************************************************************************
else if (not (FirstMU.UnitType in HeadquartersSet + [utRailwayGun]))
and
(not Map.IsStationHex(MULF.Column, MULF.Row)) then
Result := mvNoStation
else
begin
if (FirstMU.RailMoveCost(RR.RHexCount) >
UnitControllingMajorCountry(FirstMU).CurrLimits.RailMoves) or
((FirstMU.Country = CommunistChina.ID) and
(FirstMU.RailMoveCost(RR.RHexCount) >
CCLimitsCurr.RailMoves)) then
Result := mvRailMoves
else
Result := CanStack(MapHex); // Rail movement.
end;
end;
end; // End of pRailMovement.
// ****************************************************************************
// Check land movement (including advance after combat).
// ****************************************************************************
pLandMovement:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] pLandMovement A.');
if MULF.AtSea then Result := mvNoLandMove
else if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement then
Exit
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
else if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] pLandMovement ' +
// 'FTC failure after call to CanMoveMinor.');
if CantTravel then Result := mvMinorLimit // pLandMovement.
else Result := mvForeignCommitment;
Exit;
end
else
begin
ComputeUnits; // Count the # of activities needed to move the stack.
if not CanMove(MULF.Column, MULF.Row, Disrupt) then
begin // Move fails. Determine why for error message.
UFSide := Game.PhasingSide;
if EnemyStack(MapHex) or
(EnemyHex(MULF.Column, MULF.Row) and
MapHex.HasUnit(UFilterNotSide)) then
begin
if ((HDistance = 1) and
(FirstMU.HexsideMP[PickUpPoint.Hex, MULF.Hex] <>
ProhibitedTerrain)) then
Result := mvNoOverrun
else
Result := mvOverrunAdjacent;
end
else if InEnemyZOC and (not FirstMove) then
Result := mvEnemyZOC
else if MapHex.HasUnit(UFilterNotSide) and
(not EnemyHex(MULF.Column, MULF.Row)) then
Result := mvNeutralNotAtWar
else if MajPower(FirstMU).HexViolatesPact(MULF.Hex) then
Result := mvPact
else
begin
Result := CanStack(MapHex); // Land movement.
if Result = mvOK then Result := mvMP; // Land movement.
end;
end
else
begin // Legal move, check against action limits.
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves)
and UnitsNotMoved then
begin
Result := mvLandMoves;
Break;
end;
end;
// ****************************************************************************
// Perform another check of all the units in Self to see how many Communist
// Chinese units are in the stack.
// ****************************************************************************
CCCount := 0;
for CCIndex := 0 to Self.Count - 1 do
begin
CCUnit := Self[CCIndex];
if CCUnit.Country = CommunistChina.ID then
Inc(CCCount);
end;
// ****************************************************************************
// If Self has a CC unit, then check available CCLimitsCurr.LandMoves.
// ****************************************************************************
if (CCCount > 0) and (CCLimitsCurr.LandMoves <> aUnlimited) and
(CCCount > CCLimitsCurr.LandMoves) then
Result := mvLandMoves;
end;
if Result = mvOK then
begin // Determine HR. Check for stacking limits.
LandHexList.Search(MULF.RefCol, MULF.RefRow, Index, LHR);
Result := CanStack(MapHex, False, nil, False, False, Side);
if (Result = mvOK) and Disrupt then
Result := mvDisruptedExt;
end;
end;
end; // End of pLandMovement.
pAirTransport:
begin
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement or
UnableToFlyToHex then
Exit;
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] pAirTransport ' +
// 'FTC failure.');
if CantTravel then Result := mvMinorLimit // pAirTransport.
else Result := mvForeignCommitment;
Exit;
end;
ComputeUnits; // Count the # of limited activities needed to move stack.
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
aspCAP:
begin
UFSide := Game.PhasingSide;
Good := EnemyHex(MULF.Column, MULF.Row);
end;
aspFlyA:
begin
// ****************************************************************************
// First check for a transport in the moving stack that is carrying half of a
// unit in the LargeAirTransportSet, while a second transport, which is not in
// the moving stack is carrying the other half. If this condition is detected,
// then automatically add the second transport to the moving stack.
// ****************************************************************************
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
// ****************************************************************************
// Check if Cargo is being carried by another ATR (Second, which is not in the
// moving stack).
// ****************************************************************************
if NotSupplyLoaded(F1stMovSUni2) then
begin
if Second <> nil then
begin
PickingUpMultipleUnits := False;
AddToMovingStack(Second);
end;
if First <> nil then
begin
PickingUpMultipleUnits := False;
AddToMovingStack(First);
end;
end;
Inc(F1stMovSUIndx2);
end; // End of while.
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves) then
begin // Insufficient land moves available.
Result := mvLandMoves;
Break;
end;
end;
if Result = mvOK then
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NotSupplyLoaded(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end; // End of while.
if FoundMovSUni2 <> nil then Result := mvNotSupplyLoaded
else
begin
Res := CanStack(MapHex); // Air transport.
if Res = mvOK then
begin
if Good then Good := FriendlyHex(MULF.Column, MULF.Row);
// ****************************************************************************
// It is a friendly hex. Check if this is an attempt to fly to a hex that has a
// unit which can be loaded onto an air transport. For example, the ATR can not
// fly to an empty hex unless it is already carrying a unit it loaded before it
// took to the air.
// ****************************************************************************
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NotLoaded(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if Good and
(not AirTransportReturn) and
(FoundMovSUni2 <> nil) then
begin
UFPhase := Game.Phase;
UFSubPhase := Game.AirSubPhase;
Good := OtherStackFilter(MapHex, UFilterLoadable);
end;
end // End of Res = mvOK.
else Result := Res;
end; // End of FoundMovSUni2 = nil.
end; // End of Result = mvOK.
end; // End of AspFlyA.
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
// ****************************************************************************
// First check for a transport in the moving stack that is carrying half of a
// unit in the LargeAirTransportSet, while a second transport, which is not in
// the moving stack is carrying the other half. If this condition is detected,
// then automatically add the second transport to the moving stack.
// ****************************************************************************
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
// ****************************************************************************
// Check if Cargo is being carried by another ATR (Second, which is not in the
// moving stack).
// ****************************************************************************
if NotSupplyLoaded(F1stMovSUni2) then
begin
if Second <> nil then
begin
PickingUpMultipleUnits := False;
AddToMovingStack(Second);
end;
if First <> nil then
begin
PickingUpMultipleUnits := False;
AddToMovingStack(First);
end;
end;
Inc(F1stMovSUIndx2);
end; // End of while.
CantReturnAD;
Exit;
end;
end; // End of Case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pAirTransport.
// ****************************************************************************
// Check unloading land units from a sea area into a friendly land hex.
// ****************************************************************************
pUnloadLandUnits:
begin
if FTCFails or RestrictedReinforcement then Exit;
ComputeUnits; // Count the # of limited activities needed to move stack.
if MULF.OnLand and EnemyStackOrHex(MapHex) then Result := mvHexControl
else Result := CanStack(MapHex); // Unloading land units.
if Result = mvOK then
begin
if not FirstMU.CanUnloadIntoHex(MULF.Hex) then Result := mvNoDebark
else
begin
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves) then
begin
Result := mvLandMoves;
Break;
end;
end;
if (Result = mvOK) and AnyWillBeDisrupted then
Result := mvDisruptedExt;
end;
end;
end;
// ****************************************************************************
// Check invasion.
// ****************************************************************************
pInvasion:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion A.');
ComputeUnits; // Count the # of limited activities needed to move stack.
Result := CanStack(MapHex, True); // Invasion.
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion B.');
if Result = mvOK then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion C.');
if not FirstMU.CanInvadeIntoHex(MULF.Hex) then Result := mvNoInvasion
else
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion D.');
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves) then
begin
Result := mvLandMoves;
Break;
end;
end;
end;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion E.');
if Result = mvOK then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion F.');
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NoAttack(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 <> nil then Result := mvLandAttacks;
end;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion G.');
if (Result = mvOK) and AnyWillBeDisrupted then
Result := mvDisruptedExt;
end;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion H.');
end;
pParadrop:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pParadrop A.');
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit;
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pParadrop B.');
case Game.AirSubPhase of
aspCAP:
begin
UFSide := Game.PhasingSide;
LandCombatHexes.Search(MULF.Hex, Index, CR);
Good := ((CR = nil) or
(not CR.CombatHexUnits.SurprisedStack(MapHex,
Game.NonPhasingSide)))
and
FriendlyHex(MULF.Column, MULF.Row);
if Good then
begin
if MapHex.Empty then
begin
// ****************************************************************************
// If the hex is empty, then all units in Self need to cooperate with the
// notional unit (i.e., whoever controls the hex).
// ****************************************************************************
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if DoesntCooperatesWithHex(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
Good := FoundMovSUni2 = nil;
end
else
// ****************************************************************************
// If the hex contains units, Self has to cooperate with all the friendly units
// in the hex.
// ****************************************************************************
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < MapHex.Count do
begin
F1stMovSUni2 := TUnit(MapHex.Item[F1stMovSUIndx2]);
if not DoesCooperate(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
Good := FoundMovSUni2 = nil;
end;
end; // End of Good so far.
end; // End of AspCAP.
aspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
if Map.WeatherTerrain[MULF.Column, MULF.Row] in [teSea,
teLake] then
Result := mvNoTarget
else
Result := CanStack(MapHex, True); // Paradrop.
if Result = mvOK then
begin
CR := LandCombatHexes.FindCombat(MULF.Hex);
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if not MPow.LegalCountry then Continue;
UFCountry := MPow.ID;
if (MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves) then
Result := mvLandMoves
else if (LandUnits[MPI] > 0) and
(MPow.CurrLimits.LandAttacks = 0) and
((CR = nil) or
(not (CR.CombatHexUnits.HasUnit
(UFilterControllingMajorCountry)))) then
Result := mvLandAttacks;
if Result <> mvOK then Break;
end; // End of for each major power.
// ****************************************************************************
// Perform another check of all the units in Self to see if a Communist Chinese
// unit is part of the attack. If so, then see if a Communist Chinese unit is
// already in CR.CombatHexUnits.
// ****************************************************************************
CCCount := 0;
for CCIndex := 0 to Self.Count - 1 do
begin
CCUnit := Self[CCIndex];
// CC unit in Self.
if CCUnit.Country = CommunistChina.ID then Inc(CCCount);
end;
// ****************************************************************************
// If Self has a CC unit, then a check has to be made for both
// CCLimitsCurr.LandMoves and CCLimitsCurr.LandAttacks.
// ****************************************************************************
if CCCount > 0 then
begin
if (CCLimitsCurr.LandMoves <> aUnlimited) and
(CCCount > CCLimitsCurr.LandMoves) then
Result := mvLandMoves
else
begin // Enough land moves available.
if CCLimitsCurr.LandAttacks = 0 then
begin // Check existing attack.
if (CR = nil) then Result := mvLandAttacks
else
begin
CCFound := False;
for CCIndex := 0 to CR.CombatHexUnits.Count - 1 do
begin
CCUnit := CR.CombatHexUnits[CCIndex];
if CCUnit.Country = CommunistChina.ID then
begin
CCFound := True;
Break; // CC unit already in the attack.
end;
end;
if not CCFound then Result := mvLandAttacks;
end;
end; // End of CCLimitsCurr.LandAttacks = 0.
end; // End of enough land moves available.
end; // End of CCCount > 0.
if Result = mvOK then
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NotLoaded(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 <> nil then Result := mvNotLoaded
else
begin
F1stMovSUIndx2 := 0;
U := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if IsAirLandingUnit(F1stMovSUni2) then
begin
U := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
// ****************************************************************************
// If there is no air landing unit in the stack, just check for an enemy hex.
// ****************************************************************************
if U = nil then Good := EnemyHex(MULF.Column, MULF.Row)
else
// ****************************************************************************
// If an air landing unit has been found (U <> nil), then check for an
// accompanying paratroop unit in the moving stack or in the hex.
// ****************************************************************************
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
// ****************************************************************************
// Moving stack check for accompanying paratroop unit.
// ****************************************************************************
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if IsParatroopUnit(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
// ****************************************************************************
// If there is paradrop unit in the stack, check for an enemy hex.
// ****************************************************************************
if FoundMovSUni2 <> nil then
Good := EnemyHex(MULF.Column, MULF.Row)
else
// ****************************************************************************
// Check for an accompanying paratroop unit in the hex.
// ****************************************************************************
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < MapHex.Count do
begin
F1stMovSUni2 := TUnit(MapHex.Item[F1stMovSUIndx2]);
if IsParatroopUnit(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 = nil then
Result := mvNoExistingPara
// ****************************************************************************
// Friendly paratroop unit found in hex.
// ****************************************************************************
else Good := EnemyHex(MULF.Column, MULF.Row);
end;
end;
end;
end;
end;
end; // End AspFlyA.
AspInterceptA:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pParadrop AspInterceptA.');
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of Case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pParadrop.
// ****************************************************************************
// Check land combat declaration.
// ****************************************************************************
pLandCombatDeclaration: // CanMoveTo.
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
if not CanMove(MULF.Column, MULF.Row, Disrupt) then
Result := mvAttack // No.
else
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NoAttack(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 <> nil then
Result := mvLandAttacks // No.
else if LandCombatHexes.Search(MULF.Hex, Index, CR) and
((not CooperatesNotFlying(Self)) or
(not CooperatesNotFlying(CR.CombatHexUnits))) then
Result := mvAttackNoCoop; // No.
end;
end;
// ****************************************************************************
// Check shore bombardment.
// ****************************************************************************
pShoreBombardmentA, pShoreBombardmentD:
begin
F1stMovSUIndx4 := 0;
FoundMovSUni4 := nil;
while F1stMovSUIndx4 < Count do
begin
F1stMovSUni4 := TNavalUnit(Item[F1stMovSUIndx4]);
if NoSB(F1stMovSUni4) then
begin
FoundMovSUni4 := F1stMovSUni4;
Break;
end;
Inc(F1stMovSUIndx4);
end;
if FoundMovSUni4 <> nil then
Result := mvNoShoreBombardment
else
begin
F1stMovSUIndx4 := 0;
FoundMovSUni4 := nil;
while F1stMovSUIndx4 < Count do
begin
F1stMovSUni4 := TNavalUnit(Item[F1stMovSUIndx4]);
if NoSBFactors(F1stMovSUni4) then
begin
FoundMovSUni4 := F1stMovSUni4;
Break;
end;
Inc(F1stMovSUIndx4);
end;
if FoundMovSUni4 <> nil then
Result := mvNoShoreBombardmentFactors;
end;
end;
// ****************************************************************************
// Check attacking HQ support.
// ****************************************************************************
pHQSupportA:
begin
TMapArea.ConnectingHexsideRange(PickUpPoint.Hex, MULF.Hex, HS);
if (not LandCombatHexes.Search(MULF.Hex, Index, CR)) or
(HS <> FirstMU.AttackingHexside) then
Result := mvHQSupportA
else if CR.HQA <> nil then
Result := mvHasHQSupportA;
end;
// ****************************************************************************
// Check defending HQ support.
// ****************************************************************************
pHQSupportD: // CanMoveTo.
begin
HQSameHex := ExactSameLocation(PickUpPoint, MULF);
HQAdjacent := THexArea.AdjacentHexes(PickUpPoint.Column,
PickUpPoint.Row, MULF.Column, MULF.Row);
if (not LandCombatHexes.Search(SmallPoint(MULF.Column, MULF.Row),
Index, CR)) or ((not HQSameHex) and (not HQAdjacent)) or
(LandCombatHexes.Search(SmallPoint(PickUpPoint.Column,
PickUpPoint.Row), Index, CR2) and HQAdjacent) then
Result := mvHQSupportD
else if (not HQSameHex) and
(not FirstMU.CooperatesWithLocation(MULF.Hex)) then
Result := mvHQSupportDNoCoop
else if (CR <> nil) and (CR.HQD <> nil) then
Result := mvHasHQSupportD;
end;
pGroundSupport:
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit;
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
UFUnit := FirstMU;
case Game.AirSubPhase of
aspCAP:
begin
Good := LandCombatHexes.Search(MULF.Hex, Index, CR) and
(not CR.CombatHexUnits.SurprisedStack(MapHex,
Game.NonPhasingSide))
and
FriendlyHex(MULF.Column, MULF.Row);
if Good then
begin
// ****************************************************************************
// If the hex is empty, then all units in Self need to cooperate with the
// notional unit (i.e., whoever controls the hex). Otherwise, Self has to
// cooperate with all the friendly units in the hex.
// ****************************************************************************
if MapHex.Empty then
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if DoesntCooperatesWithHex(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
Good := FoundMovSUni2 = nil;
end
else
// ****************************************************************************
// If the hex contains units, Self has to cooperate with all the friendly units
// in the hex.
// ****************************************************************************
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < MapHex.Count do
begin
F1stMovSUni2 := TUnit(MapHex.Item[F1stMovSUIndx2]);
if not DoesCooperate(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
Good := FoundMovSUni2 = nil;
end;
end;
end; // End of AspCAP.
aspFlyA:
begin
UFSide := Game.NonPhasingSide;
if NoCooperationWithTarget then Exit; // CanMoveTo has been set.
Good := LandCombatHexes.Search(MULF.Hex, Index, CR);
if Good then
begin
if not HasAir then
begin
if HDistance > 1 then Result := mvBombardAdjacent
else if Map.HexWeather[MULF.Column, MULF.Row] in
NoAirFactorsWeather then
Result := mvBombardWeather;
end;
end;
end;
aspFlyD:
begin
UFSide := Game.NonPhasingSide;
if NoCooperationWithTarget then Exit; // CanMoveTo has been set.
Good := LandCombatHexes.Search(MULF.Hex, Index, CR);
if Good then
begin
if CR.CombatHexUnits.SurprisedStack(MapHex,
Game.NonPhasingSide) then
Result := mvAirSurprisedGroundSupport
else if not HasAir then
begin
if HDistance > 1 then Result := mvBombardAdjacent
else if Map.HexWeather[MULF.Column, MULF.Row] in
NoAirFactorsWeather then
Result := mvBombardWeather;
end;
end;
end; // End of AspFlyD.
AspInterceptA:
begin
Good := LandCombatHexes.Search(MULF.Hex, Index, CR);
if Good then
begin
if CantInterceptA then Exit;
end;
end;
AspInterceptD:
begin
Good := LandCombatHexes.Search(MULF.Hex, Index, CR);
if Good then
begin
if CantInterceptD then Exit;
end;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of Case Game.AirSubPhase
if not Good then Result := mvNoTarget;
end; // End of pGroundSupport.
pLandCombatResolution:
begin
case Game.Phase_LandCombatResolution.CurrentSubPhase of
// ****************************************************************************
// The code should never get here for LCRspHexControl since that is processed
// using a digression.
// ****************************************************************************
LCRspLandCombatSelection, LCRspChooseCombatType,
LCRspLandCombatResolution,
LCRspAssignLosses, LCRspHexControl: ;
LCRspRetreats:
begin
if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement then
Exit;
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
if CantTravel then Result := mvMinorLimit // LCRspRetreats.
else Result := mvForeignCommitment;
Exit;
end;
if not RetreatHexes.Search(MULF.Column, MULF.Row) then
Result := mvNoRetreat
else
Result := CanStack(MapHex); // LCRspRetreats.
if (Result = mvOK) and FTCFails then Exit;
end;
LCRspAdvanceAfterCombat:
begin
if MULF.AtSea then Result := mvNoLandMove
else if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement then
Exit
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
else if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
if CantTravel then Result := mvMinorLimit // LCRspAdvanceAfterCombat.
else Result := mvForeignCommitment;
Exit;
end
else
begin
if not CanMove(MULF.Column, MULF.Row, Disrupt) then
begin // Move fails. Determine why for error message.
UFSide := Game.PhasingSide;
if (Game.CombatResult.LRetreat <> lcrBreakthrough) and
(HDistance > 1) then
Result := mvNoBreakthrough
else if EnemyStack(MapHex) or
(EnemyHex(MULF.Column, MULF.Row) and
MapHex.HasUnit(UFilterNotSide)) then
Result := mvNoOverrun
else if MapHex.HasUnit(UFilterNotSide) and
(not EnemyHex(MULF.Column, MULF.Row)) then
Result := mvNeutralNotAtWar
else
begin
Result := CanStack(MapHex); // Advance after combat.
if Result = mvOK then
Result := mvMP; // Advance after combat.
end;
end;
if Result = mvOK then
begin // Determine LHR. Check for stacking limits.
LandHexList.Search(MULF.RefCol, MULF.RefRow, Index, LHR);
Result := CanStack(MapHex, False, nil, False, False, Side);
if (Result = mvOK) and Disrupt then Result := mvDisruptedExt;
end;
end;
end; // End of LCRspAdvanceAfterCombat.
end; // End of case Phase_LandCombatResolution.CurrentSubPhase.
end; // End of pLandCombatResolution.
// ****************************************************************************
// Check air rebase movement.
// ****************************************************************************
pAirRebase:
begin // Both FTCFails and RestrictedReinforcement set CanMoveTo.
if FTCFails or RestrictedReinforcement then Exit;
C := TMajorCountry(Countries[FirstMU.ControllingMajorCountry]);
ComputeUnits; // Count the # of limited activities needed to move stack.
// ****************************************************************************
// First check for rebasing from a naval transport to a land hex.
// ****************************************************************************
if FirstMU.WasAboardTransport and
(not (FirstMU.WasAboardWhom.UnitType in CarrierSet)) then
begin
if not AirHexList.Search(MULF.Column, MULF.Row, Index, AHR) then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
' Destination hex ' + HexName(MULF.Hex) +
' was not in AirHexList, whose count = ' +
IntToStr(AirHexList.Count));
*)
Result := mvOutOfRange; // Air rebase, unload from transport.
end
else
begin
Result := CanStack(MapHex); // Air rebase, unload from transport.
if (Result = mvOK) and
(not FirstMU.CanUnloadIntoHex(MULF.Hex)) then
Result := mvNoUnload;
end;
end
else
// ****************************************************************************
// Rebasing land based air from one land hex to another, or carrier air units.
// ****************************************************************************
begin
if MULF.AtSea then
begin
if FirstMU.UnitType <> utCarrierAir then
Result := mvLandAirInSeaArea
else if not AirHexList.Search(RefC, RefR, Index, AHR) then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
UFUnit.ViewName +
' can''t rebase to ' +
HexName(SmallPoint(RefC, RefR)) +
' because it is not in AirHexList.');
*)
Result := mvNoCarrier;
end
end
// ****************************************************************************
// Check for sufficient air missions or Rebase For Free.
// ****************************************************************************
else if (C.CurrLimits.AirMissions = 0) and
((not TAirUnit(FirstMU).CanAirRebaseForFree) or
// ****************************************************************************
// Rebasing for free requires not changing the unit's hex location.
// ****************************************************************************
(not SameHex)) then
Result := mvOnlyInSameHex
else
begin // The unit is moving to a land hex.
// ****************************************************************************
// RebaseRange sets CheckExtended and DoubleRange. CanMoveTo.
// ****************************************************************************
RebaseRangeMS := TAirUnit(FirstMU).RebaseRange(CheckExtended,
DoubleRange);
(*
if (FirstMU.Country = Finland.ID) and (HexHomeC = Sweden) then
begin
DistToHex := Map.AirDistance(pAirRebase, FirstMU, PickUpPoint,
MULF.Hex,
UnitHomeCountryCommonwealth(FirstMU),
RebaseRangeMS);
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
' DistToHex = ' + IntToStr(DistToHex));
end;
*)
// ****************************************************************************
// MC is the governed area that geographically owns the destination hex, and
// HexC is the major power that controls the destination hex.
// We may want to know the minor country that controls the hex (HexMinC).
// ****************************************************************************
if EnemyStack(MapHex) then Result := mvEnemyUnit
// ****************************************************************************
// When OptRules.Internment is active, air units belonging to minor countries
// can fly into neutral minor countries to be interned. The following
// conditionals permit these moves.
// ****************************************************************************
else if OptRules.Internment and
(UnitHomeCountryCommonwealth(FirstMU).ClassType =
TMinorCountry) and
(HexHomeC.ClassType = TMinorCountry) and
(not HexHomeC.Conquered) and
HexHomeC.Neutral then
begin
if THexArea.HexDistance(PickUpPoint.Hex, MULF.Hex, MapColumns,
True) > RebaseRangeMS then
CanMoveTo := mvOutOfRange // Air rebase for internment.
else
Result := mvOK; // Internment possible.
end
else if not AirHexList.Search(MULF.RefCol, MULF.RefRow, Index,
AHR) then
begin
if not MapHex.Cooperates(Self) then Result := mvStackingNoCoop
else Result := mvOutOfRange; // Air rebase to & from a land hex.
end
else if (HexC = nil) or (HexC.Side <> C.Side) then
Result := mvHexControl;
end; // End of unit rebasing to a land hex.
if Result = mvOK then
begin
if FirstMU.UnitType = utCarrierAir then
begin
UFUnit := FirstMU;
UFPhase := pAirRebase;
UFSubPhase := AspNone;
// ****************************************************************************
// Carrier air units.
// ****************************************************************************
if MULF.AtSea then
begin
if SameHex then
begin
UFSection := MULF.Section;
// ****************************************************************************
// See if there is a carrier that the unit can load onto in the MULF Section -
// other than its current parent carrier.
// ****************************************************************************
if not MapHex.HasUnit(UFilterCarrierCanLoadPlaneInSectionExcept)
then
Result := mvNoCarrier;
end
else
begin // Destination at sea, different hex.
// ****************************************************************************
// RebaseRange sets CheckExtended and DoubleRange. CanMoveTo.
// ****************************************************************************
RebaseRangeMS := TAirUnit(FirstMU).RebaseRange(CheckExtended,
DoubleRange);
UFRange := RebaseRangeMS -
Map.AirDistance(Game.Phase, FirstMU,
PickUpPoint, MULF.Hex,
UnitHomeCountryCommonwealth(FirstMU),
RebaseRangeMS, False, True);
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
UFUnit.ViewName +
' has RebaseRange = ' + IntToStr(RebaseRangeMS) +
', has UFRange = ' + IntToStr(UFRange) +
', and is trying to rebase into ' + HexName(MULF.Hex));
*)
// ****************************************************************************
// See if there is a carrier that the unit can load onto within range - other
// than its current parent carrier.
// ****************************************************************************
if not SAStack.HasUnit(UFilterCarrierCanLoadPlaneInRangeExcept)
// if not MapHex.HasUnit(UFilterCarrierCanLoadPlaneInRangeExcept)
then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
UFUnit.ViewName +
' was unable to rebase into ' + HexName(MULF.Hex));
*)
Result := mvNoCarrier;
end;
end;
end // End of destination being AtSea.
// ****************************************************************************
// Carrier air unit whose destination is not at sea.
// ****************************************************************************
else if not MapHex.HasUnit(UFilterCanLoadOntoExcept) then
Result := CanStack(MapHex); // Carrier air unit.
end
else Result := CanStack(MapHex); // Air rebase.
end;
end;
end; // End of pAirRebase.
pAirReorganization:
begin
ComputeUnits; // Count the # of activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit; // Checks within range.
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
AspCAP:
begin
UFSide := Game.PhasingSide;
Good := MapHex.HasUnit(UFilterEnemyDisruptedNonHQUnit);
end;
AspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
if HasUnit(UFilterFlyingBoat) then
Res := CanStack(MapHex)// Air supply.
else
Res := mvOK;
if (Res = mvOK) and Good then
Good :=
MapHex.HasUnit(UFilterFriendlyCooperatingDisruptedNonHQUnit)
else
Result := Res;
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of Case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pAirReorganization.
pPartisan: CheckUsingSetupTray;
pReturnToBaseA, pReturnToBaseD: // CanMoveTo.
begin
if HasAirAndNaval then
begin
Result := mvAbortAirNaval; // Cannot move air & naval together.
Exit;
end
else if EnemyStackOrHex(MapHex) then Result := mvHexControl
else if FTCFails or
((FirstMU is TNavalUnit) and
(InvalidNavalGroup or
InvalidNavalMove)) or
// Trying to move to an all-sea hex.
RestrictedReinforcement then
Exit
else if InvalidDestination(False) then Exit;
end;
pVichy:
begin
case Game.VichySubPhase of
vspControl: ;
vspMoveNonFrenchLandAir:
begin
if MULF.OnLand and EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl;
// ShowMessageOK('[TMovingStack.CanMoveTo]: vspMoveNonFrenchLandAir ');
end
else if RestrictedReinforcement then Exit;
end;
vspMoveNonFrenchNaval:
begin
if MULF.OnLand and EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl;
// ShowMessageOK('[TMovingStack.CanMoveTo]: vspMoveNonFrenchNaval ');
end
else if RestrictedReinforcement then Exit;
end;
vspMoveFrenchAtSea:
begin
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if (not MULF.OnLand) or
((HexC <> France) and
(HexC <> VichyFrance)) then
Result := mvFranceOnly
// ****************************************************************************
// When returning French units at sea to base during Vichy declaration, only
// hexes in the NearestHexes list are legal. This might not be called.
// ****************************************************************************
else if NearHexes.Empty then
Result := mvNoRelocateHex
else if not NearHexes.Search(MULF.Column, MULF.Row) then
Result := mvOutOfRange; // French returning from sea.
end;
vspMoveFrenchLandAirAxis:
begin
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if MULF.OnLand and (HexC <> France) and (HexC <> VichyFrance) then
Result := mvFranceOnly;
end;
vspMoveFrenchNavalAxis:
begin
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if MULF.OnLand and (HexC <> France) and (HexC <> VichyFrance) then
Result := mvFranceOnly;
end;
vspDestroyFrench: ;
vspMoveFrenchLandAirAllied:
begin
if RestrictedReinforcement then Exit;
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if MULF.OnLand and (HexC <> France) and (HexC <> VichyFrance) then
Result := mvFranceOnly;
end;
vspMoveFrenchNavalAllied:
begin
if RestrictedReinforcement then Exit;
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if MULF.OnLand and (HexC <> France) and (HexC <> VichyFrance) then
Result := mvFranceOnly;
end;
vspProduction: ;
vspSetup: CheckUsingSetupTray;
vspMoveFrenchVichy: ;
vspUnitControl: ;
vspConquerFrance: ;
end; // End of case Game.VichySubPhase.
end;
end; // End of case Game.Phase.
// ShowMessageOK('[TMovingStack.CanMoveTo] Z.');
end;
// ****************************************************************************
// Finally, check if the move violates a Neutrality Pact. This takes
// restriction applies across all unit types and all phases of the game.
// ****************************************************************************
if (Result in [mvOK, mvDisruptedExt]) and HexViolatesPact(MULF.Hex) then
Result := mvPact;
end; // End of CanMoveTo.
[quote]ORIGINAL: Shannon V. OKeets
My 10 years of playing WIF over the board yielded "very little knowledge"????[:D]
[/quote]
And here I thought when we started you were new at WiF.
[quote]ORIGINAL: Centuur
I think that a sane programmer who didn't play WiF over the board wouldn't start coding it... He would read the rulebook and throw the whole thing directly into a wastebucket. Far to difficult... That's why I love the fact that there is a madman here who played the game and is a skilled games programmer too...
[/quote]
Evidently you wouldn't pass the aptitude test for programming (yes there was/is one).
I find it laughable the way you all assume programming is such a hard thing to do.
Here is how it works in the real world.
A Systems analyst is given the project and meets with the user to define the specifications of the project
The Systems analyst then assigns Programmers from their group deadlines to write programs (executables, modules, or etc.) for the project.
The Programmers are to code and desk check their programs as completely as possible before submitting their programs to the test group.
The test group submits reports on errors found in the programs to the Systems analyst.
The Systems analyst returns the error report to the correct programmer for corrections.
The Systems analyst is responsible for seeing that all programmers provide documentation of their work, that the programmers are notified of changes from the user, and for the final presentation of the finished product.
Simple huh.[:D]
(Substitute: Developer for Systems analyst, game for project, rules for user, beta testers for test group and you have an Idea how a system design works.)
[/quote]
You keep assuming you know more about a topic because you've read something about it. People who work in the field have several orders of magnitude more knowledge than someone who has merely read up on a subject.
Here is some "simple programming" for you to critique. This is an excerpt of ~4000 lines of code from a module of 11,000 lines. There are ~250 modules in MWIF, totaling over 400,000 lines of code.
The following code (which references hundreds of code segments in other modules) is to determine if a stack of units that the player has picked up can move to the hex over which the player has moved the cursor. This executes in real time as the player moves the mouse over the map. For each hex the cursor passes over, a message is shown on the Main form telling the player whether the move is legal - and if not, why not. The cursor itself is also updated to either a target icon (ok move) or an X (move illegal).
By the way, the above paragraph IS the program specification for this function. It might also include a reference to the Rules as Written document for determining whether a stack of units can move to a hex. And RAW is perfectly clear in all particulars.[;)]
EDITED: For grammar and to add the last paragraph above.
---
// ****************************************************************************
function TMovingStack.CanMoveTo(const MULF: TUnitLocationFull;
var Disrupt: Boolean;
const CheckIfValid: Boolean = True;
const CheckShift: Boolean = True): TMoveResult;
var
U: TUnit;
FirstMU: TUnit; // First Moving Unit in Self.
C: TMajorCountry;
HexMP: TMajorCountry;
MPow: TMajorCountry;
Index: Longint;
RR: TRailHexData;
Old: TUnitLocationFull;
HS: THexsideRange;
MapHex: TMapStack;
SAStack: TUnitStack;
RefC: Integer; // Reference hex for sea area.
RefR: Integer;
Good: Boolean;
HasAir: Boolean;
Res: TMoveResult;
AHR: TAirHexRecord;
LHR: TLandHexRecord;
HR: TNavalHexRecord;
SectionSet: set of TSectionRange;
HQAdjacent: Boolean;
HQSameHex: Boolean;
MC: TGovernedArea;
HexHomeC: TMinorCountry;
MajC: TMajorCountry;
HexC: TMajorCountry;
HexMinC: TMinorCountry;
AirMaxRange: Integer;
ADistance: Integer;
HDistance: Integer;
CR: TLandCombatHex;
CR2: TLandCombatHex;
SameHex: Boolean;
MoveToSameHexNotOk: Boolean;
NavalUnitCounts: array [TMajorCountries] of Word;
Bombers: array [TMajorCountries] of Word;
LandUnits: array [TMajorCountries] of Word;
AirUnits: array [TMajorCountries] of Word;
MPI: TMajorCountries;
CCCount: Integer;
CCIndex: Integer; // Index for looking for Communist Chinese units.
CCUnit: TUnit;
CCFound: Boolean;
Coop: Boolean;
RebaseRangeMS: Integer;
// DistToHex: Integer;
CheckExtended: Boolean;
DoubleRange: Integer;
Shift: TShiftState;
MaxRange: Integer;
EastPol: Boolean;
BalticStat: Boolean;
F1stMovSUIndx2: Integer;
F1stMovSUni2: TUnit;
FoundMovSUni2: TUnit;
F1stMovSUIndx4: Integer;
F1stMovSUni4: TNavalUnit;
FoundMovSUni4: TNavalUnit;
CantTravel: Boolean;
Cargo: TUnit;
First: TUnit;
Second: TUnit;
procedure CheckUsingSetupTray;
begin
// ****************************************************************************
// Check for placing units on the map from the setup tray.
// ****************************************************************************
MapHex := MapStacks[MULF.Column, MULF.Row]; // Map stack of destination.
Result := CanSetup(MULF.Column, MULF.Row); // CanMoveTo.
if Result = mvOK then Result := CanStack(MapHex); // Setup stacking limits.
if (Result = mvStackingAir) and
(not MULF.AtSea) and
(UnitCount(UFilterCarrierAirUnit) = 1) then
begin
UFUnit := FindUnit(UFilterCarrierAirUnit);
UFPhase := Game.Phase;
UFSubPhase := aspNone;
if MapHex.HasUnit(UFilterCarrierCanLoadPlane) then Result := mvOK;
end;
end; // End of CheckUsingSetupTray.
function MovingNeutral: Boolean;
begin
// ****************************************************************************
// Check for neutral country trying to move.
// ****************************************************************************
Result := False;
F1stMovSUIndx2 := 0;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if F1stMovSUni2.NeutralProhibitedHex(MULF.Column, MULF.Row) then
begin
CanMoveTo := mvNeutralMove;
Result := True;
Exit;
end;
Inc(F1stMovSUIndx2);
end;
end;
function FTCFails: Boolean;
// ****************************************************************************
// Under very special circumstances, check for foreign troop commitment.
// ****************************************************************************
function NotEnoughMPOrRange: Boolean;
var
Index: Integer;
HRMP: TnavalHexRecord;
begin // Test if range and movement points permit a naval move.
Result :=
(not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index, HRMP)) or
(MaxNavalRange - HRMP.AvoidRange <= 0) or
(MaxNavalMovement - HRMP.AvoidMP <= 0);
end;
begin // FTCFails.
Result := False;
if (HexC <> nil) and // Hex must have an owner.
(FirstMU.Side = HexC.Side) and // Moving unit & hex on same side.
(HexMP <> nil) and
(HexMP.ID = HexC.ID) and
((not CheckShift) or
(not (ssCtrl in Shift)) or
(Game.Phase <> pNavalMovement) or
LoadedInPort or
NotEnoughMPOrRange) and // Use NavalHexList to check MPs and range.
(not CanMoveToCountry(Map.HexFTCCountry[MULF.Column, MULF.Row])) then
begin // 'The foreign troop commitment limit for %s will not be satisfied.'
(*
ShowMessageOK('[TMovingStack.CanMoveTo] FTCFails' +
'. HexMP = ' + HexMP.Name +
'. FirstMU''s country = ' +
Countries[FirstMU.Country].Name );
*)
CanMoveTo := mvForeignCommitment;
Result := True;
end;
end; // End of FTCFails.
function InvalidNavalGroup: Boolean;
begin
Result := False;
SectionSet := [];
if HasSurfaceAndSubs then
begin
CanMoveTo := mvSurfaceSubs; // Cannot move surface naval & subs together.
Result := True;
Exit;
end
else
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if (SectionSet <> []) and (not(F1stMovSUni2.Section in SectionSet)) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end
else
Include(SectionSet, F1stMovSUni2.Section);
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 <> nil then
begin // Units can't start in different sections.
CanMoveTo := mvNotAllSameSection;
Result := True;
Exit;
end;
end;
end; // End of InvalidNavalGroup.
function IllegalPartisanMove: Boolean;
begin
Result := False;
F1stMovSUIndx2 := 0;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if (F1stMovSUni2.UnitType = utPartisan) and
(MULF.AtSea or (Map.HexHomeCountry[MULF.Column,
MULF.Row] <> UnitHomeCountry(F1stMovSUni2))) then
begin
CanMoveTo := mvPartisan;
Result := True; // True means the the partisan was moved illegally.
Exit;
end;
Inc(F1stMovSUIndx2);
end;
end; // End of IllegalPartisanMove.
function IllegalWarlordMove: Boolean;
function CheckWarlord(var U: TUnit): Boolean;
var
Hx: TSmallPoint;
begin // True means the the warlord was moved illegally.
Result := (U.UnitType = utWarlord);
if Result then
begin
Hx := SmallPoint(TLandUnit(U).CityColumn, TLandUnit(U).CityRow);
Result := MULF.AtSea or (TGameMapArea.HexDistance(MULF, Hx) > 6);
end;
end;
begin // IllegalWarlordMove.
Result := False;
F1stMovSUIndx2 := 0;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if CheckWarlord(F1stMovSUni2) then
begin
CanMoveTo := mvWarlordRange; // Moving a warlord unit illegally.
Result := True;
Exit;
end;
Inc(F1stMovSUIndx2);
end;
end; // End of IllegalWarlordMove.
procedure ComputeUnits;
var
MStckIndx: Integer;
MStckUni: TUnit;
// ****************************************************************************
// Count the number of limited moves needed to move the stack.
// ****************************************************************************
var
MCCounter: TMajorCountries;
ConvoyPts: array [TMajorCountries] of Word; // Convoys are worth 1/2.
procedure CountUnit(var U: TUnit);
var
MCi: TMajorCountries;
begin
MCi := UnitControllingMajorCountry(U).Value;
if UFilterStackingNonConvoyNavalUnit(U) then
Inc(NavalUnitCounts[MCi])
else if UFilterConvoyUnit(U) then
Inc(ConvoyPts[MCi], TNavalUnit(U).Convoy)
else if UFilterAirUnit(U) then
begin
Inc(AirUnits[MCi]);
if UFilterBomber(U) then
Inc(Bombers[MCi]);
end
else if UFilterLandUnit(U) and (not U.LandMovesForFree) then
Inc(LandUnits[MCi]);
end;
begin // ComputeUnits.
FillChar(NavalUnitCounts, SizeOf(NavalUnitCounts), 0); // Naval unit moves.
FillChar(ConvoyPts, SizeOf(ConvoyPts), 0); // Convoys = 1/2.
FillChar(Bombers, SizeOf(Bombers), 0); // Bombing missions.
FillChar(LandUnits, SizeOf(LandUnits), 0); // Land unit moves.
FillChar(AirUnits, SizeOf(LandUnits), 0); // Air unit moves.
MStckIndx := 0;
while MStckIndx < Count do
begin
MStckUni := TUnit(Item[MStckIndx]);
CountUnit(MStckUni);
Inc(MStckIndx);
end;
// ****************************************************************************
// For every 2 convoys add 1 naval move.
// ****************************************************************************
for MCCounter := Low(TMajorCountries) to High(TMajorCountries) do
Inc(NavalUnitCounts[MCCounter], Succ(ConvoyPts[MCCounter]) div 2);
end; // End of ComputeUnits.
function RestrictedReinforcement: Boolean;
// ****************************************************************************
// Check if the player is trying to move units to reinforce an area in the
// Pacific in violation of the US Entry options/restrictions.
// ****************************************************************************
function CheckReinforcing(var U: TUnit): Boolean;
var
Loc: TSetupLocation;
begin
Result := True; // Default is violation occurs.
// ****************************************************************************
// US Entry option #26, US relocates fleet to Pearl Harbor.
// Only US naval transports and convoys can base at Honolulu or Pago Pago unless
// option #26 has been chosen.
// ****************************************************************************
if (EqualSmallPt(MULF.Hex, Honolulu) or
EqualSmallPt(MULF.Hex, PagoPago)) and
(not USEntry.OptionChosen(useRelocate)) and
UFilterUSNotPearlHarbor(U) then
begin
CanMoveTo := mvUSPearlHarbor; // Assign result for function CanMoveTo.
Exit;
end;
// ****************************************************************************
// Allied land and aircraft units are prohibited from entering certain areas of
// the map until US entry options have been taken, or the Axis has moved there.
// ****************************************************************************
if UFilterAlliedAirNotOnCarrierOrLandUnit(U) then
begin
// ****************************************************************************
// US Entry option #43, CW reinforces the NEI.
// Only NEI land and air units can enter NEI until (1) the Comonwealth and Japan
// are at war, or (2) option #43 has been selected, or (3) an axis land unit has
// entered NEI. All 3 of these are kept track of with CWCanReinforceNEI.
// ****************************************************************************
if (MC = NEI) and // MC is the country of the hex under the cursor.
(U.SourceCountry <> NEI.ID) and (not CWCanReinforceNEI) then
begin
CanMoveTo := mvCWNEI; // Assign result for function CanMoveTo.
Exit;
end;
// ****************************************************************************
// US Entry option #36, CW reinforces the Pacific.
// Allied land and air units can not enter Hong Kong or any Commonwealth
// controlled territory in the Pacific until (1) The Comonwealth and Japan are
// at war, (2) option #36 has been selected, or (3) an Axis land unit has
// entered Hong Kong or a Commonwealth controlled territory in the Pacific. All
// 3 of these are kept track of with CWCanReinforcePacific.
// ****************************************************************************
if ((MC = HongKong) or
((MC.ControllingMajorPower = Commonwealth) and
(MC.ClassType = TGovernedArea) and
MC.Pacific)) and
(not CWCanReinforcePacific) then
begin
CanMoveTo := mvCWPacific; // Assign result for function CanMoveTo.
Exit;
end;
// ****************************************************************************
// US Entry option #40, USA reinforces Guam.
// USA land and air units can not enter Guam until (1) option #40 has been
// selected or (2) an Axis land unit has entered Guam or the Marshalls. Both of
// these are kept track of with USCanReinforceGuam.
// ****************************************************************************
if EqualSmallPt(MULF.Hex, Guam) and (not USCanReinforceGuam) then
begin
CanMoveTo := mvUSGuam; // Assign result for function CanMoveTo.
Exit;
end;
// ****************************************************************************
// US Entry option #41, US reinforces the Philippines.
// USA land and air units can not enter the Philippines until (1) option #40 has
// been selected or (2) an Axis land unit has entered the Philippines. Both of
// these are kept track of with USCanReinforcePhilippines.
// In some scenarios the US starts with units in the Philippines. An exception
// is made to permit those units to relocate within the Philippines.
// Territorial units are not subject to this rule.
// ****************************************************************************
if (MC = Philippines) and
(U.SourceCountry <> Philippines.ID) and
(not USCanReinforcePhilippines) and
((not PickUpPoint.OnMap) or
(Map.HexCountry[PickUpPoint.Column, PickUpPoint.Row] <>
Philippines)) then
begin
// ****************************************************************************
// Unit's SetupGroupIndex - 1 is the index into SULocations.
// ****************************************************************************
if (Game.Phase = pSetup) and EqualSmallPt(MULF.Hex, Manila) then
begin
Loc := SULocations[U.SetupGroupIndex - 1];
if String(Loc.CityName) <> rsManila then
begin
CanMoveTo := mvUSPhilippines; // Result for function CanMoveTo.
Exit;
end;
end
else
begin
CanMoveTo := mvUSPhilippines; // Result for function CanMoveTo.
Exit;
end;
end;
end;
Result := False; // No violation occurred.
end;
begin // RestrictedReinforcement.
Result := False;
// ****************************************************************************
// Restrictions on setting up do not apply to later scenarios.
// ****************************************************************************
if CurrScenario in [scGuadalCanal, scBruteForce, scDarkness,
scDeclineAndFall] then
Exit;
if (MC <> nil) and (not HasAllUnit(UFilterPartisan)) then
begin
F1stMovSUIndx2 := 0;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if CheckReinforcing(F1stMovSUni2) then
begin
Result := True; // Move will violate restricted reinforcement.
Exit;
end;
Inc(F1stMovSUIndx2);
end;
end;
end; // End of RestrictedReinforcement.
function InsufficientAirMissions: Boolean;
var
MPIndex: TMajorCountries;
begin
Result := False;
// ****************************************************************************
// Check for exceeding air mission limits.
// ****************************************************************************
if HasAir then
begin
// ****************************************************************************
// Compare the number of bombers in the moving stack to the air mission limits
// for the owning major power.
// ****************************************************************************
for MPIndex := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPIndex];
if MPow.LegalCountry and
(MPow.CurrLimits.AirMissions <> aUnlimited) and
(Bombers[MPIndex] > MPow.CurrLimits.AirMissions) then
begin
CanMoveTo := mvAirMissions;
Result := True;
Exit;
end;
end;
end;
end; // End of InsufficientAirMissions.
function NoCooperationWithTarget: Boolean;
// ****************************************************************************
// This routine returns True if there is no cooperation or if the target is out
// of range. Otherwise it sets Good depending on the target hex's terrain.
// ****************************************************************************
function CheckForNoBombers: Boolean;
var
NoNeedToCheck: Boolean;
// ****************************************************************************
// This routine returns True if there no bombers in the target hex and a fighter
// is attempting to fly to the hex.
// ****************************************************************************
begin
// ****************************************************************************
// There is no need to check for bombers during:
// AspCAP,
// during any subphase except AspFlyA of non-GroundSupport air missions,
// during any subphase except AspFlyA and AspFlyD of Ground Support missions.
// ****************************************************************************
NoNeedToCheck := (Game.AirSubPhase = AspCAP) or
((Game.Phase <> pGroundSupport) and
(Game.AirSubPhase <> AspFlyA)) or
((Game.Phase = pGroundSupport) and
(not (Game.AirSubPhase in [AspFlyA, AspFlyD])));
if NoNeedToCheck then
begin
Result := False;
Exit;
end;
// ****************************************************************************
// Check for fighter flying day missions.
// ****************************************************************************
Result := HasUnit(UFilterFighterDayMission) and
(not HasUnit(UFilterBomberDayMission)) and // No bomber in stack.
(((Game.AirSubPhase = AspFlyA) and // No bomber in hex.
(not MapHex.HasUnit(UFilterBomberFlyingDayMission))) or
((Game.AirSubPhase = AspFlyD) and
(not OtherStackFilter(MapHex,
UFilterFriendlyBomberFlyingDayMission))
and
((not OtherStackFilter(MapHex,
UFilterEnemyFlyingDayMission)) or
(Self.AirDistanceFromTo(PickUpPoint, MULF.Hex, MaxRange) >
MaxRange))));
if Result then Exit;
// ****************************************************************************
// Check for bombers flying night missions.
// ****************************************************************************
Result := HasUnit(UFilterFighterNightMission) and
(not HasUnit(UFilterBomberNightMission)) and // No bomber in stack.
(((Game.AirSubPhase = AspFlyA) and // No bomber in hex.
(not MapHex.HasUnit(UFilterBomberFlyingNightMission))) or
((Game.AirSubPhase = AspFlyD) and
(not OtherStackFilter(MapHex,
UFilterFriendlyBomberFlyingNightMission))
and
((not OtherStackFilter(MapHex,
UFilterEnemyFlyingNightMission)) or
(Self.AirDistanceFromTo(PickUpPoint, MULF.Hex, MaxRange) >
Values.ARngMax))));
end;
begin
// ****************************************************************************
// NoCooperationWithTarget.
// Set cooperation with target units during ground strikes and ground support.
// Check artillery too if ground strike or ground support phase.
// ****************************************************************************
if HasAir or (Game.Phase in [pGroundSupport, pGroundStrike]) then
begin
if Game.Phase in [pGroundSupport, pGroundStrike] then
begin
if Side = Game.PhasingSide then // Non-phasing side is ok.
begin
LandCombatHexes.Search(MULF.Hex, Index, CR); // Land combat hex.
if CR = nil then Coop := CooperatesFlying(MapHex)
else Coop := CooperatesNotFlying(CR.CombatHexUnits) and
CooperatesFlying(MapHex);
end // Non-phasing side in ground support and ground strike.
else Coop := Cooperates(MapHex);
end
else Coop := CooperatesFlying(MapHex);
end
else Coop := True;
// ****************************************************************************
// Set MaxRange for ground support to ???
// ****************************************************************************
if Game.Phase = pGroundSupport then MaxRange := (Values.ARngMin + 1) div 2
else MaxRange := 0;
// ****************************************************************************
// Check that bombers (and artillery) correctly cooperate and are within range.
// ****************************************************************************
Result := False;
if (not Coop) or (not Self.Cooperates(Self)) then
begin
CanMoveTo := mvStackingNoCoop; // NoCooperationWithTarget.
Result := True;
end
else if CheckForNoBombers then
begin // No bombers when one must be present.
CanMoveTo := mvAirNoBombers;
Result := True;
end
else
begin
// ****************************************************************************
// Determine terrain conditions in target hex.
// ****************************************************************************
case Map.WeatherTerrain[MULF.Column, MULF.Row] of
teSea: Good := False;
teLake: Good := HasAir;
else Good := True;
end;
end; // End of non-cooperation.
end; // End of NoCooperationWithTarget.
function UnableToFlyToHex: Boolean;
begin // Called for each of the 8 air mission phases.
Result := False;
if HasAir then
begin
if (ADistance > AirMaxRange) and
// ****************************************************************************
// More severe restrictions than simply being within range apply during port
// attacks by air units that started at sea. They must be in a sea area that is
// adjacent to the port.
// ****************************************************************************
((Game.Phase <> pPortAttack) or
(not StartedAtSea) or
(ADistance > 1) or
(Map.Port[MULF.Column, MULF.Row] = ptNone)) then
begin
CanMoveTo := mvOutOfRange; // 8 air mission phases.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: UnableToFlyToHex ' +
'ADistance = ' + IntToStr(ADistance) +
'. AirMaxRange = ' + IntToStr(AirMaxRange));
*)
Result := True;
end
else if not (Game.AirSubPhase in [aspReturnA, aspReturnD]) then
begin
// ****************************************************************************
// Check for permission to fly given bad weather in the destination hex.
// ****************************************************************************
if Map.HexWeather[MULF.Column, MULF.Row] in NoAirFactorsWeather then
begin
CanMoveTo := mvAirWeather;
Result := True;
end
// ****************************************************************************
// Record aircraft that are flying at extended range.
// ****************************************************************************
else if ((Game.Phase <> pPortAttack) or
(not StartedAtSea) or
(ADistance > 1)) and
((ADistance > AirNormalRange) or
HasUnit(UFilterForceExtendedUnit)) then
begin
CanMoveTo := mvDisruptedExt;
end;
end;
end; // End of HasAir.
end; // End of UnableToFlyToHex.
function IsInTargetRange(var TargetRangeUnit: TUnit): Boolean;
begin // TargetRangeUnit is a unit in the moving stack.
Result := UnitInTargetRange(TargetRangeUnit, Game.AirSubPhase, MULF.Hex);
end;
function CantInterceptA: Boolean;
begin
Result := False;
Good := OtherStackFilter(MapHex, UFilterSelectedEnemyAirUnitMissionTime);
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: AspInterceptA ' +
'. Good = ' + BoolToStr(Good));
*)
if Good then
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if IsInTargetRange(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 = nil then
begin
CanMoveTo := mvNoTarget;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: AspInterceptA ' +
'. Failed target in range.');
*)
Exit;
end;
end;
if not Cooperates(MapHex) then // CantInterceptA.
begin
CanMoveTo := mvStackingNoCoop;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: AspInterceptA ' +
'. Failed Cooperates.');
*)
end;
end; // End of CantInterceptA.
function CantInterceptD: Boolean;
begin
Result := False;
Good := OtherStackFilter(MapHex, UFilterSelectedEnemyAirUnitMissionTime);
if Good then
begin
if (Game.Phase = pStrategicBombardment) and
MapHex.UnitsSurpriseHex then // Air interception.
begin
CanMoveTo := mvAirSurprisedHex;
Result := True;
Exit;
end
else if MapHex.SurprisedStack(Self, Game.NonPhasingSide, UFilterFlying,
UFilterAirUnit) or
((Game.Phase in [pPortAttack, pCarpetBombing, pGroundStrike,
pGroundSupport]) and
MapHex.SurprisedStack(MapHex, Game.NonPhasingSide,
UFilterFlying)) then
begin
CanMoveTo := mvAirSurprisedIntercept;
Result := True;
Exit;
end
else
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if IsInTargetRange(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 = nil then
begin
CanMoveTo := mvNoTarget;
Result := True;
Exit;
end;
end;
if not Cooperates(MapHex) then // CantInterceptD.
begin
CanMoveTo := mvStackingNoCoop;
Result := True;
end;
end;
end; // End of CantInterceptD.
function CantReturnAD: Boolean;
var
MovRes: TMoveResult;
begin // Air units only.
Result := False;
// ****************************************************************************
// When OptRules.Internment is active, air units belonging to minor countries
// can fly into neutral minor countries to be interned. This can happen at any
// time that the air unit can fly (e.g., return to base, forced rebase, etc.).
// The following conditionals permit these moves.
// ****************************************************************************
Good := OptRules.Internment and
(FirstMU is TAirUnit) and
(UnitHomeCountryCommonwealth(FirstMU).ClassType = TMinorCountry) and
MULF.OnLand and
(Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.ClassType =
TMinorCountry) and
(not Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.Conquered)
and
Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.Neutral;
if not Good then
begin
Good := FriendlyHex(MULF.Column, MULF.Row);
if not Good then
begin
CanMoveTo := mvNotAFriendlyHex;
Result := True;
end
else
begin
if EnemyStack(MapHex) then
begin
CanMoveTo := mvEnemyUnit;
Result := True;
end
else
begin
MovRes := CanStack(MapHex, False, nil, False, True); // Air units only.
CanMoveTo := MovRes;
Result := MovRes <> mvOK;
end;
end;
end;
end; // End of CantReturnAD.
function InvalidNavalMove: Boolean;
var
MovRes: TMoveResult;
begin
Result := False;
// ****************************************************************************
// Test stacking limits for moving naval units.
// ****************************************************************************
// if Game.DigressionInProgress or
// (Game.Phase in [pReturnToBaseA, pReturnToBaseD]) then
// begin
MovRes := CanStack(MapHex);
if MovRes <> mvOK then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = MovRes.');
*)
if MULF.OnLand and (Map.Port[MULF.Column, MULF.Row] = ptNone) then
begin
CanMoveTo := mvNoPort;
Result := True;
Exit;
end
else
begin
if MULF.OnLand and
(Map.Port[MULF.Column, MULF.Row] <> ptNone) and
(MovRes in [mvStackingLand, mvStackingAir, mvStackingFlyingBoat,
mvStackingNaval]) then
begin // Nothing needs to be done here?
end
else
begin
CanMoveTo := MovRes;
Result := True;
Exit;
end;
end;
end;
// ****************************************************************************
// Test moving naval units during Vichy formation.
// In the Vichy subphases vspMoveFrenchAtSea, vspMoveFrenchNavalAxis, and
// vspMoveFrenchNavalAllied NearHexes stores the legal hex destinations.
// ****************************************************************************
if (Game.Phase = pVichy) and
(Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied]) then
begin
if NearHexes.Empty then
begin
CanMoveTo := mvNoAbortPort;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove NearHexes ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNoAbortPort.');
*)
end
else if not NearHexes.Search(MULF.Column, MULF.Row) then
begin
CanMoveTo := mvNotClosest;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNotClosest.');
*)
end;
end
// ****************************************************************************
// Test whether the hex is a valid destination or waypoint. This sets HR.
// ****************************************************************************
else if not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index, HR) then
begin
if MULF.AtSea and
(CurrScenario <> scGuadalCanal) and
(not SeaAreas[MULF.SeaAreaID].LegalSeaArea) then
begin // Barbarossa and half map scenarios only.
CanMoveTo := mvIllegalSeaArea;
Result := True;
end
else if StartedAtSea and
MULF.AtSea and
((Old.SeaAreaID <> MULF.SeaAreaID) or
// ****************************************************************************
// A stack can't move into the sea area where it started if interception is
// possible. So, if you leave a sea area (S) and then return to S, and enemy
// units can intercept the moving stack in S, then S is not a valid destination.
// ****************************************************************************
(HR.DirectMP > 0)) then
begin
CanMoveTo := mvNoSeaArea;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNoSeaArea.');
*)
end
else if Game.DigressionInProgress and
(Game.CurrentDigression = digOverrun) then
begin
CanMoveTo := mvMoveToPortWithinRange;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvMoveToPortWithinRange.');
*)
end
else if Game.Phase = pNavalMovement then
begin
CanMoveTo := mvMP;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvMP' +
'. NavalHexList count = ' + IntToStr(NavalHexList.Count));
*)
end
else if NavalHexList.Empty then
begin
CanMoveTo := mvNoAbortPort;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove NavalHexList ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNoAbortPort.');
*)
end
else
begin
CanMoveTo := mvMoveToPort;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvMoveToPort.');
*)
end;
end
else
// ****************************************************************************
// Even though the sea area in is NavalHexList, you may only be allowed to move
// there if using control-left-click. It cannot be a final stopping place.
// ****************************************************************************
begin
if MULF.AtSea and
(Shift * [ssCtrl] = []) and // Not Ctrl-Left-Shift.
((Game.DigressionInProgress and
(not HR.EnemyZOC)) or // Must end in a port.
// ****************************************************************************
// A stack can only end its move in the sea area where it started if it hasn't
// moved.
// ****************************************************************************
(StartedAtSea and
((Old.SeaAreaID <> MULF.SeaAreaID) or
((Old.SeaAreaID = MULF.SeaAreaID) and
(MPUsedSoFar > 0))))) then
begin
CanMoveTo := mvPassThruSeaArea;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvPassThruSeaArea.' +
'. Old sea area = ' + SeaAreas[Old.SeaAreaID].Name +
'. New sea area = ' + SeaAreas[MULF.SeaAreaID].Name +
'. DirectMP = ' + IntToStr(HR.DirectMP));
*)
end;
end;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. About to call CheckIfValid.');
*)
// ****************************************************************************
// Not Result means the move is ok to make.
// ****************************************************************************
if (not Result) and
CheckIfValid and
(not HR.ValidMove) and
(not (Game.VichySubPhase in [vspMoveFrenchAtSea, vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied])) then
begin
CanMoveTo := mvMoveThroughEnemySeaArea;
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalMove ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvMoveThroughEnemySeaArea.');
*)
end;
// end;
end; // End of InvalidNavalMove.
function InvalidNavalAirNavalMission: Boolean;
begin
Result := False;
if HasUnit(UFilterNightMission) then
begin
CanMoveTo := mvNightNavalAir; // Naval Air night missions not permitted.
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalAirNavalMission ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvNightNavalAir.');
*)
end
else
begin
if (Map.HexWeather[MULF.Column, MULF.Row] in NoAirFactorsWeather) then
begin
CanMoveTo := mvAirWeather; // Flights not permitted into bad weather.
Result := True;
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidNavalAirNavalMission ' +
HexName(SmallPoint(MapHex.Column, MapHex.Row)) +
'. CanMoveTo = mvAirWeather.');
*)
end;
end;
end;
function InvalidNavalAirSupport: Boolean;
begin
Result := False;
// ****************************************************************************
// Check for trying to move the stack to a hex not in the sea area where the
// current naval combat is occurring.
// ****************************************************************************
if (not MULF.AtSea) or
(MULF.SeaAreaID <> Game.CurrNavalCombat.NCHSeaArea.ID) then
begin
CanMoveTo := mvCombatSeaArea;
Result := True;
end;
end;
(*
if ((Game.Phase in [pReturnToBaseA, pReturnToBaseD]) or
Game.NavalCombatAbortDigress) or
(P in [pNavalAir, pNavalCombatA, pNavalCombatD]) then
end;
*)
function InvalidDestination(const CanUseDoubleRange: Boolean = True): Boolean;
var // Only air units. Not used for the 8 air missions or air rebases.
MovRes: TMoveResult;
MaximumRange: Integer;
begin
Result := False;
if not (FirstMU is TAirUnit) then Exit;
if MULF.AtSea then
begin // Moving to a sea area.
UFUnit := FirstMU;
if StartedAtSea then
begin
if (Game.Phase in ReturnToBasePhases) or
Game.NavalCombatAbortDigress or
(MULF.SeaAreaID <> PickUpPoint.SeaAreaID) then
begin
CanMoveTo := mvNavalAirOtherSeaArea;
Result := True; // Cannot fly Naval Air into a different sea area.
end
else
begin
// ****************************************************************************
// rsNavalAirSection0 = 'You can only fly to the same sea area if you can ' +
// 'move to a lower section'. This is not possible from section zero.
// ****************************************************************************
if FirstMU.Section = 0 then
begin
CanMoveTo := mvNavalAirSection0;
Result := True;
end;
end;
end;
end
else
begin // Moving to a land hex.
C := TMajorCountry(Countries[FirstMU.ControllingMajorCountry]);
if ((Game.Phase <> pVichy) or
(not (Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied]))) and
((HexC <> nil) and
(HexC.Side = OtherSide(C.Side))) then
begin
CanMoveTo := mvHexControl;
// ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidDestination ');
Result := True;
end
else if EnemyStack(MapHex) then
begin
CanMoveTo := mvEnemyUnit;
Result := True;
end;
end; // End of moving to a land hex.
if not Result then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: Range check.');
// ****************************************************************************
// If the air unit is at sea flying at extended range, then it can use extended
// range to return to base.
// ****************************************************************************
if CanUseDoubleRange or (TAirUnit(FirstMU).FlyingExtended) then
MaximumRange := AirMaxRange * 2
else
MaximumRange := AirMaxRange;
if Map.AirDistance(Game.Phase, FirstMU, PickUpPoint, MULF.Hex,
UnitHomeCountryCommonwealth(FirstMU),
MaximumRange) +
(Ord(Game.SectionRangePhase or
((Game.DigressionInProgress) and
(Game.CurrentDigression = digReturnToBase))) *
SeaAreaRange[FirstMU.Section]) >
MaximumRange then
begin
CanMoveTo := mvOutOfRange; // Not used for the air missions or rebases.
// ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidDestination ' +
// 'MaximumRange = ' + IntToStr(MaximumRange));
Result := True;
end;
end;
if not Result then
begin
MovRes := CanStack(MapHex);
if MovRes <> mvOK then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: InvalidDestination ' +
// 'Can not stack in hex.');
CanMoveTo := MovRes;
Result := True;
end;
end;
end;
function NotLoaded(var U: TUnit): Boolean;
var
AU: TAirUnit;
begin
AU := TAirUnit(U);
Result := (U is TAirUnit) and AU.Bomber and (AU.TransportingTotal = 0);
end;
function NotSupplyLoaded(var U: TUnit): Boolean;
var
AU: TAirUnit;
begin
Result := False;
AU := TAirUnit(U);
if (U is TAirUnit) and
AU.Bomber and
(AU.AirTransport = atrNormal) and
(AU.TransportingTotal = 1) and
(AU.TransLink[1].UnitType in LargeAirTransportSet) then
begin // LargeAirTransportSet = [utArmoredParatroop, utSupply].
Cargo := AU.TransLink[1]; // AU's cargo.
// ShowMessageOK('[TMovingStack.CanMoveTo] NotSupplyLoaded with Cargo = ' +
// Cargo.ViewName);
First := Cargo.TransportedByWhom; // First transport carrying Cargo.
Second := Cargo.TransLink[2]; // Cargo stores 2nd transporting unit.
Result := (First = nil) or
(Second = nil) or
(IndexOf(Second) = tbNone) or
(IndexOf(First) = tbNone);
end;
end;
function IsAirLandingUnit(var U: TUnit): Boolean;
begin
Result := (U.UnitType = utAirLanding) and U.AboardTransport;
end;
function IsParatroopUnit(var PU: TUnit): Boolean;
begin
Result := (PU.UnitType = utParatroop)
and PU.AboardTransport and PU.Cooperates(U);
end;
function NoAttack(var U: TUnit): Boolean;
var
CR3: TLandCombatHex;
UIndx: Integer;
UInStack: TUnit;
MPCont: TMajorCountry;
begin
if U.Country = CommunistChina.ID then
begin
if CCLimitsCurr.LandAttacks = aUnlimited then
begin
Result := False; // Unlimited attacks permitted.
Exit;
end;
if LandCombatHexes.Search(MULF.Hex, Index, CR3) then
begin
for UIndx := 0 to CR3.CombatHexUnits.Count - 1 do
begin
UInStack := CR3.CombatHexUnits[UIndx];
if UInStack.Country = CommunistChina.ID then
begin
Result := False; // Another CC unit added to existing attack.
Exit;
end;
end;
Result := CCLimitsCurr.LandAttacks = 0; // New attack needed.
end
else
Result := CCLimitsCurr.LandAttacks = 0; // New attack needed.
end
else
begin // Not a Communist Chinese unit.
MPCont := UnitControllingMajorCountry(U);
if MPCont.CurrLimits.LandAttacks = aUnlimited then
begin
Result := False; // Unlimited attacks permitted.
Exit;
end;
if LandCombatHexes.Search(MULF.Hex, Index, CR3) and
(MPCont.Value in CR3.AttLandMPs) then
Result := False// Another MPCont unit added to existing attack.
else
Result := MPCont.CurrLimits.LandAttacks = 0; // New attack needed.
end;
end;
function NoSB(var U: TNavalUnit): Boolean;
begin // SB = Shore Bombardment.
Result := not U.CanShoreBombardHex(MULF.Column, MULF.Row,
Game.Phase = pShoreBombardmentA);
end;
function NoSBFactors(var U: TNavalUnit): Boolean;
begin // SB = Shore Bombardment.
Result := U.BombardmentHex[MULF.Column, MULF.Row] = 0;
end;
function AnyWillBeDisrupted: Boolean;
var
F1stMovSUIndx: Integer;
F1stMovSUni: TLandUnit;
FoundMovSUni: TLandUnit;
begin
F1stMovSUIndx := 0;
FoundMovSUni := nil;
while F1stMovSUIndx < Count do
begin
F1stMovSUni := TLandUnit(Item[F1stMovSUIndx]);
if F1stMovSUni.TerrainMP[MULF.Column, MULF.Row] >
F1stMovSUni.CurrMove then
begin
FoundMovSUni := F1stMovSUni;
Break;
end;
Inc(F1stMovSUIndx);
end;
Result := FoundMovSUni <> nil;
end;
function DoesntCooperatesWithHex(const SelfU: TUnit): Boolean;
begin
(*
if HexMinC = nil then
MainForm.DebugPanel.Caption :=
'[TMovingStack.CanMoveTo]: DoesntCooperatesWithHex ' +
HexName(MULF.Hex) + '. HexMinC is nil.'
else
MainForm.DebugPanel.Caption :=
'[TMovingStack.CanMoveTo]: DoesntCooperatesWithHex ' +
HexName(MULF.Hex) + '. ' + HexMinC.Name;
*)
Result := (HexMinC <> nil) and (not HexMinC.CooperatesWith(SelfU.Country));
end;
function DoesCooperate(var U: TUnit): Boolean;
var // This routine is only called when flying CAP.
F1stMovSUIndx: Integer;
F1stMovSUni: TUnit;
FoundMovSUni: TUnit;
// ****************************************************************************
// Returns True if U cooperates with any units in Self.
// ****************************************************************************
function CooperatesWithUnit(var SelfU: TUnit): Boolean;
var
MinC2: TMinorCountry;
begin
MinC2 := Countries[SelfU.Country].HomeCountryCommonwealth;
Result := MinC2.CooperatesWith(U.Country);
end;
begin // DoesCooperate.
Result := Game.NonPhasingSide = U.Side; // Only check units on non-phasing side.
if Result then
begin
F1stMovSUIndx := 0;
FoundMovSUni := nil;
while F1stMovSUIndx < Count do
begin
F1stMovSUni := TUnit(Item[F1stMovSUIndx]);
if CooperatesWithUnit(F1stMovSUni) then
begin
FoundMovSUni := F1stMovSUni;
Break;
end;
Inc(F1stMovSUIndx);
end;
Result := FoundMovSUni <> nil;
end;
end;
begin
// ****************************************************************************
// TMovingStack.CanMoveTo.
// ****************************************************************************
Disrupt := False; // Initialize var parameter.
// ****************************************************************************
// No units may enter the Qattara Depression.
// ****************************************************************************
if Map.Terrain[MULF.Column, MULF.Row] = teQattaraDepression then
begin
Result := mvQattara;
Exit;
end;
// ****************************************************************************
// Switch the meaning of Left-Control-Click and Left-Click.
// ****************************************************************************
if CheckShift then
begin
Shift := CurrentShiftState;
if MoveCtrlKey then
begin
if Shift * [ssLeft, ssCtrl] = [ssLeft, ssCtrl] then Exclude(Shift, ssLeft)
else if ssLeft in Shift then Include(Shift, ssCtrl);
end;
end;
// ****************************************************************************
// Perform distance calculations which may be needed later in several places.
// HDistance is the Hex distance from PickUpPoint to MULF.
// ADistance is the Air distance from PickUpPoint to MULF (needs AirMaxRange).
// First check if the PickupPoint is on the map.
// ****************************************************************************
if PickUpPoint.OnMap then
begin
if (Game.Phase = pAirRebase) or
(Game.DigressionInProgress and
(Game.CurrentDigression = digOverrun)) then
AirMaxRange := TAirUnit(FirstMovingUnit).RebaseRange(CheckExtended,
DoubleRange)
else
AirMaxRange := AirRangeOfStack; // Minimum range of all units in stack.
// ShowMessageOK('[TMovingStack.CanMoveTo]: A ' +
// 'AirMaxRange = ' + IntToStr(AirMaxRange));
ADistance := AirDistanceFromTo(PickUpPoint, MULF.Hex, AirMaxRange);
HDistance := TGameMapArea.HexDistance(PickUpPoint, MULF.Hex);
end
else
begin // The next 3 lines of code are for the compiler.
AirMaxRange := 255;
ADistance := 0;
HDistance := 0;
end;
// ****************************************************************************
// Begin validation of move.
// ****************************************************************************
MapHex := MapStacks[MULF.Column, MULF.Row]; // Map stack at destination.
FirstMU := FirstMovingUnit;
if MULF.AtSea then
begin
SAStack := SeaAreas[MULF.SeaAreaID].SeaStack;
if FirstMU.Side = sdAxis then
begin
RefC := SeaAreas[MULF.SeaAreaID].AxisBoxZero.X;
RefR := SeaAreas[MULF.SeaAreaID].AxisBoxZero.Y;
end
else
begin
RefC := SeaAreas[MULF.SeaAreaID].AlliedBoxZero.X;
RefR := SeaAreas[MULF.SeaAreaID].AlliedBoxZero.Y;
end;
end
else
begin
SAStack := nil;
RefC := 0; // Not used.
RefR := 0;
end;
Old := PickUpPoint;
SameHex := ExactSameLocation(PickUpPoint, MULF);
Result := mvOK; // Default is that it's an ok move.
// ****************************************************************************
// Check for trying to move to the starting location for the moving stack, which
// is ok most of the time, but may be illegal in cases listed below.
// ****************************************************************************
MoveToSameHexNotOk := (Game.DigressionInProgress and
// ****************************************************************************
// May not relocate or abort to the same hex.
// ****************************************************************************
(Game.CurrentDigression = digRelocate)) or
Game.NavalCombatAbortDigress or
// ****************************************************************************
// May not fly some air phases/subphases to same hex.
// ****************************************************************************
((Game.Phase in [pPortAttack, pStrategicBombardment,
pCarpetBombing, pGroundStrike,
pParadrop]) and
(Game.AirSubPhase in [AspFlyA, AspInterceptA,
AspAntiAirA, AspReturnA])) or
// ****************************************************************************
// Air units can never fly air missions to all sea hexes.
// ****************************************************************************
((Game.Phase in AirPhases) and
MULF.AtSea);
// ****************************************************************************
// If trying to move to the same hex and that is ok, then we are done.
// ****************************************************************************
if SameHex and (not MoveToSameHexNotOk) then Exit; // Note negative.
// ****************************************************************************
// MC is the governed area that geographically owns the destination hex.
// HexHomeC converts subcountries (e.g., Transylvania) to its parent country.
// HexC is the major power that controls the destination hex.
// We may want to know the minor country that controls the hex (HexMinC).
// ****************************************************************************
MC := Map.HexCountry[MULF.Column, MULF.Row];
HexHomeC := Map.HexHomeCountry[MULF.Column, MULF.Row];
HexC := Map.HexControlMajorCountry[MULF.Column, MULF.Row];
HexMinC := Map.HexControlHex[MULF.Hex];
// ****************************************************************************
// HexMP is the major power that controls the country (governed area) that
// geographically owns the destination hex. HexMP does not necessarily = HexC.
// ****************************************************************************
if MC = nil then HexMP := nil
else HexMP := MC.ControllingMajorPower;
// ****************************************************************************
// Check for legal country (in the game).
// ****************************************************************************
if (MC <> nil) and (not (MC.ID in LegalCountries)) then
begin
Result := mvIllegalCountry;
Exit;
end;
// ****************************************************************************
// Check for neutral country trying to move. If MovingNeutral returns True,
// then the Result = mvNeutralMove.
// ****************************************************************************
if (MC <> nil) and (not Game.InSettingUpPhase) and MovingNeutral then Exit;
// ShowMessageOK('[TMovingStack.CanMoveTo]: mvNeutral check point A.');
// ****************************************************************************
// Check for moving into a neutral country.
// ****************************************************************************
if (MC <> nil) and
(not Game.InSettingUpPhase) and
(not MULF.AtSea) and
NeutralHex(MULF.Hex) and
((Map.HexControlMajorCountry[MULF.Column, MULF.Row] = nil) or
(Map.HexControlMajorCountry[MULF.Column, MULF.Row].ID <>
Self.StackControllingMP(False, True))) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: mvNeutral check point B.');
EastPol := MC.ID = EasternPoland.ID;
BalticStat := ((MC.ID = Estonia.ID) and
(Estonia.NeverInWar)) or
((MC.ID = Latvia.ID) and
(Latvia.NeverInWar)) or
((MC.ID = Lithuania.ID) and
(Lithuania.NeverInWar));
// ****************************************************************************
// Move is ok if the USSR is occupying Eastern Poland or the Baltic States.
// ****************************************************************************
if (not ((FirstMU.Country in [USSR.ID, Siberia.ID]) and
EastPol and
(not EasternPolandOccupied) and
(not Poland.Conquered))) and
(not ((FirstMU.Country in [USSR.ID, Siberia.ID]) and
EasternPolandOccupied and
BalticStat and
(not BalticStatesOccupied))) and
// ****************************************************************************
// When OptRules.Internment is active, air units belonging to minor countries
// can fly into neutral minor countries to be interned. This can happen at any
// time that the air unit can fly (e.g., return to base, forced rebase, etc.).
// The following conditionals permit these moves.
// ****************************************************************************
(not (OptRules.Internment and
(FirstMU is TAirUnit) and
(UnitHomeCountryCommonwealth(FirstMU).ClassType = TMinorCountry) and
(MC.HomeCountry.ClassType = TMinorCountry) and
MULF.OnLand and
(Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.ClassType =
TMinorCountry) and
(not Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.Conquered)
and
Map.HexHomeCountry[MULF.Column, MULF.Row].HomeCountry.Neutral)) and
// ****************************************************************************
// Move is ok to enter a neutral (e.g., Vichy France) when returning French air
// and naval units to base, or rebasing French naval units outside of French
// possessions, or moving French units back into French territories.
// ****************************************************************************
(not ((Game.Phase = pVichy) and
(Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchLandAirAxis,
vspMoveFrenchLandAirAllied,
vspMoveFrenchNavalAllied]))) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: mvNeutral check point C.');
Result := mvNeutral;
Exit;
end;
end; // End of checking for neutral country.
// ****************************************************************************
// RAC 17.4: Vichy units may only enter a hex outside Vichy France is it is
// controlled by an enemy major power.
//
// Check for moving Vichy units into hexes controlled by neither Vichy nor a
// major power at war with Vichy.
// ****************************************************************************
if (MC <> nil) and
(not MULF.AtSea) and
(FirstMU.Country = VichyFrance.ID) and
(HexC <> VichyFrance) and
(HexC <> nil) and
(HexC.Relations[VichyFrance.ID] <> crWar) then
begin
Result := mvVichyOutsideVichy;
Exit;
end;
// ++++++++++++++++++++++++++
// Process digressions first.
// ++++++++++++++++++++++++++
// ****************************************************************************
if Game.DigressionInProgress then
begin
case Game.CurrentDigression of
// ****************************************************************************
// Moving units on the map does not occur.
// ****************************************************************************
digNavalInterception, digFixOverstacking, digCollapseVichy: ;
// ****************************************************************************
// Check for relocating units to the nearest legal hex.
// ****************************************************************************
digRelocate:
begin
if NearHexes.Empty then
Result := mvNoRelocateHex
else if not NearHexes.Search(MULF.Column, MULF.Row) then
Result := mvNotClosest
else if (Game.Phase = pVichy) and
(Game.VichySubPhase in [vspMoveFrenchLandAirAxis,
vspMoveFrenchLandAirAllied]) then
Exit
else if FTCFails or RestrictedReinforcement then
Exit
else if MULF.OnLand and EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl; // digRelocate.
// ShowMessageOK('[TMovingStack.CanMoveTo]: digRelocate.');
end;
end;
// ****************************************************************************
// Check for rebasing units due to overrun or return to base digression.
// ****************************************************************************
digOverrun, digReturnToBase:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: digOverrun, digReturnToBase.');
if FTCFails or RestrictedReinforcement then Exit;
// ****************************************************************************
// Check naval group for illegal combintations.
// ****************************************************************************
if (FirstMU is TNavalUnit) and
(InvalidNavalGroup or
InvalidNavalMove) then
Exit;
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] digOverrun, digReturnToBase ' +
// 'FTC failure.');
if CantTravel then Result := mvMinorLimit // digOverrun, digReturnToBase.
else Result := mvForeignCommitment;
Exit;
end;
if ((Game.Phase <> pVichy) or
(not (Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied]))) and
InvalidDestination(Game.CurrentDigression = digOverrun) then
Exit;
if MULF.AtSea then
begin // Moving to a sea area.
UFUnit := FirstMU;
if FirstMU.UnitType = utCarrierAir then
begin
if not MapHex.HasUnit(UFilterCarrierCanLoadPlane) then
Result := mvNoCarrier; // No carrier available in sea area.
end
else if FirstMU is TAirUnit then
Result := mvLandAirInSeaArea // Only carrier air units RTB at sea.
else if (FirstMU is TNavalUnit) and
(not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index,
HR)) then
begin
if FirstMU.UnitType <> utCarrierAir then
Result := mvLandAirInSeaArea
// Only carrier air units RTB at sea.
else if not MapHex.HasUnit(UFilterCarrierCanLoadPlane) then
Result := mvNoCarrier; // No carrier available in sea area.
end;
end
else
// ****************************************************************************
// One possibility is that naval units have a viable return to base hex but in
// order to reach it, they have to pass through a sea area where they can be
// intercepted by enemy units. In that case, they are allowed to enter the sea
// area.
// ****************************************************************************
begin // Moving to a land hex.
if (Game.Phase = pVichy) and
(Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied]) then
begin // Returning French units to base during Vichy declaration.
if NearHexes.Empty then
begin
if Game.VichySubPhase = vspMoveFrenchAtSea then
Result := mvNoRelocateHex
else
Result := mvNoAbortPort;
end
else if not NearHexes.Search(MULF.Column, MULF.Row) then
Result := mvOutOfRange // French returning from sea.
else
begin
(*
if Game.VichySubPhase = vspMoveFrenchAtSea then
MainForm.DebugPanel.Caption :=
'[TMovingStack.CanMoveTo]: Ok to move to ' +
HexName(MULF.Hex);
*)
end;
end
else if EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl; // digOverrun, digReturnToBase.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: digOverrun, ' +
' digReturnToBase EnemyStackOrHex');
*)
end
else
begin
C := TMajorCountry(Countries[FirstMU.ControllingMajorCountry]);
if (HexC = nil) or (HexC.Side <> C.Side) then
begin
Result := mvHexControl; // digOverrun, digReturnToBase.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: digOverrun, ' +
' digReturnToBase Enemy controlled hex.');
*)
end
else if EnemyStack(MapHex) then
Result := mvEnemyUnit; // An enemy unit is in the hex.
end;
end;
if Result = mvOK then
begin // Check if the destination is in the predetermined list.
if (Game.Phase <> pVichy) or
(not (Game.VichySubPhase in [vspMoveFrenchAtSea,
vspMoveFrenchNavalAxis,
vspMoveFrenchNavalAllied])) then
begin
if (Game.CurrentDigression = digOverrun) or
(FirstMU is TNavalUnit) then
begin
if (FirstMU is TNavalUnit) and
(not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index,
HR)) then
Result := mvOutOfRange // Overrun naval units.
else if (FirstMU is TAirUnit) and
(not AirHexList.Search(MULF.RefCol, MULF.RefRow, Index,
AHR)) then
begin
if ADistance > AirMaxRange then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: Air unit, ' +
FirstMU.ViewName +
', in digOverrun. ADistance = ' +
IntToStr(ADistance) +
' is greater than AirMaxRange = ' +
IntToStr(AirMaxRange));
*)
Result := mvOutOfRange; // Air unit; digOverrun.
end
else
begin
Good := FriendlyHex(MULF.Column, MULF.Row);
if Good then
begin
if EnemyStack(MapHex) then Result := mvEnemyUnit
else Result := CanStack(MapHex, False, nil, False, True);
end;
end;
end;
end
else
begin // Air unit returning to base due to abort result/choice.
if ADistance > AirMaxRange then
Result := mvOutOfRange // // Air unit; digReturnToBase.
else
begin
Good := FriendlyHex(MULF.Column, MULF.Row);
if Good then
begin
if EnemyStack(MapHex) then Result := mvEnemyUnit
else Result := CanStack(MapHex, False, nil, False, True);
end;
end;
end;
end;
end;
// ****************************************************************************
// Check if move is within legal stacking limits.
// ****************************************************************************
// if Result = mvOK then Result := CanStack(MapHex);
end; // End of digOverrun, digReturnToBase.
digNavalCombatAbort:
begin
if HasAirAndNaval then
Result := mvAbortAirNaval// Cannot move air & naval together.
else if MULF.OnLand and EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl; // digNavalCombatAbort.
// ShowMessageOK('[TMovingStack.CanMoveTo]: digNavalCombatAbort ');
end
else if FTCFails or RestrictedReinforcement then
Exit
// ****************************************************************************
// Check naval group for illegal combintations.
// ****************************************************************************
else if (FirstMU is TNavalUnit) and
(InvalidNavalGroup or
InvalidNavalMove) then
Exit// Trying to move to an all-sea hex.
else if InvalidDestination then Exit;
if MULF.AtSea then
begin // Moving to a sea area.
UFUnit := FirstMU;
// ****************************************************************************
// One possibility is that naval units do have a viable return to base hex but
// in order to reach it, they have to pass through a sea area where they can be
// intercepted by enemy units. In that case, they are allowed to enter the sea
// area.
// ****************************************************************************
if (not (FirstMU is TNavalUnit)) or
(not NavalHexList.Search(MULF.RefCol, MULF.RefRow, Index, HR)) then
begin
if FirstMU.UnitType <> utCarrierAir then
Result := mvLandAirInSeaArea
// Only carrier air units RTB at sea.
else if not MapHex.HasUnit(UFilterCarrierCanLoadPlane) then
Result := mvNoCarrier; // No carrier available in sea area.
end;
end
else
begin // Moving to a land hex.
if EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl; // digNavalCombatAbort.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: digNavalCombatAbort ' +
' EnemyStackOrHex');
*)
end
else
begin
C := TMajorCountry(Countries[FirstMU.ControllingMajorCountry]);
if (HexC = nil) or (HexC.Side <> C.Side) then
begin
Result := mvHexControl; // digNavalCombatAbort.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: digNavalCombatAbort ' +
' Enemy controlled hex.');
*)
end
else if EnemyStack(MapHex) then
Result := mvEnemyUnit; // An enemy unit is in the hex.
end;
end;
end; // End of digNavalCombatAbort.
end; // End of case Game.CurrenntDigression.
Exit; // Exit after every digression.
end
else
// ****************************************************************************
// +++++++++++++++++++++++++++++
// Process by phase of the game.
// +++++++++++++++++++++++++++++
// ****************************************************************************
begin
case Game.Phase of
// ****************************************************************************
// In many phases, units can not be 'moved'. Those are listed first since they
// should never be encountered by this routine.
// ****************************************************************************
pLending, pInitiative, pWeather, pChooseAction, pIgnoreNotional,
pEmergencyHQSupply, pHQReorganization, pTRSSupply, pEndOfAction,
pEntry, pUSEntry, pProdPlanningPrelim, pStayAtSeaA, pStayAtSeaD,
pUseOil, pFinalReorganization, pBreakDown, pProdPlanningFinal,
pSearchAndSeizure, pScrapDestroyed, pNavalRepair, pProduction,
pReform, pIntelligence, pUkraine, pConquest, pMutualPeace,
pLiberation, pSurrender, pMinorSupport, pFactoryDestruction,
pVictory, pGameEnd, pQuit, pNone:
Exit;
pSetup:
begin
if RestrictedReinforcement then Exit;
if MULF.OnLand and EnemyStackOrHex(MapHex) and
(not SettingUpPartisans) then
Result := mvHexControl
else
CheckUsingSetupTray;
end;
pReinforcement:
begin
if Game.Phase_Reinforce.CurrentSubPhase[Game.LocalDeciderMP] =
RspPlaceUnits then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pReinforcement ' +
// 'Result at A = ' + IntToStr(Ord(Result)));
if MULF.OnLand and EnemyStackOrHex(MapHex) then
Result := mvHexControl
else if RestrictedReinforcement then Exit;
// ****************************************************************************
// Check for placing reinforcement units on the map from the setup tray.
// ****************************************************************************
MapHex := MapStacks[MULF.Column, MULF.Row]; // Destination MapStack.
Result := CanSetup(MULF.Column, MULF.Row); // CanMoveTo.
(*
if not (Result in [mvOK, mvWarlordHex]) then
ShowMessageOK('[TMovingStack.CanMoveTo]: pReinforcement ' +
'Result at B = ' + IntToStr(Ord(Result)));
*)
if Result = mvOK then
Result := CanStack(MapHex); // MapStack stacking limits for setup phases.
(*
if not (Result in [mvOK, mvWarlordHex]) then
ShowMessageOK('[TMovingStack.CanMoveTo]: pReinforcement ' +
'Result at C = ' + IntToStr(Ord(Result)));
*)
if (Result = mvStackingAir) and
(not MULF.AtSea) and
(UnitCount(UFilterCarrierAirUnit) = 1) then
begin
UFUnit := FindUnit(UFilterCarrierAirUnit);
UFPhase := Game.Phase;
UFSubPhase := aspNone;
if MapHex.HasUnit(UFilterCarrierCanLoadPlane) then
Result := mvOK;
end;
end;
end; // End of pReinforcement.
pDeclareWar:
begin
CheckUsingSetupTray;
end;
pPortAttack:
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if RestrictedReinforcement or UnableToFlyToHex then Exit;
// ****************************************************************************
// Check for attempting to port attack at night - which is forbidden.
// ****************************************************************************
if HasUnit(UFilterNightMission) then
begin
Result := mvNightPortAttack; // No night missions for port attacks.
Exit;
end;
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
AspCAP:
begin
UFSide := Game.PhasingSide;
Good := MapHex.HasUnit(UFilterPortAttackTargetCooperates);
end;
AspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
if Good then Good := MapHex.HasUnit(UFilterPortAttackTargetEnemy);
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end;
if not Good then Result := mvNoTarget;
end; // End of pPortAttack.
pNavalAir: // CanMoveTo.
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
if RestrictedReinforcement or InvalidNavalAirNavalMission then Exit;
if (MULF.OnLand and
PickUpPoint.OnLand) or
(MULF.AtSea and
PickUpPoint.AtSea and
(MULF.SeaAreaID <> PickUpPoint.SeaAreaID)) then
begin
Result := mvNavalAirNotSeaArea;
Exit;
end;
if InvalidDestination(False) then Exit;
// ****************************************************************************
// Check for available air missions for all air units in the moving stack. Even
// fighters count during the Naval Air phase.
// ****************************************************************************
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.AirMissions <> aUnlimited) and
(AirUnits[MPI] > MPow.CurrLimits.AirMissions) then
begin
Result := mvAirMissions;
Break;
end;
end;
end; // End of pNavalAir.
pNavalMovement: // CanMoveTo.
begin
if FTCFails or
InvalidNavalGroup or
RestrictedReinforcement or
InvalidNavalMove then // Trying to move to an all-sea hex.
Exit;
// ShowMessageOK('[TMovingStack.CanMoveTo] pNavalMovement A.');
ComputeUnits; // Count the # of limited activities needed to move stack.
// ShowMessageOK('[TMovingStack.CanMoveTo] pNavalMovement B.');
if MULF.OnLand and EnemyStackOrHex(MapHex) then Result := mvHexControl
// ****************************************************************************
// Moving into a sea area in order to fight through, does not count as a naval
// move.
// ****************************************************************************
else if (not SameHex) and (not Game.NavalInterceptionDigression) then
begin // Check for exceeding naval move limits.
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
MajC := UnitControllingMajorCountry(FirstMU);
if MajC = VichyFrance then MPow := Game.VichyFranceController
else MPow := MajC;
// ****************************************************************************
// This routine is for neutral major powers only.
// ****************************************************************************
if MPow.LegalCountry and
(MPow.CurrLimits.NavalMoves <> aUnlimited) and // CanMoveTo.
MajC.NeutralNavalMoves and // 1 move/unit.
(NavalUnitCounts[MPI] > MPow.CurrLimits.NavalMoves) then
begin
Result := mvNavalMoves; // Insufficient naval moves for neutral.
Break;
end;
end;
end;
// ShowMessageOK('[TMovingStack.CanMoveTo] pNavalMovement C.');
end;
pNavalCombatA, pNavalCombatD: // CanMoveTo.
begin
if Game.NCSubPhase in [NCspNavalAirSupportA, NCspNavalAirSupportD] then
begin
if InvalidNavalAirSupport or InvalidDestination(False) then Exit;
end;
end;
pStrategicBombardment: // CanMoveTo.
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
// ShowMessageOK('[TMovingStack.CanMoveTo]: pStrategicBombardment A.');
if UnableToFlyToHex then Exit;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pStrategicBombardment B.');
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
// ****************************************************************************
// Set UFSide to the enemy side except during a CAP subphase.
// ****************************************************************************
case Game.AirSubPhase of
aspCAP:
begin
UFSide := Game.PhasingSide;
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if DoesntCooperatesWithHex(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if Good then
Good := (FoundMovSUni2 = nil) and
((Map.FactoryList.FactoryTargetCount[MULF.Column,
MULF.Row] > 0) or
(Map.OilTargetCount[MULF.Column, MULF.Row] > 0) or
(Map.SavedBuildTargetCount[MULF.Column, MULF.Row] > 0) or
(Map.SavedOilTargetCount[MULF.Column, MULF.Row] > 0) or
MapHex.HasUnit(UFilterSynthOilUnitNotLost));
end;
aspFlyA:
begin
UFSide := Game.NonPhasingSide;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pStrategicBombardment ' +
// 'AspFlyA C.');
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pStrategicBombardment ' +
// 'AspFlyA D.');
if Good then
Good := EnemyHex(MULF.Column, MULF.Row) and
((Map.FactoryList.FactoryTargetCount[MULF.Column,
MULF.Row] > 0) or
(Map.OilTargetCount[MULF.Column, MULF.Row] > 0) or
(Map.SavedBuildTargetCount[MULF.Column, MULF.Row] > 0) or
(Map.SavedOilTargetCount[MULF.Column, MULF.Row] > 0) or
MapHex.HasUnit(UFilterSynthOilUnitNotLost));
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
aspReturnA, aspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pStrategicBombardment.
pCarpetBombing:
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit;
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
AspCAP:
begin
UFSide := Game.PhasingSide;
Good := MapHex.HasUnit(UFilterCarpetBombingTargetCooperates);
end;
AspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
// ****************************************************************************
// It is possible to carpetbomb a unit on the other side even if you are not at
// war with its controlling major power, if it is in a hex controlled by a
// country with which you are at war.
// ****************************************************************************
UFSide := Game.NonPhasingSide; // Has to be reset.
if Good then
Good := (EnemyHex(MULF.Column, MULF.Row) and
MapHex.HasUnit(UFilterCarpetBombingTargetSide)) or
MapHex.HasUnit(UFilterCarpetBombingTargetEnemy);
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pCarpetBombing.
pGroundStrike:
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit; // Only air units are checked.
// ****************************************************************************
// Set Good which indicates whether the unit can ground strike depending on the
// target units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
aspCAP:
begin
UFSide := Game.PhasingSide;
Good := LandCombatHexes.Search(MULF.Hex, Index, CR) and
FriendlyStack(MapHex) and
MapHex.HasUnit(UFilterGroundStrikeTargetCooperates);
// ****************************************************************************
// Check to see if UTarget is surprised. If it is, it cannot fly CAP.
// ****************************************************************************
// (not UTarget.SurprisedBy(UAttacker)) then
end;
aspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
// ****************************************************************************
// It is possible to ground strike a unit on the other side even if you are not
// at war with its controlling major power, if it is in a hex controlled by a
// country with which you are at war.
// ****************************************************************************
UFSide := Game.NonPhasingSide; // Has to be reset.
if Good then
Good := (EnemyHex(MULF.Column, MULF.Row) and
MapHex.HasUnit(UFilterGroundStrikeTargetSide)) or
MapHex.HasUnit(UFilterGroundStrikeTargetEnemy);
(*
if EnemyHex(MULF.Column, MULF.Row) and
(not MapHex.HasUnit(UFilterGroundStrikeTargetSide)) then
begin
ShowMessageOK('[TMovingStack.CanMoveTo]: pGroundStrike ' +
'AspFlyA UFilterGroundStrikeTargetSide ' +
' failed.');
end
else if not Good then
ShowMessageOK('[TMovingStack.CanMoveTo]: pGroundStrike ' +
'AspFlyA UFilterGroundStrikeTargetEnemy ' +
' failed.');
*)
// ****************************************************************************
// This final check is for artillery units that are bombarding.
// ****************************************************************************
if Good and (not HasAir) then
begin
UFSide := Game.NonPhasingSide; // Has to be reset.
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pGroundStrike ' +
'AspFlyA HDistance = ' + IntToStr(HDistance));
*)
if (HDistance > 1) or
(EnemyHex(MULF.Column, MULF.Row) and
(not MapHex.HasUnit(UFilterGroundStrikeTargetSide))) or
((not EnemyHex(MULF.Column, MULF.Row)) and
(not MapHex.HasUnit(UFilterGroundStrikeTargetEnemy))) then
begin
Result := mvBombardAdjacent;
end
else if Map.HexWeather[MULF.Column, MULF.Row] in
NoAirFactorsWeather then
Result := mvBombardWeather
else if Map.HexsideTerrain[PickUpPoint.Column, PickUpPoint.Row,
THexArea.ConnectingHexsideRange(PickUpPoint.Hex,
MULF.Hex), hsAlpine] then
Result := mvBombardAlpine;
end;
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pGroundStrike.
// ****************************************************************************
// Check rail movement.
// ****************************************************************************
pRailMovement: // TMovingStack.CanMoveTo.
begin
if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement then
Exit;
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] pRailMovement ' +
// 'FTC failure.');
if CantTravel then Result := mvMinorLimit // pRailMovement.
else Result := mvForeignCommitment;
Exit;
end;
ComputeUnits; // Count the # of limited activities needed to move stack.
RR := Map.RailHexes.Search(MULF.Column, MULF.Row);
if EnemyStackOrHex(MapHex) then Result := mvHexControl
else if (RR = nil) or (not RR.RInclude) then Result := mvNoRailMove
else
begin
if (FirstMU.UnitType = utFactory) and
((Map.City[MULF.Column, MULF.Row] = cyNone) or
(Map.HexHomeCountryCommonwealth[MULF.Column, MULF.Row] <>
UnitControllingMajorCountry(FirstMU))) then
Result := mvNoHomeCity
// ****************************************************************************
// There is a limit of 2 non-red factories and 3 total factories in a hex.
// ****************************************************************************
else if (FirstMU.UnitType = utFactory) and
Map.NoRoomForFactory(MULF.Column, MULF.Row) then
Result := mvCityCapacityFactory
// ****************************************************************************
// HQ units and Railway guns can move to any rail hex. The hex does not have to
// be a station.
// ****************************************************************************
else if (not (FirstMU.UnitType in HeadquartersSet + [utRailwayGun]))
and
(not Map.IsStationHex(MULF.Column, MULF.Row)) then
Result := mvNoStation
else
begin
if (FirstMU.RailMoveCost(RR.RHexCount) >
UnitControllingMajorCountry(FirstMU).CurrLimits.RailMoves) or
((FirstMU.Country = CommunistChina.ID) and
(FirstMU.RailMoveCost(RR.RHexCount) >
CCLimitsCurr.RailMoves)) then
Result := mvRailMoves
else
Result := CanStack(MapHex); // Rail movement.
end;
end;
end; // End of pRailMovement.
// ****************************************************************************
// Check land movement (including advance after combat).
// ****************************************************************************
pLandMovement:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] pLandMovement A.');
if MULF.AtSea then Result := mvNoLandMove
else if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement then
Exit
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
else if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] pLandMovement ' +
// 'FTC failure after call to CanMoveMinor.');
if CantTravel then Result := mvMinorLimit // pLandMovement.
else Result := mvForeignCommitment;
Exit;
end
else
begin
ComputeUnits; // Count the # of activities needed to move the stack.
if not CanMove(MULF.Column, MULF.Row, Disrupt) then
begin // Move fails. Determine why for error message.
UFSide := Game.PhasingSide;
if EnemyStack(MapHex) or
(EnemyHex(MULF.Column, MULF.Row) and
MapHex.HasUnit(UFilterNotSide)) then
begin
if ((HDistance = 1) and
(FirstMU.HexsideMP[PickUpPoint.Hex, MULF.Hex] <>
ProhibitedTerrain)) then
Result := mvNoOverrun
else
Result := mvOverrunAdjacent;
end
else if InEnemyZOC and (not FirstMove) then
Result := mvEnemyZOC
else if MapHex.HasUnit(UFilterNotSide) and
(not EnemyHex(MULF.Column, MULF.Row)) then
Result := mvNeutralNotAtWar
else if MajPower(FirstMU).HexViolatesPact(MULF.Hex) then
Result := mvPact
else
begin
Result := CanStack(MapHex); // Land movement.
if Result = mvOK then Result := mvMP; // Land movement.
end;
end
else
begin // Legal move, check against action limits.
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves)
and UnitsNotMoved then
begin
Result := mvLandMoves;
Break;
end;
end;
// ****************************************************************************
// Perform another check of all the units in Self to see how many Communist
// Chinese units are in the stack.
// ****************************************************************************
CCCount := 0;
for CCIndex := 0 to Self.Count - 1 do
begin
CCUnit := Self[CCIndex];
if CCUnit.Country = CommunistChina.ID then
Inc(CCCount);
end;
// ****************************************************************************
// If Self has a CC unit, then check available CCLimitsCurr.LandMoves.
// ****************************************************************************
if (CCCount > 0) and (CCLimitsCurr.LandMoves <> aUnlimited) and
(CCCount > CCLimitsCurr.LandMoves) then
Result := mvLandMoves;
end;
if Result = mvOK then
begin // Determine HR. Check for stacking limits.
LandHexList.Search(MULF.RefCol, MULF.RefRow, Index, LHR);
Result := CanStack(MapHex, False, nil, False, False, Side);
if (Result = mvOK) and Disrupt then
Result := mvDisruptedExt;
end;
end;
end; // End of pLandMovement.
pAirTransport:
begin
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement or
UnableToFlyToHex then
Exit;
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo] pAirTransport ' +
// 'FTC failure.');
if CantTravel then Result := mvMinorLimit // pAirTransport.
else Result := mvForeignCommitment;
Exit;
end;
ComputeUnits; // Count the # of limited activities needed to move stack.
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
aspCAP:
begin
UFSide := Game.PhasingSide;
Good := EnemyHex(MULF.Column, MULF.Row);
end;
aspFlyA:
begin
// ****************************************************************************
// First check for a transport in the moving stack that is carrying half of a
// unit in the LargeAirTransportSet, while a second transport, which is not in
// the moving stack is carrying the other half. If this condition is detected,
// then automatically add the second transport to the moving stack.
// ****************************************************************************
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
// ****************************************************************************
// Check if Cargo is being carried by another ATR (Second, which is not in the
// moving stack).
// ****************************************************************************
if NotSupplyLoaded(F1stMovSUni2) then
begin
if Second <> nil then
begin
PickingUpMultipleUnits := False;
AddToMovingStack(Second);
end;
if First <> nil then
begin
PickingUpMultipleUnits := False;
AddToMovingStack(First);
end;
end;
Inc(F1stMovSUIndx2);
end; // End of while.
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves) then
begin // Insufficient land moves available.
Result := mvLandMoves;
Break;
end;
end;
if Result = mvOK then
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NotSupplyLoaded(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end; // End of while.
if FoundMovSUni2 <> nil then Result := mvNotSupplyLoaded
else
begin
Res := CanStack(MapHex); // Air transport.
if Res = mvOK then
begin
if Good then Good := FriendlyHex(MULF.Column, MULF.Row);
// ****************************************************************************
// It is a friendly hex. Check if this is an attempt to fly to a hex that has a
// unit which can be loaded onto an air transport. For example, the ATR can not
// fly to an empty hex unless it is already carrying a unit it loaded before it
// took to the air.
// ****************************************************************************
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NotLoaded(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if Good and
(not AirTransportReturn) and
(FoundMovSUni2 <> nil) then
begin
UFPhase := Game.Phase;
UFSubPhase := Game.AirSubPhase;
Good := OtherStackFilter(MapHex, UFilterLoadable);
end;
end // End of Res = mvOK.
else Result := Res;
end; // End of FoundMovSUni2 = nil.
end; // End of Result = mvOK.
end; // End of AspFlyA.
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
// ****************************************************************************
// First check for a transport in the moving stack that is carrying half of a
// unit in the LargeAirTransportSet, while a second transport, which is not in
// the moving stack is carrying the other half. If this condition is detected,
// then automatically add the second transport to the moving stack.
// ****************************************************************************
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
// ****************************************************************************
// Check if Cargo is being carried by another ATR (Second, which is not in the
// moving stack).
// ****************************************************************************
if NotSupplyLoaded(F1stMovSUni2) then
begin
if Second <> nil then
begin
PickingUpMultipleUnits := False;
AddToMovingStack(Second);
end;
if First <> nil then
begin
PickingUpMultipleUnits := False;
AddToMovingStack(First);
end;
end;
Inc(F1stMovSUIndx2);
end; // End of while.
CantReturnAD;
Exit;
end;
end; // End of Case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pAirTransport.
// ****************************************************************************
// Check unloading land units from a sea area into a friendly land hex.
// ****************************************************************************
pUnloadLandUnits:
begin
if FTCFails or RestrictedReinforcement then Exit;
ComputeUnits; // Count the # of limited activities needed to move stack.
if MULF.OnLand and EnemyStackOrHex(MapHex) then Result := mvHexControl
else Result := CanStack(MapHex); // Unloading land units.
if Result = mvOK then
begin
if not FirstMU.CanUnloadIntoHex(MULF.Hex) then Result := mvNoDebark
else
begin
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves) then
begin
Result := mvLandMoves;
Break;
end;
end;
if (Result = mvOK) and AnyWillBeDisrupted then
Result := mvDisruptedExt;
end;
end;
end;
// ****************************************************************************
// Check invasion.
// ****************************************************************************
pInvasion:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion A.');
ComputeUnits; // Count the # of limited activities needed to move stack.
Result := CanStack(MapHex, True); // Invasion.
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion B.');
if Result = mvOK then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion C.');
if not FirstMU.CanInvadeIntoHex(MULF.Hex) then Result := mvNoInvasion
else
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion D.');
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if MPow.LegalCountry and
(MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves) then
begin
Result := mvLandMoves;
Break;
end;
end;
end;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion E.');
if Result = mvOK then
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion F.');
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NoAttack(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 <> nil then Result := mvLandAttacks;
end;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion G.');
if (Result = mvOK) and AnyWillBeDisrupted then
Result := mvDisruptedExt;
end;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pInvasion H.');
end;
pParadrop:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pParadrop A.');
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit;
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
// ShowMessageOK('[TMovingStack.CanMoveTo]: pParadrop B.');
case Game.AirSubPhase of
aspCAP:
begin
UFSide := Game.PhasingSide;
LandCombatHexes.Search(MULF.Hex, Index, CR);
Good := ((CR = nil) or
(not CR.CombatHexUnits.SurprisedStack(MapHex,
Game.NonPhasingSide)))
and
FriendlyHex(MULF.Column, MULF.Row);
if Good then
begin
if MapHex.Empty then
begin
// ****************************************************************************
// If the hex is empty, then all units in Self need to cooperate with the
// notional unit (i.e., whoever controls the hex).
// ****************************************************************************
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if DoesntCooperatesWithHex(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
Good := FoundMovSUni2 = nil;
end
else
// ****************************************************************************
// If the hex contains units, Self has to cooperate with all the friendly units
// in the hex.
// ****************************************************************************
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < MapHex.Count do
begin
F1stMovSUni2 := TUnit(MapHex.Item[F1stMovSUIndx2]);
if not DoesCooperate(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
Good := FoundMovSUni2 = nil;
end;
end; // End of Good so far.
end; // End of AspCAP.
aspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
if Map.WeatherTerrain[MULF.Column, MULF.Row] in [teSea,
teLake] then
Result := mvNoTarget
else
Result := CanStack(MapHex, True); // Paradrop.
if Result = mvOK then
begin
CR := LandCombatHexes.FindCombat(MULF.Hex);
for MPI := Low(TMajorCountries) to High(TMajorCountries) do
begin
MPow := MajorPowers[MPI];
if not MPow.LegalCountry then Continue;
UFCountry := MPow.ID;
if (MPow.CurrLimits.LandMoves <> aUnlimited) and
(LandUnits[MPI] > MPow.CurrLimits.LandMoves) then
Result := mvLandMoves
else if (LandUnits[MPI] > 0) and
(MPow.CurrLimits.LandAttacks = 0) and
((CR = nil) or
(not (CR.CombatHexUnits.HasUnit
(UFilterControllingMajorCountry)))) then
Result := mvLandAttacks;
if Result <> mvOK then Break;
end; // End of for each major power.
// ****************************************************************************
// Perform another check of all the units in Self to see if a Communist Chinese
// unit is part of the attack. If so, then see if a Communist Chinese unit is
// already in CR.CombatHexUnits.
// ****************************************************************************
CCCount := 0;
for CCIndex := 0 to Self.Count - 1 do
begin
CCUnit := Self[CCIndex];
// CC unit in Self.
if CCUnit.Country = CommunistChina.ID then Inc(CCCount);
end;
// ****************************************************************************
// If Self has a CC unit, then a check has to be made for both
// CCLimitsCurr.LandMoves and CCLimitsCurr.LandAttacks.
// ****************************************************************************
if CCCount > 0 then
begin
if (CCLimitsCurr.LandMoves <> aUnlimited) and
(CCCount > CCLimitsCurr.LandMoves) then
Result := mvLandMoves
else
begin // Enough land moves available.
if CCLimitsCurr.LandAttacks = 0 then
begin // Check existing attack.
if (CR = nil) then Result := mvLandAttacks
else
begin
CCFound := False;
for CCIndex := 0 to CR.CombatHexUnits.Count - 1 do
begin
CCUnit := CR.CombatHexUnits[CCIndex];
if CCUnit.Country = CommunistChina.ID then
begin
CCFound := True;
Break; // CC unit already in the attack.
end;
end;
if not CCFound then Result := mvLandAttacks;
end;
end; // End of CCLimitsCurr.LandAttacks = 0.
end; // End of enough land moves available.
end; // End of CCCount > 0.
if Result = mvOK then
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NotLoaded(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 <> nil then Result := mvNotLoaded
else
begin
F1stMovSUIndx2 := 0;
U := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if IsAirLandingUnit(F1stMovSUni2) then
begin
U := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
// ****************************************************************************
// If there is no air landing unit in the stack, just check for an enemy hex.
// ****************************************************************************
if U = nil then Good := EnemyHex(MULF.Column, MULF.Row)
else
// ****************************************************************************
// If an air landing unit has been found (U <> nil), then check for an
// accompanying paratroop unit in the moving stack or in the hex.
// ****************************************************************************
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
// ****************************************************************************
// Moving stack check for accompanying paratroop unit.
// ****************************************************************************
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if IsParatroopUnit(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
// ****************************************************************************
// If there is paradrop unit in the stack, check for an enemy hex.
// ****************************************************************************
if FoundMovSUni2 <> nil then
Good := EnemyHex(MULF.Column, MULF.Row)
else
// ****************************************************************************
// Check for an accompanying paratroop unit in the hex.
// ****************************************************************************
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < MapHex.Count do
begin
F1stMovSUni2 := TUnit(MapHex.Item[F1stMovSUIndx2]);
if IsParatroopUnit(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 = nil then
Result := mvNoExistingPara
// ****************************************************************************
// Friendly paratroop unit found in hex.
// ****************************************************************************
else Good := EnemyHex(MULF.Column, MULF.Row);
end;
end;
end;
end;
end;
end; // End AspFlyA.
AspInterceptA:
begin
// ShowMessageOK('[TMovingStack.CanMoveTo]: pParadrop AspInterceptA.');
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of Case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pParadrop.
// ****************************************************************************
// Check land combat declaration.
// ****************************************************************************
pLandCombatDeclaration: // CanMoveTo.
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
if not CanMove(MULF.Column, MULF.Row, Disrupt) then
Result := mvAttack // No.
else
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if NoAttack(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
if FoundMovSUni2 <> nil then
Result := mvLandAttacks // No.
else if LandCombatHexes.Search(MULF.Hex, Index, CR) and
((not CooperatesNotFlying(Self)) or
(not CooperatesNotFlying(CR.CombatHexUnits))) then
Result := mvAttackNoCoop; // No.
end;
end;
// ****************************************************************************
// Check shore bombardment.
// ****************************************************************************
pShoreBombardmentA, pShoreBombardmentD:
begin
F1stMovSUIndx4 := 0;
FoundMovSUni4 := nil;
while F1stMovSUIndx4 < Count do
begin
F1stMovSUni4 := TNavalUnit(Item[F1stMovSUIndx4]);
if NoSB(F1stMovSUni4) then
begin
FoundMovSUni4 := F1stMovSUni4;
Break;
end;
Inc(F1stMovSUIndx4);
end;
if FoundMovSUni4 <> nil then
Result := mvNoShoreBombardment
else
begin
F1stMovSUIndx4 := 0;
FoundMovSUni4 := nil;
while F1stMovSUIndx4 < Count do
begin
F1stMovSUni4 := TNavalUnit(Item[F1stMovSUIndx4]);
if NoSBFactors(F1stMovSUni4) then
begin
FoundMovSUni4 := F1stMovSUni4;
Break;
end;
Inc(F1stMovSUIndx4);
end;
if FoundMovSUni4 <> nil then
Result := mvNoShoreBombardmentFactors;
end;
end;
// ****************************************************************************
// Check attacking HQ support.
// ****************************************************************************
pHQSupportA:
begin
TMapArea.ConnectingHexsideRange(PickUpPoint.Hex, MULF.Hex, HS);
if (not LandCombatHexes.Search(MULF.Hex, Index, CR)) or
(HS <> FirstMU.AttackingHexside) then
Result := mvHQSupportA
else if CR.HQA <> nil then
Result := mvHasHQSupportA;
end;
// ****************************************************************************
// Check defending HQ support.
// ****************************************************************************
pHQSupportD: // CanMoveTo.
begin
HQSameHex := ExactSameLocation(PickUpPoint, MULF);
HQAdjacent := THexArea.AdjacentHexes(PickUpPoint.Column,
PickUpPoint.Row, MULF.Column, MULF.Row);
if (not LandCombatHexes.Search(SmallPoint(MULF.Column, MULF.Row),
Index, CR)) or ((not HQSameHex) and (not HQAdjacent)) or
(LandCombatHexes.Search(SmallPoint(PickUpPoint.Column,
PickUpPoint.Row), Index, CR2) and HQAdjacent) then
Result := mvHQSupportD
else if (not HQSameHex) and
(not FirstMU.CooperatesWithLocation(MULF.Hex)) then
Result := mvHQSupportDNoCoop
else if (CR <> nil) and (CR.HQD <> nil) then
Result := mvHasHQSupportD;
end;
pGroundSupport:
begin
ComputeUnits; // Count the # of limited activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit;
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
UFUnit := FirstMU;
case Game.AirSubPhase of
aspCAP:
begin
Good := LandCombatHexes.Search(MULF.Hex, Index, CR) and
(not CR.CombatHexUnits.SurprisedStack(MapHex,
Game.NonPhasingSide))
and
FriendlyHex(MULF.Column, MULF.Row);
if Good then
begin
// ****************************************************************************
// If the hex is empty, then all units in Self need to cooperate with the
// notional unit (i.e., whoever controls the hex). Otherwise, Self has to
// cooperate with all the friendly units in the hex.
// ****************************************************************************
if MapHex.Empty then
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < Count do
begin
F1stMovSUni2 := TUnit(Item[F1stMovSUIndx2]);
if DoesntCooperatesWithHex(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
Good := FoundMovSUni2 = nil;
end
else
// ****************************************************************************
// If the hex contains units, Self has to cooperate with all the friendly units
// in the hex.
// ****************************************************************************
begin
F1stMovSUIndx2 := 0;
FoundMovSUni2 := nil;
while F1stMovSUIndx2 < MapHex.Count do
begin
F1stMovSUni2 := TUnit(MapHex.Item[F1stMovSUIndx2]);
if not DoesCooperate(F1stMovSUni2) then
begin
FoundMovSUni2 := F1stMovSUni2;
Break;
end;
Inc(F1stMovSUIndx2);
end;
Good := FoundMovSUni2 = nil;
end;
end;
end; // End of AspCAP.
aspFlyA:
begin
UFSide := Game.NonPhasingSide;
if NoCooperationWithTarget then Exit; // CanMoveTo has been set.
Good := LandCombatHexes.Search(MULF.Hex, Index, CR);
if Good then
begin
if not HasAir then
begin
if HDistance > 1 then Result := mvBombardAdjacent
else if Map.HexWeather[MULF.Column, MULF.Row] in
NoAirFactorsWeather then
Result := mvBombardWeather;
end;
end;
end;
aspFlyD:
begin
UFSide := Game.NonPhasingSide;
if NoCooperationWithTarget then Exit; // CanMoveTo has been set.
Good := LandCombatHexes.Search(MULF.Hex, Index, CR);
if Good then
begin
if CR.CombatHexUnits.SurprisedStack(MapHex,
Game.NonPhasingSide) then
Result := mvAirSurprisedGroundSupport
else if not HasAir then
begin
if HDistance > 1 then Result := mvBombardAdjacent
else if Map.HexWeather[MULF.Column, MULF.Row] in
NoAirFactorsWeather then
Result := mvBombardWeather;
end;
end;
end; // End of AspFlyD.
AspInterceptA:
begin
Good := LandCombatHexes.Search(MULF.Hex, Index, CR);
if Good then
begin
if CantInterceptA then Exit;
end;
end;
AspInterceptD:
begin
Good := LandCombatHexes.Search(MULF.Hex, Index, CR);
if Good then
begin
if CantInterceptD then Exit;
end;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of Case Game.AirSubPhase
if not Good then Result := mvNoTarget;
end; // End of pGroundSupport.
pLandCombatResolution:
begin
case Game.Phase_LandCombatResolution.CurrentSubPhase of
// ****************************************************************************
// The code should never get here for LCRspHexControl since that is processed
// using a digression.
// ****************************************************************************
LCRspLandCombatSelection, LCRspChooseCombatType,
LCRspLandCombatResolution,
LCRspAssignLosses, LCRspHexControl: ;
LCRspRetreats:
begin
if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement then
Exit;
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
if CantTravel then Result := mvMinorLimit // LCRspRetreats.
else Result := mvForeignCommitment;
Exit;
end;
if not RetreatHexes.Search(MULF.Column, MULF.Row) then
Result := mvNoRetreat
else
Result := CanStack(MapHex); // LCRspRetreats.
if (Result = mvOK) and FTCFails then Exit;
end;
LCRspAdvanceAfterCombat:
begin
if MULF.AtSea then Result := mvNoLandMove
else if FTCFails or
IllegalPartisanMove or
IllegalWarlordMove or
RestrictedReinforcement then
Exit
// ****************************************************************************
// Moving a minor unit to sea or outside its home country may not be permitted.
// ****************************************************************************
else if not CanMoveMinor(MULF.Column, MULF.Row, CantTravel) then
begin
if CantTravel then Result := mvMinorLimit // LCRspAdvanceAfterCombat.
else Result := mvForeignCommitment;
Exit;
end
else
begin
if not CanMove(MULF.Column, MULF.Row, Disrupt) then
begin // Move fails. Determine why for error message.
UFSide := Game.PhasingSide;
if (Game.CombatResult.LRetreat <> lcrBreakthrough) and
(HDistance > 1) then
Result := mvNoBreakthrough
else if EnemyStack(MapHex) or
(EnemyHex(MULF.Column, MULF.Row) and
MapHex.HasUnit(UFilterNotSide)) then
Result := mvNoOverrun
else if MapHex.HasUnit(UFilterNotSide) and
(not EnemyHex(MULF.Column, MULF.Row)) then
Result := mvNeutralNotAtWar
else
begin
Result := CanStack(MapHex); // Advance after combat.
if Result = mvOK then
Result := mvMP; // Advance after combat.
end;
end;
if Result = mvOK then
begin // Determine LHR. Check for stacking limits.
LandHexList.Search(MULF.RefCol, MULF.RefRow, Index, LHR);
Result := CanStack(MapHex, False, nil, False, False, Side);
if (Result = mvOK) and Disrupt then Result := mvDisruptedExt;
end;
end;
end; // End of LCRspAdvanceAfterCombat.
end; // End of case Phase_LandCombatResolution.CurrentSubPhase.
end; // End of pLandCombatResolution.
// ****************************************************************************
// Check air rebase movement.
// ****************************************************************************
pAirRebase:
begin // Both FTCFails and RestrictedReinforcement set CanMoveTo.
if FTCFails or RestrictedReinforcement then Exit;
C := TMajorCountry(Countries[FirstMU.ControllingMajorCountry]);
ComputeUnits; // Count the # of limited activities needed to move stack.
// ****************************************************************************
// First check for rebasing from a naval transport to a land hex.
// ****************************************************************************
if FirstMU.WasAboardTransport and
(not (FirstMU.WasAboardWhom.UnitType in CarrierSet)) then
begin
if not AirHexList.Search(MULF.Column, MULF.Row, Index, AHR) then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
' Destination hex ' + HexName(MULF.Hex) +
' was not in AirHexList, whose count = ' +
IntToStr(AirHexList.Count));
*)
Result := mvOutOfRange; // Air rebase, unload from transport.
end
else
begin
Result := CanStack(MapHex); // Air rebase, unload from transport.
if (Result = mvOK) and
(not FirstMU.CanUnloadIntoHex(MULF.Hex)) then
Result := mvNoUnload;
end;
end
else
// ****************************************************************************
// Rebasing land based air from one land hex to another, or carrier air units.
// ****************************************************************************
begin
if MULF.AtSea then
begin
if FirstMU.UnitType <> utCarrierAir then
Result := mvLandAirInSeaArea
else if not AirHexList.Search(RefC, RefR, Index, AHR) then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
UFUnit.ViewName +
' can''t rebase to ' +
HexName(SmallPoint(RefC, RefR)) +
' because it is not in AirHexList.');
*)
Result := mvNoCarrier;
end
end
// ****************************************************************************
// Check for sufficient air missions or Rebase For Free.
// ****************************************************************************
else if (C.CurrLimits.AirMissions = 0) and
((not TAirUnit(FirstMU).CanAirRebaseForFree) or
// ****************************************************************************
// Rebasing for free requires not changing the unit's hex location.
// ****************************************************************************
(not SameHex)) then
Result := mvOnlyInSameHex
else
begin // The unit is moving to a land hex.
// ****************************************************************************
// RebaseRange sets CheckExtended and DoubleRange. CanMoveTo.
// ****************************************************************************
RebaseRangeMS := TAirUnit(FirstMU).RebaseRange(CheckExtended,
DoubleRange);
(*
if (FirstMU.Country = Finland.ID) and (HexHomeC = Sweden) then
begin
DistToHex := Map.AirDistance(pAirRebase, FirstMU, PickUpPoint,
MULF.Hex,
UnitHomeCountryCommonwealth(FirstMU),
RebaseRangeMS);
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
' DistToHex = ' + IntToStr(DistToHex));
end;
*)
// ****************************************************************************
// MC is the governed area that geographically owns the destination hex, and
// HexC is the major power that controls the destination hex.
// We may want to know the minor country that controls the hex (HexMinC).
// ****************************************************************************
if EnemyStack(MapHex) then Result := mvEnemyUnit
// ****************************************************************************
// When OptRules.Internment is active, air units belonging to minor countries
// can fly into neutral minor countries to be interned. The following
// conditionals permit these moves.
// ****************************************************************************
else if OptRules.Internment and
(UnitHomeCountryCommonwealth(FirstMU).ClassType =
TMinorCountry) and
(HexHomeC.ClassType = TMinorCountry) and
(not HexHomeC.Conquered) and
HexHomeC.Neutral then
begin
if THexArea.HexDistance(PickUpPoint.Hex, MULF.Hex, MapColumns,
True) > RebaseRangeMS then
CanMoveTo := mvOutOfRange // Air rebase for internment.
else
Result := mvOK; // Internment possible.
end
else if not AirHexList.Search(MULF.RefCol, MULF.RefRow, Index,
AHR) then
begin
if not MapHex.Cooperates(Self) then Result := mvStackingNoCoop
else Result := mvOutOfRange; // Air rebase to & from a land hex.
end
else if (HexC = nil) or (HexC.Side <> C.Side) then
Result := mvHexControl;
end; // End of unit rebasing to a land hex.
if Result = mvOK then
begin
if FirstMU.UnitType = utCarrierAir then
begin
UFUnit := FirstMU;
UFPhase := pAirRebase;
UFSubPhase := AspNone;
// ****************************************************************************
// Carrier air units.
// ****************************************************************************
if MULF.AtSea then
begin
if SameHex then
begin
UFSection := MULF.Section;
// ****************************************************************************
// See if there is a carrier that the unit can load onto in the MULF Section -
// other than its current parent carrier.
// ****************************************************************************
if not MapHex.HasUnit(UFilterCarrierCanLoadPlaneInSectionExcept)
then
Result := mvNoCarrier;
end
else
begin // Destination at sea, different hex.
// ****************************************************************************
// RebaseRange sets CheckExtended and DoubleRange. CanMoveTo.
// ****************************************************************************
RebaseRangeMS := TAirUnit(FirstMU).RebaseRange(CheckExtended,
DoubleRange);
UFRange := RebaseRangeMS -
Map.AirDistance(Game.Phase, FirstMU,
PickUpPoint, MULF.Hex,
UnitHomeCountryCommonwealth(FirstMU),
RebaseRangeMS, False, True);
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
UFUnit.ViewName +
' has RebaseRange = ' + IntToStr(RebaseRangeMS) +
', has UFRange = ' + IntToStr(UFRange) +
', and is trying to rebase into ' + HexName(MULF.Hex));
*)
// ****************************************************************************
// See if there is a carrier that the unit can load onto within range - other
// than its current parent carrier.
// ****************************************************************************
if not SAStack.HasUnit(UFilterCarrierCanLoadPlaneInRangeExcept)
// if not MapHex.HasUnit(UFilterCarrierCanLoadPlaneInRangeExcept)
then
begin
(*
ShowMessageOK('[TMovingStack.CanMoveTo]: pAirRebase ' +
UFUnit.ViewName +
' was unable to rebase into ' + HexName(MULF.Hex));
*)
Result := mvNoCarrier;
end;
end;
end // End of destination being AtSea.
// ****************************************************************************
// Carrier air unit whose destination is not at sea.
// ****************************************************************************
else if not MapHex.HasUnit(UFilterCanLoadOntoExcept) then
Result := CanStack(MapHex); // Carrier air unit.
end
else Result := CanStack(MapHex); // Air rebase.
end;
end;
end; // End of pAirRebase.
pAirReorganization:
begin
ComputeUnits; // Count the # of activities needed to move stack.
HasAir := HasUnit(UFilterAirUnit); // Air unit, not artillery unit.
if UnableToFlyToHex then Exit; // Checks within range.
// ****************************************************************************
// Set Good which indicates whether the unit can fly depending on the target
// units in the hex.
// ****************************************************************************
Good := True;
UFUnit := FirstMU;
case Game.AirSubPhase of
AspCAP:
begin
UFSide := Game.PhasingSide;
Good := MapHex.HasUnit(UFilterEnemyDisruptedNonHQUnit);
end;
AspFlyA:
begin
UFSide := Game.NonPhasingSide;
// CanMoveTo has been set.
if InsufficientAirMissions or NoCooperationWithTarget then Exit;
if HasUnit(UFilterFlyingBoat) then
Res := CanStack(MapHex)// Air supply.
else
Res := mvOK;
if (Res = mvOK) and Good then
Good :=
MapHex.HasUnit(UFilterFriendlyCooperatingDisruptedNonHQUnit)
else
Result := Res;
end;
AspInterceptA:
begin
if CantInterceptA then Exit;
end;
AspInterceptD:
begin
if CantInterceptD then Exit;
end;
AspReturnA, AspReturnD:
begin
CantReturnAD;
Exit;
end;
end; // End of Case Game.AirSubPhase.
if not Good then Result := mvNoTarget;
end; // End of pAirReorganization.
pPartisan: CheckUsingSetupTray;
pReturnToBaseA, pReturnToBaseD: // CanMoveTo.
begin
if HasAirAndNaval then
begin
Result := mvAbortAirNaval; // Cannot move air & naval together.
Exit;
end
else if EnemyStackOrHex(MapHex) then Result := mvHexControl
else if FTCFails or
((FirstMU is TNavalUnit) and
(InvalidNavalGroup or
InvalidNavalMove)) or
// Trying to move to an all-sea hex.
RestrictedReinforcement then
Exit
else if InvalidDestination(False) then Exit;
end;
pVichy:
begin
case Game.VichySubPhase of
vspControl: ;
vspMoveNonFrenchLandAir:
begin
if MULF.OnLand and EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl;
// ShowMessageOK('[TMovingStack.CanMoveTo]: vspMoveNonFrenchLandAir ');
end
else if RestrictedReinforcement then Exit;
end;
vspMoveNonFrenchNaval:
begin
if MULF.OnLand and EnemyStackOrHex(MapHex) then
begin
Result := mvHexControl;
// ShowMessageOK('[TMovingStack.CanMoveTo]: vspMoveNonFrenchNaval ');
end
else if RestrictedReinforcement then Exit;
end;
vspMoveFrenchAtSea:
begin
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if (not MULF.OnLand) or
((HexC <> France) and
(HexC <> VichyFrance)) then
Result := mvFranceOnly
// ****************************************************************************
// When returning French units at sea to base during Vichy declaration, only
// hexes in the NearestHexes list are legal. This might not be called.
// ****************************************************************************
else if NearHexes.Empty then
Result := mvNoRelocateHex
else if not NearHexes.Search(MULF.Column, MULF.Row) then
Result := mvOutOfRange; // French returning from sea.
end;
vspMoveFrenchLandAirAxis:
begin
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if MULF.OnLand and (HexC <> France) and (HexC <> VichyFrance) then
Result := mvFranceOnly;
end;
vspMoveFrenchNavalAxis:
begin
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if MULF.OnLand and (HexC <> France) and (HexC <> VichyFrance) then
Result := mvFranceOnly;
end;
vspDestroyFrench: ;
vspMoveFrenchLandAirAllied:
begin
if RestrictedReinforcement then Exit;
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if MULF.OnLand and (HexC <> France) and (HexC <> VichyFrance) then
Result := mvFranceOnly;
end;
vspMoveFrenchNavalAllied:
begin
if RestrictedReinforcement then Exit;
// ****************************************************************************
// There is an additional restriction during some Vichy subphases: the units
// must be moved to France or Vichy France.
// ****************************************************************************
if MULF.OnLand and (HexC <> France) and (HexC <> VichyFrance) then
Result := mvFranceOnly;
end;
vspProduction: ;
vspSetup: CheckUsingSetupTray;
vspMoveFrenchVichy: ;
vspUnitControl: ;
vspConquerFrance: ;
end; // End of case Game.VichySubPhase.
end;
end; // End of case Game.Phase.
// ShowMessageOK('[TMovingStack.CanMoveTo] Z.');
end;
// ****************************************************************************
// Finally, check if the move violates a Neutrality Pact. This takes
// restriction applies across all unit types and all phases of the game.
// ****************************************************************************
if (Result in [mvOK, mvDisruptedExt]) and HexViolatesPact(MULF.Hex) then
Result := mvPact;
end; // End of CanMoveTo.
I DID NOT READ IT ANYWHERE.