Shannon V. OKeets
Posts: 22095
Joined: 5/19/2005 From: Honolulu, Hawaii Status: offline
|
quote:
ORIGINAL: bo quote:
ORIGINAL: Shannon V. OKeets quote:
ORIGINAL: bo quote:
ORIGINAL: micheljq quote:
ORIGINAL: Joseignacio I think he made the dates public to have an extra push to meet them, since all the fans would know them. And I think now he doesn't (by now) because the time to dedicate to the bugs is unpredictable. Mich I have always respected yours and Joses opinions, always, but this bug thing annoys me to no end, if the code is written right in the first place why is there so many so called bugs, I trully dont get it. I have not posted because I will end up saying the wrong thing but I do read the posts every 2 weeks or so. BOI agree with that, but I mean there is unpredictable bugs happening in the development of all PC games anyway. So they never know when the product will be finished until it is. It is like that in all the industry, MWiF is not an exception. "... the code is written right in the first place ..." made me chuckle. You might examine the '2' sentences you wrote for spelling and grammar mistakes. And then think about writing 300,000 sentences where zero mistakes are permitted. Indeed, simply count to 300,000 while pondering the elusiveness of perfection in all things human. Mistakes where, I dont understand about bugs, I am not a programmer, but if you are a trained programmer with a lot of experience at programming why isnt the code written right in the first place I am not being smart just inquisitive after reading some of the posts since last month, I mean wouldnt it save you a lot of trouble then having to go back over it time and time again which has delayed this endeavour for years not months, I know [meaning me] go back where you came from, how dare you challenge the powers that be. I hope you posters have other lives then just hanging around her for a couple more years of frustration Bo Bo, Here is an example of the world I work in every day. This is a little over 500 lines of code that determine the odds column for a land combat. It does not roll the die/dice, nor does it "look up" the result within the column once the die/dice is/are rolled. I have revised this code quite a lot over the past 2-3 months to maintain fractions during its calculations and leave the decision about whether to use rounded numbers or not to the calling/external routines. Notice all the reference to optional rules, in particular, the dual logic paths for 1D10 and 2D10. There are 12 parameters for this routine which have to be set prior to its execution, and the routine is called from 26 different places in the code. Just setting the parameters correctly for this one routine offers 312 opportunites for typos. Any changes to the routine requires examining the 26 places this routine is called, to make sure the changes for one of them does not cause problems for any of the others. By the way, while most of the MWIF is written using "object oriented" code, this is a stand-alone routine and merely "grouped with" the object Land_Combat_Hex. ===
// ****************************************************************************
function LandCombatColumn(
const IsAssault: Boolean; // Set to False for testing overruns.
A, D: Extended; // A = Attack & D = Defend factors are input.
out DRMRounded: Integer; // Returned die roll modifier for 2D10.
out DRM: Extended; // Same as DRMRounded, but not rounded.
const ColMod: Extended; // Shifts due to Fractional Odds and 1D10 HQ support.
out TableCol: Byte; // Returned value for 1D10: between 0 and 9.
const Hex: TSmallPoint; // Used to find TLandCombatHex.
AUseSnowUnits, DUseSnowUnits: Boolean;
const FractionBonus: Boolean;
const ForceInvading: Boolean = False): Boolean;
// ****************************************************************************
// LandCombatColumn does not roll dice to determine the odds. The aspects of
// land combat resolution that require die rolls to determine odds are performed
// prior to the call to LandCombatColumn. These are for 1D10 HQ Support and
// fractional odds. These are the input parameters ColMod and FractionBonus.
// ****************************************************************************
var
CRatio: Extended;
Inverse: Integer;
AnyInvading: Boolean;
Index: Longint;
CR: TLandCombatHex;
Counter: Integer;
WorkCol: Integer;
ColShifts: Integer;
IsCombatHex: Boolean;
LCTerrain: TTerrainTypes;
LCWeather: TWeatherTypes;
InCity: Boolean;
AttackingStack: TUnitStack;
DefendingStack: TUnitStack;
AHasArmor: Boolean;
CityDRM: Extended;
procedure GetHQBonus;
begin // If there is HQ Support, it is automatic when using the 2D10 CRT.
if OptRules.TwoD10LandCRT and IsCombatHex then
begin // HQ's provide half their Reorganization value as a modifier.
if CR.HQA <> nil then DRM := DRM + (CR.HQA.Reorg / 2);
if CR.HQD <> nil then DRM := DRM - (CR.HQD.Reorg / 2);
end;
end;
procedure AddAttacker(var U: TUnit);
begin
if U.Side = Game.PhasingSide then AttackingStack.Add(U);
end;
procedure DoubleAntiTankUnits(var U: TUnit);
begin // Defending units only.
if U.UnitType in AntiTankSet then D := D + TLandUnit(U).CurrDefenseArmor;
end;
procedure DoubleRedAntiTankUnits(var U: TUnit);
begin // Attacking units only.
if (U.UnitType in AntiTankSet) and (TLandUnit(U).AntiTank = latAttDef) then
A := A + TLandUnit(U).CurrAttackArmor;
end;
function AttackModifier(const U: TUnit): Integer;
var // Calculate the die roll modifier.
AttackHex: TSmallPoint;
begin
AttackHex := AttackLocation(U);
// ****************************************************************************
// Set the attack modifier (divisor) from AttackHex through Hexside. This
// accounts for straits, all-sea hexsides, alpine hexsides, and forts
// (cumulative). A result of 0 means no attack is possible. Invasions never
// have any modifier (i.e., the divisor is 1).
// ****************************************************************************
if Map.Terrain[AttackHex.X, AttackHex.Y] = teSea then Result := 1
else Result := TLandUnit(U).HexsideAttackModifier(AttackHex,
THexArea.ConnectingHexsideRange(AttackHex, Hex), True);
end;
procedure AttackingUnitsDRM(var U: TUnit);
// ****************************************************************************
// Only used by 2D10 CRT & BlitzBonus. Modify DRM due to individual attacking
// units.
// ****************************************************************************
var
HS: THexsideRange;
HSAM: Integer;
AttackHex: TSmallPoint;
begin
AttackHex := AttackLocation(U); // Hex where attacker started.
if (U is TLandUnit) and
(not TLandUnit(U).Invading) and
(Map.Terrain[AttackHex.X, AttackHex.Y] <> teSea) then
begin
THexArea.ConnectingHexsideRange(AttackHex, Hex, HS); // This sets HS.
HSAM := AttackModifier(U);
if HSAM > 0 then
begin
// ****************************************************************************
// Include jungle benefit for some elite marines and infantry.
// ****************************************************************************
if (LCTerrain = teJungle) and
TLandUnit(U).Elite and
((U.Country = Japan.ID) or
(U.Country = Australia.ID) or
((U.Country = UnitedStates.ID) and
(U.UnitType in MarineSet))) then
DRM := DRM + (1 / HSAM); // Hexside may affect modifier.
// ****************************************************************************
// Include armor bonus for attacker.
// ****************************************************************************
if (not IsAssault) and
(U.UnitType in ArmoredSet) and
(not InCity) and
(LCTerrain in [teClear, teDesert]) and // I.e., not jungle.
(LCWeather = wFine) and // Forts negate this bonus.
(Map.HexsideTerrainSet[U.Column, U.Row, HS] * FortHexsideSet = []) then
begin // Hexside may affect modifier.
if OptRules.BlitzBonus then
begin
// ****************************************************************************
// BlitzBonus: Add 1 to the die roll for each two attacking ARM, MECH, HQ-A
// units conducting a blitz attack against a clear or desert (non-city) hex in
// fine weather.
// ****************************************************************************
if U.Small then DRM := DRM + (0.25 / HSAM) // 1/4 for a division.
else DRM := DRM + (0.5 / HSAM); // 2 full corps = 1.
end
else
begin // 2D10 modifiers is twice that of BlitzBonus.
if U.Small then DRM := DRM + (0.5 / HSAM) // Half for a division.
else DRM := DRM + (1 / HSAM);
end;
end;
// ****************************************************************************
// Include bonus for attacking with winterized units.
// ****************************************************************************
if AUseSnowUnits and TLandUnit(U).SnowUnit then
DRM := DRM + (1 / HSAM); // Hexside may affect modifier.
end;
end;
// ****************************************************************************
// Include bonus for paradrop.
// ****************************************************************************
if (U is TLandUnit) and TLandUnit(U).Paradropping then
begin
if U.Small then DRM := DRM + 0.5 // Half for a division.
else DRM := DRM + 1;
end;
end;
procedure DefendingUnitsDRM(var U: TUnit);
// ****************************************************************************
// Used by both 1D10 and 2D10 CRTs. Modify DRM due to individual defending
// units.
// ****************************************************************************
var
LU: TLandUnit;
begin
if U is TLandUnit then
begin
LU := TLandUnit(U);
// ****************************************************************************
// Increment die roll for disorganized units.
// ****************************************************************************
if U.Disrupted then
begin
if OptRules.TwoD10LandCRT then
begin
if LU.Small then DRM := DRM + 1 // Half for a division.
else DRM := DRM + 2;
end
else DRM := DRM + 1; // 1D10 CRT.
end;
// ****************************************************************************
// Benefit for winterized defenders (TwoD10LandCRT only).
// ****************************************************************************
if OptRules.TwoD10LandCRT or OptRules.BlitzBonus then
begin
if OptRules.TwoD10LandCRT and
DUseSnowUnits and
LU.SnowUnit then
DRM := DRM - 2;
// ****************************************************************************
// Benefit for anti-tank defenders (TwoD10LandCRT only).
// ****************************************************************************
if OptRules.TwoD10LandCRT and
AHasArmor and
(U.UnitType in AntiTankSet) then
DRM := DRM - 1;
// ****************************************************************************
// Benefit for armored defenders, regardless of CRT chosen.
// ****************************************************************************
if (U.UnitType in ArmoredSet) and
(not InCity) and
(LCTerrain in [teClear, teDesert]) and
(LCWeather = wFine) then
begin
if OptRules.BlitzBonus then
begin
// ****************************************************************************
// BlitzBonus: Subtract 1 from the die roll for each defending ARM, MECH, HQ-A
// unit in a clear or desert (non-city) hex in fine weather.
// ****************************************************************************
if U.Small then DRM := DRM - 0.5 // Half for a division.
else DRM := DRM - 1;
end
else
begin // OptRules.TwoD10LandCRT.
if U.Small then DRM := DRM - 1 // Half for a division.
else DRM := DRM - 2;
end;
end;
end;
end;
end;
procedure CheckEngineerD(var U: TUnit);
var // Hexside terrain may double this modifier.
LU: TLandUnit;
begin
LU := TLandUnit(U);
if LU.UnitType in EngineerSet then
CityDRM := CityDRM - (LU.Combat * LU.DefenseTerrainModifier);
end;
procedure CheckEngineerA(var U: TUnit);
var // Hexside terrain may reduce this modifier.
LU: TLandUnit;
begin
LU := TLandUnit(U);
if LU.UnitType in EngineerSet then
CityDRM := CityDRM + (LU.Combat / AttackModifier(U));
end;
procedure IsHQ(var U: TUnit);
begin // Hexside terrain may reduce this modifier.
if U.UnitType in HeadquartersSet then
CityDRM := CityDRM + (1 / AttackModifier(U));
end;
begin
// ****************************************************************************
// LandCombatColumn.
// ****************************************************************************
AttackingStack := TUnitStack.Create;
DefendingStack := TUnitStack.Create;
try
LCTerrain := Map.Terrain[Hex.X, Hex.Y]; // Attacked hex terrain.
LCWeather := Map.HexWeather[Hex.X, Hex.Y]; // Attacked hex weather.
InCity := Map.City[Hex.X, Hex.Y] <> cyNone; // Whether a city is in the hex.
// ****************************************************************************
// Negate the use of snow units if the weather is wrong.
// ****************************************************************************
if AUseSnowUnits then AUseSnowUnits := LCWeather in SnowWeather;
if DUseSnowUnits then DUseSnowUnits := LCWeather in SnowWeather;
// ****************************************************************************
// Round off attack and defense strength if not using Fractional Odds.
// ****************************************************************************
if not OptRules.FractionalOdds then
begin
A := Util.Round(A);
D := Util.Round(D);
end;
// ****************************************************************************
// See if the hex is already in the list of declared land combat hexes. If so,
// set the value of CR. Both IsCombatHex and DRM are used by GetHQBonus.
// ****************************************************************************
IsCombatHex := LandCombatHexes.Search(Hex, Index, CR); // Sets CR.
DRM := 0; // Initialized to zero.
// ****************************************************************************
// Check for whether any units are attacking the hex.
// ****************************************************************************
if (A = 0) and // No attack strength.
((D > 0) or // Some defense strength, or
((Game.Phase <> pHQSupportD) and // not in these 2 phases.
(not Game.AdvanceSubphase))) then
begin
TableCol := 0;
GetHQBonus; // If zero attack strength only HQ Support applies .
Result := False;
Exit;
end;
// ****************************************************************************
// Place attacking units in AttackingStack. CR contains units previously
// committed to the attack - they are not in MovingStack.
// ****************************************************************************
if IsCombatHex then CR.CombatHexUnits.ForEach(@AddAttacker);
if (not (Game.Phase in [pHQSupportD, pHQSupportA, pShoreBombardmentD])) and
((Game.Phase <> pGroundSupport) or
((Game.AirSubPhase = aspFlyA) and
MovingStack.HasLandUnit)) then
AttackingStack.AddStack(MovingStack);
// ****************************************************************************
// Set flags based on the armor units attacking & defending, and units invading.
// ****************************************************************************
AHasArmor := AttackingStack.HasUnit(UFilterArmoredUnit);
AnyInvading := ForceInvading or AttackingStack.HasUnit(UFilterInvading);
// ****************************************************************************
// Place defending land units in DefendingStack. Filter removes notional units.
// ****************************************************************************
DefendingStack.AddStack(MapStacks[Hex.X, Hex.Y], UFilterLandUnitNotInvading);
// ****************************************************************************
// Place defensive ground support (artillery) units in DefendingStack.
// ****************************************************************************
if (Game.Phase = pGroundSupport) and
(Game.AirSubPhase = aspFlyD) and
MovingStack.HasLandUnit then
DefendingStack.AddStack(MovingStack);
// ****************************************************************************
// Now calculate the Die Roll Modifier (DRM).
// ****************************************************************************
// Loss of attack effectiveness when multiple major powers are attacking.
// ****************************************************************************
if OptRules.AlliedCombatFriction then
DRM := DRM - Pred(MajorCountryCount(AttackingStack.MajorPowersInStack));
// ****************************************************************************
// Invasion against a notional unit benefits the attacker.
// ****************************************************************************
if AnyInvading and
((not IsCombatHex) or
(not CR.IgnoreNotional)) then
DRM := DRM + 1;
// ****************************************************************************
// -2 for all attacking units being territorials and at least 1 defending unit
// being a non-territoiral land unit.
// ****************************************************************************
if AttackingStack.HasAllUnit(UFilterTerritorialUnit) and
DefendingStack.HasLandUnit and
(DefendingStack.HasUnit(UFilterNotTerritorialUnitDefending)) then
begin
if OptRules.TwoD10LandCRT then DRM := DRM - 2
else DRM := DRM - 1; // 1D10.
end;
// ****************************************************************************
// +2 for all defending units being territorials and at least 1 attacking unit
// being a non-territoiral land unit.
// ****************************************************************************
if DefendingStack.HasAllUnit(UFilterTerritorialUnit) and
AttackingStack.HasLandUnit and
(AttackingStack.HasUnit(UFilterNotTerritorialUnitAttacking)) then
begin
if OptRules.TwoD10LandCRT then DRM := DRM + 2
else DRM := DRM + 1; // 1D10.
end;
// ****************************************************************************
//
// Final calculations for 2D10 CRT.
//
// ****************************************************************************
if OptRules.TwoD10LandCRT then
begin
if D = 0 then DRM := AutoVictoryModifier // Automatic land combat victory.
else
begin
// ****************************************************************************
// Modify the die roll modifier due to individual units.
// ****************************************************************************
AttackingStack.ForEach(@AttackingUnitsDRM); // Includes armor shifts.
DefendingStack.ForEach(@DefendingUnitsDRM); // Includes armor shifts.
GetHQBonus; // Include HQ support for both sides.
// ****************************************************************************
// Calculate the effect of a city on DRM.
// ****************************************************************************
if InCity then
begin // CityDRM can never be more than 0.
CityDRM := -1;
CityDRM := CityDRM - Map.FactoryList.PrintedFactory[Hex.X, Hex.Y];
DefendingStack.ForEach(@CheckEngineerD); // Decrements CityDRM.
AttackingStack.ForEach(@CheckEngineerA); // Increments CityDRM.
AttackingStack.ForEach(@IsHQ); // Increments CityDRM.
DRM := DRM + MinExtended([0, CityDRM]); // Never a positive effect.
end;
// ****************************************************************************
// Jungle benefit.
// ****************************************************************************
if LCTerrain = teJungle then DRM := DRM - 4;
// ****************************************************************************
// Include the effect of weather on the die roll modifier.
// ****************************************************************************
DRM := DRM - (Map.HexWeatherCombatMod[Hex.X, Hex.Y] * 2);
CRatio := A / D; // Raw odds ratio.
TableCol := 0; // Default value.
// ****************************************************************************
// Determine the CRT column based on the raw odds ratio.
// ****************************************************************************
for Counter := 16 downto 0 do
begin
if CRatio >= OddsColumnFraction[Counter] then
begin
TableCol := Counter;
Break;
end;
end;
// ****************************************************************************
// Take into consideration fractional odds.
// ****************************************************************************
if OptRules.FractionalOdds then
begin // ColMod was set using ComputeFractionalOdds.
if CRatio >= 1 then DRM := DRM + ColMod + (CRatio * 2)
else DRM := DRM + ColMod + 4 - ((1/CRatio) * 2);
end
else
begin // Without fractional odds, the given ColMod is applied directly.
TableCol := Range(TableCol + Round(ColMod), 0, 16);
DRM := DRM + OddsModifier[TableCol];
end;
end;
end
else
// ****************************************************************************
//
// Final calculations for 1D10 CRT.
//
// ****************************************************************************
begin
// ****************************************************************************
// Modify the die roll modifier due to individual units.
// ****************************************************************************
if OptRules.BlitzBonus then
begin
AttackingStack.ForEach(@AttackingUnitsDRM); // Armor benefit.
// ****************************************************************************
// Include penalty for attacking a city with 2 or 3 printed factories.
// ****************************************************************************
if Map.FactoryList.PrintedFactory[Hex.X, Hex.Y] > 1 then DRM := DRM - 1;
end;
DefendingStack.ForEach(@DefendingUnitsDRM);
if D = 0 then TableCol := AutoVictoryColumn // Automatic combat victory.
else
begin
CRatio := A / D; // Raw odds ratio.
// ****************************************************************************
// ColMod includes 1D10 HQ support shifts. The 1D10 CRT only has the attacking
// snow bonus, and it's a two column shift. FractionBonus is the fractional
// odds shift that was passed as a parameter; it's value was calculated using
// ComputeFractionalOdds.
// ****************************************************************************
ColShifts := Round(ColMod) +
(Ord(AUseSnowUnits) * 2) +
Ord(FractionBonus) -
Map.HexWeatherCombatMod[Hex.X, Hex.Y];
WorkCol := 0;
for Counter := 8 downto 0 do
begin
if IsAssault then
begin // (0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 7.0, 10.0);
if CRatio >= AssaultColumnRatios[Counter] then
begin
WorkCol := Counter;
Break;
end;
end
else // Blitzkrieg table.
begin // (0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0);
if CRatio >= BlitzkriegColumnRatios[Counter] then
begin
WorkCol := Counter;
Break;
end;
end;
end;
// ****************************************************************************
// Give an extra column shift for every odds ratio lower than 1:2.
// ****************************************************************************
if CRatio < 0.5 then
begin // Ord(False) = 0; Ord(True) = -1.
Inverse := Trunc(D / A) - Ord(Frac(D / A) > 0); // +1 if remainder > 0.
ColShifts := ColShifts - Inverse;
end;
WorkCol := WorkCol + ColShifts; // This may be negative.
if WorkCol < 0 then
begin // Add to die roll modifier for shifts below 1:2.
TableCol := 0;
DRM := DRM - WorkCol;
end // WorkCol is > 0.
else TableCol := Min([WorkCol, 8]);
end;
end;
Result := True;
finally
AttackingStack.Clear;
DefendingStack.Clear;
AttackingStack.Free;
DefendingStack.Free;
if OptRules.TwoD10LandCRT then DRMRounded := Range(Util.Round(DRM), -21, 21)
else DRMRounded := Util.Round(DRM);
end;
end;
_____________________________
Steve Perfection is an elusive goal.
|