Shannon V. OKeets
Posts: 22095
Joined: 5/19/2005 From: Honolulu, Hawaii Status: offline
|
quote:
ORIGINAL: Extraneous quote:
ORIGINAL: Shannon V. OKeets My 10 years of playing WIF over the board yielded "very little knowledge"???? 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... 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. (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.) 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.
< Message edited by Shannon V. OKeets -- 6/12/2013 8:50:52 PM >
_____________________________
Steve Perfection is an elusive goal.
|