├── .gitignore ├── LICENSE ├── README.md ├── Screenshots ├── AttitudeAdvisorCelebratingYellow.png ├── CDAudioTrackProgress.png ├── CityChangeProductionShieldsAndCancel.png ├── CityStatusAdvisorSortingAndTotal.png ├── CityWindowColorAttitude.png ├── CityWindowSupport.png ├── EngineersCounter.png ├── GameTurn.png ├── PathLineCity.png ├── PathLineUnit.png ├── QuickinfoTooltip.png ├── RadioButtonsHotkeys.png ├── ResizableLists.png ├── ScienceAdvisor.png ├── SortSupportedUnitsList.png ├── SuppressSimplePopups.png ├── TradeAdvisorZeroCost.png ├── UIASettings.png └── UnitsPopupListWithScrollbar.png └── src ├── CIV2UIA_FormStrings.ddp ├── CIV2UIA_FormStrings.dfm ├── CIV2UIA_FormStrings.pas ├── Civ2 with UI Additions - Debug.lnk ├── Civ2 with UI Additions.lnk ├── Civ2Proc.pas ├── Civ2ProcDeclF.inc ├── Civ2ProcImplF.inc ├── Civ2Types.pas ├── Civ2UIA.dof ├── Civ2UIA.dpr ├── Civ2UIA.res ├── Civ2UIAL_FormMain.dfm ├── Civ2UIAL_FormMain.pas ├── Civ2UIAL_FormOptions.dfm ├── Civ2UIAL_FormOptions.pas ├── Civ2UIAL_FormOptions.rc ├── Civ2UIAL_FormOptions.res ├── Civ2UIAL_FormOptions.xml ├── Civ2UIAL_Proc.pas ├── Civ2UIALauncher.dof ├── Civ2UIALauncher.dpr ├── Civ2UIALauncher.ico ├── Civ2UIALauncher.res ├── Civ2UIA_CanvasEx.pas ├── Civ2UIA_FormAbout.ddp ├── Civ2UIA_FormAbout.dfm ├── Civ2UIA_FormAbout.pas ├── Civ2UIA_FormConsole.ddp ├── Civ2UIA_FormConsole.dfm ├── Civ2UIA_FormConsole.pas ├── Civ2UIA_FormSettings.ddp ├── Civ2UIA_FormSettings.dfm ├── Civ2UIA_FormSettings.pas ├── Civ2UIA_FormTest.ddp ├── Civ2UIA_FormTest.dfm ├── Civ2UIA_FormTest.pas ├── Civ2UIA_GIFS.rc ├── Civ2UIA_GIFS.res ├── Civ2UIA_Hooks.pas ├── Civ2UIA_MapMessage.pas ├── Civ2UIA_MapMessages.pas ├── Civ2UIA_MapOverlay.pas ├── Civ2UIA_MapOverlayModule.pas ├── Civ2UIA_Options.pas ├── Civ2UIA_PathLine.pas ├── Civ2UIA_Proc.pas ├── Civ2UIA_QuickInfo.pas ├── Civ2UIA_SnowFlakes.pas ├── Civ2UIA_SortedAbstractList.pas ├── Civ2UIA_SortedCitiesList.pas ├── Civ2UIA_SortedUnitsList.pas ├── Civ2UIA_Types.pas ├── FileInfo.pas ├── GIFS ├── 105.gif ├── 229.gif ├── 250.gif ├── 30001.gif └── 30002.gif ├── MemWatchDog.pas ├── Patches ├── UiaPatch.pas ├── UiaPatch64Bit.pas ├── UiaPatchAI.pas ├── UiaPatchArrangeWindows.pas ├── UiaPatchAttitudeAdvisor.pas ├── UiaPatchCDAudio.pas ├── UiaPatchCDCheck.pas ├── UiaPatchCPUUsage.pas ├── UiaPatchCityStatusAdvisor.pas ├── UiaPatchCityView.pas ├── UiaPatchCityWindow.pas ├── UiaPatchColorCorrection.pas ├── UiaPatchCommon.pas ├── UiaPatchDllGif.pas ├── UiaPatchDrawUnit.pas ├── UiaPatchLimits.pas ├── UiaPatchMapWindow.pas ├── UiaPatchMenu.pas ├── UiaPatchMultiplayer.pas ├── UiaPatchResizableWindows.pas ├── UiaPatchScienceAdvisor.pas ├── UiaPatchSideBar.pas ├── UiaPatchSuppressPopup.pas ├── UiaPatchTaxWindow.pas ├── UiaPatchTests.pas ├── UiaPatchTimers.pas ├── UiaPatchTradeAdvisor.pas ├── UiaPatchUnits.pas ├── UiaPatchUnitsLimit.pas └── UiaPatchUnitsListPopup.pas ├── Tests.pas ├── UiaMain.pas └── UiaSettings.pas /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | *.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | 68 | *.lnk 69 | 70 | Civ2UIALauncher.ini 71 | 72 | # GExperts 73 | *.gex 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 FoxAhead 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Screenshots/AttitudeAdvisorCelebratingYellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/AttitudeAdvisorCelebratingYellow.png -------------------------------------------------------------------------------- /Screenshots/CDAudioTrackProgress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/CDAudioTrackProgress.png -------------------------------------------------------------------------------- /Screenshots/CityChangeProductionShieldsAndCancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/CityChangeProductionShieldsAndCancel.png -------------------------------------------------------------------------------- /Screenshots/CityStatusAdvisorSortingAndTotal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/CityStatusAdvisorSortingAndTotal.png -------------------------------------------------------------------------------- /Screenshots/CityWindowColorAttitude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/CityWindowColorAttitude.png -------------------------------------------------------------------------------- /Screenshots/CityWindowSupport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/CityWindowSupport.png -------------------------------------------------------------------------------- /Screenshots/EngineersCounter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/EngineersCounter.png -------------------------------------------------------------------------------- /Screenshots/GameTurn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/GameTurn.png -------------------------------------------------------------------------------- /Screenshots/PathLineCity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/PathLineCity.png -------------------------------------------------------------------------------- /Screenshots/PathLineUnit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/PathLineUnit.png -------------------------------------------------------------------------------- /Screenshots/QuickinfoTooltip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/QuickinfoTooltip.png -------------------------------------------------------------------------------- /Screenshots/RadioButtonsHotkeys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/RadioButtonsHotkeys.png -------------------------------------------------------------------------------- /Screenshots/ResizableLists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/ResizableLists.png -------------------------------------------------------------------------------- /Screenshots/ScienceAdvisor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/ScienceAdvisor.png -------------------------------------------------------------------------------- /Screenshots/SortSupportedUnitsList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/SortSupportedUnitsList.png -------------------------------------------------------------------------------- /Screenshots/SuppressSimplePopups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/SuppressSimplePopups.png -------------------------------------------------------------------------------- /Screenshots/TradeAdvisorZeroCost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/TradeAdvisorZeroCost.png -------------------------------------------------------------------------------- /Screenshots/UIASettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/UIASettings.png -------------------------------------------------------------------------------- /Screenshots/UnitsPopupListWithScrollbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/Screenshots/UnitsPopupListWithScrollbar.png -------------------------------------------------------------------------------- /src/CIV2UIA_FormStrings.ddp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/CIV2UIA_FormStrings.ddp -------------------------------------------------------------------------------- /src/CIV2UIA_FormStrings.dfm: -------------------------------------------------------------------------------- 1 | object FormStrings: TFormStrings 2 | Left = 548 3 | Top = 303 4 | BorderStyle = bsDialog 5 | Caption = 'Popup names' 6 | ClientHeight = 402 7 | ClientWidth = 305 8 | Color = clBtnFace 9 | Font.Charset = DEFAULT_CHARSET 10 | Font.Color = clWindowText 11 | Font.Height = -11 12 | Font.Name = 'MS Sans Serif' 13 | Font.Style = [] 14 | OldCreateOrder = False 15 | Position = poOwnerFormCenter 16 | DesignSize = ( 17 | 305 18 | 402) 19 | PixelsPerInch = 96 20 | TextHeight = 13 21 | object Label1: TLabel 22 | Left = 2 23 | Top = 2 24 | Width = 301 25 | Height = 78 26 | Caption = 27 | 'List of popup names from GAME.TXT without first @ symbol.'#13#10'For e' + 28 | 'xample:'#13#10'BOND007'#13#10'BONDGLORY'#13#10'This list is saved in the Civ2UIASu' + 29 | 'ppressPopup.txt file.'#13#10'Suppressed messages will be shown in the ' + 30 | 'map overlay instead.' 31 | end 32 | object Memo1: TMemo 33 | Left = 0 34 | Top = 88 35 | Width = 305 36 | Height = 283 37 | Anchors = [akLeft, akTop, akRight, akBottom] 38 | ScrollBars = ssBoth 39 | TabOrder = 0 40 | OnKeyDown = Memo1KeyDown 41 | end 42 | object Button1: TButton 43 | Left = 116 44 | Top = 376 45 | Width = 75 46 | Height = 25 47 | Anchors = [akBottom] 48 | Cancel = True 49 | Caption = 'Close' 50 | TabOrder = 1 51 | OnClick = Button1Click 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /src/CIV2UIA_FormStrings.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_FormStrings; 2 | 3 | interface 4 | 5 | uses 6 | Windows, 7 | Classes, 8 | Controls, 9 | Forms, 10 | StdCtrls; 11 | 12 | type 13 | TFormStrings = class(TForm) 14 | Memo1: TMemo; 15 | Button1: TButton; 16 | Label1: TLabel; 17 | procedure Button1Click(Sender: TObject); 18 | procedure Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 19 | private { Private declarations } 20 | public { Public declarations } 21 | end; 22 | 23 | var 24 | FormStrings: TFormStrings; 25 | 26 | implementation 27 | 28 | {$R *.dfm} 29 | 30 | procedure TFormStrings.Button1Click(Sender: TObject); 31 | begin 32 | Close; 33 | end; 34 | 35 | procedure TFormStrings.Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 36 | begin 37 | if Key = VK_ESCAPE then 38 | begin 39 | Close; 40 | end; 41 | end; 42 | 43 | end. 44 | 45 | -------------------------------------------------------------------------------- /src/Civ2 with UI Additions - Debug.lnk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2 with UI Additions - Debug.lnk -------------------------------------------------------------------------------- /src/Civ2 with UI Additions.lnk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2 with UI Additions.lnk -------------------------------------------------------------------------------- /src/Civ2Proc.pas: -------------------------------------------------------------------------------- 1 | unit Civ2Proc; 2 | 3 | interface 4 | 5 | uses 6 | SysUtils, 7 | Windows, 8 | Civ2Types; 9 | 10 | type 11 | PShortIntArray = ^TShortIntArray; 12 | 13 | TShortIntArray = array[0..32767] of ShortInt; 14 | 15 | TCiv2 = class 16 | private 17 | protected 18 | public 19 | AdjDX: PShortIntArray; 20 | AdjDY: PShortIntArray; 21 | AdvisorWindow: PAdvisorWindow; 22 | CDRoot: PChar; 23 | ChText: PChar; 24 | Cities: ^TCities; 25 | CityGlobals: PCityGlobals; 26 | CitySpiralDX: PShortIntArray; 27 | CitySpiralDY: PShortIntArray; 28 | CityDX: PShortIntArray; 29 | CityDY: PShortIntArray; 30 | CityWindow: PCityWindow; 31 | Civs: ^TCivs; 32 | ColorIndex: PByte; 33 | Commodities: PIntegerArray; 34 | Cosmic: PCosmic; 35 | CurrPopupInfo: PPDialogWindow; 36 | CursorX: PSmallInt; 37 | CursorY: PSmallInt; 38 | FontTimes14b: PFontInfo; 39 | FontTimes16: PFontInfo; 40 | FontTimes18: PFontInfo; 41 | Game: PGame; 42 | HModules: PIntegerArray; 43 | HModulesCount: PInteger; 44 | HumanCivIndex: PInteger; 45 | Improvements: ^TImprovements; 46 | Leaders: ^TLeaders; 47 | LoadedTxtSectionName: PChar; 48 | LockCityWindow: PInteger; 49 | MenuBar: PMenuBar; 50 | MainWindowInfo: PWindowInfo1; 51 | MapCivData: PMapCivData; 52 | MapData: ^PMapSquares; 53 | MapHeader: PMapHeader; 54 | MapWindow: PMapWindow; 55 | MapWindows: PMapWindows; 56 | MciInfo: PMciInfo; 57 | Palette: PPalette; 58 | PathCivilizationDirectory: PChar; 59 | PathWorking: PChar; 60 | PFDX: PShortIntArray; 61 | PFDY: PShortIntArray; 62 | PFStopX: PInteger; 63 | PFStopY: PInteger; 64 | PFData: PPFData; 65 | PrevWindowInfo: PWindowInfo; 66 | RulesCivilizes: ^TRulesCivilizes; 67 | ScreenRectSize: PSize; 68 | ShieldFontInfo: PFontInfo; 69 | ShieldLeft: ^TShieldLeft; 70 | ShieldTop: ^TShieldTop; 71 | SideBar: PSideBarWindow; 72 | SideBarClientRect: PRect; 73 | SprEco: PSprites; 74 | SprRes: PSprites; 75 | SprResS: PSprites; 76 | SprUnits: PSprites; 77 | Units: ^TUnits; 78 | UnitSelected: ^LongBool; 79 | UnitTypes: ^TUnitTypes; 80 | 81 | // Functions 82 | // (All THISCALLs are described as STDCALLs, then assigned with PThisCall()) 83 | {$INCLUDE 'Civ2ProcDeclF.inc'} 84 | 85 | constructor Create(); 86 | destructor Destroy; override; 87 | published 88 | end; 89 | 90 | var 91 | Civ2: TCiv2; 92 | 93 | implementation 94 | 95 | uses 96 | Civ2UIA_FormConsole; 97 | 98 | type 99 | TThisCallThunk = packed record 100 | Part1: Byte; 101 | Part2: Cardinal; 102 | Address: Cardinal; 103 | Part3: Byte; 104 | end; 105 | 106 | const 107 | MAX_THISCALL_THUNKS = 255; 108 | 109 | type 110 | TThisCallThunks = array[0..MAX_THISCALL_THUNKS] of TThisCallThunk; 111 | 112 | PThisCallThunks = ^TThisCallThunks; 113 | 114 | var 115 | ThisCallThunksIndex: Integer = 0; 116 | ThisCallThunks: PThisCallThunks; 117 | 118 | function PThisCall(Address: Cardinal): Pointer; 119 | begin 120 | if ThisCallThunksIndex > MAX_THISCALL_THUNKS then 121 | raise Exception.Create('Maximum ThisCallThunksIndex reached'); 122 | ThisCallThunks[ThisCallThunksIndex].Part1 := $59; // pop ecx 123 | ThisCallThunks[ThisCallThunksIndex].Part2 := $68240C87; // xchg ecx, [esp] 124 | ThisCallThunks[ThisCallThunksIndex].Address := Address; // push Address 125 | ThisCallThunks[ThisCallThunksIndex].Part3 := $C3; // ret 126 | Result := @ThisCallThunks[ThisCallThunksIndex]; 127 | Inc(ThisCallThunksIndex); 128 | end; 129 | 130 | { TCiv2 } 131 | 132 | constructor TCiv2.Create; 133 | begin 134 | if Assigned(Civ2) then 135 | raise Exception.Create('TCiv2 is already created'); 136 | 137 | inherited; 138 | 139 | ThisCallThunks := VirtualAlloc(nil, SizeOf(ThisCallThunks), MEM_COMMIT, PAGE_EXECUTE_READWRITE); 140 | 141 | // For inline ASM all TCiv2 fileds can be referenced directly only inside TCiv2 class, and as Self.FieldName 142 | // Important: by default EAX register contains Self reference 143 | {(*} 144 | 145 | // Variables 146 | AdjDX := Pointer($0062833C); 147 | AdjDY := Pointer($00628344); 148 | AdvisorWindow := Pointer($0063EB10); 149 | CDRoot := Pointer($006AB680); 150 | ChText := Pointer($00679640); 151 | Cities := Pointer($0064F340); 152 | CityWindow := Pointer($006A91B8); 153 | CityGlobals := Pointer($006A6528); 154 | CitySpiralDX := Pointer($00628370); 155 | CitySpiralDY := Pointer($006283A0); 156 | CityDX := Pointer($00630D38); 157 | CityDY := Pointer($00630D50); 158 | Civs := Pointer($0064C6A0); 159 | ColorIndex := Pointer($00637E78); 160 | Commodities := Pointer($0064B168); 161 | Cosmic := Pointer($0064BCC8); 162 | CurrPopupInfo := Pointer($006CEC84); 163 | CursorX := Pointer($0064B1B4); 164 | CursorY := Pointer($0064B1B0); 165 | FontTimes14b := Pointer($0063EAB8); 166 | FontTimes16 := Pointer($006AB1A0); 167 | FontTimes18 := Pointer($0063EAC0); 168 | Game := Pointer($00655AE8); 169 | HModules := Pointer($006E4F60); 170 | HModulesCount := Pointer($006387CC); 171 | HumanCivIndex := Pointer($006D1DA0); 172 | Improvements := Pointer($0064C488); 173 | Leaders := Pointer($006554F8); 174 | LoadedTxtSectionName := Pointer($006CECB0); 175 | LockCityWindow := Pointer($0062EDF8); 176 | MenuBar := Pointer($006A64F8); 177 | MainWindowInfo := Pointer($006553D8); 178 | MapCivData := Pointer($006365C0); 179 | MapData := Pointer($00636598); 180 | MapHeader := Pointer($006D1160); 181 | MapWindow := Pointer($0066C7A8); 182 | MapWindows := Pointer($0066C7A8); 183 | MciInfo := Pointer($006389D4); 184 | Palette := Pointer($006A8C00); 185 | PathCivilizationDirectory := Pointer($006AB600); 186 | PathWorking := Pointer($0064BB08); 187 | PFDX := Pointer($00628350); 188 | PFDY := Pointer($00628360); 189 | PFStopX := Pointer($00673FA0); 190 | PFStopY := Pointer($00673FA4); 191 | PFData := Pointer($0062D03C); 192 | PrevWindowInfo := Pointer($00637EA4); 193 | RulesCivilizes := Pointer($00627680); 194 | ScreenRectSize := Pointer($006AB198); 195 | ShieldFontInfo := Pointer($006AC090); 196 | ShieldLeft := Pointer($00642C48); 197 | ShieldTop := Pointer($00642B48); 198 | SideBar := Pointer($006ABC68); 199 | SideBarClientRect := Pointer($006ABC28); 200 | SprEco := Pointer($00648860); 201 | SprRes := Pointer($00644F00); 202 | SprResS := Pointer($00645068); 203 | SprUnits := Pointer($00641848); 204 | Units := Pointer($006560F0); 205 | UnitSelected := Pointer($006D1DA8); 206 | UnitTypes := Pointer($0064B1B8); 207 | 208 | // Functions 209 | {$INCLUDE 'Civ2ProcImplF.inc'} 210 | {*)} 211 | 212 | // Check structure sizes 213 | if SizeOf(TMSWindow) <> $2D8 then 214 | raise Exception.Create('Wrong size of TMSWindow'); 215 | if SizeOf(TWindowInfo) <> $C5 then 216 | raise Exception.Create('Wrong size of TWindowInfo'); 217 | if SizeOf(TCityWindow) <> $16E0 then 218 | raise Exception.Create('Wrong size of TCityWindow'); 219 | if SizeOf(TGraphicsInfo) <> $114 then 220 | raise Exception.Create('Wrong size of TGraphicsInfo'); 221 | if SizeOf(TMapWindow) <> $3F0 then 222 | raise Exception.Create('Wrong size of TMapWindow'); 223 | if SizeOf(TUnit) <> $20 then 224 | raise Exception.Create('Wrong size of TUnit'); 225 | if SizeOf(TCity) <> $58 then 226 | raise Exception.Create('Wrong size of TCity'); 227 | if SizeOf(TCiv) <> $594 then 228 | raise Exception.Create('Wrong size of TCiv'); 229 | if SizeOf(TAdvisorWindow) <> $4A4 then 230 | raise Exception.Create('Wrong size of TAdvisorWindow'); 231 | if SizeOf(TDialogWindow) <> $2F4 then 232 | raise Exception.Create('Wrong size of TDialogWindow'); 233 | if SizeOf(TCityGlobals) <> $140 then 234 | raise Exception.Create('Wrong size of TCityGlobals'); 235 | if SizeOf(TGame) <> $14A then 236 | raise Exception.Create('Wrong size of TGame'); 237 | 238 | TFormConsole.Log('Created TCiv2'); 239 | end; 240 | 241 | destructor TCiv2.Destroy; 242 | begin 243 | VirtualFree(ThisCallThunks, 0, MEM_RELEASE); 244 | inherited; 245 | end; 246 | 247 | initialization 248 | Civ2 := TCiv2.Create(); 249 | 250 | finalization 251 | Civ2.Free(); 252 | 253 | end. 254 | -------------------------------------------------------------------------------- /src/Civ2UIA.dof: -------------------------------------------------------------------------------- 1 | [FileVersion] 2 | Version=7.0 3 | [Compiler] 4 | A=8 5 | B=0 6 | C=0 7 | D=0 8 | E=0 9 | F=0 10 | G=1 11 | H=1 12 | I=1 13 | J=0 14 | K=0 15 | L=0 16 | M=0 17 | N=1 18 | O=1 19 | P=1 20 | Q=0 21 | R=0 22 | S=0 23 | T=0 24 | U=0 25 | V=1 26 | W=0 27 | X=1 28 | Y=2 29 | Z=1 30 | ShowHints=0 31 | ShowWarnings=1 32 | UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; 33 | NamespacePrefix= 34 | SymbolDeprecated=1 35 | SymbolLibrary=1 36 | SymbolPlatform=0 37 | UnitLibrary=1 38 | UnitPlatform=1 39 | UnitDeprecated=1 40 | HResultCompat=1 41 | HidingMember=1 42 | HiddenVirtual=1 43 | Garbage=1 44 | BoundsError=1 45 | ZeroNilCompat=1 46 | StringConstTruncated=1 47 | ForLoopVarVarPar=1 48 | TypedConstVarPar=1 49 | AsgToTypedConst=1 50 | CaseLabelRange=1 51 | ForVariable=1 52 | ConstructingAbstract=1 53 | ComparisonFalse=1 54 | ComparisonTrue=1 55 | ComparingSignedUnsigned=1 56 | CombiningSignedUnsigned=1 57 | UnsupportedConstruct=1 58 | FileOpen=1 59 | FileOpenUnitSrc=1 60 | BadGlobalSymbol=1 61 | DuplicateConstructorDestructor=1 62 | InvalidDirective=1 63 | PackageNoLink=1 64 | PackageThreadVar=1 65 | ImplicitImport=1 66 | HPPEMITIgnored=1 67 | NoRetVal=1 68 | UseBeforeDef=1 69 | ForLoopVarUndef=1 70 | UnitNameMismatch=1 71 | NoCFGFileFound=1 72 | MessageDirective=1 73 | ImplicitVariants=1 74 | UnicodeToLocale=1 75 | LocaleToUnicode=1 76 | ImagebaseMultiple=1 77 | SuspiciousTypecast=1 78 | PrivatePropAccessor=1 79 | UnsafeType=0 80 | UnsafeCode=0 81 | UnsafeCast=0 82 | [Linker] 83 | MapFile=0 84 | OutputObjs=0 85 | ConsoleApp=1 86 | DebugInfo=0 87 | RemoteSymbols=0 88 | MinStackSize=16384 89 | MaxStackSize=1048576 90 | ImageBase=536870912 91 | ExeDescription= 92 | [Directories] 93 | OutputDir=bin 94 | UnitOutputDir=dcu 95 | PackageDLLOutputDir= 96 | PackageDCPOutputDir= 97 | SearchPath=D:\Delphi\Civ2 UI Additions\src;D:\Delphi\Civ2 UI Additions\src\Patches 98 | Packages=vclx;vcl;rtl;VclSmp;dsnapcon;dsnap;dbrtl;vcldb;bdertl;vcldbx;ibxpress;teeui;teedb;tee;dss;visualclx;visualdbclx;vclactnband;adortl 99 | Conditionals= 100 | DebugSourceDirs=D:\Delphi\Civ2 UI Additions\src;D:\Delphi\Civ2 UI Additions\src\Patches 101 | UsePackages=0 102 | [Parameters] 103 | RunParams= 104 | HostApplication= 105 | Launcher= 106 | UseLauncher=0 107 | DebugCWD= 108 | [Language] 109 | ActiveLang= 110 | ProjectLang= 111 | RootDir= 112 | [Version Info] 113 | IncludeVerInfo=1 114 | AutoIncBuild=1 115 | MajorVer=1 116 | MinorVer=21 117 | Release=4 118 | Build=2754 119 | Debug=0 120 | PreRelease=0 121 | Special=0 122 | Private=0 123 | DLL=0 124 | Locale=1033 125 | CodePage=1252 126 | [Version Info Keys] 127 | CompanyName= 128 | FileDescription= 129 | FileVersion=1.21.4.2754 130 | InternalName= 131 | LegalCopyright= 132 | LegalTrademarks= 133 | OriginalFilename= 134 | ProductName= 135 | ProductVersion=1.0.0.0 136 | Comments= 137 | [HistoryLists\hlDebugSourcePath] 138 | Count=1 139 | Item0=D:\Delphi\Civ2 UI Additions\src;D:\Delphi\Civ2 UI Additions\src\Patches 140 | [HistoryLists\hlUnitAliases] 141 | Count=1 142 | Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; 143 | [HistoryLists\hlSearchPath] 144 | Count=1 145 | Item0=D:\Delphi\Civ2 UI Additions\src;D:\Delphi\Civ2 UI Additions\src\Patches 146 | [HistoryLists\hlUnitOutputDirectory] 147 | Count=1 148 | Item0=dcu 149 | [HistoryLists\hlOutputDirectorry] 150 | Count=1 151 | Item0=bin 152 | -------------------------------------------------------------------------------- /src/Civ2UIA.dpr: -------------------------------------------------------------------------------- 1 | library Civ2UIA; 2 | 3 | {$R 'Civ2UIA_GIFS.res' 'Civ2UIA_GIFS.rc'} 4 | {%File 'Civ2ProcDeclF.inc'} 5 | {%File 'Civ2ProcImplF.inc'} 6 | 7 | uses 8 | Windows, 9 | Civ2Types in 'Civ2Types.pas', 10 | Civ2Proc in 'Civ2Proc.pas', 11 | Civ2UIA_CanvasEx in 'Civ2UIA_CanvasEx.pas', 12 | Civ2UIA_FormAbout in 'Civ2UIA_FormAbout.pas' {FormAbout}, 13 | Civ2UIA_FormConsole in 'Civ2UIA_FormConsole.pas' {FormConsole}, 14 | Civ2UIA_FormSettings in 'Civ2UIA_FormSettings.pas' {FormSettings}, 15 | Civ2UIA_FormStrings in 'Civ2UIA_FormStrings.pas' {FormStrings}, 16 | Civ2UIA_FormTest in 'Civ2UIA_FormTest.pas' {FormTest}, 17 | Civ2UIA_Hooks in 'Civ2UIA_Hooks.pas', 18 | Civ2UIA_MapMessage in 'Civ2UIA_MapMessage.pas', 19 | Civ2UIA_MapMessages in 'Civ2UIA_MapMessages.pas', 20 | Civ2UIA_MapOverlay in 'Civ2UIA_MapOverlay.pas', 21 | Civ2UIA_MapOverlayModule in 'Civ2UIA_MapOverlayModule.pas', 22 | Civ2UIA_Options in 'Civ2UIA_Options.pas', 23 | Civ2UIA_PathLine in 'Civ2UIA_PathLine.pas', 24 | Civ2UIA_Proc in 'Civ2UIA_Proc.pas', 25 | Civ2UIA_QuickInfo in 'Civ2UIA_QuickInfo.pas', 26 | Civ2UIA_SortedAbstractList in 'Civ2UIA_SortedAbstractList.pas', 27 | Civ2UIA_SortedCitiesList in 'Civ2UIA_SortedCitiesList.pas', 28 | Civ2UIA_SortedUnitsList in 'Civ2UIA_SortedUnitsList.pas', 29 | Civ2UIA_Types in 'Civ2UIA_Types.pas', 30 | UiaMain in 'UiaMain.pas', 31 | UiaPatch in 'Patches\UiaPatch.pas', 32 | UiaPatch64Bit in 'Patches\UiaPatch64Bit.pas', 33 | UiaPatchAI in 'Patches\UiaPatchAI.pas', 34 | UiaPatchArrangeWindows in 'Patches\UiaPatchArrangeWindows.pas', 35 | UiaPatchAttitudeAdvisor in 'Patches\UiaPatchAttitudeAdvisor.pas', 36 | UiaPatchCDAudio in 'Patches\UiaPatchCDAudio.pas', 37 | UiaPatchCDCheck in 'Patches\UiaPatchCDCheck.pas', 38 | UiaPatchCityStatusAdvisor in 'Patches\UiaPatchCityStatusAdvisor.pas', 39 | UiaPatchCityView in 'Patches\UiaPatchCityView.pas', 40 | UiaPatchCityWindow in 'Patches\UiaPatchCityWindow.pas', 41 | UiaPatchColorCorrection in 'Patches\UiaPatchColorCorrection.pas', 42 | UiaPatchCommon in 'Patches\UiaPatchCommon.pas', 43 | UiaPatchCPUUsage in 'Patches\UiaPatchCPUUsage.pas', 44 | UiaPatchDllGif in 'Patches\UiaPatchDllGif.pas', 45 | UiaPatchDrawUnit in 'Patches\UiaPatchDrawUnit.pas', 46 | UiaPatchLimits in 'Patches\UiaPatchLimits.pas', 47 | UiaPatchMapWindow in 'Patches\UiaPatchMapWindow.pas', 48 | UiaPatchMenu in 'Patches\UiaPatchMenu.pas', 49 | UiaPatchMultiplayer in 'Patches\UiaPatchMultiplayer.pas', 50 | UiaPatchResizableWindows in 'Patches\UiaPatchResizableWindows.pas', 51 | UiaPatchScienceAdvisor in 'Patches\UiaPatchScienceAdvisor.pas', 52 | UiaPatchSideBar in 'Patches\UiaPatchSideBar.pas', 53 | UiaPatchSuppressPopup in 'Patches\UiaPatchSuppressPopup.pas', 54 | UiaPatchTaxWindow in 'Patches\UiaPatchTaxWindow.pas', 55 | UiaPatchTests in 'Patches\UiaPatchTests.pas', 56 | UiaPatchTimers in 'Patches\UiaPatchTimers.pas', 57 | UiaPatchTradeAdvisor in 'Patches\UiaPatchTradeAdvisor.pas', 58 | UiaPatchUnits in 'Patches\UiaPatchUnits.pas', 59 | UiaPatchUnitsLimit in 'Patches\UiaPatchUnitsLimit.pas', 60 | UiaPatchUnitsListPopup in 'Patches\UiaPatchUnitsListPopup.pas', 61 | UiaSettings in 'UiaSettings.pas', 62 | Tests in 'Tests.pas', 63 | Civ2UIA_SnowFlakes in 'Civ2UIA_SnowFlakes.pas'; 64 | 65 | {$R *.res} 66 | 67 | procedure DllMain(Reason: Integer); 68 | var 69 | HProcess: Cardinal; 70 | begin 71 | case Reason of 72 | DLL_PROCESS_ATTACH: 73 | begin 74 | HProcess := OpenProcess(PROCESS_ALL_ACCESS, False, GetCurrentProcessId()); 75 | Uia.AttachPatches(HProcess); 76 | CloseHandle(HProcess); 77 | SendMessageToLoader(0, 0); 78 | end; 79 | DLL_PROCESS_DETACH: 80 | ; 81 | end; 82 | end; 83 | 84 | begin 85 | DllProc := @DllMain; 86 | DllProc(DLL_PROCESS_ATTACH); 87 | end. 88 | -------------------------------------------------------------------------------- /src/Civ2UIA.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2UIA.res -------------------------------------------------------------------------------- /src/Civ2UIAL_FormMain.dfm: -------------------------------------------------------------------------------- 1 | object Form1: TForm1 2 | Left = 485 3 | Top = 313 4 | BorderIcons = [biSystemMenu, biMinimize] 5 | BorderStyle = bsSingle 6 | Caption = 'Civilization II UI Additions Launcher' 7 | ClientHeight = 297 8 | ClientWidth = 513 9 | Color = clBtnFace 10 | Constraints.MaxWidth = 521 11 | Constraints.MinWidth = 521 12 | Font.Charset = DEFAULT_CHARSET 13 | Font.Color = clWindowText 14 | Font.Height = -11 15 | Font.Name = 'MS Sans Serif' 16 | Font.Style = [] 17 | KeyPreview = True 18 | OldCreateOrder = False 19 | Position = poScreenCenter 20 | ShowHint = True 21 | OnClose = FormClose 22 | OnCreate = FormCreate 23 | OnKeyDown = FormKeyDown 24 | DesignSize = ( 25 | 513 26 | 297) 27 | PixelsPerInch = 96 28 | TextHeight = 13 29 | object LabelExe: TLabel 30 | Tag = 1 31 | Left = 8 32 | Top = 8 33 | Width = 28 34 | Height = 13 35 | Caption = 'Game' 36 | end 37 | object LabelDll: TLabel 38 | Tag = 1 39 | Left = 8 40 | Top = 36 41 | Width = 12 42 | Height = 13 43 | Caption = 'Dll' 44 | end 45 | object LabelVersion: TLabel 46 | Left = 470 47 | Top = 280 48 | Width = 35 49 | Height = 13 50 | Alignment = taRightJustify 51 | Anchors = [akRight, akBottom] 52 | Caption = 'Version' 53 | Enabled = False 54 | end 55 | object LabelAuthor: TLabel 56 | Left = 8 57 | Top = 272 58 | Width = 75 59 | Height = 13 60 | Anchors = [akLeft, akBottom] 61 | Caption = '2022 FoxAhead' 62 | Enabled = False 63 | end 64 | object LabelGitHub: TLabel 65 | Left = 472 66 | Top = 264 67 | Width = 33 68 | Height = 13 69 | Cursor = crHandPoint 70 | Alignment = taRightJustify 71 | Anchors = [akRight, akBottom] 72 | Caption = 'GitHub' 73 | Font.Charset = DEFAULT_CHARSET 74 | Font.Color = clBlue 75 | Font.Height = -11 76 | Font.Name = 'MS Sans Serif' 77 | Font.Style = [fsUnderline] 78 | ParentFont = False 79 | OnClick = LabelGitHubClick 80 | end 81 | object LabelDebug: TLabel 82 | Left = 404 83 | Top = 280 84 | Width = 101 85 | Height = 13 86 | Anchors = [akRight, akBottom] 87 | AutoSize = False 88 | Transparent = True 89 | OnClick = LabelDebugClick 90 | end 91 | object Memo1: TMemo 92 | Left = 4 93 | Top = 60 94 | Width = 505 95 | Height = 201 96 | TabStop = False 97 | Anchors = [akLeft, akTop, akRight, akBottom] 98 | Font.Charset = DEFAULT_CHARSET 99 | Font.Color = clWindowText 100 | Font.Height = -11 101 | Font.Name = 'MS Sans Serif' 102 | Font.Style = [] 103 | Lines.Strings = ( 104 | 105 | 'This launcher adds some enhancements without modifying game exec' + 106 | 'utable.' 107 | 108 | 'Most features are intended to extend usability without affecting' + 109 | ' original game rules or limitations. Those ' 110 | 'include:' 111 | 112 | 'mouse wheel and middle button support; choosing any unit in larg' + 113 | 'e stacks; displaying work counter for ' 114 | 115 | 'Settlers/Engineers, displaying game turn, research progress; cor' + 116 | 'rect CD Music playing; Alt+Tab icon; ' 117 | '64-bit patch; no-CD patch.' 118 | 119 | 'Also some enhancements from civ2patch project included: fix for ' + 120 | 'CPU usage and patches that affect ' 121 | 'some game rules and limitations.' 122 | 'Features can be switched in '#39'Options...'#39' dialog.' 123 | '' 124 | 125 | 'This launcher will search for CIV2.EXE and Civ2UIA.dll in its cu' + 126 | 'rrent folder and try to set all paths ' 127 | 'automatically.' 128 | 129 | 'You can create shortcut to start game immediately. All selected ' + 130 | 'paths are saved in shortcut.' 131 | 132 | 'Game version Multiplayer Gold Edition 5.4.0f (Patch 3) is suppor' + 133 | 'ted only.') 134 | ParentFont = False 135 | ReadOnly = True 136 | ScrollBars = ssVertical 137 | TabOrder = 0 138 | end 139 | object ButtonBrowseExe: TButton 140 | Tag = 1 141 | Left = 444 142 | Top = 2 143 | Width = 65 144 | Height = 25 145 | Caption = 'Browse...' 146 | TabOrder = 2 147 | OnClick = ButtonBrowseExeClick 148 | end 149 | object EditExe: TEdit 150 | Tag = 1 151 | Left = 44 152 | Top = 4 153 | Width = 393 154 | Height = 21 155 | TabStop = False 156 | AutoSize = False 157 | Color = clBtnFace 158 | Font.Charset = RUSSIAN_CHARSET 159 | Font.Color = clWindowText 160 | Font.Height = -11 161 | Font.Name = 'MS Sans Serif' 162 | Font.Style = [] 163 | ParentFont = False 164 | ReadOnly = True 165 | TabOrder = 6 166 | end 167 | object ButtonStart: TButton 168 | Left = 300 169 | Top = 268 170 | Width = 101 171 | Height = 25 172 | Hint = 'Close this screen and start game' 173 | Anchors = [akBottom] 174 | Caption = 'Play' 175 | Default = True 176 | Font.Charset = DEFAULT_CHARSET 177 | Font.Color = clWindowText 178 | Font.Height = -11 179 | Font.Name = 'MS Sans Serif' 180 | Font.Style = [fsBold] 181 | ParentFont = False 182 | TabOrder = 1 183 | OnClick = ButtonStartClick 184 | end 185 | object EditDll: TEdit 186 | Tag = 1 187 | Left = 44 188 | Top = 32 189 | Width = 393 190 | Height = 21 191 | TabStop = False 192 | AutoSize = False 193 | Color = clBtnFace 194 | Font.Charset = RUSSIAN_CHARSET 195 | Font.Color = clWindowText 196 | Font.Height = -11 197 | Font.Name = 'MS Sans Serif' 198 | Font.Style = [] 199 | ParentFont = False 200 | ReadOnly = True 201 | TabOrder = 7 202 | end 203 | object ButtonBrowseDll: TButton 204 | Tag = 1 205 | Left = 444 206 | Top = 30 207 | Width = 65 208 | Height = 25 209 | Caption = 'Browse...' 210 | TabOrder = 3 211 | OnClick = ButtonBrowseDllClick 212 | end 213 | object ButtonShortcut: TButton 214 | Left = 192 215 | Top = 268 216 | Width = 101 217 | Height = 25 218 | Anchors = [akBottom] 219 | Caption = 'Create shortcut...' 220 | TabOrder = 5 221 | OnClick = ButtonShortcutClick 222 | end 223 | object ButtonOptions: TButton 224 | Left = 120 225 | Top = 268 226 | Width = 65 227 | Height = 25 228 | Anchors = [akBottom] 229 | Caption = 'Options...' 230 | TabOrder = 4 231 | OnClick = ButtonOptionsClick 232 | end 233 | object OpenDialogExe: TOpenDialog 234 | Filter = '*.exe|*.exe' 235 | Options = [ofHideReadOnly, ofFileMustExist, ofEnableSizing] 236 | Left = 404 237 | Top = 4 238 | end 239 | object OpenDialogDll: TOpenDialog 240 | Filter = '*.dll|*.dll' 241 | Options = [ofHideReadOnly, ofFileMustExist, ofEnableSizing] 242 | Left = 404 243 | Top = 32 244 | end 245 | object SaveDialogLnk: TSaveDialog 246 | FileName = 'Civ2 with UI Additions.lnk' 247 | Filter = '*.lnk|*.lnk' 248 | Options = [ofOverwritePrompt, ofHideReadOnly, ofPathMustExist, ofEnableSizing] 249 | Left = 184 250 | Top = 192 251 | end 252 | object Timer1: TTimer 253 | Interval = 500 254 | OnTimer = Timer1Timer 255 | Left = 404 256 | Top = 64 257 | end 258 | end 259 | -------------------------------------------------------------------------------- /src/Civ2UIAL_FormMain.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIAL_FormMain; 2 | 3 | interface 4 | 5 | uses 6 | Classes, 7 | Controls, 8 | Dialogs, 9 | Forms, 10 | Messages, 11 | StdCtrls, 12 | ShellAPI, 13 | SysUtils, 14 | Windows, 15 | ExtCtrls; 16 | 17 | type 18 | TForm1 = class(TForm) 19 | Memo1: TMemo; 20 | OpenDialogExe: TOpenDialog; 21 | ButtonBrowseExe: TButton; 22 | EditExe: TEdit; 23 | ButtonStart: TButton; 24 | EditDll: TEdit; 25 | ButtonBrowseDll: TButton; 26 | OpenDialogDll: TOpenDialog; 27 | LabelExe: TLabel; 28 | LabelDll: TLabel; 29 | ButtonShortcut: TButton; 30 | LabelVersion: TLabel; 31 | SaveDialogLnk: TSaveDialog; 32 | LabelAuthor: TLabel; 33 | LabelGitHub: TLabel; 34 | LabelDebug: TLabel; 35 | Timer1: TTimer; 36 | ButtonOptions: TButton; 37 | procedure ButtonBrowseExeClick(Sender: TObject); 38 | procedure ButtonBrowseDllClick(Sender: TObject); 39 | procedure FormCreate(Sender: TObject); 40 | procedure ButtonStartClick(Sender: TObject); 41 | procedure ButtonShortcutClick(Sender: TObject); 42 | procedure LabelGitHubClick(Sender: TObject); 43 | procedure LabelDebugClick(Sender: TObject); 44 | procedure Timer1Timer(Sender: TObject); 45 | procedure FormClose(Sender: TObject; var Action: TCloseAction); 46 | procedure ButtonOptionsClick(Sender: TObject); 47 | procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 48 | private 49 | MessagesCounter: Integer; 50 | DebugLayout: Boolean; 51 | procedure OnMessage(var MSG: TMessage); message WM_APP + 1; 52 | procedure AdjustFormToDebug(); 53 | procedure AdjustFormLayout(); 54 | public 55 | { Public declarations } 56 | end; 57 | 58 | var 59 | Form1: TForm1; 60 | 61 | //-------------------------------------------------------------------------------------------------- 62 | implementation 63 | //-------------------------------------------------------------------------------------------------- 64 | 65 | uses 66 | ActiveX, 67 | Civ2UIAL_Proc; 68 | 69 | {$R *.dfm} 70 | 71 | //-------------------------------------------------------------------------------------------------- 72 | // TForm1 73 | //-------------------------------------------------------------------------------------------------- 74 | 75 | procedure TForm1.ButtonBrowseExeClick(Sender: TObject); 76 | begin 77 | if OpenDialogExe.Execute then 78 | begin 79 | EditExe.Text := OpenDialogExe.FileName; 80 | ExeName := OpenDialogExe.FileName; 81 | end; 82 | end; 83 | 84 | procedure TForm1.ButtonBrowseDllClick(Sender: TObject); 85 | begin 86 | if OpenDialogDll.Execute then 87 | begin 88 | EditDll.Text := OpenDialogDll.FileName; 89 | DllName := OpenDialogDll.FileName; 90 | LabelVersion.Caption := 'Version ' + CurrentFileInfo(DllName); 91 | end; 92 | end; 93 | 94 | procedure TForm1.OnMessage(var MSG: TMessage); 95 | begin 96 | Inc(MessagesCounter); 97 | Log(IntToStr(MessagesCounter) + '. Recieved message: wParam = ' + IntToHex(MSG.WParam, 4) + ', lParam = ' + IntToHex(MSG.LParam, 4)); 98 | if (MSG.WParam = 0) and (MSG.LParam = 0) then 99 | DllLoaded := True; 100 | end; 101 | 102 | procedure TForm1.FormCreate(Sender: TObject); 103 | var 104 | Terminating: Boolean; 105 | begin 106 | Terminating := False; 107 | EditExe.Text := ExeName; 108 | EditDll.Text := DllName; 109 | LogMemo := Memo1; 110 | 111 | if IsSilentLaunch() then 112 | begin 113 | Visible := False; 114 | Terminating := CheckLaunchClose(); 115 | end; 116 | if not Terminating then 117 | begin 118 | Visible := True; 119 | Application.ShowMainForm := True; 120 | //LabelVersion.Caption := 'Version ' + CurrentFileInfo(Application.ExeName); 121 | LabelVersion.Caption := 'Version ' + CurrentFileInfo(DllName); 122 | AdjustFormToDebug(); 123 | end; 124 | end; 125 | 126 | procedure TForm1.ButtonStartClick(Sender: TObject); 127 | var 128 | Civ2ProcessId: THandle; 129 | Civ2Process: THandle; 130 | begin 131 | if Civ2IsRunning() then 132 | begin 133 | Civ2ProcessId := GetProcessHandle('civ2.exe'); 134 | if Civ2ProcessId > 0 then 135 | begin 136 | Civ2Process := OpenProcess(PROCESS_TERMINATE, False, Civ2ProcessId); 137 | TerminateProcess(Civ2Process, 1); 138 | end; 139 | end 140 | else 141 | begin 142 | Self.Enabled := False; 143 | if not CheckLaunchClose() then 144 | begin 145 | Self.Enabled := True; 146 | AdjustFormLayout(); 147 | end; 148 | end; 149 | end; 150 | 151 | procedure TForm1.ButtonShortcutClick(Sender: TObject); 152 | var 153 | Arguments: string; 154 | begin 155 | if SaveDialogLnk.Execute() then 156 | begin 157 | Arguments := '-play -exe "' + ExeName + '" -dll "' + DllName + '"'; 158 | if DebugEnabled then 159 | Arguments := '-debug ' + Arguments; 160 | CreateLnk(SaveDialogLnk.FileName, Application.ExeName, ExtractFilePath(ExeName), 'Play Civilzation II with UI Additions', Arguments); 161 | end; 162 | end; 163 | 164 | procedure TForm1.LabelGitHubClick(Sender: TObject); 165 | begin 166 | ShellExecute(Application.Handle, 'open', 'https://github.com/FoxAhead/Civ2-UI-Additions', nil, nil, SW_SHOW); 167 | end; 168 | 169 | procedure TForm1.LabelDebugClick(Sender: TObject); 170 | begin 171 | DebugEnabled := not DebugEnabled; 172 | AdjustFormToDebug(); 173 | Log('DebugEnabled: ' + BoolToStr(DebugEnabled, True)); 174 | end; 175 | 176 | procedure TForm1.AdjustFormToDebug; 177 | begin 178 | LabelVersion.Enabled := DebugEnabled; 179 | if DebugEnabled then 180 | FormStyle := fsStayOnTop 181 | else 182 | FormStyle := fsNormal; 183 | end; 184 | 185 | procedure TForm1.Timer1Timer(Sender: TObject); 186 | begin 187 | AdjustFormLayout(); 188 | end; 189 | 190 | procedure TForm1.AdjustFormLayout; 191 | var 192 | Civ2Running: Boolean; 193 | I: Integer; 194 | NewDebugLayout: Bool; 195 | begin 196 | Civ2Running := Civ2IsRunning(); 197 | if Civ2Running then 198 | ButtonStart.Caption := 'Stop' 199 | else 200 | ButtonStart.Caption := 'Play'; 201 | 202 | NewDebugLayout := DebugEnabled and Civ2Running; 203 | if NewDebugLayout <> DebugLayout then 204 | begin 205 | DebugLayout := NewDebugLayout; 206 | for I := 0 to ComponentCount - 1 do 207 | if Components[I].Tag = 1 then 208 | begin 209 | LabelExe.Visible := not DebugLayout; 210 | LabelDll.Visible := not DebugLayout; 211 | EditExe.Visible := not DebugLayout; 212 | EditDll.Visible := not DebugLayout; 213 | ButtonBrowseExe.Visible := not DebugLayout; 214 | ButtonBrowseDll.Visible := not DebugLayout; 215 | end; 216 | if DebugLayout then 217 | begin 218 | ClientHeight := 201; 219 | Memo1.Top := 4; 220 | Memo1.Height := 161; 221 | Left := 0; 222 | Top := Monitor.Height - Height; 223 | end 224 | else 225 | begin 226 | ClientHeight := 297; 227 | Memo1.Top := 60; 228 | Memo1.Height := 201; 229 | if Left < 0 then 230 | Left := 0; 231 | if Top < 0 then 232 | Top := 0; 233 | if (Left + Width) > Monitor.Width then 234 | Left := Monitor.Width - Width; 235 | if (Top + Height) > Monitor.Height then 236 | Top := Monitor.Height - Height; 237 | end; 238 | end; 239 | end; 240 | 241 | procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); 242 | begin 243 | CoUninitialize(); 244 | end; 245 | 246 | procedure TForm1.ButtonOptionsClick(Sender: TObject); 247 | begin 248 | ShowOptionsDialog(); 249 | end; 250 | 251 | procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 252 | begin 253 | if Key = VK_ESCAPE then 254 | Close; 255 | end; 256 | 257 | end. 258 | 259 | -------------------------------------------------------------------------------- /src/Civ2UIAL_FormOptions.dfm: -------------------------------------------------------------------------------- 1 | object FormOptions: TFormOptions 2 | Left = 605 3 | Top = 250 4 | BorderIcons = [biSystemMenu, biMinimize] 5 | BorderStyle = bsSingle 6 | Caption = 'Options' 7 | ClientHeight = 257 8 | ClientWidth = 513 9 | Color = clBtnFace 10 | Font.Charset = DEFAULT_CHARSET 11 | Font.Color = clWindowText 12 | Font.Height = -11 13 | Font.Name = 'MS Sans Serif' 14 | Font.Style = [] 15 | OldCreateOrder = False 16 | Position = poMainFormCenter 17 | OnCloseQuery = FormCloseQuery 18 | OnCreate = FormCreate 19 | DesignSize = ( 20 | 513 21 | 257) 22 | PixelsPerInch = 96 23 | TextHeight = 13 24 | object CheckListBox1: TCheckListBox 25 | Left = 4 26 | Top = 4 27 | Width = 133 28 | Height = 217 29 | OnClickCheck = CheckListBox1ClickCheck 30 | Anchors = [akLeft, akTop, akBottom] 31 | ItemHeight = 13 32 | TabOrder = 0 33 | OnClick = CheckListBox1Click 34 | end 35 | object ButtonOK: TButton 36 | Left = 152 37 | Top = 228 38 | Width = 101 39 | Height = 25 40 | Anchors = [akLeft, akBottom] 41 | Caption = 'OK' 42 | Default = True 43 | Font.Charset = DEFAULT_CHARSET 44 | Font.Color = clWindowText 45 | Font.Height = -11 46 | Font.Name = 'MS Sans Serif' 47 | Font.Style = [fsBold] 48 | ModalResult = 1 49 | ParentFont = False 50 | TabOrder = 1 51 | OnClick = ButtonOKClick 52 | end 53 | object ButtonCancel: TButton 54 | Left = 260 55 | Top = 228 56 | Width = 101 57 | Height = 25 58 | Anchors = [akLeft, akBottom] 59 | Cancel = True 60 | Caption = 'Cancel' 61 | ModalResult = 2 62 | TabOrder = 2 63 | end 64 | object Memo1: TMemo 65 | Left = 144 66 | Top = 4 67 | Width = 365 68 | Height = 217 69 | TabStop = False 70 | Anchors = [akLeft, akTop, akBottom] 71 | ReadOnly = True 72 | ScrollBars = ssVertical 73 | TabOrder = 3 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /src/Civ2UIAL_FormOptions.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIAL_FormOptions; 2 | 3 | interface 4 | 5 | uses 6 | CheckLst, 7 | Classes, 8 | Controls, 9 | Forms, 10 | StdCtrls, 11 | SysUtils, 12 | Civ2UIAL_Proc; 13 | 14 | type 15 | TFormOptions = class(TForm) 16 | CheckListBox1: TCheckListBox; 17 | ButtonOK: TButton; 18 | ButtonCancel: TButton; 19 | Memo1: TMemo; 20 | procedure FormCreate(Sender: TObject); 21 | procedure CheckListBox1Click(Sender: TObject); 22 | procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); 23 | procedure ButtonOKClick(Sender: TObject); 24 | procedure CheckListBox1ClickCheck(Sender: TObject); 25 | private 26 | { Private declarations } 27 | Labels: array of TLabel; 28 | Edits: array of TEdit; 29 | EditsOwnerIndex: Integer; 30 | OKPressed: Boolean; 31 | procedure FreeEdits(); 32 | public 33 | { Public declarations } 34 | procedure CopyOptionsToBackend(); 35 | procedure CopyBackendToOptions(); 36 | procedure CopyFrontendToBackend(); 37 | procedure CopyBackendToFrontend(); 38 | function ValidateInput(Index: Integer): Boolean; 39 | end; 40 | 41 | implementation 42 | 43 | uses 44 | Graphics, 45 | Variants; 46 | 47 | {$R *.dfm} 48 | 49 | procedure TFormOptions.FormCreate(Sender: TObject); 50 | var 51 | i: Integer; 52 | begin 53 | 54 | CheckListBox1.Clear(); 55 | 56 | for i := 0 to High(OptionItems) do 57 | begin 58 | CheckListBox1.Items.Append(OptionItems[i].Name); 59 | if OptionItems[i].Key = '' then 60 | CheckListBox1.Header[i] := True; 61 | end; 62 | 63 | CopyOptionsToBackend(); 64 | CopyBackendToFrontend(); 65 | end; 66 | 67 | procedure TFormOptions.CheckListBox1Click(Sender: TObject); 68 | var 69 | N: Integer; 70 | M: Integer; 71 | i: Integer; 72 | EditsTop: Integer; 73 | begin 74 | if not ValidateInput(EditsOwnerIndex) then 75 | begin 76 | CheckListBox1.ItemIndex := EditsOwnerIndex; 77 | Exit; 78 | end; 79 | 80 | N := CheckListBox1.ItemIndex; 81 | if N >= 0 then 82 | begin 83 | Memo1.Text := AdjustLineBreaks(OptionItems[N].Description); 84 | FreeEdits(); 85 | 86 | M := Length(OptionItems[N].OptionSubItems); 87 | SetLength(Labels, M); 88 | SetLength(Edits, M); 89 | EditsTop := CheckListBox1.Top + CheckListBox1.Height - M * 27 + 6; 90 | Memo1.Height := CheckListBox1.Height - M * 27; 91 | for i := 0 to M - 1 do 92 | begin 93 | Edits[i] := TEdit.Create(Self); 94 | Edits[i].Parent := Self; 95 | Edits[i].Visible := True; 96 | Edits[i].Left := 144; 97 | Edits[i].Top := EditsTop + i * 27; 98 | Edits[i].Height := 21; 99 | Edits[i].Width := 85; 100 | Edits[i].Text := OptionItems[N].OptionSubItems[i].Value; 101 | if OptionItems[N].ParentIndex <> -1 then 102 | Edits[i].Enabled := OptionItems[OptionItems[N].ParentIndex].Checked; 103 | Labels[i] := TLabel.Create(Self); 104 | Labels[i].Parent := Self; 105 | Labels[i].Visible := True; 106 | Labels[i].Left := Edits[i].Left + Edits[i].Width + 4; 107 | Labels[i].Top := Edits[i].Top + 4; 108 | Labels[i].Caption := OptionItems[N].OptionSubItems[i].Name; 109 | end; 110 | EditsOwnerIndex := N; 111 | end; 112 | 113 | end; 114 | 115 | procedure TFormOptions.FreeEdits; 116 | var 117 | i: Integer; 118 | begin 119 | for i := Low(Edits) to High(Edits) do 120 | if Edits[i] <> nil then 121 | FreeAndNil(Edits[i]); 122 | SetLength(Edits, 0); 123 | for i := Low(Labels) to High(Labels) do 124 | if Labels[i] <> nil then 125 | FreeAndNil(Labels[i]); 126 | SetLength(Labels, 0); 127 | end; 128 | 129 | function TFormOptions.ValidateInput(Index: Integer): Boolean; 130 | var 131 | M: Integer; 132 | i: Integer; 133 | begin 134 | Result := True; 135 | if Length(Edits) > 0 then 136 | begin 137 | M := Length(OptionItems[Index].OptionSubItems); 138 | for i := 0 to M - 1 do 139 | begin 140 | try 141 | OptionItems[Index].OptionSubItems[i].Value := VarAsType(Edits[i].Text, VarType(OptionItems[Index].OptionSubItems[i].Value)); 142 | Edits[i].Font.Color := clWindowText; 143 | except 144 | Edits[i].Font.Color := clRed; 145 | Result := False; 146 | end; 147 | end; 148 | end; 149 | end; 150 | 151 | procedure TFormOptions.FormCloseQuery(Sender: TObject; var CanClose: Boolean); 152 | begin 153 | if OKPressed then 154 | if not ValidateInput(EditsOwnerIndex) then 155 | begin 156 | OKPressed := False; 157 | CanClose := False; 158 | end 159 | else 160 | CopyBackendToOptions(); 161 | end; 162 | 163 | procedure TFormOptions.ButtonOKClick(Sender: TObject); 164 | begin 165 | OKPressed := True; 166 | end; 167 | 168 | procedure TFormOptions.CheckListBox1ClickCheck(Sender: TObject); 169 | begin 170 | CopyFrontendToBackend(); 171 | CopyBackendToFrontend(); 172 | end; 173 | 174 | procedure TFormOptions.CopyOptionsToBackend; 175 | var 176 | i, j: Integer; 177 | begin 178 | for i := 0 to High(OptionItems) do 179 | begin 180 | OptionItems[i].Checked := GetOptionByKey(OptionItems[i].Key); 181 | for j := 0 to High(OptionItems[i].OptionSubItems) do 182 | OptionItems[i].OptionSubItems[j].Value := GetOptionByKey(OptionItems[i].OptionSubItems[j].Key); 183 | end; 184 | end; 185 | 186 | procedure TFormOptions.CopyBackendToOptions; 187 | var 188 | i, j: Integer; 189 | begin 190 | for i := 0 to High(OptionItems) do 191 | begin 192 | SetOptionByKey(OptionItems[i].Key, OptionItems[i].Checked); 193 | for j := 0 to High(OptionItems[i].OptionSubItems) do 194 | SetOptionByKey(OptionItems[i].OptionSubItems[j].Key, OptionItems[i].OptionSubItems[j].Value); 195 | end; 196 | end; 197 | 198 | procedure TFormOptions.CopyFrontendToBackend; 199 | var 200 | i: Integer; 201 | begin 202 | for i := 0 to CheckListBox1.Count - 1 do 203 | begin 204 | if CheckListBox1.Header[i] = False then 205 | begin 206 | OptionItems[i].Checked := CheckListBox1.Checked[i]; 207 | end; 208 | end; 209 | end; 210 | 211 | procedure TFormOptions.CopyBackendToFrontend; 212 | var 213 | i, j: Integer; 214 | begin 215 | for i := 0 to High(OptionItems) do 216 | begin 217 | if CheckListBox1.Header[i] = False then 218 | CheckListBox1.Checked[i] := OptionItems[i].Checked; 219 | if OptionItems[i].ParentIndex <> -1 then 220 | begin 221 | CheckListBox1.ItemEnabled[i] := OptionItems[OptionItems[i].ParentIndex].Checked; 222 | end; 223 | end; 224 | CheckListBox1.Repaint(); 225 | end; 226 | 227 | end. 228 | -------------------------------------------------------------------------------- /src/Civ2UIAL_FormOptions.rc: -------------------------------------------------------------------------------- 1 | FormOptions RCDATA Civ2UIAL_FormOptions.xml -------------------------------------------------------------------------------- /src/Civ2UIAL_FormOptions.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2UIAL_FormOptions.res -------------------------------------------------------------------------------- /src/Civ2UIAL_FormOptions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
5 | 17 |
18 |
20 | 21 | 23 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 |
43 |
46 | 47 | 48 | 49 |
50 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
71 |
-------------------------------------------------------------------------------- /src/Civ2UIALauncher.dof: -------------------------------------------------------------------------------- 1 | [FileVersion] 2 | Version=7.0 3 | [Compiler] 4 | A=8 5 | B=0 6 | C=1 7 | D=0 8 | E=0 9 | F=0 10 | G=1 11 | H=1 12 | I=1 13 | J=0 14 | K=0 15 | L=0 16 | M=0 17 | N=1 18 | O=1 19 | P=1 20 | Q=0 21 | R=0 22 | S=0 23 | T=0 24 | U=0 25 | V=1 26 | W=0 27 | X=1 28 | Y=0 29 | Z=1 30 | ShowHints=0 31 | ShowWarnings=1 32 | UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; 33 | NamespacePrefix= 34 | SymbolDeprecated=1 35 | SymbolLibrary=1 36 | SymbolPlatform=1 37 | UnitLibrary=1 38 | UnitPlatform=1 39 | UnitDeprecated=1 40 | HResultCompat=1 41 | HidingMember=1 42 | HiddenVirtual=1 43 | Garbage=1 44 | BoundsError=1 45 | ZeroNilCompat=1 46 | StringConstTruncated=1 47 | ForLoopVarVarPar=1 48 | TypedConstVarPar=1 49 | AsgToTypedConst=1 50 | CaseLabelRange=1 51 | ForVariable=1 52 | ConstructingAbstract=1 53 | ComparisonFalse=1 54 | ComparisonTrue=1 55 | ComparingSignedUnsigned=1 56 | CombiningSignedUnsigned=1 57 | UnsupportedConstruct=1 58 | FileOpen=1 59 | FileOpenUnitSrc=1 60 | BadGlobalSymbol=1 61 | DuplicateConstructorDestructor=1 62 | InvalidDirective=1 63 | PackageNoLink=1 64 | PackageThreadVar=1 65 | ImplicitImport=1 66 | HPPEMITIgnored=1 67 | NoRetVal=1 68 | UseBeforeDef=1 69 | ForLoopVarUndef=1 70 | UnitNameMismatch=1 71 | NoCFGFileFound=1 72 | MessageDirective=1 73 | ImplicitVariants=1 74 | UnicodeToLocale=1 75 | LocaleToUnicode=1 76 | ImagebaseMultiple=1 77 | SuspiciousTypecast=1 78 | PrivatePropAccessor=1 79 | UnsafeType=0 80 | UnsafeCode=0 81 | UnsafeCast=0 82 | [Linker] 83 | MapFile=0 84 | OutputObjs=0 85 | ConsoleApp=1 86 | DebugInfo=0 87 | RemoteSymbols=0 88 | MinStackSize=16384 89 | MaxStackSize=1048576 90 | ImageBase=4194304 91 | ExeDescription= 92 | [Directories] 93 | OutputDir= 94 | UnitOutputDir= 95 | PackageDLLOutputDir= 96 | PackageDCPOutputDir= 97 | SearchPath= 98 | Packages=vclx;vcl;rtl;VclSmp;dsnapcon;dsnap;dbrtl;vcldb;bdertl;vcldbx;ibxpress;teeui;teedb;tee;dss;visualclx;visualdbclx;vclactnband;adortl 99 | Conditionals= 100 | DebugSourceDirs= 101 | UsePackages=0 102 | [Parameters] 103 | RunParams= 104 | HostApplication= 105 | Launcher= 106 | UseLauncher=0 107 | DebugCWD= 108 | [Language] 109 | ActiveLang= 110 | ProjectLang= 111 | RootDir= 112 | [Version Info] 113 | IncludeVerInfo=0 114 | AutoIncBuild=1 115 | MajorVer=1 116 | MinorVer=0 117 | Release=0 118 | Build=0 119 | Debug=0 120 | PreRelease=0 121 | Special=0 122 | Private=0 123 | DLL=0 124 | Locale=1033 125 | CodePage=1252 126 | [Version Info Keys] 127 | CompanyName= 128 | FileDescription= 129 | FileVersion=1.0.0.0 130 | InternalName= 131 | LegalCopyright= 132 | LegalTrademarks= 133 | OriginalFilename= 134 | ProductName= 135 | ProductVersion=1.0.0.0 136 | Comments= 137 | -------------------------------------------------------------------------------- /src/Civ2UIALauncher.dpr: -------------------------------------------------------------------------------- 1 | program Civ2UIALauncher; 2 | 3 | {$R 'Civ2UIAL_FormOptions.res' 'Civ2UIAL_FormOptions.rc'} 4 | 5 | uses 6 | Forms, 7 | Civ2UIA_Options in 'Civ2UIA_Options.pas', 8 | Civ2UIAL_Proc in 'Civ2UIAL_Proc.pas', 9 | Civ2UIAL_FormMain in 'Civ2UIAL_FormMain.pas' {Form1}, 10 | Civ2UIAL_FormOptions in 'Civ2UIAL_FormOptions.pas' {FormOptions}; 11 | 12 | {$R *.res} 13 | 14 | begin 15 | if AlreadyRunning() then Exit; 16 | InitializeVars(); 17 | Application.Title := 'Civilization II UI Additions Launcher'; 18 | Application.ShowMainForm := not IsSilentLaunch(); 19 | Application.CreateForm(TForm1, Form1); 20 | Application.Run; 21 | end. 22 | 23 | -------------------------------------------------------------------------------- /src/Civ2UIALauncher.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2UIALauncher.ico -------------------------------------------------------------------------------- /src/Civ2UIALauncher.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2UIALauncher.res -------------------------------------------------------------------------------- /src/Civ2UIA_CanvasEx.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_CanvasEx; 2 | 3 | interface 4 | 5 | uses 6 | Graphics, 7 | Windows, 8 | Civ2Types; 9 | 10 | const 11 | SHADOW_NONE = $00; 12 | SHADOW_TL = $01; 13 | SHADOW_T_ = $02; 14 | SHADOW_TR = $04; 15 | SHADOW__L = $08; 16 | SHADOW__R = $10; 17 | SHADOW_BL = $20; 18 | SHADOW_B_ = $40; 19 | SHADOW_BR = $80; 20 | SHADOW_ALL = $FF; 21 | 22 | type 23 | TCanvasEx = class(TCanvas) 24 | private 25 | FSavedDC: HDC; 26 | FDrawPort: PDrawPort; 27 | FSavedPen: TPoint; 28 | FMaxPen: TPoint; 29 | procedure SetMaxPen(const Value: TPoint); 30 | 31 | protected 32 | public 33 | FontShadows: Cardinal; 34 | FontShadowColor: TColor; 35 | PenOrigin: TPoint; 36 | LineHeight: Integer; 37 | constructor Create(DC: HDC); reintroduce; overload; 38 | constructor Create(DrawPort: PDrawPort); reintroduce; overload; 39 | destructor Destroy; override; 40 | function ColorFromIndex(Index: Integer): TColor; // Index = Palette index + 10 41 | function SetTextColors(MainColorIndex, ShadowColorIndex: Integer): TCanvasEx; 42 | function SetSpriteZoom(Zoom: Integer): TCanvasEx; 43 | function CopySprite(Sprite: PSprite; DX: Integer = 0; DY: Integer = 0): TCanvasEx; 44 | function TextOutWithShadows(const Text: string; DX: Integer = 0; DY: Integer = 0; Align: Cardinal = DT_LEFT; Rect: PRect = nil): TCanvasEx; overload; 45 | procedure TextOutRect(X, Y: Integer; const Text: string; Rect: PRect); 46 | function PenReset(): TCanvasEx; 47 | function PenX(X: Integer): TCanvasEx; 48 | function PenY(Y: Integer): TCanvasEx; 49 | function PenDX(DX: Integer): TCanvasEx; 50 | function PenDY(DY: Integer): TCanvasEx; 51 | function PenDXDY(DX, DY: Integer): TCanvasEx; 52 | function PenBR(): TCanvasEx; 53 | function PenSave(): TCanvasEx; 54 | function PenRestore(): TCanvasEx; 55 | procedure CopyFont(SourceFontDataHandle: HGLOBAL); 56 | property MaxPen: TPoint read FMaxPen write SetMaxPen; 57 | published 58 | end; 59 | 60 | implementation 61 | 62 | uses 63 | Civ2Proc, 64 | Civ2UIA_Proc; 65 | 66 | { TCanvasEx } 67 | 68 | constructor TCanvasEx.Create(DC: HDC); 69 | begin 70 | inherited Create; 71 | FSavedDC := SaveDC(DC); 72 | Self.Handle := DC; 73 | end; 74 | 75 | constructor TCanvasEx.Create(DrawPort: PDrawPort); 76 | begin 77 | Create(DrawPort.DrawInfo.DeviceContext); 78 | FDrawPort := DrawPort; 79 | end; 80 | 81 | destructor TCanvasEx.Destroy; 82 | var 83 | DC: HDC; 84 | begin 85 | DC := Self.Handle; 86 | Self.Handle := 0; 87 | RestoreDC(DC, FSavedDC); 88 | inherited; 89 | end; 90 | 91 | function TCanvasEx.ColorFromIndex(Index: Integer): TColor; 92 | var 93 | RGBQuad: Cardinal; 94 | begin 95 | if GetDIBColorTable(Self.Handle, Index, 1, RGBQuad) > 0 then 96 | Result := TColor(FastSwap(RGBQuad) shr 8) 97 | else 98 | Result := clRed; 99 | end; 100 | 101 | function TCanvasEx.SetTextColors(MainColorIndex, ShadowColorIndex: Integer): TCanvasEx; 102 | begin 103 | Font.Color := ColorFromIndex(MainColorIndex); 104 | FontShadowColor := ColorFromIndex(ShadowColorIndex); 105 | Result := Self; 106 | end; 107 | 108 | function TCanvasEx.SetSpriteZoom(Zoom: Integer): TCanvasEx; 109 | var 110 | Numerator, Denominator: Integer; 111 | A1, A2: Integer; 112 | begin 113 | Civ2.GetSpriteRatios(Numerator, Denominator); 114 | A1 := Numerator shl 16 div Denominator; 115 | A2 := (Zoom + 8) shl 13; 116 | if A1 <> A2 then 117 | begin 118 | Civ2.SetSpriteZoom(Zoom); 119 | end; 120 | Result := Self; 121 | end; 122 | 123 | function TCanvasEx.CopySprite(Sprite: PSprite; DX, DY: Integer): TCanvasEx; 124 | var 125 | R: TRect; 126 | begin 127 | if FDrawPort <> nil then 128 | begin 129 | Civ2.Sprite_CopyToPortNC(Sprite, @R, FDrawPort, PenPos.X + DX, PenPos.Y + DY); 130 | PenDX(RectWidth(R) + DX); 131 | end; 132 | Result := Self; 133 | end; 134 | 135 | function TCanvasEx.TextOutWithShadows(const Text: string; DX, DY: Integer; Align: Cardinal; Rect: PRect): TCanvasEx; 136 | var 137 | FontMainColor: TColor; 138 | P: TPoint; 139 | SX, SY: Integer; 140 | FontShadows1: Cardinal; 141 | OffsetX, OffsetY: Integer; 142 | begin 143 | OffsetX := 0; 144 | OffsetY := 0; 145 | if (Align and DT_CENTER) <> 0 then 146 | OffsetX := -TextWidth(Text) div 2 147 | else if (Align and DT_RIGHT) <> 0 then 148 | OffsetX := -TextWidth(Text); 149 | if (Align and DT_VCENTER) <> 0 then 150 | OffsetY := -TextHeight(Text) div 2 151 | else if (Align and DT_BOTTOM) <> 0 then 152 | OffsetY := -TextHeight(Text); 153 | FontMainColor := Font.Color; 154 | P := PenPos; 155 | if FontShadows <> SHADOW_NONE then 156 | begin 157 | Font.Color := FontShadowColor; 158 | FontShadows1 := FontShadows; 159 | for SY := -1 to 1 do 160 | begin 161 | for SX := -1 to 1 do 162 | begin 163 | if (SX = 0) and (SY = 0) then 164 | Continue; 165 | if (FontShadows1 and 1) = 1 then 166 | TextOutRect(P.X + SX + DX + OffsetX, P.Y + SY + DY + OffsetY, Text, Rect); 167 | FontShadows1 := FontShadows1 shr 1; 168 | if FontShadows1 = 0 then 169 | Break; 170 | end; 171 | end; 172 | end; 173 | Font.Color := FontMainColor; 174 | TextOutRect(P.X + DX + OffsetX, P.Y + DY + OffsetY, Text, Rect); 175 | MaxPen := PenPos; 176 | Result := Self; 177 | end; 178 | 179 | procedure TCanvasEx.TextOutRect(X, Y: Integer; const Text: string; Rect: PRect); 180 | begin 181 | if Rect <> nil then 182 | begin 183 | TextRect(Rect^, X, Y, Text); 184 | MoveTo(X + TextWidth(Text), Y); 185 | end 186 | else 187 | TextOut(X, Y, Text); 188 | end; 189 | 190 | function TCanvasEx.PenReset: TCanvasEx; 191 | begin 192 | MoveTo(PenOrigin.X, PenOrigin.Y); 193 | MaxPen := PenPos; 194 | Result := Self; 195 | end; 196 | 197 | function TCanvasEx.PenX(X: Integer): TCanvasEx; 198 | begin 199 | MoveTo(X, PenPos.Y); 200 | MaxPen := PenPos; 201 | Result := Self; 202 | end; 203 | 204 | function TCanvasEx.PenY(Y: Integer): TCanvasEx; 205 | begin 206 | MoveTo(PenPos.X, Y); 207 | MaxPen := PenPos; 208 | Result := Self; 209 | end; 210 | 211 | function TCanvasEx.PenDX(DX: Integer): TCanvasEx; 212 | begin 213 | MoveTo(PenPos.X + DX, PenPos.Y); 214 | MaxPen := PenPos; 215 | Result := Self; 216 | end; 217 | 218 | function TCanvasEx.PenDY(DY: Integer): TCanvasEx; 219 | begin 220 | MoveTo(PenPos.X, PenPos.Y + DY); 221 | MaxPen := PenPos; 222 | Result := Self; 223 | end; 224 | 225 | function TCanvasEx.PenDXDY(DX, DY: Integer): TCanvasEx; 226 | begin 227 | MoveTo(PenPos.X + DX, PenPos.Y + DY); 228 | MaxPen := PenPos; 229 | Result := Self; 230 | end; 231 | 232 | function TCanvasEx.PenBR: TCanvasEx; 233 | begin 234 | if LineHeight <> 0 then 235 | MoveTo(PenOrigin.X, PenPos.Y + LineHeight) 236 | else 237 | MoveTo(PenOrigin.X, PenPos.Y + TextHeight('8')); 238 | MaxPen := PenPos; 239 | Result := Self; 240 | end; 241 | 242 | function TCanvasEx.PenRestore: TCanvasEx; 243 | begin 244 | PenPos := FSavedPen; 245 | Result := Self; 246 | end; 247 | 248 | function TCanvasEx.PenSave: TCanvasEx; 249 | begin 250 | FSavedPen := PenPos; 251 | Result := Self; 252 | end; 253 | 254 | procedure TCanvasEx.SetMaxPen(const Value: TPoint); 255 | begin 256 | if Value.X > FMaxPen.X then 257 | FMaxPen.X := Value.X; 258 | if Value.Y > FMaxPen.Y then 259 | FMaxPen.Y := Value.Y; 260 | end; 261 | 262 | procedure TCanvasEx.CopyFont(SourceFontDataHandle: HGLOBAL); 263 | var 264 | LogFont: TLogFont; 265 | FontData: PFontData; 266 | begin 267 | ZeroMemory(@LogFont, SizeOf(LogFont)); 268 | FontData := GlobalLock(SourceFontDataHandle); 269 | GetObject(FontData.FontHandle, SizeOf(LogFont), @LogFont); 270 | GlobalUnlock(SourceFontDataHandle); 271 | Font.Handle := CreateFontIndirect(LogFont); 272 | end; 273 | 274 | end. 275 | -------------------------------------------------------------------------------- /src/Civ2UIA_FormAbout.ddp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2UIA_FormAbout.ddp -------------------------------------------------------------------------------- /src/Civ2UIA_FormAbout.dfm: -------------------------------------------------------------------------------- 1 | object FormAbout: TFormAbout 2 | Left = 665 3 | Top = 207 4 | BorderStyle = bsDialog 5 | Caption = 'About Civ2 UI Additions' 6 | ClientHeight = 240 7 | ClientWidth = 353 8 | Color = clBtnFace 9 | Font.Charset = DEFAULT_CHARSET 10 | Font.Color = clWindowText 11 | Font.Height = -11 12 | Font.Name = 'MS Sans Serif' 13 | Font.Style = [] 14 | OldCreateOrder = False 15 | Position = poOwnerFormCenter 16 | OnCreate = FormCreate 17 | DesignSize = ( 18 | 353 19 | 240) 20 | PixelsPerInch = 96 21 | TextHeight = 13 22 | object Button1: TButton 23 | Left = 139 24 | Top = 207 25 | Width = 75 26 | Height = 25 27 | Anchors = [akBottom] 28 | Cancel = True 29 | Caption = 'OK' 30 | Default = True 31 | ModalResult = 1 32 | TabOrder = 0 33 | end 34 | object GroupBox1: TGroupBox 35 | Left = 8 36 | Top = 4 37 | Width = 337 38 | Height = 195 39 | TabOrder = 1 40 | DesignSize = ( 41 | 337 42 | 195) 43 | object lbl0001: TLabel 44 | Left = 69 45 | Top = 16 46 | Width = 199 47 | Height = 19 48 | Anchors = [akTop] 49 | Caption = 'User Interface Additions' 50 | Font.Charset = RUSSIAN_CHARSET 51 | Font.Color = clWindowText 52 | Font.Height = -16 53 | Font.Name = 'Tahoma' 54 | Font.Style = [fsBold] 55 | ParentFont = False 56 | end 57 | object lbl0027: TLabel 58 | Left = 124 59 | Top = 168 60 | Width = 48 61 | Height = 13 62 | Cursor = crHandPoint 63 | Caption = 'FoxAhead' 64 | Font.Charset = DEFAULT_CHARSET 65 | Font.Color = clBlue 66 | Font.Height = -11 67 | Font.Name = 'MS Sans Serif' 68 | Font.Style = [fsUnderline] 69 | ParentFont = False 70 | OnClick = lbl0027Click 71 | end 72 | object lbl0017: TLabel 73 | Left = 92 74 | Top = 168 75 | Width = 24 76 | Height = 13 77 | Caption = '2024' 78 | end 79 | object lbl0016: TLabel 80 | Left = 92 81 | Top = 152 82 | Width = 230 83 | Height = 13 84 | Cursor = crHandPoint 85 | Caption = 'https://forums.civfanatics.com/threads/623515/' 86 | Color = clBtnFace 87 | Font.Charset = DEFAULT_CHARSET 88 | Font.Color = clBlue 89 | Font.Height = -11 90 | Font.Name = 'MS Sans Serif' 91 | Font.Style = [fsUnderline] 92 | ParentColor = False 93 | ParentFont = False 94 | OnClick = lbl0016Click 95 | end 96 | object lbl0015: TLabel 97 | Left = 92 98 | Top = 136 99 | Width = 232 100 | Height = 13 101 | Cursor = crHandPoint 102 | Caption = 'https://github.com/FoxAhead/Civ2-UI-Additions/' 103 | Color = clBtnFace 104 | Font.Charset = DEFAULT_CHARSET 105 | Font.Color = clBlue 106 | Font.Height = -11 107 | Font.Name = 'MS Sans Serif' 108 | Font.Style = [fsUnderline] 109 | ParentColor = False 110 | ParentFont = False 111 | OnClick = lbl0015Click 112 | end 113 | object lbl0014Version: TLabel 114 | Left = 92 115 | Top = 120 116 | Width = 35 117 | Height = 13 118 | Caption = 'Version' 119 | end 120 | object lbl0007: TLabel 121 | Left = 44 122 | Top = 168 123 | Width = 31 124 | Height = 13 125 | Caption = 'Author' 126 | end 127 | object lbl0006: TLabel 128 | Left = 24 129 | Top = 152 130 | Width = 51 131 | Height = 13 132 | Caption = 'Discussion' 133 | end 134 | object lbl0005: TLabel 135 | Left = 14 136 | Top = 136 137 | Width = 61 138 | Height = 13 139 | Caption = 'Source code' 140 | end 141 | object lbl0004: TLabel 142 | Left = 40 143 | Top = 120 144 | Width = 35 145 | Height = 13 146 | Caption = 'Version' 147 | end 148 | object lbl0003: TLabel 149 | Left = 40 150 | Top = 64 151 | Width = 256 152 | Height = 32 153 | Alignment = taCenter 154 | Anchors = [akTop] 155 | Caption = 156 | 'Sid Meier'#39's Civilization II'#13#10'Multiplayer Gold Edition 5.4.0f (Pa' + 157 | 'tch 3)' 158 | Font.Charset = RUSSIAN_CHARSET 159 | Font.Color = clWindowText 160 | Font.Height = -13 161 | Font.Name = 'Tahoma' 162 | Font.Style = [fsBold] 163 | ParentFont = False 164 | end 165 | object lbl0002: TLabel 166 | Left = 160 167 | Top = 40 168 | Width = 16 169 | Height = 16 170 | Anchors = [akTop] 171 | Caption = 'for' 172 | Font.Charset = RUSSIAN_CHARSET 173 | Font.Color = clWindowText 174 | Font.Height = -13 175 | Font.Name = 'Tahoma' 176 | Font.Style = [] 177 | ParentFont = False 178 | end 179 | end 180 | end 181 | -------------------------------------------------------------------------------- /src/Civ2UIA_FormAbout.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_FormAbout; 2 | 3 | interface 4 | 5 | uses 6 | Windows, 7 | 8 | 9 | 10 | Classes, 11 | 12 | Controls, 13 | Forms, 14 | 15 | StdCtrls; 16 | 17 | type 18 | TFormAbout = class(TForm) 19 | Button1: TButton; 20 | lbl0001: TLabel; 21 | lbl0002: TLabel; 22 | lbl0003: TLabel; 23 | lbl0004: TLabel; 24 | lbl0005: TLabel; 25 | lbl0006: TLabel; 26 | lbl0014Version: TLabel; 27 | lbl0015: TLabel; 28 | lbl0016: TLabel; 29 | lbl0007: TLabel; 30 | lbl0017: TLabel; 31 | lbl0027: TLabel; 32 | GroupBox1: TGroupBox; 33 | procedure FormCreate(Sender: TObject); 34 | procedure lbl0015Click(Sender: TObject); 35 | procedure lbl0016Click(Sender: TObject); 36 | procedure lbl0027Click(Sender: TObject); 37 | private 38 | { Private declarations } 39 | public 40 | { Public declarations } 41 | end; 42 | 43 | var 44 | FormAbout: TFormAbout; 45 | 46 | procedure ShowFormAbout; 47 | 48 | implementation 49 | 50 | uses 51 | ShellAPI, 52 | Civ2Proc, 53 | UiaMain; 54 | 55 | {$R *.dfm} 56 | 57 | procedure ShowFormAbout; 58 | var 59 | FormAbout: TFormAbout; 60 | begin 61 | FormAbout := TFormAbout.Create(nil); 62 | SetWindowLong(FormAbout.Handle, GWL_HWNDPARENT, Civ2.MainWindowInfo.WindowStructure.HWindow); 63 | FormAbout.ShowModal(); 64 | FormAbout.Free(); 65 | end; 66 | 67 | procedure TFormAbout.FormCreate(Sender: TObject); 68 | begin 69 | lbl0014Version.Caption := Uia.VersionString; 70 | end; 71 | 72 | procedure TFormAbout.lbl0015Click(Sender: TObject); 73 | begin 74 | ShellExecute(Application.Handle, 'open', PChar(lbl0015.Caption), nil, nil, SW_SHOW); 75 | end; 76 | 77 | procedure TFormAbout.lbl0016Click(Sender: TObject); 78 | begin 79 | ShellExecute(Application.Handle, 'open', PChar(lbl0016.Caption), nil, nil, SW_SHOW); 80 | end; 81 | 82 | procedure TFormAbout.lbl0027Click(Sender: TObject); 83 | begin 84 | ShellExecute(Application.Handle, 'open', 'https://github.com/FoxAhead/', nil, nil, SW_SHOW); 85 | end; 86 | 87 | end. 88 | -------------------------------------------------------------------------------- /src/Civ2UIA_FormConsole.ddp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2UIA_FormConsole.ddp -------------------------------------------------------------------------------- /src/Civ2UIA_FormConsole.dfm: -------------------------------------------------------------------------------- 1 | object FormConsole: TFormConsole 2 | Left = 217 3 | Top = 243 4 | AutoScroll = False 5 | Caption = 'Debug Console' 6 | ClientHeight = 175 7 | ClientWidth = 1201 8 | Color = clBtnFace 9 | Font.Charset = DEFAULT_CHARSET 10 | Font.Color = clWindowText 11 | Font.Height = -11 12 | Font.Name = 'MS Sans Serif' 13 | Font.Style = [] 14 | OldCreateOrder = False 15 | Position = poOwnerFormCenter 16 | OnClose = FormClose 17 | OnDestroy = FormDestroy 18 | DesignSize = ( 19 | 1201 20 | 175) 21 | PixelsPerInch = 96 22 | TextHeight = 13 23 | object LabelFocus: TLabel 24 | Left = 8 25 | Top = 4 26 | Width = 64 27 | Height = 16 28 | Caption = '00000000' 29 | Font.Charset = RUSSIAN_CHARSET 30 | Font.Color = clWindowText 31 | Font.Height = -13 32 | Font.Name = 'Fixedsys' 33 | Font.Style = [] 34 | ParentFont = False 35 | end 36 | object LabelCursor: TLabel 37 | Left = 88 38 | Top = 4 39 | Width = 64 40 | Height = 16 41 | Caption = '00000000' 42 | Font.Charset = RUSSIAN_CHARSET 43 | Font.Color = clWindowText 44 | Font.Height = -13 45 | Font.Name = 'Fixedsys' 46 | Font.Style = [] 47 | ParentFont = False 48 | end 49 | object Memo1: TMemo 50 | Left = 0 51 | Top = 24 52 | Width = 1201 53 | Height = 151 54 | Anchors = [akLeft, akTop, akRight, akBottom] 55 | Color = clBlack 56 | Font.Charset = RUSSIAN_CHARSET 57 | Font.Color = clSilver 58 | Font.Height = -13 59 | Font.Name = 'Fixedsys' 60 | Font.Style = [] 61 | HideSelection = False 62 | Lines.Strings = ( 63 | 64 | '1. SetFocus(0004050C): 408607 40863C 5C5C20 5EAC35 5EAB47 254BB7' + 65 | '7 ' 66 | 67 | '2. SetFocus(00080580): 5F7075 55AE20 4C428B 41FD3A 41F0C2 40BC9A' + 68 | ' 5A62EF 59DB95 451918 5BD109 254BB77 ' 69 | 70 | '3. SetFocus(00020590): 408607 40863C 5C5C20 5EAC60 5EAAC2 254BB7' + 71 | '7 ' 72 | '4. SetFocus(00020590): 5CAD40 254BB77 ' 73 | 74 | '5. SetFocus(00080580): 5F7075 55AE20 4C428B 41FD3A 41F2E0 4785AE' + 75 | ' 421EB4 4190E5 419119 419151 51D4B0 40BC9A 5A62EF 59DB95 451918 ' + 76 | '5BD109 254BB77 ' 77 | 78 | '6. SetFocus(00080580): 5F7075 55AE20 4C428B 41FD3A 41F2E0 4785AE' + 79 | ' 421EB4 4190E5 419119 419151 51D54A 59DF9E 59DB95 451918 5BD109 ' + 80 | '254BB77 ' 81 | 82 | '7. SetFocus(00080580): 5F7075 55AE20 4C428B 41FD3A 41F685 59DF9E' + 83 | ' 59DB95 451918 5BD109 254BB77 ') 84 | ParentFont = False 85 | ReadOnly = True 86 | ScrollBars = ssBoth 87 | TabOrder = 0 88 | WordWrap = False 89 | end 90 | object Button1: TButton 91 | Left = 1153 92 | Top = 0 93 | Width = 48 94 | Height = 24 95 | Anchors = [akTop, akRight] 96 | Caption = 'Clear' 97 | TabOrder = 1 98 | OnClick = Button1Click 99 | end 100 | object Button2: TButton 101 | Left = 1096 102 | Top = 0 103 | Width = 48 104 | Height = 24 105 | Anchors = [akTop, akRight] 106 | Caption = 'Snow' 107 | TabOrder = 2 108 | OnClick = Button2Click 109 | end 110 | object Timer1: TTimer 111 | Interval = 100 112 | OnTimer = Timer1Timer 113 | Left = 16 114 | Top = 40 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /src/Civ2UIA_FormConsole.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_FormConsole; 2 | 3 | interface 4 | 5 | uses 6 | Windows, 7 | SysUtils, 8 | Classes, 9 | Controls, 10 | Forms, 11 | StdCtrls, 12 | ExtCtrls; 13 | 14 | type 15 | TFormConsole = class(TForm) 16 | Memo1: TMemo; 17 | Button1: TButton; 18 | LabelFocus: TLabel; 19 | Timer1: TTimer; 20 | LabelCursor: TLabel; 21 | Button2: TButton; 22 | procedure FormClose(Sender: TObject; var Action: TCloseAction); 23 | procedure FormDestroy(Sender: TObject); 24 | procedure Button1Click(Sender: TObject); 25 | procedure Timer1Timer(Sender: TObject); 26 | procedure Button2Click(Sender: TObject); 27 | private 28 | { Private declarations } 29 | FMessagesCounter: Integer; 30 | class procedure EnsureInstance(); 31 | procedure AddToMemo(Text: string); 32 | public 33 | { Public declarations } 34 | class procedure Open(); 35 | class procedure Log(Text: string = ''); overload; 36 | class procedure Log(Number: Integer); overload; 37 | class procedure Log(const F: string; const A: array of const); overload; 38 | procedure ShowUp(); 39 | end; 40 | 41 | implementation 42 | 43 | uses 44 | Messages, 45 | Civ2Proc, 46 | UiaMain; 47 | 48 | {$R *.dfm} 49 | 50 | { TFormConsole } 51 | 52 | var 53 | FormConsole: TFormConsole; 54 | 55 | class procedure TFormConsole.EnsureInstance(); 56 | begin 57 | if FormConsole = nil then 58 | begin 59 | FormConsole := TFormConsole.Create(nil); 60 | FormConsole.Memo1.Clear(); 61 | end; 62 | end; 63 | 64 | class procedure TFormConsole.Open; 65 | begin 66 | EnsureInstance(); 67 | FormConsole.ShowUp(); 68 | end; 69 | 70 | class procedure TFormConsole.Log(Text: string); 71 | begin 72 | EnsureInstance(); 73 | //FormConsole.ShowUp(); 74 | FormConsole.AddToMemo(Text); 75 | end; 76 | 77 | class procedure TFormConsole.Log(Number: Integer); 78 | begin 79 | Log(IntToHex(Number, 8)); 80 | end; 81 | 82 | class procedure TFormConsole.Log(const F: string; const A: array of const); 83 | begin 84 | Log(Format(F, A)); 85 | end; 86 | 87 | procedure TFormConsole.AddToMemo(Text: string); 88 | begin 89 | //PostMessage(Memo1.Handle, WM_SETREDRAW, 0, 0); 90 | Memo1.Lines.BeginUpdate; 91 | while Memo1.Lines.Count >= 500 do 92 | Memo1.Lines.Delete(0); 93 | Inc(FMessagesCounter); 94 | Memo1.Lines.EndUpdate; 95 | Memo1.Lines.Add(Format('%d. %s', [FMessagesCounter, Text])); 96 | //Memo1.Text := Trim(Memo1.Text); 97 | //PostMessage(Memo1.Handle, WM_SETREDRAW, 1, 0); 98 | end; 99 | 100 | procedure TFormConsole.ShowUp; 101 | var 102 | R: TRect; 103 | P: TPoint; 104 | begin 105 | WindowState := wsNormal; 106 | SendMessage(Memo1.Handle, EM_LINESCROLL, 0, Memo1.Lines.Count); 107 | if (Civ2 <> nil) and (Civ2.MainWindowInfo.WindowStructure <> nil) then 108 | begin 109 | SetWindowLong(Handle, GWL_HWNDPARENT, Civ2.MainWindowInfo.WindowStructure.HWindow); 110 | Windows.GetClientRect(Civ2.MainWindowInfo.WindowStructure.HWindow, R); 111 | P := Point(R.Right, R.Top); 112 | MapWindowPoints(Civ2.MainWindowInfo.WindowStructure.HWindow, 0, P, 1); 113 | Show(); 114 | Left := P.X - Width; 115 | Top := P.Y; 116 | end 117 | else 118 | Show(); 119 | end; 120 | 121 | procedure TFormConsole.FormClose(Sender: TObject; var Action: TCloseAction); 122 | begin 123 | //Action := caFree; 124 | end; 125 | 126 | procedure TFormConsole.FormDestroy(Sender: TObject); 127 | begin 128 | //FormConsole := nil; 129 | end; 130 | 131 | procedure TFormConsole.Button1Click(Sender: TObject); 132 | begin 133 | Memo1.Clear(); 134 | end; 135 | 136 | procedure TFormConsole.Timer1Timer(Sender: TObject); 137 | var 138 | P: TPoint; 139 | begin 140 | LabelFocus.Caption := IntToHex(GetFocus(), 8); 141 | GetCursorPos(P); 142 | LabelCursor.Caption := IntToHex(WindowFromPoint(P), 8); 143 | end; 144 | 145 | procedure TFormConsole.Button2Click(Sender: TObject); 146 | begin 147 | Uia.SnowFlakes.Switch(); 148 | end; 149 | 150 | end. 151 | -------------------------------------------------------------------------------- /src/Civ2UIA_FormSettings.ddp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2UIA_FormSettings.ddp -------------------------------------------------------------------------------- /src/Civ2UIA_FormSettings.dfm: -------------------------------------------------------------------------------- 1 | object FormSettings: TFormSettings 2 | Left = 582 3 | Top = 263 4 | BorderStyle = bsDialog 5 | Caption = 'UIA Settings' 6 | ClientHeight = 324 7 | ClientWidth = 321 8 | Color = clBtnFace 9 | Font.Charset = DEFAULT_CHARSET 10 | Font.Color = clWindowText 11 | Font.Height = -11 12 | Font.Name = 'MS Sans Serif' 13 | Font.Style = [] 14 | OldCreateOrder = False 15 | Position = poOwnerFormCenter 16 | OnCreate = FormCreate 17 | OnDestroy = FormDestroy 18 | DesignSize = ( 19 | 321 20 | 324) 21 | PixelsPerInch = 96 22 | TextHeight = 13 23 | object ButtonClose: TButton 24 | Left = 120 25 | Top = 296 26 | Width = 81 27 | Height = 25 28 | Anchors = [akBottom] 29 | Cancel = True 30 | Caption = 'Close' 31 | TabOrder = 0 32 | OnClick = ButtonCloseClick 33 | end 34 | object GroupBoxFlags: TGroupBox 35 | Left = 8 36 | Top = 128 37 | Width = 305 38 | Height = 161 39 | Caption = 'Options' 40 | ParentShowHint = False 41 | ShowHint = True 42 | TabOrder = 2 43 | object ButtonList: TButton 44 | Left = 248 45 | Top = 16 46 | Width = 49 47 | Height = 17 48 | Caption = 'List...' 49 | TabOrder = 1 50 | OnClick = ButtonListClick 51 | end 52 | object CheckBox1: TCheckBox 53 | Left = 8 54 | Top = 16 55 | Width = 233 56 | Height = 17 57 | Hint = 58 | 'These popups will be shown in the map overlay instead, eliminati' + 59 | 'ng the annoying need to click '#39'OK'#39' button.'#13#10'Click List... button' + 60 | ' to set the list of popup names.' 61 | Caption = 'Suppress simple GAME.TXT popups' 62 | TabOrder = 0 63 | OnClick = CheckBoxFlagsClick 64 | end 65 | object CheckBox2: TCheckBox 66 | Tag = 1 67 | Left = 8 68 | Top = 40 69 | Width = 289 70 | Height = 17 71 | Hint = 72 | 'If there is already a worker in the tile, then when adding a new' + 73 | ' one,'#13#10'he takes the work counter for himself,'#13#10'and the order of ' + 74 | 'the previous one is additionally reset.'#13#10'Thus, there should be o' + 75 | 'nly one worker with the same order in a cell.' 76 | Caption = 'Reset Engineer'#39's order after passing its work to coworker' 77 | TabOrder = 2 78 | OnClick = CheckBoxFlagsClick 79 | end 80 | object CheckBox3: TCheckBox 81 | Tag = 2 82 | Left = 8 83 | Top = 64 84 | Width = 289 85 | Height = 17 86 | Hint = 87 | 'If unit has '#39'Go To'#39' order, it doesn'#39't stop when entering ZOC (zo' + 88 | 'ne of control).'#13#10'Of course, the ZOC rule still applies.' 89 | Caption = 'Don'#39't break unit movement on ZOC' 90 | TabOrder = 3 91 | OnClick = CheckBoxFlagsClick 92 | end 93 | object CheckBox4: TCheckBox 94 | Tag = 3 95 | Left = 8 96 | Top = 88 97 | Width = 289 98 | Height = 17 99 | Hint = 100 | 'When manually activating an unit it clears the flag for all unit' + 101 | 's of this stack,'#13#10'and vice versa sets it for all the rest.'#13#10'Also' + 102 | ' store activation position for later switching to nearby units.'#13 + 103 | #10'All this should build alternative convenient sequence for units' + 104 | ' rotation.' 105 | Caption = 'Tweak unit rotation algorithm' 106 | TabOrder = 4 107 | OnClick = CheckBoxFlagsClick 108 | end 109 | object CheckBox5: TCheckBox 110 | Tag = 4 111 | Left = 8 112 | Top = 112 113 | Width = 289 114 | Height = 17 115 | Hint = 'Can speed up the selection of options using the keyboard' 116 | Caption = 'Radiobuttons hotkeys' 117 | TabOrder = 5 118 | OnClick = CheckBoxFlagsClick 119 | end 120 | object CheckBox6: TCheckBox 121 | Tag = 5 122 | Left = 8 123 | Top = 136 124 | Width = 289 125 | Height = 17 126 | Hint = 127 | 'With unit activated hold Shift and Right-Click on the destinatio' + 128 | 'n square.'#13#10'All units of the same type capable of moving (with mo' + 129 | 've points, not fortified, not on sentry etc.) will be ordered to' + 130 | ' move.' 131 | Caption = 'Mass move units of the same type with Shift+RightClick' 132 | TabOrder = 6 133 | end 134 | end 135 | object GroupBoxColor: TGroupBox 136 | Left = 8 137 | Top = 8 138 | Width = 305 139 | Height = 113 140 | Caption = 'Color correction' 141 | TabOrder = 1 142 | object Label1: TLabel 143 | Left = 8 144 | Top = 24 145 | Width = 44 146 | Height = 13 147 | Caption = 'Exposure' 148 | end 149 | object LabelGamma: TLabel 150 | Left = 256 151 | Top = 48 152 | Width = 6 153 | Height = 13 154 | Caption = '0' 155 | end 156 | object Label3: TLabel 157 | Left = 8 158 | Top = 48 159 | Width = 36 160 | Height = 13 161 | Caption = 'Gamma' 162 | end 163 | object LabelExposure: TLabel 164 | Left = 256 165 | Top = 24 166 | Width = 6 167 | Height = 13 168 | Caption = '0' 169 | end 170 | object btn9: TButton 171 | Tag = 3 172 | Left = 216 173 | Top = 72 174 | Width = 24 175 | Height = 24 176 | Caption = '+3' 177 | TabOrder = 10 178 | OnClick = ButtonColorPresetClick 179 | end 180 | object btn8: TButton 181 | Tag = 2 182 | Left = 192 183 | Top = 72 184 | Width = 24 185 | Height = 24 186 | Caption = '+2' 187 | TabOrder = 9 188 | OnClick = ButtonColorPresetClick 189 | end 190 | object btn7: TButton 191 | Tag = 1 192 | Left = 168 193 | Top = 72 194 | Width = 24 195 | Height = 24 196 | Caption = '+1' 197 | TabOrder = 8 198 | OnClick = ButtonColorPresetClick 199 | end 200 | object btn6: TButton 201 | Left = 144 202 | Top = 72 203 | Width = 24 204 | Height = 24 205 | Caption = '0' 206 | Font.Charset = DEFAULT_CHARSET 207 | Font.Color = clWindowText 208 | Font.Height = -11 209 | Font.Name = 'MS Sans Serif' 210 | Font.Style = [fsBold] 211 | ParentFont = False 212 | TabOrder = 7 213 | OnClick = ButtonColorPresetClick 214 | end 215 | object btn5: TButton 216 | Tag = -1 217 | Left = 120 218 | Top = 72 219 | Width = 24 220 | Height = 24 221 | Caption = '-1' 222 | TabOrder = 6 223 | OnClick = ButtonColorPresetClick 224 | end 225 | object btn4: TButton 226 | Tag = -2 227 | Left = 96 228 | Top = 72 229 | Width = 24 230 | Height = 24 231 | Caption = '-2' 232 | TabOrder = 5 233 | OnClick = ButtonColorPresetClick 234 | end 235 | object btn3: TButton 236 | Tag = -3 237 | Left = 72 238 | Top = 72 239 | Width = 24 240 | Height = 24 241 | Caption = '-3' 242 | TabOrder = 4 243 | OnClick = ButtonColorPresetClick 244 | end 245 | object btn2: TButton 246 | Tag = -4 247 | Left = 48 248 | Top = 72 249 | Width = 24 250 | Height = 24 251 | Caption = '-4' 252 | TabOrder = 3 253 | OnClick = ButtonColorPresetClick 254 | end 255 | object btn11: TButton 256 | Tag = 5 257 | Left = 264 258 | Top = 72 259 | Width = 24 260 | Height = 24 261 | Caption = '+5' 262 | TabOrder = 12 263 | OnClick = ButtonColorPresetClick 264 | end 265 | object btn10: TButton 266 | Tag = 4 267 | Left = 240 268 | Top = 72 269 | Width = 24 270 | Height = 24 271 | Caption = '+4' 272 | TabOrder = 11 273 | OnClick = ButtonColorPresetClick 274 | end 275 | object btn1: TButton 276 | Tag = -5 277 | Left = 24 278 | Top = 72 279 | Width = 24 280 | Height = 24 281 | Caption = '-5' 282 | TabOrder = 2 283 | OnClick = ButtonColorPresetClick 284 | end 285 | object ScrollBar1: TScrollBar 286 | Left = 64 287 | Top = 24 288 | Width = 185 289 | Height = 16 290 | LargeChange = 4 291 | Max = 30 292 | Min = -30 293 | PageSize = 0 294 | TabOrder = 0 295 | TabStop = False 296 | OnChange = ScrollBar1Change 297 | end 298 | object ScrollBar2: TScrollBar 299 | Left = 64 300 | Top = 48 301 | Width = 185 302 | Height = 16 303 | LargeChange = 4 304 | Max = 50 305 | Min = 1 306 | PageSize = 0 307 | Position = 1 308 | TabOrder = 1 309 | TabStop = False 310 | OnChange = ScrollBar1Change 311 | end 312 | end 313 | end 314 | -------------------------------------------------------------------------------- /src/Civ2UIA_FormSettings.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_FormSettings; 2 | 3 | interface 4 | 5 | uses 6 | Classes, 7 | Controls, 8 | Forms, 9 | StdCtrls; 10 | 11 | type 12 | TFormSettings = class(TForm) 13 | ScrollBar1: TScrollBar; 14 | Label1: TLabel; 15 | LabelExposure: TLabel; 16 | Label3: TLabel; 17 | ScrollBar2: TScrollBar; 18 | LabelGamma: TLabel; 19 | ButtonClose: TButton; 20 | btn4: TButton; 21 | btn5: TButton; 22 | btn6: TButton; 23 | btn7: TButton; 24 | btn8: TButton; 25 | btn3: TButton; 26 | btn9: TButton; 27 | btn2: TButton; 28 | btn10: TButton; 29 | btn1: TButton; 30 | btn11: TButton; 31 | ButtonList: TButton; 32 | GroupBoxFlags: TGroupBox; 33 | CheckBox1: TCheckBox; 34 | CheckBox2: TCheckBox; 35 | CheckBox3: TCheckBox; 36 | CheckBox4: TCheckBox; 37 | CheckBox5: TCheckBox; 38 | GroupBoxColor: TGroupBox; 39 | CheckBox6: TCheckBox; 40 | procedure FormCreate(Sender: TObject); 41 | procedure ScrollBar1Change(Sender: TObject); 42 | procedure PropagatePaletteChanges(); 43 | procedure ButtonCloseClick(Sender: TObject); 44 | procedure ButtonColorPresetClick(Sender: TObject); 45 | procedure ButtonListClick(Sender: TObject); 46 | procedure CheckBoxFlagsClick(Sender: TObject); 47 | procedure FormDestroy(Sender: TObject); 48 | private 49 | { Private declarations } 50 | FChangeEventActive: Boolean; 51 | FHintHidePause: Integer; 52 | procedure SetColor(Exposure, Gamma: Double); 53 | procedure SetControls(); 54 | public 55 | { Public declarations } 56 | end; 57 | 58 | var 59 | FormSettings: TFormSettings; 60 | 61 | procedure ShowFormSettings; 62 | 63 | implementation 64 | 65 | uses 66 | 67 | SysUtils, 68 | Windows, 69 | UiaMain, 70 | 71 | Civ2Types, 72 | Civ2Proc, 73 | Civ2UIA_FormStrings; 74 | 75 | {$R *.dfm} 76 | 77 | procedure ShowFormSettings; 78 | var 79 | FormSettings: TFormSettings; 80 | begin 81 | FormSettings := TFormSettings.Create(nil); 82 | SetWindowLong(FormSettings.Handle, GWL_HWNDPARENT, Civ2.MainWindowInfo.WindowStructure.HWindow); 83 | FormSettings.ShowModal(); 84 | FormSettings.Free(); 85 | Uia.Settings.Save(); 86 | //Ex.SaveSettingsFile(); 87 | end; 88 | 89 | procedure TFormSettings.FormCreate(Sender: TObject); 90 | var 91 | i: Integer; 92 | begin 93 | FHintHidePause := Application.HintHidePause; 94 | Application.HintHidePause := 15000; 95 | for i := 0 to GroupBoxFlags.ControlCount - 1 do 96 | begin 97 | if GroupBoxFlags.Controls[i] is TCheckBox then 98 | begin 99 | TCheckBox(GroupBoxFlags.Controls[i]).OnClick := CheckBoxFlagsClick; 100 | end; 101 | end; 102 | SetControls(); 103 | end; 104 | 105 | procedure TFormSettings.ScrollBar1Change(Sender: TObject); 106 | begin 107 | if not FChangeEventActive then 108 | Exit; 109 | Uia.Settings.Dat.ColorExposure := ScrollBar1.Position / 20; 110 | Uia.Settings.Dat.ColorGamma := ScrollBar2.Position / 20; 111 | SetControls(); 112 | end; 113 | 114 | procedure TFormSettings.PropagatePaletteChanges; 115 | var 116 | HWindow: HWND; 117 | GraphicsInfo: PGraphicsInfo; 118 | Palette: PPalette; 119 | begin 120 | GraphicsInfo := @Civ2.MapWindow.MSWindow.GraphicsInfo; 121 | Palette := GraphicsInfo.WindowInfo.WindowInfo1.Palette; 122 | if Palette = nil then 123 | Palette := Civ2.Palette; 124 | Civ2.Palette_SetRandomID(Palette); 125 | Civ2.DrawPort_UpdateDIBColorTableFromPaletteSafe(@GraphicsInfo.DrawPort, Palette); 126 | // Also recreate main window brush for background 127 | Civ2.WindowInfo1_RecreateBrush(Civ2.MainWindowInfo, $9E); 128 | // Also set new palette for map overlay - to be consistent with the game look 129 | Uia.MapOverlay.SetDIBColorTableFromPalette(Palette); 130 | // Redraw main window with all subwindows 131 | HWindow := GetParent(GraphicsInfo.WindowInfo.WindowInfo1.WindowStructure^.HWindow); 132 | RedrawWindow(HWindow, nil, 0, RDW_INVALIDATE + RDW_UPDATENOW + RDW_ALLCHILDREN); 133 | end; 134 | 135 | procedure TFormSettings.ButtonCloseClick(Sender: TObject); 136 | begin 137 | Close; 138 | end; 139 | 140 | procedure TFormSettings.SetControls(); 141 | var 142 | i: Integer; 143 | Tag: Integer; 144 | begin 145 | FChangeEventActive := False; 146 | // Color correction 147 | ScrollBar1.Position := Trunc(Uia.Settings.Dat.ColorExposure * 20); 148 | ScrollBar2.Position := Trunc(Uia.Settings.Dat.ColorGamma * 20); 149 | LabelExposure.Caption := FloatToStr(Uia.Settings.Dat.ColorExposure); 150 | LabelGamma.Caption := FloatToStr(Uia.Settings.Dat.ColorGamma); 151 | // Flags 152 | for i := 0 to GroupBoxFlags.ControlCount - 1 do 153 | begin 154 | if GroupBoxFlags.Controls[i] is TCheckBox then 155 | begin 156 | TCheckBox(GroupBoxFlags.Controls[i]).Checked := Uia.Settings.DatFlagSet(GroupBoxFlags.Controls[i].Tag); 157 | end; 158 | end; 159 | FChangeEventActive := True; 160 | PropagatePaletteChanges(); 161 | end; 162 | 163 | procedure TFormSettings.SetColor(Exposure, Gamma: Double); 164 | begin 165 | Uia.Settings.Dat.ColorExposure := Exposure; 166 | Uia.Settings.Dat.ColorGamma := Gamma; 167 | SetControls(); 168 | end; 169 | 170 | procedure TFormSettings.ButtonColorPresetClick(Sender: TObject); 171 | var 172 | Tag: Integer; 173 | Exposure, Gamma: Double; 174 | begin 175 | Tag := TComponent(Sender).Tag; 176 | Exposure := 0 + Tag * 0.10; 177 | Gamma := 1.0 + Tag * 0.05; 178 | SetColor(Exposure, Gamma); 179 | end; 180 | 181 | procedure TFormSettings.ButtonListClick(Sender: TObject); 182 | var 183 | FormStrings: TFormStrings; 184 | i: Integer; 185 | begin 186 | FormStrings := TFormStrings.Create(Self); 187 | SetWindowLong(FormStrings.Handle, GWL_HWNDPARENT, Self.Handle); 188 | FormStrings.Memo1.Lines.Assign(Uia.Settings.SuppressPopupList); 189 | FormStrings.ShowModal(); 190 | for i := 0 to FormStrings.Memo1.Lines.Count - 1 do 191 | FormStrings.Memo1.Lines[i] := UpperCase(FormStrings.Memo1.Lines[i]); 192 | Uia.Settings.SuppressPopupList.Assign(FormStrings.Memo1.Lines); 193 | FormStrings.Free(); 194 | for i := Uia.Settings.SuppressPopupList.Count - 1 downto 0 do 195 | begin 196 | if Uia.Settings.SuppressPopupList[i] = '' then 197 | Uia.Settings.SuppressPopupList.Delete(i); 198 | end; 199 | end; 200 | 201 | procedure TFormSettings.CheckBoxFlagsClick(Sender: TObject); 202 | var 203 | Tag: Integer; 204 | begin 205 | Tag := TComponent(Sender).Tag; 206 | Uia.Settings.SetDatFlag(Tag, TCheckBox(Sender).Checked); 207 | end; 208 | 209 | procedure TFormSettings.FormDestroy(Sender: TObject); 210 | begin 211 | Application.HintHidePause := FHintHidePause; 212 | end; 213 | 214 | end. 215 | -------------------------------------------------------------------------------- /src/Civ2UIA_FormTest.ddp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2UIA_FormTest.ddp -------------------------------------------------------------------------------- /src/Civ2UIA_FormTest.dfm: -------------------------------------------------------------------------------- 1 | object FormTest: TFormTest 2 | Left = 579 3 | Top = 353 4 | BorderStyle = bsDialog 5 | Caption = 'FormTest' 6 | ClientHeight = 240 7 | ClientWidth = 353 8 | Color = clBtnFace 9 | Font.Charset = DEFAULT_CHARSET 10 | Font.Color = clWindowText 11 | Font.Height = -11 12 | Font.Name = 'MS Sans Serif' 13 | Font.Style = [] 14 | OldCreateOrder = False 15 | Position = poOwnerFormCenter 16 | OnCreate = FormCreate 17 | PixelsPerInch = 96 18 | TextHeight = 13 19 | object Label1: TLabel 20 | Left = 8 21 | Top = 208 22 | Width = 32 23 | Height = 13 24 | Caption = 'Label1' 25 | end 26 | object Label2: TLabel 27 | Left = 240 28 | Top = 208 29 | Width = 32 30 | Height = 13 31 | Caption = 'Label2' 32 | end 33 | object Button1: TButton 34 | Left = 136 35 | Top = 208 36 | Width = 75 37 | Height = 25 38 | Cancel = True 39 | Caption = 'OK' 40 | Default = True 41 | ModalResult = 1 42 | TabOrder = 0 43 | end 44 | object Button2: TButton 45 | Left = 48 46 | Top = 208 47 | Width = 75 48 | Height = 25 49 | Caption = 'All' 50 | TabOrder = 1 51 | OnClick = Button2Click 52 | end 53 | object Button3: TButton 54 | Left = 280 55 | Top = 208 56 | Width = 59 57 | Height = 25 58 | Caption = 'City' 59 | TabOrder = 2 60 | OnClick = Button3Click 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /src/Civ2UIA_FormTest.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_FormTest; 2 | 3 | interface 4 | 5 | uses 6 | Windows, 7 | SysUtils, 8 | Classes, 9 | Controls, 10 | Forms, 11 | StdCtrls; 12 | 13 | type 14 | TFormTest = class(TForm) 15 | Button1: TButton; 16 | Label1: TLabel; 17 | Label2: TLabel; 18 | Button2: TButton; 19 | Button3: TButton; 20 | procedure FormCreate(Sender: TObject); 21 | procedure CheckBoxClick(Sender: TObject); 22 | procedure Button2Click(Sender: TObject); 23 | procedure Button3Click(Sender: TObject); 24 | private 25 | { Private declarations } 26 | FTerrainType: Byte; 27 | FTerrainFeatures: Byte; 28 | FCheckBoxes: array[1..2, 0..7] of TCheckBox; 29 | FUpdating: Boolean; 30 | procedure SetTerrainFeatures(const Value: Byte); 31 | procedure SetTerrainType(const Value: Byte); 32 | procedure SetCheckBoxes(Group, Value: Integer); 33 | public 34 | { Public declarations } 35 | property TerrainType: Byte read FTerrainType write SetTerrainType; 36 | property TerrainFeatures: Byte read FTerrainFeatures write SetTerrainFeatures; 37 | end; 38 | 39 | var 40 | FormTest: TFormTest; 41 | 42 | procedure ShowFormTest; 43 | 44 | implementation 45 | 46 | uses 47 | Civ2Types, 48 | Civ2Proc; 49 | 50 | {$R *.dfm} 51 | 52 | procedure ShowFormTest; 53 | var 54 | FormTest: TFormTest; 55 | MapSquare: PMapSquare; 56 | i: Integer; 57 | begin 58 | 59 | FormTest := TFormTest.Create(nil); 60 | SetWindowLong(FormTest.Handle, GWL_HWNDPARENT, Civ2.MainWindowInfo.WindowStructure.HWindow); 61 | 62 | MapSquare := Civ2.MapGetSquare(Civ2.CursorX^, Civ2.CursorY^); 63 | FormTest.TerrainType := MapSquare.TerrainType; 64 | FormTest.TerrainFeatures := MapSquare.TerrainFeatures; 65 | 66 | FormTest.ShowModal(); 67 | 68 | MapSquare.TerrainType := FormTest.TerrainType; 69 | MapSquare.TerrainFeatures := FormTest.TerrainFeatures; 70 | 71 | for i := 0 to 7 do 72 | if Civ2.MapSquareIsVisibleTo(Civ2.CursorX^, Civ2.CursorY^, i) then 73 | Civ2.MapUpdateKnownTerrainFeatures(Civ2.CursorX^, Civ2.CursorY^, i); 74 | 75 | FormTest.Free(); 76 | end; 77 | 78 | procedure TFormTest.FormCreate(Sender: TObject); 79 | var 80 | i: Integer; 81 | begin 82 | for i := 0 to 7 do 83 | begin 84 | FCheckBoxes[1][i] := TCheckBox.Create(Self); 85 | FCheckBoxes[1][i].Parent := Self; 86 | FCheckBoxes[1][i].Left := 8; 87 | FCheckBoxes[1][i].Top := 8 + i * 24; 88 | FCheckBoxes[1][i].Caption := Format('0x%.2x', [1 shl i]); 89 | FCheckBoxes[1][i].Tag := 1; 90 | FCheckBoxes[1][i].OnClick := CheckBoxClick; 91 | end; 92 | 93 | for i := 0 to 7 do 94 | begin 95 | FCheckBoxes[2][i] := TCheckBox.Create(Self); 96 | FCheckBoxes[2][i].Parent := Self; 97 | FCheckBoxes[2][i].Left := 184; 98 | FCheckBoxes[2][i].Top := 8 + i * 24; 99 | FCheckBoxes[2][i].Caption := Format('0x%.2x', [1 shl i]); 100 | FCheckBoxes[2][i].Tag := 2; 101 | FCheckBoxes[2][i].OnClick := CheckBoxClick; 102 | end; 103 | end; 104 | 105 | procedure TFormTest.CheckBoxClick(Sender: TObject); 106 | var 107 | i, Group, j: Integer; 108 | begin 109 | if FUpdating then 110 | Exit; 111 | j := 0; 112 | Group := (Sender as TCheckBox).Tag; 113 | for i := 0 to 7 do 114 | j := j + (Ord(FCheckBoxes[Group][i].Checked) shl i); 115 | if Group = 1 then 116 | TerrainType := j 117 | else 118 | TerrainFeatures := j; 119 | end; 120 | 121 | procedure TFormTest.SetTerrainType(const Value: Byte); 122 | begin 123 | FTerrainType := Value; 124 | Label1.Caption := Format('0x%.2x', [FTerrainType]); 125 | SetCheckBoxes(1, FTerrainType); 126 | end; 127 | 128 | procedure TFormTest.SetTerrainFeatures(const Value: Byte); 129 | begin 130 | FTerrainFeatures := Value; 131 | Label2.Caption := Format('0x%.2x', [FTerrainFeatures]); 132 | SetCheckBoxes(2, FTerrainFeatures); 133 | end; 134 | 135 | procedure TFormTest.SetCheckBoxes(Group, Value: Integer); 136 | var 137 | i: Integer; 138 | begin 139 | if FUpdating then 140 | Exit; 141 | FUpdating := True; 142 | for i := 0 to 7 do 143 | FCheckBoxes[Group][i].Checked := Value and (1 shl i) <> 0; 144 | FUpdating := False; 145 | end; 146 | 147 | procedure TFormTest.Button2Click(Sender: TObject); 148 | var 149 | X, Y, i: Integer; 150 | Square: PMapSquare; 151 | begin 152 | X := 0; 153 | Y := 0; 154 | for i := 0 to Civ2.MapHeader.Area - 1 do 155 | begin 156 | Square := Civ2.MapGetSquare(X, Y); 157 | Square.TerrainType := FTerrainType; 158 | X := X + 2; 159 | if X >= Civ2.MapHeader.SizeX then 160 | begin 161 | Y := Y + 1; 162 | X := Y and 1; 163 | end; 164 | end; 165 | end; 166 | 167 | procedure TFormTest.Button3Click(Sender: TObject); 168 | var 169 | X, Y, i, j: Integer; 170 | Square: PMapSquare; 171 | begin 172 | for i := 0 to 20 do 173 | begin 174 | X := Civ2.MapWrapX(Civ2.CursorX^ + Civ2.CitySpiralDX[i]); 175 | Y := Civ2.CursorY^ + Civ2.CitySpiralDY[i]; 176 | Square := Civ2.MapGetSquare(X, Y); 177 | Square.TerrainFeatures := FTerrainFeatures; 178 | for j := 0 to 7 do 179 | if Civ2.MapSquareIsVisibleTo(X, Y, j) then 180 | Civ2.MapUpdateKnownTerrainFeatures(X, Y, j); 181 | end; 182 | end; 183 | 184 | end. 185 | 186 | -------------------------------------------------------------------------------- /src/Civ2UIA_GIFS.rc: -------------------------------------------------------------------------------- 1 | 105 GIFS GIFS\105.GIF 2 | 229 GIFS GIFS\229.GIF 3 | 250 GIFS GIFS\250.GIF 4 | 30001 GIFS GIFS\30001.gif 5 | 30002 GIFS GIFS\30002.gif 6 | -------------------------------------------------------------------------------- /src/Civ2UIA_GIFS.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/Civ2UIA_GIFS.res -------------------------------------------------------------------------------- /src/Civ2UIA_MapMessage.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_MapMessage; 2 | 3 | interface 4 | 5 | type 6 | TMapMessage = class 7 | public 8 | Timer: Integer; 9 | TextOut: string; 10 | constructor Create(TextOut: string); 11 | destructor Destroy; override; 12 | published 13 | end; 14 | 15 | implementation 16 | 17 | { TMapMessages } 18 | 19 | constructor TMapMessage.Create(TextOut: string); 20 | begin 21 | inherited Create; 22 | Self.Timer := 50; 23 | Self.TextOut := TextOut; 24 | end; 25 | 26 | destructor TMapMessage.Destroy; 27 | begin 28 | 29 | inherited; 30 | end; 31 | 32 | end. 33 | -------------------------------------------------------------------------------- /src/Civ2UIA_MapMessages.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_MapMessages; 2 | 3 | interface 4 | 5 | uses 6 | Classes, 7 | Civ2Types, 8 | Civ2UIA_MapMessage, 9 | Civ2UIA_MapOverlayModule; 10 | 11 | type 12 | TMapMessages = class(TInterfacedObject, IMapOverlayModule) 13 | private 14 | FMapMessagesList: TList; 15 | protected 16 | public 17 | constructor Create; 18 | destructor Destroy; override; 19 | procedure Add(MapMessage: TMapMessage); overload; 20 | procedure Add(TextOut: string); overload; 21 | procedure Update(); 22 | procedure Draw(DrawPort: PDrawPort); 23 | function HasSomethingToDraw(): Boolean; 24 | published 25 | end; 26 | 27 | implementation 28 | 29 | uses 30 | Graphics, 31 | Math, 32 | Types, 33 | Civ2Proc, 34 | Civ2UIA_CanvasEx; 35 | 36 | { TMapMessages } 37 | 38 | procedure TMapMessages.Add(MapMessage: TMapMessage); 39 | begin 40 | FMapMessagesList.Add(MapMessage); 41 | end; 42 | 43 | procedure TMapMessages.Add(TextOut: string); 44 | begin 45 | FMapMessagesList.Add(TMapMessage.Create(TextOut)); 46 | end; 47 | 48 | constructor TMapMessages.Create; 49 | begin 50 | inherited; 51 | FMapMessagesList := TList.Create(); 52 | end; 53 | 54 | destructor TMapMessages.Destroy; 55 | begin 56 | FMapMessagesList.Free(); 57 | inherited; 58 | end; 59 | 60 | procedure TMapMessages.Draw(DrawPort: PDrawPort); 61 | var 62 | Canvas: TCanvasEx; 63 | i: Integer; 64 | TextOut: string; 65 | X1, Y1: Integer; 66 | TextSize: TSize; 67 | TextExtent: TSize; 68 | TextColor: TColor; 69 | MapMessage: TMapMessage; 70 | begin 71 | if HasSomethingToDraw() then 72 | begin 73 | Canvas := TCanvasEx.Create(DrawPort); 74 | Canvas.Brush.Style := bsClear; 75 | Canvas.Font.Style := []; 76 | Canvas.Font.Size := 10; 77 | Canvas.Font.Name := 'Arial'; 78 | // Message Queue 79 | for i := 0 to FMapMessagesList.Count - 1 do 80 | begin 81 | if i > 35 then 82 | Break; 83 | MapMessage := TMapMessage(FMapMessagesList.Items[i]); 84 | X1 := Min(255, 512 div 50 * (MapMessage.Timer + 10)); 85 | TextColor := TColor(X1 * $10101); 86 | TextSize := Canvas.TextExtent(MapMessage.TextOut); 87 | Y1 := Civ2.MapWindow.MSWindow.GraphicsInfo.DrawPort.ClientRectangle.Right - TextSize.cx - 20; 88 | //TextOutWithShadows(TCanvas(Canvas), MapMessage.TextOut, Y1, 100 + i * 20, TextColor, clBlack, SHADOW_ALL); 89 | Canvas.MoveTo(Y1, 100 + i * 20); 90 | Canvas.Font.Color := TextColor; 91 | Canvas.FontShadowColor := clBlack; 92 | Canvas.FontShadows := SHADOW_ALL; 93 | Canvas.TextOutWithShadows(MapMessage.TextOut); 94 | end; 95 | Canvas.Free(); 96 | end; 97 | end; 98 | 99 | function TMapMessages.HasSomethingToDraw: Boolean; 100 | begin 101 | Result := (FMapMessagesList.Count > 0); 102 | end; 103 | 104 | procedure TMapMessages.Update; 105 | var 106 | i: Integer; 107 | MapMessage: TMapMessage; 108 | begin 109 | if Civ2.CurrPopupInfo^ = nil then 110 | begin 111 | for i := 0 to FMapMessagesList.Count - 1 do 112 | begin 113 | if i > 35 then 114 | Break; 115 | MapMessage := TMapMessage(FMapMessagesList.Items[i]); 116 | Dec(MapMessage.Timer); 117 | if MapMessage.Timer <= 0 then 118 | begin 119 | MapMessage.Free(); 120 | FMapMessagesList.Items[i] := nil; 121 | end; 122 | end; 123 | FMapMessagesList.Pack(); 124 | end; 125 | end; 126 | 127 | end. 128 | 129 | -------------------------------------------------------------------------------- /src/Civ2UIA_MapOverlay.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_MapOverlay; 2 | 3 | interface 4 | 5 | uses 6 | Classes, 7 | Windows, 8 | Civ2Types, 9 | Civ2UIA_MapOverlayModule; 10 | 11 | type 12 | TMapOverlay = class 13 | private 14 | FDrawPort: TDrawPort; 15 | FModulesList: TInterfaceList; 16 | FTimesToDraw: Integer; 17 | FMapDeviceContext: HDC; 18 | procedure RefreshDrawInfo(); 19 | procedure DrawModules(); 20 | protected 21 | public 22 | constructor Create; 23 | destructor Destroy; override; 24 | function HasSomethingToDraw(): Boolean; 25 | procedure UpdateModules(); 26 | procedure AddModule(Module: IMapOverlayModule); 27 | function CopyToScreenBitBlt(SrcDI: PDrawInfo; DestWS: PWindowStructure): Boolean; 28 | procedure SetDIBColorTableFromPalette(Palette: Pointer); 29 | published 30 | end; 31 | 32 | implementation 33 | 34 | uses 35 | Civ2Proc; 36 | 37 | { TMapOverlay } 38 | 39 | procedure TMapOverlay.AddModule(Module: IMapOverlayModule); 40 | begin 41 | FModulesList.Add(Module); 42 | end; 43 | 44 | function TMapOverlay.CopyToScreenBitBlt(SrcDI: PDrawInfo; DestWS: PWindowStructure): Boolean; 45 | var 46 | VSrcDC: HDC; 47 | begin 48 | if (SrcDI = Civ2.MapWindow.MSWindow.GraphicsInfo.DrawPort.DrawInfo) and (HasSomethingToDraw()) then 49 | begin 50 | Result := True; 51 | RefreshDrawInfo(); 52 | VSrcDC := FDrawPort.DrawInfo^.DeviceContext; 53 | BitBlt(VSrcDC, 0, 0, SrcDI.Width, SrcDI.Height, SrcDI.DeviceContext, 0, 0, SRCCOPY); 54 | DrawModules(); 55 | BitBlt(DestWS.DeviceContext, 0, 0, SrcDI.Width, SrcDI.Height, VSrcDC, 0, 0, SRCCOPY); 56 | end 57 | else 58 | Result := False; 59 | end; 60 | 61 | constructor TMapOverlay.Create; 62 | begin 63 | inherited; 64 | FModulesList := TInterfaceList.Create(); 65 | end; 66 | 67 | destructor TMapOverlay.Destroy; 68 | begin 69 | FModulesList.Free(); 70 | inherited; 71 | end; 72 | 73 | procedure TMapOverlay.DrawModules(); 74 | var 75 | i: Integer; 76 | Module: IMapOverlayModule; 77 | begin 78 | if FDrawPort.DrawInfo.DeviceContext <> 0 then 79 | begin 80 | for i := 0 to FModulesList.Count - 1 do 81 | begin 82 | Module := IMapOverlayModule(FModulesList.Items[i]); 83 | Module.Draw(@FDrawPort); 84 | end; 85 | Dec(FTimesToDraw); 86 | end; 87 | end; 88 | 89 | function TMapOverlay.HasSomethingToDraw: Boolean; 90 | var 91 | i: Integer; 92 | Module: IMapOverlayModule; 93 | begin 94 | for i := 0 to FModulesList.Count - 1 do 95 | begin 96 | Module := IMapOverlayModule(FModulesList.Items[i]); 97 | if Module.HasSomethingToDraw() then 98 | begin 99 | Result := True; 100 | FTimesToDraw := 2; 101 | Exit; 102 | end; 103 | end; 104 | Result := (FTimesToDraw > 0); 105 | end; 106 | 107 | procedure TMapOverlay.RefreshDrawInfo; 108 | var 109 | MapDrawPort: PDrawPort; 110 | begin 111 | MapDrawPort := @Civ2.MapWindow.MSWindow.GraphicsInfo.DrawPort; 112 | if MapDrawPort.DrawInfo <> nil then 113 | begin 114 | if MapDrawPort.DrawInfo.DeviceContext <> 0 then 115 | begin 116 | if FMapDeviceContext <> MapDrawPort.DrawInfo.DeviceContext then 117 | begin 118 | FMapDeviceContext := MapDrawPort.DrawInfo.DeviceContext; 119 | Civ2.DrawPort_ResetWH(@FDrawPort, MapDrawPort.Width, MapDrawPort.Height); 120 | if FDrawPort.ColorDepth = 1 then 121 | //Civ2.SetDIBColorTableFromPalette(FDrawPort.DrawInfo, Civ2.MapWindow.MSWindow.GraphicsInfo.WindowInfo.WindowInfo1.Palette); 122 | Civ2.SetDIBColorTableFromPalette(FDrawPort.DrawInfo, Civ2.Palette); 123 | end; 124 | end; 125 | end; 126 | end; 127 | 128 | procedure TMapOverlay.SetDIBColorTableFromPalette(Palette: Pointer); 129 | begin 130 | if FDrawPort.DrawInfo <> nil then 131 | if FDrawPort.ColorDepth = 1 then 132 | Civ2.SetDIBColorTableFromPalette(FDrawPort.DrawInfo, Palette); 133 | end; 134 | 135 | procedure TMapOverlay.UpdateModules; 136 | var 137 | i: Integer; 138 | Module: IMapOverlayModule; 139 | begin 140 | for i := 0 to FModulesList.Count - 1 do 141 | begin 142 | Module := IMapOverlayModule(FModulesList.Items[i]); 143 | Module.Update(); 144 | end; 145 | end; 146 | 147 | end. 148 | 149 | -------------------------------------------------------------------------------- /src/Civ2UIA_MapOverlayModule.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_MapOverlayModule; 2 | 3 | interface 4 | 5 | uses 6 | Civ2Types; 7 | 8 | type 9 | IMapOverlayModule = interface 10 | function HasSomethingToDraw(): Boolean; 11 | procedure Update(); 12 | procedure Draw(DrawPort: PDrawPort); 13 | end; 14 | 15 | implementation 16 | 17 | end. 18 | -------------------------------------------------------------------------------- /src/Civ2UIA_Options.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_Options; 2 | 3 | interface 4 | 5 | type 6 | PUIAOptions = ^TUIAOptions; 7 | 8 | TUIAOptions = packed record 9 | UIAEnable: Boolean; 10 | Patch64bitOn: Boolean; 11 | DisableCDCheckOn: Boolean; 12 | CpuUsageOn: Boolean; 13 | SocketBufferOn: Boolean; 14 | SimultaneousOn: Boolean; 15 | // Experimental 16 | bUnitsLimit: Boolean; 17 | iUnitsLimit: Word; 18 | // civ2patch 19 | civ2patchEnable: Boolean; 20 | //HostileAiOn: Boolean; 21 | RetirementYearOn: Boolean; 22 | RetirementWarningYear: Word; 23 | RetirementYear: Word; 24 | PopulationLimitOn: Boolean; 25 | PopulationLimit: Cardinal; 26 | GoldLimitOn: Boolean; 27 | GoldLimit: Cardinal; 28 | MapSizeLimitOn: Boolean; 29 | MapXLimit: Word; 30 | MapYLimit: Word; 31 | MapSizeLimit: Word; 32 | // CPU Throttling Tweaks 33 | MessagesPurgeIntervalMs: Cardinal; 34 | MessageWaitTimeMinMs: Cardinal; 35 | MessageWaitTimeMaxMs: Cardinal; 36 | MessageWaitTimeThresholdMs: Cardinal; 37 | MessageProcessingTimeThresholdMs: Cardinal; 38 | end; 39 | 40 | var 41 | Options: TUIAOptions = ( 42 | UIAEnable: True; 43 | Patch64bitOn: True; 44 | DisableCDCheckOn: True; 45 | CpuUsageOn: True; 46 | SocketBufferOn: True; 47 | SimultaneousOn: False; 48 | 49 | // Experimental 50 | bUnitsLimit: False; 51 | iUnitsLimit: 32000; // Default = 2048 52 | // civ2patch 53 | civ2patchEnable: True; 54 | 55 | //HostileAiOn: False; 56 | RetirementYearOn: False; 57 | RetirementWarningYear: 2000; // Default = 2000 58 | RetirementYear: 2020; // Default = 2020 59 | PopulationLimitOn: True; 60 | PopulationLimit: $3FFFFFFF; // Default = 32000 (0x7D00) 61 | GoldLimitOn: True; 62 | GoldLimit: $3FFFFFFF; // Default = 30000 (0x7530) 63 | MapSizeLimitOn: True; 64 | MapXLimit: $1FF; // Default = 250 (0xFA) 65 | MapYLimit: $1FF; // Default = 250 (0xFA) 66 | MapSizeLimit: $7FFF; // Default = 10000 (0x2710) 67 | // CPU Throttling Tweaks 68 | MessagesPurgeIntervalMs: 3000; 69 | MessageWaitTimeMinMs: 1; 70 | MessageWaitTimeMaxMs: 10; 71 | MessageWaitTimeThresholdMs: 250; 72 | MessageProcessingTimeThresholdMs: 50; 73 | ); 74 | UIAOPtions: PUIAOptions = Pointer($006560F0); 75 | 76 | implementation 77 | 78 | end. 79 | -------------------------------------------------------------------------------- /src/Civ2UIA_SnowFlakes.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_SnowFlakes; 2 | 3 | interface 4 | 5 | uses 6 | Classes, 7 | Civ2Types, 8 | Civ2UIA_MapMessage, 9 | Civ2UIA_MapOverlayModule, 10 | UiaPatchDrawUnit; 11 | 12 | type 13 | TSnowFlake = record 14 | Size: Integer; 15 | Xd: Double; 16 | X, Y: Integer; 17 | Vx, Vy: Integer; 18 | StopY: Integer; 19 | MapX, MapY: Integer; 20 | end; 21 | 22 | TReplacebleSprite = record 23 | Origin: PSprite; 24 | Saved: TSprite; 25 | New: TSprite; 26 | end; 27 | 28 | const 29 | MAX_FLAKES = 1000; 30 | PRECISION = 10000; 31 | type 32 | TSnowFlakes = class(TInterfacedObject, IMapOverlayModule) 33 | private 34 | FInited: Boolean; 35 | FEnabled: Boolean; 36 | FStart: Integer; 37 | FCount: Integer; 38 | FCursor: Integer; 39 | FCursorPainted: Integer; 40 | FFlakes: array[0..MAX_FLAKES - 1] of TSnowFlake; 41 | FTick: Integer; 42 | FReplacebleSprites: array[0..2] of TReplacebleSprite; 43 | procedure SetEnabled(const Value: Boolean); 44 | protected 45 | public 46 | constructor Create; 47 | destructor Destroy; override; 48 | procedure Update(); 49 | procedure Draw(DrawPort: PDrawPort); 50 | function HasSomethingToDraw(): Boolean; 51 | procedure Reset(); 52 | function IsItTime(): Boolean; 53 | procedure Switch(); 54 | procedure Init(); 55 | procedure SaveOriginalSprites(); 56 | procedure ReplaceSprites(); 57 | property Enabled: Boolean read FEnabled write SetEnabled; 58 | published 59 | end; 60 | 61 | implementation 62 | 63 | uses 64 | DateUtils, 65 | Graphics, 66 | Math, 67 | SysUtils, 68 | Windows, 69 | Civ2Proc, 70 | Civ2UIA_CanvasEx, 71 | Civ2UIA_FormConsole, 72 | Civ2UIA_Proc; 73 | 74 | procedure CoordToMap(var MapX, MapY: Integer; CoordX, CoordY: Integer); 75 | var 76 | Width, Height: Integer; 77 | X, Y, i: Integer; 78 | MX, MY: Integer; 79 | begin 80 | Width := Civ2.MapHeader.SizeX * Civ2.MapWindow.MapCellSize2.cx; 81 | Height := (Civ2.MapHeader.SizeY + 1) * Civ2.MapWindow.MapCellSize2.cy; 82 | 83 | MX := CoordX * (Civ2.MapHeader.SizeX) div PRECISION div 2 * 2; 84 | MY := CoordY * (Civ2.MapHeader.SizeY + 1) div PRECISION div 2 * 2; 85 | 86 | X := CoordX * Width div PRECISION mod Civ2.MapWindow.MapCellSize.cx; 87 | Y := CoordY * Height div PRECISION mod Civ2.MapWindow.MapCellSize.cy; 88 | i := (Civ2.DrawPort_GetPixel(@Civ2.MapWindow.DrawPortMouse, X, Y) - 10) shr 4; 89 | 90 | if (i > 0) and (i < 5) then 91 | begin 92 | MX := MX + Civ2.AdjDX[i - 1]; 93 | MY := MY + Civ2.AdjDY[i - 1]; 94 | end; 95 | //else if i > 4 then 96 | // TFormConsole.Log('X %d Y %d MX %d MY %d i %d MapX %d MapY %d', [X, Y, MX, MY, i, MapX, MapY]); 97 | MapX := Civ2.MapWrapX(MX); 98 | MapY := MY; 99 | 100 | //TFormConsole.Log('Width %d Height %d MapX %d MapY %d X %d Y %d i %d', [Width, Height, MapX, MapY, X, Y, i]); 101 | //TFormConsole.Log('MX %d MY %d i %d MapX %d MapY %d', [MX, MY, i, MapX, MapY]); 102 | 103 | end; 104 | 105 | { TSnowFlakes } 106 | 107 | constructor TSnowFlakes.Create; 108 | begin 109 | 110 | end; 111 | 112 | destructor TSnowFlakes.Destroy; 113 | begin 114 | 115 | inherited; 116 | end; 117 | 118 | procedure TSnowFlakes.Draw(DrawPort: PDrawPort); 119 | var 120 | Canvas: TCanvasEx; 121 | i: Integer; 122 | Flake: ^TSnowFlake; 123 | Width, Height: Integer; 124 | MapX, MapY: Double; 125 | WindowX, WindowY: Integer; 126 | P: TPoint; 127 | X0, Y0, X, Y, Y2: Integer; 128 | R: PRect; 129 | begin 130 | if not Enabled then 131 | Exit; 132 | //if FCursor = FCursorPainted then Exit; 133 | //TFormConsole.Log('TSnowFlakes.Draw %d %d', [Civ2.MapWindow.MapRect.Left, Civ2.MapWindow.MapRect.Top]); 134 | //TFormConsole.Log('Civ2.MapWindow.MapCellSize %d %d', [Civ2.MapWindow.MapCellSize.cx, Civ2.MapWindow.MapCellSize.cy]); 135 | //TFormConsole.Log('Civ2.MapHeader.Size %d %d', [Civ2.MapHeader.SizeX, Civ2.MapHeader.SizeY]); 136 | //TFormConsole.Log('MapRect %d %d Unk_330 %d %d', [Civ2.MapWindow.MapRect.Left, Civ2.MapWindow.MapRect.Top, Civ2.MapWindow.Unknown_330.cx, Civ2.MapWindow.Unknown_330.cy]); 137 | R := @Civ2.MapWindow.MSWindow.GraphicsInfo.DrawPort.ClientRectangle; 138 | 139 | Canvas := TCanvasEx.Create(DrawPort); 140 | 141 | // Width := R.Right - R.Left; 142 | // Height := R.Bottom - R.Top; 143 | Width := Civ2.MapHeader.SizeX * Civ2.MapWindow.MapCellSize2.cx; 144 | Height := (Civ2.MapHeader.SizeY + 1) * Civ2.MapWindow.MapCellSize2.cy; 145 | X0 := (Civ2.MapWindow.MapRect.Left + 1) * Civ2.MapWindow.MapCellSize2.cx; 146 | Y0 := (Civ2.MapWindow.MapRect.Top + 1) * Civ2.MapWindow.MapCellSize2.cy; 147 | 148 | //TFormConsole.Log('X0 %d Y0 %d', [X0, Y0]); 149 | 150 | //Canvas.Rectangle(R^); 151 | Canvas.Brush.Style := bsClear; 152 | Canvas.Font.Color := clWhite; 153 | Canvas.Font.Name := 'Times'; 154 | for i := 0 to MAX_FLAKES - 1 do 155 | begin 156 | Flake := @FFlakes[i]; 157 | if Flake.Size > 0 then 158 | begin 159 | //X := R.Left + Flake.X * Width div PRECISION; 160 | //Y := R.Top + Flake.Y * Height div PRECISION; 161 | X := Flake.X * Width div PRECISION - X0; 162 | if X < 0 then 163 | Inc(X, Width); 164 | Y := Flake.Y * Height div PRECISION - Y0; 165 | P.X := R.Left + X + Civ2.MapWindow.Unknown_330.cx; 166 | P.Y := R.Top + Y + Civ2.MapWindow.Unknown_330.cy; 167 | {MapX := Flake.X * Civ2.MapHeader.SizeX / PRECISION; 168 | MapY := Flake.Y * Civ2.MapHeader.SizeY / PRECISION; 169 | Civ2.MapWindow_MapToWindow(Civ2.MapWindow, WindowX, WindowY, Floor(MapX), Floor(MapY)); 170 | P.X := R.Left + WindowX + Floor(Frac(MapX) * Civ2.MapWindow.MapCellSize2.cx); 171 | P.Y := R.Top + WindowY + Floor(Frac(MapY) * Civ2.MapWindow.MapCellSize4.cy);} 172 | if PtInRect(R^, P) then 173 | begin 174 | if (Flake.Vy = 0) and (Flake.MapX <> -1) then 175 | if not (Civ2.MapSquareIsVisibleTo(Flake.MapX, Flake.MapY, Civ2.HumanCivIndex^) or Civ2.Game.RevealMap) then 176 | continue; 177 | 178 | Canvas.Font.Height := -(Civ2.MapWindow.MapCellSize.cy * Flake.Size div 20); 179 | Canvas.MoveTo(P.X, P.Y); 180 | Canvas.TextOutWithShadows('*', 0, 0, DT_VCENTER, R); 181 | 182 | {Canvas.Brush.Color := clWhite; 183 | Canvas.FrameRect(Rect(P.X - 1, P.Y - 1, P.X + 1, P.Y + 1));} 184 | 185 | // Canvas.TextOutRect(X, Y, '*', R); 186 | end; 187 | end; 188 | end; 189 | {Canvas.MoveTo(R.Left, R.Top); 190 | Canvas.TextOutWithShadows('TEST'); 191 | Canvas.MoveTo(R.Left + 100, R.Top); 192 | Canvas.TextOutWithShadows('TEST', 0, 0, DT_VCENTER);} 193 | 194 | Canvas.Free(); 195 | FCursorPainted := FCursor; 196 | end; 197 | 198 | function TSnowFlakes.HasSomethingToDraw: Boolean; 199 | begin 200 | Result := Enabled; 201 | end; 202 | 203 | procedure TSnowFlakes.Init; 204 | var 205 | Palette: TPalette; 206 | SpriteSheet: TDrawPort; 207 | begin 208 | if FInited then 209 | Exit; 210 | FInited := True; 211 | Civ2.Palette_Create(@Palette); 212 | Civ2.DrawPort_Init(@SpriteSheet); 213 | 214 | Civ2.HModules[Civ2.HModulesCount^] := HInstance; 215 | Inc(Civ2.HModulesCount^); 216 | // 0 217 | Civ2.DrawPort_LoadResGIFS(@SpriteSheet, MakeIntResource(30001), $A, $C0, @Palette); 218 | Civ2.Sprite_Dispose(@FReplacebleSprites[0].New); 219 | Civ2.Sprite_ExtractB(@FReplacebleSprites[0].New, @SpriteSheet, 7, 66, 1, 64, 32); 220 | Civ2.Sprite_ChangeColor(@FReplacebleSprites[0].New, 9, 7); 221 | // 1 222 | Civ2.DrawPort_LoadResGIFS(@SpriteSheet, MakeIntResource(30002), $A, $C0, @Palette); 223 | Civ2.Sprite_Dispose(@FReplacebleSprites[1].New); 224 | Civ2.Sprite_ExtractB(@FReplacebleSprites[1].New, @SpriteSheet, 7, 66, 1, 64, 48); 225 | Civ2.Sprite_ChangeColor(@FReplacebleSprites[1].New, 9, 7); 226 | // 2 227 | Civ2.Sprite_Dispose(@FReplacebleSprites[2].New); 228 | Civ2.Sprite_CopyToSprite(@FReplacebleSprites[1].New, @FReplacebleSprites[2].New); 229 | SpriteConvertToGray(@FReplacebleSprites[2].New); 230 | 231 | Civ2.DrawPort_ResetWH(@SpriteSheet, 0, 0); 232 | 233 | Dec(Civ2.HModulesCount^); 234 | Civ2.Palette_Dispose(@Palette); 235 | Enabled := IsItTime() and not FileExists('Civ2UIANoSanta.txt'); 236 | end; 237 | 238 | function TSnowFlakes.IsItTime: Boolean; 239 | var 240 | w: Word; 241 | begin 242 | w := WeekOf(Now()); 243 | Result := (w >= 51) or (w <= 3); 244 | end; 245 | 246 | procedure TSnowFlakes.ReplaceSprites; 247 | var 248 | i: Integer; 249 | begin 250 | if Enabled then 251 | begin 252 | for i := 0 to High(FReplacebleSprites) do 253 | begin 254 | Civ2.Sprite_Dispose(FReplacebleSprites[i].Origin); 255 | Civ2.Sprite_CopyToSprite(@FReplacebleSprites[i].New, FReplacebleSprites[i].Origin); 256 | end; 257 | end 258 | else 259 | begin 260 | for i := 0 to High(FReplacebleSprites) do 261 | begin 262 | Civ2.Sprite_Dispose(FReplacebleSprites[i].Origin); 263 | Civ2.Sprite_CopyToSprite(@FReplacebleSprites[i].Saved, FReplacebleSprites[i].Origin); 264 | end; 265 | end; 266 | if Civ2.MapWindow.MSWindow.GraphicsInfo.DrawPort.DrawInfo <> nil then 267 | Civ2.MapWindow_RedrawMap(Civ2.MapWindow, Civ2.HumanCivIndex^, True); 268 | end; 269 | 270 | procedure TSnowFlakes.Reset; 271 | var 272 | i: Integer; 273 | begin 274 | FCursor := 0; 275 | for i := 0 to MAX_FLAKES - 1 do 276 | FFlakes[i].Size := 0; 277 | SaveOriginalSprites(); 278 | Init(); 279 | ReplaceSprites(); 280 | end; 281 | 282 | procedure TSnowFlakes.SaveOriginalSprites; 283 | var 284 | i: Integer; 285 | begin 286 | FReplacebleSprites[0].Origin := PSprite($00646158); 287 | FReplacebleSprites[1].Origin := @Civ2.SprUnits[49]; 288 | FReplacebleSprites[2].Origin := @UnitSpriteSentry[49]; 289 | for i := 0 to High(FReplacebleSprites) do 290 | begin 291 | Civ2.Sprite_Dispose(@FReplacebleSprites[i].Saved); 292 | Civ2.Sprite_CopyToSprite(FReplacebleSprites[i].Origin, @FReplacebleSprites[i].Saved); 293 | end; 294 | end; 295 | 296 | procedure TSnowFlakes.SetEnabled(const Value: Boolean); 297 | begin 298 | if Value <> FEnabled then 299 | begin 300 | FEnabled := Value; 301 | ReplaceSprites(); 302 | end; 303 | end; 304 | 305 | procedure TSnowFlakes.Switch; 306 | begin 307 | Enabled := not Enabled; 308 | end; 309 | 310 | procedure TSnowFlakes.Update; 311 | var 312 | i: Integer; 313 | Flake: ^TSnowFlake; 314 | Square: PMapSquare; 315 | begin 316 | Init(); 317 | if not Enabled then 318 | Exit; 319 | //TFormConsole.Log('TSnowFlakes.Update'); 320 | Flake := @FFlakes[FCursor]; 321 | Flake.MapX := -1; 322 | Flake.Mapy := -1; 323 | Flake.Size := Random(8) + 12; 324 | Flake.X := Random(PRECISION); 325 | Flake.Vx := Random(5) - 2; 326 | Flake.Vy := Random(5) + 1; 327 | Flake.StopY := Random(PRECISION); 328 | 329 | //Flake.StopY := PRECISION * 10 div (Civ2.MapHeader.SizeY + 1); 330 | {Flake.StopY := FTick mod PRECISION; 331 | Flake.X := FTick mod PRECISION; 332 | Flake.Vx := 0;} 333 | 334 | {Flake.X := 5; 335 | Flake.Vx := -2;} 336 | 337 | Flake.Y := Flake.StopY - PRECISION div 10; 338 | 339 | for i := FCursor to FCursor + MAX_FLAKES - 1 do 340 | begin 341 | Flake := @FFlakes[i mod MAX_FLAKES]; 342 | if (Flake.Size > 0) and (Flake.Vy > 0) then 343 | begin 344 | Inc(Flake.Y, Flake.Vy * 5); 345 | if Flake.Y >= Flake.StopY then 346 | begin 347 | // Landed 348 | Flake.Y := Flake.StopY; 349 | Flake.Vy := 0; 350 | CoordToMap(Flake.MapX, Flake.MapY, Flake.X, Flake.Y); 351 | if Civ2.MapTerrainIsOcean(Flake.MapX, Flake.MapY) then 352 | Flake.Size := 0; 353 | end 354 | else 355 | begin 356 | Inc(Flake.X, Flake.Vx); 357 | if Flake.X < 0 then 358 | Inc(Flake.X, PRECISION) 359 | else if Flake.X >= PRECISION then 360 | Dec(Flake.X, PRECISION); 361 | end; 362 | end; 363 | end; 364 | 365 | if FCursor < MAX_FLAKES - 1 then 366 | Inc(FCursor) 367 | else 368 | FCursor := 0; 369 | 370 | //Inc(FTick, 10); 371 | end; 372 | 373 | end. 374 | -------------------------------------------------------------------------------- /src/Civ2UIA_SortedAbstractList.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_SortedAbstractList; 2 | 3 | interface 4 | 5 | uses 6 | Classes; 7 | 8 | type 9 | TSortedAbstractList = class 10 | private 11 | FBaseAddress: Pointer; 12 | FSizeOfElement: Integer; 13 | FCursorLimit: Integer; 14 | function GetCount: Integer; 15 | function GetItem(Index: Integer): Pointer; 16 | procedure SetItem(Index: Integer; const Value: Pointer); 17 | 18 | protected 19 | FList: TList; 20 | 21 | public 22 | Cursor: Integer; 23 | constructor Create(BaseAddress: Pointer; SizeOfElement, CursorLimit: Integer); virtual; 24 | destructor Destroy; override; 25 | function GetIndexIndex(Index: Integer): Integer; 26 | function GetNextIndex(CursorIncrement: Integer): Integer; 27 | procedure Pack(); 28 | property Count: Integer read GetCount; 29 | property Items[Index: Integer]: Pointer read GetItem write SetItem; default; 30 | published 31 | end; 32 | 33 | implementation 34 | 35 | { TSortedAbstractList } 36 | 37 | constructor TSortedAbstractList.Create(BaseAddress: Pointer; SizeOfElement, CursorLimit: Integer); 38 | begin 39 | FList := TList.Create(); 40 | FBaseAddress := BaseAddress; 41 | FSizeOfElement := SizeOfElement; 42 | FCursorLimit := CursorLimit; 43 | end; 44 | 45 | destructor TSortedAbstractList.Destroy; 46 | begin 47 | FList.Free(); 48 | inherited; 49 | end; 50 | 51 | function TSortedAbstractList.GetCount: Integer; 52 | begin 53 | Result := Flist.Count; 54 | end; 55 | 56 | function TSortedAbstractList.GetItem(Index: Integer): Pointer; 57 | begin 58 | Result := FList[Index]; 59 | end; 60 | 61 | procedure TSortedAbstractList.SetItem(Index: Integer; const Value: Pointer); 62 | begin 63 | FList[Index] := Value; 64 | end; 65 | 66 | function TSortedAbstractList.GetIndexIndex(Index: Integer): Integer; 67 | begin 68 | Result := (Integer(FList[Index]) - Integer(FBaseAddress)) div FSizeOfElement; 69 | end; 70 | 71 | function TSortedAbstractList.GetNextIndex(CursorIncrement: Integer): Integer; 72 | begin 73 | Cursor := Cursor + CursorIncrement; 74 | if Cursor >= FList.Count then 75 | Result := FCursorLimit 76 | else 77 | Result := GetIndexIndex(Cursor); 78 | end; 79 | 80 | procedure TSortedAbstractList.Pack; 81 | begin 82 | FList.Pack(); 83 | end; 84 | 85 | end. 86 | 87 | -------------------------------------------------------------------------------- /src/Civ2UIA_SortedCitiesList.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_SortedCitiesList; 2 | 3 | interface 4 | 5 | uses 6 | Classes, 7 | Civ2UIA_SortedAbstractList; 8 | 9 | type 10 | TSortedCitiesList = class(TSortedAbstractList) 11 | public 12 | constructor Create(CivIndex, SortCriteria: Integer); reintroduce; 13 | published 14 | end; 15 | 16 | implementation 17 | 18 | uses 19 | Math, 20 | SysUtils, 21 | Civ2Types, 22 | Civ2Proc; 23 | 24 | var 25 | CitiesSortCriteria: Integer; 26 | 27 | function CompareCities(Item1, Item2: Pointer): Integer; 28 | var 29 | Cities: array[1..2] of PCity; 30 | Building1, Building2: Integer; 31 | begin 32 | Result := 0; 33 | Cities[1] := PCity(Item1); 34 | Cities[2] := PCity(Item2); 35 | case Abs(CitiesSortCriteria) of 36 | 1: 37 | Result := Cities[1].Size - Cities[2].Size; 38 | 2: 39 | Result := StrComp(Cities[1].Name, Cities[2].Name); 40 | 3: 41 | Result := Cities[1].TotalFood - Cities[2].TotalFood; 42 | 4: 43 | Result := Cities[1].TotalShield - Cities[2].TotalShield; 44 | 5: 45 | Result := Cities[1].Trade - Cities[2].Trade; 46 | 6: 47 | begin 48 | Building1 := Cities[1].Building; 49 | Building2 := Cities[2].Building; 50 | if Building1 < 0 then 51 | Building1 := 100 - Building1; 52 | if Building2 < 0 then 53 | Building2 := 100 - Building2; 54 | Result := Building1 - Building2; 55 | end; 56 | end; 57 | Result := Result * Sign(CitiesSortCriteria); 58 | end; 59 | 60 | { TSortedCitiesList } 61 | 62 | constructor TSortedCitiesList.Create(CivIndex, SortCriteria: Integer); 63 | var 64 | i: Integer; 65 | begin 66 | inherited Create(Civ2.Cities, SizeOf(TCity), Civ2.Game.TotalCities); 67 | CitiesSortCriteria := SortCriteria; 68 | for i := 0 to Civ2.Game.TotalCities - 1 do 69 | begin 70 | if (Civ2.Cities[i].ID <> 0) and (Civ2.Cities[i].Owner = CivIndex) then 71 | begin 72 | FList.Add(@Civ2.Cities[i]); 73 | end; 74 | end; 75 | if CitiesSortCriteria <> 0 then 76 | FList.Sort(@CompareCities); 77 | end; 78 | 79 | end. 80 | 81 | -------------------------------------------------------------------------------- /src/Civ2UIA_SortedUnitsList.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_SortedUnitsList; 2 | 3 | interface 4 | 5 | uses 6 | Classes, 7 | Types, 8 | Civ2UIA_SortedAbstractList; 9 | 10 | type 11 | TSortedUnitsList = class(TSortedAbstractList) 12 | TypesCount: array[0..61] of Integer; 13 | public 14 | constructor Create(CityIndex: Integer; Unique: Boolean = False); reintroduce; overload; 15 | constructor Create(MapPoint: TPoint; Unique: Boolean = False); reintroduce; overload; 16 | procedure Distinct(); 17 | published 18 | end; 19 | 20 | implementation 21 | 22 | uses 23 | Windows, 24 | Civ2Types, 25 | Civ2Proc; 26 | 27 | function CompareUnits(Item1, Item2: Pointer): Integer; 28 | var 29 | Units: array[1..2] of PUnit; 30 | i: Integer; 31 | Weights: array[1..2] of Integer; 32 | UnitType: TUnitType; 33 | begin 34 | Units[1] := PUnit(Item1); 35 | Units[2] := PUnit(Item2); 36 | for i := 1 to 2 do 37 | begin 38 | UnitType := Civ2.UnitTypes[Units[i]^.UnitType]; 39 | if UnitType.Role = 5 then 40 | Weights[i] := $00100000 * (Units[i]^.UnitType + 1) 41 | else if UnitType.Att > 0 then 42 | Weights[i] := UnitType.Def * $100 + ($F - UnitType.Domain) * $10000 + UnitType.Att 43 | else 44 | Weights[i] := 0; 45 | end; 46 | Result := Weights[2] - Weights[1]; 47 | if Result = 0 then 48 | Result := Units[2]^.ID - Units[1]^.ID 49 | end; 50 | 51 | { TSortedUnitsList } 52 | 53 | constructor TSortedUnitsList.Create(CityIndex: Integer; Unique: Boolean); 54 | var 55 | i: Integer; 56 | begin 57 | inherited Create(Civ2.Units, SizeOf(TUnit), Civ2.Game.TotalUnits); 58 | for i := 0 to Civ2.Game.TotalUnits - 1 do 59 | begin 60 | if (Civ2.Units[i].ID > 0) and (Civ2.Units[i].HomeCity = CityIndex) then 61 | begin 62 | FList.Add(@Civ2.Units[i]); 63 | end; 64 | end; 65 | FList.Sort(@CompareUnits); 66 | if Unique then 67 | Distinct(); 68 | end; 69 | 70 | constructor TSortedUnitsList.Create(MapPoint: TPoint; Unique: Boolean); 71 | var 72 | i: Integer; 73 | begin 74 | inherited Create(Civ2.Units, SizeOf(TUnit), Civ2.Game.TotalUnits); 75 | for i := 0 to Civ2.Game.TotalUnits - 1 do 76 | begin 77 | if (Civ2.Units[i].ID > 0) and (Civ2.Units[i].X = MapPoint.X) and (Civ2.Units[i].Y = MapPoint.Y) then 78 | begin 79 | FList.Add(@Civ2.Units[i]); 80 | end; 81 | end; 82 | FList.Sort(@CompareUnits); 83 | if Unique then 84 | Distinct(); 85 | end; 86 | 87 | procedure TSortedUnitsList.Distinct; 88 | var 89 | i: Integer; 90 | UnitType: Integer; 91 | begin 92 | ZeroMemory(@TypesCount, SizeOf(TypesCount)); 93 | for i := 0 to FList.Count - 1 do 94 | begin 95 | UnitType := PUnit(FList[i]).UnitType; 96 | Inc(TypesCount[UnitType]); 97 | if TypesCount[UnitType] > 1 then 98 | begin 99 | FList[i] := nil; 100 | end; 101 | end; 102 | FList.Pack(); 103 | end; 104 | 105 | end. 106 | 107 | -------------------------------------------------------------------------------- /src/Civ2UIA_Types.pas: -------------------------------------------------------------------------------- 1 | unit Civ2UIA_Types; 2 | 3 | interface 4 | 5 | type 6 | PCallerChain = ^TCallerChain; 7 | 8 | TCallerChain = packed record 9 | Prev: PCallerChain; 10 | Caller: Pointer; 11 | end; 12 | 13 | implementation 14 | 15 | end. 16 | 17 | -------------------------------------------------------------------------------- /src/FileInfo.pas: -------------------------------------------------------------------------------- 1 | unit FileInfo; 2 | 3 | interface 4 | 5 | function CurrentFileInfo(NameApp: string): string; 6 | 7 | implementation 8 | 9 | uses 10 | SysUtils, 11 | Windows; 12 | 13 | function CurrentFileInfo(NameApp: string): string; 14 | var 15 | dump: Cardinal; 16 | size: Integer; 17 | buffer: PChar; 18 | VersionPointer, TransBuffer: PChar; 19 | Temp: Integer; 20 | CalcLangCharSet: string; 21 | begin 22 | size := GetFileVersionInfoSize(PChar(NameApp), dump); 23 | buffer := StrAlloc(size + 1); 24 | try 25 | GetFileVersionInfo(PChar(NameApp), 0, size, buffer); 26 | 27 | VerQueryValue(buffer, '\VarFileInfo\Translation', Pointer(TransBuffer), dump); 28 | if dump >= 4 then 29 | begin 30 | Temp := 0; 31 | StrLCopy(@Temp, TransBuffer, 2); 32 | CalcLangCharSet := IntToHex(Temp, 4); 33 | StrLCopy(@Temp, TransBuffer + 2, 2); 34 | CalcLangCharSet := CalcLangCharSet + IntToHex(Temp, 4); 35 | end; 36 | 37 | VerQueryValue(buffer, PChar('\StringFileInfo\' + CalcLangCharSet + '\' + 'FileVersion'), Pointer(VersionPointer), dump); 38 | if (dump > 1) then 39 | begin 40 | SetLength(Result, dump); 41 | StrLCopy(PChar(Result), VersionPointer, dump); 42 | Result := Trim(Result); 43 | end 44 | else 45 | Result := '0.0.0.0'; 46 | finally 47 | StrDispose(buffer); 48 | end; 49 | end; 50 | 51 | end. 52 | -------------------------------------------------------------------------------- /src/GIFS/105.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/GIFS/105.gif -------------------------------------------------------------------------------- /src/GIFS/229.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/GIFS/229.gif -------------------------------------------------------------------------------- /src/GIFS/250.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/GIFS/250.gif -------------------------------------------------------------------------------- /src/GIFS/30001.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/GIFS/30001.gif -------------------------------------------------------------------------------- /src/GIFS/30002.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoxAhead/Civ2-UI-Additions/76a2618f43ef6ba185515742a44677325f5bb76d/src/GIFS/30002.gif -------------------------------------------------------------------------------- /src/MemWatchDog.pas: -------------------------------------------------------------------------------- 1 | unit MemWatchDog; 2 | 3 | interface 4 | 5 | uses 6 | Classes; 7 | 8 | type 9 | TMemWatchDog = class 10 | private 11 | FAddrLow: Cardinal; 12 | FAddrHigh: Cardinal; 13 | FDoException: Boolean; 14 | FPatchedMem: array of Byte; 15 | FLog: TStringList; 16 | procedure AddrToByteAndBit(Addr: Cardinal; var ByteN, BitN: Integer); 17 | procedure Log(const F: string; const A: array of const); 18 | protected 19 | public 20 | constructor Create(AddrLow, AddrHigh: Cardinal; DoLog, DoException: Boolean); 21 | destructor Destroy; override; 22 | procedure MarkAddr(Addr: Cardinal; Count: Cardinal); 23 | published 24 | end; 25 | 26 | implementation 27 | 28 | uses 29 | SysUtils, 30 | Math; 31 | 32 | { TMemWatcDog } 33 | 34 | procedure TMemWatchDog.AddrToByteAndBit(Addr: Cardinal; var ByteN, BitN: Integer); 35 | var 36 | a: Integer; 37 | begin 38 | a := Addr - FAddrLow; 39 | ByteN := a div 8; 40 | BitN := a mod 8; 41 | end; 42 | 43 | constructor TMemWatchDog.Create(AddrLow, AddrHigh: Cardinal; DoLog, DoException: Boolean); 44 | var 45 | ArrHigh, BitN: Integer; 46 | ArraySize: Integer; 47 | begin 48 | FAddrLow := AddrLow; 49 | FAddrHigh := AddrHigh; 50 | FDoException := DoException; 51 | AddrToByteAndBit(AddrHigh, ArrHigh, BitN); 52 | SetLength(FPatchedMem, ArrHigh + 1); 53 | if DoLog then 54 | FLog := TStringList.Create(); 55 | end; 56 | 57 | destructor TMemWatchDog.Destroy; 58 | begin 59 | SetLength(FPatchedMem, 0); 60 | if FLog <> nil then 61 | begin 62 | FLog.SaveToFile('Civ2UIAMemWatchDog.log'); 63 | FLog.Free(); 64 | end; 65 | inherited; 66 | end; 67 | 68 | procedure TMemWatchDog.Log(const F: string; const A: array of const); 69 | begin 70 | if FLog <> nil then 71 | FLog.Add(Format(F, A)); 72 | end; 73 | 74 | procedure TMemWatchDog.MarkAddr(Addr: Cardinal; Count: Cardinal); 75 | var 76 | i, i1, i2: Integer; 77 | ByteN, BitN: Integer; 78 | Mask: Byte; 79 | OldByte, NewByte: Byte; 80 | begin 81 | i1 := Max(Addr, FAddrLow); 82 | i2 := Min(Addr + Count - 1, FAddrHigh); 83 | for i := i1 to i2 do 84 | begin 85 | AddrToByteAndBit(i, ByteN, BitN); 86 | Mask := 1 shl BitN; 87 | OldByte := FPatchedMem[ByteN]; 88 | NewByte := OldByte or Mask; 89 | Log('%x: ByteN=%d, BitN=%d, %x -> %x', [i, ByteN, BitN, OldByte, NewByte]); 90 | if OldByte = NewByte then 91 | begin 92 | Log('Memory 0x%x already patched (from %x, count %d)', [i, Addr, Count]); 93 | if FDoException then 94 | raise Exception.CreateFmt('Memory 0x%x already patched (from %x, count %d)', [i, Addr, Count]); 95 | end; 96 | FPatchedMem[ByteN] := NewByte; 97 | end; 98 | end; 99 | 100 | end. 101 | -------------------------------------------------------------------------------- /src/Patches/UiaPatch.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatch; 2 | 3 | interface 4 | 5 | uses 6 | Civ2UIA_Options; 7 | 8 | type 9 | TUiaPatch = class 10 | private 11 | protected 12 | procedure WriteMemory(HProcess: THandle; Address: Integer; Opcodes: array of Byte; ProcAddress: Pointer = nil; Abs: Boolean = False); 13 | function UIAOptions(): PUIAOptions; 14 | public 15 | class procedure RegisterMe(); 16 | function Active(): Boolean; virtual; 17 | procedure Attach(HProcess: Cardinal); virtual; abstract; 18 | end; 19 | 20 | const 21 | OP_NOP: Byte = $90; 22 | OP_CALL: Byte = $E8; 23 | OP_JMP: Byte = $E9; 24 | OP_0F: Byte = $0F; 25 | OP_JZ: Byte = $84; 26 | OP_JG: Byte = $8F; 27 | OP_RET: Byte = $C3; 28 | 29 | implementation 30 | 31 | uses 32 | Windows, 33 | UiaMain; 34 | 35 | { TUiaPatch } 36 | 37 | function TUiaPatch.Active: Boolean; 38 | begin 39 | Result := UIAOPtions.UIAEnable; 40 | end; 41 | 42 | class procedure TUiaPatch.RegisterMe; 43 | begin 44 | Uia.RegisterPatch(Self); 45 | end; 46 | 47 | function TUiaPatch.UIAOptions: PUIAOptions; 48 | begin 49 | Result := Civ2UIA_Options.UIAOPtions; 50 | end; 51 | 52 | procedure TUiaPatch.WriteMemory(HProcess: THandle; Address: Integer; Opcodes: array of Byte; ProcAddress: Pointer; Abs: Boolean); 53 | var 54 | SizeOP: Integer; 55 | BytesWritten: Cardinal; 56 | Offset: Integer; 57 | begin 58 | SizeOP := SizeOf(Opcodes); 59 | if SizeOP > 0 then begin 60 | WriteProcessMemory(HProcess, Pointer(Address), @Opcodes, SizeOP, BytesWritten); 61 | Uia.MemWatchDog.MarkAddr(Address, SizeOP); 62 | end; 63 | if ProcAddress <> nil then 64 | begin 65 | Offset := Integer(ProcAddress); 66 | if not Abs then 67 | Offset := Offset - Address - 4 - SizeOP; 68 | WriteProcessMemory(HProcess, Pointer(Address + SizeOP), @Offset, 4, BytesWritten); 69 | Uia.MemWatchDog.MarkAddr(Address + SizeOP, 4); 70 | end; 71 | end; 72 | 73 | end. 74 | -------------------------------------------------------------------------------- /src/Patches/UiaPatch64Bit.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatch64Bit; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatch64Bit = class(TUiaPatch) 10 | public 11 | function Active(): Boolean; override; 12 | procedure Attach(HProcess: Cardinal); override; 13 | end; 14 | 15 | implementation 16 | 17 | uses 18 | Windows; 19 | 20 | procedure PatchEditBox64Bit(); register; 21 | asm 22 | push GCL_CBWNDEXTRA 23 | mov eax, [ebp + $08] 24 | push eax 25 | call [$006E7E9C] // GetClassLongA 26 | mov ebx, eax 27 | sub al, 4 28 | push eax 29 | mov eax, [ebp + $08] 30 | push eax 31 | call [$006E7E2C] // GetWindowLongA 32 | sub ebx, 8 33 | mov [ebp - $08], eax 34 | mov eax, [ebp - $0C] 35 | push ebx 36 | mov eax, [ebp + $08] 37 | push eax 38 | call [$006E7E2C] // GetWindowLongA 39 | mov [ebp - $14], eax 40 | mov eax, [ebp + $0C] 41 | mov [ebp - $1C], eax 42 | push $005D2C94 43 | ret 44 | end; 45 | 46 | { TUiaPatch64Bit } 47 | 48 | function TUiaPatch64Bit.Active: Boolean; 49 | begin 50 | Result := UIAOptions().Patch64bitOn; 51 | end; 52 | 53 | procedure TUiaPatch64Bit.Attach(HProcess: Cardinal); 54 | begin 55 | WriteMemory(HProcess, $005D2A0A, [OP_JMP], @PatchEditBox64Bit); 56 | end; 57 | 58 | initialization 59 | TUiaPatch64Bit.RegisterMe(); 60 | 61 | end. 62 | 63 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchAI.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchAI; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchAI = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | procedure PatchAIAttitude(); register; 17 | asm 18 | mov [ebp - $30], 0 // Fix: Initialize variable 19 | mov eax, $004031CF // Restore overwritten call to j_Q_CalcViolation_sub_55BBC0 20 | call eax 21 | push $00560DAB 22 | ret 23 | end; 24 | 25 | {procedure C2PatchHostileAi(HProcess: THandle); 26 | begin 27 | WriteMemory(HProcess, $00561FC9, [$90, $90, $90, $90, $90, $90, $90, $90]); 28 | end;} 29 | 30 | { TUiaPatchAI } 31 | 32 | procedure TUiaPatchAI.Attach(HProcess: Cardinal); 33 | begin 34 | // Patch AI Attitude: initialize variable int vDeltaAttitude [ebp-30h] with 0 35 | WriteMemory(HProcess, $00560DA6, [OP_JMP], @PatchAIAttitude); 36 | 37 | // Old variant - Just completely disables call to j_Q_ChangeAttitude_sub_456F20 38 | {if UIAOPtions^.HostileAiOn then 39 | C2PatchHostileAi(HProcess);} 40 | end; 41 | 42 | initialization 43 | TUiaPatchAI.RegisterMe(); 44 | 45 | end. 46 | 47 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchArrangeWindows.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchArrangeWindows; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchArrangeWindows = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | var 15 | ArrangeWindowMiniMapWidth: Integer = -1; 16 | 17 | implementation 18 | 19 | uses 20 | Math, 21 | Windows, 22 | Civ2Proc, 23 | Civ2UIA_Proc; 24 | 25 | procedure PatchSetWinRectMiniMapEx(var Width, Height: Integer); stdcall; 26 | var 27 | Scale: Integer; 28 | MaxScale: Integer; 29 | R: TRect; 30 | begin 31 | if ArrangeWindowMiniMapWidth > 0 then 32 | begin 33 | Width := ArrangeWindowMiniMapWidth; 34 | GetClientRect(Civ2.MainWindowInfo.WindowStructure.HWindow, R); 35 | MaxScale := RectHeight(R) div 5 div Civ2.MapHeader.SizeY; 36 | Scale := Max(Min(Width div Civ2.MapHeader.SizeX, MaxScale), 1); 37 | Height := Scale * Civ2.MapHeader.SizeY; 38 | end; 39 | end; 40 | 41 | procedure PatchSetWinRectMiniMap(); register; 42 | asm 43 | lea eax, [ebp - $14] // int vHeight 44 | push eax 45 | lea eax, [ebp - $10] // int vWidth 46 | push eax 47 | call PatchSetWinRectMiniMapEx 48 | mov eax, [$0063359C] // int V_CaptionLeft_dword_63359C 49 | push $004078DC 50 | ret 51 | end; 52 | 53 | { TUiaPatchArrangeWindows } 54 | 55 | procedure TUiaPatchArrangeWindows.Attach(HProcess: Cardinal); 56 | begin 57 | // Arrange windows 58 | WriteMemory(HProcess, $004078D7, [OP_JMP], @PatchSetWinRectMiniMap); 59 | 60 | end; 61 | 62 | initialization 63 | TUiaPatchArrangeWindows.RegisterMe(); 64 | 65 | end. 66 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchAttitudeAdvisor.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchAttitudeAdvisor; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchAttitudeAdvisor = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | Civ2Types, 18 | Civ2Proc, 19 | Civ2UIA_CanvasEx; 20 | 21 | {procedure PatchUpdateAdvisorAttitudeEx(ScrollPosition, Page, A20: Integer; var Top1: Integer); stdcall; 22 | var 23 | CivIndex: Integer; 24 | i, j: Integer; 25 | City: PCity; 26 | begin 27 | CivIndex := Civ2.AdvisorWindow.CurrCivIndex; 28 | j := 0; 29 | for i := 0 to Civ2.Game.TotalCities - 1 do 30 | begin 31 | City := @Civ2.Cities[i]; 32 | if (City.ID <> 0) and (City.Owner = CivIndex) and (j >= ScrollPosition) and (Page + ScrollPosition > j - 1) then 33 | begin 34 | Inc(j); 35 | if (A20 <> 0) and (Civ2.Civs[CivIndex].Government >= 5) then 36 | Civ2.CalcCityGlobals(i, True); 37 | Civ2.DrawCitySprite(@Civ2.AdvisorWindow.MSWindow.GraphicsInfo.DrawPort, i, 0, Civ2.AdvisorWindow.MSWindow.ClientTopLeft.X + ((j and 1) shl 6) + 2, Top1, 0); 38 | Civ2.SetCurrFont(Pointer($0063EAB8)); 39 | Civ2.SetFontColorWithShadow($25, $12, 1, 1); 40 | // Etc... May be... TODO 41 | Top1 := Top1 + 32; 42 | end; 43 | end; 44 | end; 45 | 46 | procedure PatchUpdateAdvisorAttitude(); register; 47 | asm 48 | lea eax, [ebp - $4C] // int vTop1 49 | push eax 50 | push [ebp - $2C] // int v20 51 | push [ebp - $4] // int vPage 52 | push [ebp - $40] // int vScrollPosition 53 | call PatchUpdateAdvisorAttitudeEx 54 | push $0042DF7B 55 | ret 56 | end;} 57 | 58 | procedure PatchUpdateAdvisorAttitudeLineEx(CityIndex: Integer; var Top1: Integer); stdcall; 59 | var 60 | Canvas: TCanvasEx; 61 | SavedCityGlobals: TCityGlobals; 62 | FoodDelta: Integer; 63 | begin 64 | SavedCityGlobals := Civ2.CityGlobals^; 65 | Civ2.CalcCityGlobals(CityIndex, True); 66 | FoodDelta := Civ2.CityGlobals.TotalRes[0] - Civ2.CityGlobals.SettlersEat * Civ2.CityGlobals.Settlers - Civ2.Cosmic.CitizenEats * Civ2.Cities[CityIndex].Size; 67 | if FoodDelta <> 0 then 68 | begin 69 | Canvas := TCanvasEx.Create(@Civ2.AdvisorWindow.MSWindow.GraphicsInfo.DrawPort); 70 | Canvas.MoveTo(Civ2.AdvisorWindow.MSWindow.ClientTopLeft.X + $8C - 16, Top1 + 11); 71 | if FoodDelta > 0 then 72 | Canvas.CopySprite(@Civ2.SprRes[1]) 73 | else 74 | Canvas.CopySprite(@Civ2.SprRes[0]); 75 | Canvas.Free(); 76 | end; 77 | Civ2.CityGlobals^ := SavedCityGlobals; 78 | Top1 := Top1 + Civ2.AdvisorWindow.LineHeight; 79 | end; 80 | 81 | procedure PatchUpdateAdvisorAttitudeLine(); register; 82 | asm 83 | lea eax, [ebp - $4C] // int vTop1 84 | push eax 85 | push [ebp - $30] // int i 86 | call PatchUpdateAdvisorAttitudeLineEx 87 | push $0042DD16 88 | ret 89 | end; 90 | 91 | { TUiaPatchAttitudeAdvisor } 92 | 93 | procedure TUiaPatchAttitudeAdvisor.Attach(HProcess: Cardinal); 94 | begin 95 | // Celebrating city yellow color instead of white in Attitude Advisor (F4) 96 | WriteMemory(HProcess, $0042DE86, [$72]); // 97 | 98 | // Enhanced Attitude Advisor 99 | //WriteMemory(HProcess, $0042DD0A, [OP_JMP], @PatchUpdateAdvisorAttitude); 100 | WriteMemory(HProcess, $0042DF70, [OP_JMP], @PatchUpdateAdvisorAttitudeLine); 101 | 102 | end; 103 | 104 | initialization 105 | TUiaPatchAttitudeAdvisor.RegisterMe(); 106 | 107 | end. 108 | 109 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchCDCheck.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchCDCheck; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchCDCheck = class(TUiaPatch) 10 | public 11 | function Active(): Boolean; override; 12 | procedure Attach(HProcess: Cardinal); override; 13 | end; 14 | 15 | implementation 16 | 17 | uses 18 | Civ2Proc, 19 | SysUtils, 20 | Windows, 21 | Civ2UIA_FormConsole; 22 | 23 | function TryCDRootFind(CheckFileName: PChar; DriveType: Cardinal): PChar; 24 | var 25 | PrevMode, LogicalDrives: Cardinal; 26 | i: Integer; 27 | Buffer: array[0..255] of Char; 28 | RootPathName: PChar; 29 | ReOpenBuff: TOFStruct; 30 | begin 31 | PrevMode := SetErrorMode(SEM_FAILCRITICALERRORS); 32 | LogicalDrives := GetLogicalDrives(); 33 | GetLogicalDriveStrings($100, Buffer); 34 | RootPathName := Buffer; 35 | for i := 0 to 25 do 36 | begin 37 | if ((1 shl i) and LogicalDrives) <> 0 then 38 | begin 39 | if GetDriveType(RootPathName) = DriveType then 40 | begin 41 | StrCopy(Civ2.ChText, RootPathName); 42 | StrCat(Civ2.ChText, CheckFileName); 43 | //TFormConsole.Log('TryCDRootFind: ' + RootPathName); 44 | if OpenFile(Civ2.ChText, ReOpenBuff, OF_EXIST) <> HFILE_ERROR then 45 | begin 46 | Civ2.CDRoot[0] := RootPathName[0]; 47 | Civ2.CDRoot[1] := #0; 48 | StrCat(Civ2.CDRoot, ':\'); 49 | //TFormConsole.Log('Found!'); 50 | Break; 51 | end; 52 | end; 53 | while RootPathName[0] <> #0 do 54 | begin 55 | Inc(RootPathName); 56 | end; 57 | Inc(RootPathName); 58 | end; 59 | end; 60 | SetErrorMode(PrevMode); 61 | if Civ2.CDRoot[0] <> #0 then 62 | Result := Civ2.CDRoot 63 | else 64 | Result := nil; 65 | end; 66 | 67 | function PatchCDRootFindAuto(CheckFileName: PChar): PChar; cdecl; 68 | begin 69 | //TFormConsole.Log('ModuleFileName: ' + Civ2.Path); 70 | Result := TryCDRootFind(CheckFileName, DRIVE_CDROM); 71 | if Result = nil then 72 | begin 73 | Civ2.CDRoot := '.\'; 74 | Result := '.\'; 75 | end; 76 | TFormConsole.Log('CDRoot: ' + Civ2.CDRoot); 77 | end; 78 | 79 | { TUiaPatchCDCheck } 80 | 81 | function TUiaPatchCDCheck.Active: Boolean; 82 | begin 83 | Result := UIAOPtions.DisableCDCheckOn; 84 | end; 85 | 86 | procedure TUiaPatchCDCheck.Attach(HProcess: Cardinal); 87 | begin 88 | // Auto CDCheck 89 | WriteMemory(HProcess, $00402BE9, [OP_JMP], @PatchCDRootFindAuto); 90 | // Old variant 91 | {WriteMemory(HProcess, $0056463C, [$03]); 92 | WriteMemory(HProcess, $0056467A, [$EB, $12]); 93 | WriteMemory(HProcess, $005646A7, [$80]);} 94 | end; 95 | 96 | initialization 97 | TUiaPatchCDCheck.RegisterMe(); 98 | 99 | end. 100 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchCPUUsage.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchCPUUsage; 2 | 3 | { 4 | civ2patch 5 | https://github.com/vinceho/civ2patch 6 | } 7 | 8 | interface 9 | 10 | uses 11 | UiaPatch; 12 | 13 | type 14 | TUiaPatchCPUUsage = class(TUiaPatch) 15 | public 16 | function Active(): Boolean; override; 17 | procedure Attach(HProcess: Cardinal); override; 18 | end; 19 | 20 | implementation 21 | 22 | uses 23 | Math, 24 | MMSystem, 25 | Windows; 26 | 27 | var 28 | g_dwMessageWaitTime: DWORD = 1; 29 | g_dwMessageWaitTimeMin: DWORD = 1; 30 | g_dwMessageWaitTimeMax: DWORD = 10; 31 | g_dwMessageWaitTimeInc: DWORD = 1; 32 | g_dMessageWaitTimeThreshold: Double = 250000.0; 33 | g_dMessageProcessingTimeThreshold: Double = 50000.0; 34 | g_dMessagesPurgeInterval: Double = 3000000.0; 35 | g_dLastMessagePurgeTime: Double = 0.0; 36 | g_dBeginSleepTime: Double = 0.0; 37 | g_dBeginWorkTime: Double = 0.0; 38 | g_dTimerFrequency: Double; 39 | g_dTimerStart: Double; 40 | g_bTimerHighResolution: Boolean = False; 41 | 42 | procedure C2PatchInitializeOptions(MessagesPurgeIntervalMs, MessageProcessingTimeThresholdMs, MessageWaitTimeThresholdMs, MessageWaitTimeMinMs, MessageWaitTimeMaxMs: Cardinal); 43 | begin 44 | g_dMessagesPurgeInterval := MessagesPurgeIntervalMs * 1000.0; 45 | g_dMessageProcessingTimeThreshold := MessageProcessingTimeThresholdMs * 1000.0; 46 | g_dMessageWaitTimeThreshold := MessageWaitTimeThresholdMs * 1000.0; 47 | g_dwMessageWaitTimeMin := MessageWaitTimeMinMs; 48 | g_dwMessageWaitTimeMax := MessageWaitTimeMaxMs; 49 | g_dwMessageWaitTime := g_dwMessageWaitTimeMin; 50 | g_dwMessageWaitTimeInc := Max(1, Trunc((g_dwMessageWaitTimeMax - g_dwMessageWaitTimeMin) / 10)); 51 | end; 52 | 53 | function C2PatchInitializeTimer(): Boolean; 54 | var 55 | timerFrequency: TLargeInteger; 56 | timerCounter: TLargeInteger; 57 | begin 58 | if not QueryPerformanceFrequency(timerFrequency) then 59 | begin 60 | g_bTimerHighResolution := False; 61 | g_dTimerStart := timeGetTime() * 1000; 62 | end 63 | else 64 | begin 65 | g_bTimerHighResolution := True; 66 | g_dTimerFrequency := LARGE_INTEGER(timerFrequency).QuadPart / 1000000.0; 67 | QueryPerformanceCounter(timerCounter); 68 | g_dTimerStart := LARGE_INTEGER(timerCounter).QuadPart / g_dTimerFrequency; 69 | end; 70 | Result := True; 71 | end; 72 | 73 | function C2PatchGetTimerCurrentTime(): Double; 74 | var 75 | dNow: Double; 76 | timerCounter: TLargeInteger; 77 | begin 78 | dNow := 0.0; 79 | if not g_bTimerHighResolution then 80 | begin 81 | timeBeginPeriod(1); 82 | dNow := timeGetTime() * 1000; 83 | timeEndPeriod(1); 84 | end 85 | else 86 | begin 87 | QueryPerformanceCounter(timerCounter); 88 | dNow := LARGE_INTEGER(timerCounter).QuadPart / g_dTimerFrequency; 89 | end; 90 | // Overflow check. 91 | if (g_dTimerStart > dNow) then 92 | begin 93 | g_dTimerStart := dNow; 94 | end; 95 | Result := Round(dNow - g_dTimerStart); 96 | end; 97 | 98 | function C2PatchPeekMessageEx(var lpMsg: TMsg; hWnd: hWnd; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL; stdcall; 99 | const 100 | MWMO_INPUTAVAILABLE = $0004; 101 | var 102 | dBeginTime: Double; 103 | dNow: Double; 104 | dwMsgWaitResult: DWORD; 105 | msg: TMsg; 106 | begin 107 | dBeginTime := C2PatchGetTimerCurrentTime(); 108 | // Civilization 2 uses filter value 957 as a spinning wait. 109 | if (wMsgFilterMin = 957) then 110 | begin 111 | // Wait for user input. 112 | dwMsgWaitResult := MsgWaitForMultipleObjectsEx(0, Pointer(nil)^, g_dwMessageWaitTime, QS_ALLINPUT, MWMO_INPUTAVAILABLE); 113 | dNow := C2PatchGetTimerCurrentTime(); 114 | 115 | if (dwMsgWaitResult = WAIT_TIMEOUT) then 116 | begin 117 | // Idle again, reset work timer. 118 | g_dBeginWorkTime := 0.0; 119 | 120 | if SameValue(g_dBeginSleepTime, 0.0) or (dNow < g_dBeginSleepTime) then 121 | begin 122 | g_dBeginSleepTime := dNow; 123 | end; 124 | 125 | if ((dNow - g_dBeginSleepTime) >= g_dMessageWaitTimeThreshold) then 126 | begin 127 | g_dwMessageWaitTime := Min(g_dwMessageWaitTimeMax, g_dwMessageWaitTime + g_dwMessageWaitTimeInc); 128 | g_dBeginSleepTime := 0.0; 129 | end; 130 | end 131 | else 132 | begin 133 | // Messages available, reset sleep timer. 134 | g_dBeginSleepTime := 0.0; 135 | 136 | if SameValue(g_dBeginWorkTime, 0.0) or (dNow < g_dBeginWorkTime) then 137 | begin 138 | g_dBeginWorkTime := dNow; 139 | end; 140 | 141 | if ((dNow - g_dBeginWorkTime) >= g_dMessageProcessingTimeThreshold) then 142 | begin 143 | g_dwMessageWaitTime := 0; 144 | end 145 | else 146 | begin 147 | g_dwMessageWaitTime := g_dwMessageWaitTimeMin; 148 | end; 149 | end; 150 | 151 | // Disable message purge if set to 0. 152 | if not SameValue(g_dMessagesPurgeInterval, 0.0) then 153 | begin 154 | // Prime last purge time. 155 | if SameValue(g_dLastMessagePurgeTime, 0.0) or (dBeginTime < g_dLastMessagePurgeTime) then 156 | begin 157 | g_dLastMessagePurgeTime := dBeginTime; 158 | end; 159 | 160 | // Purge message queue to fix "Not Responding" problem during long AI turns. 161 | if ((dBeginTime - g_dLastMessagePurgeTime) >= g_dMessagesPurgeInterval) then 162 | begin 163 | if (GetQueueStatus(QS_ALLINPUT) <> 0) then 164 | begin 165 | while (PeekMessage(msg, hWnd, 0, 0, PM_REMOVE)) do 166 | begin 167 | TranslateMessage(msg); 168 | DispatchMessageA(msg); 169 | end; 170 | end; 171 | g_dLastMessagePurgeTime := dBeginTime; 172 | end; 173 | end; 174 | end 175 | else 176 | begin 177 | g_dLastMessagePurgeTime := dBeginTime; 178 | end; 179 | Result := PeekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); 180 | end; 181 | 182 | { TUiaPatchCPUUsage } 183 | 184 | function TUiaPatchCPUUsage.Active: Boolean; 185 | begin 186 | Result := UIAOPtions().CpuUsageOn 187 | end; 188 | 189 | procedure TUiaPatchCPUUsage.Attach(HProcess: Cardinal); 190 | begin 191 | C2PatchInitializeOptions( 192 | UIAOPtions.MessagesPurgeIntervalMs, 193 | UIAOPtions.MessageProcessingTimeThresholdMs, 194 | UIAOPtions.MessageWaitTimeThresholdMs, 195 | UIAOPtions.MessageWaitTimeMinMs, 196 | UIAOPtions.MessageWaitTimeMaxMs 197 | ); 198 | C2PatchInitializeTimer(); 199 | WriteMemory(HProcess, $005BBA64, [OP_NOP, OP_CALL], @C2PatchPeekMessageEx); 200 | WriteMemory(HProcess, $005BBB91, [OP_NOP, OP_CALL], @C2PatchPeekMessageEx); 201 | WriteMemory(HProcess, $005BD2F9, [OP_NOP, OP_CALL], @C2PatchPeekMessageEx); 202 | WriteMemory(HProcess, $005BD31D, [OP_NOP, OP_CALL], @C2PatchPeekMessageEx); 203 | end; 204 | 205 | initialization 206 | TUiaPatchCPUUsage.RegisterMe(); 207 | 208 | end. 209 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchCityView.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchCityView; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchCityView = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | Windows; 18 | 19 | procedure PatchCVCopyToPort(var lprc: TRect; xLeft, yTop, xRight, yBottom: Integer); stdcall; 20 | var 21 | CityViewXPos: Integer; 22 | begin 23 | CityViewXPos := PInteger($626A00)^; // V_CityViewXPos_dword_626A00 24 | if CityViewXPos < 0 then 25 | SetRect(lprc, -CityViewXPos, yTop, 1280 - CityViewXPos, yBottom) 26 | else 27 | SetRect(lprc, xLeft, yTop, xRight, yBottom); 28 | end; 29 | 30 | 31 | { TUiaPatchCityView } 32 | 33 | procedure TUiaPatchCityView.Attach(HProcess: Cardinal); 34 | begin 35 | // Center image on the screen wider than 1280 36 | WriteMemory(HProcess, $0045608B, [OP_NOP, OP_CALL], @PatchCVCopyToPort); 37 | 38 | end; 39 | 40 | initialization 41 | TUiaPatchCityView.RegisterMe(); 42 | 43 | end. 44 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchColorCorrection.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchColorCorrection; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchColorCorrection = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | Math, 18 | UiaMain; 19 | 20 | procedure GammaCorrection(var Value: Byte; Gamma, Exposure: Double); 21 | var 22 | NewValue: Double; 23 | begin 24 | NewValue := Value / 255; 25 | if Exposure <> 0 then 26 | begin 27 | NewValue := Exp(Ln(NewValue) * 2.2); 28 | NewValue := NewValue * Power(2, Exposure); 29 | NewValue := Exp(Ln(NewValue) / 2.2); 30 | end; 31 | if Gamma <> 1 then 32 | begin 33 | NewValue := Exp(Ln(NewValue) / Gamma); 34 | end; 35 | Value := Byte(Min(Round(NewValue * 255), 255)); 36 | end; 37 | 38 | procedure PatchPaletteGammaEx(A1: Pointer; A2: Integer; var A3, A4, A5: Byte); stdcall; 39 | var 40 | Gamma, Exposure: Double; 41 | begin 42 | Gamma := Uia.Settings.Dat.ColorGamma; 43 | Exposure := Uia.Settings.Dat.ColorExposure; 44 | if (Gamma <> 1.0) or (Exposure <> 0.0) then 45 | begin 46 | GammaCorrection(A3, Gamma, Exposure); 47 | GammaCorrection(A4, Gamma, Exposure); 48 | GammaCorrection(A5, Gamma, Exposure); 49 | end; 50 | end; 51 | 52 | procedure PatchPaletteGamma; register; 53 | asm 54 | push [ebp + $18] 55 | push [ebp + $14] 56 | push [ebp + $10] 57 | push [ebp + $0C] 58 | push [ebp + $08] 59 | call PatchPaletteGammaEx 60 | push $005DEAD6 61 | ret 62 | end; 63 | 64 | { TUiaPatchColorCorrection } 65 | 66 | procedure TUiaPatchColorCorrection.Attach(HProcess: Cardinal); 67 | begin 68 | // Color correction 69 | WriteMemory(HProcess, $005DEAD1, [OP_JMP], @PatchPaletteGamma); 70 | 71 | end; 72 | 73 | initialization 74 | TUiaPatchColorCorrection.RegisterMe(); 75 | 76 | end. 77 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchDllGif.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchDllGif; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchDllGif = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | SysUtils, 18 | Windows; 19 | 20 | type 21 | TDllGifsToBeFixed = packed record 22 | ResNum: Integer; 23 | WrongSize: Cardinal; 24 | end; 25 | 26 | {(*} 27 | const 28 | DllGifsToBeFixed: array[1..3] of TDllGifsToBeFixed = ( 29 | ( ResNum: 105; WrongSize: 74478 ), 30 | ( ResNum: 229; WrongSize: 29923 ), 31 | ( ResNum: 250; WrongSize: 27741 ) 32 | ); 33 | {*)} 34 | 35 | var 36 | ResNumsDoFixCache: array[105..250] of Shortint; // 1 - Yes, 0 - Undefined, -1 - No 37 | 38 | function DllGifNeedFixing(ResNum: Integer): Boolean; 39 | type 40 | TModules = array[0..34] of HMODULE; 41 | var 42 | i, j, k, l: Integer; 43 | ResInfo: HRSRC; 44 | ModulesCount: Integer; 45 | HModules: ^TModules; 46 | ResSize: Cardinal; 47 | begin 48 | Result := False; 49 | if ResNum in [Low(ResNumsDoFixCache)..High(ResNumsDoFixCache)] then 50 | begin 51 | ModulesCount := PInteger($006387CC)^; 52 | HModules := Pointer($006E4F60); 53 | if ResNumsDoFixCache[ResNum] = 0 then // Undefined 54 | begin 55 | ResNumsDoFixCache[ResNum] := -1; 56 | for i := Low(DllGifsToBeFixed) to High(DllGifsToBeFixed) do 57 | begin 58 | if DllGifsToBeFixed[i].ResNum = ResNum then 59 | begin 60 | for j := 0 to ModulesCount - 1 do 61 | begin 62 | ResInfo := FindResource(HModules^[j], MakeIntResource(ResNum), 'GIFS'); 63 | if ResInfo <> 0 then 64 | begin 65 | Inc(l); 66 | ResSize := SizeofResource(HModules^[j], ResInfo); 67 | if ResSize = DllGifsToBeFixed[i].WrongSize then 68 | ResNumsDoFixCache[ResNum] := 1; 69 | Break; 70 | end; 71 | end; 72 | Break; 73 | end; 74 | end; 75 | //SendMessageToLoader(ResNum, ResNumsDoFixCache[ResNum]); 76 | end; 77 | Result := (ResNumsDoFixCache[ResNum] = 1); 78 | end; 79 | end; 80 | 81 | function PatchFindAndLoadResourceEx(ResType: PChar; ResNum: Integer; var Module: HMODULE; var ResInfo: HRSRC): HGLOBAL; stdcall; 82 | var 83 | MyResInfo: HRSRC; 84 | Gifs: array[0..4] of char; 85 | begin 86 | Gifs := 'GIFS'; 87 | if StrLComp(ResType, Gifs, 4) = 0 then 88 | begin 89 | if DllGifNeedFixing(ResNum) then 90 | begin 91 | MyResInfo := FindResource(HInstance, MakeIntResource(ResNum), Gifs); 92 | if MyResInfo <> 0 then 93 | begin 94 | Module := HInstance; 95 | ResInfo := MyResInfo; 96 | end; 97 | end; 98 | end; 99 | Result := LoadResource(Module, ResInfo); 100 | end; 101 | 102 | procedure PatchFindAndLoadResource(); register; 103 | asm 104 | lea eax, [ebp - $0C] // HRSRC hResInfo 105 | push eax 106 | lea eax, [ebp - $18] // HMODULE hModule 107 | push eax 108 | push [ebp + $0C] // char *aResName 109 | push [ebp + $08] // char *aResType 110 | call PatchFindAndLoadResourceEx 111 | push $005DB2F3 112 | ret 113 | end; 114 | 115 | { TUiaPatchDllGif } 116 | 117 | procedure TUiaPatchDllGif.Attach(HProcess: Cardinal); 118 | begin 119 | // Fix mk.dll (229.gif, 250.gif) and pv.dll (105.gif) 120 | WriteMemory(HProcess, $005DB2D4, [OP_JMP], @PatchFindAndLoadResource); 121 | end; 122 | 123 | initialization 124 | TUiaPatchDllGif.RegisterMe(); 125 | 126 | end. 127 | 128 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchDrawUnit.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchDrawUnit; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch, 7 | Civ2Types; 8 | 9 | type 10 | TUiaPatchDrawUnit = class(TUiaPatch) 11 | public 12 | procedure Attach(HProcess: Cardinal); override; 13 | end; 14 | 15 | var 16 | UnitSpriteSentry: array[0..63] of TSprite; 17 | 18 | implementation 19 | 20 | uses 21 | Math, 22 | Graphics, 23 | SysUtils, 24 | Windows, 25 | Civ2UIA_CanvasEx, 26 | Civ2UIA_Proc, 27 | Civ2Proc; 28 | 29 | procedure PatchDrawUnitAfterEx(DrawPort: PDrawPort; UnitIndex, A3, Left, Top, Zoom, WithoutFortress: Integer); stdcall; 30 | var 31 | Unit1: PUnit; 32 | Canvas: TCanvasEx; 33 | UnitType: Byte; 34 | TextOut: string; 35 | begin 36 | if (UnitIndex < 0) or (UnitIndex > High(Civ2.Units^)) then 37 | Exit; 38 | UnitType := Civ2.Units^[UnitIndex].UnitType; 39 | 40 | // Draw Settlers/Engineers work counter or aircraft round 41 | if ((Civ2.UnitTypes^[UnitType].Role = 5) or (Civ2.UnitTypes^[UnitType].Domain = 1)) and (Civ2.Units^[UnitIndex].CivIndex = Civ2.HumanCivIndex^) and (Civ2.Units^[UnitIndex].Counter > 0) then 42 | begin 43 | TextOut := IntToStr(Civ2.Units^[UnitIndex].Counter); 44 | Canvas := TCanvasEx.Create(DrawPort); 45 | Canvas.Brush.Style := bsClear; 46 | Canvas.Font.Style := []; 47 | Canvas.Font.Size := ScaleByZoom(8, Zoom); 48 | if Canvas.Font.Size > 7 then 49 | Canvas.Font.Name := 'Arial' 50 | else 51 | Canvas.Font.Name := 'Small Fonts'; 52 | Canvas.FontShadows := SHADOW_ALL; 53 | Canvas.Font.Color := Canvas.ColorFromIndex(114); // Yellow 54 | Canvas.FontShadowColor := Canvas.ColorFromIndex(10); // Black 55 | Canvas.MoveTo(Left + ScaleByZoom(32, Zoom), Top + ScaleByZoom(32, Zoom)); 56 | Canvas.TextOutWithShadows(TextOut, 0, 0, DT_CENTER or DT_VCENTER, @DrawPort.ClientRectangle); 57 | Canvas.Free(); 58 | end; 59 | 60 | // Debug: unit index 61 | {Unit1 := @Civ2.Units[UnitIndex]; 62 | Canvas := TCanvasEx.Create(DrawPort); 63 | TextOut := Format('%.04x', [UnitIndex]); 64 | Canvas.Brush.Style := bsClear; 65 | Canvas.Font.Size := ScaleByZoom(8, Zoom); 66 | if Canvas.Font.Size > 7 then 67 | Canvas.Font.Name := 'Arial' 68 | else 69 | Canvas.Font.Name := 'Small Fonts'; 70 | 71 | Canvas.FontShadows := SHADOW_ALL; 72 | Canvas.Font.Color := Canvas.ColorFromIndex(41); 73 | Canvas.FontShadowColor := Canvas.ColorFromIndex(10); // Black 74 | 75 | Canvas.MoveTo(Left + ScaleByZoom(32, Zoom), Top + ScaleByZoom(08, Zoom)); 76 | Canvas.TextOutWithShadows(TextOut, 0, 0, DT_CENTER or DT_VCENTER); 77 | 78 | TextOut := Format('%.04x,%.04x', [Unit1.PrevInStack, Unit1.NextInStack]); 79 | Canvas.MoveTo(Left + ScaleByZoom(32, Zoom), Top + ScaleByZoom(20, Zoom)); 80 | Canvas.TextOutWithShadows(TextOut, 0, 0, DT_CENTER or DT_VCENTER); 81 | 82 | Canvas.Free();} 83 | 84 | Civ2.ResetSpriteZoom(); // Restored 0x0056C5E8 85 | end; 86 | 87 | procedure PatchDrawUnitAfter(); register; 88 | asm 89 | push [ebp + $20] // WithoutFortress 90 | push [ebp + $1C] // Zoom 91 | push [ebp + $18] // Top 92 | push [ebp + $14] // Left 93 | push [ebp + $10] // A3 94 | push [ebp + $0C] // UnitIndex 95 | push [ebp + $08] // DrawPort 96 | call PatchDrawUnitAfterEx 97 | push $0056C5ED 98 | ret 99 | end; 100 | 101 | procedure PatchLoadSpritesUnitsEx(DrawPort: PDrawPort); stdcall; 102 | var 103 | i, j, k, l, m: Integer; 104 | ColorIndex: Integer; 105 | MinIndex, MaxIndex: Integer; 106 | RGB1: RGBQuad; 107 | X, Y: Integer; 108 | RGBs: array[0..255] of RGBQuad; 109 | Pixel: COLORREF; 110 | DrawPort2: TDrawPort; 111 | Weight, Gray, MinGray, MaxGray: Integer; 112 | Delta, MidGray2, MinGray2, MaxGray2: Integer; 113 | GrayK1, GrayK2: Double; 114 | Height, Len: Integer; 115 | Pxl: PByte; 116 | Sprite: PSprite; 117 | SumGray, CountGray: Integer; 118 | MidGray: Double; 119 | begin 120 | GetDIBColorTable(DrawPort.DrawInfo.DeviceContext, 0, 256, RGBs); 121 | 122 | for i := 0 to 62 do 123 | begin 124 | X := 1 + (i mod 9) * 65; 125 | Y := 1 + (i div 9) * 49; 126 | Civ2.Sprite_Dispose(@UnitSpriteSentry[i]); 127 | Civ2.ExtractSprite64x48(@UnitSpriteSentry[i], X, Y); 128 | end; 129 | 130 | for i := 0 to 62 do 131 | begin 132 | Sprite := @UnitSpriteSentry[i]; 133 | SpriteConvertToGray(Sprite); 134 | 135 | {Height := RectHeight(Sprite.Rectangle2); 136 | MinGray := 31; 137 | MaxGray := 0; 138 | SumGray := 0; 139 | CountGray := 0; 140 | // First pass 141 | Pxl := Sprite.pMem; 142 | for j := 0 to Height - 1 do 143 | begin 144 | Inc(Pxl, 4); 145 | Len := PInteger(Pxl)^; 146 | Inc(Pxl, 4); 147 | for k := 0 to Len - 1 do 148 | begin 149 | if (Pxl^ >= 10) and (Pxl^ <= 245) then 150 | begin 151 | RGB1 := RGBs[Pxl^]; 152 | Gray := (RGB1.rgbBlue + RGB1.rgbGreen + RGB1.rgbRed) * 31 div 765; 153 | Inc(SumGray, Gray); 154 | Inc(CountGray); 155 | MinGray := Min(MinGray, Gray); 156 | MaxGray := Max(MaxGray, Gray); 157 | Pxl^ := Gray + 10; 158 | end; 159 | Inc(Pxl); 160 | end; 161 | end; 162 | if CountGray <> 0 then 163 | MidGray := SumGray / CountGray 164 | else 165 | MidGray := (MinGray + MaxGray) div 2; 166 | Delta := 4; 167 | MidGray2 := 16; 168 | MinGray2 := MidGray2 - Delta; 169 | MaxGray2 := MidGray2 + Delta; 170 | GrayK1 := 0; 171 | GrayK2 := 0; 172 | if MidGray <> MinGray then 173 | GrayK1 := (MidGray2 - MinGray2) / (MidGray - MinGray); 174 | if MaxGray <> MidGray then 175 | GrayK2 := (MaxGray2 - MidGray2) / (MaxGray - MidGray); 176 | // Second pass 177 | Pxl := Sprite.pMem; 178 | for j := 0 to Height - 1 do 179 | begin 180 | Inc(Pxl, 4); 181 | Len := PInteger(Pxl)^; 182 | Inc(Pxl, 4); 183 | for k := 0 to Len - 1 do 184 | begin 185 | if (Pxl^ >= 10) and (Pxl^ <= 245) then 186 | begin 187 | Gray := Pxl^ - 10; 188 | if Gray < MidGray then 189 | Gray := Trunc(MidGray2 - (MidGray - Gray) * GrayK1) 190 | else 191 | Gray := Trunc(MidGray2 + (Gray - MidGray) * GrayK2); 192 | Pxl^ := Gray + 10; 193 | end; 194 | Inc(Pxl); 195 | end; 196 | end;} 197 | end; 198 | 199 | Civ2.DrawPort_ResetWH(DrawPort, 0, 0); 200 | end; 201 | 202 | procedure PatchLoadSpritesUnits(); register; 203 | asm 204 | push ecx 205 | call PatchLoadSpritesUnitsEx 206 | push $0044B499 207 | ret 208 | end; 209 | 210 | procedure PatchDrawUnitSentry(DummyEAX, DummyEDX: Integer; Sprite: PSprite; ATint, ATop, ALeft: Integer; DrawPort: PDrawPort; ARect: PRect); register; 211 | var 212 | UnitType: Integer; 213 | begin 214 | UnitType := (Integer(Sprite) - Integer(Civ2.SprUnits)) div SizeOf(TSprite); 215 | Civ2.Sprite_CopyToPortNC(@UnitSpriteSentry[UnitType], ARect, DrawPort, ALeft, ATop); 216 | end; 217 | 218 | procedure PatchDrawUnitVeteranBadgeEx(DrawPort: PDrawPort; UnitIndex: Integer; R: PRect); stdcall; 219 | var 220 | Canvas: TCanvasEx; 221 | H, H2: Integer; 222 | Rgn: HRGN; 223 | begin 224 | if (Civ2.Units[UnitIndex].Attributes and $2000 <> 0) and (Civ2.Units[UnitIndex].CivIndex = Civ2.HumanCivIndex^) then 225 | begin 226 | Canvas := TCanvasEx.Create(DrawPort); 227 | Rgn := CreateRectRgn(DrawPort.ClientRectangle.Left, DrawPort.ClientRectangle.Top, DrawPort.ClientRectangle.Right, DrawPort.ClientRectangle.Bottom); 228 | SelectClipRgn(DrawPort.DrawInfo.DeviceContext, Rgn); 229 | Canvas.Brush.Color := Canvas.ColorFromIndex(122); 230 | Canvas.Pen.Color := Canvas.ColorFromIndex(114); 231 | H := R.Bottom - R.Top; 232 | H2 := (H + 1) div 2; 233 | Canvas.MoveTo(R.Right - H2 - 1, R.Top); 234 | Canvas.LineTo(Canvas.PenPos.X + H2, Canvas.PenPos.Y + H2); 235 | Canvas.PenDXDY(-1, -1); 236 | Canvas.LineTo(Canvas.PenPos.X - H2, Canvas.PenPos.Y + H2); 237 | Canvas.MoveTo(R.Right - H2 - 3, R.Top); 238 | Canvas.LineTo(Canvas.PenPos.X + H2, Canvas.PenPos.Y + H2); 239 | Canvas.PenDXDY(-1, -1); 240 | Canvas.LineTo(Canvas.PenPos.X - H2, Canvas.PenPos.Y + H2); 241 | DeleteObject(Rgn); 242 | Canvas.Free(); 243 | end; 244 | end; 245 | 246 | procedure PatchDrawUnitVeteranBadge(); register; 247 | asm 248 | mov [ebp - $D4], eax // Restore overwritten mov [ebp+vOrder], eax 249 | lea eax, [ebp - $10] // vRect3 250 | push eax 251 | push [ebp - $C4] // vUnitIndex 252 | push [ebp + $08] // aDrawPort 253 | call PatchDrawUnitVeteranBadgeEx 254 | push $0056C1A4 255 | ret 256 | end; 257 | 258 | { TUiaPatchDrawUnit} 259 | 260 | procedure TUiaPatchDrawUnit.Attach(HProcess: Cardinal); 261 | begin 262 | // Draw additionals: counter for settlers, aircrafts 263 | WriteMemory(HProcess, $0056C5E8, [OP_JMP], @PatchDrawUnitAfter); 264 | 265 | // Unit Sentry: Prepare sentry sprites 266 | WriteMemory(HProcess, $0044B48F, [OP_CALL], @PatchLoadSpritesUnits); 267 | // Unit Sentry: Draw 268 | WriteMemory(HProcess, $0056C4EF, [OP_CALL], @PatchDrawUnitSentry); 269 | 270 | // Draw Veteran badge 271 | WriteMemory(HProcess, $0056C19E, [OP_JMP], @PatchDrawUnitVeteranBadge); 272 | end; 273 | 274 | initialization 275 | TUiaPatchDrawUnit.RegisterMe(); 276 | 277 | end. 278 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchLimits.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchLimits; 2 | 3 | { 4 | civ2patch 5 | https://github.com/vinceho/civ2patch 6 | } 7 | 8 | interface 9 | 10 | uses 11 | UiaPatch; 12 | 13 | type 14 | TUiaPatchLimits = class(TUiaPatch) 15 | private 16 | procedure C2PatchTimeLimit(HProcess: Cardinal); 17 | procedure C2PatchPopulationLimit(HProcess: Cardinal); 18 | procedure C2PatchGoldLimit(HProcess: Cardinal); 19 | procedure C2PatchMapTilesLimit(HProcess: Cardinal); 20 | public 21 | function Active(): Boolean; override; 22 | procedure Attach(HProcess: Cardinal); override; 23 | end; 24 | 25 | implementation 26 | 27 | uses 28 | SysUtils; 29 | 30 | { TUiaPatchLimits } 31 | 32 | procedure TUiaPatchLimits.C2PatchTimeLimit(HProcess: Cardinal); 33 | begin 34 | WriteMemory(HProcess, $0048B069, WordRec(UIAOPtions^.RetirementWarningYear).Bytes); 35 | WriteMemory(HProcess, $0048B2AD, WordRec(UIAOPtions^.RetirementYear).Bytes); 36 | WriteMemory(HProcess, $0048B0BB, WordRec(UIAOPtions^.RetirementYear).Bytes); 37 | end; 38 | 39 | procedure TUiaPatchLimits.C2PatchPopulationLimit(HProcess: Cardinal); 40 | begin 41 | // Original = 0x00007D00 42 | WriteMemory(HProcess, $0043CD74, LongRec(UIAOPtions^.PopulationLimit).Bytes); 43 | WriteMemory(HProcess, $0043CD81, LongRec(UIAOPtions^.PopulationLimit).Bytes); 44 | end; 45 | 46 | procedure TUiaPatchLimits.C2PatchGoldLimit(HProcess: Cardinal); 47 | begin 48 | WriteMemory(HProcess, $00489608, LongRec(UIAOPtions^.GoldLimit).Bytes); 49 | WriteMemory(HProcess, $0048962A, LongRec(UIAOPtions^.GoldLimit).Bytes); 50 | // And more 51 | WriteMemory(HProcess, $004FAA28, LongRec(UIAOPtions^.GoldLimit).Bytes); 52 | WriteMemory(HProcess, $004FAA5A, LongRec(UIAOPtions^.GoldLimit).Bytes); 53 | WriteMemory(HProcess, $0054E586, LongRec(UIAOPtions^.GoldLimit).Bytes); 54 | WriteMemory(HProcess, $00556200, LongRec(UIAOPtions^.GoldLimit).Bytes); 55 | WriteMemory(HProcess, $0055620D, LongRec(UIAOPtions^.GoldLimit).Bytes); 56 | // Text limit of an edit control (Menu - Cheat - Change Money) 57 | WriteMemory(HProcess, $0051D787, [$0A], nil); 58 | end; 59 | 60 | procedure TUiaPatchLimits.C2PatchMapTilesLimit(HProcess: Cardinal); 61 | begin 62 | WriteMemory(HProcess, $0041D6B7, WordRec(UIAOPtions^.MapXLimit).Bytes); 63 | WriteMemory(HProcess, $0041D6DE, WordRec(UIAOPtions^.MapYLimit).Bytes); 64 | WriteMemory(HProcess, $0041D6FF, WordRec(UIAOPtions^.MapSizeLimit).Bytes); 65 | end; 66 | 67 | function TUiaPatchLimits.Active: Boolean; 68 | begin 69 | Result := True; 70 | end; 71 | 72 | procedure TUiaPatchLimits.Attach(HProcess: Cardinal); 73 | begin 74 | if UIAOPtions.civ2patchEnable then 75 | begin 76 | if UIAOPtions^.RetirementYearOn then 77 | C2PatchTimeLimit(HProcess); 78 | if UIAOPtions^.PopulationLimitOn then 79 | C2PatchPopulationLimit(HProcess); 80 | if UIAOPtions^.GoldLimitOn then 81 | C2PatchGoldLimit(HProcess); 82 | if UIAOPtions^.MapSizeLimitOn then 83 | C2PatchMapTilesLimit(HProcess); 84 | end; 85 | end; 86 | 87 | initialization 88 | TUiaPatchLimits.RegisterMe(); 89 | 90 | end. 91 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchMapWindow.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchMapWindow; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchMapWindow = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | Windows, 18 | Civ2Types, 19 | UiaMain; 20 | 21 | const 22 | LowMapZoom: Byte = $FA; 23 | 24 | procedure PatchCopyToScreenBitBlt(SrcDI: PDrawInfo; XSrc, YSrc, Width, Height: Integer; DestWS: PWindowStructure; XDest, YDest: Integer); cdecl; 25 | begin 26 | if SrcDI <> nil then 27 | begin 28 | if DestWS <> nil then 29 | begin 30 | if (DestWS.Palette <> 0) and (PInteger($00638B48)^ = 1) then // V_PaletteBasedDevice_dword_638B48 31 | RealizePalette(DestWS.DeviceContext); 32 | if not Uia.MapOverlay.CopyToScreenBitBlt(SrcDI, DestWS) then 33 | BitBlt(DestWS.DeviceContext, XDest, YDest, Width, Height, SrcDI.DeviceContext, XSrc, YSrc, SRCCOPY); 34 | end; 35 | end; 36 | end; 37 | 38 | // Extend vertical map overscroll 39 | procedure PatchCalcMapRectTopEx(MapWindow: PMapWindow); stdcall; 40 | begin 41 | if MapWindow.MapRect.Top + 2 * MapWindow.MapHalf.cy > PWord($006D1162)^ + 2 then 42 | begin 43 | MapWindow.MapRect.Top := PWord($006D1162)^ + 2 - 2 * MapWindow.MapHalf.cy; 44 | end; 45 | end; 46 | 47 | procedure PatchCalcMapRectTop(); register; 48 | asm 49 | push [ebp - $28] // P_MapWindow this 50 | call PatchCalcMapRectTopEx 51 | push $0047A334 52 | ret 53 | end; 54 | 55 | { TUiaPatchMapWindow} 56 | 57 | procedure TUiaPatchMapWindow.Attach(HProcess: Cardinal); 58 | begin 59 | // Map Overlay 60 | WriteMemory(HProcess, $005C0A2F, [OP_CALL], @PatchCopyToScreenBitBlt); 61 | 62 | // Extend vertical map overscroll 63 | WriteMemory(HProcess, $0047A2F2, [OP_JMP], @PatchCalcMapRectTop); 64 | 65 | // MapZoom: keep details on lower zoom levels 66 | WriteMemory(HProcess, $0047ABA4 + 3, [LowMapZoom]); 67 | WriteMemory(HProcess, $0047AD3F + 3, [LowMapZoom]); 68 | WriteMemory(HProcess, $0047AE85 + 3, [LowMapZoom]); 69 | WriteMemory(HProcess, $0047B004 + 3, [LowMapZoom]); 70 | WriteMemory(HProcess, $0047B092 + 3, [LowMapZoom]); 71 | WriteMemory(HProcess, $0047B1C1 + 3, [LowMapZoom]); 72 | WriteMemory(HProcess, $0047B20A + 3, [LowMapZoom]); 73 | WriteMemory(HProcess, $0047B279 + 3, [LowMapZoom]); 74 | WriteMemory(HProcess, $0047B572 + 3, [LowMapZoom]); 75 | WriteMemory(HProcess, $0047B794 + 3, [LowMapZoom]); 76 | 77 | end; 78 | 79 | initialization 80 | TUiaPatchMapWindow.RegisterMe(); 81 | 82 | end. 83 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchMenu.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchMenu; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchMenu = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | SysUtils, 18 | Windows, 19 | Civ2Types, 20 | Civ2Proc, 21 | UiaMain, 22 | UiaPatchArrangeWindows, 23 | Civ2UIA_FormAbout, 24 | Civ2UIA_FormSettings, 25 | Civ2UIA_FormConsole, 26 | Civ2UIA_Proc, 27 | Tests; 28 | 29 | const 30 | IDM_CARAVAN_REVENUE = $211; 31 | IDM_ARRANGE_S = $329; 32 | IDM_ARRANGE_L = $32A; 33 | IDM_SETTINGS = $A01; 34 | IDM_ABOUT = $A02; 35 | IDM_SWITCH_SNOWFALL = $A03; 36 | IDM_TEST = $A04; 37 | IDM_TEST2 = $A05; 38 | 39 | procedure MoveMenu(Menu, Menu1: PMenu; Before: Boolean = False); stdcall; 40 | var 41 | MenuCopy, Menu1Copy: TMenu; 42 | begin 43 | MenuCopy := Menu^; 44 | Menu1Copy := Menu1^; 45 | if Before then 46 | begin 47 | if (Menu1.Prev = Menu) or (Menu.Next = Menu1) then 48 | Exit; 49 | if Menu1.Prev <> nil then 50 | Menu1.Prev.Next := Menu; 51 | Menu.Prev := Menu1Copy.Prev; 52 | Menu.Next := Menu1; 53 | Menu1.Prev := Menu; 54 | if MenuCopy.Prev <> nil then 55 | MenuCopy.Prev.Next := MenuCopy.Next; 56 | if MenuCopy.Next <> nil then 57 | MenuCopy.Next.Prev := MenuCopy.Prev; 58 | end 59 | else 60 | begin 61 | if (Menu1.Next = Menu) or (Menu.Prev = Menu1) then 62 | Exit; 63 | if Menu1.Next <> nil then 64 | Menu1.Next.Prev := Menu; 65 | Menu.Prev := Menu1; 66 | Menu.Next := Menu1Copy.Next; 67 | Menu1.Next := Menu; 68 | if MenuCopy.Prev <> nil then 69 | MenuCopy.Prev.Next := MenuCopy.Next; 70 | if MenuCopy.Next <> nil then 71 | MenuCopy.Next.Prev := MenuCopy.Prev; 72 | end; 73 | end; 74 | 75 | procedure StrCopyTitleFromDialog(Text, SectionName: PChar); 76 | var 77 | Dlg: TDialogWindow; 78 | begin 79 | Civ2.Dlg_InitWithHeap(@Dlg, $2000); 80 | Civ2.Dlg_LoadGAMESimpleL0F0(@Dlg, SectionName); 81 | StrCopy(Text, Dlg.Title); 82 | Civ2.Dlg_CleanupHeap(@Dlg); 83 | end; 84 | 85 | procedure PatchBuildMenuBarEx(); stdcall; 86 | var 87 | MenuArrange: PMenu; 88 | MenuArrangeS: PMenu; 89 | MenuArrangeL: PMenu; 90 | Text: array[0..79] of Char; 91 | P: PChar; 92 | MenuFindCity: PMenu; 93 | MenuCaravanRevenue: PMenu; 94 | begin 95 | Civ2.MenuBar_AddMenu(Civ2.MenuBar, $A, '&UI Additions'); 96 | Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, $A, IDM_SETTINGS, '&Settings...', 0); 97 | Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, $A, 0, nil, 0); 98 | Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, $A, IDM_ABOUT, '&About...', 0); 99 | if Uia.SnowFlakes.IsItTime() then 100 | Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, $A, IDM_SWITCH_SNOWFALL, '&Switch snowfall', 0); 101 | //Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, $A, IDM_TEST, '&Test...', 0); 102 | //Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, $A, IDM_TEST2, '&Test2...', 0); 103 | // 104 | MenuArrange := Civ2.MenuBar_GetSubMenu(Civ2.MenuBar, $328); 105 | if MenuArrange <> nil then 106 | begin 107 | StrCopy(Text, MenuArrange.Text); 108 | StrCat(Text, ' S'); 109 | P := StrEnd(Text) - 1; 110 | Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, 3, 0, nil, 0); 111 | MenuArrangeS := Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, 3, IDM_ARRANGE_S, Text, 0); 112 | MoveMenu(MenuArrange, MenuArrangeS, False); 113 | P^ := 'L'; 114 | MenuArrangeL := Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, 3, IDM_ARRANGE_L, Text, 0); 115 | end; 116 | // 117 | MenuFindCity := Civ2.MenuBar_GetSubMenu(Civ2.MenuBar, $210); 118 | if MenuFindCity <> nil then 119 | begin 120 | Civ2.DlgParams_SetStringText(0, '...'); 121 | StrCopyTitleFromDialog(Text, 'SUPPLYSHOW'); 122 | StrCat(Text, '|Shift+G'); 123 | MenuCaravanRevenue := Civ2.MenuBar_AddSubMenu(Civ2.MenuBar, 2, IDM_CARAVAN_REVENUE, Text, 0); 124 | MoveMenu(MenuCaravanRevenue, MenuFindCity); 125 | end; 126 | 127 | end; 128 | 129 | procedure PatchBuildMenuBar(); register; 130 | asm 131 | mov eax, $0040194C // Restored j_Q_FClose_sub_4A2020(); 132 | call eax 133 | call PatchBuildMenuBarEx 134 | push $004E4C3D 135 | ret 136 | end; 137 | 138 | procedure PatchUpdateMenuBarEx(); stdcall; 139 | var 140 | CaravanRevenueActive: Boolean; 141 | Commodity: Integer; 142 | Unit1: PUnit; 143 | Text: array[0..79] of Char; 144 | begin 145 | CaravanRevenueActive := False; 146 | with Civ2 do 147 | begin 148 | DlgParams_SetStringText(0, '...'); 149 | if (UnitSelected^) and (Game.ActiveUnitIndex >= 0) then 150 | begin 151 | Unit1 := @Units[Game.ActiveUnitIndex]; 152 | Commodity := Unit1.Counter; 153 | if (Unit1.ID <> 0) and (Unit1.HomeCity <> $FF) and (Unit1.CivIndex = HumanCivIndex^) and (Unit1.Orders = -1) and (UnitTypes[Unit1.UnitType].Role = 7) and (Commodity >= 0) then 154 | begin 155 | CaravanRevenueActive := True; 156 | DlgParams_SetString(0, Commodities[Commodity]); 157 | end; 158 | end; 159 | StrCopyTitleFromDialog(Text, 'SUPPLYSHOW'); 160 | StrCat(Text, '|Shift+G'); 161 | MenuBar_ModifyMenuText(MenuBar, IDM_CARAVAN_REVENUE, Text); 162 | MenuBar_EnableSubMenu(MenuBar, IDM_CARAVAN_REVENUE, CaravanRevenueActive); 163 | MenuBar_EnableSubMenu(MenuBar, IDM_CARAVAN_REVENUE, not CaravanRevenueActive); 164 | 165 | MenuBar_CreateMenu(MenuBar, nil); // Restored 166 | end; 167 | end; 168 | 169 | procedure PatchUpdateMenuBar(); register; 170 | asm 171 | call PatchUpdateMenuBarEx 172 | push $004E5B92 173 | ret 174 | end; 175 | 176 | procedure PatchMenuExecDefaultCaseEx(SubNum: Integer); stdcall; 177 | begin 178 | case SubNum of 179 | IDM_SETTINGS: 180 | ShowFormSettings(); 181 | IDM_ABOUT: 182 | if GetAsyncKeyState(VK_SHIFT) and $8000 = 0 then 183 | ShowFormAbout() 184 | else 185 | TFormConsole.Open(); 186 | IDM_SWITCH_SNOWFALL: 187 | Uia.SnowFlakes.Switch(); 188 | //IDM_TEST: 189 | //ShowFormTest(); 190 | //IDM_TEST2: 191 | //Test2(); 192 | IDM_ARRANGE_S: 193 | begin 194 | ArrangeWindowMiniMapWidth := 185; 195 | Civ2.ArrangeWindows(); 196 | ArrangeWindowMiniMapWidth := -1; 197 | end; 198 | IDM_ARRANGE_L: 199 | begin 200 | ArrangeWindowMiniMapWidth := 350; 201 | Civ2.ArrangeWindows(); 202 | ArrangeWindowMiniMapWidth := -1; 203 | end; 204 | IDM_CARAVAN_REVENUE: 205 | begin 206 | PopupCaravanDeliveryRevenues(Civ2.HumanCivIndex^); 207 | end; 208 | end; 209 | end; 210 | 211 | procedure PatchMenuExecDefaultCase(); register; 212 | asm 213 | push [ebp + $0C] // int aSubNum 214 | call PatchMenuExecDefaultCaseEx 215 | push $004E3A81 216 | ret 217 | end; 218 | 219 | { TUiaPatchMenu } 220 | 221 | procedure TUiaPatchMenu.Attach(HProcess: Cardinal); 222 | begin 223 | WriteMemory(HProcess, $004E4C38, [OP_JMP], @PatchBuildMenuBar); 224 | WriteMemory(HProcess, $004E5B8D, [OP_CALL], @PatchUpdateMenuBar); 225 | WriteMemory(HProcess, $004E3A72, [OP_CALL], @PatchMenuExecDefaultCase); 226 | end; 227 | 228 | initialization 229 | TUiaPatchMenu.RegisterMe(); 230 | 231 | end. 232 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchMultiplayer.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchMultiplayer; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchMultiplayer = class(TUiaPatch) 10 | public 11 | function Active(): Boolean; override; 12 | procedure Attach(HProcess: Cardinal); override; 13 | end; 14 | 15 | implementation 16 | 17 | uses 18 | WinSock; 19 | 20 | function PatchSocketBuffer(af, Struct, protocol: Integer): TSocket; stdcall; 21 | var 22 | Val: Integer; 23 | Len: Integer; 24 | begin 25 | Result := socket(af, Struct, protocol); 26 | if Result <> INVALID_SOCKET then 27 | begin 28 | Len := SizeOf(Integer); 29 | getsockopt(Result, SOL_SOCKET, SO_SNDBUF, @Val, Len); 30 | if Val > $2000 then 31 | begin 32 | Val := $2000; 33 | setsockopt(Result, SOL_SOCKET, SO_SNDBUF, PChar(@Val), Len); 34 | end; 35 | getsockopt(Result, SOL_SOCKET, SO_RCVBUF, @Val, Len); 36 | if Val > $2000 then 37 | begin 38 | Val := $2000; 39 | setsockopt(Result, SOL_SOCKET, SO_RCVBUF, PChar(@Val), Len); 40 | end; 41 | end; 42 | end; 43 | 44 | { TUiaPatchMultiplayer } 45 | 46 | function TUiaPatchMultiplayer.Active: Boolean; 47 | begin 48 | Result := True; 49 | end; 50 | 51 | procedure TUiaPatchMultiplayer.Attach(HProcess: Cardinal); 52 | begin 53 | // Enable simultaneous moves in multiplayer. 54 | if UIAOPtions().SimultaneousOn then 55 | begin 56 | WriteMemory(HProcess, $0041FAF0, [$01]); 57 | end; 58 | 59 | // Fix for multiplayer game by limiting socket buffer length to old default 0x2000 bytes. 60 | if UIAOPtions().SocketBufferOn then 61 | begin 62 | WriteMemory(HProcess, $10003673, [OP_CALL], @PatchSocketBuffer); 63 | WriteMemory(HProcess, $100044F9, [OP_CALL], @PatchSocketBuffer); 64 | WriteMemory(HProcess, $10004BAB, [OP_CALL], @PatchSocketBuffer); 65 | WriteMemory(HProcess, $10004BD1, [OP_CALL], @PatchSocketBuffer); 66 | WriteMemory(HProcess, $10004E29, [OP_CALL], @PatchSocketBuffer); 67 | WriteMemory(HProcess, $10004E4F, [OP_CALL], @PatchSocketBuffer); 68 | end; 69 | 70 | // Fix crash in hotseat game (V_HumanCivIndex_dword_6D1DA0 = 0xFFFFFFFF, must be JG instead of JNZ) 71 | if UIAOPtions().UIAEnable then 72 | WriteMemory(HProcess, $00569EC7, [OP_JG]); 73 | 74 | end; 75 | 76 | initialization 77 | TUiaPatchMultiplayer.RegisterMe(); 78 | 79 | end. 80 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchScienceAdvisor.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchScienceAdvisor; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchScienceAdvisor = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | Graphics, 18 | SysUtils, 19 | Windows, 20 | Civ2Proc, 21 | Civ2UIA_Proc, 22 | Civ2UIA_CanvasEx; 23 | 24 | procedure PatchUpdateAdvisorScienceEx(CivIndex, AdvanceCost, TotalScience: Integer); stdcall; 25 | var 26 | Canvas: TCanvasEx; 27 | TextOut: string; 28 | X, Y, FontHeight: Integer; 29 | Beakers: Integer; 30 | begin 31 | if Civ2.Civs[CivIndex].ResearchingTech < 0 then 32 | Exit; 33 | Beakers := Civ2.Civs[CivIndex].Beakers; 34 | TextOut := Format('%d + %d / %d', [Beakers, TotalScience, AdvanceCost]); 35 | FontHeight := Civ2.FontInfo_GetHeightWithExLeading(Civ2.FontTimes18); 36 | X := Civ2.AdvisorWindow.MSWindow.RectClient.Left + 5; 37 | Y := Civ2.AdvisorWindow.MSWindow.ClientTopLeft.Y + 2; 38 | Y := Y + FontHeight * 4 + 6 + 9; 39 | Canvas := TCanvasEx.Create(@Civ2.AdvisorWindow.MSWindow.GraphicsInfo.DrawPort); 40 | Canvas.CopyFont(Civ2.FontTimes14b.FontDataHandle); 41 | Canvas.Brush.Style := bsClear; 42 | Canvas.SetTextColors(41, 18); 43 | Canvas.FontShadows := SHADOW_BR; 44 | Canvas.MoveTo(X + 8, Y - 2); 45 | Canvas.TextOutWithShadows(TextOut, 0, 0, DT_BOTTOM); 46 | TextOut := ConvertTurnsToString(GetTurnsToComplete(Beakers, TotalScience, AdvanceCost), $21); 47 | X := Civ2.AdvisorWindow.MSWindow.RectClient.Right - 5; 48 | Canvas.MoveTo(X - 8, Y); 49 | Canvas.TextOutWithShadows(TextOut, 0, 0, DT_RIGHT); 50 | Canvas.Free(); 51 | end; 52 | 53 | procedure PatchUpdateAdvisorScience(); register; 54 | asm 55 | push [ebp - $64] // vTotalScience 56 | push [ebp - $30] // vAdvanceCost 57 | push [ebp - $6C] // vCivIndex 58 | call PatchUpdateAdvisorScienceEx 59 | mov ecx, $0063EB10 // Restore: mov ecx, offset V_AdvisorWindow_stru_63EB10 60 | push $0042B531 61 | ret 62 | end; 63 | 64 | { TUiaPatchScienceAdvisor } 65 | 66 | procedure TUiaPatchScienceAdvisor.Attach(HProcess: Cardinal); 67 | begin 68 | // More info (beakers, production, turns to complete) 69 | WriteMemory(HProcess, $0042B52C, [OP_JMP], @PatchUpdateAdvisorScience); 70 | 71 | end; 72 | 73 | initialization 74 | TUiaPatchScienceAdvisor.RegisterMe(); 75 | 76 | end. 77 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchSideBar.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchSideBar; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchSideBar = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | SysUtils, 18 | Civ2Proc, 19 | Civ2UIA_Proc; 20 | 21 | procedure PatchDrawSideBarTopEx; stdcall; 22 | var 23 | TextOut: string; 24 | Top: Integer; 25 | begin 26 | TextOut := Format('%s %d', [GetLabelString($2D), Civ2.Game.Turn]); // 'Turn' 27 | StrCopy(Civ2.ChText, PChar(TextOut)); 28 | Top := Civ2.SideBarClientRect^.Top + (Civ2.SideBar.FontInfo.Height - 1) * 2; 29 | Civ2.DrawStringRightCurrDrawPort2(Civ2.ChText, Civ2.SideBarClientRect^.Right, Top, 0); 30 | end; 31 | 32 | procedure PatchDrawSideBarTop; register; 33 | asm 34 | mov eax, $00401E0B //j_Q_DrawStringCurrDrawPort2_sub_43C8D0 35 | call eax 36 | add esp, $0C 37 | call PatchDrawSideBarTopEx 38 | push $00569552 39 | ret 40 | end; 41 | 42 | { TUiaPatchSideBar } 43 | 44 | procedure TUiaPatchSideBar.Attach(HProcess: Cardinal); 45 | begin 46 | 47 | WriteMemory(HProcess, $0056954A, [OP_JMP], @PatchDrawSideBarTop); 48 | 49 | end; 50 | 51 | initialization 52 | TUiaPatchSideBar.RegisterMe(); 53 | 54 | end. 55 | 56 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchSuppressPopup.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchSuppressPopup; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchSuppressPopup = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | SysUtils, 18 | UiaMain, 19 | Civ2Types; 20 | 21 | function SimplePopupSuppressed(SectionName: PChar): Boolean; 22 | begin 23 | Result := False; 24 | if (SectionName <> nil) and Uia.Settings.DatFlagSet(0) then 25 | begin 26 | Result := (Uia.Settings.SuppressPopupList.IndexOf(string(SectionName)) > -1); 27 | end; 28 | end; 29 | 30 | procedure PatchLoadPopupDialogAfterEx(Dialog: PDialogWindow; FileName, SectionName: PChar); stdcall; 31 | var 32 | TextLine: PDlgTextLine; 33 | Text: string; 34 | begin 35 | if (FileName <> nil) and (StrComp(FileName, 'GAME') = 0) then 36 | begin 37 | if SimplePopupSuppressed(SectionName) then 38 | begin 39 | if Dialog.Title <> nil then 40 | Text := string(Dialog.Title) + ': '; 41 | TextLine := Dialog.FirstTextLine; 42 | while TextLine <> nil do 43 | begin 44 | Text := Text + ' ' + string(TextLine.Text); 45 | TextLine := TextLine.Next; 46 | end; 47 | Uia.MapMessages.Add(Text); 48 | Dialog.PressedButton := $12345678; 49 | end 50 | end; 51 | end; 52 | 53 | procedure PatchLoadPopupDialogAfter(); register; 54 | asm 55 | push eax 56 | push [ebp + $0C] // char *aSectionName 57 | push [ebp + $08] // char *aFileName 58 | push [ebp - $180] // P_DialogWindow this 59 | call PatchLoadPopupDialogAfterEx 60 | pop eax 61 | push $005A6C1C 62 | ret 63 | end; 64 | 65 | procedure PatchCreateDialogAndWaitBefore(); register; 66 | asm 67 | mov [ebp - $114], ecx 68 | cmp [ecx + $DC], $12345678 // Dialog.PressedButton 69 | je @LABEL_NODIAOLOG 70 | push $005A5F46 71 | ret 72 | 73 | @LABEL_NODIAOLOG: 74 | mov [ecx + $DC], 0 75 | XOR eax, eax 76 | push $005A6323 77 | ret 78 | end; 79 | 80 | { TUiaPatchSuppressPopup } 81 | 82 | procedure TUiaPatchSuppressPopup.Attach(HProcess: Cardinal); 83 | begin 84 | // (0) Suppress specific simple popup message 85 | WriteMemory(HProcess, $005A6C12, [OP_JMP], @PatchLoadPopupDialogAfter); 86 | WriteMemory(HProcess, $005A5F40, [OP_JMP], @PatchCreateDialogAndWaitBefore); 87 | 88 | end; 89 | 90 | initialization 91 | TUiaPatchSuppressPopup.RegisterMe(); 92 | 93 | end. 94 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchTaxWindow.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchTaxWindow; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchTaxWindow = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | Graphics, 18 | Windows, 19 | Civ2Types, 20 | Civ2Proc, 21 | Civ2UIA_Proc, 22 | Civ2UIA_CanvasEx; 23 | 24 | procedure PatchUpdateTaxWindowEx(TaxWindow: PTaxWindow); stdcall; 25 | var 26 | Civ: PCiv; 27 | Canvas: TCanvasEx; 28 | Xc, Y: Integer; 29 | Text, Text1, Text2: string; 30 | Beakers, AdvanceCost: Integer; 31 | i: Integer; 32 | begin 33 | Civ := @Civ2.Civs[TaxWindow.CivIndex]; 34 | Beakers := Civ.Beakers; 35 | AdvanceCost := Civ2.GetAdvanceCost(TaxWindow.CivIndex); 36 | 37 | Canvas := TCanvasEx.Create(@TaxWindow.MSWindow.GraphicsInfo.DrawPort); 38 | Canvas.CopyFont(Civ2.FontTimes16.FontDataHandle); 39 | Canvas.Brush.Style := bsClear; 40 | Canvas.SetTextColors(37, 18); 41 | Xc := (TaxWindow.x0 + TaxWindow.ScrollW) div 2; 42 | Y := TaxWindow.yDis; //+ TaxWindow.FontHeight; 43 | 44 | Canvas.MoveTo(Xc, Y); 45 | Text1 := GetLabelString(368) + ': ' + ConvertTurnsToString(GetTurnsToComplete(0, TaxWindow.TotalScience, AdvanceCost), $22); // Discoveries Every 46 | Canvas.TextOutWithShadows(Text1, 0, 0, DT_CENTER); 47 | 48 | if Civ.ResearchingTech >= 0 then 49 | begin 50 | Canvas.MoveTo(Xc, Y + TaxWindow.FontHeight); 51 | Text := string(Civ2.GetStringInList(Civ2.RulesCivilizes[Civ.ResearchingTech].TextIndex)); // Advance name 52 | Text2 := ConvertTurnsToString(GetTurnsToComplete(Beakers, TaxWindow.TotalScience, AdvanceCost), $22); 53 | Text := '(' + Text + ': ' + Text2 + ')'; 54 | Canvas.TextOutWithShadows(Text, 0, 0, DT_CENTER); 55 | end; 56 | 57 | Canvas.Free(); 58 | 59 | Civ2.GraphicsInfo_CopyToScreenAndValidateW(@TaxWindow.MSWindow.GraphicsInfo); 60 | 61 | // Update advisors 62 | //TaxRate := Civ2.Civs[TaxWindow.CivIndex].TaxRate; 63 | //ScienceRate := Civ2.Civs[TaxWindow.CivIndex].ScienceRate; 64 | Civ.TaxRate := TaxWindow.TaxRateF; 65 | Civ.ScienceRate := TaxWindow.ScienceRateF; 66 | 67 | Civ2.Game.word_655AEE := Civ2.Game.word_655AEE and $FFFB; 68 | for i := 0 to Civ2.Game.TotalCities - 1 do 69 | begin 70 | if (Civ2.Cities[i].ID <> 0) and (Civ2.Cities[i].Owner = TaxWindow.CivIndex) then 71 | Civ2.CalcCityGlobals(i, True); 72 | end; 73 | Civ2.CityWindow_Update(Civ2.CityWindow, 0); 74 | case Civ2.AdvisorWindow.AdvisorType of 75 | 4, 5, 6, 9: 76 | Civ2.UpdateCopyValidateAdvisor(Civ2.AdvisorWindow.AdvisorType); 77 | end; 78 | //Civ2.Civs[TaxWindow.CivIndex].TaxRate := TaxRate; 79 | //Civ2.Civs[TaxWindow.CivIndex].ScienceRate := ScienceRate; 80 | end; 81 | 82 | procedure PatchUpdateTaxWindow(); register; 83 | asm 84 | push [ebp - $0C] // pTaxWindow 85 | call PatchUpdateTaxWindowEx 86 | push $0040CD52 87 | ret 88 | end; 89 | 90 | { TUiaPatchTaxWindow } 91 | 92 | procedure TUiaPatchTaxWindow.Attach(HProcess: Cardinal); 93 | begin 94 | // Tax Window 95 | WriteMemory(HProcess, $0040CC9A, [OP_JMP], @PatchUpdateTaxWindow); 96 | 97 | end; 98 | 99 | initialization 100 | TUiaPatchTaxWindow.RegisterMe(); 101 | 102 | end. 103 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchTimers.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchTimers; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchTimers = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | UiaMain; 18 | 19 | procedure PatchOnWmTimerDrawEx1(); stdcall; 20 | begin 21 | Uia.MapOverlay.UpdateModules(); 22 | end; 23 | 24 | procedure PatchOnWmTimerDraw(); register; 25 | asm 26 | call PatchOnWmTimerDrawEx1 27 | mov eax, $004131C0 28 | call eax 29 | end; 30 | 31 | 32 | { TUiaPatchTimers } 33 | 34 | procedure TUiaPatchTimers.Attach(HProcess: Cardinal); 35 | begin 36 | // MrTimerProc1 - Timer event for drawing map cursor (every 500 ms) 37 | WriteMemory(HProcess, $0040364D, [OP_JMP], @PatchOnWmTimerDraw); 38 | 39 | end; 40 | 41 | initialization 42 | TUiaPatchTimers.RegisterMe(); 43 | 44 | end. 45 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchTradeAdvisor.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchTradeAdvisor; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchTradeAdvisor = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | { TUiaPatchTradeAdvisor } 17 | 18 | procedure TUiaPatchTradeAdvisor.Attach(HProcess: Cardinal); 19 | begin 20 | // Show buildings even with zero maintenance cost in Trade Advisor 21 | WriteMemory(HProcess, $0042C107, [$00, $00, $00, $00]); 22 | 23 | end; 24 | 25 | initialization 26 | TUiaPatchTradeAdvisor.RegisterMe(); 27 | 28 | end. 29 | -------------------------------------------------------------------------------- /src/Patches/UiaPatchUnits.pas: -------------------------------------------------------------------------------- 1 | unit UiaPatchUnits; 2 | 3 | interface 4 | 5 | uses 6 | UiaPatch; 7 | 8 | type 9 | TUiaPatchUnits = class(TUiaPatch) 10 | public 11 | procedure Attach(HProcess: Cardinal); override; 12 | end; 13 | 14 | implementation 15 | 16 | uses 17 | Classes, 18 | Windows, 19 | UiaMain, 20 | Civ2Types, 21 | Civ2Proc, 22 | Civ2UIA_FormConsole; 23 | 24 | procedure PatchResetEngineersOrderEx(ThisWorker, AlreadyWorker: Integer); stdcall; 25 | var 26 | X, Y: Word; 27 | begin 28 | Civ2.Units[AlreadyWorker].Counter := 0; // Restored 29 | if not Uia.Settings.DatFlagSet(1) then 30 | Exit; 31 | X := Civ2.Units[ThisWorker].X; 32 | Y := Civ2.Units[ThisWorker].Y; 33 | Civ2.PickUpUnit(ThisWorker, 0); 34 | Civ2.PutDownUnit(ThisWorker, X, Y, 0); 35 | if Civ2.Units[AlreadyWorker].CivIndex = Civ2.HumanCivIndex^ then 36 | begin 37 | Civ2.Units[AlreadyWorker].Orders := -1; 38 | end; 39 | end; 40 | 41 | procedure PatchResetEngineersOrder(); register; 42 | asm 43 | push [ebp - $10] // int vAlreadyWorker 44 | push [ebp - $C] // int vThisWorker 45 | call PatchResetEngineersOrderEx 46 | push $004C452F 47 | ret 48 | end; 49 | 50 | procedure PatchBreakUnitMoving(UnitIndex: Integer); cdecl; 51 | var 52 | Unit1: PUnit; 53 | begin 54 | Unit1 := @Civ2.Units[UnitIndex]; 55 | if (Civ2.Game.HumanPlayers and (1 shl Unit1.CivIndex) = 0) or (not Uia.Settings.DatFlagSet(2)) then 56 | if ((Unit1.Orders and $F) = $B) and (Civ2.UnitTypes[Unit1.UnitType].Role <> 7) then 57 | begin 58 | Unit1.Orders := -1; 59 | end; 60 | end; 61 | 62 | procedure PatchOnActivateUnitEx(UnitIndex: Integer); stdcall; 63 | var 64 | i: Integer; 65 | Unit1, Unit2: PUnit; 66 | begin 67 | if not Uia.Settings.DatFlagSet(3) then 68 | Exit; 69 | Unit1 := @Civ2.Units[UnitIndex]; 70 | for i := 0 to Civ2.Game^.TotalUnits - 1 do 71 | begin 72 | Unit2 := @Civ2.Units[i]; 73 | if (Unit2.ID > 0) and (Unit2.CivIndex = Civ2.Game.SomeCivIndex) then 74 | begin 75 | if (Unit2.X = Unit1.X) and (Unit2.Y = Unit1.Y) then 76 | Unit2.Attributes := Unit2.Attributes and not $4000 77 | else 78 | Unit2.Attributes := Unit2.Attributes or $4000; 79 | end; 80 | end; 81 | end; 82 | 83 | procedure PatchOnActivateUnit(); register; 84 | asm 85 | push [ebp - 4] // int vUnitIndex 86 | call PatchOnActivateUnitEx 87 | mov eax, $004016EF // Civ2.AfterActiveUnitChanged 88 | call eax 89 | push $0058D5D4 90 | ret 91 | end; 92 | 93 | // 94 | // Set search origin to the saved coordinates instead of active unit coordinates 95 | // 96 | var 97 | GetNextActiveUnitOrigin: TSmallPoint; 98 | 99 | function PatchGetNextActiveUnit1Ex(UnitIndex: Integer; var X, Y: Integer): Integer; stdcall; 100 | begin 101 | // Return int GameParameters.ActiveUnitIndex 102 | Result := Civ2.Game.ActiveUnitIndex; 103 | if not Uia.Settings.DatFlagSet(3) then 104 | Exit; 105 | end; 106 | 107 | procedure PatchGetNextActiveUnit1(); register; 108 | asm 109 | lea eax, [ebp - $18] // int Y 110 | push eax 111 | lea eax, [ebp - $14] // int X 112 | push eax 113 | push [ebp + $08] // int UnitIndex 114 | call PatchGetNextActiveUnit1Ex 115 | push $005B65A7 116 | ret 117 | end; 118 | 119 | // 120 | // Save search origin as where next active unit was found 121 | // 122 | // Return int vNextUnit 123 | function PatchGetNextActiveUnit2Ex(NextIndex: Integer): Integer; stdcall; 124 | begin 125 | Result := NextIndex; 126 | if not Uia.Settings.DatFlagSet(3) then 127 | Exit; 128 | if NextIndex >= 0 then 129 | begin 130 | GetNextActiveUnitOrigin.x := Civ2.Units[NextIndex].X; 131 | GetNextActiveUnitOrigin.y := Civ2.Units[NextIndex].Y; 132 | end; 133 | end; 134 | 135 | procedure PatchGetNextActiveUnit2(); register; 136 | asm 137 | push [ebp - $1C] // int vNextUnit 138 | call PatchGetNextActiveUnit2Ex 139 | push $005B6782 140 | ret 141 | end; 142 | 143 | procedure PatchResetMoveIterationEx; stdcall; 144 | begin 145 | Civ2.Units[Civ2.Game.ActiveUnitIndex].MoveIteration := 0; 146 | end; 147 | 148 | procedure PatchResetMoveIteration; register; 149 | asm 150 | call PatchResetMoveIterationEx 151 | mov eax, $00401145 // Civ2.ProcessOrdersGoTo 152 | call eax 153 | push $0041141E 154 | ret 155 | end; 156 | 157 | procedure PatchResetMoveIteration2; register; 158 | asm 159 | call PatchResetMoveIterationEx 160 | mov eax, $0058DDAA 161 | call eax 162 | push $0058DDA5 163 | ret 164 | end; 165 | 166 | // 167 | // Mass move all active units of the same type 168 | // 169 | function MassMove(GotoX, GotoY: Integer): Integer; 170 | var 171 | MapX, MapY: Integer; 172 | UnitIndex: Integer; 173 | Unit1, Unit2: PUnit; 174 | i: Integer; 175 | begin 176 | Result := 0; 177 | UnitIndex := Civ2.Game.ActiveUnitIndex; 178 | if (UnitIndex >= 0) and (UnitIndex < Civ2.Game.TotalUnits) then 179 | begin 180 | Unit1 := @Civ2.Units[UnitIndex]; 181 | MapX := Unit1.X; 182 | MapY := Unit1.Y; 183 | for i := 0 to Civ2.Game.TotalUnits - 1 do 184 | begin 185 | Unit2 := @Civ2.Units[i]; 186 | if (Unit2.X = MapX) and (Unit2.Y = MapY) and (Unit2.UnitType = Unit1.UnitType) and Civ2.UnitCanMove(i) and (Unit2.Orders = -1) then 187 | begin 188 | Unit2.GotoX := GotoX; 189 | Unit2.GotoY := GotoY; 190 | Unit2.Orders := $0B; 191 | Unit2.MoveIteration := 0; 192 | repeat 193 | Civ2.Game.ActiveUnitIndex := i; 194 | Civ2.ProcessUnit(); 195 | until ((not Civ2.UnitCanMove(i)) or ((Unit2.X = GotoX) and (Unit2.Y = GotoY)) or (Unit2.Orders <> $0B)); 196 | Result := 1; 197 | end; 198 | end; 199 | if Result = 1 then 200 | begin 201 | PInteger($0062BCB0)^ := 0; 202 | PInteger($006AD8D4)^ := 0; 203 | end; 204 | end; 205 | end; 206 | 207 | function PatchMapWindowClickMassMoveEx(MapX, MapY: Integer; RMButton: LongBool): Integer; stdcall 208 | begin 209 | Result := 0; 210 | if (Uia.Settings.DatFlagSet(5)) and (RMButton) and ((GetAsyncKeyState(VK_SHIFT) and $8000) <> 0) then 211 | begin 212 | Result := MassMove(MapX, MapY); 213 | end; 214 | end; 215 | 216 | procedure PatchMapWindowClickMassMove(); register; 217 | asm 218 | push [ebp + $10] // int RMButton 219 | push [ebp - $0C] // DWORD vMapY 220 | push [ebp - $20] // DWORD vMapX 221 | call PatchMapWindowClickMassMoveEx 222 | cmp eax, 1 223 | je @@LABEL_PROCESSED 224 | push $00411239 225 | ret 226 | 227 | @@LABEL_PROCESSED: 228 | push $004116BC 229 | ret 230 | end; 231 | 232 | procedure PatchOrderLoadUnload(); stdcall; 233 | var 234 | UnitIndex, i: Integer; 235 | Unit1: PUnit; 236 | Unloaded: Boolean; 237 | begin 238 | UnitIndex := -1; 239 | Unloaded := False; 240 | if Civ2.Game.ActiveUnitIndex >= 0 then 241 | begin 242 | Unit1 := @Civ2.Units[Civ2.Game.ActiveUnitIndex]; 243 | if Civ2.UnitTypes[Unit1.UnitType].Domain = 2 then 244 | Unit1.Attributes := Unit1.Attributes or $4000; 245 | i := Civ2.GetTopUnitInStack(Civ2.Game.ActiveUnitIndex); 246 | while i >= 0 do 247 | begin 248 | Unit1 := @Civ2.Units[i]; 249 | if (Civ2.UnitTypes[Unit1.UnitType].Domain = 0) and (Unit1.Orders = 3) then 250 | begin 251 | Unit1.Orders := -1; 252 | Unloaded := True; 253 | if Civ2.UnitCanMove(i) then 254 | UnitIndex := i; 255 | end; 256 | i := Civ2.GetNextUnitInStack(i); 257 | end; 258 | if Unloaded then 259 | begin 260 | if UnitIndex >= 0 then 261 | begin 262 | Civ2.Game.ActiveUnitIndex := UnitIndex; 263 | Civ2.UnitSelected^ := False; 264 | Civ2.AfterActiveUnitChanged(0); 265 | end; 266 | end 267 | else 268 | begin 269 | i := Civ2.GetTopUnitInStack(Civ2.Game.ActiveUnitIndex); 270 | while i >= 0 do 271 | begin 272 | Unit1 := @Civ2.Units[i]; 273 | if (Civ2.UnitTypes[Unit1.UnitType].Domain = 0) and (Unit1.Orders = -1) and (Civ2.UnitCanMove(i)) then 274 | begin 275 | Unit1.Orders := 3; 276 | Unit1.GotoX := $FFFF; 277 | end; 278 | i := Civ2.GetNextUnitInStack(i); 279 | end; 280 | end; 281 | end; 282 | end; 283 | 284 | { TUiaPatchUnits} 285 | 286 | procedure TUiaPatchUnits.Attach(HProcess: Cardinal); 287 | begin 288 | // (1) Reset Engineer's order after passing its work to coworker 289 | WriteMemory(HProcess, $004C4522, [OP_JMP], @PatchResetEngineersOrder); 290 | // (2) Don't break unit movement 291 | WriteMemory(HProcess, $00402112, [OP_JMP], @PatchBreakUnitMoving); 292 | // (3) Reset Units wait flag after activating 293 | WriteMemory(HProcess, $0058D5CF, [OP_JMP], @PatchOnActivateUnit); 294 | WriteMemory(HProcess, $005B65A0, [OP_JMP], @PatchGetNextActiveUnit1); 295 | WriteMemory(HProcess, $005B677D, [OP_JMP], @PatchGetNextActiveUnit2); 296 | // Reset MoveIteration before start moving to prevent wrong warning 297 | WriteMemory(HProcess, $00411419, [OP_JMP], @PatchResetMoveIteration); 298 | WriteMemory(HProcess, $0058DDA0, [OP_JMP], @PatchResetMoveIteration2); 299 | // (5) Mass move units with Shift-RightClick 300 | WriteMemory(HProcess, $004111F3, [], @PatchMapWindowClickMassMove); 301 | WriteMemory(HProcess, $004111FD, [], @PatchMapWindowClickMassMove); 302 | // Order load/unload 303 | WriteMemory(HProcess, $00402EB9, [OP_JMP], @PatchOrderLoadUnload); 304 | 305 | end; 306 | 307 | initialization 308 | TUiaPatchUnits.RegisterMe(); 309 | 310 | end. 311 | -------------------------------------------------------------------------------- /src/Tests.pas: -------------------------------------------------------------------------------- 1 | unit Tests; 2 | 3 | interface 4 | 5 | procedure Test2(); 6 | 7 | implementation 8 | 9 | uses 10 | SysUtils, 11 | Civ2Types, 12 | Civ2Proc; 13 | 14 | procedure Test2(); 15 | var 16 | Unit1: PUnit; 17 | Dlg: TDialogWindow; 18 | i: Integer; 19 | Text: string; 20 | begin 21 | if (Civ2.UnitSelected^) and (Civ2.Game.ActiveUnitIndex >= 0) then 22 | begin 23 | Unit1 := @Civ2.Units[Civ2.Game.ActiveUnitIndex]; 24 | if (Unit1.ID <> 0) and (Unit1.CivIndex = Civ2.HumanCivIndex^) and (Unit1.Orders = -1) and (Civ2.UnitTypes[Unit1.UnitType].Role = 7) then 25 | with Civ2 do 26 | begin 27 | Dlg_InitWithHeap(@Dlg, $2000); 28 | DlgParams_SetString(0, UnitTypes[Unit1.UnitType].StringIndex); 29 | Dlg_LoadPopup(@Dlg, 'CIV2UIA', 'TEST1', 0, nil, nil, nil, nil, $00800001); 30 | for i := 0 to Game.TotalCities - 1 do 31 | if Cities[i].ID <> 0 then 32 | begin 33 | Text := Format('%s|(+%d#648860:1#)', [Cities[i].Name, i]); 34 | Dlg_AddListboxItem(@Dlg, PChar(Text), i, 0); 35 | end; 36 | 37 | Dlg_CreateAndWait(@Dlg, 0); 38 | Dlg_CleanupHeap(@Dlg); 39 | end; 40 | end; 41 | end; 42 | 43 | end. 44 | -------------------------------------------------------------------------------- /src/UiaMain.pas: -------------------------------------------------------------------------------- 1 | unit UiaMain; 2 | 3 | interface 4 | 5 | uses 6 | Contnrs, 7 | Windows, 8 | Civ2Types, 9 | UiaSettings, 10 | MemWatchDog, 11 | Civ2UIA_PathLine, 12 | Civ2UIA_QuickInfo, 13 | Civ2UIA_SnowFlakes, 14 | Civ2UIA_MapMessages, 15 | Civ2UIA_MapOverlay; 16 | 17 | type 18 | TWindowType = (wtUnknown, wtCityStatus, //F1 19 | wtDefenceMinister, //F2 20 | wtIntelligenceReport, //F3 21 | wtAttitudeAdvisor, //F4 22 | wtTradeAdvisor, //F5 23 | wtScienceAdvisor, //F6 24 | wtWindersOfTheWorld, //F7 25 | wtTop5Cities, //F8 26 | wtCivilizationScore, //F9 27 | wtDemographics, //F11 28 | wtCityWindow, wtTaxRate, wtCivilopedia, wtUnitsListPopup, wtMap, wtDialogMultiColumns); 29 | 30 | type 31 | TCityGlobalsEx = record 32 | TotalMapRes: array[0..2] of Integer; 33 | TradeRouteLevel: array[0..2] of Integer; 34 | end; 35 | 36 | type 37 | TUia = class 38 | private 39 | RegisteredHWND: array[TWindowType] of HWND; 40 | protected 41 | Patches: TClassList; 42 | public 43 | Settings: TUiaSettings; 44 | MapMessages: TMapMessages; 45 | SnowFlakes: TSnowFlakes; 46 | MapOverlay: TMapOverlay; 47 | ModuleNameString: string; 48 | VersionString: string; 49 | CityGlobalsEx: TCityGlobalsEx; 50 | MemWatchDog: TMemWatchDog; 51 | constructor Create; 52 | destructor Destroy; override; 53 | procedure AttachPatches(HProcess: THandle); 54 | procedure GetModuleVersion(); 55 | function GuessWindowType(HWindow: HWND): TWindowType; 56 | function FindScrolBar(HWindow: HWND): HWND; 57 | procedure RegisterPatch(PatchClass: TClass); 58 | procedure RegisterWindowByAddress(HWindow: HWND; ReturnAddress1, ReturnAddress2: Cardinal); 59 | published 60 | end; 61 | 62 | var 63 | Uia: TUia; 64 | 65 | implementation 66 | 67 | uses 68 | SysUtils, 69 | UiaPatch, 70 | FileInfo, 71 | Civ2Proc, 72 | Civ2UIA_FormConsole; 73 | 74 | { TUia } 75 | 76 | constructor TUia.Create; 77 | begin 78 | TFormConsole.Log('Creating TUia'); 79 | if Assigned(Uia) then 80 | raise Exception.Create('TUia is already created'); 81 | 82 | GetModuleVersion(); 83 | 84 | MapMessages := TMapMessages.Create(); 85 | SnowFlakes := TSnowFlakes.Create(); 86 | MapOverlay := TMapOverlay.Create(); 87 | MapOverlay.AddModule(TPathLine.Create()); 88 | MapOverlay.AddModule(SnowFlakes); 89 | MapOverlay.AddModule(TQuickInfo.Create()); 90 | MapOverlay.AddModule(MapMessages); 91 | 92 | Patches := TClassList.Create(); 93 | Settings := TUiaSettings.Create(); 94 | 95 | TFormConsole.Log('Created TUia'); 96 | end; 97 | 98 | destructor TUia.Destroy; 99 | begin 100 | Settings.Free(); 101 | Patches.Free(); 102 | 103 | MapOverlay.Free(); 104 | MapMessages := nil; 105 | 106 | inherited; 107 | end; 108 | 109 | procedure TUia.AttachPatches; 110 | var 111 | i: Integer; 112 | Patch: TUiaPatch; 113 | begin 114 | MemWatchDog := TMemWatchDog.Create($00401000, $006E80E0, False, True); 115 | for i := 0 to Patches.Count - 1 do 116 | begin 117 | Patch := TUiaPatch(Patches[i].Create()); 118 | if Patch.Active() then 119 | begin 120 | Patch.Attach(HProcess); 121 | TFormConsole.Log(Patch.ClassName + ' Attached') 122 | end 123 | else 124 | TFormConsole.Log(Patch.ClassName + ' NOT Active'); 125 | Patch.Free(); 126 | end; 127 | MemWatchDog.Free(); 128 | end; 129 | 130 | procedure TUia.GetModuleVersion(); 131 | var 132 | ModuleName: array[0..MAX_PATH] of Char; 133 | begin 134 | Windows.GetModuleFileName(HInstance, ModuleName, SizeOf(ModuleName)); 135 | ModuleNameString := string(ModuleName); 136 | VersionString := CurrentFileInfo(ModuleNameString); 137 | end; 138 | 139 | function TUia.GuessWindowType(HWindow: HWND): TWindowType; 140 | var 141 | WindowInfo: Integer; 142 | i: TWindowType; 143 | Dialog: PDialogWindow; 144 | begin 145 | Result := wtUnknown; 146 | WindowInfo := GetWindowLongA(HWindow, 4); 147 | if WindowInfo = $006A9200 then 148 | Result := wtCityWindow 149 | else if WindowInfo = $006A66B0 then 150 | Result := wtCivilopedia 151 | else if WindowInfo = $0066C7F0 then 152 | Result := wtMap 153 | else 154 | begin 155 | Dialog := Civ2.CurrPopupInfo^; 156 | if (Dialog <> nil) then 157 | begin 158 | if (Dialog.GraphicsInfo = Pointer(GetWindowLongA(HWindow, $0C))) and (Dialog.NumListItems > 0) and (Dialog.NumLines = 0) then 159 | Result := wtUnitsListPopup 160 | else if Dialog.Columns > 1 then 161 | Result := wtDialogMultiColumns; 162 | end; 163 | end; 164 | if Result = wtUnknown then 165 | begin 166 | for i := Low(TWindowType) to High(TWindowType) do 167 | begin 168 | if RegisteredHWND[i] = HWindow then 169 | begin 170 | Result := i; 171 | Break; 172 | end; 173 | end; 174 | end; 175 | end; 176 | 177 | procedure TUia.RegisterWindowByAddress(HWindow: HWND; ReturnAddress1, ReturnAddress2: Cardinal); 178 | var 179 | WindowType: TWindowType; 180 | begin 181 | //TFormConsole.Log(Format('ReturnAddress1=%x, ReturnAddress2=%x',[ReturnAddress1, ReturnAddress2])); 182 | WindowType := wtUnknown; 183 | case ReturnAddress1 of 184 | $0040D35C: 185 | WindowType := wtTaxRate; 186 | //$0042AB8A: 187 | else 188 | case ReturnAddress2 of 189 | $0042D742: 190 | WindowType := wtCityStatus; // F1 191 | $0042F0A0: 192 | WindowType := wtDefenceMinister; // F2 193 | $00430632: 194 | WindowType := wtIntelligenceReport; // F3 195 | $0042E1A9: 196 | WindowType := wtAttitudeAdvisor; // F4 197 | $0042CD56: 198 | WindowType := wtTradeAdvisor; // F5 199 | $0042B6A4: 200 | WindowType := wtScienceAdvisor; // F6 201 | end; 202 | end; 203 | if WindowType <> wtUnknown then 204 | begin 205 | RegisteredHWND[WindowType] := HWindow; 206 | end; 207 | end; 208 | 209 | procedure TUia.RegisterPatch(PatchClass: TClass); 210 | begin 211 | Patches.Add(PatchClass); 212 | TFormConsole.Log('Registered ' + PatchClass.ClassName); 213 | end; 214 | 215 | function TUia.FindScrolBar(HWindow: HWND): HWND; 216 | var 217 | ClassName: array[0..31] of Char; 218 | begin 219 | if GetClassName(HWindow, ClassName, 32) > 0 then 220 | if ClassName = 'MSScrollBarClass' then 221 | begin 222 | Result := HWindow; 223 | Exit; 224 | end; 225 | Result := FindWindowEx(HWindow, 0, 'MSScrollBarClass', nil); 226 | end; 227 | 228 | initialization 229 | Uia := TUia.Create(); 230 | 231 | finalization 232 | Uia.Free(); 233 | 234 | end. 235 | -------------------------------------------------------------------------------- /src/UiaSettings.pas: -------------------------------------------------------------------------------- 1 | unit UiaSettings; 2 | 3 | interface 4 | 5 | uses 6 | Classes; 7 | 8 | type 9 | TUiaDat = packed record 10 | Version: Integer; 11 | Size: Integer; 12 | ColorExposure: Double; 13 | ColorGamma: Double; 14 | AdvisorHeights: array[1..12] of Word; 15 | DialogLines: array[1..16] of Byte; 16 | Flags: array[0..31] of Byte; // 256 flags 17 | AdvisorSorts: array[1..12] of ShortInt; // 0 - no sort, 1 - sort by first column ascending, -1 - sort by first column descending, etc... 18 | end; 19 | 20 | TUiaSettings = class 21 | private 22 | protected 23 | public 24 | Dat: TUiaDat; 25 | SuppressPopupList: TStringList; 26 | constructor Create; 27 | destructor Destroy; override; 28 | function DatFlagSet(i: Integer): Boolean; 29 | procedure Load(); 30 | procedure LoadDatFile(); 31 | procedure LoadDefaultDat(); 32 | procedure Save(); 33 | procedure SaveDatFile(); 34 | procedure SetDatFlag(i: Integer; v: Boolean); 35 | published 36 | end; 37 | 38 | implementation 39 | 40 | uses 41 | SysUtils, 42 | Windows, 43 | Civ2UIA_Proc, 44 | Civ2UIA_FormConsole; 45 | 46 | const 47 | FILENAME_CIV2UIA_DAT = 'CIV2UIA.DAT'; 48 | FILENAME_CIV2UIASP_TXT = 'Civ2UIASuppressPopup.txt'; 49 | 50 | { TUiaSettings } 51 | 52 | constructor TUiaSettings.Create; 53 | begin 54 | SuppressPopupList := TStringList.Create(); 55 | Load(); 56 | end; 57 | 58 | function TUiaSettings.DatFlagSet(i: Integer): Boolean; 59 | var 60 | j, k: Integer; 61 | begin 62 | j := i shr 3; 63 | k := 1 shl (i and 7); 64 | Result := (Dat.Flags[j] and k) <> 0; 65 | end; 66 | 67 | destructor TUiaSettings.Destroy; 68 | begin 69 | SuppressPopupList.Free(); 70 | inherited; 71 | end; 72 | 73 | procedure TUiaSettings.Load; 74 | begin 75 | SuppressPopupList.Clear(); 76 | try 77 | SuppressPopupList.LoadFromFile(FILENAME_CIV2UIASP_TXT); 78 | except 79 | end; 80 | LoadDatFile(); 81 | end; 82 | 83 | procedure TUiaSettings.LoadDatFile; 84 | var 85 | FileHandle, BytesRead, SizeOfDat: Integer; 86 | begin 87 | SizeOfDat := SizeOf(Dat); 88 | ZeroMemory(@Dat, SizeOfDat); 89 | FileHandle := FileOpen(FILENAME_CIV2UIA_DAT, fmOpenRead); 90 | if FileHandle > 0 then 91 | begin 92 | BytesRead := FileRead(FileHandle, Dat, SizeOfDat); 93 | FileClose(FileHandle); 94 | if (BytesRead <= SizeOfDat) and (Dat.Version = 1) and (Dat.Size <= SizeOfDat) then 95 | Exit; 96 | end; 97 | LoadDefaultDat(); 98 | end; 99 | 100 | procedure TUiaSettings.LoadDefaultDat; 101 | begin 102 | Dat.Version := 1; 103 | Dat.Size := SizeOf(Dat); 104 | Dat.ColorExposure := 0.0; 105 | Dat.ColorGamma := 1.0; 106 | FillChar(Dat.Flags, SizeOf(Dat.Flags), $FF); 107 | end; 108 | 109 | procedure TUiaSettings.Save; 110 | begin 111 | try 112 | SuppressPopupList.SaveToFile(FILENAME_CIV2UIASP_TXT); 113 | SaveDatFile(); 114 | except 115 | on E: Exception do 116 | begin 117 | TFormConsole.Log('TUiaSettings.Save() Error %s', [E.Message]); 118 | end; 119 | end; 120 | end; 121 | 122 | procedure TUiaSettings.SaveDatFile; 123 | var 124 | FileHandle, BytesWritten: Integer; 125 | begin 126 | FileHandle := FileCreate(FILENAME_CIV2UIA_DAT); 127 | if FileHandle > 0 then 128 | begin 129 | BytesWritten := FileWrite(FileHandle, Dat, SizeOf(Dat)); 130 | FileClose(FileHandle); 131 | if BytesWritten <> SizeOf(Dat) then 132 | DeleteFile(FILENAME_CIV2UIA_DAT); 133 | end; 134 | end; 135 | 136 | procedure TUiaSettings.SetDatFlag(i: Integer; v: Boolean); 137 | var 138 | j, k: Integer; 139 | begin 140 | j := i shr 3; 141 | k := 1 shl (i and 7); 142 | if v then 143 | Dat.Flags[j] := Dat.Flags[j] or k 144 | else 145 | Dat.Flags[j] := Dat.Flags[j] and not k; 146 | end; 147 | 148 | end. 149 | --------------------------------------------------------------------------------