├── .gitignore ├── ArcNET.DataTypes ├── ArcNET.DataTypes.csproj ├── BinaryReaderExtensions.cs ├── Common │ ├── ArtId.cs │ ├── Color.cs │ ├── Location.cs │ └── PrefixedString.cs ├── FacadeWalk.cs ├── FacadeWalkReader.cs ├── GameObjects │ ├── Classes │ │ ├── AIPacket.cs │ │ ├── AutoLevelSchemes.cs │ │ ├── Background.cs │ │ ├── CritterXpLevels.cs │ │ ├── Descriptions.cs │ │ ├── Entity.cs │ │ ├── Faction.cs │ │ ├── InventorySource.cs │ │ ├── InventorySourceBuy.cs │ │ ├── Monster.cs │ │ ├── NPC.cs │ │ ├── QuestXpLevels.cs │ │ ├── RandomEncounters.cs │ │ ├── Unique.cs │ │ ├── WorldMapEncounterInfo.cs │ │ └── XpLevels.cs │ ├── Enums.cs │ ├── Flags │ │ ├── ObjFBlitFlag.cs │ │ ├── ObjFCritterFlags.cs │ │ ├── ObjFCritterFlags2.cs │ │ ├── ObjFFlags.cs │ │ ├── ObjFNpcFlags.cs │ │ └── ObjFSpellFlags.cs │ ├── GameObject.cs │ ├── GameObjectGuid.cs │ ├── GameObjectHeader.cs │ ├── GameObjectHeaderReader.cs │ ├── GameObjectManager.cs │ ├── GameObjectReader.cs │ ├── GameObjectScript.cs │ └── Types │ │ ├── Ammo.cs │ │ ├── Armor.cs │ │ ├── Common.cs │ │ ├── Container.cs │ │ ├── Critter.cs │ │ ├── Food.cs │ │ ├── Generic.cs │ │ ├── Gold.cs │ │ ├── Item.cs │ │ ├── Key.cs │ │ ├── KeyRing.cs │ │ ├── NPC.cs │ │ ├── PC.cs │ │ ├── Portal.cs │ │ ├── Projectile.cs │ │ ├── Scenery.cs │ │ ├── Scroll.cs │ │ ├── Trap.cs │ │ ├── Unknown.cs │ │ ├── Wall.cs │ │ ├── Weapon.cs │ │ └── Written.cs ├── Generators │ └── Wikia.cs ├── MessageEntry.cs ├── MessageEntryReader.cs ├── MessageReader.cs ├── Sector.cs ├── SectorReader.cs └── TextDataReader.cs ├── ArcNET.Terminal ├── ArcNET.Terminal.csproj ├── Parser.cs ├── Program.cs └── Terminal.cs ├── ArcNET.Utilities ├── ArcNET.Utilities.csproj ├── GitHub.cs └── HighResConfig.cs ├── ArcNET.sln ├── ArcNET.sln.DotSettings └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /ArcNET.DataTypes/ArcNET.DataTypes.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ArcNET.DataTypes/BinaryReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.Common; 2 | using ArcNET.DataTypes.GameObjects; 3 | using System; 4 | using System.Collections; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Reflection; 8 | 9 | namespace ArcNET.DataTypes; 10 | 11 | public static class BinaryReaderExtensions 12 | { 13 | public static Location ReadLocation(this BinaryReader reader, bool force = false) 14 | { 15 | var loc = new Location(); 16 | if (!force) 17 | { 18 | byte flag = reader.ReadByte(); 19 | if (flag == 0x00) return null; 20 | 21 | if (flag != 0x01) 22 | throw new Exception(); 23 | } 24 | 25 | loc.X = reader.ReadInt32(); 26 | loc.Y = reader.ReadInt32(); 27 | return loc; 28 | } 29 | 30 | public static GameObjectGuid ReadGameObjectGuid(this BinaryReader reader, bool force = false) 31 | { 32 | if (!force) 33 | { 34 | byte flag = reader.ReadByte(); 35 | if (flag == 0x00) return null; 36 | 37 | if (flag != 0x01) 38 | throw new Exception(); 39 | } 40 | 41 | var result = new GameObjectGuid 42 | { 43 | Type = reader.ReadInt16(), 44 | Foo0 = reader.ReadInt16(), 45 | Foo2 = reader.ReadInt32() 46 | }; 47 | byte[] guidData = reader.ReadBytes(16); 48 | result.Guid = new Guid(guidData); 49 | 50 | return result; 51 | } 52 | 53 | public static ArtId ReadArtId(this BinaryReader reader) 54 | => new(reader.ReadInt32().ToString("X2")); 55 | } 56 | 57 | public static class BitArrayUtils 58 | { 59 | public static bool Get(this BitArray bitArray, int index, bool isPrototype) 60 | => bitArray.Get(index) || isPrototype; 61 | } 62 | 63 | public class OrderAttribute : Attribute 64 | { 65 | public int Order { get; private set; } 66 | 67 | public OrderAttribute(int order) 68 | { 69 | Order = order; 70 | } 71 | } 72 | 73 | public static class PropertyInfoUtils 74 | { 75 | public static int PropertyOrder(this PropertyInfo propInfo) 76 | { 77 | var orderAttr = (OrderAttribute)propInfo.GetCustomAttributes(typeof(OrderAttribute), true).SingleOrDefault(); 78 | int output = orderAttr?.Order ?? int.MaxValue; 79 | return output; 80 | } 81 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/Common/ArtId.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ArcNET.DataTypes.Common; 4 | 5 | public class ArtId 6 | { 7 | public string Path; 8 | public static List ArtIds = new(); 9 | 10 | public ArtId(string path) 11 | { 12 | Path = path; 13 | } 14 | 15 | public override string ToString() 16 | => Path; 17 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/Common/Color.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ArcNET.DataTypes.Common; 4 | 5 | public class Color 6 | { 7 | public byte R; 8 | public byte G; 9 | public byte B; 10 | public byte A; 11 | 12 | public Color() 13 | { 14 | R = 255; 15 | G = 255; 16 | B = 255; 17 | A = 0; 18 | } 19 | 20 | public Color(int data) 21 | { 22 | byte[] temp = BitConverter.GetBytes(data); 23 | B = temp[0]; 24 | G = temp[1]; 25 | R = temp[2]; 26 | A = temp[3]; 27 | } 28 | 29 | public override string ToString() 30 | => "R: " + R + "," + "G: " + G + "," + "B: " + B + "," + "A: " + A; 31 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/Common/Location.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.Common; 2 | 3 | public class Location 4 | { 5 | public int X { get; set; } 6 | public int Y { get; set; } 7 | 8 | public override string ToString() 9 | => $"{X},{Y}"; 10 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/Common/PrefixedString.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.Common; 2 | 3 | public class PrefixedString 4 | { 5 | private readonly string _value; 6 | 7 | public PrefixedString() 8 | { 9 | _value = string.Empty; 10 | } 11 | 12 | public PrefixedString(string value) 13 | { 14 | _value = value; 15 | } 16 | 17 | public override string ToString() 18 | => _value; 19 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/FacadeWalk.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace ArcNET.DataTypes; 5 | 6 | public class FacadeWalk 7 | { 8 | public FacWalkMarker Marker; 9 | public FacWalkHeader Header; 10 | public FacWalkEntry[] Entries; 11 | } 12 | 13 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 14 | public struct FacWalkMarker 15 | { 16 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] 17 | public string fileMarker; // fileTypeMarker 'F', 'a', 'c', 'W', 'a', 'l', 'k', ' ', + fileTypeVersion 'V', '1', '0', '1', ' ', ' ' 18 | } 19 | 20 | [Serializable] 21 | [StructLayout(LayoutKind.Sequential)] 22 | public struct FacWalkHeader 23 | { 24 | // base terrain, index + outdoor + flippable 25 | [MarshalAs(UnmanagedType.U4)] 26 | public uint terrain; // index to tilename.mes 27 | 28 | [MarshalAs(UnmanagedType.U4)] 29 | public uint outdoor; // boolean, 1 = outdoor 30 | 31 | [MarshalAs(UnmanagedType.U4)] 32 | public uint flippable; // boolean, 1 = flippable 33 | 34 | [MarshalAs(UnmanagedType.U4)] 35 | public uint width; // width of facade, isometric 36 | 37 | // ReSharper disable once MissingBlankLines 38 | [MarshalAs(UnmanagedType.U4)] 39 | public uint height; // height of facade, isometric 40 | 41 | [MarshalAs(UnmanagedType.U4)] 42 | public uint entryCount; // equals to number of frames of equivalent Art file. 43 | } 44 | 45 | [Serializable] 46 | [StructLayout(LayoutKind.Sequential)] 47 | public struct FacWalkEntry 48 | { 49 | [MarshalAs(UnmanagedType.U4)] 50 | public uint x; // x position 51 | 52 | [MarshalAs(UnmanagedType.U4)] 53 | public uint y; // y position 54 | 55 | [MarshalAs(UnmanagedType.U4)] 56 | public uint walkable; // boolean, 0 = blocked 57 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/FacadeWalkReader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Utils.Console; 3 | using Utils.Marshalling; 4 | 5 | namespace ArcNET.DataTypes; 6 | 7 | public class FacadeWalkReader 8 | { 9 | private readonly BinaryReader _reader; 10 | 11 | public FacadeWalkReader(BinaryReader reader) 12 | { 13 | _reader = reader; 14 | } 15 | 16 | public FacadeWalk Read() 17 | { 18 | var obj = new FacadeWalk 19 | { 20 | Marker = MarshallingExtensions.ByteArrayToStructure(_reader) 21 | }; 22 | //ConsoleExtensions.Log($"Parsed file marker: {obj.Marker.fileMarker}", "success"); 23 | 24 | const string markerConst = "FacWalk V101 "; 25 | string markerActual = obj.Marker.fileMarker; 26 | if (markerActual != markerConst) 27 | { 28 | ConsoleExtensions.Log("Filetype or version mismatch!", "error"); 29 | ConsoleExtensions.Log($"Expected: {markerConst}", "error"); 30 | ConsoleExtensions.Log($"Parsed: {markerActual}", "error"); 31 | return null; 32 | } 33 | 34 | obj.Header = MarshallingExtensions.ByteArrayToStructure(_reader); 35 | obj.Entries = new FacWalkEntry[obj.Header.entryCount]; 36 | 37 | #if DEBUG 38 | //ConsoleExtensions.Log($"Parsed Header: {obj.Header}", "success"); 39 | //ConsoleExtensions.Log($"Parsing {obj.Header.entryCount} entries", "info"); 40 | #endif 41 | 42 | for (var i = 0; i < obj.Header.entryCount; i++) 43 | obj.Entries[i] = MarshallingExtensions.ByteArrayToStructure(_reader); 44 | //AnsiConsole.WriteLine($"Parsing entry: {obj.Entries[i]} index:{i}"); 45 | return obj; 46 | } 47 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/AIPacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ArcNET.DataTypes.GameObjects.Classes; 5 | 6 | public class AIPacket 7 | { 8 | public struct Packet 9 | { 10 | public struct FleeingParams 11 | { 12 | public int NPCHPBelow; //percentage of NPC hit points below which NPC will flee 13 | public int PeopleAround; //number of people besides PC beyond which NPC will flee 14 | public int LevelAbove; //number of levels above NPC beyond which NPC will flee 15 | public int PCHPBelow; //percentage of PC hit points below which NPC will never flee 16 | public int FleeRange; //how far to flee, in tiles 17 | } 18 | 19 | public struct FollowerParams 20 | { 21 | public int ReactionLevel; //the reaction level at which the NPC will not follow the PC 22 | public int PCAlignAboveNPC; //how far PC alignment is above NPC align before NPC wont follow 23 | public int PCAlignBelowNPC; //how far PC align is below NPC align before NPC wont follow 24 | public int NPCLevelsAbovePC; //how many levels the NPC can be above the PC and still join 25 | public int NPCAbuseReaction; //how much a non-accidental hit will lower a follower's reaction 26 | } 27 | 28 | public struct KillOnSightParams 29 | { 30 | public int NPCReactionLevelBelow; //NPC will attack if his reaction is below this 31 | public int NPCAlignDiffToPC; //how different alignments can be before non-follower NPC attacks 32 | public int TargetAlignGoodNoAtt; //alignment of target at (or above) which the good-aligned follower NPC will not attack 33 | } 34 | 35 | public struct SpellParams 36 | { 37 | public int DefensiveSpellChance; //chance of throwing defensive spell (as opposed to offensive) 38 | public int HealingSpellInCmbChance; //chance of throwing a healing spell in combat 39 | } 40 | 41 | public struct CombatParams 42 | { 43 | public int CombatMinDistance; //minimum distance in combat 44 | } 45 | 46 | public struct DoorAndWindowsParams 47 | { 48 | public int CanOpenPortals; //the NPC can open portals if this is nonzero and cannot if it is zero 49 | } 50 | } 51 | 52 | public List> Entries; 53 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/AutoLevelSchemes.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace ArcNET.DataTypes.GameObjects.Classes; 7 | 8 | public class AutoLevelSchemes 9 | { 10 | public static AutoLevelSchemes LoadedAutoLevelSchemes = new(); 11 | 12 | [SuppressMessage("ReSharper", "InconsistentNaming")] 13 | public enum Abbreviations 14 | { 15 | //Stats 16 | st, 17 | dx, 18 | cn, 19 | be, 20 | @in, 21 | pe, 22 | wp, 23 | ch, 24 | 25 | //Skills 26 | bow, 27 | dodge, 28 | melee, 29 | throwing, 30 | backstab, 31 | pickpocket, 32 | prowling, 33 | spottrap, 34 | gambling, 35 | haggle, 36 | heal, 37 | persuasion, 38 | repair, 39 | firearms, 40 | picklock, 41 | armtrap, 42 | 43 | //Spells 44 | conveyance, 45 | divination, 46 | air, 47 | earth, 48 | fire, 49 | water, 50 | force, 51 | mental, 52 | meta, 53 | morph, 54 | nature, 55 | necro_evil, 56 | necro_good, 57 | phantasm, 58 | summoning, 59 | temporal, 60 | 61 | //Tech 62 | anatomical, 63 | chemistry, 64 | electric, 65 | explosives, 66 | gun_smithy, 67 | mechanical, 68 | smithy, 69 | therapeutics, 70 | 71 | //Misc 72 | maxhps, 73 | maxfatigue 74 | } 75 | 76 | public class AutoLevelSchemeEntry 77 | { 78 | public int Id; 79 | public string Name; 80 | public List> Data; 81 | 82 | public AutoLevelSchemeEntry(int id, string name) 83 | { 84 | Id = id; 85 | Name = name; 86 | Data = new List>(); 87 | } 88 | } 89 | 90 | public List Entries = new(); 91 | 92 | public static void InitFromText(IEnumerable textData) 93 | { 94 | try 95 | { 96 | foreach (string line in textData) 97 | { 98 | string[] idNameData = line.Split("}", 2); 99 | idNameData[0] = idNameData[0].Replace("{", ""); 100 | var id = int.Parse(idNameData[0]); 101 | 102 | string nameAndData = idNameData[1]; 103 | if (nameAndData.StartsWith("-")) nameAndData.Remove(0); 104 | 105 | string[] nameAndDataSplit = nameAndData.Split("{"); 106 | string name = nameAndDataSplit[0].Trim(); 107 | string data = nameAndDataSplit[1]; 108 | 109 | var schemeEntry = new AutoLevelSchemeEntry(id, name); 110 | 111 | data = data.Replace("}", ""); 112 | 113 | //bad data cleanup 114 | if (data.EndsWith(",")) data = data.Remove(data.Length - 1, 1); 115 | if (data.EndsWith("\t// default level scheme")) data = data.Replace("\t// default level scheme", ""); 116 | if (data.Contains(" ")) data = data.Replace(" ", " "); 117 | 118 | string[] dataArray = data.Split(","); 119 | foreach (string dataValueTuple in dataArray) 120 | { 121 | string[] abbreviationAndValue = dataValueTuple.TrimStart().Split(" "); 122 | string abbreviation = abbreviationAndValue[0]; 123 | var value = int.Parse(abbreviationAndValue[1]); 124 | 125 | Tuple dataTuple = null; 126 | foreach (Abbreviations abrev in (Abbreviations[])Enum.GetValues(typeof(Abbreviations))) 127 | { 128 | string enumValueName = Enum.GetName(typeof(Abbreviations), abrev); 129 | if (enumValueName != null && !enumValueName.Equals(abbreviation)) continue; 130 | 131 | dataTuple = new Tuple(abrev, value); 132 | } 133 | 134 | schemeEntry.Data.Add(dataTuple); 135 | } 136 | 137 | LoadedAutoLevelSchemes.Entries.Add(schemeEntry); 138 | } 139 | } 140 | catch (Exception ex) 141 | { 142 | AnsiConsole.WriteException(ex); 143 | throw; 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/Background.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | using Utils.Console; 5 | 6 | namespace ArcNET.DataTypes.GameObjects.Classes; 7 | 8 | public class Background 9 | { 10 | public static List LoadedBackgrounds = new(); 11 | 12 | public enum RaceGenderCombo 13 | { 14 | HumanFemale, // HUF - human female 15 | HumanMale, // HUM - human male 16 | DwarfFemale, // DWF - dwarf female 17 | DwarfMale, // DWM - dwarf male 18 | ElfFemale, // ELF - elf female 19 | ElfMale, // ELM - elf male 20 | HalfElfFemale, // HEF - half elf female 21 | HalfElfMale, // HEM - half elf male 22 | GnomeFemale, // GNF - gnome female 23 | GnomeMale, // GNM - gnome male 24 | HalflingFemale, // HAF - halfling female 25 | HalflingMale, // HAM - halfling male 26 | HalfOrcFemale, // HOF - half orc female 27 | HalfOrcMale, // HOM - half orc male 28 | HalfOrgeFemale, // HGF - half ogre female 29 | HalfOrgeMale, // HGM - half ogre male 30 | AnyNPC, // NPC - any NPC can use this background, regardless of their race or gender 31 | Any, // ANY - anyone can use this background (same as blank line, included for clarity) 32 | } 33 | 34 | public int DescriptionMessageIndex; 35 | public int EffectMessageIndex; 36 | public List ValidRaceGenderCombinations; 37 | public int MoneyGiven; 38 | public List ItemsGiven; 39 | 40 | public static void InitFromText(IEnumerable textData) 41 | { 42 | Background curBackground = null; 43 | 44 | try 45 | { 46 | foreach (string line in textData) 47 | { 48 | string[] indexAndData = line.Split("}", 2); 49 | indexAndData[0] = indexAndData[0].Replace("{", ""); 50 | var index = int.Parse(indexAndData[0]); 51 | 52 | indexAndData[1] = indexAndData[1].Replace("{", ""); 53 | indexAndData[1] = indexAndData[1].Replace("}", ""); 54 | indexAndData[1] = indexAndData[1].TrimEnd(); 55 | string data = indexAndData[1]; 56 | if (data.Contains("//")) 57 | { 58 | string[] idAndName = data.Split("//"); 59 | data = idAndName[0]; 60 | } 61 | 62 | if (index == 0 || index % 10 == 0) 63 | curBackground = new Background 64 | { 65 | DescriptionMessageIndex = int.Parse(data), 66 | ValidRaceGenderCombinations = new List(), 67 | ItemsGiven = new List() 68 | }; 69 | else switch (index % 10) 70 | { 71 | case 1: 72 | if (data.Equals("")) break; 73 | 74 | if (curBackground != null) 75 | curBackground.EffectMessageIndex = int.Parse(data); 76 | break; 77 | case 2: 78 | string[] combos = indexAndData[1].Split(" "); 79 | foreach (string combo in combos) 80 | switch (combo) 81 | { 82 | case "HUF": 83 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HumanFemale); 84 | break; 85 | case "HUM": 86 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HumanMale); 87 | break; 88 | case "DWF": 89 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.DwarfFemale); 90 | break; 91 | case "DWM": 92 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.DwarfMale); 93 | break; 94 | case "ELF": 95 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.ElfFemale); 96 | break; 97 | case "ELM": 98 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.ElfMale); 99 | break; 100 | case "HEF": 101 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HalfElfFemale); 102 | break; 103 | case "HEM": 104 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HalfElfMale); 105 | break; 106 | case "GNF": 107 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.GnomeFemale); 108 | break; 109 | case "GNM": 110 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.GnomeMale); 111 | break; 112 | case "HAF": 113 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HalflingFemale); 114 | break; 115 | case "HAM": 116 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HalflingMale); 117 | break; 118 | case "HOF": 119 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HalfOrcFemale); 120 | break; 121 | case "HOM": 122 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HalfOrcMale); 123 | break; 124 | case "HGF": 125 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HalfOrgeFemale); 126 | break; 127 | case "HGM": 128 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.HalfOrgeMale); 129 | break; 130 | case "NPC": 131 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.AnyNPC); 132 | break; 133 | case "" or "ANY" or "400": //400 is supposed to be money given 134 | curBackground?.ValidRaceGenderCombinations.Add(RaceGenderCombo.Any); 135 | break; 136 | 137 | default: 138 | ConsoleExtensions.Log($"Unrecognized race gender combo:|{combo}|", "warn"); 139 | break; 140 | } 141 | 142 | break; 143 | case 3: 144 | if (data.Equals("")) data = "400"; 145 | if (curBackground != null) curBackground.MoneyGiven = int.Parse(data); 146 | 147 | break; 148 | case 4: 149 | string[] items = indexAndData[1].Split(" "); 150 | if (items[0].Equals("")) 151 | { 152 | LoadedBackgrounds.Add(curBackground); 153 | break; 154 | } 155 | 156 | foreach (string itemId in items) 157 | curBackground?.ItemsGiven.Add(int.Parse(itemId)); 158 | 159 | LoadedBackgrounds.Add(curBackground); 160 | break; 161 | } 162 | } 163 | } 164 | catch (Exception ex) 165 | { 166 | AnsiConsole.WriteException(ex); 167 | throw; 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/CritterXpLevels.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ArcNET.DataTypes.GameObjects.Classes; 6 | 7 | public class CritterXpLevels 8 | { 9 | public static CritterXpLevels LoadedCritterXpLevels = new(); 10 | 11 | public class CritterXpLevelEntry 12 | { 13 | public int Level; 14 | public int Experience; 15 | 16 | public CritterXpLevelEntry(int level, int experience) 17 | { 18 | Level = level; 19 | Experience = experience; 20 | } 21 | } 22 | 23 | public List Entries = new(); 24 | 25 | public static void InitFromText(IEnumerable textData) 26 | { 27 | try 28 | { 29 | foreach (string line in textData) 30 | { 31 | string[] levelAndXp = line.Split("}", 2); 32 | levelAndXp[0] = levelAndXp[0].Replace("{", ""); 33 | var level = int.Parse(levelAndXp[0]); 34 | levelAndXp[1] = levelAndXp[1].Replace("{", ""); 35 | levelAndXp[1] = levelAndXp[1].Replace("}", ""); 36 | levelAndXp[1] = levelAndXp[1].TrimEnd(); 37 | var xp = int.Parse(levelAndXp[1]); 38 | 39 | LoadedCritterXpLevels.Entries.Add(new CritterXpLevelEntry(level, xp)); 40 | } 41 | } 42 | catch (Exception ex) 43 | { 44 | AnsiConsole.WriteException(ex); 45 | throw; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/Descriptions.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Utils.Console; 6 | 7 | namespace ArcNET.DataTypes.GameObjects.Classes; 8 | 9 | public class Descriptions 10 | { 11 | public static Descriptions LoadedDescriptions = new(); 12 | 13 | public class DescriptionsEntry 14 | { 15 | public int Id; 16 | public string Name; 17 | 18 | public DescriptionsEntry(int id, string name) 19 | { 20 | Id = id; 21 | Name = name; 22 | } 23 | } 24 | 25 | public List Entries = new(); 26 | 27 | public static string GetNameFromId(int id) 28 | { 29 | var entries = LoadedDescriptions.Entries.Where(entry => entry.Id.Equals(id)).ToList(); 30 | if (entries.Count == 0) return $"NOT_FOUND id:{id}"; 31 | 32 | string name = entries.First().Name; 33 | ConsoleExtensions.Log($"Entries found: |{entries.Count}| Name: |{name}|", "info"); 34 | 35 | return name; 36 | } 37 | 38 | public static void InitFromText(IEnumerable textData) 39 | { 40 | try 41 | { 42 | foreach (string line in textData) 43 | { 44 | string noFirstBrace = line.Replace("{", ""); 45 | string[] idName = noFirstBrace.Split("}", 2); 46 | var id = int.Parse(idName[0]); 47 | idName[1] = idName[1].Replace("}", ""); 48 | 49 | LoadedDescriptions.Entries.Add(new DescriptionsEntry(id, idName[1])); 50 | } 51 | } 52 | catch (Exception ex) 53 | { 54 | AnsiConsole.WriteException(ex); 55 | throw; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/Entity.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.GameObjects.Flags; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ArcNET.DataTypes.GameObjects.Classes; 6 | 7 | public class Entity 8 | { 9 | public enum BasicStatType 10 | { 11 | Gender, 12 | Race, 13 | Strength, 14 | Dexterity, 15 | Constitution, 16 | Beauty, 17 | Intelligence, 18 | Willpower, 19 | Charisma, 20 | Perception, 21 | TechPoints, 22 | MagickPoints 23 | } 24 | 25 | public enum DamageType 26 | { 27 | Normal, 28 | Fatigue, 29 | Poison, 30 | Electrical, 31 | Fire, 32 | } 33 | 34 | public enum ResistanceType 35 | { 36 | Damage, 37 | Fire, 38 | Electrical, 39 | Poison, 40 | Magic, 41 | } 42 | 43 | public Tuple Description; 44 | public int InternalName; 45 | public int Level; 46 | public Tuple ArtNumberAndPalette; 47 | public int Scale; 48 | public int Alignment; 49 | public List ObjectFlags; 50 | public List CritterFlags; //4 critter flags 51 | public List CritterFlags2; //4 critter flags2 52 | public List NpcFlags; //4 npc flags? 53 | public List BlitFlags; //4 blit flags? 54 | public List SpellFlags; //4 spell flags? 55 | public int HitChart; 56 | public List> BasicStats; // max 12 basic stats 57 | public List Spells; //max 10?? 58 | public List> Scripts; // max ?? scripts 59 | public int Faction; 60 | public int AIPacket; 61 | public int Material; 62 | public int HitPoints; 63 | public int Fatigue; 64 | public List> Resistances; // max 5 resistances 65 | public List> Damages; // max ?? dmg types 66 | public int SoundBank; 67 | public int Category; 68 | public int AutoLevelScheme; 69 | public int InventorySource; 70 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/Faction.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ArcNET.DataTypes.GameObjects.Classes; 6 | 7 | public class Faction 8 | { 9 | public static Faction LoadedFactions = new(); 10 | 11 | public class FactionEntry 12 | { 13 | public int Id; 14 | public string FactionName; 15 | 16 | public FactionEntry(int id, string factionName) 17 | { 18 | Id = id; 19 | FactionName = factionName; 20 | } 21 | } 22 | 23 | public List Entries = new(); 24 | 25 | public static void InitFromText(IEnumerable textData) 26 | { 27 | try 28 | { 29 | foreach (string line in textData) 30 | { 31 | string[] idAndFaction = line.Split("}", 2); 32 | idAndFaction[0] = idAndFaction[0].Replace("{", ""); 33 | var id = int.Parse(idAndFaction[0]); 34 | idAndFaction[1] = idAndFaction[1].Replace("{", ""); 35 | idAndFaction[1] = idAndFaction[1].Replace("}", ""); 36 | idAndFaction[1] = idAndFaction[1].TrimEnd(); 37 | string faction = idAndFaction[1]; 38 | 39 | LoadedFactions.Entries.Add(new FactionEntry(id, faction)); 40 | } 41 | } 42 | catch (Exception ex) 43 | { 44 | AnsiConsole.WriteException(ex); 45 | throw; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/InventorySource.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Utils.Enumeration; 6 | 7 | namespace ArcNET.DataTypes.GameObjects.Classes; 8 | 9 | public class InventorySource 10 | { 11 | public static List LoadedInventorySources = new(); 12 | 13 | public class InventorySourceEntry 14 | { 15 | public int PrototypeId; 16 | public double Chance; 17 | 18 | public InventorySourceEntry(int prototypeId, double chance) 19 | { 20 | PrototypeId = prototypeId; 21 | Chance = chance; 22 | } 23 | } 24 | 25 | public int Id; //0 reserved 26 | public string Name; 27 | public List Entries; 28 | 29 | public static IEnumerable> NamedDropTableFromId(int id) 30 | { 31 | return (from inventorySource in LoadedInventorySources.Where(invSrc => invSrc.Id.Equals(id)) 32 | from inventorySourceEntry in inventorySource.Entries 33 | let name = Descriptions.GetNameFromId(inventorySourceEntry.PrototypeId) 34 | select new Tuple(name, inventorySourceEntry.Chance)).ToList(); 35 | } 36 | 37 | public static void InitFromText(IEnumerable textData) 38 | { 39 | try 40 | { 41 | foreach ((string line, int _) in textData.WithIndex()) 42 | { 43 | string[] idAndData = line.Split("}", 2); 44 | string id = idAndData[0]; 45 | string data = idAndData[1]; 46 | 47 | id = id.Remove(0, 1); 48 | var invSource = new InventorySource 49 | { 50 | Id = int.Parse(id), 51 | Entries = new List() 52 | }; 53 | string[] nameAndData = data.Split(":", 2); 54 | invSource.Name = nameAndData[0]; 55 | 56 | string dropsAndChance = nameAndData[1]; 57 | dropsAndChance = dropsAndChance.Replace("}", ""); 58 | dropsAndChance = dropsAndChance.TrimStart().TrimEnd(); 59 | 60 | //order matters else recursion is needed 61 | string[] badSplits = new[] { " ", " ", " ", " ", " " }; 62 | foreach (string badSplit in badSplits) 63 | { 64 | if (!dropsAndChance.Contains(badSplit)) continue; 65 | 66 | dropsAndChance = dropsAndChance.Replace(badSplit, " "); 67 | } 68 | 69 | //fix buggy data 70 | if (dropsAndChance.Contains(", ")) 71 | dropsAndChance = dropsAndChance.Replace(", ", ","); 72 | if (dropsAndChance.Contains(" 10141 ")) 73 | dropsAndChance = dropsAndChance.Replace(" 10141 ", ""); 74 | 75 | string[] dropChanceArray = dropsAndChance.Split(" "); 76 | foreach (string dropChance in dropChanceArray) 77 | { 78 | string[] dropAndChance = dropChance.Split(","); 79 | var chance = int.Parse(dropAndChance[0]); 80 | var dropId = int.Parse(dropAndChance[1]); 81 | 82 | invSource.Entries.Add(new InventorySourceEntry(dropId, chance)); 83 | } 84 | 85 | LoadedInventorySources.Add(invSource); 86 | } 87 | } 88 | catch (Exception ex) 89 | { 90 | AnsiConsole.WriteException(ex); 91 | throw; 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/InventorySourceBuy.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | using Utils.Enumeration; 5 | 6 | namespace ArcNET.DataTypes.GameObjects.Classes; 7 | 8 | public class InventorySourceBuy 9 | { 10 | public static List LoadedInventoryBuySources = new(); 11 | 12 | public class InventorySourceBuyEntry 13 | { 14 | public int PrototypeId; 15 | 16 | public InventorySourceBuyEntry(int prototypeId) 17 | { 18 | PrototypeId = prototypeId; 19 | } 20 | } 21 | 22 | public int Id; //0 reserved 23 | public string Name; 24 | public List Entries; //TODO: Can be just {all} 25 | 26 | public static void InitFromText(IEnumerable textData) 27 | { 28 | try 29 | { 30 | foreach ((string line, int _) in textData.WithIndex()) 31 | { 32 | string[] idAndData = line.Split("}", 2); 33 | string id = idAndData[0]; 34 | string data = idAndData[1]; 35 | 36 | id = id.Remove(0, 1); 37 | var invSourceBuy = new InventorySourceBuy 38 | { 39 | Id = int.Parse(id), 40 | Entries = new List() 41 | }; 42 | string[] nameAndData = data.Split(":", 2); 43 | invSourceBuy.Name = nameAndData[0]; 44 | string itemId = nameAndData[1]; 45 | 46 | //bad data 47 | if (itemId.Contains(" 0,0")) 48 | itemId = itemId.Replace(" 0,0", ""); 49 | 50 | itemId = itemId.Replace("{", ""); 51 | itemId = itemId.Replace("}", ""); 52 | itemId = itemId.TrimStart().TrimEnd(); 53 | 54 | //placeholders 55 | if (itemId.Equals("all")) itemId = "99999"; 56 | if (itemId.Equals("")) itemId = "00000"; 57 | 58 | //order matters else recursion is needed 59 | string[] badSplits = new[] { " ", " ", " ", " ", " " }; 60 | foreach (string badSplit in badSplits) 61 | { 62 | if (!itemId.Contains(badSplit)) continue; 63 | 64 | itemId = itemId.Replace(badSplit, " "); 65 | } 66 | 67 | string[] boughtItemsIds = itemId.Split(" "); 68 | foreach (string boughtItemId in boughtItemsIds) 69 | invSourceBuy.Entries.Add(new InventorySourceBuyEntry(int.Parse(boughtItemId))); 70 | LoadedInventoryBuySources.Add(invSourceBuy); 71 | } 72 | } 73 | catch (Exception ex) 74 | { 75 | AnsiConsole.WriteException(ex); 76 | throw; 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/Monster.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.GameObjects.Flags; 2 | using Spectre.Console; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using Utils.Console; 8 | using Utils.Enumeration; 9 | 10 | namespace ArcNET.DataTypes.GameObjects.Classes; 11 | 12 | public class Monster : Entity 13 | { 14 | private static List GetWhitespaceIndexes(string input) 15 | { 16 | var whiteSpaceIndexes = new List(); 17 | char[] stringAsChars = input.ToCharArray(); 18 | 19 | foreach ((char item, int index) in stringAsChars.WithIndex()) 20 | if (char.IsWhiteSpace(item)) 21 | whiteSpaceIndexes.Add(index); 22 | 23 | return whiteSpaceIndexes; 24 | } 25 | 26 | private static ObjFFlags ParseObjFFlags(string paramValue) 27 | { 28 | var flag = (ObjFFlags) 0; 29 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 30 | 31 | if (!((IList)Enum.GetNames(typeof(ObjFFlags))).Contains(trimmedFlag)) 32 | ConsoleExtensions.Log($"Unrecognized ObjFFlags param:|{trimmedFlag}|", "warn"); 33 | 34 | foreach (ObjFFlags objFlag in (ObjFFlags[])Enum.GetValues(typeof(ObjFFlags))) 35 | { 36 | if (!Enum.GetName(typeof(ObjFFlags), objFlag).Equals(trimmedFlag)) continue; 37 | //ConsoleExtensions.Log($"Recognized ObjFFlags param:|{trimmedFlag}|", "success"); 38 | flag = objFlag; 39 | } 40 | 41 | return flag; 42 | } 43 | 44 | private static ObjFCritterFlags ParseObjFCritterFlags(string paramValue) 45 | { 46 | var flag = (ObjFCritterFlags)0; 47 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 48 | 49 | if (!((IList)Enum.GetNames(typeof(ObjFCritterFlags))).Contains(trimmedFlag)) 50 | ConsoleExtensions.Log($"Unrecognized ObjFCritterFlags param:|{trimmedFlag}|", "warn"); 51 | 52 | foreach (ObjFCritterFlags critterFlag in (ObjFCritterFlags[])Enum.GetValues(typeof(ObjFCritterFlags))) 53 | { 54 | if (!Enum.GetName(typeof(ObjFCritterFlags), critterFlag).Equals(trimmedFlag)) continue; 55 | //ConsoleExtensions.Log($"Recognized ObjFCritterFlags param:|{trimmedFlag}|", "success"); 56 | flag = critterFlag; 57 | } 58 | 59 | return flag; 60 | } 61 | 62 | private static ObjFCritterFlags2 ParseObjFCritterFlags2(string paramValue) 63 | { 64 | var flag = (ObjFCritterFlags2)0; 65 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 66 | 67 | if (!((IList)Enum.GetNames(typeof(ObjFCritterFlags2))).Contains(trimmedFlag)) 68 | ConsoleExtensions.Log($"Unrecognized ObjFCritterFlags2 param:|{trimmedFlag}|", "warn"); 69 | 70 | foreach (ObjFCritterFlags2 critterFlag2 in (ObjFCritterFlags2[])Enum.GetValues(typeof(ObjFCritterFlags2))) 71 | { 72 | if (!Enum.GetName(typeof(ObjFCritterFlags2), critterFlag2).Equals(trimmedFlag)) continue; 73 | //ConsoleExtensions.Log($"Recognized ObjFCritterFlags2 param:|{trimmedFlag}|", "success"); 74 | flag = critterFlag2; 75 | } 76 | 77 | return flag; 78 | } 79 | 80 | private static ObjFNpcFlags ParseObjFNpcFlags(string paramValue) 81 | { 82 | var flag = (ObjFNpcFlags)0; 83 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 84 | 85 | if (!((IList)Enum.GetNames(typeof(ObjFNpcFlags))).Contains(trimmedFlag)) 86 | ConsoleExtensions.Log($"Unrecognized ObjFNpcFlags param:|{trimmedFlag}|", "warn"); 87 | 88 | foreach (ObjFNpcFlags npcFlag in (ObjFNpcFlags[])Enum.GetValues(typeof(ObjFNpcFlags))) 89 | { 90 | if (!Enum.GetName(typeof(ObjFNpcFlags), npcFlag).Equals(trimmedFlag)) continue; 91 | //ConsoleExtensions.Log($"Recognized ObjFNpcFlags param:|{trimmedFlag}|", "success"); 92 | flag = npcFlag; 93 | } 94 | 95 | return flag; 96 | } 97 | 98 | private static ObjFBlitFlag ParseObjFBlitFlag(string paramValue) 99 | { 100 | var flag = (ObjFBlitFlag)0; 101 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 102 | 103 | if (!((IList)Enum.GetNames(typeof(ObjFBlitFlag))).Contains(trimmedFlag)) 104 | ConsoleExtensions.Log($"Unrecognized ObjFBlitFlag param:|{trimmedFlag}|", "warn"); 105 | 106 | foreach (ObjFBlitFlag blitFlags in (ObjFBlitFlag[])Enum.GetValues(typeof(ObjFBlitFlag))) 107 | { 108 | if (!Enum.GetName(typeof(ObjFBlitFlag), blitFlags).Equals(trimmedFlag)) continue; 109 | //ConsoleExtensions.Log($"Recognized ObjFBlitFlag param:|{trimmedFlag}|", "success"); 110 | flag = blitFlags; 111 | } 112 | 113 | return flag; 114 | } 115 | 116 | private static ObjFSpellFlags ParseObjFSpellFlags(string paramValue) 117 | { 118 | var flag = (ObjFSpellFlags)0; 119 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 120 | 121 | if (!((IList)Enum.GetNames(typeof(ObjFSpellFlags))).Contains(trimmedFlag)) 122 | ConsoleExtensions.Log($"Unrecognized ObjFSpellFlags param:|{trimmedFlag}|", "warn"); 123 | 124 | foreach (ObjFSpellFlags spellFlags in (ObjFSpellFlags[])Enum.GetValues(typeof(ObjFSpellFlags))) 125 | { 126 | if (!Enum.GetName(typeof(ObjFSpellFlags), spellFlags).Equals(trimmedFlag)) continue; 127 | //ConsoleExtensions.Log($"Recognized ObjFSpellFlags param:|{trimmedFlag}|", "success"); 128 | flag = spellFlags; 129 | } 130 | 131 | return flag; 132 | } 133 | 134 | private static Tuple GetBasicStat(string paramValue) 135 | { 136 | Tuple basicStatTuple = null; 137 | string trimmedStats = paramValue.TrimStart(); 138 | 139 | //skip junk 140 | if (trimmedStats.Equals("Strength 13") || trimmedStats.Equals("Dexterity 15") || 141 | trimmedStats.Equals("Dexterity 14")) return null; 142 | 143 | var separator = "\t\t"; 144 | if (trimmedStats.Contains("Gender") || trimmedStats.Contains("Race")) 145 | separator = " "; 146 | if (trimmedStats.Contains("tech points") || trimmedStats.Contains("magick points")) 147 | { 148 | List whitespaceIndexes = GetWhitespaceIndexes(trimmedStats); 149 | if (whitespaceIndexes.Count >= 2) 150 | trimmedStats = trimmedStats.Remove(whitespaceIndexes.First(), 1); 151 | //ConsoleExtensions.Log($"trimmedStats after removal:|{trimmedStats}|", "warn"); 152 | separator = " "; 153 | } 154 | 155 | if (trimmedStats.Contains("Constitution") || trimmedStats.Contains("Intelligence")) 156 | separator = "\t"; 157 | 158 | string[] statAndValue = trimmedStats.Split(separator, 2); 159 | string statType = statAndValue[0].Trim(); 160 | statType = statType switch 161 | { 162 | "magickpoints" => "MagickPoints", 163 | "techpoints" => "TechPoints", 164 | _ => statType 165 | }; 166 | 167 | if (!((IList)Enum.GetNames(typeof(BasicStatType))).Contains(statType)) 168 | ConsoleExtensions.Log($"unrecognized Entity.BasicStatType param:|{statType}|", "warn"); 169 | 170 | string statValue = statAndValue[1].Trim(); 171 | //ConsoleExtensions.Log($"statType:|{statType}| value:|{statValue}|", "warn"); 172 | 173 | foreach (BasicStatType basicStatType in (BasicStatType[])Enum.GetValues(typeof(BasicStatType))) 174 | { 175 | string enumValueName = Enum.GetName(typeof(BasicStatType), basicStatType); 176 | if (!enumValueName.Equals(statType)) 177 | //ConsoleExtensions.Log($"Failed to match enumValueName:|{enumValueName}| vs statType:|{statType}|", "warn"); 178 | continue; 179 | 180 | basicStatTuple = new Tuple(basicStatType, int.Parse(statValue)); 181 | } 182 | 183 | return basicStatTuple; 184 | } 185 | 186 | private static Tuple GetResistTuple(string paramName, string paramValue) 187 | { 188 | string trimmedResist = paramName.TrimStart(); 189 | string[] resist = trimmedResist.Split(" ", 2); 190 | string resistTypeStr = resist[0]; 191 | var resistanceTypes = (ResistanceType[])Enum.GetValues(typeof(ResistanceType)); 192 | 193 | return (from resistType in resistanceTypes 194 | let resistTypeName = Enum.GetName(typeof(ResistanceType), resistType) 195 | where resistTypeName.Equals(resistTypeStr) 196 | select new Tuple(resistType, int.Parse(paramValue))) 197 | .FirstOrDefault(); 198 | } 199 | 200 | private static Tuple GetDmgTuple(string paramName, string paramValue) 201 | { 202 | string trimmedDmg = paramValue.TrimStart(); 203 | string[] dmgRange = trimmedDmg.Split(" ", 2); 204 | var min = int.Parse(dmgRange[0]); 205 | var max = int.Parse(dmgRange[1]); 206 | 207 | return paramName switch 208 | { 209 | "Normal Damage" => new Tuple(DamageType.Normal, min, max), 210 | "Fatigue Damage" => new Tuple(DamageType.Fatigue, min, max), 211 | "Poison Damage" => new Tuple(DamageType.Poison, min, max), 212 | "Electrical Damage" => new Tuple(DamageType.Electrical, min, max), 213 | "Fire Damage" => new Tuple(DamageType.Fire, min, max), 214 | _ => null 215 | }; 216 | } 217 | 218 | public static Monster GetFromText(IEnumerable mobText) 219 | { 220 | var monster = new Monster 221 | { 222 | ObjectFlags = new List(), 223 | CritterFlags = new List(), 224 | CritterFlags2 = new List(), 225 | NpcFlags = new List(), 226 | BlitFlags = new List(), 227 | SpellFlags = new List(), 228 | BasicStats = new List>(), 229 | Spells = new List(), 230 | Scripts = new List>(), 231 | Resistances = new List>(), 232 | Damages = new List>() 233 | }; 234 | 235 | foreach (string curLine in mobText) 236 | { 237 | if (string.IsNullOrWhiteSpace(curLine)) continue; 238 | 239 | string[] lines = curLine.Split(":", 2); 240 | string paramName = lines[0]; 241 | string paramValue = lines[1]; 242 | 243 | try 244 | { 245 | switch (paramName) 246 | { 247 | case "Description" when paramValue.Contains("//"): 248 | { 249 | string[] idAndName = paramValue.Split("//", 2); 250 | string monsterId = idAndName[0]; 251 | string monsterName = idAndName[1]; 252 | 253 | monster.Description = new Tuple(int.Parse(monsterId), monsterName); 254 | break; 255 | } 256 | case "Description": 257 | //ConsoleExtensions.Log($"paramValue:|{paramValue}|", "warn"); 258 | break; 259 | case "Internal Name" or "internal name": 260 | monster.InternalName = int.Parse(paramValue); 261 | break; 262 | case "Level": 263 | monster.Level = int.Parse(paramValue); 264 | break; 265 | case "Art Number and Palette": 266 | string trimmed = paramValue.TrimStart(); 267 | string[] artNumberAndPalette = trimmed.Split(" ", 2); 268 | string artNumber = artNumberAndPalette[0]; 269 | string paletteNumber = artNumberAndPalette[1]; 270 | 271 | monster.ArtNumberAndPalette = new Tuple(int.Parse(artNumber), int.Parse(paletteNumber)); 272 | break; 273 | case "Scale": 274 | monster.Scale = int.Parse(paramValue); 275 | break; 276 | case "Alignment": 277 | monster.Alignment = int.Parse(paramValue); 278 | break; 279 | case "Object Flag": 280 | monster.ObjectFlags.Add(ParseObjFFlags(paramValue)); 281 | break; 282 | case "Critter Flag": 283 | monster.CritterFlags.Add(ParseObjFCritterFlags(paramValue)); 284 | break; 285 | case "Critter2 Flag": 286 | monster.CritterFlags2.Add(ParseObjFCritterFlags2(paramValue)); 287 | break; 288 | case "NPC Flag": 289 | monster.NpcFlags.Add(ParseObjFNpcFlags(paramValue)); 290 | break; 291 | case "Blit Flag": 292 | monster.BlitFlags.Add(ParseObjFBlitFlag(paramValue)); 293 | break; 294 | case "Spell Flag": 295 | monster.SpellFlags.Add(ParseObjFSpellFlags(paramValue)); 296 | break; 297 | case "Hit Chart": 298 | monster.HitChart = int.Parse(paramValue); 299 | break; 300 | case "Basic Stat" or "basic stat": 301 | monster.BasicStats.Add(GetBasicStat(paramValue)); 302 | break; 303 | case "Spell" or "spell": 304 | monster.Spells.Add(paramValue); 305 | break; 306 | case "Script": 307 | string trimmedScript = paramValue.TrimStart(); 308 | string[] scriptParams = trimmedScript.Split(" ", 6); 309 | 310 | //foreach (var paramVal in scriptParams) 311 | // ConsoleExtensions.Log($"script param value:|{paramVal}|", "warn"); 312 | 313 | var paramValues = scriptParams.Select(int.Parse).ToList(); 314 | 315 | monster.Scripts.Add(new Tuple(paramValues[0], paramValues[1], paramValues[2], paramValues[3], paramValues[4], paramValues[5])); 316 | break; 317 | case "Faction": 318 | monster.Faction = int.Parse(paramValue); 319 | break; 320 | case "AI Packet": 321 | if (paramValue.Contains("//")) 322 | paramValue = paramValue.Split("//")[0].Trim(); 323 | 324 | monster.AIPacket = int.Parse(paramValue); 325 | break; 326 | case "Material": 327 | if (paramValue.Contains("//")) 328 | paramValue = paramValue.Split("//")[0].Trim(); 329 | 330 | monster.Material = int.Parse(paramValue); 331 | break; 332 | case "Hit Points": 333 | monster.HitPoints = int.Parse(paramValue); 334 | break; 335 | case "Fatigue": 336 | monster.Fatigue = int.Parse(paramValue); 337 | break; 338 | case "Damage Resistance" or "damage resistance": 339 | monster.Resistances.Add(GetResistTuple(paramName, paramValue)); 340 | break; 341 | case "Fire Resistance": 342 | monster.Resistances.Add(GetResistTuple(paramName, paramValue)); 343 | break; 344 | case "Electrical Resistance": 345 | monster.Resistances.Add(GetResistTuple(paramName, paramValue)); 346 | break; 347 | case "Poison Resistance": 348 | monster.Resistances.Add(GetResistTuple(paramName, paramValue)); 349 | break; 350 | case "Magic Resistance": 351 | monster.Resistances.Add(GetResistTuple(paramName, paramValue)); 352 | break; 353 | case "Normal Damage": 354 | monster.Damages.Add(GetDmgTuple(paramName, paramValue)); 355 | break; 356 | case "Fatigue Damage": 357 | monster.Damages.Add(GetDmgTuple(paramName, paramValue)); 358 | break; 359 | case "Poison Damage": 360 | monster.Damages.Add(GetDmgTuple(paramName, paramValue)); 361 | break; 362 | case "Electrical Damage": 363 | monster.Damages.Add(GetDmgTuple(paramName, paramValue)); 364 | break; 365 | case "Fire Damage": 366 | monster.Damages.Add(GetDmgTuple(paramName, paramValue)); 367 | break; 368 | case "Sound Bank" or "sound bank": 369 | monster.SoundBank = int.Parse(paramValue); 370 | break; 371 | case "Category": 372 | monster.Category = int.Parse(paramValue); 373 | break; 374 | case "Auto Level Scheme": 375 | monster.AutoLevelScheme = int.Parse(paramValue); 376 | break; 377 | case "Inventory Source": 378 | monster.InventorySource = int.Parse(paramValue); 379 | break; 380 | 381 | default: 382 | ConsoleExtensions.Log($"unrecognized entity param:|{paramName}|", "error"); 383 | break; 384 | } 385 | } 386 | catch (Exception ex) 387 | { 388 | AnsiConsole.WriteException(ex); 389 | throw; 390 | } 391 | } 392 | 393 | return monster; 394 | } 395 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/NPC.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.GameObjects.Flags; 2 | using Spectre.Console; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using Utils.Console; 8 | 9 | namespace ArcNET.DataTypes.GameObjects.Classes; 10 | 11 | public class NPC : Entity 12 | { 13 | public int Portrait; 14 | public int RetailPriceMultiplier; 15 | public int SocialClass; 16 | 17 | private static ObjFFlags ParseObjFFlags(string paramValue) 18 | { 19 | var flag = (ObjFFlags)0; 20 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 21 | 22 | if (!((IList)Enum.GetNames(typeof(ObjFFlags))).Contains(trimmedFlag)) 23 | ConsoleExtensions.Log($"Unrecognized ObjFFlags param:|{trimmedFlag}|", "warn"); 24 | 25 | foreach (ObjFFlags objFlag in (ObjFFlags[])Enum.GetValues(typeof(ObjFFlags))) 26 | { 27 | if (!Enum.GetName(typeof(ObjFFlags), objFlag).Equals(trimmedFlag)) continue; 28 | //ConsoleExtensions.Log($"Recognized ObjFFlags param:|{trimmedFlag}|", "success"); 29 | flag = objFlag; 30 | } 31 | 32 | return flag; 33 | } 34 | 35 | private static ObjFCritterFlags ParseObjFCritterFlags(string paramValue) 36 | { 37 | var flag = (ObjFCritterFlags)0; 38 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 39 | 40 | if (!((IList)Enum.GetNames(typeof(ObjFCritterFlags))).Contains(trimmedFlag)) 41 | ConsoleExtensions.Log($"Unrecognized ObjFCritterFlags param:|{trimmedFlag}|", "warn"); 42 | 43 | foreach (ObjFCritterFlags critterFlag in (ObjFCritterFlags[])Enum.GetValues(typeof(ObjFCritterFlags))) 44 | { 45 | if (!Enum.GetName(typeof(ObjFCritterFlags), critterFlag).Equals(trimmedFlag)) continue; 46 | //ConsoleExtensions.Log($"Recognized ObjFCritterFlags param:|{trimmedFlag}|", "success"); 47 | flag = critterFlag; 48 | } 49 | 50 | return flag; 51 | } 52 | 53 | private static ObjFCritterFlags2 ParseObjFCritterFlags2(string paramValue) 54 | { 55 | var flag = (ObjFCritterFlags2)0; 56 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 57 | 58 | if (!((IList)Enum.GetNames(typeof(ObjFCritterFlags2))).Contains(trimmedFlag)) 59 | ConsoleExtensions.Log($"Unrecognized ObjFCritterFlags2 param:|{trimmedFlag}|", "warn"); 60 | 61 | foreach (ObjFCritterFlags2 critterFlag2 in (ObjFCritterFlags2[])Enum.GetValues(typeof(ObjFCritterFlags2))) 62 | { 63 | if (!Enum.GetName(typeof(ObjFCritterFlags2), critterFlag2).Equals(trimmedFlag)) continue; 64 | //ConsoleExtensions.Log($"Recognized ObjFCritterFlags2 param:|{trimmedFlag}|", "success"); 65 | flag = critterFlag2; 66 | } 67 | 68 | return flag; 69 | } 70 | 71 | private static ObjFNpcFlags ParseObjFNpcFlags(string paramValue) 72 | { 73 | var flag = (ObjFNpcFlags)0; 74 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 75 | 76 | if (!((IList)Enum.GetNames(typeof(ObjFNpcFlags))).Contains(trimmedFlag)) 77 | ConsoleExtensions.Log($"Unrecognized ObjFNpcFlags param:|{trimmedFlag}|", "warn"); 78 | 79 | foreach (ObjFNpcFlags npcFlag in (ObjFNpcFlags[])Enum.GetValues(typeof(ObjFNpcFlags))) 80 | { 81 | if (!Enum.GetName(typeof(ObjFNpcFlags), npcFlag).Equals(trimmedFlag)) continue; 82 | //ConsoleExtensions.Log($"Recognized ObjFNpcFlags param:|{trimmedFlag}|", "success"); 83 | flag = npcFlag; 84 | } 85 | 86 | return flag; 87 | } 88 | 89 | private static ObjFBlitFlag ParseObjFBlitFlag(string paramValue) 90 | { 91 | var flag = (ObjFBlitFlag)0; 92 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 93 | 94 | if (!((IList)Enum.GetNames(typeof(ObjFBlitFlag))).Contains(trimmedFlag)) 95 | ConsoleExtensions.Log($"Unrecognized ObjFBlitFlag param:|{trimmedFlag}|", "warn"); 96 | 97 | foreach (ObjFBlitFlag blitFlags in (ObjFBlitFlag[])Enum.GetValues(typeof(ObjFBlitFlag))) 98 | { 99 | if (!Enum.GetName(typeof(ObjFBlitFlag), blitFlags).Equals(trimmedFlag)) continue; 100 | //ConsoleExtensions.Log($"Recognized ObjFBlitFlag param:|{trimmedFlag}|", "success"); 101 | flag = blitFlags; 102 | } 103 | 104 | return flag; 105 | } 106 | 107 | private static ObjFSpellFlags ParseObjFSpellFlags(string paramValue) 108 | { 109 | var flag = (ObjFSpellFlags)0; 110 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 111 | 112 | if (!((IList)Enum.GetNames(typeof(ObjFSpellFlags))).Contains(trimmedFlag)) 113 | ConsoleExtensions.Log($"Unrecognized ObjFSpellFlags param:|{trimmedFlag}|", "warn"); 114 | 115 | foreach (ObjFSpellFlags spellFlags in (ObjFSpellFlags[])Enum.GetValues(typeof(ObjFSpellFlags))) 116 | { 117 | if (!Enum.GetName(typeof(ObjFSpellFlags), spellFlags).Equals(trimmedFlag)) continue; 118 | //ConsoleExtensions.Log($"Recognized ObjFSpellFlags param:|{trimmedFlag}|", "success"); 119 | flag = spellFlags; 120 | } 121 | 122 | return flag; 123 | } 124 | 125 | private static Tuple GetBasicStat(string paramValue) 126 | { 127 | Tuple basicStatTuple = null; 128 | string trimmedStats = paramValue.TrimStart(); 129 | string[] statAndValue = trimmedStats.Split(" ", 2); 130 | string statType = statAndValue[0].Trim(); 131 | 132 | if (!((IList)Enum.GetNames(typeof(BasicStatType))).Contains(statType)) 133 | ConsoleExtensions.Log($"unrecognized Entity.BasicStatType param:|{statType}|", "warn"); 134 | 135 | string statValue = statAndValue[1].Trim(); 136 | //ConsoleExtensions.Log($"statType:|{statType}| value:|{statValue}|", "warn"); 137 | 138 | foreach (BasicStatType basicStatType in (BasicStatType[])Enum.GetValues(typeof(BasicStatType))) 139 | { 140 | string enumValueName = Enum.GetName(typeof(BasicStatType), basicStatType); 141 | if (!enumValueName.Equals(statType)) 142 | //ConsoleExtensions.Log($"Failed to match enumValueName:|{enumValueName}| vs statType:|{statType}|", "warn"); 143 | continue; 144 | 145 | basicStatTuple = new Tuple(basicStatType, int.Parse(statValue)); 146 | } 147 | 148 | return basicStatTuple; 149 | } 150 | 151 | private static Tuple GetResistTuple(string paramName, string paramValue) 152 | { 153 | string trimmedResist = paramName.TrimStart(); 154 | string[] resist = trimmedResist.Split(" ", 2); 155 | string resistTypeStr = resist[0]; 156 | var resistanceTypes = (ResistanceType[])Enum.GetValues(typeof(ResistanceType)); 157 | 158 | return (from resistType in resistanceTypes 159 | let resistTypeName = Enum.GetName(typeof(ResistanceType), resistType) 160 | where resistTypeName.Equals(resistTypeStr) 161 | select new Tuple(resistType, int.Parse(paramValue))) 162 | .FirstOrDefault(); 163 | } 164 | 165 | private static Tuple GetDmgTuple(string paramName, string paramValue) 166 | { 167 | string trimmedDmg = paramValue.TrimStart(); 168 | string[] dmgRange = trimmedDmg.Split(" ", 2); 169 | var min = int.Parse(dmgRange[0]); 170 | var max = int.Parse(dmgRange[1]); 171 | 172 | return paramName switch 173 | { 174 | "Normal Damage" => new Tuple(DamageType.Normal, min, max), 175 | "Fatigue Damage" => new Tuple(DamageType.Fatigue, min, max), 176 | "Poison Damage" => new Tuple(DamageType.Poison, min, max), 177 | "Electrical Damage" => new Tuple(DamageType.Electrical, min, max), 178 | "Fire Damage" => new Tuple(DamageType.Fire, min, max), 179 | _ => null 180 | }; 181 | } 182 | 183 | public static NPC GetFromText(IEnumerable npcText) 184 | { 185 | var npc = new NPC 186 | { 187 | ObjectFlags = new List(), 188 | CritterFlags = new List(), 189 | CritterFlags2 = new List(), 190 | NpcFlags = new List(), 191 | BlitFlags = new List(), 192 | SpellFlags = new List(), 193 | BasicStats = new List>(), 194 | Spells = new List(), 195 | Scripts = new List>(), 196 | Resistances = new List>(), 197 | Damages = new List>() 198 | }; 199 | 200 | foreach (string curLine in npcText) 201 | { 202 | if (string.IsNullOrWhiteSpace(curLine)) continue; 203 | 204 | string[] lines = curLine.Split(":", 2); 205 | string paramName = lines[0]; 206 | string paramValue = lines[1]; 207 | 208 | try 209 | { 210 | switch (paramName) 211 | { 212 | case "Description" when paramValue.Contains("//"): 213 | { 214 | string[] idAndName = paramValue.Split("//", 2); 215 | string monsterId = idAndName[0]; 216 | string monsterName = idAndName[1]; 217 | 218 | npc.Description = new Tuple(int.Parse(monsterId), monsterName); 219 | break; 220 | } 221 | case "Description": 222 | break; 223 | case "Internal Name" or "internal name": 224 | npc.InternalName = int.Parse(paramValue); 225 | break; 226 | case "Level": 227 | npc.Level = int.Parse(paramValue); 228 | break; 229 | case "Art Number and Palette": 230 | string trimmed = paramValue.TrimStart(); 231 | string[] artNumberAndPalette = trimmed.Split(" ", 2); 232 | string artNumber = artNumberAndPalette[0]; 233 | string paletteNumber = artNumberAndPalette[1]; 234 | 235 | npc.ArtNumberAndPalette = new Tuple(int.Parse(artNumber), int.Parse(paletteNumber)); 236 | break; 237 | case "Scale": 238 | npc.Scale = int.Parse(paramValue); 239 | break; 240 | case "Alignment": 241 | npc.Alignment = int.Parse(paramValue); 242 | break; 243 | case "Object Flag": 244 | npc.ObjectFlags.Add(ParseObjFFlags(paramValue)); 245 | break; 246 | case "Critter Flag": 247 | npc.CritterFlags.Add(ParseObjFCritterFlags(paramValue)); 248 | break; 249 | case "Critter2 Flag": 250 | npc.CritterFlags2.Add(ParseObjFCritterFlags2(paramValue)); 251 | break; 252 | case "NPC Flag": 253 | npc.NpcFlags.Add(ParseObjFNpcFlags(paramValue)); 254 | break; 255 | case "Blit Flag": 256 | npc.BlitFlags.Add(ParseObjFBlitFlag(paramValue)); 257 | break; 258 | case "Spell Flag": 259 | npc.SpellFlags.Add(ParseObjFSpellFlags(paramValue)); 260 | break; 261 | case "Hit Chart": 262 | npc.HitChart = int.Parse(paramValue); 263 | break; 264 | case "Basic Stat" or "basic stat": 265 | npc.BasicStats.Add(GetBasicStat(paramValue)); 266 | break; 267 | case "Spell" or "spell": 268 | npc.Spells.Add(paramValue); 269 | break; 270 | case "Script": 271 | string trimmedScript = paramValue.TrimStart(); 272 | string[] scriptParams = trimmedScript.Split(" ", 6); 273 | var paramValues = scriptParams.Select(int.Parse).ToList(); 274 | 275 | npc.Scripts.Add(new Tuple(paramValues[0], paramValues[1], paramValues[2], paramValues[3], paramValues[4], paramValues[5])); 276 | break; 277 | case "Faction": 278 | npc.Faction = int.Parse(paramValue); 279 | break; 280 | case "AI Packet": 281 | if (paramValue.Contains("//")) 282 | paramValue = paramValue.Split("//")[0].Trim(); 283 | 284 | npc.AIPacket = int.Parse(paramValue); 285 | break; 286 | case "Material": 287 | if (paramValue.Contains("//")) 288 | paramValue = paramValue.Split("//")[0].Trim(); 289 | 290 | npc.Material = int.Parse(paramValue); 291 | break; 292 | case "Hit Points": 293 | npc.HitPoints = int.Parse(paramValue); 294 | break; 295 | case "Fatigue": 296 | npc.Fatigue = int.Parse(paramValue); 297 | break; 298 | case "Damage Resistance" or "damage resistance": 299 | npc.Resistances.Add(GetResistTuple(paramName, paramValue)); 300 | break; 301 | case "Fire Resistance": 302 | npc.Resistances.Add(GetResistTuple(paramName, paramValue)); 303 | break; 304 | case "Electrical Resistance": 305 | npc.Resistances.Add(GetResistTuple(paramName, paramValue)); 306 | break; 307 | case "Poison Resistance": 308 | npc.Resistances.Add(GetResistTuple(paramName, paramValue)); 309 | break; 310 | case "Magic Resistance": 311 | npc.Resistances.Add(GetResistTuple(paramName, paramValue)); 312 | break; 313 | case "Normal Damage": 314 | npc.Damages.Add(GetDmgTuple(paramName, paramValue)); 315 | break; 316 | case "Fatigue Damage": 317 | npc.Damages.Add(GetDmgTuple(paramName, paramValue)); 318 | break; 319 | case "Poison Damage": 320 | npc.Damages.Add(GetDmgTuple(paramName, paramValue)); 321 | break; 322 | case "Electrical Damage": 323 | npc.Damages.Add(GetDmgTuple(paramName, paramValue)); 324 | break; 325 | case "Fire Damage": 326 | npc.Damages.Add(GetDmgTuple(paramName, paramValue)); 327 | break; 328 | case "Sound Bank" or "sound bank": 329 | npc.SoundBank = int.Parse(paramValue); 330 | break; 331 | case "Portrait": 332 | npc.Portrait = int.Parse(paramValue); 333 | break; 334 | case "Retail Price Multiplier": 335 | npc.RetailPriceMultiplier = int.Parse(paramValue); 336 | break; 337 | case "Social Class": 338 | npc.SocialClass = int.Parse(paramValue); 339 | break; 340 | case "Category": 341 | npc.Category = int.Parse(paramValue); 342 | break; 343 | case "Auto Level Scheme": 344 | npc.AutoLevelScheme = int.Parse(paramValue); 345 | break; 346 | case "Inventory Source": 347 | npc.InventorySource = int.Parse(paramValue); 348 | break; 349 | 350 | default: 351 | ConsoleExtensions.Log($"unrecognized entity param:|{paramName}|", "error"); 352 | break; 353 | } 354 | } 355 | catch (Exception ex) 356 | { 357 | AnsiConsole.WriteException(ex); 358 | throw; 359 | } 360 | } 361 | 362 | return npc; 363 | } 364 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/QuestXpLevels.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ArcNET.DataTypes.GameObjects.Classes; 6 | 7 | public class QuestXpLevels 8 | { 9 | public static QuestXpLevels LoadedXpQuestLevels = new(); 10 | 11 | public class QuestXpLevelEntry 12 | { 13 | public int Level; 14 | public int Experience; 15 | 16 | public QuestXpLevelEntry(int level, int experience) 17 | { 18 | Level = level; 19 | Experience = experience; 20 | } 21 | } 22 | 23 | public List Entries = new(); 24 | 25 | public static void InitFromText(IEnumerable textData) 26 | { 27 | try 28 | { 29 | foreach (string line in textData) 30 | { 31 | string[] levelAndXp = line.Split("}", 2); 32 | levelAndXp[0] = levelAndXp[0].Replace("{", ""); 33 | var level = int.Parse(levelAndXp[0]); 34 | levelAndXp[1] = levelAndXp[1].Replace("{", ""); 35 | levelAndXp[1] = levelAndXp[1].Replace("}", ""); 36 | levelAndXp[1] = levelAndXp[1].TrimEnd(); 37 | var xp = int.Parse(levelAndXp[1]); 38 | 39 | LoadedXpQuestLevels.Entries.Add(new QuestXpLevelEntry(level, xp)); 40 | } 41 | } 42 | catch (Exception ex) 43 | { 44 | AnsiConsole.WriteException(ex); 45 | throw; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/RandomEncounters.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ArcNET.DataTypes.GameObjects.Classes; 4 | 5 | public class RandomEncounters 6 | { 7 | public static List LoadedEncounters; 8 | 9 | public class EncounterEntry 10 | { 11 | public int PrototypeId; 12 | public int CountMin; 13 | public int CountMax; 14 | public int MinLevel; 15 | public int MaxLevel; 16 | public int GlobalFlag; 17 | } 18 | 19 | public int Id; 20 | public int ChancePercentage; 21 | public List EncounterEntries; 22 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/Unique.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.GameObjects.Flags; 2 | using Spectre.Console; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using Utils.Console; 8 | using Utils.Enumeration; 9 | 10 | namespace ArcNET.DataTypes.GameObjects.Classes; 11 | 12 | public class Unique : Entity 13 | { 14 | private static List GetWhitespaceIndexes(string input) 15 | { 16 | var whiteSpaceIndexes = new List(); 17 | char[] stringAsChars = input.ToCharArray(); 18 | 19 | foreach ((char item, int index) in stringAsChars.WithIndex()) 20 | if (char.IsWhiteSpace(item)) 21 | whiteSpaceIndexes.Add(index); 22 | 23 | return whiteSpaceIndexes; 24 | } 25 | 26 | private static ObjFFlags ParseObjFFlags(string paramValue) 27 | { 28 | var flag = (ObjFFlags)0; 29 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 30 | 31 | if (!((IList)Enum.GetNames(typeof(ObjFFlags))).Contains(trimmedFlag)) 32 | ConsoleExtensions.Log($"Unrecognized ObjFFlags param:|{trimmedFlag}|", "warn"); 33 | 34 | foreach (ObjFFlags objFlag in (ObjFFlags[])Enum.GetValues(typeof(ObjFFlags))) 35 | { 36 | if (!Enum.GetName(typeof(ObjFFlags), objFlag).Equals(trimmedFlag)) continue; 37 | //ConsoleExtensions.Log($"Recognized ObjFFlags param:|{trimmedFlag}|", "success"); 38 | flag = objFlag; 39 | } 40 | 41 | return flag; 42 | } 43 | 44 | private static ObjFCritterFlags ParseObjFCritterFlags(string paramValue) 45 | { 46 | var flag = (ObjFCritterFlags)0; 47 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 48 | 49 | if (!((IList)Enum.GetNames(typeof(ObjFCritterFlags))).Contains(trimmedFlag)) 50 | ConsoleExtensions.Log($"Unrecognized ObjFCritterFlags param:|{trimmedFlag}|", "warn"); 51 | 52 | foreach (ObjFCritterFlags critterFlag in (ObjFCritterFlags[])Enum.GetValues(typeof(ObjFCritterFlags))) 53 | { 54 | if (!Enum.GetName(typeof(ObjFCritterFlags), critterFlag).Equals(trimmedFlag)) continue; 55 | //ConsoleExtensions.Log($"Recognized ObjFCritterFlags param:|{trimmedFlag}|", "success"); 56 | flag = critterFlag; 57 | } 58 | 59 | return flag; 60 | } 61 | 62 | private static ObjFCritterFlags2 ParseObjFCritterFlags2(string paramValue) 63 | { 64 | var flag = (ObjFCritterFlags2)0; 65 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 66 | 67 | if (!((IList)Enum.GetNames(typeof(ObjFCritterFlags2))).Contains(trimmedFlag)) 68 | ConsoleExtensions.Log($"Unrecognized ObjFCritterFlags2 param:|{trimmedFlag}|", "warn"); 69 | 70 | foreach (ObjFCritterFlags2 critterFlag2 in (ObjFCritterFlags2[])Enum.GetValues(typeof(ObjFCritterFlags2))) 71 | { 72 | if (!Enum.GetName(typeof(ObjFCritterFlags2), critterFlag2).Equals(trimmedFlag)) continue; 73 | //ConsoleExtensions.Log($"Recognized ObjFCritterFlags2 param:|{trimmedFlag}|", "success"); 74 | flag = critterFlag2; 75 | } 76 | 77 | return flag; 78 | } 79 | 80 | private static ObjFNpcFlags ParseObjFNpcFlags(string paramValue) 81 | { 82 | var flag = (ObjFNpcFlags)0; 83 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 84 | 85 | if (!((IList)Enum.GetNames(typeof(ObjFNpcFlags))).Contains(trimmedFlag)) 86 | ConsoleExtensions.Log($"Unrecognized ObjFNpcFlags param:|{trimmedFlag}|", "warn"); 87 | 88 | foreach (ObjFNpcFlags npcFlag in (ObjFNpcFlags[])Enum.GetValues(typeof(ObjFNpcFlags))) 89 | { 90 | if (!Enum.GetName(typeof(ObjFNpcFlags), npcFlag).Equals(trimmedFlag)) continue; 91 | //ConsoleExtensions.Log($"Recognized ObjFNpcFlags param:|{trimmedFlag}|", "success"); 92 | flag = npcFlag; 93 | } 94 | 95 | return flag; 96 | } 97 | 98 | private static ObjFBlitFlag ParseObjFBlitFlag(string paramValue) 99 | { 100 | var flag = (ObjFBlitFlag)0; 101 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 102 | 103 | if (!((IList)Enum.GetNames(typeof(ObjFBlitFlag))).Contains(trimmedFlag)) 104 | ConsoleExtensions.Log($"Unrecognized ObjFBlitFlag param:|{trimmedFlag}|", "warn"); 105 | 106 | foreach (ObjFBlitFlag blitFlags in (ObjFBlitFlag[])Enum.GetValues(typeof(ObjFBlitFlag))) 107 | { 108 | if (!Enum.GetName(typeof(ObjFBlitFlag), blitFlags).Equals(trimmedFlag)) continue; 109 | //ConsoleExtensions.Log($"Recognized ObjFBlitFlag param:|{trimmedFlag}|", "success"); 110 | flag = blitFlags; 111 | } 112 | 113 | return flag; 114 | } 115 | 116 | private static ObjFSpellFlags ParseObjFSpellFlags(string paramValue) 117 | { 118 | var flag = (ObjFSpellFlags)0; 119 | string trimmedFlag = paramValue.TrimStart().TrimEnd(); 120 | 121 | if (!((IList)Enum.GetNames(typeof(ObjFSpellFlags))).Contains(trimmedFlag)) 122 | ConsoleExtensions.Log($"Unrecognized ObjFSpellFlags param:|{trimmedFlag}|", "warn"); 123 | 124 | foreach (ObjFSpellFlags spellFlags in (ObjFSpellFlags[])Enum.GetValues(typeof(ObjFSpellFlags))) 125 | { 126 | if (!Enum.GetName(typeof(ObjFSpellFlags), spellFlags).Equals(trimmedFlag)) continue; 127 | //ConsoleExtensions.Log($"Recognized ObjFSpellFlags param:|{trimmedFlag}|", "success"); 128 | flag = spellFlags; 129 | } 130 | 131 | return flag; 132 | } 133 | 134 | private static Tuple GetBasicStat(string paramValue) 135 | { 136 | Tuple basicStatTuple = null; 137 | string trimmedStats = paramValue.TrimStart(); 138 | 139 | //skip junk 140 | if (trimmedStats.Equals("Strength 13") || trimmedStats.Equals("Dexterity 15") || 141 | trimmedStats.Equals("Dexterity 14")) return null; 142 | 143 | var separator = "\t\t"; 144 | if (trimmedStats.Contains("Gender") || trimmedStats.Contains("Race")) 145 | separator = " "; 146 | if (trimmedStats.Contains("tech points") || trimmedStats.Contains("magick points")) 147 | { 148 | List whitespaceIndexes = GetWhitespaceIndexes(trimmedStats); 149 | if (whitespaceIndexes.Count >= 2) 150 | trimmedStats = trimmedStats.Remove(whitespaceIndexes.First(), 1); 151 | //ConsoleExtensions.Log($"trimmedStats after removal:|{trimmedStats}|", "warn"); 152 | separator = " "; 153 | } 154 | 155 | if (trimmedStats.Contains("Constitution") || trimmedStats.Contains("Intelligence")) 156 | separator = "\t"; 157 | 158 | string[] statAndValue = trimmedStats.Split(separator, 2); 159 | string statType = statAndValue[0].Trim(); 160 | statType = statType switch 161 | { 162 | "magickpoints" => "MagickPoints", 163 | "techpoints" => "TechPoints", 164 | _ => statType 165 | }; 166 | 167 | if (!((IList)Enum.GetNames(typeof(BasicStatType))).Contains(statType)) 168 | ConsoleExtensions.Log($"unrecognized Entity.BasicStatType param:|{statType}|", "warn"); 169 | 170 | string statValue = statAndValue[1].Trim(); 171 | //ConsoleExtensions.Log($"statType:|{statType}| value:|{statValue}|", "warn"); 172 | 173 | foreach (BasicStatType basicStatType in (BasicStatType[])Enum.GetValues(typeof(BasicStatType))) 174 | { 175 | string enumValueName = Enum.GetName(typeof(BasicStatType), basicStatType); 176 | if (!enumValueName.Equals(statType)) 177 | //ConsoleExtensions.Log($"Failed to match enumValueName:|{enumValueName}| vs statType:|{statType}|", "warn"); 178 | continue; 179 | 180 | basicStatTuple = new Tuple(basicStatType, int.Parse(statValue)); 181 | } 182 | 183 | return basicStatTuple; 184 | } 185 | 186 | private static Tuple GetResistTuple(string paramName, string paramValue) 187 | { 188 | if (paramValue.Equals("1 0")) paramValue = " 10"; 189 | //ConsoleExtensions.Log($"paramName:|{paramName}| paramValue:|{paramValue}|", "warn"); 190 | 191 | 192 | string trimmedResist = paramName.TrimStart(); 193 | string[] resist = trimmedResist.Split(" ", 2); 194 | string resistTypeStr = resist[0]; 195 | var resistanceTypes = (ResistanceType[])Enum.GetValues(typeof(ResistanceType)); 196 | 197 | return (from resistType in resistanceTypes 198 | let resistTypeName = Enum.GetName(typeof(ResistanceType), resistType) 199 | where resistTypeName.Equals(resistTypeStr) 200 | select new Tuple(resistType, int.Parse(paramValue))) 201 | .FirstOrDefault(); 202 | } 203 | 204 | private static Tuple GetDmgTuple(string paramName, string paramValue) 205 | { 206 | string trimmedDmg = paramValue.TrimStart(); 207 | string[] dmgRange = trimmedDmg.Split(" ", 2); 208 | var min = int.Parse(dmgRange[0]); 209 | var max = int.Parse(dmgRange[1]); 210 | 211 | return paramName switch 212 | { 213 | "Normal Damage" => new Tuple(DamageType.Normal, min, max), 214 | "Fatigue Damage" => new Tuple(DamageType.Fatigue, min, max), 215 | "Poison Damage" => new Tuple(DamageType.Poison, min, max), 216 | "Electrical Damage" => new Tuple(DamageType.Electrical, min, max), 217 | "Fire Damage" => new Tuple(DamageType.Fire, min, max), 218 | _ => null 219 | }; 220 | } 221 | 222 | public static Unique GetFromText(IEnumerable uniqueText) 223 | { 224 | var unique = new Unique 225 | { 226 | ObjectFlags = new List(), 227 | CritterFlags = new List(), 228 | CritterFlags2 = new List(), 229 | NpcFlags = new List(), 230 | BlitFlags = new List(), 231 | SpellFlags = new List(), 232 | BasicStats = new List>(), 233 | Spells = new List(), 234 | Scripts = new List>(), 235 | Resistances = new List>(), 236 | Damages = new List>() 237 | }; 238 | 239 | foreach (string curLine in uniqueText) 240 | { 241 | if (string.IsNullOrWhiteSpace(curLine)) continue; 242 | 243 | string[] lines = curLine.Split(":", 2); 244 | string paramName = lines[0]; 245 | string paramValue = lines[1]; 246 | 247 | try 248 | { 249 | switch (paramName) 250 | { 251 | case "Description" when paramValue.Contains("//"): 252 | { 253 | string[] idAndName = paramValue.Split("//", 2); 254 | string monsterId = idAndName[0]; 255 | string monsterName = idAndName[1]; 256 | 257 | unique.Description = new Tuple(int.Parse(monsterId), monsterName); 258 | break; 259 | } 260 | case "Description": 261 | break; 262 | case "Internal Name" or "internal name": 263 | unique.InternalName = int.Parse(paramValue); 264 | break; 265 | case "Level": 266 | unique.Level = int.Parse(paramValue); 267 | break; 268 | case "Art Number and Palette": 269 | string trimmed = paramValue.TrimStart(); 270 | string[] artNumberAndPalette = trimmed.Split(" ", 2); 271 | string artNumber = artNumberAndPalette[0]; 272 | string paletteNumber = artNumberAndPalette[1]; 273 | 274 | unique.ArtNumberAndPalette = new Tuple(int.Parse(artNumber), int.Parse(paletteNumber)); 275 | break; 276 | case "Scale": 277 | unique.Scale = int.Parse(paramValue); 278 | break; 279 | case "Alignment": 280 | unique.Alignment = int.Parse(paramValue); 281 | break; 282 | case "Object Flag": 283 | unique.ObjectFlags.Add(ParseObjFFlags(paramValue)); 284 | break; 285 | case "Critter Flag": 286 | unique.CritterFlags.Add(ParseObjFCritterFlags(paramValue)); 287 | break; 288 | case "Critter2 Flag": 289 | unique.CritterFlags2.Add(ParseObjFCritterFlags2(paramValue)); 290 | break; 291 | case "NPC Flag": 292 | unique.NpcFlags.Add(ParseObjFNpcFlags(paramValue)); 293 | break; 294 | case "Blit Flag": 295 | unique.BlitFlags.Add(ParseObjFBlitFlag(paramValue)); 296 | break; 297 | case "Spell Flag": 298 | unique.SpellFlags.Add(ParseObjFSpellFlags(paramValue)); 299 | break; 300 | case "Hit Chart": 301 | unique.HitChart = int.Parse(paramValue); 302 | break; 303 | case "Basic Stat" or "basic stat": 304 | unique.BasicStats.Add(GetBasicStat(paramValue)); 305 | break; 306 | case "Spell" or "spell": 307 | unique.Spells.Add(paramValue); 308 | break; 309 | case "Script": 310 | string trimmedScript = paramValue.TrimStart(); 311 | string[] scriptParams = trimmedScript.Split(" ", 6); 312 | var paramValues = scriptParams.Select(int.Parse).ToList(); 313 | 314 | unique.Scripts.Add(new Tuple(paramValues[0], paramValues[1], paramValues[2], paramValues[3], paramValues[4], paramValues[5])); 315 | break; 316 | case "Faction": 317 | unique.Faction = int.Parse(paramValue); 318 | break; 319 | case "AI Packet": 320 | if (paramValue.Contains("//")) 321 | paramValue = paramValue.Split("//")[0].Trim(); 322 | 323 | unique.AIPacket = int.Parse(paramValue); 324 | break; 325 | case "Material": 326 | if (paramValue.Contains("//")) 327 | paramValue = paramValue.Split("//")[0].Trim(); 328 | 329 | unique.Material = int.Parse(paramValue); 330 | break; 331 | case "Hit Points": 332 | unique.HitPoints = int.Parse(paramValue); 333 | break; 334 | case "Fatigue": 335 | unique.Fatigue = int.Parse(paramValue); 336 | break; 337 | case "Damage Resistance" or "damage resistance": 338 | unique.Resistances.Add(GetResistTuple(paramName, paramValue)); 339 | break; 340 | case "Fire Resistance": 341 | unique.Resistances.Add(GetResistTuple(paramName, paramValue)); 342 | break; 343 | case "Electrical Resistance": 344 | unique.Resistances.Add(GetResistTuple(paramName, paramValue)); 345 | break; 346 | case "Poison Resistance": 347 | unique.Resistances.Add(GetResistTuple(paramName, paramValue)); 348 | break; 349 | case "Magic Resistance": 350 | unique.Resistances.Add(GetResistTuple(paramName, paramValue)); 351 | break; 352 | case "Normal Damage": 353 | unique.Damages.Add(GetDmgTuple(paramName, paramValue)); 354 | break; 355 | case "Fatigue Damage": 356 | unique.Damages.Add(GetDmgTuple(paramName, paramValue)); 357 | break; 358 | case "Poison Damage": 359 | unique.Damages.Add(GetDmgTuple(paramName, paramValue)); 360 | break; 361 | case "Electrical Damage": 362 | unique.Damages.Add(GetDmgTuple(paramName, paramValue)); 363 | break; 364 | case "Fire Damage": 365 | unique.Damages.Add(GetDmgTuple(paramName, paramValue)); 366 | break; 367 | case "Sound Bank" or "sound bank": 368 | unique.SoundBank = int.Parse(paramValue); 369 | break; 370 | case "Category": 371 | unique.Category = int.Parse(paramValue); 372 | break; 373 | case "Auto Level Scheme": 374 | unique.AutoLevelScheme = int.Parse(paramValue); 375 | break; 376 | case "Inventory Source": 377 | unique.InventorySource = int.Parse(paramValue); 378 | break; 379 | 380 | default: 381 | ConsoleExtensions.Log($"unrecognized entity param:|{paramName}|", "error"); 382 | break; 383 | } 384 | } 385 | catch (Exception ex) 386 | { 387 | AnsiConsole.WriteException(ex); 388 | throw; 389 | } 390 | } 391 | 392 | return unique; 393 | } 394 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/WorldMapEncounterInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ArcNET.DataTypes.GameObjects.Classes; 4 | 5 | public class WorldMapEncounterInfo 6 | { 7 | public List LoadedWorldMapEncounters; 8 | 9 | public class WorldMapEncounterInfoEntry 10 | { 11 | public int WorldWidth; 12 | public int WorldHeight; 13 | public int TriggerRadius; 14 | public int OccurrenceChance; 15 | } 16 | 17 | public int Id; 18 | public List Entries; 19 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Classes/XpLevels.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ArcNET.DataTypes.GameObjects.Classes; 6 | 7 | public class XpLevels 8 | { 9 | public static XpLevels LoadedXpLevels = new(); 10 | 11 | public class LevelXpEntry 12 | { 13 | public int Level; 14 | public int Experience; 15 | 16 | public LevelXpEntry(int level, int experience) 17 | { 18 | Level = level; 19 | Experience = experience; 20 | } 21 | } 22 | 23 | public List Entries = new(); 24 | 25 | public static void InitFromText(IEnumerable textData) 26 | { 27 | try 28 | { 29 | foreach (string line in textData) 30 | { 31 | string[] levelAndXp = line.Split("}", 2); 32 | levelAndXp[0] = levelAndXp[0].Replace("{", ""); 33 | var level = int.Parse(levelAndXp[0]); 34 | levelAndXp[1] = levelAndXp[1].Replace("{", ""); 35 | levelAndXp[1] = levelAndXp[1].Replace("}", ""); 36 | levelAndXp[1] = levelAndXp[1].TrimEnd(); 37 | var xp = int.Parse(levelAndXp[1]); 38 | 39 | LoadedXpLevels.Entries.Add(new LevelXpEntry(level, xp)); 40 | } 41 | } 42 | catch (Exception ex) 43 | { 44 | AnsiConsole.WriteException(ex); 45 | throw; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Enums.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects; 2 | 3 | public static class Enums 4 | { 5 | public enum ObjectFieldBitmap 6 | { 7 | Wall = 12, 8 | Portal = 12, 9 | Container = 12, 10 | Scenery = 12, 11 | Projectile = 12, 12 | Trap = 12, 13 | Weapon = 16, 14 | Ammo = 16, 15 | Armor = 16, 16 | Gold = 16, 17 | Food = 16, 18 | Scroll = 16, 19 | Key = 16, 20 | KeyRing = 16, 21 | Written = 16, 22 | Generic = 16, 23 | Pc = 20, 24 | Npc = 20, 25 | } 26 | 27 | public enum ObjectType 28 | { 29 | Wall = 0, 30 | Portal, 31 | Container, 32 | Scenery, 33 | Projectile, 34 | Weapon, 35 | Ammo, 36 | Armor, 37 | Gold, 38 | Food, 39 | Scroll, 40 | Key, 41 | KeyRing, 42 | Written, 43 | Generic, 44 | Pc, 45 | Npc, 46 | Trap 47 | } 48 | 49 | public enum ObjectField 50 | { 51 | ObjFCurrentAid = 0, 52 | ObjFLocation = 1, 53 | ObjFOffsetX = 2, 54 | ObjFOffsetY = 3, 55 | ObjFShadow = 4, 56 | ObjFOverlayFore = 5, 57 | ObjFOverlayBack = 6, 58 | ObjFUnderlay = 7, 59 | ObjFBlitFlags = 8, 60 | ObjFBlitColor = 9, 61 | ObjFBlitAlpha = 10, 62 | ObjFBlitScale = 11, 63 | ObjFLightFlags = 12, 64 | ObjFLightAid = 13, 65 | ObjFLightColor = 14, 66 | ObjFOverlayLightFlags = 15, 67 | ObjFOverlayLightAid = 16, 68 | ObjFOverlayLightColor = 17, 69 | ObjFFlags = 18, 70 | ObjFSpellFlags = 19, 71 | ObjFBlockingMask = 20, 72 | ObjFName = 21, 73 | ObjFDescription = 22, 74 | ObjFAid = 23, 75 | ObjFDestroyedAid = 24, 76 | ObjFAc = 25, 77 | ObjFHpPts = 26, 78 | ObjFHpAdj = 27, 79 | ObjFHpDamage = 28, 80 | ObjFMaterial = 29, 81 | ObjFResistanceIdx = 30, 82 | ObjFScriptsIdx = 31, 83 | ObjFSoundEffect = 32, 84 | ObjFCategory = 33, 85 | ObjFPadIas1 = 34, 86 | ObjFPadI64As1 = 35, 87 | ObjFWallFlags = 64, 88 | ObjFWallPadI1 = 65, 89 | ObjFWallPadI2 = 66, 90 | ObjFWallPadIas1 = 67, 91 | ObjFWallPadI64As1 = 68, 92 | 93 | ObjFPortalFlags = 64, 94 | ObjFPortalLockDifficulty = 65, 95 | ObjFPortalKeyId = 66, 96 | ObjFPortalNotifyNpc = 67, 97 | ObjFPortalPadI1 = 68, 98 | ObjFPortalPadI2 = 69, 99 | ObjFPortalPadIas1 = 70, 100 | ObjFPortalPadI64As1 = 71, 101 | 102 | ObjFContainerFlags = 64, 103 | ObjFContainerLockDifficulty = 65, 104 | ObjFContainerKeyId = 66, 105 | ObjFContainerInventoryNum = 67, 106 | ObjFContainerInventoryListIdx = 68, 107 | ObjFContainerInventorySource = 69, 108 | ObjFContainerNotifyNpc = 70, 109 | ObjFContainerPadI1 = 71, 110 | ObjFContainerPadI2 = 72, 111 | ObjFContainerPadIas1 = 73, 112 | ObjFContainerPadI64As1 = 74, 113 | 114 | ObjFSceneryFlags = 64, 115 | ObjFSceneryWhosInMe = 65, 116 | ObjFSceneryRespawnDelay = 66, 117 | ObjFSceneryPadI2 = 67, 118 | ObjFSceneryPadIas1 = 68, 119 | ObjFSceneryPadI64As1 = 69, 120 | 121 | ObjFProjectileFlagsCombat = 0, 122 | ObjFProjectileFlagsCombatDamage = 1, 123 | ObjFProjectileHitLoc = 2, 124 | ObjFProjectileParentWeapon = 3, 125 | ObjFProjectilePadI1 = 4, 126 | ObjFProjectilePadI2 = 5, 127 | ObjFProjectilePadIas1 = 6, 128 | ObjFProjectilePadI64As1 = 7, 129 | 130 | ObjFItemFlags = 64, 131 | ObjFItemParent = 65, 132 | ObjFItemWeight = 66, 133 | ObjFItemMagicWeightAdj = 67, 134 | ObjFItemWorth = 68, 135 | ObjFItemManaStore = 69, 136 | ObjFItemInvAid = 70, 137 | ObjFItemInvLocation = 71, 138 | ObjFItemUseAidFragment = 72, 139 | ObjFItemMagicTechComplexity = 73, 140 | ObjFItemDiscipline = 74, 141 | ObjFItemDescriptionUnknown = 75, 142 | ObjFItemDescriptionEffects = 76, 143 | ObjFItemSpell1 = 77, 144 | ObjFItemSpell2 = 78, 145 | ObjFItemSpell3 = 79, 146 | ObjFItemSpell4 = 80, 147 | ObjFItemSpell5 = 81, 148 | ObjFItemSpellManaStore = 82, 149 | ObjFItemAiAction = 83, 150 | ObjFItemPadI1 = 84, 151 | ObjFItemPadIas1 = 85, 152 | ObjFItemPadI64As1 = 86, 153 | 154 | ObjFWeaponFlags = 96, 155 | ObjFWeaponPaperDollAid = 97, 156 | ObjFWeaponBonusToHit = 98, 157 | ObjFWeaponMagicHitAdj = 99, 158 | ObjFWeaponDamageLowerIdx = 100, 159 | ObjFWeaponDamageUpperIdx = 101, 160 | ObjFWeaponMagicDamageAdjIdx = 102, 161 | ObjFWeaponSpeedFactor = 103, 162 | ObjFWeaponMagicSpeedAdj = 104, 163 | ObjFWeaponRange = 105, 164 | ObjFWeaponMagicRangeAdj = 106, 165 | ObjFWeaponMinStrength = 107, 166 | ObjFWeaponMagicMinStrengthAdj = 108, 167 | ObjFWeaponAmmoType = 109, 168 | ObjFWeaponAmmoConsumption = 110, 169 | ObjFWeaponMissileAid = 111, 170 | ObjFWeaponVisualEffectAid = 112, 171 | ObjFWeaponCritHitChart = 113, 172 | ObjFWeaponMagicCritHitChance = 114, 173 | ObjFWeaponMagicCritHitEffect = 115, 174 | ObjFWeaponCritMissChart = 116, 175 | ObjFWeaponMagicCritMissChance = 117, 176 | ObjFWeaponMagicCritMissEffect = 118, 177 | 178 | ObjFWeaponPadI1 = 119, 179 | ObjFWeaponPadI2 = 120, 180 | ObjFWeaponPadIas1 = 121, 181 | ObjFWeaponPadI64As1 = 122, 182 | 183 | ObjFAmmoFlags = 96, 184 | ObjFAmmoQuantity = 97, 185 | ObjFAmmoType = 98, 186 | ObjFAmmoPadI1 = 99, 187 | ObjFAmmoPadI2 = 100, 188 | ObjFAmmoPadIas1 = 101, 189 | ObjFAmmoPadI64As1 = 102, 190 | 191 | ObjFArmorFlags = 96, 192 | ObjFArmorPaperDollAid = 97, 193 | ObjFArmorAcAdj = 98, 194 | ObjFArmorMagicAcAdj = 99, 195 | ObjFArmorResistanceAdjIdx = 100, 196 | ObjFArmorMagicResistanceAdjIdx = 101, 197 | ObjFArmorSilentMoveAdj = 102, 198 | ObjFArmorMagicSilentMoveAdj = 103, 199 | ObjFArmorUnarmedBonusDamage = 104, 200 | ObjFArmorPadI2 = 105, 201 | ObjFArmorPadIas1 = 106, 202 | ObjFArmorPadI64As1 = 107, 203 | 204 | ObjFGoldFlags = 96, 205 | ObjFGoldQuantity = 97, 206 | ObjFGoldPadI1 = 98, 207 | ObjFGoldPadI2 = 99, 208 | ObjFGoldPadIas1 = 100, 209 | ObjFGoldPadI64As1 = 101, 210 | 211 | ObjFFoodFlags = 96, 212 | ObjFFoodPadI1 = 97, 213 | ObjFFoodPadI2 = 98, 214 | ObjFFoodPadIas1 = 99, 215 | ObjFFoodPadI64As1 = 100, 216 | 217 | ObjFScrollFlags = 96, 218 | ObjFScrollPadI1 = 97, 219 | ObjFScrollPadI2 = 98, 220 | ObjFScrollPadIas1 = 99, 221 | ObjFScrollPadI64As1 = 100, 222 | 223 | ObjFKeyKeyId = 96, 224 | ObjFKeyPadI1 = 97, 225 | ObjFKeyPadI2 = 98, 226 | ObjFKeyPadIas1 = 99, 227 | ObjFKeyPadI64As1 = 100, 228 | ObjFKeyRingFlags = 96, 229 | ObjFKeyRingListIdx = 97, 230 | ObjFKeyRingPadI1 = 98, 231 | ObjFKeyRingPadI2 = 99, 232 | ObjFKeyRingPadIas1 = 100, 233 | ObjFKeyRingPadI64As1 = 101, 234 | 235 | ObjFWrittenFlags = 96, 236 | ObjFWrittenSubtype = 97, 237 | ObjFWrittenTextStartLine = 98, 238 | ObjFWrittenTextEndLine = 99, 239 | ObjFWrittenPadI1 = 100, 240 | ObjFWrittenPadI2 = 101, 241 | ObjFWrittenPadIas1 = 102, 242 | ObjFWrittenPadI64As1 = 103, 243 | 244 | ObjFGenericFlags = 96, 245 | ObjFGenericUsageBonus = 97, 246 | ObjFGenericUsageCountRemaining = 98, 247 | ObjFGenericPadIas1 = 99, 248 | ObjFGenericPadI64As1 = 100, 249 | 250 | ObjFCritterFlags = 64, 251 | ObjFCritterFlags2 = 65, 252 | ObjFCritterStatBaseIdx = 66, 253 | ObjFCritterBasicSkillIdx = 67, 254 | ObjFCritterTechSkillIdx = 68, 255 | ObjFCritterSpellTechIdx = 69, 256 | ObjFCritterFatiguePts = 70, 257 | ObjFCritterFatigueAdj = 71, 258 | ObjFCritterFatigueDamage = 72, 259 | ObjFCritterCritHitChart = 73, 260 | ObjFCritterEffectsIdx = 74, 261 | ObjFCritterEffectCauseIdx = 75, 262 | ObjFCritterFleeingFrom = 76, 263 | ObjFCritterPortrait = 77, 264 | ObjFCritterGold = 78, 265 | ObjFCritterArrows = 79, 266 | ObjFCritterBullets = 80, 267 | ObjFCritterPowerCells = 81, 268 | ObjFCritterFuel = 82, 269 | ObjFCritterInventoryNum = 83, 270 | ObjFCritterInventoryListIdx = 84, 271 | ObjFCritterInventorySource = 85, 272 | ObjFCritterDescriptionUnknown = 86, 273 | ObjFCritterFollowerIdx = 87, 274 | ObjFCritterTeleportDest = 88, 275 | ObjFCritterTeleportMap = 89, 276 | ObjFCritterDeathTime = 90, 277 | ObjFCritterAutoLevelScheme = 91, 278 | ObjFCritterPadI1 = 92, 279 | ObjFCritterPadI2 = 93, 280 | ObjFCritterPadI3 = 94, 281 | ObjFCritterPadIas1 = 95, 282 | ObjFCritterPadI64As1 = 96, 283 | 284 | ObjFPcFlags = 128, 285 | ObjFPcFlagsFate = 129, 286 | ObjFPcReputationIdx = 130, 287 | ObjFPcReputationTsIdx = 131, 288 | ObjFPcBackground = 132, 289 | ObjFPcBackgroundText = 133, 290 | ObjFPcQuestIdx = 134, 291 | ObjFPcBlessingIdx = 134, 292 | ObjFPcBlessingTsIdx = 135, 293 | ObjFPcCurseIdx = 136, 294 | ObjFPcCurseTsIdx = 137, 295 | ObjFPcPartyId = 138, 296 | ObjFPcRumorIdx = 139, 297 | ObjFPcPadIas2 = 140, 298 | ObjFPcSchematicsFoundIdx = 141, 299 | ObjFPcLogbookEgoIdx = 142, 300 | ObjFPcFogMask = 143, 301 | ObjFPcPlayerName = 144, 302 | ObjFPcBankMoney = 145, 303 | ObjFPcGlobalFlags = 146, 304 | ObjFPcGlobalVariables = 147, 305 | ObjFPcPadI1 = 148, 306 | ObjFPcPadI2 = 149, 307 | ObjFPcPadIas1 = 150, 308 | ObjFPcPadI64As1 = 151, 309 | ObjFPcPadI64As2 = 152, 310 | 311 | ObjFNpcFlags = 128, 312 | ObjFNpcLeader = 129, 313 | ObjFNpcAiData = 130, 314 | ObjFNpcCombatFocus = 131, 315 | ObjFNpcWhoHitMeLast = 132, 316 | ObjFNpcExperienceWorth = 133, 317 | ObjFNpcExperiencePool = 134, 318 | ObjFNpcWaypointsIdx = 135, 319 | ObjFNpcWaypointCurrent = 136, 320 | ObjFNpcStandpointDay = 137, 321 | ObjFNpcStandpointNight = 138, 322 | ObjFNpcOrigin = 139, 323 | ObjFNpcFaction = 140, 324 | ObjFNpcRetailPriceMultiplier = 141, 325 | ObjFNpcSubstituteInventory = 142, 326 | ObjFNpcReactionBase = 143, 327 | ObjFNpcSocialClass = 144, 328 | ObjFNpcReactionPcIdx = 145, 329 | ObjFNpcReactionLevelIdx = 146, 330 | ObjFNpcReactionTimeIdx = 147, 331 | ObjFNpcWait = 148, 332 | ObjFNpcGeneratorData = 149, 333 | ObjFNpcPadI1 = 150, 334 | ObjFNpcDamageIdx = 151, 335 | ObjFNpcShitListIdx = 152, 336 | 337 | ObjFTrapFlags = 64, 338 | ObjFTrapDifficulty = 65, 339 | ObjFTrapPadI2 = 66, 340 | ObjFTrapPadIas1 = 67, 341 | ObjFTrapPadI64As1 = 68 342 | } 343 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Flags/ObjFBlitFlag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ArcNET.DataTypes.GameObjects.Flags; 4 | 5 | [Flags] 6 | public enum ObjFBlitFlag 7 | { 8 | TAB_BLEND_ADD, 9 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Flags/ObjFCritterFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace ArcNET.DataTypes.GameObjects.Flags; 5 | 6 | [Flags] 7 | [SuppressMessage("ReSharper", "InconsistentNaming")] 8 | public enum ObjFCritterFlags 9 | { 10 | OCF_IS_CONCEALED = 1, 11 | OCF_MOVING_SILENTLY, 12 | OCF_UNDEAD, 13 | OCF_ANIMAL, 14 | OCF_FLEEING, 15 | OCF_STUNNED, 16 | OCF_PARALYZED, 17 | OCF_BLINDED, 18 | OCF_CRIPPLED_ARMS_ONE, 19 | OCF_CRIPPLED_ARMS_BOTH, 20 | OCF_CRIPPLED_LEGS_BOTH, 21 | OCF_UNUSED, 22 | OCF_SLEEPING, 23 | OCF_MUTE, 24 | OCF_SURRENDERED, 25 | OCF_MONSTER, 26 | OCF_SPELL_FLEE, 27 | OCF_ENCOUNTER, 28 | OCF_COMBAT_MODE_ACTIVE, 29 | OCF_LIGHT_SMALL, 30 | OCF_LIGHT_MEDIUM, 31 | OCF_LIGHT_LARGE, 32 | OCF_LIGHT_XLARGE, 33 | OCF_UNREVIVIFIABLE, 34 | OCF_UNRESSURECTABLE, 35 | OCF_DEMON, 36 | OCF_FATIGUE_IMMUNE, 37 | OCF_NO_FLEE, 38 | OCF_NON_LETHAL_COMBAT, 39 | OCF_MECHANICAL, 40 | OCF_ANIMAL_ENSHROUD, 41 | OCF_FATIGUE_LIMITING 42 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Flags/ObjFCritterFlags2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace ArcNET.DataTypes.GameObjects.Flags; 5 | 6 | [Flags] 7 | [SuppressMessage("ReSharper", "InconsistentNaming")] 8 | public enum ObjFCritterFlags2 9 | { 10 | OCF2_ITEM_STOLEN = 1, 11 | OCF2_AUTO_ANIMATES, 12 | OCF2_USING_BOOMERANG, 13 | OCF2_FATIGUE_DRAINING, 14 | OCF2_SLOW_PARTY, 15 | OCF2_COMBAT_TOGGLE_FX, 16 | OCF2_NO_DECAY, 17 | OCF2_NO_PICKPOCKET, 18 | OCF2_NO_BLOOD_SPLOTCHES, 19 | OCF2_NIGH_INVULNERABLE, 20 | OCF2_ELEMENTAL, 21 | OCF2_DARK_SIGHT, 22 | OCF2_NO_SLIP, 23 | OCF2_NO_DISINTEGRATE, 24 | OCF2_REACTION_0, 25 | OCF2_REACTION_1, 26 | OCF2_REACTION_2, 27 | OCF2_REACTION_3, 28 | OCF2_REACTION_4, 29 | OCF2_REACTION_5, 30 | OCF2_REACTION_6, 31 | OCF2_TARGET_LOCK, 32 | OCF2_PERMA_POLYMORPH, 33 | OCF2_SAFE_OFF, 34 | OCF2_CHECK_REACTION_BAD, 35 | OCF2_CHECK_ALIGN_GOOD, 36 | OCF2_CHECK_ALIGN_BAD 37 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Flags/ObjFFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ArcNET.DataTypes.GameObjects.Flags; 4 | 5 | [Flags] 6 | public enum ObjFFlags 7 | { 8 | OF_DESTROYED = 1, 9 | OF_OFF, 10 | OF_FLAT, 11 | OF_TEXT, 12 | OF_SEE_THROUGH, 13 | OF_SHOOT_THROUGH, 14 | OF_TRANSLUCENT, 15 | OF_SHRUNK, 16 | OF_DONTDRAW, 17 | OF_INVISIBLE, 18 | OF_NO_BLOCK, 19 | OF_CLICK_THROUGH, 20 | OF_INVENTORY, 21 | OF_DYNAMIC, 22 | OF_PROVIDES_COVER, 23 | OF_HAS_OVERLAYS, 24 | OF_HAS_UNDERLAYS, 25 | OF_WADING, 26 | OF_WATER_WALKING, 27 | OF_STONED, 28 | OF_DONTLIGHT, 29 | OF_TEXT_FLOATER, 30 | OF_INVULNERABLE, 31 | OF_EXTINCT, 32 | OF_TRAP_PC, 33 | OF_TRAP_SPOTTED, 34 | OF_DISALLOW_WADING, 35 | OF_MULTIPLAYER_LOCK, 36 | OF_FROZEN, 37 | OF_ANIMATED_DEAD, 38 | OF_TELEPORTED 39 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Flags/ObjFNpcFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace ArcNET.DataTypes.GameObjects.Flags; 5 | 6 | [Flags] 7 | [SuppressMessage("ReSharper", "InconsistentNaming")] 8 | public enum ObjFNpcFlags 9 | { 10 | ONF_FIGHTING = 1, 11 | ONF_WAYPOINTS_DAY, 12 | ONF_WAYPOINTS_NIGHT, 13 | ONF_AI_WAIT_HERE, 14 | ONF_AI_SPREAD_OUT, 15 | ONF_JILTED, 16 | ONF_CHECK_WIELD, 17 | ONF_CHECK_WEAPON, 18 | ONF_KOS, 19 | ONF_WAYPOINTS_BED, 20 | ONF_FORCED_FOLLOWER, 21 | ONF_KOS_OVERRIDE, 22 | ONF_WANDERS, 23 | ONF_WANDERS_IN_DARK, 24 | ONF_FENCE, 25 | ONF_FAMILIAR, 26 | ONF_CHECK_LEADER, 27 | ONF_ALOOF, 28 | ONF_CAST_HIGHEST, 29 | ONF_GENERATOR, 30 | ONF_GENERATED, 31 | ONF_GENERATOR_RATE1, 32 | ONF_GENERATOR_RATE2, 33 | ONF_GENERATOR_RATE3, 34 | ONF_DEMAINTAIN_SPELLS, 35 | ONF_LOOK_FOR_WEAPON, 36 | ONF_LOOK_FOR_ARMOR, 37 | ONF_LOOK_FOR_AMMO, 38 | ONF_BACKING_OFF, 39 | ONF_NO_ATTACK 40 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Flags/ObjFSpellFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace ArcNET.DataTypes.GameObjects.Flags; 5 | 6 | [Flags] 7 | [SuppressMessage("ReSharper", "InconsistentNaming")] 8 | public enum ObjFSpellFlags 9 | { 10 | OSF_INVISIBLE = 1, 11 | OSF_FLOATING, 12 | OSF_BODY_OF_AIR, 13 | OSF_BODY_OF_EARTH, 14 | OSF_BODY_OF_FIRE, 15 | OSF_BODY_OF_WATER, 16 | OSF_DETECTING_MAGIC, 17 | OSF_DETECTING_ALIGNMENT, 18 | OSF_DETECTING_TRAPS, 19 | OSF_DETECTING_INVISIBLE, 20 | OSF_SHIELDED, 21 | OSF_ANTI_MAGIC_SHELL, 22 | OSF_BONDS_OF_MAGIC, 23 | OSF_FULL_REFLECTION, 24 | OSF_SUMMONED, 25 | OSF_ILLUSION, 26 | OSF_STONED, 27 | OSF_POLYMORPHED, 28 | OSF_MIRRORED, 29 | OSF_SHRUNK, 30 | OSF_PASSWALLED, 31 | OSF_WATER_WALKING, 32 | OSF_MAGNETIC_INVERSION, 33 | OSF_CHARMED, 34 | OSF_ENTANGLED, 35 | OSF_SPOKEN_WITH_DEAD, 36 | OSF_TEMPUS_FUGIT, 37 | OSF_MIND_CONTROLLED, 38 | OSF_DRUNK, 39 | OSF_ENSHROUDED, 40 | OSF_FAMILIAR, 41 | OSF_HARDENED_HANDS 42 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/GameObject.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects; 2 | 3 | public class GameObject 4 | { 5 | public GameObjectHeader Header; 6 | public object Obj; 7 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/GameObjectGuid.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ArcNET.DataTypes.GameObjects; 4 | 5 | public class GameObjectGuid 6 | { 7 | public short Type; 8 | public short Foo0; 9 | public int Foo2; 10 | public Guid Guid; 11 | 12 | public GameObjectGuid() 13 | { 14 | } 15 | 16 | public GameObjectGuid(short type, short foo0, int foo2) 17 | { 18 | Type = type; 19 | Foo0 = foo0; 20 | Foo2 = foo2; 21 | } 22 | 23 | public bool IsProto() 24 | => Type == -1; 25 | 26 | public int GetId() 27 | => BitConverter.ToInt32(Guid.ToByteArray(), 0); 28 | 29 | public override string ToString() 30 | => $"{Type} {Foo0:x8} {Foo2:x8} {Guid}"; 31 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/GameObjectHeader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace ArcNET.DataTypes.GameObjects; 4 | 5 | public class GameObjectHeader 6 | { 7 | public string Filename; 8 | public int Version; 9 | public GameObjectGuid ProtoId { get; set; } 10 | public GameObjectGuid ObjectId { get; set; } 11 | public Enums.ObjectType GameObjectType { get; set; } 12 | public short PropCollectionItems; 13 | public BitArray Bitmap; 14 | 15 | public bool IsPrototype() 16 | => ProtoId.IsProto(); 17 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/GameObjectHeaderReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.IO; 4 | using Utils.Console; 5 | 6 | namespace ArcNET.DataTypes.GameObjects; 7 | 8 | public static class GameObjectHeaderReader 9 | { 10 | public static GameObjectHeader Read(this BinaryReader reader) 11 | { 12 | var header = new GameObjectHeader 13 | { 14 | Version = reader.ReadInt32() 15 | }; 16 | 17 | if (header.Version != 0x77) 18 | throw new InvalidDataException("Unknown object file version: " + header.Version); 19 | 20 | header.ProtoId = reader.ReadGameObjectGuid(true); 21 | header.ObjectId = reader.ReadGameObjectGuid(true); 22 | header.GameObjectType = (Enums.ObjectType)reader.ReadUInt32(); 23 | 24 | if (!header.ProtoId.IsProto()) 25 | header.PropCollectionItems = reader.ReadInt16(); 26 | 27 | var bitmapLength = (int)Enum.Parse(typeof(Enums.ObjectFieldBitmap), header.GameObjectType.ToString()); 28 | header.Bitmap = new BitArray(reader.ReadBytes(bitmapLength)); 29 | 30 | ConsoleExtensions.Log($"Parsed GameOjb headerVersion: {header.Version} " 31 | + $"\n ProtoId: {header.ProtoId}" 32 | + $"\n ObjectId: {header.ObjectId}" 33 | + $"\n GameObjectType: {header.GameObjectType}" 34 | + $"\n bitmapLength: {bitmapLength}" 35 | + $"\n Bitmap: {header.Bitmap}", "warn"); 36 | return header; 37 | } 38 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/GameObjectManager.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.GameObjects.Classes; 2 | using System.Collections.Generic; 3 | 4 | namespace ArcNET.DataTypes.GameObjects; 5 | 6 | public class GameObjectManager 7 | { 8 | public static List ObjectList; 9 | public static List Monsters; 10 | public static List NPCs = new(); 11 | public static List Uniques = new(); 12 | 13 | public static void Init() 14 | { 15 | ObjectList = new List(); 16 | Monsters = new List(); 17 | NPCs = new List(); 18 | Uniques = new List(); 19 | } 20 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/GameObjectReader.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.Common; 2 | using Spectre.Console; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Reflection; 8 | using Utils.Console; 9 | 10 | namespace ArcNET.DataTypes.GameObjects; 11 | 12 | public static class GameObjectReader 13 | { 14 | public static GameObject GetGameObject(this BinaryReader reader) 15 | { 16 | var gameObject = new GameObject(); 17 | var prototypeGameObject = new GameObject(); 18 | 19 | gameObject.Header = GameObjectHeaderReader.Read(reader); 20 | 21 | if (!gameObject.Header.IsPrototype()) 22 | prototypeGameObject = GameObjectManager.ObjectList.Find(x => 23 | x.Header.ObjectId.GetId().CompareTo(gameObject.Header.ProtoId.GetId()) == 0); 24 | 25 | const string pathToTypes = "ArcNET.DataTypes.GameObjects.Types."; 26 | var gameObjectObjType = Type.GetType(pathToTypes + gameObject.Header.GameObjectType); 27 | const string pathToCustomReader = "ArcNET.DataTypes.BinaryReaderExtensions"; 28 | var binaryReader = Type.GetType(pathToCustomReader); 29 | gameObject.Obj = Activator.CreateInstance(gameObjectObjType ?? throw new InvalidOperationException()); 30 | 31 | IOrderedEnumerable props = from p in gameObjectObjType.GetProperties() 32 | where p.CanWrite 33 | orderby p.PropertyOrder() 34 | select p; 35 | 36 | PropertyInfo[] propertyArray = props.ToArray(); 37 | 38 | foreach (PropertyInfo propertyInfo in propertyArray) 39 | { 40 | if (binaryReader == null) 41 | throw new InvalidOperationException("binaryReader is null"); 42 | 43 | MethodInfo readMethod = binaryReader.GetMethod(propertyInfo.PropertyType.Name.Contains("Tuple") 44 | ? "ReadArray" 45 | : "Read" + propertyInfo.PropertyType.Name); 46 | 47 | if (readMethod is not null && readMethod.IsGenericMethod) 48 | if (propertyInfo.PropertyType.FullName != null) 49 | { 50 | string genericTypeName = propertyInfo.PropertyType.FullName 51 | .Replace("System.Tuple`2[[", "").Split(new[] { ',' })[0] 52 | .Replace("[]", ""); 53 | readMethod = readMethod.MakeGenericMethod(Type.GetType(genericTypeName) ?? throw new InvalidOperationException()); 54 | } 55 | 56 | var parameters = new List 57 | { 58 | reader, 59 | }; 60 | 61 | var bit = (int)Enum.Parse(typeof(Enums.ObjectField), propertyInfo.Name); 62 | 63 | foreach (object param in parameters) 64 | ConsoleExtensions.Log($"Parameters: {param} Bit: {bit}", "debug"); 65 | 66 | if (gameObject.Header.Bitmap.Get(bit, gameObject.Header.IsPrototype())) 67 | { 68 | if (readMethod is null) continue; 69 | 70 | try 71 | { 72 | propertyInfo.SetValue(gameObject.Obj, readMethod.Invoke(binaryReader, parameters.ToArray())); 73 | } 74 | catch (Exception e) 75 | { 76 | AnsiConsole.WriteException(e); 77 | throw; 78 | } 79 | } 80 | else 81 | { 82 | if (prototypeGameObject == null || prototypeGameObject.Header.GameObjectType.ToString() 83 | != gameObjectObjType.ToString()) continue; 84 | 85 | Type tempType = prototypeGameObject.Obj.GetType(); 86 | PropertyInfo tempProperty = tempType.GetProperty(propertyInfo.Name); 87 | object tempObj = tempProperty?.GetValue(prototypeGameObject.Obj); 88 | 89 | propertyInfo.SetValue(gameObject.Obj, tempObj); 90 | } 91 | } 92 | 93 | IEnumerable artIds = props 94 | .Where(item => 95 | item.PropertyType.ToString() == "ArcNET.DataTypes.Common.ArtId" && 96 | item.GetValue(gameObject.Obj) != null).Select(item => ((ArtId)item.GetValue(gameObject.Obj))?.Path); 97 | 98 | foreach (string artId in artIds) 99 | ArtId.ArtIds.Add(artId); 100 | 101 | return gameObject; 102 | } 103 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/GameObjectScript.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects; 2 | 3 | public class GameObjectScript 4 | { 5 | public byte[] Counters; 6 | public int Flags; 7 | public int ScriptId; 8 | 9 | public GameObjectScript() 10 | { 11 | Counters = new byte[] { 0x00, 0x00, 0x00, 0x00 }; 12 | Flags = 0x00; 13 | ScriptId = 0x00; 14 | } 15 | 16 | public GameObjectScript(byte c0, byte c1, byte c2, byte c3, int f0, int id) 17 | { 18 | Counters = new[] { c0, c1, c2, c3 }; 19 | Flags = f0; 20 | ScriptId = id; 21 | } 22 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Ammo.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Ammo : Item 4 | { 5 | [Order(60)] public int ObjFAmmoFlags { get; set; } 6 | [Order(61)] public int ObjFAmmoQuantity { get; set; } 7 | [Order(62)] public int ObjFAmmoType { get; set; } 8 | [Order(63)] public int ObjFAmmoPadI1 { get; set; } 9 | [Order(64)] public int ObjFAmmoPadI2 { get; set; } 10 | [Order(65)] public Unknown ObjFAmmoPadIas1 { get; set; } 11 | [Order(66)] public Unknown ObjFAmmoPadI64As1 { get; set; } 12 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Armor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ArcNET.DataTypes.GameObjects.Types; 4 | 5 | public class Armor : Item 6 | { 7 | [Order(60)] public int ObjFArmorFlags { get; set; } 8 | [Order(61)] public int ObjFArmorPaperDollAid { get; set; } 9 | [Order(62)] public int ObjFArmorAcAdj { get; set; } 10 | [Order(63)] public int ObjFArmorMagicAcAdj { get; set; } 11 | [Order(64)] public Tuple ObjFArmorResistanceAdjIdx { get; set; } 12 | [Order(65)] public Tuple ObjFArmorMagicResistanceAdjIdx { get; set; } 13 | [Order(66)] public int ObjFArmorSilentMoveAdj { get; set; } 14 | [Order(67)] public int ObjFArmorMagicSilentMoveAdj { get; set; } 15 | [Order(68)] public int ObjFArmorUnarmedBonusDamage { get; set; } 16 | [Order(69)] public int ObjFArmorPadI2 { get; set; } 17 | [Order(70)] public Unknown ObjFArmorPadIas1 { get; set; } 18 | [Order(71)] public Unknown ObjFArmorPadI64As1 { get; set; } 19 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Common.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ArcNET.DataTypes.Common; 3 | 4 | namespace ArcNET.DataTypes.GameObjects.Types; 5 | 6 | public class Common 7 | { 8 | [Order(01)] public ArtId ObjFCurrentAid { get; set; } 9 | [Order(02)] public Location ObjFLocation { get; set; } 10 | [Order(03)] public int ObjFOffsetX { get; set; } 11 | [Order(04)] public int ObjFOffsetY { get; set; } 12 | [Order(05)] public ArtId ObjFShadow { get; set; } //may be bool/int 13 | [Order(06)] public Tuple ObjFOverlayFore { get; set; } 14 | [Order(07)] public Tuple ObjFOverlayBack { get; set; } 15 | [Order(08)] public Tuple ObjFUnderlay { get; set; } 16 | [Order(09)] public int ObjFBlitFlags { get; set; } 17 | [Order(10)] public Color ObjFBlitColor { get; set; } 18 | [Order(11)] public int ObjFBlitAlpha { get; set; } 19 | [Order(12)] public int ObjFBlitScale { get; set; } 20 | [Order(13)] public int ObjFLightFlags { get; set; } 21 | [Order(14)] public ArtId ObjFLightAid { get; set; } 22 | [Order(15)] public Color ObjFLightColor { get; set; } 23 | [Order(16)] public Unknown ObjFOverlayLightFlags { get; set; } 24 | [Order(17)] public Tuple ObjFOverlayLightAid { get; set; } 25 | [Order(18)] public Unknown ObjFOverlayLightColor { get; set; } 26 | [Order(19)] public int ObjFFlags { get; set; } 27 | [Order(20)] public int ObjFSpellFlags { get; set; } 28 | [Order(21)] public int ObjFBlockingMask { get; set; } 29 | [Order(22)] public int ObjFName { get; set; } 30 | [Order(23)] public int ObjFDescription { get; set; } 31 | [Order(24)] public ArtId ObjFAid { get; set; } 32 | [Order(25)] public ArtId ObjFDestroyedAid { get; set; } 33 | [Order(26)] public int ObjFAc { get; set; } 34 | [Order(27)] public int ObjFHpPts { get; set; } 35 | [Order(28)] public int ObjFHpAdj { get; set; } 36 | [Order(29)] public int ObjFHpDamage { get; set; } 37 | [Order(30)] public int ObjFMaterial { get; set; } 38 | [Order(31)] public Tuple ObjFResistanceIdx { get; set; } 39 | [Order(32)] public Tuple ObjFScriptsIdx { get; set; } 40 | [Order(33)] public int ObjFSoundEffect { get; set; } 41 | [Order(34)] public int ObjFCategory { get; set; } 42 | [Order(35)] public Unknown ObjFPadIas1 { get; set; } 43 | [Order(36)] public Unknown ObjFPadI64As1 { get; set; } 44 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Container.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ArcNET.DataTypes.GameObjects.Types; 4 | 5 | public class Container : Common 6 | { 7 | [Order(37)] public int ObjFContainerFlags { get; set; } 8 | [Order(38)] public int ObjFContainerLockDifficulty { get; set; } 9 | [Order(39)] public int ObjFContainerKeyId { get; set; } 10 | [Order(40)] public int ObjFContainerInventoryNum { get; set; } 11 | [Order(41)] public Tuple ObjFContainerInventoryListIdx { get; set; } 12 | [Order(42)] public int ObjFContainerInventorySource { get; set; } 13 | [Order(43)] public int ObjFContainerNotifyNpc { get; set; } 14 | [Order(44)] public int ObjFContainerPadI1 { get; set; } 15 | [Order(45)] public int ObjFContainerPadI2 { get; set; } 16 | [Order(46)] public Unknown ObjFContainerPadIas1 { get; set; } 17 | [Order(47)] public Unknown ObjFContainerPadI64As1 { get; set; } 18 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Critter.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.Common; 2 | using System; 3 | 4 | namespace ArcNET.DataTypes.GameObjects.Types; 5 | 6 | public class Critter : Common 7 | { 8 | [Order(37)] public int ObjFCritterFlags { get; set; } 9 | [Order(38)] public int ObjFCritterFlags2 { get; set; } 10 | [Order(39)] public Tuple ObjFCritterStatBaseIdx { get; set; } 11 | [Order(40)] public Tuple ObjFCritterBasicSkillIdx { get; set; } 12 | [Order(41)] public Tuple ObjFCritterTechSkillIdx { get; set; } 13 | [Order(42)] public Tuple ObjFCritterSpellTechIdx { get; set; } 14 | [Order(43)] public int ObjFCritterFatiguePts { get; set; } 15 | [Order(44)] public int ObjFCritterFatigueAdj { get; set; } 16 | [Order(45)] public int ObjFCritterFatigueDamage { get; set; } 17 | [Order(46)] public int ObjFCritterCritHitChart { get; set; } 18 | [Order(47)] public Tuple ObjFCritterEffectsIdx { get; set; } 19 | [Order(48)] public Tuple ObjFCritterEffectCauseIdx { get; set; } 20 | [Order(49)] public GameObjectGuid ObjFCritterFleeingFrom { get; set; } 21 | [Order(50)] public int ObjFCritterPortrait { get; set; } //strinf aid 22 | [Order(51)] public GameObjectGuid ObjFCritterGold { get; set; } 23 | [Order(52)] public GameObjectGuid ObjFCritterArrows { get; set; } 24 | [Order(53)] public GameObjectGuid ObjFCritterBullets { get; set; } 25 | [Order(54)] public GameObjectGuid ObjFCritterPowerCells { get; set; } 26 | [Order(55)] public GameObjectGuid ObjFCritterFuel { get; set; } 27 | [Order(56)] public int ObjFCritterInventoryNum { get; set; } 28 | [Order(57)] public Tuple ObjFCritterInventoryListIdx { get; set; } 29 | [Order(58)] public int ObjFCritterInventorySource { get; set; } 30 | [Order(59)] public int ObjFCritterDescriptionUnknown { get; set; } 31 | [Order(60)] public Tuple ObjFCritterFollowerIdx { get; set; } 32 | [Order(61)] public Location ObjFCritterTeleportDest { get; set; } 33 | [Order(62)] public int ObjFCritterTeleportMap { get; set; } 34 | [Order(63)] public int ObjFCritterDeathTime { get; set; } 35 | [Order(64)] public int ObjFCritterAutoLevelScheme { get; set; } 36 | [Order(65)] public int ObjFCritterPadI1 { get; set; } 37 | [Order(66)] public int ObjFCritterPadI2 { get; set; } 38 | [Order(67)] public int ObjFCritterPadI3 { get; set; } 39 | [Order(68)] public Unknown ObjFCritterPadIas1 { get; set; } 40 | [Order(69)] public Unknown ObjFCritterPadI64As1 { get; set; } 41 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Food.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Food : Item 4 | { 5 | [Order(60)] public int ObjFFoodFlags { get; set; } 6 | [Order(61)] public int ObjFFoodPadI1 { get; set; } 7 | [Order(62)] public int ObjFFoodPadI2 { get; set; } 8 | [Order(63)] public Unknown ObjFFoodPadIas1 { get; set; } 9 | [Order(64)] public Unknown ObjFFoodPadI64As1 { get; set; } 10 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Generic.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Generic : Item 4 | { 5 | [Order(60)] public int ObjFGenericFlags { get; set; } 6 | [Order(61)] public int ObjFGenericUsageBonus { get; set; } 7 | [Order(62)] public int ObjFGenericUsageCountRemaining { get; set; } 8 | [Order(63)] public Unknown ObjFGenericPadIas1 { get; set; } 9 | [Order(64)] public Unknown ObjFGenericPadI64As1 { get; set; } 10 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Gold.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Gold : Item 4 | { 5 | [Order(60)] public int ObjFGoldFlags { get; set; } 6 | [Order(61)] public int ObjFGoldQuantity { get; set; } 7 | [Order(62)] public int ObjFGoldPadI1 { get; set; } 8 | [Order(63)] public int ObjFGoldPadI2 { get; set; } 9 | [Order(64)] public Unknown ObjFGoldPadIas1 { get; set; } 10 | [Order(65)] public Unknown ObjFGoldPadI64As1 { get; set; } 11 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Item.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Item : Common 4 | { 5 | [Order(37)] public int ObjFItemFlags { get; set; } 6 | [Order(38)] public GameObjectGuid ObjFItemParent { get; set; } 7 | [Order(39)] public int ObjFItemWeight { get; set; } 8 | [Order(40)] public int ObjFItemMagicWeightAdj { get; set; } 9 | [Order(41)] public int ObjFItemWorth { get; set; } 10 | [Order(42)] public int ObjFItemManaStore { get; set; } 11 | [Order(43)] public int ObjFItemInvAid { get; set; } //string aid 12 | [Order(44)] public int ObjFItemInvLocation { get; set; } 13 | [Order(45)] public int ObjFItemUseAidFragment { get; set; } //string aid 14 | [Order(46)] public int ObjFItemMagicTechComplexity { get; set; } 15 | [Order(47)] public int ObjFItemDiscipline { get; set; } 16 | [Order(48)] public int ObjFItemDescriptionUnknown { get; set; } 17 | [Order(49)] public int ObjFItemDescriptionEffects { get; set; } 18 | [Order(50)] public int ObjFItemSpell1 { get; set; } 19 | [Order(51)] public int ObjFItemSpell2 { get; set; } 20 | [Order(52)] public int ObjFItemSpell3 { get; set; } 21 | [Order(53)] public int ObjFItemSpell4 { get; set; } 22 | [Order(54)] public int ObjFItemSpell5 { get; set; } 23 | [Order(55)] public int ObjFItemSpellManaStore { get; set; } 24 | [Order(56)] public int ObjFItemAiAction { get; set; } 25 | [Order(57)] public int ObjFItemPadI1 { get; set; } 26 | [Order(58)] public Unknown ObjFItemPadIas1 { get; set; } 27 | [Order(59)] public Unknown ObjFItemPadI64As1 { get; set; } 28 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Key.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Key : Item 4 | { 5 | [Order(60)] public int ObjFKeyKeyId { get; set; } 6 | [Order(61)] public int ObjFKeyPadI1 { get; set; } 7 | [Order(62)] public int ObjFKeyPadI2 { get; set; } 8 | [Order(63)] public Unknown ObjFKeyPadIas1 { get; set; } 9 | [Order(64)] public Unknown ObjFKeyPadI64As1 { get; set; } 10 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/KeyRing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ArcNET.DataTypes.GameObjects.Types; 4 | 5 | public class KeyRing : Item 6 | { 7 | [Order(60)] public int ObjFKeyRingFlags { get; set; } 8 | [Order(61)] public Tuple ObjFKeyRingListIdx { get; set; } 9 | [Order(62)] public int ObjFKeyRingPadI1 { get; set; } 10 | [Order(63)] public int ObjFKeyRingPadI2 { get; set; } 11 | [Order(64)] public Unknown ObjFKeyRingPadIas1 { get; set; } 12 | [Order(65)] public Unknown ObjFKeyRingPadI64As1 { get; set; } 13 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/NPC.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.Common; 2 | using System; 3 | 4 | namespace ArcNET.DataTypes.GameObjects.Types; 5 | 6 | public class Npc : Critter 7 | { 8 | [Order(70)] public int ObjFNpcFlags { get; set; } 9 | [Order(71)] public GameObjectGuid ObjFNpcLeader { get; set; } 10 | [Order(72)] public int ObjFNpcAiData { get; set; } 11 | [Order(73)] public GameObjectGuid ObjFNpcCombatFocus { get; set; } 12 | [Order(74)] public GameObjectGuid ObjFNpcWhoHitMeLast { get; set; } 13 | [Order(75)] public int ObjFNpcExperienceWorth { get; set; } 14 | [Order(76)] public int ObjFNpcExperiencePool { get; set; } 15 | [Order(77)] public Tuple ObjFNpcWaypointsIdx { get; set; } 16 | [Order(78)] public int ObjFNpcWaypointCurrent { get; set; } 17 | [Order(79)] public Location ObjFNpcStandpointDay { get; set; } 18 | [Order(80)] public Location ObjFNpcStandpointNight { get; set; } 19 | [Order(81)] public int ObjFNpcOrigin { get; set; } 20 | [Order(82)] public int ObjFNpcFaction { get; set; } 21 | [Order(83)] public int ObjFNpcRetailPriceMultiplier { get; set; } 22 | [Order(84)] public GameObjectGuid ObjFNpcSubstituteInventory { get; set; } 23 | [Order(85)] public int ObjFNpcReactionBase { get; set; } 24 | [Order(86)] public int ObjFNpcSocialClass { get; set; } 25 | [Order(87)] public Tuple ObjFNpcReactionPcIdx { get; set; } 26 | [Order(88)] public Tuple ObjFNpcReactionLevelIdx { get; set; } 27 | [Order(89)] public Tuple ObjFNpcReactionTimeIdx { get; set; } 28 | [Order(90)] public int ObjFNpcWait { get; set; } 29 | [Order(91)] public int ObjFNpcGeneratorData { get; set; } 30 | [Order(92)] public int ObjFNpcPadI1 { get; set; } 31 | [Order(93)] public Tuple ObjFNpcDamageIdx { get; set; } 32 | [Order(94)] public Tuple ObjFNpcShitListIdx { get; set; } 33 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/PC.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.Common; 2 | using System; 3 | 4 | namespace ArcNET.DataTypes.GameObjects.Types; 5 | 6 | public class Pc : Critter 7 | { 8 | [Order(70)] public int ObjFPcFlags { get; set; } 9 | [Order(71)] public int ObjFPcFlagsFate { get; set; } 10 | [Order(72)] public Tuple ObjFPcReputationIdx { get; set; } 11 | [Order(73)] public Tuple ObjFPcReputationTsIdx { get; set; } 12 | [Order(74)] public int ObjFPcBackground { get; set; } 13 | [Order(75)] public int ObjFPcBackgroundText { get; set; } 14 | [Order(76)] public Tuple ObjFPcQuestIdx { get; set; } 15 | [Order(77)] public Tuple ObjFPcBlessingIdx { get; set; } 16 | [Order(78)] public Tuple ObjFPcBlessingTsIdx { get; set; } 17 | [Order(79)] public Tuple ObjFPcCurseIdx { get; set; } 18 | [Order(80)] public Tuple ObjFPcCurseTsIdx { get; set; } 19 | [Order(81)] public int ObjFPcPartyId { get; set; } 20 | [Order(82)] public Tuple ObjFPcRumorIdx { get; set; } 21 | [Order(83)] public Unknown ObjFPcPadIas2 { get; set; } 22 | [Order(84)] public Tuple ObjFPcSchematicsFoundIdx { get; set; } 23 | [Order(85)] public Tuple ObjFPcLogbookEgoIdx { get; set; } 24 | [Order(86)] public int ObjFPcFogMask { get; set; } 25 | [Order(87)] public PrefixedString ObjFPcPlayerName { get; set; } 26 | [Order(88)] public int ObjFPcBankMoney { get; set; } 27 | [Order(89)] public Tuple ObjFPcGlobalFlags { get; set; } 28 | [Order(90)] public Tuple ObjFPcGlobalVariables { get; set; } 29 | [Order(91)] public int ObjFPcPadI1 { get; set; } 30 | [Order(92)] public int ObjFPcPadI2 { get; set; } 31 | [Order(93)] public Unknown ObjFPcPadIas1 { get; set; } 32 | [Order(94)] public Unknown ObjFPcPadI64As1 { get; set; } 33 | [Order(95)] public Unknown ObjFPcPadI64As2 { get; set; } 34 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Portal.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Portal : Common 4 | { 5 | [Order(37)] public int ObjFPortalFlags { get; set; } 6 | [Order(38)] public int ObjFPortalLockDifficulty { get; set; } 7 | [Order(39)] public int ObjFPortalKeyId { get; set; } 8 | [Order(40)] public int ObjFPortalNotifyNpc { get; set; } 9 | [Order(41)] public int ObjFPortalPadI1 { get; set; } 10 | [Order(42)] public int ObjFPortalPadI2 { get; set; } 11 | [Order(43)] public Unknown ObjFPortalPadIas1 { get; set; } 12 | [Order(44)] public Unknown ObjFPortalPadI64As1 { get; set; } 13 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Projectile.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.Common; 2 | 3 | namespace ArcNET.DataTypes.GameObjects.Types; 4 | 5 | public class Projectile : Common 6 | { 7 | [Order(37)] public int ObjFProjectileFlagsCombat { get; set; } 8 | [Order(38)] public int ObjFProjectileFlagsCombatDamage { get; set; } 9 | [Order(39)] public Location ObjFProjectileHitLoc { get; set; } 10 | [Order(40)] public int ObjFProjectileParentWeapon { get; set; } 11 | [Order(41)] public int ObjFProjectilePadI1 { get; set; } 12 | [Order(42)] public int ObjFProjectilePadI2 { get; set; } 13 | [Order(43)] public Unknown ObjFProjectilePadIas1 { get; set; } 14 | [Order(44)] public Unknown ObjFProjectilePadI64As1 { get; set; } 15 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Scenery.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Scenery : Common 4 | { 5 | [Order(37)] public int ObjFSceneryFlags { get; set; } 6 | [Order(38)] public GameObjectGuid ObjFSceneryWhosInMe { get; set; } 7 | [Order(39)] public int ObjFSceneryRespawnDelay { get; set; } 8 | [Order(40)] public int ObjFSceneryPadI2 { get; set; } 9 | [Order(41)] public Unknown ObjFSceneryPadIas1 { get; set; } //byte 10 | [Order(42)] public Unknown ObjFSceneryPadI64As1 { get; set; } //byte 11 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Scroll.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Scroll : Item 4 | { 5 | [Order(60)] public int ObjFScrollFlags { get; set; } 6 | [Order(61)] public int ObjFScrollPadI1 { get; set; } 7 | [Order(62)] public int ObjFScrollPadI2 { get; set; } 8 | [Order(63)] public Unknown ObjFScrollPadIas1 { get; set; } 9 | [Order(64)] public Unknown ObjFScrollPadI64As1 { get; set; } 10 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Trap.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Trap : Common 4 | { 5 | [Order(37)] public int ObjFTrapFlags { get; set; } 6 | [Order(38)] public int ObjFTrapDifficulty { get; set; } 7 | [Order(39)] public int ObjFTrapPadI2 { get; set; } 8 | [Order(40)] public Unknown ObjFTrapPadIas1 { get; set; } 9 | [Order(41)] public Unknown ObjFTrapPadI64As1 { get; set; } 10 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Unknown.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Unknown 4 | { 5 | public const byte Unk = 0x00; 6 | 7 | public override string ToString() 8 | => Unk.ToString(); 9 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Wall.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Wall : Common 4 | { 5 | [Order(37)] public int ObjFWallFlags { get; set; } 6 | [Order(38)] public int ObjFWallPadI1 { get; set; } 7 | [Order(39)] public int ObjFWallPadI2 { get; set; } 8 | [Order(40)] public Unknown ObjFWallPadIas1 { get; set; } 9 | [Order(41)] public Unknown ObjFWallPadI64As1 { get; set; } 10 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Weapon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ArcNET.DataTypes.GameObjects.Types; 4 | 5 | public class Weapon : Item 6 | { 7 | [Order(60)] public int ObjFWeaponFlags { get; set; } 8 | [Order(61)] public int ObjFWeaponPaperDollAid { get; set; } //string 9 | [Order(62)] public int ObjFWeaponBonusToHit { get; set; } 10 | [Order(63)] public int ObjFWeaponMagicHitAdj { get; set; } 11 | [Order(64)] public Tuple ObjFWeaponDamageLowerIdx { get; set; } 12 | [Order(65)] public Tuple ObjFWeaponDamageUpperIdx { get; set; } 13 | [Order(66)] public Tuple ObjFWeaponMagicDamageAdjIdx { get; set; } 14 | [Order(67)] public int ObjFWeaponSpeedFactor { get; set; } 15 | [Order(68)] public int ObjFWeaponMagicSpeedAdj { get; set; } 16 | [Order(69)] public int ObjFWeaponRange { get; set; } 17 | [Order(70)] public int ObjFWeaponMagicRangeAdj { get; set; } 18 | [Order(71)] public int ObjFWeaponMinStrength { get; set; } 19 | [Order(72)] public int ObjFWeaponMagicMinStrengthAdj { get; set; } 20 | [Order(73)] public int ObjFWeaponAmmoType { get; set; } //enum 21 | [Order(74)] public int ObjFWeaponAmmoConsumption { get; set; } 22 | [Order(75)] public int ObjFWeaponMissileAid { get; set; } //string 23 | [Order(76)] public int ObjFWeaponVisualEffectAid { get; set; } //string 24 | [Order(77)] public int ObjFWeaponCritHitChart { get; set; } 25 | [Order(78)] public int ObjFWeaponMagicCritHitChance { get; set; } 26 | [Order(79)] public int ObjFWeaponMagicCritHitEffect { get; set; } 27 | [Order(80)] public int ObjFWeaponCritMissChart { get; set; } 28 | [Order(81)] public int ObjFWeaponMagicCritMissChance { get; set; } 29 | [Order(82)] public int ObjFWeaponMagicCritMissEffect { get; set; } 30 | [Order(83)] public int ObjFWeaponPadI1 { get; set; } 31 | [Order(84)] public int ObjFWeaponPadI2 { get; set; } 32 | [Order(85)] public Unknown ObjFWeaponPadIas1 { get; set; } 33 | [Order(86)] public Unknown ObjFWeaponPadI64As1 { get; set; } 34 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/GameObjects/Types/Written.cs: -------------------------------------------------------------------------------- 1 | namespace ArcNET.DataTypes.GameObjects.Types; 2 | 3 | public class Written : Item 4 | { 5 | [Order(60)] public int ObjFWrittenFlags { get; set; } 6 | [Order(61)] public int ObjFWrittenSubtype { get; set; } 7 | [Order(62)] public int ObjFWrittenTextStartLine { get; set; } 8 | [Order(63)] public int ObjFWrittenTextEndLine { get; set; } 9 | [Order(64)] public int ObjFWrittenPadI1 { get; set; } 10 | [Order(65)] public int ObjFWrittenPadI2 { get; set; } 11 | [Order(66)] public Unknown ObjFWrittenPadIas1 { get; set; } 12 | [Order(67)] public Unknown ObjFWrittenPadI64As1 { get; set; } 13 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/Generators/Wikia.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ArcNET.DataTypes.GameObjects.Classes; 3 | 4 | namespace ArcNET.DataTypes.Generators; 5 | 6 | public class Wikia 7 | { 8 | public static string Header = "{{EntityInfobox"; 9 | 10 | public static string[] EntityInfoboxElements = 11 | { 12 | "| image = ", 13 | "| race = ", 14 | "| gender = ", 15 | "| level = ", 16 | "| hit points = ", 17 | "| fatigue = ", 18 | "| alignment = ", 19 | "| aptitude = ", 20 | "| faction = ", 21 | "| st = ", 22 | "| cn = ", 23 | "| dx = ", 24 | "| be = ", 25 | "| in = ", 26 | "| wp = ", 27 | "| ch = ", 28 | "| normal = ", 29 | "| fire = ", 30 | "| electrical = ", 31 | "| poison = ", 32 | "| magic = ", 33 | "| normalDmg = ", 34 | "| fireDmg = ", 35 | "| electricalDmg =", 36 | "| poisonDmg = ", 37 | "| magicDmg = ", 38 | }; 39 | 40 | public static string Footer = "}}"; 41 | 42 | public static string GetEntityInfobox(Entity entity) 43 | { 44 | string result = string.Empty; 45 | result += Header; 46 | foreach (string infoboxElement in EntityInfoboxElements) 47 | switch (infoboxElement) 48 | { 49 | case "| image = ": 50 | result += infoboxElement; 51 | break; 52 | case "| race = ": 53 | var raceStr = ""; 54 | bool race = entity.BasicStats.Any(stat => stat.Item1 == Entity.BasicStatType.Race); 55 | if (race) raceStr = entity.BasicStats.First(stat => stat.Item1 == Entity.BasicStatType.Race).Item2.ToString(); 56 | 57 | result = result + infoboxElement + raceStr; 58 | break; 59 | case "| gender = ": 60 | var genderStr = ""; 61 | bool gender = entity.BasicStats.Any(stat => stat.Item1 == Entity.BasicStatType.Gender); 62 | if (gender) genderStr = entity.BasicStats.First(stat => stat.Item1 == Entity.BasicStatType.Gender).Item2.ToString(); 63 | 64 | result = result + infoboxElement + genderStr; 65 | break; 66 | case "| level = ": 67 | result = result + infoboxElement + entity.Level; 68 | break; 69 | case "| hit points = ": 70 | result = result + infoboxElement + entity.HitPoints; 71 | break; 72 | case "| fatigue = ": 73 | result = result + infoboxElement + entity.Fatigue; 74 | break; 75 | case "| alignment = ": 76 | result = result + infoboxElement + entity.Alignment; 77 | break; 78 | case "| aptitude = ": 79 | result += infoboxElement; 80 | break; 81 | case "| faction = ": 82 | result = result + infoboxElement + entity.Faction; 83 | break; 84 | } 85 | 86 | result += Footer; 87 | return result; 88 | } 89 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/MessageEntry.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.IO; 4 | 5 | namespace ArcNET.DataTypes; 6 | 7 | public class MessageEntry 8 | { 9 | private int _index; 10 | private int _dataCount; 11 | private string[] _data; 12 | 13 | public MessageEntry(string[] data) 14 | { 15 | _data = data; 16 | if (!ParseIndex()) 17 | throw new Exception("Failure to parse index for MessageEntry"); 18 | } 19 | 20 | public MessageEntry(string line) 21 | { 22 | _data = new MessageEntryReader(line).Parse(); 23 | if (!ParseIndex()) 24 | throw new Exception("Failure to parse index for MessageEntry"); 25 | } 26 | 27 | public MessageEntry(TextReader textReader) 28 | { 29 | _data = new MessageEntryReader(textReader).Parse(); 30 | if (!ParseIndex()) 31 | throw new Exception("Failure to parse index for MessageEntry"); 32 | } 33 | 34 | private bool ParseIndex() 35 | { 36 | var indexParsed = false; 37 | 38 | try 39 | { 40 | indexParsed = int.TryParse(_data[0], out _index); 41 | } 42 | catch (Exception ex) 43 | { 44 | AnsiConsole.WriteException(ex); 45 | } 46 | 47 | _dataCount = _data.Length; 48 | 49 | return indexParsed && (_dataCount is 2 or 7); 50 | } 51 | 52 | public int GetIndex() 53 | => _index; 54 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/MessageEntryReader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text.RegularExpressions; 3 | using Utils.Console; 4 | 5 | namespace ArcNET.DataTypes; 6 | 7 | public class MessageEntryReader 8 | { 9 | private readonly string _input; 10 | private const string Pattern = "{(.*?)}"; 11 | private const string Substitution = "$1"; 12 | 13 | public MessageEntryReader(TextReader textReader) 14 | { 15 | _input = textReader.ReadLine(); 16 | } 17 | 18 | public MessageEntryReader(string input) 19 | { 20 | _input = input; 21 | } 22 | 23 | private static string[] Parse(string input) 24 | { 25 | //failures to conform to pattern: 26 | input = input switch 27 | { 28 | "2028 door light wood" => "{2028}{door-light-wood}", 29 | "2029 door heavy wood" => "{2029}{door-heavy-wood}", 30 | "2030 door metal gate" => "{2030}{door-metal-gate}", 31 | "2031 door metal" => "{2031}{door-metal}", 32 | "2032 door glass" => "{2032}{door-glass}", 33 | "2033 door stone" => "{2033}{door-stone}", 34 | "2034 door rock cave in" => "{2034}{door-rock-cave-in}", 35 | "2035 window house standard" => "{2035}{window-house-standard}", 36 | "2036 window store front" => "{2036}{window-store-front}", 37 | "2037 window curtains only" => "{2037}{window-curtains-only}", 38 | "2038 window jail bars" => "{2038}{window-jail-bars}", 39 | "2039 window wooden shutters" => "{2039}{window-wooden-shutters}", 40 | "2040 window metal shutters" => "{2040}{window-metal-shutters}", 41 | "2041 window stained glass" => "{2041}{window-stained-glass}", 42 | "2042 window boarded up" => "{2042}{window-boarded-up}", 43 | _ => input 44 | }; 45 | 46 | var regex = new Regex(Pattern); 47 | if (Regex.Matches(input, Pattern).Count == 0) 48 | ConsoleExtensions.Log($"Input: |{input}| failed to match pattern: {Pattern}!", "error"); 49 | MatchCollection matches = Regex.Matches(input, Pattern); 50 | 51 | var output = new string[matches.Count]; 52 | for (var i = 0; i < matches.Count; i++) 53 | output[i] = regex.Replace(matches[i].Value, Substitution); 54 | 55 | return output; 56 | } 57 | 58 | public string[] Parse() 59 | => Parse(_input); 60 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/MessageReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using Utils.Enumeration; 4 | 5 | namespace ArcNET.DataTypes; 6 | 7 | public class MessageReader 8 | { 9 | private readonly StreamReader _reader; 10 | 11 | public MessageReader(StreamReader reader) 12 | { 13 | _reader = reader; 14 | } 15 | 16 | public List Parse(string fileType) 17 | { 18 | var data = new List(); 19 | var lines = new List(); 20 | 21 | //Get all data 22 | while (!_reader.EndOfStream) 23 | lines.Add(_reader.ReadLine()); 24 | 25 | //for each fileType different filtering of data 26 | switch (fileType) 27 | { 28 | case "InvenSource.mes": 29 | { 30 | foreach ((string line, int _) in lines.WithIndex()) 31 | { 32 | if (EmptyOrBadLine(line)) continue; 33 | 34 | data.Add(line); 35 | } 36 | 37 | break; 38 | } 39 | case "InvenSourceBuy.mes": 40 | { 41 | foreach ((string line, int _) in lines.WithIndex()) 42 | { 43 | if (EmptyOrBadLine(line)) continue; 44 | 45 | data.Add(line); 46 | } 47 | 48 | break; 49 | } 50 | case "xp_level.mes" or "xp_critter.mes" or "xp_quest.mes": 51 | { 52 | foreach ((string line, int _) in lines.WithIndex()) 53 | { 54 | if (EmptyOrBadLine(line)) continue; 55 | 56 | data.Add(line); 57 | } 58 | 59 | break; 60 | } 61 | case "backgrnd.mes": 62 | { 63 | foreach ((string line, int _) in lines.WithIndex()) 64 | { 65 | if (EmptyOrBadLine(line)) continue; 66 | 67 | data.Add(line); 68 | } 69 | 70 | break; 71 | } 72 | case "faction.mes": 73 | { 74 | foreach ((string line, int _) in lines.WithIndex()) 75 | { 76 | if (EmptyOrBadLine(line)) continue; 77 | 78 | data.Add(line); 79 | } 80 | 81 | break; 82 | } 83 | case "gamelevel.mes": 84 | { 85 | foreach ((string line, int _) in lines.WithIndex()) 86 | { 87 | if (EmptyOrBadLine(line)) continue; 88 | 89 | data.Add(line); 90 | } 91 | 92 | break; 93 | } 94 | case "description.mes": 95 | { 96 | foreach ((string line, int _) in lines.WithIndex()) 97 | { 98 | if (EmptyOrBadLine(line)) continue; 99 | 100 | data.Add(line); 101 | } 102 | 103 | break; 104 | } 105 | } 106 | 107 | return data; 108 | } 109 | 110 | private static bool EmptyOrBadLine(string line) 111 | => string.IsNullOrEmpty(line) || !line.StartsWith("{"); 112 | 113 | /* 114 | curLine = curLine.TrimStart(' ', '\t'); 115 | var unicodeLines = new[] { 116 | "WILDERNESS (NICE) MUSIC", 117 | "WILDERNESS (EVIL) MUSIC", 118 | "VILLAGE MUSIC", 119 | "TOWN MUSIC MUSIC", 120 | "CITY TARRANT MUSIC", 121 | "CITY CALADON MUSIC", 122 | "CITY DERHOLM MUSIC", 123 | "ELVEN MUSIC", 124 | "DARK ELVEN MUSIC", 125 | "DWARVEN MUSIC", 126 | "CHAMBER DARK 1 MUSIC", 127 | "CHAMBER DARK 2 MUSIC", 128 | "CHAMBER CHASE MUSIC", 129 | "DARK AMBIENT (HEAVY) MUSIC", 130 | "DARK AMBIENT (LIGHT) MUSIC", 131 | "CRASH SITE MUSIC", 132 | "MIN'GOURAD'S LIAR MUSIC", 133 | "KREE MUSIC", 134 | "TEMPLE OF THE DERIAN KA MUSIC", 135 | "THE DREDGE/IRON CLAN MUSIC", 136 | "THE VOID MUSIC", 137 | "KERGHAN'S LIAR MUSIC", 138 | "ISLE OF DESPAIR MUSIC", 139 | "TULLA MUSIC", 140 | "ARCANUM THEME", 141 | "MAIN MENU MUSIC" 142 | }; 143 | 144 | if (unicodeLines.Any(curLine.Contains)) 145 | { 146 | ConsoleExtensions.Log($"unicode line:|{curLine}|", "warn"); 147 | continue; 148 | } 149 | if (curLine.StartsWith("//") || curLine.StartsWith("/\t\t") || curLine.StartsWith("***") || !curLine.StartsWith("{")) 150 | { 151 | ConsoleExtensions.Log($"bad line:|{curLine}|", "warn"); 152 | continue; 153 | } 154 | if (string.IsNullOrEmpty(curLine)) 155 | { 156 | ConsoleExtensions.Log($"empty line:|{curLine}|", "warn"); 157 | continue; 158 | } 159 | 160 | //TODO: multiline messages 161 | var mesEntry = new MessageEntry(curLine); 162 | if (!mes.ExistEntryWithIndex(mesEntry.GetIndex())) 163 | mes.AddEntry(mesEntry.GetIndex(), curLine); 164 | */ 165 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/Sector.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.Common; 2 | using ArcNET.DataTypes.GameObjects; 3 | using Newtonsoft.Json; 4 | using System.Collections.Generic; 5 | 6 | namespace ArcNET.DataTypes; 7 | 8 | public class Sector 9 | { 10 | public struct SectorTile 11 | { 12 | public uint Data { get; set; } 13 | } 14 | 15 | public IList Lights { get; private set; } = new List(); 16 | public SectorTile[] Tiles { get; private set; } = new SectorTile[4096]; 17 | public List Objects { get; private set; } = new(); 18 | public GameObjectScript SectorScript { get; set; } 19 | public List TileScripts { get; private set; } = new(); 20 | 21 | public static uint GetSectorLoc(int x, int y) 22 | => (((uint)y << 26) & 0xFC) | ((uint)x & 0xFC); 23 | 24 | public string GetEntriesAsJson() 25 | => JsonConvert.SerializeObject(this, Formatting.Indented); 26 | } 27 | 28 | public class SectorLight 29 | { 30 | public ulong Handle { get; set; } 31 | public Location Position { get; set; } // x, y, offsx, offsy 32 | public int OffsetX { get; set; } 33 | public int OffsetY { get; set; } 34 | public int Flags0 { get; set; } 35 | public int Art { get; set; } 36 | public int Color0 { get; set; } 37 | public int Color1 { get; set; } 38 | public int Unk0 { get; set; } // MAYBE FLAGS 39 | public int Unk1 { get; set; } 40 | } 41 | 42 | public class TileScript 43 | { 44 | public int F1 { get; set; } 45 | public int F2 { get; set; } 46 | public int F3 { get; set; } 47 | public int F4 { get; set; } 48 | public int F5 { get; set; } 49 | public int F6 { get; set; } 50 | 51 | public override string ToString() 52 | => $"{F1} {F2} {F3} {F4} {F5} {F6}"; 53 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/SectorReader.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.Common; 2 | using ArcNET.DataTypes.GameObjects; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using Utils.Console; 8 | 9 | namespace ArcNET.DataTypes; 10 | 11 | public class SectorReader 12 | { 13 | private readonly BinaryReader _reader; 14 | 15 | public SectorReader(BinaryReader reader) 16 | { 17 | _reader = reader; 18 | } 19 | 20 | public Sector ReadSector() 21 | { 22 | var sector = new Sector(); 23 | ReadLights(_reader, sector.Lights); 24 | ReadTiles(_reader, sector.Tiles); 25 | SkipRoofList(_reader); 26 | 27 | int placeholder = _reader.ReadInt32(); 28 | ConsoleExtensions.Log(placeholder.ToString("X4"), "info"); 29 | ConsoleExtensions.Log(_reader.BaseStream.Position.ToString("X4"), "info"); 30 | 31 | if (placeholder < 0xAA0000 || placeholder > 0xAA0004) 32 | throw new InvalidDataException("Invalid placeholder value read from sector."); 33 | 34 | // All of these seems to be old Arcanum leftovers 35 | if (placeholder >= 0xAA0001) 36 | ReadTileScripts(_reader, sector); 37 | 38 | if (placeholder >= 0xAA0002) 39 | ReadSectorScripts(_reader, sector); 40 | 41 | if (placeholder >= 0xAA0003) 42 | { 43 | _reader.ReadInt32(); // Townmap Info 44 | _reader.ReadInt32(); // Aptitude Adjustment 45 | _reader.ReadInt32(); // Light Scheme 46 | _reader.ReadBytes(12); // Sound List 47 | } 48 | 49 | if (placeholder >= 0xAA0004) 50 | _reader.ReadBytes(512); 51 | 52 | ReadObjects(_reader, sector); 53 | 54 | if (_reader.BaseStream.Position + 4 != _reader.BaseStream.Length) 55 | throw new Exception(); 56 | 57 | return sector; 58 | } 59 | 60 | private static void ReadLights(BinaryReader reader, ICollection sectorLights) 61 | { 62 | int lightCount = reader.ReadInt32(); 63 | 64 | for (var i = 0; i < lightCount; ++i) 65 | { 66 | SectorLight light = ReadLight(reader); 67 | sectorLights.Add(light); 68 | } 69 | } 70 | 71 | private static SectorLight ReadLight(BinaryReader reader) 72 | { 73 | ulong handle = reader.ReadUInt64(); 74 | 75 | //var type = reader.ReadInt32(); 76 | 77 | var result = new SectorLight 78 | { 79 | Handle = handle, 80 | Position = reader.ReadLocation(true), 81 | OffsetX = reader.ReadInt32(), 82 | OffsetY = reader.ReadInt32(), 83 | Flags0 = reader.ReadInt32(), 84 | Art = reader.ReadInt32(), 85 | Color0 = reader.ReadInt32(), 86 | Color1 = reader.ReadInt32(), 87 | Unk0 = reader.ReadInt32(), 88 | Unk1 = reader.ReadInt32() 89 | }; 90 | 91 | // Read the basic light information first 92 | /* 93 | if ((type & 0x10) == 0x10 || (type & 0x40) == 0x40) 94 | { 95 | var partSys = new SectorLightParticles(); 96 | partSys.ParticleSystemHash = reader.ReadInt32(); 97 | partSys.ParticleSystemHandle = reader.ReadInt32(); 98 | result.Particles = partSys; 99 | } 100 | if ((type & 0x40) == 0x40) 101 | { 102 | var atNight = new SectorLightAtNight(); 103 | atNight.field0 = reader.ReadInt32(); 104 | atNight.field4 = reader.ReadInt32(); 105 | atNight.field8 = reader.ReadInt32(); 106 | atNight.fieldc = reader.ReadInt32(); 107 | atNight.field10 = reader.ReadInt32(); 108 | atNight.field14 = reader.ReadInt32(); 109 | atNight.field18 = reader.ReadInt32(); 110 | result.AtNight = atNight; 111 | 112 | var partSys = new SectorLightParticles(); 113 | partSys.ParticleSystemHash = reader.ReadInt32(); 114 | partSys.ParticleSystemHandle = reader.ReadInt32(); 115 | atNight.Particles = partSys; 116 | } 117 | */ 118 | 119 | return result; 120 | } 121 | 122 | private static void ReadTiles(BinaryReader reader, Sector.SectorTile[] tiles) 123 | { 124 | for (var i = 0; i < tiles.Length; ++i) 125 | { 126 | tiles[i].Data = reader.ReadUInt32(); 127 | ArtId.ArtIds.Add(tiles[i].Data.ToString("X2")); 128 | } 129 | } 130 | 131 | private static void SkipRoofList(BinaryReader reader) 132 | { 133 | int isPresent = reader.ReadInt32(); 134 | if (isPresent == 0) 135 | reader.BaseStream.Seek(256 * 4, SeekOrigin.Current); 136 | } 137 | 138 | private static void ReadTileScripts(BinaryReader reader, Sector sector) 139 | { 140 | int count = reader.ReadInt32(); 141 | //TODO: count > 0 142 | for (var i = 0; i < count; ++i) 143 | { 144 | var script = new TileScript 145 | { 146 | F1 = reader.ReadInt32(), 147 | F2 = reader.ReadInt32(), 148 | F3 = reader.ReadInt32(), 149 | F4 = reader.ReadInt32(), 150 | F5 = reader.ReadInt32(), 151 | F6 = reader.ReadInt32() 152 | }; 153 | sector.TileScripts.Add(script); 154 | } 155 | } 156 | 157 | private static void ReadSectorScripts(BinaryReader reader, Sector sector) 158 | { 159 | var script = new GameObjectScript 160 | { 161 | Counters = reader.ReadBytes(4), 162 | Flags = reader.ReadInt32(), 163 | ScriptId = reader.ReadInt32() 164 | }; 165 | 166 | if (script.ScriptId != 0 || !script.Counters.SequenceEqual(new byte[] { 0x00, 0x00, 0x00, 0x00 }) || script.Flags != 0) 167 | sector.SectorScript = script; 168 | } 169 | 170 | private static void ReadObjects(BinaryReader reader, Sector sector) 171 | { 172 | Stream stream = reader.BaseStream; 173 | long startOfObjects = stream.Position; 174 | 175 | // Move to end of file, last 4 bytes are count of objects 176 | stream.Seek(-4, SeekOrigin.End); 177 | int count = reader.ReadInt32(); 178 | 179 | // Now move back to start and parse for count found 180 | stream.Seek(startOfObjects, SeekOrigin.Begin); 181 | for (var i = 0; i < count; ++i) 182 | sector.Objects.Add(reader.GetGameObject()); 183 | } 184 | } -------------------------------------------------------------------------------- /ArcNET.DataTypes/TextDataReader.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes.GameObjects; 2 | using ArcNET.DataTypes.GameObjects.Classes; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using Utils.Console; 6 | 7 | namespace ArcNET.DataTypes; 8 | 9 | public class TextDataReader 10 | { 11 | private readonly StreamReader _reader; 12 | 13 | public TextDataReader(StreamReader reader) 14 | { 15 | _reader = reader; 16 | } 17 | 18 | public void Parse(string type) 19 | { 20 | var mobStringList = new List(); 21 | 22 | while (true) 23 | { 24 | string curLine = _reader.ReadLine(); 25 | 26 | if (!string.IsNullOrWhiteSpace(curLine)) 27 | { 28 | string[] lines = curLine.Split(":", 2); 29 | string paramName = lines[0]; 30 | string paramValue = lines[1]; 31 | 32 | switch (paramName) 33 | { 34 | case "Description" when paramValue.Contains(@"\\"): 35 | mobStringList.Add(curLine); 36 | break; 37 | case "Description": 38 | mobStringList.Add(curLine); 39 | break; 40 | case "Internal Name" or "internal name": 41 | mobStringList.Add(curLine); 42 | break; 43 | case "Level": 44 | mobStringList.Add(curLine); 45 | break; 46 | case "Art Number and Palette": 47 | mobStringList.Add(curLine); 48 | break; 49 | case "Scale": 50 | mobStringList.Add(curLine); 51 | break; 52 | case "Alignment": 53 | mobStringList.Add(curLine); 54 | break; 55 | case "Object Flag": 56 | mobStringList.Add(curLine); 57 | break; 58 | case "Critter Flag": 59 | mobStringList.Add(curLine); 60 | break; 61 | case "Critter2 Flag": 62 | mobStringList.Add(curLine); 63 | break; 64 | case "NPC Flag": 65 | mobStringList.Add(curLine); 66 | break; 67 | case "Blit Flag": 68 | mobStringList.Add(curLine); 69 | break; 70 | case "Spell Flag": 71 | mobStringList.Add(curLine); 72 | break; 73 | case "Hit Chart": 74 | mobStringList.Add(curLine); 75 | break; 76 | case "Basic Stat" or "basic stat": 77 | mobStringList.Add(curLine); 78 | break; 79 | case "Spell" or "spell": 80 | mobStringList.Add(curLine); 81 | break; 82 | case "Script": 83 | mobStringList.Add(curLine); 84 | break; 85 | case "Faction": 86 | mobStringList.Add(curLine); 87 | break; 88 | case "AI Packet": 89 | mobStringList.Add(curLine); 90 | break; 91 | case "Material": 92 | mobStringList.Add(curLine); 93 | break; 94 | case "Hit Points": 95 | mobStringList.Add(curLine); 96 | break; 97 | case "Fatigue": 98 | mobStringList.Add(curLine); 99 | break; 100 | case "Damage Resistance" or "damage resistance": 101 | mobStringList.Add(curLine); 102 | break; 103 | case "Fire Resistance": 104 | mobStringList.Add(curLine); 105 | break; 106 | case "Electrical Resistance": 107 | mobStringList.Add(curLine); 108 | break; 109 | case "Poison Resistance": 110 | mobStringList.Add(curLine); 111 | break; 112 | case "Magic Resistance": 113 | mobStringList.Add(curLine); 114 | break; 115 | case "Normal Damage": 116 | mobStringList.Add(curLine); 117 | break; 118 | case "Fatigue Damage": 119 | mobStringList.Add(curLine); 120 | break; 121 | case "Poison Damage": 122 | mobStringList.Add(curLine); 123 | break; 124 | case "Electrical Damage": 125 | mobStringList.Add(curLine); 126 | break; 127 | case "Fire Damage": 128 | mobStringList.Add(curLine); 129 | break; 130 | case "Sound Bank" or "sound bank": 131 | mobStringList.Add(curLine); 132 | break; 133 | case "Portrait": 134 | mobStringList.Add(curLine); 135 | break; 136 | case "Retail Price Multiplier": 137 | mobStringList.Add(curLine); 138 | break; 139 | case "Social Class": 140 | mobStringList.Add(curLine); 141 | break; 142 | case "Category": 143 | mobStringList.Add(curLine); 144 | break; 145 | case "Auto Level Scheme": 146 | mobStringList.Add(curLine); 147 | break; 148 | case "Inventory Source": 149 | mobStringList.Add(curLine); 150 | break; 151 | 152 | default: 153 | ConsoleExtensions.Log($"unrecognized entity param:|{paramName}|", "warn"); 154 | break; 155 | } 156 | } 157 | 158 | if (string.IsNullOrWhiteSpace(curLine)) 159 | { 160 | //assuming end of one mob block... 161 | switch (type) 162 | { 163 | case "Monster": 164 | { 165 | var currentMob = Monster.GetFromText(mobStringList); 166 | GameObjectManager.Monsters.Add(currentMob); 167 | break; 168 | } 169 | case "NPC": 170 | { 171 | var currentNpc = NPC.GetFromText(mobStringList); 172 | GameObjectManager.NPCs.Add(currentNpc); 173 | break; 174 | } 175 | case "Unique": 176 | { 177 | var currentUnique = Unique.GetFromText(mobStringList); 178 | GameObjectManager.Uniques.Add(currentUnique); 179 | break; 180 | } 181 | } 182 | 183 | mobStringList.Clear(); 184 | } 185 | 186 | if (curLine == null) break; 187 | } 188 | } 189 | } -------------------------------------------------------------------------------- /ArcNET.Terminal/ArcNET.Terminal.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ArcNET.Terminal/Parser.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.DataTypes; 2 | using ArcNET.DataTypes.GameObjects; 3 | using ArcNET.DataTypes.GameObjects.Classes; 4 | using Spectre.Console; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Text.RegularExpressions; 10 | using ArcNET.DataTypes.Generators; 11 | using Utils.Console; 12 | 13 | namespace ArcNET.Terminal; 14 | 15 | public static class Parser 16 | { 17 | private static int _facadeWalksRed; 18 | private static int _messagesRed; 19 | private static int _textsRed; 20 | private static int _sectorsRed; 21 | private static int _prototypesRed; 22 | private static int _artsRed; 23 | 24 | public enum FileType 25 | { 26 | DataArchive, 27 | FacadeWalk, 28 | Message, 29 | Sector, 30 | Prototype, 31 | PlayerBackground, 32 | Mobile, 33 | Art, 34 | Jump, 35 | Script, 36 | Dialog, 37 | Terrain, 38 | MapProperties, 39 | SoundWav, 40 | SoundMp3, 41 | Video, 42 | Bitmap, 43 | Text, 44 | Any 45 | } 46 | 47 | private static readonly Regex DataArchiveRegex = new(@"^.*\.dat$"); 48 | private static readonly Regex FacadeWalkRegex = new(@"facwalk\..{1,3}$"); 49 | private static readonly Regex MessageRegex = new(@"^.*\.mes$"); 50 | private static readonly Regex SectorRegex = new(@"^.*\.sec$"); 51 | private static readonly Regex PrototypeRegex = new(@"^.*\.pro$"); 52 | private static readonly Regex PlayerRegex = new(@"^.*\.mpc$"); 53 | private static readonly Regex MobileRegex = new(@"^.*\.mob$"); 54 | private static readonly Regex ArtRegex = new(@"^.*\.art$", RegexOptions.IgnoreCase); 55 | private static readonly Regex JumpRegex = new(@"^.*\.jmp$"); 56 | private static readonly Regex ScriptRegex = new(@"^.*\.scr$"); 57 | private static readonly Regex DialogRegex = new(@"^.*\.dlg$"); 58 | private static readonly Regex TerrainRegex = new(@"^.*\.tdf$"); 59 | private static readonly Regex MapPropertiesRegex = new(@"^.*\.prp$"); 60 | private static readonly Regex SoundWavRegex = new(@"^.*\.wav$", RegexOptions.IgnoreCase); 61 | private static readonly Regex SoundMp3Regex = new(@"^.*\.mp3$"); 62 | private static readonly Regex VideoRegex = new(@"^.*\.bik$"); 63 | private static readonly Regex BitmapRegex = new(@"^.*\.bmp$"); 64 | private static readonly Regex TextRegex = new(@"^.*\.txt$"); 65 | 66 | //TODO: rework, make entire parsing async 67 | private static void ParseAndWriteAllInDir(string dirPath) 68 | { 69 | List, FileType>> data = LoadLocalData(dirPath); 70 | 71 | AnsiConsole.Write(Terminal.DirectoryTable(dirPath, data)); 72 | 73 | GameObjectManager.Init(); 74 | 75 | //Removes potential task which are done already 76 | //TODO: remains unclear why a finished task is not at 100%, but rather demands percentage calculation. 77 | var toRemove = new HashSet, FileType>>(); 78 | foreach (Tuple, FileType> tupleList in data.Where(tuple => tuple.Item1.Count == 0 || (tuple.Item2 != FileType.Text && tuple.Item2 != FileType.Message))) 79 | toRemove.Add(tupleList); 80 | data.RemoveAll(toRemove.Contains); 81 | 82 | var tasks = new (string name, List data, FileType fileType)[data.Count]; 83 | for (var i = 0; i < data.Count; i++) 84 | tasks[i] = ($"[green]Parsing {Enum.GetName(typeof(FileType), data[i].Item2)}" 85 | + " files :[/]", data[i].Item1, data[i].Item2); 86 | 87 | AnsiConsole.Progress().Columns( 88 | new TaskDescriptionColumn(), 89 | new ProgressBarColumn(), 90 | new PercentageColumn(), 91 | new RemainingTimeColumn(), 92 | new SpinnerColumn()) 93 | .Start(ctx => 94 | { 95 | foreach ((string name, List files, FileType fileType) in tasks) 96 | { 97 | ProgressTask currentTask = ctx.AddTask(name, new ProgressTaskSettings 98 | { 99 | MaxValue = files.Count, 100 | AutoStart = false, 101 | }); 102 | 103 | foreach (string file in files) 104 | ParseAndWriteFile(file, fileType, currentTask, dirPath + @"\out\"); 105 | } 106 | }); 107 | 108 | //testing 109 | var mobsWithDrops = GameObjectManager.Monsters.Where(mob => mob.InventorySource > 0).ToList(); 110 | ConsoleExtensions.Log($"mobsWithDrops: |{mobsWithDrops.Count}|", "warn"); 111 | foreach (Monster mob in mobsWithDrops) 112 | { 113 | IEnumerable> namedDropTable = InventorySource.NamedDropTableFromId(mob.InventorySource); 114 | ConsoleExtensions.Log($"mobName: |{mob.Description.Item2}| invSrcId: |{mob.InventorySource}|", "warn"); 115 | foreach ((string name, double chance) in namedDropTable) 116 | ConsoleExtensions.Log($"itemName: |{name}| chance:|{chance}|", "warn"); 117 | 118 | string mobText = Wikia.GetEntityInfobox(mob); 119 | ConsoleExtensions.Log($"mobText: |{mobText}|", "warn"); 120 | } 121 | 122 | AnsiConsole.Write(Terminal.ReportTable(dirPath, data)); 123 | } 124 | 125 | //Todo: make async, will likely need Async BinaryRead/Write 126 | private static void ParseAndWriteFile(string fileName, FileType fileType, ProgressTask task, string outputFolder = null) 127 | { 128 | //ConsoleExtensions.Log($"Parsing file: {fileName} FileType: {fileType}", "info"); 129 | 130 | string outputPath = new FileInfo(fileName).Name; 131 | if (!string.IsNullOrEmpty(outputFolder)) 132 | { 133 | if (!Directory.Exists(outputFolder)) 134 | Directory.CreateDirectory(outputFolder); 135 | 136 | outputPath = outputFolder + outputPath; 137 | } 138 | 139 | switch (fileType) 140 | { 141 | case FileType.FacadeWalk: 142 | { 143 | /*using var reader = new BinaryReader(new FileStream(fileName, FileMode.Open)); 144 | var obj = new FacadeWalkReader(reader).Read(); 145 | if (obj == null) return; 146 | _facadeWalksRed++; 147 | task.Increment(_facadeWalksRed); 148 | 149 | FileWriter.ToJson(outputPath, obj);*/ 150 | break; 151 | } 152 | case FileType.Text: 153 | { 154 | task.StartTask(); 155 | using var reader = new StreamReader(new FileStream(fileName, FileMode.Open)); 156 | ConsoleExtensions.Log($"Parsing text file:|{fileName}|", "warn"); 157 | 158 | switch (new FileInfo(fileName).Name) 159 | { 160 | case "monster.txt": 161 | { 162 | var mobReader = new TextDataReader(reader); 163 | mobReader.Parse("Monster"); 164 | 165 | int mobCount = GameObjectManager.Monsters.Count; 166 | if (mobCount == 0) return; 167 | 168 | _textsRed++; 169 | task.Increment(+1); 170 | ConsoleExtensions.Log($"Monsters parsed: |{mobCount}|", "warn"); 171 | break; 172 | } 173 | case "npc.txt": 174 | { 175 | var npcReader = new TextDataReader(reader); 176 | npcReader.Parse("NPC"); 177 | 178 | int npcCount = GameObjectManager.NPCs.Count; 179 | if (npcCount == 0) return; 180 | 181 | _textsRed++; 182 | task.Increment(+1); 183 | ConsoleExtensions.Log($"NPCs parsed: |{npcCount}|", "warn"); 184 | break; 185 | } 186 | case "unique.txt": 187 | { 188 | var uniqueReader = new TextDataReader(reader); 189 | uniqueReader.Parse("Unique"); 190 | 191 | int uniqueCount = GameObjectManager.Uniques.Count; 192 | if (uniqueCount == 0) return; 193 | 194 | _textsRed++; 195 | task.Increment(+1); 196 | ConsoleExtensions.Log($"Uniques parsed: |{uniqueCount}|", "warn"); 197 | break; 198 | } 199 | default: 200 | throw new InvalidOperationException(fileName, null); 201 | } 202 | 203 | break; 204 | } 205 | case FileType.Message: 206 | { 207 | task.StartTask(); 208 | using var reader = new StreamReader(new FileStream(fileName, FileMode.Open)); 209 | ConsoleExtensions.Log($"Parsing mes file:|{fileName}|", "warn"); 210 | 211 | switch (new FileInfo(fileName).Name) 212 | { 213 | case "InvenSource.mes": 214 | { 215 | List textData = new MessageReader(reader).Parse("InvenSource.mes"); 216 | if (textData == null || textData.Count == 0) return; 217 | 218 | InventorySource.InitFromText(textData); 219 | _messagesRed++; 220 | task.Increment(+1); 221 | 222 | ConsoleExtensions.Log($"Loaded invSources: |{InventorySource.LoadedInventorySources.Count}|", "warn"); 223 | break; 224 | } 225 | case "InvenSourceBuy.mes": 226 | { 227 | List textData = new MessageReader(reader).Parse("InvenSourceBuy.mes"); 228 | if (textData == null || textData.Count == 0) return; 229 | 230 | InventorySourceBuy.InitFromText(textData); 231 | _messagesRed++; 232 | task.Increment(+1); 233 | 234 | ConsoleExtensions.Log($"Loaded BuyInvSources: |{InventorySourceBuy.LoadedInventoryBuySources.Count}|", "warn"); 235 | break; 236 | } 237 | case "xp_level.mes": 238 | { 239 | List textData = new MessageReader(reader).Parse("xp_level.mes"); 240 | if (textData == null || textData.Count == 0) return; 241 | 242 | XpLevels.InitFromText(textData); 243 | _messagesRed++; 244 | task.Increment(+1); 245 | 246 | ConsoleExtensions.Log($"Loaded XpLevels: |{XpLevels.LoadedXpLevels.Entries.Count}|", "warn"); 247 | break; 248 | } 249 | case "xp_critter.mes": 250 | { 251 | List textData = new MessageReader(reader).Parse("xp_level.mes"); 252 | if (textData == null || textData.Count == 0) return; 253 | 254 | CritterXpLevels.InitFromText(textData); 255 | _messagesRed++; 256 | task.Increment(+1); 257 | 258 | ConsoleExtensions.Log($"Loaded XpCritterLevels: |{CritterXpLevels.LoadedCritterXpLevels.Entries.Count}|", "warn"); 259 | break; 260 | } 261 | case "xp_quest.mes": 262 | { 263 | List textData = new MessageReader(reader).Parse("xp_quest.mes"); 264 | if (textData == null || textData.Count == 0) return; 265 | 266 | QuestXpLevels.InitFromText(textData); 267 | _messagesRed++; 268 | task.Increment(+1); 269 | 270 | ConsoleExtensions.Log($"Loaded XpQuestLevels: |{QuestXpLevels.LoadedXpQuestLevels.Entries.Count}|", "warn"); 271 | break; 272 | } 273 | case "backgrnd.mes": 274 | { 275 | List textData = new MessageReader(reader).Parse("backgrnd.mes"); 276 | if (textData == null || textData.Count == 0) return; 277 | 278 | Background.InitFromText(textData); 279 | _messagesRed++; 280 | task.Increment(+1); 281 | 282 | ConsoleExtensions.Log($"Loaded Backgrounds: |{Background.LoadedBackgrounds.Count}|", "warn"); 283 | break; 284 | } 285 | case "faction.mes": 286 | { 287 | List textData = new MessageReader(reader).Parse("faction.mes"); 288 | if (textData == null || textData.Count == 0) return; 289 | 290 | Faction.InitFromText(textData); 291 | _messagesRed++; 292 | task.Increment(+1); 293 | 294 | ConsoleExtensions.Log($"Loaded Factions: |{Faction.LoadedFactions.Entries.Count}|", "warn"); 295 | break; 296 | } 297 | case "gamelevel.mes": 298 | { 299 | List textData = new MessageReader(reader).Parse("gamelevel.mes"); 300 | if (textData == null || textData.Count == 0) return; 301 | 302 | AutoLevelSchemes.InitFromText(textData); 303 | _messagesRed++; 304 | task.Increment(+1); 305 | 306 | ConsoleExtensions.Log($"Loaded Auto Level Schemes: |{AutoLevelSchemes.LoadedAutoLevelSchemes.Entries.Count}|", "warn"); 307 | break; 308 | } 309 | case "description.mes": 310 | { 311 | List textData = new MessageReader(reader).Parse("description.mes"); 312 | if (textData == null || textData.Count == 0) return; 313 | 314 | Descriptions.InitFromText(textData); 315 | _messagesRed++; 316 | task.Increment(+1); 317 | 318 | ConsoleExtensions.Log($"Loaded description: |{Descriptions.LoadedDescriptions.Entries.Count}|", "warn"); 319 | break; 320 | } 321 | 322 | default: 323 | _messagesRed++; 324 | task.Increment(+1); 325 | break; 326 | //throw new InvalidOperationException(fileName, null); 327 | } 328 | 329 | break; 330 | } 331 | case FileType.Sector: 332 | { 333 | /*using var reader = new BinaryReader(new FileStream(fileName, FileMode.Open)); 334 | var obj = new SectorReader(reader).ReadSector(); 335 | if (obj == null) return; 336 | _sectorsRed++; 337 | task.Increment(_sectorsRed); 338 | 339 | FileWriter.ToJson(outputPath, obj.GetEntriesAsJson());*/ 340 | break; 341 | } 342 | case FileType.Art: 343 | /*_artsRed++; 344 | task.Increment(_artsRed);*/ 345 | break; 346 | case FileType.Prototype: 347 | /*_prototypesRed++; 348 | task.Increment(_prototypesRed);*/ 349 | break; 350 | case FileType.Any: 351 | break; 352 | case FileType.Mobile: 353 | break; 354 | case FileType.Jump: 355 | break; 356 | case FileType.Script: 357 | break; 358 | case FileType.Dialog: 359 | break; 360 | case FileType.Terrain: 361 | break; 362 | case FileType.MapProperties: 363 | break; 364 | case FileType.DataArchive: 365 | break; 366 | case FileType.PlayerBackground: 367 | break; 368 | case FileType.SoundWav: 369 | break; 370 | case FileType.SoundMp3: 371 | break; 372 | case FileType.Video: 373 | break; 374 | case FileType.Bitmap: 375 | break; 376 | 377 | default: 378 | throw new ArgumentOutOfRangeException(nameof(fileType), fileType, null); 379 | } 380 | } 381 | 382 | public static void ParseExtractedData() 383 | { 384 | ConsoleExtensions.Log("Insert path to file or dirPath:", "info"); 385 | string inputPath = AnsiConsole.Ask("[green]Input[/]"); 386 | while (string.IsNullOrEmpty(inputPath) || inputPath.Length < 10) 387 | { 388 | ConsoleExtensions.Log("Path either empty or incorrect format!", "error"); 389 | ConsoleExtensions.Log("Usage:", "error"); 390 | inputPath = AnsiConsole.Ask("[green]Insert path to file or dirPath[/]:"); 391 | } 392 | 393 | if (Directory.Exists(inputPath)) 394 | { 395 | try 396 | { 397 | ParseAndWriteAllInDir(inputPath); 398 | } 399 | catch (Exception ex) 400 | { 401 | AnsiConsole.WriteException(ex); 402 | throw; 403 | } 404 | } 405 | else 406 | { 407 | string fileName = Path.GetFileName(inputPath); 408 | if (string.IsNullOrEmpty(fileName) || fileName.Length < 10) 409 | { 410 | ConsoleExtensions.Log($"File: {inputPath} does not exists!", "error"); 411 | throw new Exception("File not found!"); 412 | } 413 | 414 | try 415 | { 416 | FileType fileTypeToParse = FileType.Any; 417 | if (FacadeWalkRegex.IsMatch(fileName)) 418 | fileTypeToParse = FileType.FacadeWalk; 419 | else if (MessageRegex.IsMatch(fileName)) 420 | fileTypeToParse = FileType.Message; 421 | else if (SectorRegex.IsMatch(fileName)) 422 | fileTypeToParse = FileType.Sector; 423 | else if (ArtRegex.IsMatch(fileName)) 424 | fileTypeToParse = FileType.Art; 425 | 426 | //ParseAndWriteFile(inputPath, fileTypeToParse); 427 | } 428 | catch (Exception ex) 429 | { 430 | AnsiConsole.WriteException(ex); 431 | throw; 432 | } 433 | } 434 | } 435 | 436 | public static List, FileType>> LoadLocalData(string dirPath) 437 | { 438 | var allFiles = Directory.EnumerateFiles(dirPath, "*.*", SearchOption.AllDirectories).ToList(); 439 | 440 | var datFiles = allFiles.Where(str => DataArchiveRegex.IsMatch(str)).ToList(); 441 | var facFiles = allFiles.Where(str => FacadeWalkRegex.IsMatch(str)).ToList(); 442 | var mesFiles = allFiles.Where(str => MessageRegex.IsMatch(str)).ToList(); 443 | var secFiles = allFiles.Where(str => SectorRegex.IsMatch(str)).ToList(); 444 | var proFiles = allFiles.Where(str => PrototypeRegex.IsMatch(str)).ToList(); 445 | var playerFiles = allFiles.Where(str => PlayerRegex.IsMatch(str)).ToList(); 446 | var mobFiles = allFiles.Where(str => MobileRegex.IsMatch(str)).ToList(); 447 | var artFiles = allFiles.Where(str => ArtRegex.IsMatch(str)).ToList(); 448 | var jumpFiles = allFiles.Where(str => JumpRegex.IsMatch(str)).ToList(); 449 | var scriptFiles = allFiles.Where(str => ScriptRegex.IsMatch(str)).ToList(); 450 | var dialogFiles = allFiles.Where(str => DialogRegex.IsMatch(str)).ToList(); 451 | var terrainFiles = allFiles.Where(str => TerrainRegex.IsMatch(str)).ToList(); 452 | var mapPropertiesFiles = allFiles.Where(str => MapPropertiesRegex.IsMatch(str)).ToList(); 453 | var soundWavFiles = allFiles.Where(str => SoundWavRegex.IsMatch(str)).ToList(); 454 | var soundMp3Files = allFiles.Where(str => SoundMp3Regex.IsMatch(str)).ToList(); 455 | var videoFiles = allFiles.Where(str => VideoRegex.IsMatch(str)).ToList(); 456 | var bitmapFiles = allFiles.Where(str => BitmapRegex.IsMatch(str)).ToList(); 457 | var textFiles = allFiles.Where(str => TextRegex.IsMatch(str)).ToList(); 458 | 459 | return new List, FileType>> 460 | { 461 | new(allFiles, FileType.Any), 462 | new(datFiles, FileType.DataArchive), 463 | new(textFiles, FileType.Text), 464 | new(mesFiles, FileType.Message), 465 | new(secFiles, FileType.Sector), 466 | new(proFiles, FileType.Prototype), 467 | new(playerFiles, FileType.PlayerBackground), 468 | new(mobFiles, FileType.Mobile), 469 | new(artFiles, FileType.Art), 470 | new(jumpFiles, FileType.Jump), 471 | new(scriptFiles, FileType.Script), 472 | new(dialogFiles, FileType.Dialog), 473 | new(terrainFiles, FileType.Terrain), 474 | new(mapPropertiesFiles, FileType.MapProperties), 475 | new(soundWavFiles, FileType.SoundWav), 476 | new(soundMp3Files, FileType.SoundMp3), 477 | new(videoFiles, FileType.Video), 478 | new(bitmapFiles, FileType.Bitmap), 479 | new(facFiles, FileType.FacadeWalk), 480 | }; 481 | } 482 | } -------------------------------------------------------------------------------- /ArcNET.Terminal/Program.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.Utilities; 2 | using Spectre.Console; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using Utils.Console; 7 | using Utils.Device; 8 | using Utils.Process; 9 | 10 | namespace ArcNET.Terminal 11 | { 12 | internal static class Program 13 | { 14 | private static string GetHighResDir() 15 | { 16 | string pathToArcDir = AnsiConsole.Ask("[green]Insert path to Arcanum dir[/]:"); 17 | string pathToHighResDir = Path.Combine(pathToArcDir + "\\HighRes"); 18 | 19 | while (!Directory.Exists(pathToHighResDir)) 20 | { 21 | ConsoleExtensions.Log("HighRes dir not found!", "error"); 22 | pathToArcDir = AnsiConsole.Ask("[green]Insert path to Arcanum dir again[/]:"); 23 | pathToHighResDir = Path.Combine(pathToArcDir + "\\HighRes"); 24 | } 25 | 26 | return pathToHighResDir; 27 | } 28 | 29 | private static void LaunchWeidu(string launchArgs, string pathToDir = "") 30 | { 31 | string dirPath = string.IsNullOrEmpty(pathToDir) ? GetHighResDir() : pathToDir; 32 | string procPath = Path.Combine(dirPath + "\\weidu.exe"); 33 | if (!File.Exists(procPath)) 34 | throw new InvalidOperationException($"weidu.exe file not found at path: {procPath}"); 35 | 36 | Launcher launcher; 37 | switch (launchArgs) 38 | { 39 | case CmdArgs.Weidu.InstallHighResPatch: 40 | launcher = new Launcher(procPath, CmdArgs.Weidu.InstallHighResPatch); 41 | launcher.Launch(); 42 | break; 43 | case CmdArgs.Weidu.UninstallHighResPatch: 44 | launcher = new Launcher(procPath, CmdArgs.Weidu.UninstallHighResPatch); 45 | launcher.Launch(); 46 | break; 47 | 48 | default: 49 | throw new InvalidOperationException($"Unknown launch arguments: {launchArgs}"); 50 | } 51 | } 52 | 53 | private static void Main() 54 | { 55 | Terminal.RenderLogo(); 56 | 57 | string choice = Terminal.GetMainMenuChoice(); 58 | ConsoleExtensions.Log($"Selected choice: {choice}", "info"); 59 | switch (choice) 60 | { 61 | case "Extract game data": 62 | ConsoleExtensions.Log($"Choice: {choice} is currently unsupported!", "error"); 63 | break; 64 | case "Parse extracted game data": 65 | Parser.ParseExtractedData(); 66 | break; 67 | case "Install High-Res patch": 68 | string pathToHighResDir = GetHighResDir(); 69 | var files = Directory.EnumerateFiles( 70 | pathToHighResDir, "*.*", SearchOption.AllDirectories).ToList(); 71 | 72 | if (files.Count == 0) 73 | { 74 | ConsoleExtensions.Log("HighResFolder empty proceeding to clone latest version", "info"); 75 | GitHub.CloneHighResPatch(pathToHighResDir); 76 | } 77 | 78 | string configPath = Path.Combine(pathToHighResDir + "\\config.ini"); 79 | if (!File.Exists(configPath)) 80 | throw new InvalidOperationException($"Config file not found at path: {configPath}"); 81 | 82 | ConsoleExtensions.Log("Gathering environment info", "info"); 83 | var envInfo = new EnvironmentInfo(); 84 | envInfo.Print(); 85 | 86 | ConsoleExtensions.Log("Auto-config according to environment info", "info"); 87 | HighResConfig.AutoConfigure(envInfo); 88 | 89 | ConsoleExtensions.Log("Summary of config.ini:", "info"); 90 | AnsiConsole.Write(Terminal.ConfigTable()); 91 | 92 | if (AnsiConsole.Confirm("Would you like to change config?")) 93 | { 94 | AnsiConsole.WriteException(new NotImplementedException()); 95 | return; 96 | } 97 | 98 | HighResConfig.Write(configPath); 99 | LaunchWeidu(CmdArgs.Weidu.InstallHighResPatch, pathToHighResDir); 100 | break; 101 | case "Uninstall High-Res patch": 102 | LaunchWeidu(CmdArgs.Weidu.UninstallHighResPatch); 103 | break; 104 | 105 | default: 106 | ConsoleExtensions.Log($"Choice: {choice} is currently unsupported!", "error"); 107 | break; 108 | } 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /ArcNET.Terminal/Terminal.cs: -------------------------------------------------------------------------------- 1 | using ArcNET.Utilities; 2 | using Spectre.Console; 3 | using Spectre.Console.Rendering; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Reflection; 7 | 8 | namespace ArcNET.Terminal; 9 | 10 | public static class Terminal 11 | { 12 | public static void RenderLogo() 13 | { 14 | AnsiConsole.Write(new FigletText("ArcNET v0.0.1").Color(Color.Green)); 15 | } 16 | 17 | public static IRenderable DirectoryTable(string dirPath, IEnumerable, Parser.FileType>> data) 18 | { 19 | Table table = new Table() 20 | .RoundedBorder() 21 | .AddColumn("Summary for dirPath:") 22 | .AddColumn($"{dirPath}"); 23 | 24 | foreach ((List pathToFiles, Parser.FileType fileType) in data) 25 | table.AddRow($"{Enum.GetName(typeof(Parser.FileType), fileType)}", $"{pathToFiles.Count}"); 26 | 27 | return table; 28 | } 29 | 30 | public static IRenderable ConfigTable() 31 | { 32 | Table table = new Table() 33 | .RoundedBorder() 34 | .AddColumn("Parameter name") 35 | .AddColumn("Parameter value"); 36 | 37 | const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | 38 | BindingFlags.Instance | BindingFlags.Static; 39 | 40 | foreach (FieldInfo field in typeof(HighResConfig).GetFields(bindingFlags)) 41 | table.AddRow($"{field.Name}", $"{field.GetValue(field)}"); 42 | 43 | return table; 44 | } 45 | 46 | //TODO: merge with summary table? 47 | public static IRenderable ReportTable(string dirPath, IEnumerable, Parser.FileType>> data) 48 | { 49 | Table table = new Table() 50 | .RoundedBorder() 51 | .AddColumn("Parsing report for dirPath:") 52 | .AddColumn($"{dirPath}"); 53 | 54 | foreach ((List pathToFiles, Parser.FileType fileType) in data) 55 | table.AddRow($"{Enum.GetName(typeof(Parser.FileType), fileType)} files parsed:", $"{pathToFiles.Count}"); 56 | 57 | return table; 58 | } 59 | 60 | public static string GetMainMenuChoice() 61 | => AnsiConsole.Prompt( 62 | new SelectionPrompt() 63 | .Title("[green]What would you like to do[/]?") 64 | .PageSize(5) 65 | .MoreChoicesText("[grey](Move up and down to reveal more choices)[/]") 66 | .AddChoices("Extract game data", "Parse extracted game data", "Install High-Res patch", 67 | "Uninstall High-Res patch", "Launch Arcanum.exe")); 68 | } -------------------------------------------------------------------------------- /ArcNET.Utilities/ArcNET.Utilities.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ArcNET.Utilities/GitHub.cs: -------------------------------------------------------------------------------- 1 | using LibGit2Sharp; 2 | using System.IO; 3 | using System.Linq; 4 | using Utils.Console; 5 | using Utils.FileSystem; 6 | 7 | namespace ArcNET.Utilities; 8 | 9 | public static class GitHub 10 | { 11 | public static void CloneHighResPatch(string resultLocation) 12 | { 13 | const string repo = "https://github.com/ArcNET-Modding/HighResPatch.git"; 14 | ConsoleExtensions.Log($"Cloning repo: {repo} into: \n {resultLocation}", "info"); 15 | Repository.Clone(repo, resultLocation); 16 | var files = Directory.EnumerateFiles(resultLocation, "*.*", SearchOption.AllDirectories).ToList(); 17 | if (files.Count == 0) return; 18 | 19 | ConsoleExtensions.Log($"Cloned {files.Count} files", "success"); 20 | string highResPath = Path.Combine(resultLocation, "HighRes"); 21 | new DirectoryInfo(highResPath).CopyTo(resultLocation); 22 | 23 | var foldersToDelete = new DirectoryInfo(highResPath); 24 | ConsoleExtensions.Log($"Removing empty folder: {foldersToDelete}", "info"); 25 | foldersToDelete.ExistsDelete(); 26 | } 27 | } -------------------------------------------------------------------------------- /ArcNET.Utilities/HighResConfig.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using Utils.Device; 8 | using Utils.Device.Display; 9 | 10 | // ReSharper disable InconsistentNaming 11 | // ReSharper disable NotAccessedField.Local 12 | // ReSharper disable UnusedMember.Local 13 | 14 | namespace ArcNET.Utilities; 15 | 16 | public static class HighResConfig 17 | { 18 | //Todo: config.ini is not standardized, create sections? 19 | //Arcanum High Resolution Patch Settings 20 | //Basic: 21 | private static int Width; // original: 800 22 | private static int Height; // original: 600 23 | private static int BitDepth; // original: 16 24 | private static int DialogFont; // 0 = size 12, 1 = size 14, 2 = size 18 25 | private static int LogbookFont; // 0 = size 12, 1 = size 14 26 | private static int MenuPosition; // 0 = top, 1 = center, 2 = bottom 27 | private static int MainMenuArt; // 0 = black, 1 = fade to black, 2 = wood 28 | private static int Borders; // 1 = add borders to most UI graphics 29 | private static int Language; // 0 = English, 1 = German, 2 = French, 3 = Russian 30 | 31 | //Graphics: 32 | private static int Windowed; // 0 = fullscreen mode, 1 = windowed mode 33 | private static int Renderer; // 0 = software, 1 = hardware 34 | private static int DoubleBuffer; // 0 = disabled, 1 = enabled (unless windowed) 35 | private static int DDrawWrapper; // 1 = install DDrawCompat wrapper 36 | private static int DxWrapper; // 1 = install DxWrapper's DDrawCompat 37 | private static int ShowFPS; // 0 = no change, 1 = always enabled 38 | 39 | //Advanced: 40 | private static int ScrollFPS; // original: 35, max: 255 41 | private static int ScrollDist; // original: 10, infinite: 0 42 | private static int PreloadLimit; // original: 30 tiles, max: 255 43 | private static int BroadcastLimit; // original: 10 tiles, max 255 44 | private static int Logos; // 0 = skip Sierra/Troika logos 45 | private static int Intro; // 0 = skip the main menu intro clip 46 | 47 | private enum Lang 48 | { 49 | En = 0, 50 | De = 1, 51 | Fr = 2, 52 | Ru = 3, 53 | } 54 | 55 | public static void Init(string iniPath) 56 | { 57 | foreach ((string key, string value) in ParseIni(iniPath)) 58 | switch (key) 59 | { 60 | case "Width": 61 | Width = int.Parse(value); 62 | break; 63 | case "Height": 64 | Height = int.Parse(value); 65 | break; 66 | case "BitDepth": 67 | BitDepth = int.Parse(value); 68 | break; 69 | case "DialogFont": 70 | DialogFont = int.Parse(value); 71 | break; 72 | case "LogbookFont": 73 | LogbookFont = int.Parse(value); 74 | break; 75 | case "MenuPosition": 76 | MenuPosition = int.Parse(value); 77 | break; 78 | case "MainMenuArt": 79 | MainMenuArt = int.Parse(value); 80 | break; 81 | case "Borders": 82 | Borders = int.Parse(value); 83 | break; 84 | case "Language": 85 | Language = int.Parse(value); 86 | break; 87 | case "Windowed": 88 | Windowed = int.Parse(value); 89 | break; 90 | case "Renderer": 91 | Renderer = int.Parse(value); 92 | break; 93 | case "DoubleBuffer": 94 | DoubleBuffer = int.Parse(value); 95 | break; 96 | case "DDrawWrapper": 97 | DDrawWrapper = int.Parse(value); 98 | break; 99 | case "DxWrapper": 100 | DxWrapper = int.Parse(value); 101 | break; 102 | case "ShowFPS": 103 | ShowFPS = int.Parse(value); 104 | break; 105 | case "ScrollFPS": 106 | ScrollFPS = int.Parse(value); 107 | break; 108 | case "ScrollDist": 109 | ScrollDist = int.Parse(value); 110 | break; 111 | case "PreloadLimit": 112 | PreloadLimit = int.Parse(value); 113 | break; 114 | case "BroadcastLimit": 115 | BroadcastLimit = int.Parse(value); 116 | break; 117 | case "Logos": 118 | Logos = int.Parse(value); 119 | break; 120 | case "Intro": 121 | Intro = int.Parse(value); 122 | break; 123 | 124 | default: 125 | throw new InvalidOperationException($"Unknown parameter: {key}"); 126 | } 127 | } 128 | 129 | public static void AutoConfigure(EnvironmentInfo envInfo) 130 | { 131 | DisplaySettings displaySettings = envInfo.DisplaySettings; 132 | OperatingSystem osInfo = envInfo.OperatingSystem; 133 | 134 | #region Basics 135 | 136 | Width = (int)displaySettings.DevMode.dmPelsWidth; 137 | Height = (int)displaySettings.DevMode.dmPelsHeight; 138 | BitDepth = (int)displaySettings.DevMode.dmBitsPerPel; 139 | 140 | switch (Width) 141 | { 142 | //2K at aspectRatio 16:9 143 | case >= 2560 when Height >= 1440: 144 | DialogFont = 1; 145 | LogbookFont = 1; 146 | break; 147 | //4k at aspectRatio 16:9 148 | case >= 3840 when Height >= 2160: 149 | DialogFont = 2; 150 | LogbookFont = 1; 151 | break; 152 | } 153 | 154 | MenuPosition = 1; 155 | MainMenuArt = 1; 156 | Borders = 1; 157 | Language = (int)Lang.En; 158 | 159 | #endregion Basics 160 | 161 | #region Graphics 162 | 163 | Windowed = 0; 164 | Renderer = 0; 165 | DoubleBuffer = 0; 166 | DDrawWrapper = 0; 167 | 168 | //Check for windows 7 to 10, use DxWrapper 169 | if (osInfo.Version.Major is >= 7 and <= 10 || BitDepth == 16) 170 | DxWrapper = 1; 171 | else 172 | DxWrapper = 0; 173 | 174 | ShowFPS = 1; 175 | 176 | #endregion Graphics 177 | 178 | #region Advanced 179 | 180 | ScrollFPS = 60; 181 | ScrollDist = 30; 182 | PreloadLimit = 60; 183 | BroadcastLimit = 20; 184 | Logos = 0; 185 | Intro = 0; 186 | 187 | #endregion Advanced 188 | } 189 | 190 | private static IEnumerable> ParseIni(string iniPath) 191 | { 192 | if (!File.Exists(iniPath)) 193 | throw new FileNotFoundException("Unable to find " + iniPath); 194 | 195 | TextReader iniFile = null; 196 | var kvpList = new List>(); 197 | 198 | try 199 | { 200 | iniFile = new StreamReader(iniPath); 201 | string strLine = iniFile.ReadLine(); 202 | 203 | while (strLine != null) 204 | { 205 | if (strLine != "" && strLine[..2] != "//") 206 | { 207 | string[] keyValuePair = strLine.Split(new[] { '=' }, 2); 208 | string key = keyValuePair[0]; 209 | string value = keyValuePair[1]; 210 | 211 | if (value.Contains("//")) 212 | value = value.Split(new[] { '/' }, 2).First(); 213 | 214 | var nameValuePair = new KeyValuePair(key.Trim(' '), value.Trim(' ')); 215 | kvpList.Add(nameValuePair); 216 | } 217 | 218 | strLine = iniFile.ReadLine(); 219 | } 220 | 221 | return kvpList; 222 | } 223 | catch (Exception ex) 224 | { 225 | AnsiConsole.WriteException(ex); 226 | throw; 227 | } 228 | finally 229 | { 230 | iniFile?.Close(); 231 | } 232 | } 233 | 234 | public static void Write(string iniPath) 235 | { 236 | if (!File.Exists(iniPath)) 237 | throw new FileNotFoundException("Unable to find " + iniPath); 238 | 239 | TextWriter writer = null; 240 | Type structType = typeof(HighResConfig); 241 | FieldInfo[] fields = structType.GetFields(BindingFlags.NonPublic | BindingFlags.Static); 242 | 243 | try 244 | { 245 | writer = new StreamWriter(iniPath, false); 246 | writer.WriteLine("//Arcanum High Resolution Patch Settings"); 247 | writer.WriteLine(" "); 248 | writer.WriteLine("//Basic:"); 249 | for (var i = 0; i <= 8; i++) 250 | writer.WriteLine($"{fields[i].Name} = {fields[i].GetValue(fields[i])}"); 251 | 252 | writer.WriteLine(" "); 253 | writer.WriteLine("//Graphics:"); 254 | for (var i = 9; i <= 14; i++) 255 | writer.WriteLine($"{fields[i].Name} = {fields[i].GetValue(fields[i])}"); 256 | 257 | writer.WriteLine(" "); 258 | writer.WriteLine("//Advanced:"); 259 | for (var i = 15; i <= 20; i++) 260 | writer.WriteLine($"{fields[i].Name} = {fields[i].GetValue(fields[i])}"); 261 | } 262 | catch (Exception ex) 263 | { 264 | AnsiConsole.WriteException(ex); 265 | throw; 266 | } 267 | finally 268 | { 269 | writer?.Close(); 270 | } 271 | } 272 | } -------------------------------------------------------------------------------- /ArcNET.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31112.23 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcNET.Terminal", "ArcNET.Terminal\ArcNET.Terminal.csproj", "{E22CF211-1FFA-47C4-A85B-71D7C4D6C420}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcNET.DataTypes", "ArcNET.DataTypes\ArcNET.DataTypes.csproj", "{64BAB215-374D-4D69-8ADA-816B52F600B8}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcNET.Utilities", "ArcNET.Utilities\ArcNET.Utilities.csproj", "{8F91C29F-8D76-40A3-B1F4-AD82BEF224C5}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {E22CF211-1FFA-47C4-A85B-71D7C4D6C420}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {E22CF211-1FFA-47C4-A85B-71D7C4D6C420}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {E22CF211-1FFA-47C4-A85B-71D7C4D6C420}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {E22CF211-1FFA-47C4-A85B-71D7C4D6C420}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {64BAB215-374D-4D69-8ADA-816B52F600B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {64BAB215-374D-4D69-8ADA-816B52F600B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {64BAB215-374D-4D69-8ADA-816B52F600B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {64BAB215-374D-4D69-8ADA-816B52F600B8}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {8F91C29F-8D76-40A3-B1F4-AD82BEF224C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {8F91C29F-8D76-40A3-B1F4-AD82BEF224C5}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {8F91C29F-8D76-40A3-B1F4-AD82BEF224C5}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {8F91C29F-8D76-40A3-B1F4-AD82BEF224C5}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {7F2B817C-C75D-46FD-8BC8-3E3CF96023E4} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /ArcNET.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚧 ArcNET (WIP) 🚧 2 | Is aimed to be a .NET 5 toolkit for modding/patching/fixing a great oldskool game 3 | [Arcanum: Of Steamworks and Magick Obscura](https://en.wikipedia.org/wiki/Arcanum:_Of_Steamworks_and_Magick_Obscura) 4 | 5 | ## ☑️ Roadmap ☑️ 6 | - [ ] Extract/Pack game data archive 7 | - [ ] DAT 8 | - [ ] Parse/Write game data 9 | - [x] FacWalk 10 | - [x] MES 11 | - [ ] ART 12 | - [ ] PRO 13 | - [ ] MOB 14 | - [ ] TDF 15 | - [ ] SEC 16 | - [ ] PRP 17 | - [ ] JMP 18 | - [ ] SCR 19 | - [ ] High-Res Patch 20 | - [x] Install via Weidu 21 | - [ ] Install via ArcNET 22 | - [x] Uninstall via Weidu 23 | - [ ] Uninstall via ArcNET 24 | - [ ] Arcanum.exe 25 | - [ ] Launch 26 | - [ ] Inject 27 | - [ ] Attach 28 | - [ ] Terminate 29 | 30 | ## 🐞 Bugs and Issues 🐞 31 | Feel free to open any issue which is related to Arcanum. 32 | * Make sure your using latest Unofficial Arcanum Patch(2.0.2) with [High-Res Patch(1.5.1)](https://github.com/ArcNET-Modding/HighResPatch) installed. 33 | * Make sure you have sufficiently described the problem and provided enough information for its reproduction. 34 | 35 | ## ⭐️ Acknowledgements ⭐️ 36 | * [ArcanumFileFormats](https://github.com/iamkisly/ArcanumFileFormats) - [iamkisly](https://github.com/iamkisly) did a great start, most of this project is based on it 37 | * [Unofficial Arcanum Patch 2.0.2](https://terra-arcanum.com/drog/uap.html) - a must have patch 38 | * [Terra Arcanum](https://terra-arcanum.com/) - usefull info 39 | * [Arcanum Wiki](https://arcanum.fandom.com//) - more info 40 | * [Spectre.Console](https://github.com/spectresystems/spectre.console) - great package, which functions as backbone of our terminal 41 | --------------------------------------------------------------------------------