├── .gitattributes
├── .github
└── workflows
│ └── github-releases-to-discord.yml
├── .gitignore
├── LICENSE
├── README.md
├── Siege_installation_instructions.md
└── SingleplayerLauncher
├── .editorconfig
├── Api
├── GameController.cs
├── JoinGameRequestDto.cs
└── LaunchGameRequestDto.cs
├── Configuration
├── BaseLoadouts.cs
├── GameConfig.cs
├── IConfiguration.cs
├── Settings.cs
├── SiegeLoadouts.cs
└── SurvivalLoadouts.cs
├── Entities
├── Battlegrounds
│ ├── Endless.cs
│ ├── IBattleground.cs
│ ├── Prologue.cs
│ ├── Siege.cs
│ ├── Survival.cs
│ └── WeeklyChallenge.cs
├── BotDifficulty.cs
├── BotLoadout.cs
├── Difficulty.cs
├── Dye.cs
├── GameMode.cs
├── Hero.cs
├── Loadout
│ ├── Archetype.cs
│ ├── Consumable.cs
│ ├── Gear.cs
│ ├── Glyph.cs
│ ├── Guardian.cs
│ ├── SiegeRole.cs
│ ├── SiegeTrait.cs
│ ├── SurvivalTrait.cs
│ ├── Trait.cs
│ ├── Trap.cs
│ ├── TrapPart.cs
│ └── Wave.cs
├── Map.cs
├── Names
│ ├── BotDifficulty.cs
│ ├── Consumable.cs
│ ├── Difficulty.cs
│ ├── Dye.cs
│ ├── GameMode.cs
│ ├── Gear.cs
│ ├── Glyph.cs
│ ├── Guardian.cs
│ ├── Hero.cs
│ ├── Map.cs
│ ├── Mod.cs
│ ├── SiegeRole.cs
│ ├── Skin.cs
│ ├── Trait.cs
│ ├── Trap.cs
│ ├── TrapPart.cs
│ ├── TrapPartSlot.cs
│ ├── Wave.cs
│ └── WeeklyChallenge.cs
└── Skin.cs
├── Forms
├── ComboBoxHelper.cs
├── FolderPicker.cs
├── InputValidator.cs
├── LauncherMainForm.Designer.cs
├── LauncherMainForm.cs
└── LauncherMainForm.resx
├── GameFiles
├── Config
│ ├── CharacterData.cs
│ ├── ConfigFile.cs
│ └── DefaultGame.cs
├── Cooked
│ ├── PawnWeaponUPK.cs
│ ├── SpitfireGameUPK.cs
│ ├── UPKFile.cs
│ └── UPKHeroObjectContent.cs
└── FileUtils.cs
├── GameLauncher.cs
├── Model
├── BaseLoadout.cs
├── Battleground.cs
├── GameInfo.cs
├── Language.cs
├── LoadoutItem.cs
├── SiegeLoadout.cs
├── SlotItem.cs
└── SurvivalLoadout.cs
├── Mods
├── AccountLevelOverride.cs
├── AdditionalheroWeapon.cs
├── EnhancedTrapRotation.cs
├── GodMode.cs
├── Hardcore.cs
├── InvincibleBarricades.cs
├── Mod.cs
├── Mods.cs
├── NoLimitUniqueTraps.cs
├── NoTrapCap.cs
├── NoTrapGrid.cs
├── SellTrapsAnytime.cs
├── ShowTrapDamageFlyoffs.cs
├── StartingCoinOverride.cs
├── TrapTierOverride.cs
├── TrapsAnySurface.cs
└── TrapsInTraps.cs
├── Program.cs
├── ProjectRechained.sln
├── ProjectRechainedLauncher.csproj
├── Properties
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── Utils
├── Base62Converter.cs
├── Comment.cs
├── Entry.cs
├── IniElement.cs
├── IniFile.cs
└── Section.cs
├── app.config
└── omdu-U1_512.ico
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/github-releases-to-discord.yml:
--------------------------------------------------------------------------------
1 | on:
2 | release:
3 | types: [published]
4 |
5 | jobs:
6 | github-releases-to-discord:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v4
11 | - name: Github Releases To Discord
12 | uses: SethCohen/github-releases-to-discord@v1.15.0
13 | with:
14 | webhook_url: ${{ secrets.WEBHOOK_URL }}
15 | color: "2105893"
16 | username: "Release Changelog"
17 | avatar_url: "https://cdn.discordapp.com/avatars/487431320314576937/bd64361e4ba6313d561d54e78c9e7171.png"
18 | content: "||<@&874767553271038012>||"
19 |
--------------------------------------------------------------------------------
/.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 |
33 | # Visual Studio 2015/2017 cache/options directory
34 | .vs/
35 | # Uncomment if you have tasks that create the project's static files in wwwroot
36 | #wwwroot/
37 |
38 | # Visual Studio 2017 auto generated files
39 | Generated\ Files/
40 |
41 | # MSTest test Results
42 | [Tt]est[Rr]esult*/
43 | [Bb]uild[Ll]og.*
44 |
45 | # NUnit
46 | *.VisualState.xml
47 | TestResult.xml
48 | nunit-*.xml
49 |
50 | # Build Results of an ATL Project
51 | [Dd]ebugPS/
52 | [Rr]eleasePS/
53 | dlldata.c
54 |
55 | # Benchmark Results
56 | BenchmarkDotNet.Artifacts/
57 |
58 | # .NET Core
59 | project.lock.json
60 | project.fragment.lock.json
61 | artifacts/
62 |
63 | # StyleCop
64 | StyleCopReport.xml
65 |
66 | # Files built by Visual Studio
67 | *_i.c
68 | *_p.c
69 | *_h.h
70 | *.ilk
71 | *.meta
72 | *.obj
73 | *.iobj
74 | *.pch
75 | *.pdb
76 | *.ipdb
77 | *.pgc
78 | *.pgd
79 | *.rsp
80 | *.sbr
81 | *.tlb
82 | *.tli
83 | *.tlh
84 | *.tmp
85 | *.tmp_proj
86 | *_wpftmp.csproj
87 | *.log
88 | *.vspscc
89 | *.vssscc
90 | .builds
91 | *.pidb
92 | *.svclog
93 | *.scc
94 |
95 | # Chutzpah Test files
96 | _Chutzpah*
97 |
98 | # Visual C++ cache files
99 | ipch/
100 | *.aps
101 | *.ncb
102 | *.opendb
103 | *.opensdf
104 | *.sdf
105 | *.cachefile
106 | *.VC.db
107 | *.VC.VC.opendb
108 |
109 | # Visual Studio profiler
110 | *.psess
111 | *.vsp
112 | *.vspx
113 | *.sap
114 |
115 | # Visual Studio Trace Files
116 | *.e2e
117 |
118 | # TFS 2012 Local Workspace
119 | $tf/
120 |
121 | # Guidance Automation Toolkit
122 | *.gpState
123 |
124 | # ReSharper is a .NET coding add-in
125 | _ReSharper*/
126 | *.[Rr]e[Ss]harper
127 | *.DotSettings.user
128 |
129 | # JustCode is a .NET coding add-in
130 | .JustCode
131 |
132 | # TeamCity is a build add-in
133 | _TeamCity*
134 |
135 | # DotCover is a Code Coverage Tool
136 | *.dotCover
137 |
138 | # AxoCover is a Code Coverage Tool
139 | .axoCover/*
140 | !.axoCover/settings.json
141 |
142 | # Visual Studio code coverage results
143 | *.coverage
144 | *.coveragexml
145 |
146 | # NCrunch
147 | _NCrunch_*
148 | .*crunch*.local.xml
149 | nCrunchTemp_*
150 |
151 | # MightyMoose
152 | *.mm.*
153 | AutoTest.Net/
154 |
155 | # Web workbench (sass)
156 | .sass-cache/
157 |
158 | # Installshield output folder
159 | [Ee]xpress/
160 |
161 | # DocProject is a documentation generator add-in
162 | DocProject/buildhelp/
163 | DocProject/Help/*.HxT
164 | DocProject/Help/*.HxC
165 | DocProject/Help/*.hhc
166 | DocProject/Help/*.hhk
167 | DocProject/Help/*.hhp
168 | DocProject/Help/Html2
169 | DocProject/Help/html
170 |
171 | # Click-Once directory
172 | publish/
173 |
174 | # Publish Web Output
175 | *.[Pp]ublish.xml
176 | *.azurePubxml
177 | # Note: Comment the next line if you want to checkin your web deploy settings,
178 | # but database connection strings (with potential passwords) will be unencrypted
179 | *.pubxml
180 | *.publishproj
181 |
182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
183 | # checkin your Azure Web App publish settings, but sensitive information contained
184 | # in these scripts will be unencrypted
185 | PublishScripts/
186 |
187 | # NuGet Packages
188 | *.nupkg
189 | # NuGet Symbol Packages
190 | *.snupkg
191 | # The packages folder can be ignored because of Package Restore
192 | **/[Pp]ackages/*
193 | # except build/, which is used as an MSBuild target.
194 | !**/[Pp]ackages/build/
195 | # Uncomment if necessary however generally it will be regenerated when needed
196 | #!**/[Pp]ackages/repositories.config
197 | # NuGet v3's project.json files produces more ignorable files
198 | *.nuget.props
199 | *.nuget.targets
200 |
201 | # Microsoft Azure Build Output
202 | csx/
203 | *.build.csdef
204 |
205 | # Microsoft Azure Emulator
206 | ecf/
207 | rcf/
208 |
209 | # Windows Store app package directories and files
210 | AppPackages/
211 | BundleArtifacts/
212 | Package.StoreAssociation.xml
213 | _pkginfo.txt
214 | *.appx
215 | *.appxbundle
216 | *.appxupload
217 |
218 | # Visual Studio cache files
219 | # files ending in .cache can be ignored
220 | *.[Cc]ache
221 | # but keep track of directories ending in .cache
222 | !?*.[Cc]ache/
223 |
224 | # Others
225 | ClientBin/
226 | ~$*
227 | *~
228 | *.dbmdl
229 | *.dbproj.schemaview
230 | *.jfm
231 | *.pfx
232 | *.publishsettings
233 | orleans.codegen.cs
234 |
235 | # Including strong name files can present a security risk
236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
237 | #*.snk
238 |
239 | # Since there are multiple workflows, uncomment next line to ignore bower_components
240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
241 | #bower_components/
242 |
243 | # RIA/Silverlight projects
244 | Generated_Code/
245 |
246 | # Backup & report files from converting an old project file
247 | # to a newer Visual Studio version. Backup files are not needed,
248 | # because we have git ;-)
249 | _UpgradeReport_Files/
250 | Backup*/
251 | UpgradeLog*.XML
252 | UpgradeLog*.htm
253 | ServiceFabricBackup/
254 | *.rptproj.bak
255 |
256 | # SQL Server files
257 | *.mdf
258 | *.ldf
259 | *.ndf
260 |
261 | # Business Intelligence projects
262 | *.rdl.data
263 | *.bim.layout
264 | *.bim_*.settings
265 | *.rptproj.rsuser
266 | *- [Bb]ackup.rdl
267 | *- [Bb]ackup ([0-9]).rdl
268 | *- [Bb]ackup ([0-9][0-9]).rdl
269 |
270 | # Microsoft Fakes
271 | FakesAssemblies/
272 |
273 | # GhostDoc plugin setting file
274 | *.GhostDoc.xml
275 |
276 | # Node.js Tools for Visual Studio
277 | .ntvs_analysis.dat
278 | node_modules/
279 |
280 | # Visual Studio 6 build log
281 | *.plg
282 |
283 | # Visual Studio 6 workspace options file
284 | *.opt
285 |
286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
287 | *.vbw
288 |
289 | # Visual Studio LightSwitch build output
290 | **/*.HTMLClient/GeneratedArtifacts
291 | **/*.DesktopClient/GeneratedArtifacts
292 | **/*.DesktopClient/ModelManifest.xml
293 | **/*.Server/GeneratedArtifacts
294 | **/*.Server/ModelManifest.xml
295 | _Pvt_Extensions
296 |
297 | # Paket dependency manager
298 | .paket/paket.exe
299 | paket-files/
300 |
301 | # FAKE - F# Make
302 | .fake/
303 |
304 | # CodeRush personal settings
305 | .cr/personal
306 |
307 | # Python Tools for Visual Studio (PTVS)
308 | __pycache__/
309 | *.pyc
310 |
311 | # Cake - Uncomment if you are using it
312 | # tools/**
313 | # !tools/packages.config
314 |
315 | # Tabs Studio
316 | *.tss
317 |
318 | # Telerik's JustMock configuration file
319 | *.jmconfig
320 |
321 | # BizTalk build output
322 | *.btp.cs
323 | *.btm.cs
324 | *.odx.cs
325 | *.xsd.cs
326 |
327 | # OpenCover UI analysis results
328 | OpenCover/
329 |
330 | # Azure Stream Analytics local run output
331 | ASALocalRun/
332 |
333 | # MSBuild Binary and Structured Log
334 | *.binlog
335 |
336 | # NVidia Nsight GPU debugger configuration file
337 | *.nvuser
338 |
339 | # MFractors (Xamarin productivity tool) working folder
340 | .mfractor/
341 |
342 | # Local History for Visual Studio
343 | .localhistory/
344 |
345 | # BeatPulse healthcheck temp database
346 | healthchecksdb
347 |
348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
349 | MigrationBackup/
350 |
351 | /SingleplayerLauncher/Properties/launchSettings.json
352 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Project Rechained
2 |
3 | The aim of this project is to provide the game of **Orcs Must Die! Unchained** a way to be played again. This is achieved by applying modifications to the game.
4 |
5 | **This mod will modify your game files**. But it will also create backups for the original files automatically.
6 |
7 |
8 | If you want to give feedback, contribute, participate in testing or just hang around and talk about OMDU, **feel free to join our Discord server** clicking the label below:
9 |
10 | [](https://discord.gg/xkZskPXtwm)
11 |
12 | ### How to install
13 |
14 | #### How to install for Survival:
15 |
16 | 1. Download and Install .NET 8.0 from: [Microsoft .NET runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0/runtime) [Microsoft .NET SDK](https://dotnet.microsoft.com/en-us/download)
17 | 2. Download Game from: [Steam](https://intradeus.github.io/http-protocol-redirector?r=steam://rungameid/427270) (If link doesn't work, ask for help at the Discord server)
18 | 3. Download the Project Rechained Launcher latest release from: [Latest release .zip](https://github.com/TimeMaster18/Project-Rechained/releases/latest/download/Project_Rechained_Launcher.zip)
19 | 4. Extract the downloaded Project_Rechained_Launcher.zip contents inside the root folder of your game installation. (Same folder that contains "Binaries", "Engine", etc)
20 | 5. Run the "ProjectRechainedLauncher.exe" inside the ProjectRechained folder.
21 | 6. (Optional) Close the Launcher and launch from Steam
22 |
23 | Note: Once installed, it can be launched from Steam by Pressing play on the Orcs Must Die! Unchained Game in your library.
24 |
25 | #### How to install for Siege (Not required for survival, requires a second game installation):
26 | [Siege installation instructions](Siege_installation_instructions.md)
27 |
28 | ### Features:
29 | - Launch the game from **Steam**
30 | - **Peer to peer multiplayer**
31 | - Survival **up to 5 players**, **Survival** or **Endless** modes
32 | - Siege 5 vs 5 players
33 | - Fully customisable loadouts, including Trap Parts
34 | - **Save your loadouts and share them with other players**
35 | - In Survival, Hero and **Trap Tiers** are matched with enemy/map level
36 | - **New Survival extra difficulties** for every map
37 | - **New Survival Mods**:
38 | - Remove Trap Cap
39 | - Invincible barricades
40 | - Custom intial Coin
41 | - God Mode
42 | - Show Trap Damage
43 | - Sell Traps during waves
44 | - and more!
45 | - Settings and mods are saved between executions
46 |
47 | ### Coming Next
48 | - Campaign Mode
49 | - Loadout randomizer
50 | - Fix (some) Coop issues
51 |
52 | ### Known Problems
53 | #### Survival
54 | - Host player has no enemy / guardian / hero voices
55 | - (Coop) Client sometimes needs to rejoin to get all skills
56 | - (Coop) Client traits "stack up" when reconnecting
57 | - Host player hero mobility skills (Gabriella teleport, Mdnight dash, etc) partially work
58 | - (Coop) Some mods create issues for Client
59 | - (Visual only) Host player wave count at top of screen is wrong (press Tab to see actual wave limit)
60 | - (Visual only) Host player Unchained meter (right side of screen) stuck after first activation
61 | - The **indicator over the hero icon at the bottom left of the screen works correctly**
62 | - (Visual only) Crogon Keep textures can be black
63 | - (Visual only) Host player own Midnight "invisibility" not showing properly
64 |
65 | #### Siege
66 | - Host player doesn't have all sounds (mostly hero and minion related)
67 | - Host Hero Upgrades popup doesn't close automatically (requires pressing U) after picking upgrade
68 | - (Visual only) Host player own Midnight "invisibility" not showing properly
69 |
70 | ## Acknowledgements
71 |
72 | This project would not have been possible without the help and support of several people and projects:
73 |
74 | A big thank you to all the people who contributed to this project:
75 |
76 | - **DaTeddy** - Contributed with reverse-engineering, testing, documenting and much more.
77 | - **DanMander** - Contributed with a more visual and extended browser-based UI.
78 |
79 | This project makes use of the following open-source projects:
80 |
81 | - **[Unreal Package Decompressor by gildor](https://github.com/gildor2/UEViewer)** - Used to decompress upk files.
82 | - **[Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)** - Used to handle the settings and save state.
83 | - **[OMDU Hook by trumank](https://github.com/trumank/omdu-hook)** - Unlocks the possibility of multiplayer and further loadout customisation.
84 |
85 | ## Contact
86 |
87 | If you have any questions, suggestions, or feedback, feel free to reach out:
88 |
89 | - **Project Maintainer:** [TimeMaster](https://github.com/TimeMaster18) - Discord: timemaster
90 | - **Discord Server:** [OMD Modding Server](https://discord.gg/xkZskPXtwm)
91 |
--------------------------------------------------------------------------------
/Siege_installation_instructions.md:
--------------------------------------------------------------------------------
1 | ## Step-by-Step Guide to Downloading OMDU Siege version Using DepotDownloader
2 |
3 | ### Prerequisites
4 |
5 | 1. **Steam Account**: Ensure you have a valid Steam account.
6 | 2. **DepotDownloader**: Download the latest version of DepotDownloader from its [GitHub repository](https://github.com/SteamRE/DepotDownloader).
7 | - [Direct download latest version of DepotDownloader](https://github.com/SteamRE/DepotDownloader/releases/latest/download/DepotDownloader-framework.zip)
8 |
9 | ### Steps to Install the Specific OMDU Siege version
10 |
11 | 1. **Download and Install .NET SDK**:
12 | - Go to the [.NET download page](https://dotnet.microsoft.com/download/dotnet).
13 | - Download and install the latest .NET SDK suitable for your operating system.
14 |
15 | 2. **Download DepotDownloader**:
16 | - If not done already, visit the [DepotDownloader releases page](https://github.com/SteamRE/DepotDownloader/releases).
17 | - Download the latest release zip file for your operating system. For Windows, it should look similar to `DepotDownloader-framework.zip`
18 | - Extract the contents of the zip file to a convenient location on your computer.
19 | - Make sure to not be within a sync'd folder such as OneDrive or MEGA.
20 |
21 | 3. **Open Command Prompt or Powershell**:
22 | - On Windows: Press `Win + R`, type `cmd`, and hit Enter.
23 | - Or Hold `Shift + Right click` in an empty space of a folder, then `Open powershell window here`.
24 |
25 | 4. **Navigate to DepotDownloader Folder**:
26 | - Use the `cd` command to navigate to the folder where you extracted DepotDownloader.
27 | - Example: `cd path\to\DepotDownloader`
28 |
29 | 5. **Run DepotDownloader Command**:
30 | - Use the following command to download the specific version:
31 | ```sh
32 | dotnet DepotDownloader.dll -app 427270 -depot 427271 -manifest 808827202674972462 -username YourSteamUsername
33 | ```
34 |
35 | Replace `YourSteamUsername` with your actual Steam credentials. It will ask for your password once entered.
36 |
37 | **Example**:
38 | ```sh
39 | dotnet DepotDownloader.dll -app 427270 -depot 427271 -manifest 808827202674972462 -username yourusername
40 | ```
41 | **Example with specificed directory**:
42 | ```sh
43 | dotnet DepotDownloader.dll -app 427270 -depot 427271 -manifest 808827202674972462 -username yourusername -dir C:\OMDUSiegeVersion
44 | ```
45 |
46 | 6. **Enter Steam Guard Code (if needed)**:
47 | - If Steam Guard is enabled on your account, you may be prompted to enter a code sent to your email or generated by the Steam mobile app.
48 |
49 | 7. **Wait for the Download to Complete**:
50 | - DepotDownloader will now connect to Steam and download the specified version.
51 | - The downloaded files will be saved in a folder named `depots` within the DepotDownloader directory.
52 |
53 | 8. **Verify the Download**:
54 | - Once the download is complete, navigate to the download folder to ensure all files are there. Game root folder will be some folders deeper.
55 |
56 | 9. Now that you have OMDU Siege version downloaded, download the Project Rechained Launcher latest release from: [Latest release .zip](https://github.com/TimeMaster18/Project-Rechained/releases/latest/download/Project_Rechained_Launcher.zip)
57 |
58 | 10. Extract the downloaded Project_Rechained_Launcher.zip contents inside the root folder of your Siege game installation. (Same folder that contains "Binaries", "Engine", etc)
59 |
60 | 11. Run the "ProjectRechainedLauncher.exe" inside the ProjectRechained folder.
61 | - Siege game installation is independent of Survival (for the time being) and can't be launcher from Steam.
62 |
63 | ### Additional Tips
64 |
65 | - **Two-Factor Authentication**: If you have Steam Guard enabled, be ready to enter the code sent to your email or generated by the Steam mobile app.
66 | - **Privacy**: In case of security concerns, feel free to change your Steam password after completing the download.
67 | - If you want to have both versions (Survival and Siege), don't delete or replace any folders/files and keep both installations.
68 | - [Video Tutorial on how to download using DepotDownloader](https://www.youtube.com/watch?v=nTnmCj5v024)
69 |
70 | By following these steps, you should be able to successfully download and install the old version of OMDU using DepotDownloader with the provided App ID, Depot ID, and Manifest ID. If you encounter any issues, refer to the Discord server to seek assistance from the community on the text channels about Project Rechained.
71 |
72 | [](https://discord.gg/xkZskPXtwm)
73 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CA1416: Validate platform compatibility
4 | dotnet_diagnostic.CA1416.severity = silent
5 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Api/JoinGameRequestDto.cs:
--------------------------------------------------------------------------------
1 | namespace ProjectRechained.Api
2 | {
3 | public class JoinGameRequestDto
4 | {
5 | ///
6 | /// Language setting for the game, default is "English".
7 | ///
8 | public string GameLanguage { get; set; } = "English";
9 |
10 | ///
11 | /// Encoded loadout.
12 | /// Represents the selected weapons, traps, guardians, etc for the player.
13 | ///
14 | public string Loadout { get; set; }
15 |
16 | ///
17 | /// Determines whether trap damage numbers should be displayed on screen.
18 | /// Defaults to `false`.
19 | ///
20 | public bool ShowTrapDamage { get; set; } = false;
21 |
22 | ///
23 | /// The IP address of the host in a multiplayer session.
24 | /// Used to connect clients to the host. Example format: "127.0.0.1"
25 | ///
26 | public string HostIP { get; set; }
27 |
28 |
29 | // Add function to pretty print the object
30 | public override string ToString()
31 | {
32 | return $"GameLanguage: {GameLanguage}, Loadout: {Loadout}, ShowTrapDamage: {ShowTrapDamage}, HostIP: {HostIP}";
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Api/LaunchGameRequestDto.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Api.Controllers;
2 | using System.Collections.Generic;
3 |
4 | namespace ProjectRechained.Api
5 | {
6 |
7 | public class LaunchGameRequestDto
8 | {
9 | ///
10 | /// Language setting for the game, default is "English".
11 | ///
12 | public string GameLanguage { get; set; } = "English";
13 |
14 | ///
15 | /// Host or local player's name.
16 | ///
17 | public string PlayerName { get; set; }
18 |
19 | ///
20 | /// "Survival", "Endless", etc.
21 | ///
22 | public string GameMode { get; set; }
23 |
24 | ///
25 | /// For Survival, this would be "Apprentice", "War Mage", "Master", or "Rift lord".
26 | /// For Endless, can be ignored.
27 | ///
28 | public string Difficulty { get; set; }
29 |
30 | ///
31 | /// Additional difficulty settings, if applicable.
32 | /// Examples: "Apprentice+", "Master++", "Rift Lord+3"
33 | ///
34 | public string ExtraDifficulty { get; set; }
35 |
36 | ///
37 | /// Name of the map, e.g. "Cliffside Clash", "The Baths", etc.
38 | ///
39 | public string MapName { get; set; }
40 |
41 | ///
42 | /// Encoded loadouts for each player, or at least the host/local player.
43 | /// Represents selected weapons, traps, guardians, etc.
44 | ///
45 | public List Loadouts { get; set; }
46 |
47 | ///
48 | /// Names of mods to enable. They must match the internal
49 | /// `Mods.ModList` items by name.
50 | /// e.g. [ "No Trap Cap", "God Mode", "Show Trap Damage Flyoffs" ]
51 | ///
52 | public List ActiveMods { get; set; }
53 |
54 | ///
55 | /// Whether to display trap damage numbers on screen.
56 | ///
57 | public bool ShowTrapDamage { get; set; } = false;
58 |
59 | ///
60 | /// The amount of starting in-game currency for the player.
61 | /// Defaults to `GameController.DEFAULT_INVALID_VALUE` if not set.
62 | ///
63 | public int StartingCoin { get; set; } = GameController.DEFAULT_INVALID_VALUE;
64 |
65 | ///
66 | /// Overrides the default trap tier level if specified.
67 | /// Defaults to `GameController.DEFAULT_INVALID_VALUE` if not set.
68 | ///
69 | public int OverrideTrapTier { get; set; } = GameController.DEFAULT_INVALID_VALUE;
70 |
71 | ///
72 | /// Overrides the player's account level if specified.
73 | /// Defaults to `GameController.DEFAULT_INVALID_VALUE` if not set.
74 | ///
75 | public int OverrideAccountLevel { get; set; } = GameController.DEFAULT_INVALID_VALUE;
76 |
77 |
78 | // Add function to pretty print the object
79 | public override string ToString()
80 | {
81 | return $"GameLanguage: {GameLanguage}, PlayerName: {PlayerName}, GameMode: {GameMode}, Difficulty: {Difficulty}, ExtraDifficulty: {ExtraDifficulty}, MapName: {MapName}, Loadouts: {string.Join(", ", Loadouts)}, ActiveMods: {string.Join(", ", ActiveMods)}, ShowTrapDamage: {ShowTrapDamage}, StartingCoin: {StartingCoin}, OverrideTrapTier: {OverrideTrapTier}, OverrideAccountLevel: {OverrideAccountLevel}";
82 | }
83 |
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Configuration/BaseLoadouts.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using SingleplayerLauncher.Configuration;
3 | using SingleplayerLauncher.GameFiles;
4 | using System.Collections.Generic;
5 | using System.IO;
6 |
7 | namespace SingleplayerLauncher
8 | {
9 | public abstract class BaseLoadouts : IConfiguration
10 | {
11 | public abstract string FileName { get; }
12 |
13 | public List LoadoutList { get; set; } = [];
14 |
15 | protected BaseLoadouts() { }
16 |
17 | public void AddLoadout(string name, string code)
18 | {
19 | if (!LoadoutList.Exists(loadout => loadout.Name == name))
20 | {
21 | LoadoutList.Add(new LoadoutDTO { Name = name, Code = code });
22 | }
23 | }
24 |
25 | public void RemoveLoadout(string name)
26 | {
27 | LoadoutList.RemoveAll(loadout => loadout.Name == name);
28 | }
29 |
30 | public LoadoutDTO GetLoadoutByName(string name)
31 | {
32 | return LoadoutList.Find(l => l.Name == name);
33 | }
34 |
35 | public List GetLoadoutNames()
36 | {
37 | return LoadoutList.ConvertAll(l => l.Name);
38 | }
39 |
40 | public bool Exists(string name)
41 | {
42 | return LoadoutList.Exists(l => l.Name == name);
43 | }
44 |
45 | public bool UpdateLoadout(string name, string newCode)
46 | {
47 | var loadout = LoadoutList.Find(l => l.Name == name);
48 | if (loadout != null)
49 | {
50 | loadout.Code = newCode;
51 | return true;
52 | }
53 | return false;
54 | }
55 |
56 | public void Load()
57 | {
58 | string loadoutsFile = Path.Combine(Settings.Instance.LauncherInstallationPath, FileUtils.CONFIG_FOLDER_NAME, FileName);
59 | if (File.Exists(loadoutsFile))
60 | {
61 | var root = JsonConvert.DeserializeObject(File.ReadAllText(loadoutsFile));
62 | LoadoutList = root.Loadouts;
63 | }
64 | else
65 | {
66 | Save();
67 | }
68 | }
69 |
70 | public void Save()
71 | {
72 | string loadoutsFile = Path.Combine(Settings.Instance.LauncherInstallationPath, FileUtils.CONFIG_FOLDER_NAME, FileName);
73 | var root = new LoadoutRootDTO { Loadouts = LoadoutList };
74 | File.WriteAllText(loadoutsFile, JsonConvert.SerializeObject(root, Formatting.Indented));
75 | }
76 | }
77 |
78 | public class LoadoutDTO
79 | {
80 | public string Name { get; set; }
81 | public string Code { get; set; }
82 | }
83 |
84 | public class LoadoutRootDTO
85 | {
86 | public List Loadouts { get; set; } = [];
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Configuration/GameConfig.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using SingleplayerLauncher.Configuration;
3 | using SingleplayerLauncher.GameFiles;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 |
8 | namespace SingleplayerLauncher
9 | {
10 | public class GameConfig : IConfiguration
11 | {
12 | public static readonly string GAME_CONFIG_FILE_NAME = "gameconfig.json";
13 | private static GameConfig _instance;
14 | private static readonly object _lock = new();
15 |
16 | public bool ModsEnabled { get; set; }
17 | public string GameMode { get; set; } = "Survival";
18 | public string Difficulty { get; set; }
19 | public string Battleground { get; set; }
20 | public string SiegeBattleground { get; set; }
21 | public bool SiegeEnemyTeamAsBots { get; set; }
22 | public bool SiegeAllyBots { get; set; }
23 | public string ExtraDifficulty { get; set; }
24 | public string SiegeBotDifficulty { get; set; }
25 | public bool GodMode { get; set; }
26 | public bool ShowTrapDamage { get; set; }
27 | public bool NoTrapCap { get; set; }
28 | public bool InvincibleBarricades { get; set; }
29 | public bool TrapsInTraps { get; set; }
30 | public bool Hardcore { get; set; }
31 | public bool NoLimitUniqueTraps { get; set; }
32 | public bool NoTrapGrid { get; set; }
33 | public bool TrapsAnySurface { get; set; }
34 | public bool EnhancedTrapRotation { get; set; }
35 | public bool SellTrapsAnytime { get; set; }
36 | public bool CustomStartCoinEnabled { get; set; }
37 | public int StartingCoin { get; set; }
38 | public bool OverrideAccountLevel { get; set; }
39 | public int AccountLevel { get; set; } = 1;
40 | public bool OverrideTrapTier { get; set; }
41 | public int TrapTier { get; set; } = 1;
42 | public string AdditionalHeroWeapon { get; set; } = "";
43 | public bool AdditionalHeroWeaponEnabled { get; set; }
44 |
45 | private GameConfig() { }
46 |
47 | public static GameConfig Instance
48 | {
49 | get
50 | {
51 | lock (_lock)
52 | {
53 | if (_instance == null)
54 | {
55 | _instance = new GameConfig();
56 | }
57 | }
58 | return _instance;
59 | }
60 | }
61 |
62 | public void Load()
63 | {
64 | string gameConfigFile = Path.Combine(Settings.Instance.LauncherInstallationPath, FileUtils.CONFIG_FOLDER_NAME, GAME_CONFIG_FILE_NAME);
65 | if (File.Exists(gameConfigFile))
66 | {
67 | var settings = JsonConvert.DeserializeObject>(File.ReadAllText(gameConfigFile));
68 | LoadFromDictionary(settings);
69 | }
70 | else
71 | {
72 | Save();
73 | }
74 | }
75 |
76 | private void LoadFromDictionary(Dictionary settings)
77 | {
78 | foreach (var property in GetType().GetProperties())
79 | {
80 | if (settings.ContainsKey(property.Name) && property.CanWrite && property.Name != "Instance")
81 | {
82 | property.SetValue(this, Convert.ChangeType(settings[property.Name], property.PropertyType));
83 | }
84 | }
85 | }
86 |
87 | public void Save()
88 | {
89 | var settings = new Dictionary();
90 |
91 | foreach (var property in GetType().GetProperties())
92 | {
93 | if (property.CanRead && property.Name != "Instance")
94 | {
95 | settings[property.Name] = property.GetValue(this);
96 | }
97 | }
98 |
99 | string gameConfigFile = Path.Combine(Settings.Instance.LauncherInstallationPath, FileUtils.CONFIG_FOLDER_NAME, GAME_CONFIG_FILE_NAME);
100 | File.WriteAllText(gameConfigFile, JsonConvert.SerializeObject(settings, Formatting.Indented));
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Configuration/IConfiguration.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace SingleplayerLauncher.Configuration
3 | {
4 | public interface IConfiguration
5 | {
6 |
7 | void Load();
8 | void Save();
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Configuration/Settings.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using SingleplayerLauncher.Configuration;
3 | using SingleplayerLauncher.GameFiles;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 |
8 | namespace SingleplayerLauncher
9 | {
10 | public class Settings : IConfiguration
11 | {
12 | public static readonly string SETTINGS_FILE_NAME = "settings.json";
13 | private static readonly string SETTINGS_LEGACY_FILE_NAME = "settings.txt";
14 | private static Settings _instance;
15 | private static readonly object _lock = new();
16 |
17 | public bool FirstRun { get; set; } = true;
18 | public bool ShowOldUI { get; set; }
19 | public bool Debug { get; set; }
20 | public bool RunAs32 { get; set; }
21 | public string Language { get; set; } = "English";
22 | public string RootGamePath { get; set; }
23 | public string LauncherInstallationPath { get; set; }
24 | public bool IsSiegeInstallation { get; set; } = false;
25 |
26 | private Settings() { }
27 |
28 | public static Settings Instance
29 | {
30 | get
31 | {
32 | lock (_lock)
33 | {
34 | if (_instance == null)
35 | {
36 | _instance = new Settings();
37 | }
38 | }
39 | return _instance;
40 | }
41 | }
42 |
43 | public void Load()
44 | {
45 | // TODO: Remove in future release
46 | string settingsFileLegacy = Path.Combine(RootGamePath, FileUtils.BINARIES_FOLDER_NAME, SETTINGS_LEGACY_FILE_NAME);
47 | if (File.Exists(settingsFileLegacy))
48 | {
49 | try
50 | {
51 | File.Delete(settingsFileLegacy);
52 | }
53 | finally
54 | {
55 | // Do nothing - ignore legacy file
56 | }
57 | }
58 |
59 | string settingsFile = Path.Combine(Settings.Instance.LauncherInstallationPath, FileUtils.CONFIG_FOLDER_NAME, SETTINGS_FILE_NAME);
60 | if (File.Exists(settingsFile))
61 | {
62 | var settings = JsonConvert.DeserializeObject>(File.ReadAllText(settingsFile));
63 | LoadFromDictionary(settings);
64 | }
65 | else
66 | {
67 | Save();
68 | }
69 | }
70 |
71 | private void LoadFromDictionary(Dictionary settings)
72 | {
73 | foreach (var property in GetType().GetProperties())
74 | {
75 | if (settings.ContainsKey(property.Name) && property.CanWrite && property.Name != "Instance")
76 | {
77 | property.SetValue(this, Convert.ChangeType(settings[property.Name], property.PropertyType));
78 | }
79 | }
80 | }
81 |
82 | public void Save()
83 | {
84 | var settings = new Dictionary();
85 |
86 | foreach (var property in GetType().GetProperties())
87 | {
88 | if (property.CanRead && property.Name != "Instance")
89 | {
90 | settings[property.Name] = property.GetValue(this);
91 | }
92 | }
93 |
94 | string settingsFile = Path.Combine(Settings.Instance.LauncherInstallationPath, FileUtils.CONFIG_FOLDER_NAME, SETTINGS_FILE_NAME);
95 | File.WriteAllText(settingsFile, JsonConvert.SerializeObject(settings, Formatting.Indented));
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Configuration/SiegeLoadouts.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Collections.Generic;
3 |
4 | namespace SingleplayerLauncher
5 | {
6 | public class SiegeLoadouts : BaseLoadouts
7 | {
8 | private static SiegeLoadouts _instance;
9 | private static readonly object _lock = new();
10 |
11 | public override string FileName { get; } = "siege_loadouts.json";
12 |
13 | private SiegeLoadouts() { }
14 |
15 | public static SiegeLoadouts Instance
16 | {
17 | get
18 | {
19 | lock (_lock)
20 | {
21 | if (_instance == null)
22 | {
23 | _instance = new SiegeLoadouts();
24 | _instance.Load();
25 |
26 | if (_instance.LoadoutList.Count == 0)
27 | {
28 | LoadDefaultLoadouts();
29 | }
30 | }
31 | }
32 | return _instance;
33 | }
34 | }
35 |
36 | private static void LoadDefaultLoadouts()
37 | {
38 | _instance.LoadoutList.AddRange(new List
39 | {
40 | new() { Name = "Defender 1", Code = "Defender1-B1p3-1-3P3N1j2M1r1e2R2B2O-N1BJK9AUli-LdA5" },
41 | new() { Name = "Defender 2", Code = "Defender2-E172-1-3P3N1j2M1w1t2Q2B1i-N1BJK9AUli-LdF5" },
42 | new() { Name = "Pillager 1", Code = "Pillager1-20G3-3-3J3N2G292F21251i2O-61BVK9AUli-VbE7" },
43 | new() { Name = "Attacker 1", Code = "Attacker1-90X2-2-3P3N2G292F21251i2O-NT4OdIH8qi-Wh8C" },
44 | new() { Name = "Attacker 2", Code = "Attacker2-A0v2-2-3J3N2G292F21251i2O-Y2O4DabEpf-MjC8" }
45 | });
46 | _instance.Save();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Configuration/SurvivalLoadouts.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Collections.Generic;
3 |
4 | namespace SingleplayerLauncher
5 | {
6 | public class SurvivalLoadouts : BaseLoadouts
7 | {
8 | private static SurvivalLoadouts _instance;
9 | private static readonly object _lock = new();
10 |
11 | public override string FileName { get; } = "loadouts.json";
12 |
13 | private SurvivalLoadouts() { }
14 |
15 | public static SurvivalLoadouts Instance
16 | {
17 | get
18 | {
19 | lock (_lock)
20 | {
21 | if (_instance == null)
22 | {
23 | _instance = new SurvivalLoadouts();
24 | _instance.Load();
25 |
26 | if (_instance.LoadoutList.Count == 0)
27 | {
28 | LoadDefaultLoadouts();
29 | }
30 | }
31 | }
32 | return _instance;
33 | }
34 | }
35 |
36 | private static void LoadDefaultLoadouts()
37 | {
38 | _instance.LoadoutList.AddRange(new List
39 | {
40 | new() { Name = "TimeMaster Recommendation", Code = "TimeMaster-B1s2-3G3R1j2M1v2R1x1w2B-5H-1E-UjAI-0000006DKIDJ2ScMSbISbRSW8Db" }
41 | });
42 | _instance.Save();
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Battlegrounds/Endless.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using static SingleplayerLauncher.Model.Map;
4 |
5 | namespace SingleplayerLauncher.Model
6 | {
7 | class Endless : IBattleground
8 | {
9 | public int Id { get; private set; }
10 | public string Name { get; private set; }
11 |
12 | public Map Map { get; set; }
13 |
14 | public GameMode GameMode { get; set; } = GameMode.Endless;
15 |
16 | public Difficulty Difficulty { get; set; }
17 |
18 | public int StartingCoin { get; set; }
19 |
20 | public TimeSpan ParTime { get; set; }
21 |
22 | public static Endless TheBathsEndless = new()
23 | {
24 | Id = 1,
25 | Name = "The Baths",
26 | Map = TheBaths,
27 | Difficulty = Difficulty.Endless,
28 | };
29 |
30 | public static Endless BanquetHallEndless = new()
31 | {
32 | Id = 2,
33 | Name = "Banquet Hall",
34 | Map = BanquetHall,
35 | Difficulty = Difficulty.Endless,
36 | };
37 |
38 | public static Endless ThroneRoomEndless = new()
39 | {
40 | Id = 3,
41 | Name = "Throne Room",
42 | Map = ThroneRoom,
43 | Difficulty = Difficulty.Endless,
44 | };
45 |
46 | public static Endless HighlandsEndless = new()
47 | {
48 | Id = 4,
49 | Name = "Highlands",
50 | Map = Highlands,
51 | Difficulty = Difficulty.Endless,
52 | };
53 |
54 | public static Endless TheWallEndless = new()
55 | {
56 | Id = 5,
57 | Name = "The Wall",
58 | Map = TheWall,
59 | Difficulty = Difficulty.Endless,
60 | };
61 |
62 | public static Endless ThuricvodVillageEndless = new()
63 | {
64 | Id = 6,
65 | Name = "Thuricvod Village",
66 | Map = ThuricvodVillage,
67 | Difficulty = Difficulty.Endless,
68 | };
69 |
70 | public static Endless CrogonKeepEndless = new()
71 | {
72 | Id = 7,
73 | Name = "Crogon Keep",
74 | Map = CrogonKeep,
75 | Difficulty = Difficulty.Endless,
76 | };
77 |
78 | public static Endless SharkIslandEndless = new()
79 | {
80 | Id = 8,
81 | Name = "Shark Island",
82 | Map = SharkIsland,
83 | Difficulty = Difficulty.Endless,
84 | };
85 |
86 | public static Endless FrostbiteEndless = new()
87 | {
88 | Id = 9,
89 | Name = "Frostbite",
90 | Map = Frostbite,
91 | Difficulty = Difficulty.Endless,
92 | };
93 |
94 | public static Endless AvalancheEndless = new()
95 | {
96 | Id = 10,
97 | Name = "Avalanche",
98 | Map = Avalanche,
99 | Difficulty = Difficulty.Endless,
100 | };
101 |
102 | public static Endless CastleGatesEndless = new()
103 | {
104 | Id = 11,
105 | Name = "Castle Gates",
106 | Map = CastleGates,
107 | Difficulty = Difficulty.Endless,
108 | };
109 |
110 | public static Endless EventideFortressEndless = new()
111 | {
112 | Id = 12,
113 | Name = "Eventide Fortress",
114 | Map = EventideFortress,
115 | Difficulty = Difficulty.Endless,
116 | };
117 |
118 | public static Dictionary EndlessBattlegrounds = new()
119 | {
120 | { TheBaths.Name, TheBathsEndless },
121 | { BanquetHall.Name, BanquetHallEndless },
122 | { ThroneRoom.Name, ThroneRoomEndless },
123 | { Highlands.Name, HighlandsEndless },
124 | { TheWall.Name, TheWallEndless },
125 | { ThuricvodVillage.Name, ThuricvodVillageEndless },
126 | { CrogonKeep.Name, CrogonKeepEndless },
127 | { SharkIsland.Name, SharkIslandEndless },
128 | { Frostbite.Name, FrostbiteEndless },
129 | { Avalanche.Name, AvalancheEndless },
130 | { CastleGates.Name, CastleGatesEndless },
131 | { EventideFortress.Name, EventideFortressEndless },
132 | };
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Battlegrounds/IBattleground.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SingleplayerLauncher.Model
4 | {
5 | public interface IBattleground
6 | {
7 | string Name { get; }
8 | Map Map { get; set; }
9 | GameMode GameMode { get; set; }
10 | Difficulty Difficulty { get; set; }
11 | int StartingCoin { get; set; }
12 | TimeSpan ParTime { get; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Battlegrounds/Prologue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SingleplayerLauncher.Model
4 | {
5 | class Prologue : IBattleground
6 | {
7 | /*
8 | { "Prologue 1 (Grand Foyer)", "NPE_1.umap" },
9 | { "Prologue 2 (Archmage Library)", "NPE_2.umap" },
10 | { "Prologue 3 (Dungeon)", "NPE_3.umap" },
11 | { "Prologue 4 (Canals)", "NPE_4.umap" },
12 | { "Prologue 5 (Riftmaker's Temple)", "NPE_5.umap" },
13 | //{ "SpitfireFrontEndMap", "SpitfireFrontEndMap.umap" },
14 | { "Survival Tutorial", "TutorialSurvival.umap" },
15 | { "Basics Tutorial", "NewbieTutorial.umap" }
16 | */
17 | public string Name { get; private set; }
18 |
19 | public Map Map { get; set; }
20 |
21 | public GameMode GameMode { get; set; }
22 |
23 | public Difficulty Difficulty { get; set; }
24 |
25 | public int StartingCoin { get; set; }
26 |
27 | public TimeSpan ParTime => throw new NotImplementedException();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Battlegrounds/Siege.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using static SingleplayerLauncher.Model.Map;
4 |
5 | namespace SingleplayerLauncher.Model
6 | {
7 | class Siege : IBattleground
8 | {
9 | public string Name { get; private set; }
10 |
11 | public Map Map { get; set; }
12 | public GameMode GameMode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
13 | public Difficulty Difficulty { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
14 | public int StartingCoin { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
15 |
16 | public TimeSpan ParTime => throw new NotImplementedException();
17 |
18 | public static Siege CliffsideClashSiege = new()
19 | {
20 | Name = "Cliffside Clash",
21 | Map = SiegeCliffsideClash
22 | };
23 |
24 | public static Siege UnchainedFortressSiege = new()
25 | {
26 | Name = "Unchained Fortress",
27 | Map = SiegeUnchainedFortress
28 | };
29 |
30 | public static Dictionary SiegeBattlegrounds = new()
31 | {
32 | { CliffsideClash.Name, CliffsideClashSiege },
33 | { UnchainedFortress.Name, UnchainedFortressSiege },
34 | };
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/BotDifficulty.cs:
--------------------------------------------------------------------------------
1 | using static ProjectRechained.Entities.Names.BotDifficulty;
2 | using SingleplayerLauncher.Model;
3 | using System.Collections.Generic;
4 |
5 | namespace ProjectRechained.Entities
6 | {
7 | internal class BotDifficulty
8 | {
9 | public string Name { get; private set; }
10 | public int BotLevel { get; private set; }
11 | public SiegeLoadout[] botLoadouts { get; private set; }
12 | public int TrapTier { get; private set; }
13 |
14 | private BotDifficulty(string name, int botLevel, int trapTier, SiegeLoadout[] loadouts)
15 | {
16 | Name = name;
17 | BotLevel = botLevel;
18 | TrapTier = trapTier;
19 | botLoadouts = loadouts;
20 | }
21 |
22 | public static readonly BotDifficulty Easy = new(
23 | EASY,
24 | 1,
25 | 1,
26 | [
27 | BotLoadout.BaseMaximilian,
28 | BotLoadout.BaseGabriella,
29 | BotLoadout.BaseOziel,
30 | BotLoadout.BaseIvy,
31 | BotLoadout.BaseHogarth
32 | ]
33 | );
34 |
35 | public static readonly BotDifficulty Normal = new(
36 | NORMAL,
37 | 2,
38 | 1,
39 | [
40 | BotLoadout.BaseMaximilian,
41 | BotLoadout.BaseGabriella,
42 | BotLoadout.BaseOziel,
43 | BotLoadout.BaseIvy,
44 | BotLoadout.BaseHogarth
45 | ]
46 | );
47 |
48 | public static readonly BotDifficulty Hard = new(
49 | HARD,
50 | 3,
51 | 1,
52 | [
53 | BotLoadout.BaseMaximilian,
54 | BotLoadout.BaseGabriella,
55 | BotLoadout.BaseOziel,
56 | BotLoadout.BaseIvy,
57 | BotLoadout.BaseHogarth
58 | ]
59 | );
60 |
61 | public static readonly BotDifficulty Nightmare = new(
62 | NIGHTMARE,
63 | 4,
64 | 1,
65 | [
66 | BotLoadout.BaseMaximilian,
67 | BotLoadout.BaseGabriella,
68 | BotLoadout.BaseOziel,
69 | BotLoadout.BaseIvy,
70 | BotLoadout.BaseHogarth
71 | ]
72 | );
73 |
74 | public static readonly Dictionary BotDifficultiesByName = new()
75 | {
76 | { EASY, Easy },
77 | { NORMAL, Normal },
78 | { HARD, Hard },
79 | { NIGHTMARE, Nightmare }
80 | };
81 |
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/BotLoadout.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Model;
2 | using static SingleplayerLauncher.Model.Wave;
3 | using static SingleplayerLauncher.Model.Trap;
4 | using static SingleplayerLauncher.Model.Gear;
5 | using static SingleplayerLauncher.Model.SiegeTrait;
6 |
7 | namespace ProjectRechained.Entities
8 | {
9 | public class BotLoadout
10 | {
11 | // Base Waves, SlotItems, and Traits
12 | private static readonly Wave[] BaseWaves1 =
13 | {
14 | LightSoldiersLvl1Wave, GnollHuntersLvl1Wave,
15 | LightSoldiersLvl2Wave, GnollHuntersLvl2Wave,
16 | GiantsLvl3Wave, PrideHuntersLvl3Wave,
17 | GiantsLvl4Wave, FireMagesLvl4Wave,
18 | BulgodArmoredOgre, BulgodArmoredOgre
19 | };
20 |
21 | private static readonly Wave[] BaseWaves2 =
22 | {
23 | GrizzlyBearsLvl1Wave, CrossbowOrcsLvl1Wave,
24 | GrizzlyBearsLvl2Wave, CrossbowOrcsLvl2Wave,
25 | OgresLvl3Wave, HumanCrossbowmenLvl3Wave,
26 | TrollsLvl4Wave, FireMagesLvl4Wave,
27 | BulgodArmoredOgre, BulgodArmoredOgre
28 | };
29 |
30 | private static readonly SlotItem[] BaseDefenderSlotItems =
31 | {
32 | MendingRoot, MagesClover,
33 | Barricade, ViscousTar, Brimstone, BGHCeilingBallista, CeilingBallista, ArrowWall, null
34 | };
35 |
36 | private static readonly SlotItem[] BaseAttackerSlotItems =
37 | {
38 | MendingRoot, MagesClover, MagesPicnic, FreedomTrinket,
39 | null, null, null, null, null
40 | };
41 |
42 | private static readonly SiegeTrait[] BaseDefenderTraits =
43 | {
44 | DoesntPlayWellWithOthers, StrangeSenseOfHumor, WalksItOff, OneWithTheTraps
45 | };
46 |
47 | private static readonly SiegeTrait[] BaseAttackerTraits =
48 | {
49 | BornLeader, NotScaredOfStatues, PracticesWitchcraft, MotivatesOthers
50 | };
51 |
52 | // Static Bot Loadouts
53 | public static SiegeLoadout BaseMaximilian = new()
54 | {
55 | PlayerName = "Maximilian",
56 | Hero = Hero.Maximilian,
57 | Skin = Skin.CardboardSamurai,
58 | Role = SiegeRole.Defender,
59 | Waves = BaseWaves1,
60 | SlotItems = BaseDefenderSlotItems,
61 | Traits = BaseDefenderTraits
62 | };
63 |
64 | public static SiegeLoadout BaseGabriella = new()
65 | {
66 | PlayerName = "Gabriella",
67 | Hero = Hero.Gabriella,
68 | Skin = Skin.MistressOfIllusion,
69 | Role = SiegeRole.Defender,
70 | Waves = BaseWaves2,
71 | SlotItems = BaseDefenderSlotItems,
72 | Traits = BaseDefenderTraits
73 | };
74 |
75 | public static SiegeLoadout BaseSmolder = new()
76 | {
77 | PlayerName = "Smolder",
78 | Hero = Hero.Smolder,
79 | Skin = Skin.KillAuea,
80 | Role = SiegeRole.Defender,
81 | Waves = BaseWaves2,
82 | SlotItems = BaseDefenderSlotItems,
83 | Traits = BaseDefenderTraits
84 | };
85 |
86 | public static SiegeLoadout BaseZoey = new()
87 | {
88 | PlayerName = "Zoey",
89 | Hero = Hero.Zoey,
90 | Skin = Skin.JurassicJourney,
91 | Role = SiegeRole.Defender,
92 | Waves = BaseWaves1,
93 | SlotItems = BaseDefenderSlotItems,
94 | Traits = BaseDefenderTraits
95 | };
96 |
97 | public static SiegeLoadout BaseHogarth = new()
98 | {
99 | PlayerName = "Hogarth",
100 | Hero = Hero.Hogarth,
101 | Skin = Skin.GodOfPlunder,
102 | Role = SiegeRole.Attacker,
103 | Waves = BaseWaves1,
104 | SlotItems = BaseAttackerSlotItems,
105 | Traits = BaseAttackerTraits
106 | };
107 |
108 | public static SiegeLoadout BaseIvy = new()
109 | {
110 | PlayerName = "Ivy",
111 | Hero = Hero.Ivy,
112 | Skin = Skin.FlowerFriend,
113 | Role = SiegeRole.Attacker,
114 | Waves = BaseWaves2,
115 | SlotItems = BaseAttackerSlotItems,
116 | Traits = BaseAttackerTraits
117 | };
118 |
119 | public static SiegeLoadout BaseOziel = new()
120 | {
121 | PlayerName = "Oziel",
122 | Hero = Hero.Oziel,
123 | Skin = Skin.StringTheory,
124 | Role = SiegeRole.Attacker,
125 | Waves = BaseWaves1,
126 | SlotItems = BaseAttackerSlotItems,
127 | Traits = BaseAttackerTraits
128 | };
129 |
130 | public static SiegeLoadout BaseSirWinston = new()
131 | {
132 | PlayerName = "SirWinston",
133 | Hero = Hero.SirWinston,
134 | Skin = Skin.DarkPaladin,
135 | Role = SiegeRole.Attacker,
136 | Waves = BaseWaves2,
137 | SlotItems = BaseAttackerSlotItems,
138 | Traits = BaseAttackerTraits
139 | };
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Dye.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using static SingleplayerLauncher.Names.Dye;
3 |
4 | namespace SingleplayerLauncher.Model
5 | {
6 | public class Dye : LoadoutItem
7 | {
8 | public int CodeName { get; private set; }
9 |
10 | // private constructor
11 | Dye() { }
12 |
13 | // static members
14 | public static Dye Normal = new()
15 | {
16 | Id = 1,
17 | Name = NORMAL,
18 | CodeName = 0
19 | };
20 | public static Dye Heroic = new()
21 | {
22 | Id = 2,
23 | Name = HEROIC,
24 | CodeName = 1
25 | };
26 | public static Dye Legendary = new()
27 | {
28 | Id = 3,
29 | Name = LEGENDARY,
30 | CodeName = 2
31 | };
32 |
33 | public static Dictionary Dyes = new()
34 | {
35 | { NORMAL, Normal },
36 | { HEROIC, Heroic },
37 | { LEGENDARY, Legendary },
38 | };
39 |
40 | public static Dictionary DyesById = new()
41 | {
42 | { Normal.Id, Normal },
43 | { Heroic.Id, Heroic },
44 | { Legendary.Id, Legendary },
45 | };
46 |
47 | public static Dye GetById(int id)
48 | {
49 | if (DyesById.TryGetValue(id, out var dye))
50 | {
51 | return dye;
52 | }
53 | return new Dye { Id = 0, Name = "Unknown" };
54 | }
55 |
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/GameMode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using static SingleplayerLauncher.Names.GameMode;
3 |
4 | namespace SingleplayerLauncher.Model
5 | {
6 | public class GameMode
7 | {
8 |
9 | public string Name { get; private set; }
10 | public int Id { get; private set; } // integer used in the .ini config
11 |
12 | // private constructor
13 | GameMode() { }
14 |
15 | // static members
16 | public static GameMode Survival = new()
17 | {
18 | Name = SURVIVAL,
19 | Id = 1
20 | };
21 | public static GameMode Endless = new()
22 | {
23 | Name = ENDLESS,
24 | Id = 5
25 | };
26 | public static GameMode WeeklyChallenge = new()
27 | {
28 | Name = WEEKLY_CHALLENGE,
29 | Id = 1
30 | };
31 |
32 | public static Dictionary GameModes = new()
33 | {
34 | { SURVIVAL, Survival },
35 | { ENDLESS, Endless },
36 | // { WEEKLY_CHALLENGE, WeeklyChallenge },
37 | };
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Loadout/Archetype.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Model
2 | {
3 | class Archetype : SlotItem
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Loadout/Glyph.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using static SingleplayerLauncher.Names.Glyph;
3 |
4 | namespace SingleplayerLauncher.Model
5 | {
6 | public class Glyph : SlotItem
7 | {
8 |
9 | // private constructor
10 | Glyph() { }
11 |
12 | // static members
13 | public static Glyph GlyphOfAmplification = new()
14 | {
15 | Id = 300,
16 | Name = GLYPH_OF_AMPLIFICATION,
17 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphAmplification",
18 | SiegeDescription = "Amplifies the abilities of minions with RP values that cross it. Glyphs may only be placed in dedicated glyph spaces."
19 | };
20 | public static Glyph GlyphOfBlood = new()
21 | {
22 | Id = 301,
23 | Name = GLYPH_OF_BLOOD,
24 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphBlood",
25 | SiegeDescription = "Grants a damage boost to minions that cross it. Glyphs may only be placed in dedicated glyph spaces."
26 | };
27 | public static Glyph GlyphOfHealing = new()
28 | {
29 | Id = 302,
30 | Name = GLYPH_OF_HEALING,
31 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphHealing",
32 | SiegeDescription = "Grants health to minions that cross it. Glyphs may only be placed in dedicated glyph spaces."
33 | };
34 | public static Glyph GlyphOfHealth = new()
35 | {
36 | Id = 303,
37 | Name = GLYPH_OF_HEALTH,
38 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphHealth",
39 | SiegeDescription = "Grants minor health regeneration to minions that cross it. Glyphs may only be placed in dedicated glyph spaces."
40 | };
41 | public static Glyph GlyphOfMagicArmor = new()
42 | {
43 | Id = 304,
44 | Name = GLYPH_OF_MAGIC_ARMOR,
45 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphArmorMagic",
46 | SiegeDescription = "Grants magic armor to minions that cross it. Magic armor protects against elemental damage. Glyphs may only be placed in dedicated glyph spaces."
47 | };
48 | public static Glyph GlyphOfPhysicalArmor = new()
49 | {
50 | Id = 305,
51 | Name = GLYPH_OF_PHYSICAL_ARMOR,
52 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphPhysicalArmor",
53 | SiegeDescription = "Increases the physical armor of allied minions who cross it by 20. Glyphs may only be placed in dedicated glyph spaces."
54 | };
55 | public static Glyph GlyphOfSneaking = new()
56 | {
57 | Id = 306,
58 | Name = GLYPH_OF_SNEAKING,
59 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphStealth",
60 | SiegeDescription = "Briefly grants stealth to minions that cross it. Glyphs may only be placed in dedicated glyph spaces."
61 | };
62 | public static Glyph GlyphOfSpeed = new()
63 | {
64 | Id = 307,
65 | Name = GLYPH_OF_SPEED,
66 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphSpeed",
67 | SiegeDescription = "Increases the movement speed of minions that cross it. Glyphs may only be placed in dedicated glyph spaces."
68 | };
69 | public static Glyph MinorGlyphOfHealing = new()
70 | {
71 | Id = 308,
72 | Name = MINOR_GLYPH_OF_HEALING,
73 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphHealingMinor",
74 | SiegeDescription = "Heals minions that cross it. Glyphs may only be placed in dedicated glyph spaces."
75 | };
76 | public static Glyph OrcishFieldPromotionGlyph = new()
77 | {
78 | Id = 309,
79 | Name = ORCISH_FIELD_PROMOTION_GLYPH,
80 | ItemTemplateName = "SpitfireGame.RItemTrapGlyphOrcishPromotion",
81 | SiegeDescription = "Promotes 5 orc minions that cross the glyph. Light orcs are promoted to medium orcs, medium to heavy. Glyph expires after 20 promotions."
82 | };
83 |
84 | public static Dictionary Glyphs = new()
85 | {
86 | { GLYPH_OF_AMPLIFICATION, GlyphOfAmplification },
87 | { GLYPH_OF_BLOOD, GlyphOfBlood },
88 | { GLYPH_OF_HEALING, GlyphOfHealing },
89 | { GLYPH_OF_HEALTH, GlyphOfHealth },
90 | { GLYPH_OF_MAGIC_ARMOR, GlyphOfMagicArmor },
91 | { GLYPH_OF_PHYSICAL_ARMOR, GlyphOfPhysicalArmor },
92 | { GLYPH_OF_SNEAKING, GlyphOfSneaking },
93 | { GLYPH_OF_SPEED, GlyphOfSpeed },
94 | { MINOR_GLYPH_OF_HEALING, MinorGlyphOfHealing },
95 | { ORCISH_FIELD_PROMOTION_GLYPH, OrcishFieldPromotionGlyph }
96 | };
97 |
98 | public static Dictionary GlyphsById = new()
99 | {
100 | { GlyphOfAmplification.Id, GlyphOfAmplification },
101 | { GlyphOfBlood.Id, GlyphOfBlood },
102 | { GlyphOfHealing.Id, GlyphOfHealing },
103 | { GlyphOfHealth.Id, GlyphOfHealth },
104 | { GlyphOfMagicArmor.Id, GlyphOfMagicArmor },
105 | { GlyphOfPhysicalArmor.Id, GlyphOfPhysicalArmor },
106 | { GlyphOfSneaking.Id, GlyphOfSneaking },
107 | { GlyphOfSpeed.Id, GlyphOfSpeed },
108 | { MinorGlyphOfHealing.Id, MinorGlyphOfHealing },
109 | { OrcishFieldPromotionGlyph.Id, OrcishFieldPromotionGlyph }
110 | };
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Loadout/SiegeRole.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using static SingleplayerLauncher.Names.SiegeRole;
3 |
4 | namespace SingleplayerLauncher.Model
5 | {
6 | public class SiegeRole : Trait
7 | {
8 | // private constructor
9 | SiegeRole() { }
10 | public string Effect { get; private set; }
11 |
12 | // static members
13 | public static SiegeRole Defender = new()
14 | {
15 | Id = 1,
16 | Name = DEFENDER,
17 | Effect = "+10% XP from minion kills. Starts match with coin.",
18 | SiegeDescription = "Defends the rift by building traps and killing minions.",
19 | CodeName = "SpitfireGame.RWeaverRoleDefense"
20 | };
21 | public static SiegeRole Attacker = new()
22 | {
23 | Id = 2,
24 | Name = ATTACKER,
25 | Effect = "+8% leadership aura and XP when escorting minions.",
26 | SiegeDescription = "Leads minions through enemy defenses to attack enemy rifts.",
27 | CodeName = "SpitfireGame.RWeaverRoleOffense"
28 | };
29 | public static SiegeRole Pillager = new()
30 | {
31 | Id = 3,
32 | Name = PILLAGER,
33 | Effect = "+18% XP from cache drops.",
34 | SiegeDescription = "Finds and loots caches. Assists defenders and attackers.",
35 | CodeName = "SpitfireGame.RWeaverRolePillaging"
36 | };
37 |
38 | public static Dictionary Roles = new()
39 | {
40 | { DEFENDER, Defender },
41 | { ATTACKER, Attacker },
42 | { PILLAGER, Pillager }
43 | };
44 |
45 | public static Dictionary RolesById = new()
46 | {
47 | { Defender.Id, Defender },
48 | { Attacker.Id, Attacker },
49 | { Pillager.Id, Pillager }
50 | };
51 |
52 | public static SiegeRole GetById(int id)
53 | {
54 | if (RolesById.TryGetValue(id, out var trait))
55 | {
56 | return trait;
57 | }
58 | return new SiegeRole { Id = 0, Name = "Unknown" };
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Loadout/Trait.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Model
2 | {
3 | public class Trait : LoadoutItem
4 | {
5 | public const string PENTAGON_BONUS_SLOT = "Pentagon";
6 | public const string DIAMOND_BONUS_SLOT = "Diamond";
7 | public const string TRIANGLE_BONUS_SLOT = "Triangle";
8 |
9 | public string CodeName { get; set; }
10 | public string MatchingSlot { get; set; }
11 | public string UnlockedAt { get; set; }
12 | public string Rarity { get; set; }
13 | public Trait MatchingBonusTrait { get; set; }
14 |
15 | // Bonus Traits
16 | public static Trait BONUS_TRAIT_ARMOR = new()
17 | {
18 | CodeName = "SpitfireGame.RWeaverBonusTraitArmor",
19 | Description = "+8 armor.",
20 | SiegeDescription = "+8 armor."
21 | };
22 | public static Trait BONUS_TRAIT_CC_RESIST = new()
23 | {
24 | CodeName = "SpitfireGame.RWeaverBonusTraitCCResist",
25 | Description = "-10% control effect duration.",
26 | SiegeDescription = "-10% control effect duration."
27 | };
28 | public static Trait BONUS_TRAIT_COIN_TRICKLE = new()
29 | {
30 | CodeName = "SpitfireGame.RWeaverBonusTraitCoinTrickle",
31 | Description = "+1 coin per second.",
32 | SiegeDescription = "+1 coin per second."
33 | };
34 | public static Trait BONUS_TRAIT_COOLDOWN_REDUCTION = new()
35 | {
36 | CodeName = "SpitfireGame.RWeaverBonusTraitCooldownReduction",
37 | Description = "+10.0% cooldown speed.",
38 | SiegeDescription = "+10.0% cooldown speed."
39 | };
40 | public static Trait BONUS_TRAIT_HEALTH = new()
41 | {
42 | CodeName = "SpitfireGame.RWeaverBonusTraitHealth",
43 | Description = "+4.0% max health.",
44 | SiegeDescription = "+4.0% max health."
45 | };
46 | public static Trait BONUS_TRAIT_HEALTH_REGEN = new()
47 | {
48 | CodeName = "SpitfireGame.RWeaverBonusTraitHealthRegen",
49 | Description = "Regenerates 1% max health per second.",
50 | SiegeDescription = "+12.0% health regeneration."
51 | };
52 | public static Trait BONUS_TRAIT_MAGIC_ARMOR = new()
53 | {
54 | CodeName = "SpitfireGame.RWeaverBonusTraitMagicArmor",
55 | Description = "+8 magic armor.",
56 | SiegeDescription = "+8 magic armor."
57 | };
58 | public static Trait BONUS_TRAIT_MANA_MAX = new()
59 | {
60 | CodeName = "SpitfireGame.RWeaverBonusTraitManaMax",
61 | Description = "+8.0% max mana.",
62 | SiegeDescription = "+8.0% max mana."
63 | };
64 | public static Trait BONUS_TRAIT_MANA_REGEN = new()
65 | {
66 | CodeName = "SpitfireGame.RWeaverBonusTraitManaRegen",
67 | Description = "+12.0% mana regeneration.",
68 | SiegeDescription = "+12.0% mana regeneration."
69 | };
70 | public static Trait BONUS_TRAIT_PRIMARY_DAMAGE = new()
71 | {
72 | CodeName = "SpitfireGame.RWeaverBonusTraitPrimaryDamage",
73 | Description = "+5.0% primary attack damage.",
74 | SiegeDescription = "+5.0% primary attack damage."
75 | };
76 | public static Trait BONUS_TRAIT_SPEED = new()
77 | {
78 | CodeName = "SpitfireGame.RWeaverBonusTraitSpeed",
79 | Description = "+2.5% movement speed.",
80 | SiegeDescription = "+2.5% movement speed."
81 | };
82 | }
83 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/BotDifficulty.cs:
--------------------------------------------------------------------------------
1 | namespace ProjectRechained.Entities.Names
2 | {
3 | public class BotDifficulty
4 | {
5 | public const string
6 | EASY = "Easy",
7 | NORMAL = "Normal",
8 | HARD = "Hard",
9 | NIGHTMARE = "Nightmare",
10 |
11 | NIGHTMARE_PLUS = "Nightmare",
12 | NIGHTMARE_PLUS_2 = "Nightmare+",
13 |
14 | HELL = "Hell",
15 | HELL_PLUS = "Hell+",
16 | HELL_2 = "Hell++",
17 |
18 | IMPOSSIBLE = "Impossible",
19 | IMPOSSIBLE_PLUS = "Impossible+",
20 | IMPOSSIBLE_2 = "Impossible++",
21 |
22 | TIMEMASTER = "TimeMaster";
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Consumable.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class Consumable
4 | {
5 | public const string
6 | BARGAIN_SCROLL = "Discount Scroll",
7 | COOLDOWN_POTION = "Caffeination Potion",
8 | EMPTY_RIFT_SCROLL = "Empty Rift Scroll",
9 | GUARDIAN_POTION = "Guardian Potion",
10 | HEALTH_POTION = "Health Potion",
11 | INVULNERABILITY_SCROLL = "Invulnerability Scroll",
12 | LUCK_POTION = "Luck Potion",
13 | MANA_POTION = "Mana Potion",
14 | RAGE_POTION = "Rage Potion",
15 | REPAIR_SCROLL = "Repair Scroll",
16 | RESURRECTION_SCROLL = "Resurrection Scroll",
17 | SLOW_SCROLL = "Slow Scroll",
18 | SPEED_POTION = "Speed Potion",
19 | UNCHAINED_SCROLL = "Unchained Scroll",
20 | EXPERIENCE_POTION = "Experience Potion";
21 | }
22 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Difficulty.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public class Difficulty
4 | {
5 | public const string
6 | APPRENTICE = "Apprentice",
7 | WAR_MAGE = "War Mage",
8 | MASTER = "Master",
9 | RIFT_LORD = "Rift Lord",
10 |
11 | APPRENTICE_PLUS = "Apprentice+",
12 | WAR_MAGE_PLUS = "War Mage+",
13 | MASTER_PLUS = "Master+",
14 | MASTER_PLUS_2 = "Master++",
15 | RIFT_LORD_PLUS = "Rift Lord+",
16 | RIFT_LORD_PLUS_2 = "Rift Lord++",
17 | RIFT_LORD_PLUS_3 = "Rift Lord+3",
18 | RIFT_LORD_PLUS_4 = "Rift Lord+4",
19 | RIFT_LORD_PLUS_5 = "Rift Lord+5",
20 |
21 | ENDLESS = "Endless",
22 |
23 | ENDLESS_PLUS = "Endless+",
24 | ENDLESS_PLUS_2 = "Endless++",
25 | ENDLESS_PLUS_3 = "Endless+3",
26 | ENDLESS_PLUS_4 = "Endless+4",
27 | ENDLESS_PLUS_5 = "Endless+5",
28 | ENDLESS_PLUS_6 = "Endless+6",
29 | ENDLESS_PLUS_7 = "Endless+7",
30 | ENDLESS_PLUS_8 = "Endless+8",
31 | ENDLESS_PLUS_9 = "Endless+9",
32 | ENDLESS_PLUS_10 = "Endless+10";
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Dye.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | class Dye
4 | {
5 | public const string
6 | NORMAL = "Normal",
7 | HEROIC = "Heroic",
8 | LEGENDARY = "Legendary";
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/GameMode.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | class GameMode
4 | {
5 | public const string
6 | SURVIVAL = "Survival",
7 | ENDLESS = "Endless",
8 | WEEKLY_CHALLENGE = "Weekly Challenge";
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Gear.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class Gear
4 | {
5 | public const string
6 | FREEDOM_TRINKET = "Freedom Trinket",
7 | GREATER_FREEDOM_TRINKET = "Greater Freedom Trinket",
8 | MENDING_ROOT = "Mending Root",
9 | HOBGOBLIN_CHARM = "Hobgoblin's Charm",
10 | RING_OF_LAST_STAND = "Ring of Last Stand",
11 | MAGES_PICNIC = "Mage's Picnic",
12 | MAGES_CLOVER = "Mage's Clover",
13 | GNOMISH_REPAIR_KIT = "Gnomish Repair Kit",
14 | TELEPORTATION_RING = "Teleportation Ring",
15 | ARCANE_BUBBLE_BLOWER = "Arcane Bubble Blower",
16 | FIRE_WALL_BRACERS = "Fire Wall Bracers",
17 | ICE_AMULET = "Ice Amulet",
18 | RING_OF_STORMS = "Ring of Storms",
19 | LIGHTNING_RING = "Lightning Ring",
20 | FLAME_BRACERS = "Flame Bracers",
21 | ANTITRAP_VAMBRACE = "AntiTrap Vambrace";
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Glyph.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | class Glyph
4 | {
5 | public const string
6 | GLYPH_OF_AMPLIFICATION = "Glyph of Amplification",
7 | GLYPH_OF_MAGIC_ARMOR = "Glyph of Magic Armor",
8 | GLYPH_OF_BLOOD = "Glyph of Blood",
9 | GLYPH_OF_HEALING = "Glyph of Healing",
10 | MINOR_GLYPH_OF_HEALING = "Minor Glyph of Healing",
11 | GLYPH_OF_HEALTH = "Glyph of Health",
12 | ORCISH_FIELD_PROMOTION_GLYPH = "Orcish Field Promotion Glyph",
13 | GLYPH_OF_PHYSICAL_ARMOR = "Glyph of Physical Armor",
14 | GLYPH_OF_SPEED = "Glyph of Speed",
15 | GLYPH_OF_SNEAKING = "Glyph of Sneaking";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Guardian.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class Guardian
4 | {
5 | public const string
6 | BARTENDER_GUARDIAN = "Bartender Guardian",
7 | BLACKSMITH_GUARDIAN = "Blacksmith Guardian",
8 | COOK_GUARDIAN = "Cook Guardian",
9 | DECKHAND_GUARDIAN = "Deckhand Guardian",
10 | DRAGON_GUARDIAN = "Dragon Guardian",
11 | FRIAR_GUARDIAN = "Friar Guardian",
12 | HEADHUNTER_GUARDIAN = "Headhunter Guardian",
13 | JADE_EMPIRE_GUARDIAN = "Jade Empire Guardian",
14 | JAILER_GUARDIAN = "Jailer Guardian",
15 | LION_GUARDIAN = "Lion Guardian",
16 | MOON_GUARDIAN = "Moon Guardian",
17 | PRIEST_GUARDIAN = "Priest Guardian",
18 | QUARTERMASTER_GUARDIAN = "Quartermaster Guardian",
19 | RANCH_HAND_GUARDIAN = "Ranch Hand Guardian",
20 | RUMRUDDER_GUARDIAN = "Rumrudder Guardian",
21 | SERPENT_GUARDIAN = "Serpent Guardian",
22 | STABLEHAND_GUARDIAN = "Stablehand Guardian",
23 | SUN_GUARDIAN = "Sun Guardian",
24 | WEAPONWRIGHT_GUARDIAN = "WeaponWright Guardian";
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Hero.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class Hero
4 | {
5 | public const string
6 | BIONKA = "Bionka",
7 | BLACKPAW = "Blackpaw",
8 | BLOODSPIKE = "Bloodspike",
9 | BRASS = "Brass",
10 | CYGNUS = "Cygnus",
11 | DEADEYE = "Deadeye",
12 | DOBBIN = "Dobbin",
13 | GABRIELLA = "Gabriella",
14 | HOGARTH = "Hogarth",
15 | IVY = "Ivy",
16 | MAXIMILIAN = "Maximilian",
17 | MIDNIGHT = "Midnight",
18 | OZIEL = "Oziel",
19 | SMOLDER = "Smolder",
20 | STINKEYE = "Stinkeye",
21 | TEMPER = "Temper",
22 | TUNDRA = "Tundra",
23 | YI_LIN = "Yi-Lin",
24 | ZOEY = "Zoey",
25 | SIR_WINSTON = "Sir Winston",
26 | KOBOLD_KING = "Kobold King";
27 | }
28 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Map.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class Map
4 | {
5 | public const string
6 | ACADEMY_SEWERS = "Academy Sewers",
7 | ARCHMAGE_LIBRARY = "Archmage Library",
8 | AVALANCHE = "Avalanche",
9 | BANQUET_HALL = "Banquet Hall",
10 | CASTLE_GATES = "Castle Gates",
11 | CLIFFSIDE_CLASH = "Cliffside Clash",
12 | CONFLUENCE = "Confluence",
13 | CROGON_KEEP = "Crogon Keep",
14 | DOCKS_AT_EVENTIDE = "Docks at Eventide",
15 | EVENTIDE_FORTRESS = "Eventide Fortress",
16 | EVENTIDE_RAMPARTS = "Eventide Ramparts",
17 | FROSTBITE = "Frostbite",
18 | GATES_OF_THURICVOD = "Gates of Thuricvod",
19 | HIGHLANDS = "Highlands",
20 | MAXIMUM_SECURITY = "Maximum Security",
21 | MIDNIGHT_MARKET = "Midnight Market",
22 | ORCATRAZ = "Orcatraz",
23 | ORCRI_LA = "Orcri-La",
24 | RESTRICTED_SECTION = "Restricted Section",
25 | RIFTMAKERS_TEMPLE = "Riftmaker's Temple",
26 | SHARK_ISLAND = "Shark Island",
27 | STABLES_AT_EVENTIDE = "Stables at Eventide",
28 | STORM_DRAIN = "Storm Drain",
29 | TEMPLE_GRAVEYARD = "Temple Graveyard",
30 | THE_BATHS = "The Baths",
31 | THE_FALLING_FOLLY = "The Falling Folly",
32 | THE_WALL = "The Wall",
33 | THRONE_ROOM = "Throne Room",
34 | THURICVOD_VILLAGE = "Thuricvod Village",
35 | TRAINING_GROUNDS = "Training Grounds",
36 | UNCHAINED_FORTRESS = "Unchained Fortress",
37 | WATER_GARDEN = "Water Garden",
38 | SIEGE_TUTORIAL = "Siege Tutorial";
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Mod.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | { public static class Mod
3 | {
4 | public const string
5 | NO_TRAP_CAP = "No Trap Cap",
6 | NO_LIMIT_UNIQUE_TRAPS = "No Limit Unique Traps",
7 | INVINCIBLE_BARRICADES = "Invincible Barricades",
8 | TRAPS_ANY_SURFACE = "Traps Any Surface",
9 | TRAPS_IN_TRAPS = "Traps In Traps",
10 | NO_TRAP_GRID = "No Trap Grid",
11 | GOD_MODE = "God Mode",
12 | SHOW_TRAP_DAMAGE_FLYOFFS = "Show Trap Damage Flyoffs",
13 | HARDCORE = "Hardcore",
14 | ENHANCED_TRAP_ROTATION = "Enhanced Trap Rotation",
15 | SELL_TRAPS_ANYTIME = "Sell Traps Anytime",
16 | STARTING_COIN_OVERRIDE = "Starting Coin Override",
17 | ACCOUNT_LEVEL_OVERRIDE = "Account Level Override",
18 | TRAP_TIER_OVERRIDE = "Trap Tier Override",
19 | ADDITIONAL_HERO_WEAPON = "Additional Hero Weapon";
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/SiegeRole.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | class SiegeRole
4 | {
5 | public const string
6 | ATTACKER = "Attacker",
7 | DEFENDER = "Defender",
8 | PILLAGER = "Pillager";
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Skin.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | class Skin
4 | {
5 | public const string
6 | // OZIEL
7 | OZIEL_DEFAULT = "Oziel Default",
8 | BLOOD_RUNE = "Blood Rune",
9 | STRING_THEORY = "String Theory",
10 | KNIGHT_WALKER = "Knight Walker",
11 |
12 | // ZOEY
13 | ZOEY_DEFAULT = "Zoey Default",
14 | SCHOOL_DAZE = "School Daze",
15 | PRETTY_LITTLE_STITCHES = "Pretty Little Stitches",
16 | JURASSIC_JOURNEY = "Jurassic Journey",
17 |
18 | // BIONKA
19 | BIONKA_DEFAULT = "Bionka Default",
20 | LIZARD_QUEEN = "Lizard Queen",
21 | FLUFFALUMP = "Fluffalump",
22 | BIONKA_BUNNY = "Bionka Bunny",
23 |
24 | // BLACKPAW
25 | BLACKPAW_ENCHANTED_ARMOR = "Blackpaw Enchanted Armor",
26 | PAW_OF_THE_DEAD = "Paw of the Dead",
27 | BLACKPAW_DEFAULT = "Blackpaw Default",
28 | TUTU_DLOO = "Tutu D'Loo",
29 | PRIDE_OF_THE_PACK = "Pride of the Pack",
30 | RUNECLAW = "Runeclaw",
31 | BLACKSCALE = "Blackscale",
32 | DRAGON_THRALL = "Dragon Thrall",
33 |
34 | // BLOODPSIKE
35 | BLOODPSIKE_DEFAULT = "Bloodpsike Default",
36 | GRIZZLY_KILL = "Grizzly Kill",
37 | BREAKER_OF_MOLDS = "Breaker of Molds",
38 | BLOOD_APPETIT = "Blood Appetit",
39 | GREEN_GUILLOTINE = "Green Guillotine",
40 | LAUGHING_TO_DEATH = "Laughing to Death",
41 |
42 | // BRASS
43 | BRASS_DEFAULT = "Brass Default",
44 | GUNS_GEARS_N_LACE = "Guns, Gears, 'n Lace",
45 | BOMBSHELL_BATTALION = "Bombshell Battalion",
46 |
47 | // HOGARTH
48 | HOGARTH_ENCHANTED_ARMOR = "Hogarth Enchanted Armor",
49 | GOD_OF_PLUNDER = "God of Plunder",
50 | HOGARTH_DEFAULT = "Hogarth Default",
51 | LUMBERING_JACK = "Lumbering Jack",
52 | HOGARTH_DEFAULT_GOLD = "Hogarth Default (with some gold)",
53 | ICE_ARMOR = "Ice Armor",
54 | IMPERIAL_GOLDEN_WARRIOR = "Imperial Golden Warrior",
55 | BEACHED_BOD = "Beached Bod",
56 | BLACK_THANE = "Black Thane",
57 | THE_SCHLING = "The Schling",
58 | DRAGON_WARD = "Dragon Ward",
59 |
60 | // DEADEYE
61 | DEADEYE_DEFAULT = "Deadeye Default",
62 | PRETTY_PUNK = "Pretty Punk",
63 | THE_GOOD_THE_BAD_AND_THE_DEADEYE = "The Good, the Bad, and the Deadeye",
64 |
65 | // MIDNIGHT
66 | MIDNIGHT_DEFAULT = "Midnight Default",
67 | HIDDEN_TIGER = "Hidden Tiger",
68 | MIDNIGHT_DEFAULT_GOLD = "Midnight Default (with some gold)",
69 | CAT_BURGLAR = "Cat Burglar",
70 | SHIVER_ME_WHISKERS = "Shiver Me Whiskers",
71 | WU_XING_AZURE_ASSASSIN = "Wu Xing Azure Assassin",
72 |
73 | // DOBBIN
74 | DOBBIN_DEFAULT = "Dobbin Default",
75 | GOLD_RUSH = "Gold Rush",
76 | DOBBIN_DEFAULT_GOLD = "Dobbin Default (with some gold)",
77 | EL_CAVADOR = "El Cavador",
78 | SANTAS_LITTLE_HELPER = "Santa's Little Helper",
79 |
80 | // IVY
81 | IVY_ENCHANTED_ARMOR = "Ivy Enchanted Armor",
82 | VALKYRIE = "Valkyrie",
83 | IVY_DEFAULT = "Ivy Default",
84 | FLOWER_FRIEND = "Flower Friend",
85 | IVY_DEFAULT_GOLD = "Ivy Default (with some gold)",
86 | IMPERIAL_RUBY_ARCHER = "Imperial Ruby Archer",
87 | WICKED_WARDEN = "Wicked Warden",
88 | GROVEWATCH = "Grovewatch",
89 | DRAGON_TAMER = "Dragon Tamer",
90 |
91 | //SMOLDER
92 | SMOLDER_DEFAULT = "Smolder Default",
93 | HELTER_SWELTER = "Helter Swelter",
94 | SMOLDER_DEFAULT_WHITE_HAIR = "Smolder Default (White hair)",
95 | ELITE = "Elite",
96 | KILL_AUEA = "Kill-auea",
97 | FIRE_ALARM_FEMME = "Fire-Alarm Femme",
98 | FIRESTARTER = "Firestarter",
99 | WU_XING_DRAGON_MAGE = "Wu Xing Dragon Mage",
100 |
101 | //GABRIELLA
102 | VIOLENT_VINTAGE = "Violent Vintage",
103 | GABRIELLA_ENCHANTED_ARMOR = "Gabriella Enchanted Armor",
104 | BLOOD_QUEEN = "Blood Queen",
105 | GABRIELLA_DEFAULT = "Gabriella Default",
106 | FRIGHTFULLY_DELIGHTFUL = "Frightfully Delightful",
107 | GABRIELLA_DEFAULT_RED = "Gabriella Default (Red)",
108 | LIFE_IN_PLASTIC = "Life In Plastic",
109 | ARCHMAGE_OF_THE_ORDER = "Archmage of the Order",
110 | MISTRESS_OF_ILLUSION = "Mistress of Illusion",
111 | PROMISING_PRODIGY = "Promising Prodigy",
112 | I_DREAM_OF_GABBY = "I Dream of Gabby",
113 | MIRACLE_WORKER = "Miracle Worker",
114 | WINTER_WITCH = "Winter Witch",
115 | SKULL_NINJA = "Skull Ninja",
116 | EVIL_WAYS = "Evil Ways",
117 | DRAGON_CHARMER = "Dragon Charmer",
118 |
119 | //STINKEYE
120 | FIRST_EYE_BLIND = "First Eye Blind",
121 | STINKEYE_DEFAULT = "Stinkeye Default",
122 | STINKEYE_DEFAULT_ALT = "Stinkeye Default (Alternate)",
123 | ENFANT_TERRIBLE = "Enfant Terrible",
124 | FLOATY_FUN = "Floaty Fun",
125 |
126 | // TEMPER
127 | TEMPER_DEFAULT = "Temper Default",
128 | FORGED_FURY = "Forged Fury",
129 | PRIZED_COW = "Prized Cow",
130 |
131 | // CYGNUS
132 | CYGNUS_DEFAULT = "Cygnus Default",
133 | ADVENTURER = "Adventurer",
134 | CYGNUS_DEFAULT_RED = "Cygnus Default (Red)",
135 | CYGNUS_UNKNOWN = "Cygnus Unknown",
136 | OLD_CODGER = "Old Codger",
137 | ROAD_MASTER = "Road Master",
138 |
139 | // TUNDRA
140 | TUNDRA_DEFAULT = "Tundra Default",
141 | WAR_CHIEF = "War Chief",
142 | BIG_LEBEARSKI = "Big Lebearski",
143 | KING_OF_ARCTOS = "King of Arctos",
144 | IMPERIAL_GOLDEN_MAGE = "Imperial Golden Mage",
145 |
146 | // MAXIMILLIAN
147 | LUCKY_TUNIC = "Lucky Tunic",
148 | MAXIMILLIAN_ENCHANTED_ARMOR = "Maximillian Enchanted Armor",
149 | KNIGHTS_WATCH = "Knight's Watch",
150 | MAXIMILLIAN_DEFAULT = "Maximillian Default",
151 | ORC_SLAYER = "Orc Slayer",
152 | CARDBOARD_SAMURAI = "Cardboard Samurai",
153 | SCARED_CROW = "Scared-Crow",
154 | LION_HEART = "Lion Heart",
155 | ROBIN_HOOD = "Robin Hood",
156 | PAXIMILLIAN = "Paximillian",
157 | MAXIMILLIAN_LEGENDARY_DEFAULT = "Legendary (default)",
158 | CHAMPION_OF_THE_ORDER = "Champion of the Order",
159 | JAMEZ_RIPHER = "Jamez Ripher",
160 | WINTER_WARRIOR = "Winter Warrior",
161 | BOOMSTICK = "Boomstick",
162 | BACKDRAFT = "Backdraft",
163 | DRAGON_SLAYER = "Dragon Slayer",
164 | SUMMER_OF_STUNNING = "Summer of Stunning",
165 | RED_SCARF = "Red Scarf (China Ad)",
166 | YELLOW_SCARF = "Yellow Scarf (China Ad)",
167 | BLUE_SCARF = "Blue Scarf (China Ad)",
168 |
169 | // YI_LIN
170 | YI_LIN_DEFAULT = "Yi-lin Default",
171 | SENTINEL_OF_THE_ORDER = "Sentinel of the Order",
172 | SLAYING_IN_THE_RAIN = "Slaying in the Rain",
173 | YULE_LIN = "Yule-Lin",
174 |
175 | // SIR WINSTON
176 | DARK_PALADIN = "Dark Paladin",
177 | SIR_WINSTON_DEFAULT = "SirWinston Default",
178 |
179 | // CHANG'E
180 | CHANG_E_DEFAULT = "Chang_e Default",
181 |
182 | // CHICKEN
183 | CHICKEN = "Chicken",
184 | CHICKEN_WITH_TRAINERS = "Chicken with Trainers",
185 |
186 | // MERCENARIES
187 | PRIDE_ASSASSIN = "Pride Assassin",
188 | BOUNCER_BEAR = "Bouncer Bear",
189 | IMAGE_HUMAN = "iMage Human",
190 | IMAGE_ARCANE = "iMage Arcane",
191 | IMAGE_LIGHTNING = "iMage Lightning",
192 | IMAGE_ICE = "iMage Ice",
193 | IMAGE_FIRE = "iMage Fire",
194 | CHAOTIC_KOBOLD = "Chaotic Kobold",
195 | DWARF_PRIEST = "Dwarf Priest",
196 | SCURVY_RUMRUDDER = "Scurvy Rumrudder",
197 | GNOLL_BREEDER = "Gnoll Breeder";
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Trait.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class Trait
4 | {
5 | public const string
6 | EASILY_EXCITED = "Easily Excited",
7 | MAKING_IT_RAIN = "Making it Rain",
8 | NATURAL_BORN_SPRINTER = "Natural Born Sprinter",
9 | KIDNAPPED_A_LEPRECHAUN = "Kidnapped a Leprechaun",
10 | OVER_REACTING = "Over-Reacting",
11 | AVOIDS_PAIN = "Avoids Pain",
12 | FIRE_SALE = "Fire Sale",
13 | ICE_COLD_DEALS = "Ice Cold Deals",
14 | INSANE_ARCANE_SALE = "Insane Arcane Sale",
15 | LIGHTNING_DEALS = "Lightning Deals",
16 | BIG_SPENDER = "Big Spender",
17 | NEVER_PAYS_RETAIL = "Never Pays Retail",
18 | ONE_WITH_THE_TRAPS = "One with the Traps",
19 | OVERACHIEVER = "Overachiever",
20 | OVERPROTECTIVE = "Overprotective",
21 | RIFT_GIFT = "Rift Gift",
22 | RIFT_ROCKET = "Rift Rocket",
23 | SMASHING_DEALS = "Smashing Deals",
24 | HAS_A_TRAP_FETISH = "Has a Trap Fetish",
25 | BELIEVES_SIZE_MATTERS = "Believes Size Matters",
26 | UNCLE_KILLED_BY_GIANT = "Uncle Killed by Giant",
27 | CAT_EATEN_BY_GNOLLS = "Cat Eaten by Gnolls",
28 | BROTHER_MAULED_BY_GRIZZLIES = "Brother Mauled by Grizzlies",
29 | SISTER_EATEN_BY_AN_OGRE = "Sister Eaten by an Ogre",
30 | DOG_KILLED_BY_PRIDE_HUNTERS = "Dog Killed by Pride Hunters",
31 | GRANDMOTHER_EATEN_BY_A_TROLL = "Grandmother Eaten by a Troll",
32 | DIFFICULTY_CALMING_DOWN = "Difficulty Calming Down",
33 | AMATEUR_VETERINARIAN = "Amateur Veterinarian",
34 | SOLDIERS_TOOK_MY_HORSE = "Soldiers Took My Horse",
35 | HAS_ULTIMATE_SET_OF_TOOLS = "Has Ultimate Set of Tools",
36 | BEAR_HUGS = "Bear Hugs",
37 | HAS_A_BIG_BROTHER = "Has a Big Brother",
38 | KILLER_BUZZ = "Killer Buzz",
39 | PAGING_DR_LOVE = "Paging Dr Love",
40 | RIFT_AWAY = "Rift Away",
41 | FEELING_FIERCE = "Feeling Fierce",
42 | TRIES_TOO_HARD = "Tries Too Hard",
43 | ENJOYS_PAIN = "Enjoys Pain",
44 | EXECUTION_ADVANTAGE = "Execution Advantage",
45 | FEELS_SAFE_AT_HOME = "Feels Safe at Home",
46 | PRACTICES_WITCHCRAFT = "Practices Witchcraft",
47 | HIGH_PAIN_TOLERANCE = "High Pain Tolerance",
48 | WALKS_IT_OFF = "Walks It Off",
49 | TAKES_LESSONS_FROM_CYGNUS = "Takes Lessons from Cygnus",
50 | WALL_BUILDING_PHD = "Wall Building PhD",
51 | ATTENTION_DEFICIT = "Attention Deficit",
52 | EXTRA_PADDING = "Extra Padding",
53 |
54 | // Siege Only
55 | RESPECT_MY_AUTHORITY = "Respect My Authority",
56 | CACHE_WHISPERER = "Cache Whisperer",
57 | NEEDS_ALONE_TIME = "Needs Alone Time",
58 | LARGE_AND_IN_CHARGE = "Large and in Charge",
59 | RANSACKING_OBSESSION = "Ransacking Obsession",
60 | BORN_LEADER = "Born Leader",
61 | OH_WOW_BANDAGES = "Oh, Wow! Bandages!",
62 | SELLS_MAGIC_TO_MAGES = "Sells Magic to Mages",
63 | WORKS_SMARTER = "Works Smarter",
64 | MOTIVATES_OTHERS = "Motivates Others",
65 | OH_WOW_POTIONS = "Oh, Wow! Potions!",
66 | LIKE_YOU_STOLE_IT = "Like You Stole It",
67 | DOESNT_PLAY_WELL_WITH_OTHERS = "Doesn't Play Well with Others",
68 | MOB_MENTALITY = "Mob Mentality",
69 | SETTING_AN_EXAMPLE = "Setting an Example",
70 | STRANGE_SENSE_OF_HUMOR = "Strange Sense of Humor",
71 | NEVER_KNOCKS = "Never Knocks",
72 | REALLY_COLD_HANDS = "Really Cold Hands",
73 | HAS_FIERY_RAGE = "Has Fiery Rage",
74 | RIP_THAT_SUCKER_OPEN = "Rip That Sucker Open",
75 | ITS_MINE = "It's Mine!",
76 | NOT_SCARED_OF_STATUES = "Not Scared of Statues",
77 | IM_NOT_TOUCHING_YOU = "I'm Not Touching You",
78 | GUARDIAN_VANDALISM = "Guardian Vandalism",
79 | IS_A_WALKING_PINCUSHION = "Is a Walking Pincushion",
80 | SHOW_OFF = "Show off";
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Trap.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class Trap
4 | {
5 | public const string
6 | ACID_SPRAYER = "Acid Sprayer",
7 | ARCANE_BOWLING_BALL = "Arcane Bowling Ball",
8 | ARCANE_PHASER = "Arcane Phaser",
9 | ARROW_WALL = "Arrow Wall",
10 | BGH_ARROW_WALL = "BGH Arrow Wall",
11 | CEILING_BALLISTA = "Ceiling Ballista",
12 | DRAGONS_LANCE = "Dragons Lance",
13 | BGH_CEILING_BALLISTA = "BGH Ceiling Ballista",
14 | BARRICADE = "Barricade",
15 | GREAT_WALL_BARRICADE = "Great Wall Barricade",
16 | BOOM_BARREL = "Boom Barrel",
17 | BOOM_BARREL_ROLLER = "Boom Barrel Roller",
18 | BOULDER_CHUTE = "Boulder Chute",
19 | ICICLE_IMPALER = "Icicle Impaler",
20 | BRIMSTONE = "Brimstone",
21 | COIN_FORGE = "Coin Forge",
22 | CURSED_GROUND = "Cursed Ground",
23 | DECOY = "Decoy",
24 | SPITFIRE_WALL = "Spitfire Wall",
25 | FIRE_CRACKER = "Fire Cracker",
26 | FLIP_TRAP = "Flip Trap",
27 | FLOOR_SCORCHER = "Floor Scorcher",
28 | TEMPLE_ALARM_GONG = "Temple Alarm Gong",
29 | GRINDER = "Grinder",
30 | QUARTER_POUNDER = "Quarter Pounder",
31 | HAYMAKER = "Haymaker",
32 | HEALING_WELL = "Healing Well",
33 | ICE_SHARD = "Ice Shard",
34 | ICE_VENT = "Ice Vent",
35 | LIGHTNING_ROD = "Lightning Rod",
36 | MANA_WELL = "Mana Well",
37 | SUMMONER_TRAP = "Summoner Trap",
38 | NAPHTHA_SPRAYER = "Naphtha Sprayer",
39 | OVERLOAD_TRAP = "Overload Trap",
40 | POWERUP_DAMAGE = "Powerup Damage",
41 | POUNDER = "Pounder",
42 | CONCUSSIVE_POUNDER = "Concussive Pounder",
43 | POWER_GENERATOR = "Power Generator",
44 | PUSH_TRAP = "Push Trap",
45 | SAW_OF_ARCTOS = "Saw Of Arctos",
46 | SHIELD_POWERUP = "Shield Powerup",
47 | SPEED_PAD = "Speed Pad",
48 | FLOOR_SPIKES = "Floor Spikes",
49 | SPIKE_WALL = "Spike Wall",
50 | STEAM_VENT = "Steam Vent",
51 | SWINGING_MACE = "Swinging Mace",
52 | TAR_TRAP = "Tar Trap",
53 | VISCOUS_TAR = "Viscous Tar",
54 | SHOCK_ZAPPER = "Shock Zapper",
55 | BGH_SHOCK_ZAPPER = "BGH Shock Zapper",
56 | TRIP_WIRE = "Trip Wire",
57 | WALL_BLADES = "Wall Blades",
58 | WALL_CHARGER = "Wall Charger",
59 | WEB_SPINNER = "Web Spinner",
60 |
61 | // Siege Only
62 | ANTI_MAGIC_FIELD = "Anti-Magic Field",
63 | BUFF_PYLON = "Buff Pylon",
64 | PROJECTILE_SHIELD = "Projectile Shield",
65 | HERO_SEEKER_ARROW_WALL = "Hero-Seeker Arrow Wall",
66 | HERO_SEEKER_BALLISTA = "Hero-Seeker Ballista",
67 | HERO_SEEKER_ZAPPER = "Hero-Seeker Zapper";
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/TrapPart.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class TrapPart
4 | {
5 | public const string
6 | DOUBLE_TANK_ACCUMULATOR = "Double Tank Accumulator",
7 | STUNNING_ACCUMULATOR = "Stunning Accumulator",
8 | CAPACITANCE_ATTUNER = "Capacitance Attuner",
9 | DEEP_CELL_ATTUNER = "Deep Cell Attuner",
10 | REFLECTIVE_ARMOR = "Reflective Armor",
11 | REUSABLE_SHIELDING = "Reusable Shielding",
12 | REVENGE_RUNE = "Revenge Rune",
13 | BRAIDED_COIL = "Braided Coil",
14 | DISCOUNT_COIL = "Discount Coil",
15 | SELF_IMPROVING_COIL = "Self-Improving Coil",
16 | QUICK_TRAP_COMPONENTS = "Quick Trap Components",
17 | RENTAL_PARTS = "Rental Parts",
18 | STAMPED_PARTS = "Stamped Parts",
19 | SUBSIDIZED_PARTS = "Subsidized Parts",
20 | VANITY_PLATING = "Vanity Plating",
21 | BOUNTY_GENERATOR = "Bounty Generator",
22 | HEALTH_SIPHON = "Health Siphon",
23 | MANA_SIPHON = "Mana Siphon",
24 | XP_SIPHON = "XP Siphon",
25 | DOUBLE_STRUTS = "Double Struts",
26 | HERO_RUNED_FRAME = "Hero Runed Frame",
27 | CONTROLLED_RESONATOR = "Controlled Resonator",
28 | FIRE_RESONATOR = "Fire Resonator",
29 | ICE_RESONATOR = "Ice Resonator",
30 | KINETIC_RESONATOR = "Kinetic Resonator",
31 | UNCHAINED_RESONATOR = "Unchained Resonator",
32 | WEAKNESS_RESONATOR = "Debilitation Resonator",
33 | CHARGING_SPRING = "Charging Spring",
34 | DOUBLE_SPRING = "Double Spring",
35 | SHORT_SPRING = "Short Spring",
36 | AMBUSH_TRIGGER = "Ambush Trigger",
37 | COMBO_GENERATOR = "Combo Generator",
38 | CROWD_CONTROL_TRIGGER = "Crowd Control Trigger",
39 | EXECUTION_TRIGGER = "Execution Trigger",
40 | HEAT_ACTIVATED_TRIGGER = "Heat Activated Trigger",
41 | HEAVY_PRESSURE_PLATE = "Heavy Pressure Plate",
42 | KINETIC_RECLAIMER = "Kinetic Reclaimer",
43 | LIGHT_PRESSURE_PLATE = "Light Pressure Plate";
44 | }
45 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/TrapPartSlot.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class TrapPartSlot
4 | {
5 | public const string ACCUMULATOR = "Accumulator";
6 | public const string ATTUNER = "Attuner";
7 | public const string CLADDING = "Cladding";
8 | public const string COIL = "Coil";
9 | public const string COMPONENTS = "Components";
10 | public const string FIELD = "Field";
11 | public const string FRAME = "Frame";
12 | public const string RESONATOR = "Resonator";
13 | public const string SPRING = "Spring";
14 | public const string TRIGGER = "Trigger";
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/Wave.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public class Wave
4 | {
5 | public const string
6 |
7 | // Siege Only
8 | LVL1_2_DWARF_SHAMANS = "2 Dwarf Shamans",
9 | LVL1_2_GNOLL_HUNTERS = "2 Gnoll Hunters",
10 | LVL1_2_GRIZZLY_SHIELDBEARERS = "2 Grizzly Shieldbearers",
11 | LVL1_2_GRIZZLY_BEARS = "2 Grizzly Bears",
12 | LVL1_4_CROSSBOW_ORCS = "4 Crossbow Orcs",
13 | LVL1_5_LIGHT_SOLDIERS = "5 Light Soldiers",
14 | LVL1_9_KOBOLDS = "9 Kobolds",
15 | LVL1_9_LIGHT_ORCS = "9 Light Orcs",
16 |
17 | LVL2_14_KOBOLDS = "14 Kobolds",
18 | LVL2_14_LIGHT_ORCS = "14 Light Orcs",
19 | LVL2_2_CYCLOPS_SHAMANS = "2 Cyclops Shamans",
20 | LVL2_3_DWARF_SHAMANS = "3 Dwarf Shamans",
21 | LVL2_3_GNOLL_HUNTERS = "3 Gnoll Hunters",
22 | LVL2_3_GRIZZLY_BEARS = "3 Grizzly Bears",
23 | LVL2_3_GRIZZLY_SHIELDBEARERS = "3 Grizzly Shieldbearers",
24 | LVL2_4_GNOME_TINKERERS = "4 Gnome Tinkerers",
25 | LVL2_6_CROSSBOW_ORCS = "6 Crossbow Orcs",
26 | LVL2_8_LIGHT_SOLDIERS = "8 Light Soldiers",
27 |
28 | LVL3_2_GIANTS = "2 Giants",
29 | LVL3_2_HOBGOBLIN_SHAMANS = "2 Hobgoblin Shamans",
30 | LVL3_2_PRIDE_HUNTERS = "2 Pride Hunters",
31 | LVL3_2_TROLL_SHIELDBEARERS = "2 Troll Shieldbearers",
32 | LVL3_3_CYCLOPS_SHAMANS = "3 Cyclops Shamans",
33 | LVL3_3_OGRES = "3 Ogres",
34 | LVL3_4_HUMAN_CROSSBOWMEN = "4 Human Crossbowmen",
35 | LVL3_5_MEDIUM_SOLDIERS = "5 Medium Soldiers",
36 | LVL3_9_MEDIUM_ORCS = "9 Medium Orcs",
37 | LVL3_9_SATYRS = "9 Satyrs",
38 |
39 | LVL4_14_MEDIUM_ORCS = "14 Medium Orcs",
40 | LVL4_14_SATYRS = "14 Satyrs",
41 | LVL4_2_FIRE_MAGES = "2 Fire Mages",
42 | LVL4_2_TROLLS = "2 Trolls",
43 | LVL4_3_GIANTS = "3 Giants",
44 | LVL4_3_HOBGOBLIN_SHAMANS = "3 Hobgoblin Shamans",
45 | LVL4_3_PRIDE_HUNTERS = "3 Pride Hunters",
46 | LVL4_3_TROLL_SHIELDBEARERS = "3 Troll Shieldbearers",
47 | LVL4_5_OGRES = "5 Ogres",
48 | LVL4_6_HUMAN_CROSSBOWMEN = "6 Human Crossbowmen",
49 | LVL4_8_MEDIUM_SOLDIERS = "8 Medium Soldiers",
50 | LVL4_9_GNOME_TINKERERS = "9 Gnome Tinkerers",
51 |
52 | BOSS_ORFUM_MOUNTAIN_TROLL = "Orfum, Mountain Troll",
53 | BOSS_OAKENTOES_DWARF_SHAMAN = "Oakentoes, Dwarf Shaman",
54 | BOSS_SAWBONES_HOBGOBLIN_SHAMAN = "Sawbones, Hobgoblin Shaman",
55 | BOSS_BULGOD_ARMORED_OGRE = "Bulgod, Armored Ogre",
56 | BOSS_URZA_FIRE_LORD = "Urza the Fire Lord",
57 | BOSS_GAEADIN_EARTH_LORD = "Gaeadin the Earth Lord",
58 | BOSS_SWIFTYHOOVES = "Swiftyhooves",
59 | BOSS_TIKLIK_HOBGOBLIN_SHAMAN = "TikLik, Hobgoblin Shaman",
60 | BOSS_VITALITY = "Vitality",
61 | BOSS_GATEBREAKER = "Gatebreaker",
62 | BOSS_LYZANDER_CULT_RESURRECTOR = "Lyzander, Cult Resurrector",
63 | BOSS_GRENWALDE_NATURE_LORD = "Grenwalde the Nature Lord",
64 | BOSS_TUBIFORE = "Tubifore";
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Entities/Names/WeeklyChallenge.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Names
2 | {
3 | public static class WeeklyChallenge
4 | {
5 | public const string
6 | ALMS_FOR_THE_POOR = "Alms for the Poor",
7 | DO_THE_TRAP_SHUFFLE = "Do The Trap Shuffle",
8 | DODGE_CITY = "Dodge City",
9 | DOUBLETS = "Doublets!",
10 | FREEZED_TO_MEET_YOU = "Freezed to Meet You",
11 | FUN_WITH_PHYSICS = "Fun with Physics",
12 | GOTTA_GO_FAST = "Gotta Go Fast",
13 | GRAND_FINALE = "Grand Finale",
14 | IM_THE_BOSS_NOW = "I'm the Boss Now",
15 | INFERIORITY_COMPLEX = "Inferiority Complex",
16 | KEEP_YOUR_DISTANCE = "Keep Your Distance",
17 | LIBRARY_RINTH = "Library-rinth",
18 | MEMORY_LANES = "Memory Lanes",
19 | OFF_THE_CHAIN = "Off the Chain!",
20 | ONE_AND_DONE = "One and Done",
21 | PLAYING_WITH_FIRE = "Playing With Fire",
22 | PURPLE_MAZE = "Purple Maze",
23 | RIFT_INSTABILITY = "Rift Instability",
24 | ROLLING_IN_IT = "Rolling in it",
25 | THE_MERCS_LITTER = "The Merc's Litter",
26 | VANISHING_ACT = "Vanishing Act",
27 | WE_CAN_REBUILD_IT = "We Can Rebuild It",
28 | YOUR_OWN_DEVICES = "Your Own Devices";
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Forms/ComboBoxHelper.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Model;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Drawing;
5 | using System.Windows.Forms;
6 |
7 | public class ComboBoxHelper
8 | {
9 | private readonly ToolTip _toolTip;
10 | private readonly Dictionary _items;
11 | private readonly Func _getToolTipText;
12 | private readonly Action _drawItemShape;
13 |
14 | public ComboBoxHelper(Dictionary items, Func getToolTipText, Action drawItemShape)
15 | {
16 | this._toolTip = new ToolTip();
17 | this._items = items;
18 | this._getToolTipText = getToolTipText;
19 | this._drawItemShape = drawItemShape;
20 | }
21 |
22 | public void InitializeComboBox(ComboBox comboBox)
23 | {
24 | comboBox.DrawMode = DrawMode.OwnerDrawFixed;
25 | comboBox.DrawItem += ComboBox_DrawItem;
26 | comboBox.DropDownClosed += ComboBox_DropDownClosed;
27 | }
28 |
29 | private void ComboBox_DrawItem(object sender, DrawItemEventArgs eventDrawItem)
30 | {
31 | ComboBox comboBox = (ComboBox)sender;
32 |
33 | if (eventDrawItem.Index < 0) { return; }
34 |
35 | string itemName = comboBox.GetItemText(comboBox.Items[eventDrawItem.Index]);
36 | if (!_items.TryGetValue(itemName, out T item)) { return; }
37 |
38 | // Draw the background
39 | if ((eventDrawItem.State & DrawItemState.Selected) == DrawItemState.Selected)
40 | {
41 | eventDrawItem.Graphics.FillRectangle(SystemBrushes.Highlight, eventDrawItem.Bounds);
42 | }
43 | else
44 | {
45 | eventDrawItem.Graphics.FillRectangle(SystemBrushes.Window, eventDrawItem.Bounds);
46 | }
47 |
48 | // Draw the shape with the appropriate item
49 | _drawItemShape?.Invoke(eventDrawItem.Graphics, eventDrawItem.Bounds, item);
50 |
51 | // Draw the item text
52 | using (Brush brush = new SolidBrush((eventDrawItem.State & DrawItemState.Selected) == DrawItemState.Selected ? SystemColors.HighlightText : SystemColors.ControlText))
53 | {
54 | int iXPadding = _drawItemShape != null ? 25 : 0;
55 | eventDrawItem.Graphics.DrawString(itemName, eventDrawItem.Font, brush, eventDrawItem.Bounds.X + iXPadding, eventDrawItem.Bounds.Y);
56 | }
57 |
58 | if ((eventDrawItem.State & DrawItemState.Selected) == DrawItemState.Selected)
59 | {
60 | // To show the tool tips only when the ComboBox is open
61 | if ((eventDrawItem.State & DrawItemState.ComboBoxEdit) != DrawItemState.ComboBoxEdit)
62 | {
63 | _toolTip.Show(_getToolTipText(item), comboBox, eventDrawItem.Bounds.Right, eventDrawItem.Bounds.Bottom);
64 | }
65 |
66 | // Focus is a low-level method intended primarily for custom control
67 | // authors. Instead, application programmers should use the Select
68 | // method or the ActiveControl property for child controls, or the
69 | // Activate method for forms.
70 | // https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.focus?view=windowsdesktop-8.0&redirectedfrom=MSDN#System_Windows_Forms_Control_Focus
71 | eventDrawItem.DrawFocusRectangle();
72 | }
73 | }
74 |
75 | private void ComboBox_DropDownClosed(object sender, EventArgs eventDropDown)
76 | {
77 | _toolTip.Hide((Control)sender);
78 | }
79 | }
80 |
81 | public class TraitComboBoxHelper : ComboBoxHelper where T : Trait
82 | {
83 | public TraitComboBoxHelper(Dictionary traits)
84 | : base(traits, GetToolTipText, DrawItemShape)
85 | {
86 | }
87 |
88 | private static string GetToolTipText(T trait)
89 | {
90 | return $"{trait.Name}\n\n{trait.Description}\n\nMatching Bonus: {trait.MatchingBonusTrait.Description}";
91 | }
92 |
93 | private static void DrawItemShape(Graphics g, Rectangle bounds, T trait)
94 | {
95 | Color shapeColor;
96 | Action drawShape;
97 |
98 | switch (trait.MatchingSlot)
99 | {
100 | case Trait.TRIANGLE_BONUS_SLOT:
101 | shapeColor = Color.Yellow;
102 | drawShape = DrawTriangle;
103 | break;
104 | case Trait.DIAMOND_BONUS_SLOT:
105 | shapeColor = Color.Blue;
106 | drawShape = DrawDiamond;
107 | break;
108 | case Trait.PENTAGON_BONUS_SLOT:
109 | shapeColor = Color.Green;
110 | drawShape = DrawPentagon;
111 | break;
112 | default:
113 | shapeColor = Color.Black;
114 | drawShape = DrawRectangle; // Default shape
115 | break;
116 | }
117 |
118 | using (Brush brush = new SolidBrush(shapeColor))
119 | {
120 | drawShape(g, bounds);
121 | }
122 | }
123 |
124 | private static void DrawTriangle(Graphics g, Rectangle bounds)
125 | {
126 | Point[] points = new Point[]
127 | {
128 | new(bounds.X + 10, bounds.Y + bounds.Height - 5),
129 | new(bounds.X + 20, bounds.Y + bounds.Height - 5),
130 | new(bounds.X + 15, bounds.Y + bounds.Height - 15)
131 | };
132 | g.FillPolygon(Brushes.Gold, points);
133 | }
134 |
135 | private static void DrawDiamond(Graphics g, Rectangle bounds)
136 | {
137 | Point[] points = new Point[]
138 | {
139 | new(bounds.X + 15, bounds.Y + bounds.Height - 15),
140 | new(bounds.X + 20, bounds.Y + bounds.Height - 10),
141 | new(bounds.X + 15, bounds.Y + bounds.Height - 5),
142 | new(bounds.X + 10, bounds.Y + bounds.Height - 10)
143 | };
144 | g.FillPolygon(Brushes.LightSkyBlue, points);
145 | }
146 |
147 | private static void DrawPentagon(Graphics g, Rectangle bounds)
148 | {
149 | Point[] points = new Point[]
150 | {
151 | new(bounds.X + 15, bounds.Y + bounds.Height - 15),
152 | new(bounds.X + 20, bounds.Y + bounds.Height - 10),
153 | new(bounds.X + 18, bounds.Y + bounds.Height - 5),
154 | new(bounds.X + 12, bounds.Y + bounds.Height - 5),
155 | new(bounds.X + 10, bounds.Y + bounds.Height - 10)
156 | };
157 | g.FillPolygon(Brushes.LightGreen, points);
158 | }
159 |
160 | private static void DrawRectangle(Graphics g, Rectangle bounds)
161 | {
162 | g.FillRectangle(Brushes.Gray, bounds);
163 | }
164 | }
165 |
166 |
167 | public class SlotItemComboBoxHelper : ComboBoxHelper
168 | {
169 | public SlotItemComboBoxHelper(Dictionary slotitems)
170 | : base(slotitems, GetToolTipText, null)
171 | {
172 | }
173 |
174 | private static string GetToolTipText(SlotItem slotItem)
175 | {
176 | return $"{slotItem.Name}\n\n{slotItem.Description}";
177 | }
178 | }
179 |
180 | public class SiegeSlotItemComboBoxHelper : ComboBoxHelper
181 | {
182 | public SiegeSlotItemComboBoxHelper(Dictionary slotitems)
183 | : base(slotitems, GetToolTipText, null)
184 | {
185 | }
186 |
187 | private static string GetToolTipText(SlotItem slotItem)
188 | {
189 | return $"{slotItem.Name}\n\n{slotItem.SiegeDescription}";
190 | }
191 | }
192 |
193 | public class TrapPartComboBoxHelper : ComboBoxHelper
194 | {
195 | public TrapPartComboBoxHelper(Dictionary trapParts)
196 | : base(trapParts, GetToolTipText, null)
197 | {
198 | }
199 |
200 | private static string GetToolTipText(TrapPart trapPart)
201 | {
202 | return $"{trapPart.Name}\n\n{trapPart.Description}";
203 | }
204 | }
205 |
206 | public class GuardianComboBoxHelper : ComboBoxHelper
207 | {
208 | public GuardianComboBoxHelper(Dictionary guardians)
209 | : base(guardians, GetToolTipText, null)
210 | {
211 | }
212 |
213 | private static string GetToolTipText(Guardian guardian)
214 | {
215 | return $"{guardian.Name}\n\n{guardian.AuraText}";
216 | }
217 | }
218 |
219 | public class ConsumableComboBoxHelper : ComboBoxHelper
220 | {
221 | public ConsumableComboBoxHelper(Dictionary consumables)
222 | : base(consumables, GetToolTipText, null)
223 | {
224 | }
225 |
226 | private static string GetToolTipText(Consumable consumable)
227 | {
228 | return $"{consumable.Name}\n\n{consumable.Description}";
229 | }
230 | }
231 |
232 | public class RoleComboBoxHelper : ComboBoxHelper
233 | {
234 | public RoleComboBoxHelper(Dictionary role)
235 | : base(role, GetToolTipText, null)
236 | {
237 | }
238 |
239 | private static string GetToolTipText(SiegeRole role)
240 | {
241 | return $"{role.Name}\n\n{role.SiegeDescription}\n\n{role.Effect}";
242 | }
243 | }
244 | public class WaveComboBoxHelper : ComboBoxHelper
245 | {
246 | public WaveComboBoxHelper(Dictionary waves)
247 | : base(waves, GetToolTipText, null)
248 | {
249 | }
250 |
251 | private static string GetToolTipText(Wave wave)
252 | {
253 | return $"{wave.Name}\n\n{wave.Description}\n\n{wave.KeywordText}\n{wave.KeywordDescription}";
254 | }
255 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Forms/FolderPicker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Runtime.InteropServices;
6 | using System.Runtime.InteropServices.ComTypes;
7 |
8 | public class FolderPicker
9 | {
10 | private readonly List _resultPaths = [];
11 | private readonly List _resultNames = [];
12 |
13 | public IReadOnlyList ResultPaths => _resultPaths;
14 | public IReadOnlyList ResultNames => _resultNames;
15 | public string ResultPath => ResultPaths.FirstOrDefault();
16 | public string ResultName => ResultNames.FirstOrDefault();
17 | public virtual string InputPath { get; set; }
18 | public virtual bool ForceFileSystem { get; set; }
19 | public virtual bool Multiselect { get; set; }
20 | public virtual string Title { get; set; }
21 | public virtual string OkButtonLabel { get; set; }
22 | public virtual string FileNameLabel { get; set; }
23 |
24 | protected virtual int SetOptions(int options)
25 | {
26 | if (ForceFileSystem)
27 | {
28 | options |= (int)FOS.FOS_FORCEFILESYSTEM;
29 | }
30 |
31 | if (Multiselect)
32 | {
33 | options |= (int)FOS.FOS_ALLOWMULTISELECT;
34 | }
35 | return options;
36 | }
37 |
38 | // for all .NET
39 | public virtual bool? ShowDialog(IntPtr owner, bool throwOnError = false)
40 | {
41 | var dialog = (IFileOpenDialog)new FileOpenDialog();
42 | if (!string.IsNullOrEmpty(InputPath))
43 | {
44 | if (CheckHr(SHCreateItemFromParsingName(InputPath, null, typeof(IShellItem).GUID, out var item), throwOnError) != 0)
45 | {
46 | return null;
47 | }
48 |
49 | dialog.SetFolder(item);
50 | }
51 |
52 | var options = FOS.FOS_PICKFOLDERS;
53 | options = (FOS)SetOptions((int)options);
54 | dialog.SetOptions(options);
55 |
56 | if (Title != null)
57 | {
58 | dialog.SetTitle(Title);
59 | }
60 |
61 | if (OkButtonLabel != null)
62 | {
63 | dialog.SetOkButtonLabel(OkButtonLabel);
64 | }
65 |
66 | if (FileNameLabel != null)
67 | {
68 | dialog.SetFileName(FileNameLabel);
69 | }
70 |
71 | if (owner == IntPtr.Zero)
72 | {
73 | owner = Process.GetCurrentProcess().MainWindowHandle;
74 | if (owner == IntPtr.Zero)
75 | {
76 | owner = GetDesktopWindow();
77 | }
78 | }
79 |
80 | var hr = dialog.Show(owner);
81 | if (hr == ERROR_CANCELLED)
82 | {
83 | return null;
84 | }
85 |
86 | if (CheckHr(hr, throwOnError) != 0)
87 | {
88 | return null;
89 | }
90 |
91 | if (CheckHr(dialog.GetResults(out var items), throwOnError) != 0)
92 | {
93 | return null;
94 | }
95 |
96 | items.GetCount(out var count);
97 | for (var i = 0; i < count; i++)
98 | {
99 | items.GetItemAt(i, out var item);
100 | CheckHr(item.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out var path), throwOnError);
101 | CheckHr(item.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEEDITING, out var name), throwOnError);
102 | if (path != null || name != null)
103 | {
104 | _resultPaths.Add(path);
105 | _resultNames.Add(name);
106 | }
107 | }
108 | return true;
109 | }
110 |
111 | private static int CheckHr(int hr, bool throwOnError)
112 | {
113 | if (hr != 0 && throwOnError)
114 | {
115 | Marshal.ThrowExceptionForHR(hr);
116 | }
117 |
118 | return hr;
119 | }
120 |
121 | [DllImport("shell32")]
122 | private static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IShellItem ppv);
123 |
124 | [DllImport("user32")]
125 | private static extern IntPtr GetDesktopWindow();
126 |
127 | #pragma warning disable IDE1006 // Naming Styles
128 | private const int ERROR_CANCELLED = unchecked((int)0x800704C7);
129 | #pragma warning restore IDE1006 // Naming Styles
130 |
131 | [ComImport, Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")] // CLSID_FileOpenDialog
132 | private class FileOpenDialog { }
133 |
134 | [ComImport, Guid("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
135 | private interface IFileOpenDialog
136 | {
137 | [PreserveSig] int Show(IntPtr parent); // IModalWindow
138 | [PreserveSig] int SetFileTypes(); // not fully defined
139 | [PreserveSig] int SetFileTypeIndex(int iFileType);
140 | [PreserveSig] int GetFileTypeIndex(out int piFileType);
141 | [PreserveSig] int Advise(); // not fully defined
142 | [PreserveSig] int Unadvise();
143 | [PreserveSig] int SetOptions(FOS fos);
144 | [PreserveSig] int GetOptions(out FOS pfos);
145 | [PreserveSig] int SetDefaultFolder(IShellItem psi);
146 | [PreserveSig] int SetFolder(IShellItem psi);
147 | [PreserveSig] int GetFolder(out IShellItem ppsi);
148 | [PreserveSig] int GetCurrentSelection(out IShellItem ppsi);
149 | [PreserveSig] int SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);
150 | [PreserveSig] int GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName);
151 | [PreserveSig] int SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
152 | [PreserveSig] int SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);
153 | [PreserveSig] int SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
154 | [PreserveSig] int GetResult(out IShellItem ppsi);
155 | [PreserveSig] int AddPlace(IShellItem psi, int alignment);
156 | [PreserveSig] int SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
157 | [PreserveSig] int Close(int hr);
158 | [PreserveSig] int SetClientGuid(); // not fully defined
159 | [PreserveSig] int ClearClientData();
160 | [PreserveSig] int SetFilter([MarshalAs(UnmanagedType.IUnknown)] object pFilter);
161 | [PreserveSig] int GetResults(out IShellItemArray ppenum);
162 | [PreserveSig] int GetSelectedItems([MarshalAs(UnmanagedType.IUnknown)] out object ppsai);
163 | }
164 |
165 | [ComImport, Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
166 | private interface IShellItem
167 | {
168 | [PreserveSig] int BindToHandler(); // not fully defined
169 | [PreserveSig] int GetParent(); // not fully defined
170 | [PreserveSig] int GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
171 | [PreserveSig] int GetAttributes(); // not fully defined
172 | [PreserveSig] int Compare(); // not fully defined
173 | }
174 |
175 | [ComImport, Guid("b63ea76d-1f85-456f-a19c-48159efa858b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
176 | private interface IShellItemArray
177 | {
178 | [PreserveSig] int BindToHandler(); // not fully defined
179 | [PreserveSig] int GetPropertyStore(); // not fully defined
180 | [PreserveSig] int GetPropertyDescriptionList(); // not fully defined
181 | [PreserveSig] int GetAttributes(); // not fully defined
182 | [PreserveSig] int GetCount(out int pdwNumItems);
183 | [PreserveSig] int GetItemAt(int dwIndex, out IShellItem ppsi);
184 | [PreserveSig] int EnumItems(); // not fully defined
185 | }
186 |
187 | #pragma warning disable CA1712 // Do not prefix enum values with type name
188 | private enum SIGDN : uint
189 | {
190 | SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000,
191 | SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,
192 | SIGDN_FILESYSPATH = 0x80058000,
193 | SIGDN_NORMALDISPLAY = 0,
194 | SIGDN_PARENTRELATIVE = 0x80080001,
195 | SIGDN_PARENTRELATIVEEDITING = 0x80031001,
196 | SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001,
197 | SIGDN_PARENTRELATIVEPARSING = 0x80018001,
198 | SIGDN_URL = 0x80068000
199 | }
200 |
201 | [Flags]
202 | private enum FOS
203 | {
204 | FOS_OVERWRITEPROMPT = 0x2,
205 | FOS_STRICTFILETYPES = 0x4,
206 | FOS_NOCHANGEDIR = 0x8,
207 | FOS_PICKFOLDERS = 0x20,
208 | FOS_FORCEFILESYSTEM = 0x40,
209 | FOS_ALLNONSTORAGEITEMS = 0x80,
210 | FOS_NOVALIDATE = 0x100,
211 | FOS_ALLOWMULTISELECT = 0x200,
212 | FOS_PATHMUSTEXIST = 0x800,
213 | FOS_FILEMUSTEXIST = 0x1000,
214 | FOS_CREATEPROMPT = 0x2000,
215 | FOS_SHAREAWARE = 0x4000,
216 | FOS_NOREADONLYRETURN = 0x8000,
217 | FOS_NOTESTFILECREATE = 0x10000,
218 | FOS_HIDEMRUPLACES = 0x20000,
219 | FOS_HIDEPINNEDPLACES = 0x40000,
220 | FOS_NODEREFERENCELINKS = 0x100000,
221 | FOS_OKBUTTONNEEDSINTERACTION = 0x200000,
222 | FOS_DONTADDTORECENT = 0x2000000,
223 | FOS_FORCESHOWHIDDEN = 0x10000000,
224 | FOS_DEFAULTNOMINIMODE = 0x20000000,
225 | FOS_FORCEPREVIEWPANEON = 0x40000000,
226 | FOS_SUPPORTSTREAMABLEITEMS = unchecked((int)0x80000000)
227 | }
228 | #pragma warning restore CA1712 // Do not prefix enum values with type name
229 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Forms/InputValidator.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Model;
2 | using System.Text.RegularExpressions;
3 |
4 | public static class InputValidator
5 | {
6 | public static (bool isValid, string errorMessage) ValidateLoadoutName(string loadoutName)
7 | {
8 | if (loadoutName.Length < 3)
9 | {
10 | return (false, "Loadout name must be at least 3 characters long.");
11 | }
12 |
13 | if (loadoutName.Length > 32)
14 | {
15 | return (false, "Loadout name must be at most 32 characters long.");
16 | }
17 |
18 | if (!Regex.IsMatch(loadoutName, "^[a-zA-Z0-9 _-]+$"))
19 | {
20 | return (false, "Loadout name must be alphanumeric and can include spaces, hyphens, and underscores.");
21 | }
22 |
23 | return (true, string.Empty);
24 | }
25 |
26 | public static (bool isValid, string errorMessage) ValidatePlayerName(string playerName)
27 | {
28 | if (playerName.Length < 3)
29 | {
30 | return (false, "Player name must be at least 3 characters long.");
31 | }
32 |
33 | if (playerName.Length > 12)
34 | {
35 | return (false, "Player name must be at most 12 characters long.");
36 | }
37 |
38 | if (!Regex.IsMatch(playerName, "^[a-zA-Z0-9]+$"))
39 | {
40 | return (false, "Player name must be alphanumeric.");
41 | }
42 |
43 | return (true, string.Empty);
44 | }
45 |
46 | public static (bool isValid, string errorMessage) ValidateLoadoutCode(string loadoutCode)
47 | {
48 | try
49 | {
50 | SurvivalLoadout loadout = new();
51 | loadout.Decode(loadoutCode);
52 | return (true, string.Empty);
53 | }
54 | catch
55 | {
56 | return (false, "Invalid loadout code.");
57 | }
58 | }
59 |
60 | public static (bool isValid, string errorMessage) ValidateSiegeLoadoutCode(string loadoutCode)
61 | {
62 | try
63 | {
64 | SiegeLoadout loadout = new();
65 | loadout.Decode(loadoutCode);
66 | return (true, string.Empty);
67 | }
68 | catch
69 | {
70 | return (false, "Invalid loadout code.");
71 | }
72 | }
73 |
74 | public static (bool isValid, string errorMessage) ValidateIpAddress(string ipAddress)
75 | {
76 | string pattern = @"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\." +
77 | @"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\." +
78 | @"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\." +
79 | @"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
80 |
81 | if (Regex.IsMatch(ipAddress, pattern))
82 | {
83 | return (true, string.Empty);
84 | }
85 |
86 | return (false, "Invalid IP address format.");
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Forms/LauncherMainForm.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
124 | 144
125 |
126 |
127 |
128 |
129 | iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
130 | vAAADrwBlbxySQAAAcJJREFUSEvd1bsvg1EYBvBWQhoimlhqICEsjVjEfyAGQw1WgskqRGJlJKSbS8J/
131 | YO3okjCZDBJ3CSJuQRq6tC7PU8/H8fYmaaee5Jd83/u855y236W+8h6R0UQ9NOo0YzBjj07/NzAhBCOw
132 | DtdwCC3AeqvwuBmOgD3s5ZyQlsk+1PQAn8YN3MGL3Kpm++5hWMv9HQj4yZ7UWAyu0aZlfweKq05Tsda0
133 | 7PdAoR0STkOxuFaHlk9vsOCEpRL1Fq+DMycolXMIcoNup+h6hijMw6NqLl5MfnPKdXP0cINpU6QP6E9/
134 | RQwc90JSGb1Dn2LmEdXcNWiGYcwUiQ9XQPPZUwH7yugAKhUzr1LNXYNiDI9NkS6gVvPZEwA+tV5+CtWK
135 | mdeo5q5BJwz5W9uApjSfPWMmo0nFzCdM5nlmmDJFD6/DNmxAtt+X+RZsQracUvk2KIUkN4ibIvEe3jO1
136 | fNjL62brcW4wBPbNyA3GYQD4LOwAH0a+RflWZb4LzAaB18BuwDWHvIvUBEvwBm5Tp3I/BKFBeOxX1gXu
137 | nFdYhMw/KhTDMAdXsKxywYHeFbiEWQirnHugiZ/w5z4vNNjLOTotq+HzfQGlv6ywArnnwAAAAABJRU5E
138 | rkJggg==
139 |
140 |
141 |
142 | 140, 17
143 |
144 |
145 |
146 | How it works?
147 | - Peer to peer, LAN supported
148 | - Online multiplayer step by step guide to setup in our Discord
149 | - Play online using VPN software such as Radmin
150 |
151 |
152 | Looking for group? Join our discord community!
153 | - We have dedicated roles and channels for LFG and matchmaking
154 |
155 |
156 |
157 |
158 | How it works?
159 | - Peer to peer, LAN supported
160 | - Online multiplayer step by step guide to setup in our Discord
161 | - Play online using VPN software such as Radmin
162 |
163 |
164 | Looking for group? Join our discord community!
165 | - We have dedicated roles and channels for LFG and matchmaking
166 |
167 |
168 |
169 |
170 | How it works?
171 | - Peer to peer, LAN supported
172 | - Online multiplayer step by step guide to setup in our Discord
173 | - Play online using VPN software such as Radmin
174 |
175 |
176 | Looking for group? Join our discord community!
177 | - We have dedicated roles and channels for LFG and matchmaking
178 |
179 |
180 |
181 |
182 | How it works?
183 | - Peer to peer, LAN supported
184 | - Online multiplayer step by step guide to setup in our Discord
185 | - Play online using VPN software such as Radmin
186 |
187 |
188 | Looking for group? Join our discord community!
189 | - We have dedicated roles and channels for LFG and matchmaking
190 |
191 |
192 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/GameFiles/Config/ConfigFile.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Utils;
2 | using System.IO;
3 |
4 | namespace SingleplayerLauncher.GameFiles
5 | {
6 | internal class ConfigFile
7 | {
8 | private readonly string path; // filename included
9 | public IniFile data;
10 |
11 | public ConfigFile(string filePath, bool newFile = false)
12 | {
13 | path = filePath;
14 | data = new IniFile();
15 |
16 | if (!newFile && File.Exists(path))
17 | {
18 | data.Load(path);
19 | }
20 | }
21 |
22 | public void Write()
23 | {
24 | data.Save(path);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/GameFiles/Config/DefaultGame.cs:
--------------------------------------------------------------------------------
1 | using ProjectRechained.Entities;
2 | using SingleplayerLauncher.Model;
3 | using SingleplayerLauncher.Utils;
4 | using System;
5 | using System.IO;
6 | using System.Windows.Forms;
7 |
8 | namespace SingleplayerLauncher.GameFiles
9 | {
10 | public static class DefaultGame
11 | {
12 | // TODO: make singleton
13 |
14 | private const string RDisplayColorInfoSection = "SpitfireGame.RDisplayColorInfo";
15 | private const string RGameReplicationInfoSection = "SpitfireGame.RGameReplicationInfo";
16 | private const string RHUDBaseSection = "SpitfireGame.RHUDBase";
17 | private const string RGameStateSection = "SpitfireGame.RGameState";
18 |
19 | private const string GameReplicationInfoKeyGameMode = "DefaultOfflineDifficulty";
20 | private const string GameReplicationInfoKeyMapLevel = "DefaultOfflineSuggestedLevel";
21 | private const string GameReplicationInfoKeyPlayerLevel = "DefaultOfflinePlayerLevel";
22 | private const string GameReplicationInfoKeyPlayerCount = "PlayerCountOverride";
23 | private const string GameReplicationInfoKeyPauseTimerInSeconds = "DefaultOfflinePauseTimerDurationInSeconds";
24 | private const string RHUDBaseKeyShowFlyoffsForTrapDamage = "ShowFlyoffsForTrapDamage";
25 | private const string DebugBotPlayerIDs = ".DebugBotPlayerIDs";
26 |
27 | private const string DefaultPauseTimerInSeconds = "999999";
28 | private const int MAX_BOT_COUNT = 10;
29 |
30 | public static void ApplySurvival()
31 | {
32 | _ = GameInfo.Instance.SurvivalBattleground ?? throw new ArgumentNullException(nameof(Battleground), "Mandatory parameter");
33 | _ = GameInfo.Instance.SurvivalBattleground.Difficulty ?? throw new ArgumentNullException(nameof(Battleground.Difficulty), "Mandatory parameter");
34 | _ = Mods.Mods.ShowTrapDamageFlyoffs ?? throw new ArgumentNullException(nameof(Mods.Mods.ShowTrapDamageFlyoffs), "Mandatory parameter");
35 | _ = Mods.Mods.AccountLevelOverride ?? throw new ArgumentNullException(nameof(Mods.Mods.AccountLevelOverride), "Mandatory parameter");
36 |
37 | ConfigFile defaultGame = new(Path.Combine(Settings.Instance.RootGamePath, FileUtils.INI_CONFIGS_FOLDER_RELATIVE_PATH, FileUtils.INI_DEFAULT_GAME_FILENAME));
38 | IniFile data = defaultGame.data;
39 |
40 | data.UpdateEntry(RHUDBaseSection, RHUDBaseKeyShowFlyoffsForTrapDamage, Mods.Mods.ShowTrapDamageFlyoffs.IsEnabled.ToString());
41 | data.UpdateEntry(RGameReplicationInfoSection, GameReplicationInfoKeyGameMode, GameInfo.Instance.SurvivalBattleground.GameMode.Id.ToString());
42 | data.UpdateEntry(RGameReplicationInfoSection, GameReplicationInfoKeyMapLevel, GameInfo.Instance.SurvivalBattleground.Difficulty.EnemyLevel.ToString());
43 | int accountLevel = Mods.Mods.AccountLevelOverride.IsEnabled ? Mods.Mods.AccountLevelOverride.Value : GameInfo.Instance.SurvivalBattleground.Difficulty.AccountLevel;
44 | data.UpdateEntry(RGameReplicationInfoSection, GameReplicationInfoKeyPlayerLevel, accountLevel.ToString());
45 | data.UpdateEntry(RGameReplicationInfoSection, GameReplicationInfoKeyPlayerCount, GameInfo.Instance.SurvivalBattleground.Difficulty.PlayerCount.ToString());
46 |
47 | defaultGame.Write();
48 | }
49 |
50 | public static void ApplySurvivalSettings()
51 | {
52 | _ = Mods.Mods.ShowTrapDamageFlyoffs ?? throw new ArgumentNullException(nameof(Mods.Mods.ShowTrapDamageFlyoffs), "Mandatory parameter");
53 |
54 | ConfigFile defaultGame = new(Path.Combine(Settings.Instance.RootGamePath, FileUtils.INI_CONFIGS_FOLDER_RELATIVE_PATH, FileUtils.INI_DEFAULT_GAME_FILENAME));
55 | IniFile data = defaultGame.data;
56 |
57 | data.UpdateEntry(RHUDBaseSection, RHUDBaseKeyShowFlyoffsForTrapDamage, Mods.Mods.ShowTrapDamageFlyoffs.IsEnabled.ToString());
58 |
59 | defaultGame.Write();
60 | }
61 |
62 | public static void ApplySiegeBots(bool isSiegeAllyBots = false)
63 | {
64 | _ = GameConfig.Instance.SiegeEnemyTeamAsBots ? true : throw new ArgumentNullException(nameof(GameConfig.Instance.SiegeEnemyTeamAsBots), "Mandatory parameter");
65 | _ = GameConfig.Instance.SiegeBotDifficulty ?? throw new ArgumentNullException(nameof(GameConfig.Instance.SiegeBotDifficulty), "Mandatory parameter");
66 |
67 | ConfigFile defaultGame = new(Path.Combine(Settings.Instance.RootGamePath, FileUtils.INI_CONFIGS_FOLDER_RELATIVE_PATH, FileUtils.INI_DEFAULT_GAME_FILENAME));
68 | IniFile data = defaultGame.data;
69 |
70 | String botDifficultyName = GameConfig.Instance.SiegeBotDifficulty;
71 | BotDifficulty botDifficulty = BotDifficulty.BotDifficultiesByName[botDifficultyName];
72 |
73 | data.UpdateEntry(RGameReplicationInfoSection, GameReplicationInfoKeyGameMode, botDifficulty.BotLevel.ToString());
74 |
75 | data.RemoveSection(RGameStateSection);
76 |
77 | int botCount = 0;
78 | foreach (SiegeLoadout botLoadout in botDifficulty.botLoadouts)
79 | {
80 | data.UpdateEntry(RGameStateSection, DebugBotPlayerIDs, botLoadout.PlayerName, botCount);
81 | botCount++;
82 | }
83 |
84 | if (isSiegeAllyBots)
85 | {
86 | foreach (SiegeLoadout allyBotLoadout in GameInfo.Instance.AllyBotsLoadouts)
87 | {
88 | data.UpdateEntry(RGameStateSection, DebugBotPlayerIDs, allyBotLoadout.PlayerName, botCount);
89 | botCount++;
90 | }
91 | }
92 |
93 | defaultGame.Write();
94 | }
95 |
96 | private static void ClearBotsConfiguration(IniFile data)
97 | {
98 | for (int i = 0; i < MAX_BOT_COUNT; i++)
99 | {
100 | data.UpdateEntry(RGameStateSection, DebugBotPlayerIDs, "", i);
101 | }
102 | }
103 |
104 | public static void Initialize()
105 | {
106 | ConfigFile defaultGame = new(Path.Combine(Settings.Instance.RootGamePath, FileUtils.INI_CONFIGS_FOLDER_RELATIVE_PATH, FileUtils.INI_DEFAULT_GAME_FILENAME));
107 | IniFile data = defaultGame.data;
108 |
109 | data.UpdateEntry(RGameReplicationInfoSection, GameReplicationInfoKeyPauseTimerInSeconds, DefaultPauseTimerInSeconds);
110 |
111 | defaultGame.Write();
112 | }
113 | }
114 |
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/GameFiles/Cooked/PawnWeaponUPK.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Model;
2 |
3 | namespace SingleplayerLauncher
4 | {
5 | class PawnWeaponUPK
6 | {
7 | // TODO: make singleton * Load after initialized
8 |
9 | private readonly UPKFile uPKFile;
10 | private readonly Hero hero;
11 |
12 | public PawnWeaponUPK(UPKFile uPKFile, Hero hero)
13 | {
14 | this.uPKFile = uPKFile;
15 | this.hero = hero;
16 | }
17 |
18 | public void SaveChanges()
19 | {
20 | uPKFile.Save();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/GameFiles/Cooked/SpitfireGameUPK.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Model;
2 | using SingleplayerLauncher.Mods;
3 | using System;
4 |
5 | namespace SingleplayerLauncher
6 | {
7 | class SpitfireGameUPK
8 | {
9 | // TODO: make singleton * Load after initialized
10 |
11 | public static UPKFile SpitfireGameUPKFile;
12 |
13 | private const int MAX_TRAP_TIER = 7;
14 | private const string TRAP_TIER_STRING = "src.TrapStrength";
15 |
16 | private const int PAR_TIME_CHANGE_INDEX = 0x155A3C2;
17 | private static readonly byte[] UNIQUE_BYTES_REFERENCE_PAR_TIME = { 0x1B, 0x8B, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x1D };
18 |
19 | public static void ApplyParTime(int parTimeSeconds)
20 | {
21 | byte[] parTimeBytes = BitConverter.GetBytes(parTimeSeconds);
22 | SpitfireGameUPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE_PAR_TIME, PAR_TIME_CHANGE_INDEX, 0, parTimeBytes);
23 | }
24 |
25 | public static void ApplyMods(bool areModsEnabled)
26 | {
27 | Mod.UPKFile = SpitfireGameUPKFile;
28 | foreach (Mod mod in Mods.Mods.ModList)
29 | {
30 | if (areModsEnabled && mod.IsEnabled)
31 | {
32 | mod.InstallMod();
33 | }
34 | else
35 | {
36 | mod.UninstallMod();
37 | }
38 | }
39 | }
40 |
41 | public static void ApplyMultiplayerPatches(bool isHost)
42 | {
43 | byte gameClientType = isHost ? (byte)2 : (byte)0;
44 | // Fix the weaver upgrade menu not closing when selecting
45 | byte[] uniqueBytesWeaverUpgrade = { 0x07, 0x79, 0x00, 0x9A, 0x38, 0x3A, 0x19, 0x01, 0x07, 0xFA, 0xFF, 0xFF, 0x09, 0x00, 0xE3, 0xFE, 0xFF, 0xFF, 0x00, 0x01, 0xE3, 0xFE, 0xFF, 0xFF, 0x38, 0x3A, 0x24 };
46 | int changeIndexWeaverUpgrade = 0xECCA73;
47 | SpitfireGameUPKFile.ApplyModification(uniqueBytesWeaverUpgrade, changeIndexWeaverUpgrade, 0, gameClientType);
48 |
49 | // Fix the no aggro (also the map overriding the trap cap, initial coins, etc.)
50 | byte[] uniqueBytesNoAggro = { 0x18, 0x27, 0x00, 0x9A, 0x38, 0x3A, 0x19, 0x00, 0xAF, 0xA5, 0x00, 0x00, 0x09, 0x00, 0xE3, 0xFE, 0xFF, 0xFF, 0x00, 0x01, 0xE3, 0xFE, 0xFF, 0xFF, 0x38, 0x3A, 0x24 };
51 | int changeIndexNoAggro = 0x155A4D1;
52 | SpitfireGameUPKFile.ApplyModification(uniqueBytesNoAggro, changeIndexNoAggro, 0, gameClientType);
53 |
54 | // Fix the par time at top for Host
55 | byte[] uniqueBytesParTime = { 0x07, 0x65, 0x00, 0x9A, 0x38, 0x3A, 0x19, 0x01, 0x07, 0xFA, 0xFF, 0xFF, 0x09, 0x00, 0xE3, 0xFE, 0xFF, 0xFF, 0x00, 0x01, 0xE3, 0xFE, 0xFF, 0xFF, 0x38, 0x3A, 0x24 };
56 | int changeIndexParTime = 0x1111436;
57 | SpitfireGameUPKFile.ApplyModification(uniqueBytesParTime, changeIndexParTime, 0, gameClientType);
58 |
59 | // Fix the shown Rift Points for Host
60 | byte[] uniqueBytesRiftPoints = { 0x07, 0xAA, 0x00, 0x9A, 0x38, 0x3A, 0x19, 0x01, 0x07, 0xFA, 0xFF, 0xFF, 0x09, 0x00, 0xE3, 0xFE, 0xFF, 0xFF, 0x00, 0x01, 0xE3, 0xFE, 0xFF, 0xFF, 0x38, 0x3A, 0x24 };
61 | int changeIndexRiftPoints = 0x110C979;
62 | SpitfireGameUPKFile.ApplyModification(uniqueBytesRiftPoints, changeIndexRiftPoints, 0, gameClientType);
63 |
64 | // Fix the score for Host
65 | byte[] uniqueBytesScore = { 0x07, 0x76, 0x01, 0x9A, 0x38, 0x3A, 0x19, 0x01, 0x07, 0xFA, 0xFF, 0xFF, 0x09, 0x00, 0xE3, 0xFE, 0xFF, 0xFF, 0x00, 0x01, 0xE3, 0xFE, 0xFF, 0xFF, 0x38, 0x3A, 0x24 };
66 | int changeIndexScore = 0x110A5EC;
67 | SpitfireGameUPKFile.ApplyModification(uniqueBytesScore, changeIndexScore, 0, gameClientType);
68 | }
69 |
70 | public static void SaveChanges()
71 | {
72 | SpitfireGameUPKFile.Save();
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/GameFiles/Cooked/UPKHeroObjectContent.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher
2 | {
3 |
4 | public sealed class UPKHeroObjectContent
5 | {
6 |
7 | public UPKFile HeroObjectContent { get; private set; }
8 |
9 | private const int LoadoutSlotByteSize = 4;
10 | private const int LoadoutSlotsNumber = 9;
11 | // Where the actual array of the loadout starts is + 28 bytes from the start of the loadout header.
12 | // Header + Field Type + Field Size in bytes + Array (start?) index + Array number of elements (8 + 8 + 4 + 4 + 4)
13 | private const int LoadoutArraySizeOffset = 16;
14 | private const int LoadoutArrayElementCountOffset = 24;
15 | private const int LoadoutSlotsOffset = 28;
16 | private const int LoadoutSectionLength = LoadoutSlotsOffset + 4 * LoadoutSlotsNumber;
17 |
18 | private const int GuardianSlotsNumber = 2;
19 | // Where the actual array of the guardians starts is + 28 bytes from the start of the guardians header.
20 | // Header + Field type + Field Size in bytes + Array (start?) index + Array number of elements (8 + 8 + 4 + 4 + 4)
21 | private const int GuardiansArraySizeOffset = 16;
22 | private const int GuardiansArrayElementCountOffset = 24;
23 | private const int GuardiansOffset = 28;
24 |
25 | // Header + Field type + Size in bytes + (start?) index (8 + 8 + 4 + 4)
26 | private const int SkinOffsetFromHeader = 24;
27 |
28 | // Header + Field type + Size in bytes + (start?) index (8 + 8 + 4 + 4)
29 | private const int HealthOffsetFromHeader = 24;
30 | private const int HealthMaxOffsetFromHeader = 24;
31 | private const int DefaultArchetypesSectionSize = 32;
32 |
33 | public UPKHeroObjectContent(UPKFile heroObjectContent)
34 | {
35 | HeroObjectContent = heroObjectContent;
36 | }
37 |
38 | private void FillRemovedBytes(int insertIndex)
39 | {
40 | HeroObjectContent.InsertZeroedBytes(insertIndex, 0);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/GameLauncher.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.GameFiles;
2 | using SingleplayerLauncher.Model;
3 | using System;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Windows.Forms;
7 |
8 | namespace SingleplayerLauncher
9 | {
10 | class GameLauncher
11 | {
12 | private static readonly SpitfireGameUPK SpitfireGameUPK = new();
13 |
14 | public static void ApplyChanges(bool isHost, bool isSiege = false, int parTimeSeconds = 0, bool isSiegeCoop = false, bool isSiegeAllyBots = false)
15 | {
16 | if (!isSiege)
17 | {
18 | SpitfireGameUPK.SpitfireGameUPKFile = new UPKFile(Path.Combine(Settings.Instance.RootGamePath, FileUtils.UPKS_PATH, FileUtils.SPITFIREGAME_UPK_FILENAME));
19 | SpitfireGameUPK.ApplyMultiplayerPatches(isHost);
20 |
21 | if (isHost)
22 | {
23 | SpitfireGameUPK.ApplyMods(GameConfig.Instance.ModsEnabled);
24 | SpitfireGameUPK.ApplyParTime(parTimeSeconds);
25 |
26 | // Loadouts are applied as soon as input is received
27 |
28 | GameFiles.DefaultGame.ApplySurvival();
29 |
30 | GameFiles.CharacterData.ApplyMods(GameConfig.Instance.ModsEnabled);
31 | }
32 |
33 | SpitfireGameUPK.SaveChanges();
34 | }
35 | else
36 | {
37 | if (isSiegeCoop)
38 | {
39 | GameFiles.DefaultGame.ApplySiegeBots(isSiegeAllyBots);
40 | }
41 | }
42 |
43 | // TODO add multiplayer patches for Siege Host
44 |
45 | GameFiles.DefaultGame.ApplySurvivalSettings();
46 | }
47 |
48 | public static void FirstLaunchInitialization()
49 | {
50 | string launcherInstallationPath = FileUtils.GetLauncherInstallationPath(Settings.Instance.RootGamePath);
51 | Settings.Instance.LauncherInstallationPath = launcherInstallationPath;
52 |
53 | FileUtils.CreateBackup(FileUtils.INI_CHARACTER_DATA_FILENAME, Path.Combine(Settings.Instance.RootGamePath, FileUtils.INI_CONFIGS_FOLDER_RELATIVE_PATH, FileUtils.INI_CHARACTER_DATA_FILENAME));
54 | FileUtils.CreateBackup(FileUtils.INI_DEFAULT_GAME_FILENAME, Path.Combine(Settings.Instance.RootGamePath, FileUtils.INI_CONFIGS_FOLDER_RELATIVE_PATH, FileUtils.INI_DEFAULT_GAME_FILENAME));
55 |
56 | GameFiles.CharacterData.Initialize();
57 | GameFiles.DefaultGame.Initialize();
58 |
59 | // SpitfireGame (decompress) Initialization
60 | if (!Settings.Instance.IsSiegeInstallation)
61 | {
62 | // TODO extend to decompress Siege UPK
63 | int spitfireGameUPKFileSize = Settings.Instance.IsSiegeInstallation ? FileUtils.SPITFIREGAME_SIEGE_UPK_ORG_SIZE : FileUtils.SPITFIREGAME_UPK_ORG_SIZE;
64 | FileUtils.DecompressUPKFile(FileUtils.SPITFIREGAME_UPK_FILENAME, spitfireGameUPKFileSize);
65 | }
66 |
67 | // Hook (for co-op and non-dedicated server) Initialization
68 | string p2pHookFileName = Settings.Instance.IsSiegeInstallation ? FileUtils.P2P_HOOK_SIEGE_FILENAME : FileUtils.P2P_HOOK_FILENAME;
69 | FileUtils.ApplyHook(p2pHookFileName, FileUtils.P2P_HOOK_TARGET_FILENAME, Path.Combine(Settings.Instance.RootGamePath, FileUtils.SPITFIREGAME_BINARIES_WIN64_PATH));
70 | FileUtils.ApplyHook(p2pHookFileName, FileUtils.P2P_HOOK_TARGET_FILENAME, Path.Combine(Settings.Instance.RootGamePath, FileUtils.SPITFIREGAME_BINARIES_WIN32_PATH));
71 | FileUtils.CreateBackup(p2pHookFileName, Path.Combine(Settings.Instance.LauncherInstallationPath, FileUtils.MOD_HOOKS_FOLDER_NAME, p2pHookFileName));
72 |
73 | // Delete Hooks folder (not needed anymore)
74 | try
75 | {
76 | // TODO: Remove in future release
77 | if (Directory.Exists(Path.Combine(Settings.Instance.RootGamePath, FileUtils.BINARIES_FOLDER_NAME, FileUtils.MOD_HOOKS_FOLDER_NAME)))
78 | {
79 | Directory.Delete(Path.Combine(Settings.Instance.RootGamePath, FileUtils.BINARIES_FOLDER_NAME, FileUtils.MOD_HOOKS_FOLDER_NAME), true);
80 | }
81 | }
82 | catch (Exception e)
83 | {
84 | MessageBox.Show("Error deleting obsolete Hooks folder: " + e.Message);
85 | }
86 |
87 | // Delete UE Extractor folder (not needed here anymore)
88 | try
89 | {
90 | // TODO: Remove in future release
91 | if (Directory.Exists(Path.Combine(Settings.Instance.RootGamePath, FileUtils.BINARIES_FOLDER_NAME, FileUtils.UE_EXTRACTOR_FOLDER_NAME)))
92 | {
93 | Directory.Delete(Path.Combine(Settings.Instance.RootGamePath, FileUtils.BINARIES_FOLDER_NAME, FileUtils.UE_EXTRACTOR_FOLDER_NAME), true);
94 | }
95 | }
96 | catch (Exception e)
97 | {
98 | MessageBox.Show("Error deleting obsolete UE Extractor folder: " + e.Message);
99 | }
100 |
101 | Settings.Instance.FirstRun = false;
102 | Settings.Instance.Save();
103 | }
104 |
105 | public static void StartGame(string playerName, bool isHost, string hostIP = "", string mapCode = "", int playerCount = 1)
106 | {
107 | Process p = new();
108 | string filePath = Path.Combine(Settings.Instance.RootGamePath,
109 | Settings.Instance.RunAs32 ? FileUtils.SPITFIREGAME_BINARIES_WIN32_PATH : FileUtils.BINARIES_FOLDER_NAME);
110 |
111 | string exeFullPath = Path.Combine(filePath, FileUtils.SPITFIREGAME_EXE_FILENAME);
112 |
113 | p.StartInfo.FileName = exeFullPath;
114 | p.StartInfo.WorkingDirectory = filePath;
115 | p.StartInfo.Arguments = CreateExeArguments(Settings.Instance.Debug, Settings.Instance.Language, isHost, hostIP, playerName, mapCode, playerCount);
116 | p.StartInfo.UseShellExecute = false;
117 |
118 | p.Start();
119 | p.WaitForExit();
120 | }
121 |
122 | private const string EXE_ARGUMENTS = " -seekfreeloadingpcconsole -writepid";
123 | private const string DEBUG_ARGUMENTS = " -log -ABSLOG=log.txt";
124 | private const string LANGUAGE_OPTION = " -language=";
125 |
126 | private static string CreateExeArguments(bool debug, string language, bool isHost, string hostIP, string playerName, string mapCode, int playerCount)
127 | {
128 | string arguments = "";
129 |
130 | string map = mapCode;
131 | string playerGUID = playerName;
132 | string defaultArgs = EXE_ARGUMENTS;
133 | string languageArg = LANGUAGE_OPTION + Language.survivalLanguageMap[language];
134 |
135 | arguments += isHost ? map : hostIP;
136 | arguments += isHost ? "?listen" : "";
137 | arguments += isHost ? "?PlayerReadyCount=" + playerCount : "";
138 | arguments += "?PlayerGUID=" + playerGUID;
139 | arguments += defaultArgs;
140 | arguments += languageArg;
141 |
142 | if (debug)
143 | {
144 | arguments += DEBUG_ARGUMENTS;
145 | }
146 |
147 | arguments += isHost ? " --net_mode=ListenServer" : " --net_mode=Client";
148 |
149 | return arguments;
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Model/BaseLoadout.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Utils;
2 | using System.Text;
3 |
4 | namespace SingleplayerLauncher.Model
5 | {
6 | public abstract class BaseLoadout
7 | {
8 | public string Name { get; set; } = "";
9 | public string PlayerName { get; set; } = "TimeMaster";
10 | public Hero Hero { get; set; } = Hero.Maximilian;
11 | public Skin Skin { get; set; } = Skin.MaximillianDefault;
12 | public Dye Dye { get; set; } = Dye.Normal;
13 | public SlotItem[] SlotItems { get; set; } = new SlotItem[] { };
14 |
15 | public Trait[] Traits { get; set; } = new Trait[] { };
16 |
17 | public const int SLOT_ITEMS_COUNT = 9;
18 | public const int TRAIT_SLOT_COUNT = 4;
19 | public const int TRAIT_BONUS_SLOT_COUNT = 3;
20 |
21 | public const int TRAIT_GREEN_SLOT_IDX = 0;
22 | public const int TRAIT_BLUE_SLOT_IDX = 1;
23 | public const int TRAIT_YELLOW_SLOT_IDX = 2;
24 | public const int TRAIT_NO_BONUS_SLOT_IDX = 3;
25 |
26 | protected const bool FORCE_LENGTH_TWO = true;
27 |
28 | public abstract string Encode();
29 | public abstract BaseLoadout Decode(string encodedString);
30 |
31 | public bool IsTraitMatchingBonus(int traitIdx)
32 | {
33 | if (traitIdx == TRAIT_NO_BONUS_SLOT_IDX)
34 | {
35 | return false;
36 | }
37 |
38 | Trait trait = Traits[traitIdx];
39 |
40 | if (traitIdx == TRAIT_BLUE_SLOT_IDX && trait.MatchingSlot == Trait.DIAMOND_BONUS_SLOT ||
41 | traitIdx == TRAIT_GREEN_SLOT_IDX && trait.MatchingSlot == Trait.PENTAGON_BONUS_SLOT ||
42 | traitIdx == TRAIT_YELLOW_SLOT_IDX && trait.MatchingSlot == Trait.TRIANGLE_BONUS_SLOT)
43 | {
44 | return true;
45 | }
46 |
47 | return false;
48 | }
49 |
50 | protected string EncodeBase()
51 | {
52 | var encodedString = new StringBuilder();
53 |
54 | // Encode PlayerName
55 | encodedString.Append($"{PlayerName}-");
56 |
57 | // Encode basic properties
58 | encodedString.Append($"{Base62Converter.Encode(Hero.Id)}{Base62Converter.Encode(Skin.Id, FORCE_LENGTH_TWO)}{Base62Converter.Encode(Dye.Id)}-");
59 |
60 | return encodedString.ToString();
61 | }
62 |
63 | protected void DecodeBase(string encodedString)
64 | {
65 | var code = encodedString.Split('-');
66 | int codeIdx = 0;
67 |
68 | // Decode PlayerName
69 | PlayerName = code[codeIdx++];
70 |
71 | // Decode basic properties
72 | var basicParts = code[codeIdx++];
73 | Hero = Hero.GetById(Base62Converter.Decode(basicParts.Substring(0, 1)));
74 | Skin = Skin.GetById(Base62Converter.Decode(basicParts.Substring(1, 2))); // Forced to be 2 chars
75 | Dye = Dye.GetById(Base62Converter.Decode(basicParts.Substring(3, 1)));
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Model/Battleground.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SingleplayerLauncher.Model
4 | {
5 | public class Battleground : IBattleground
6 | {
7 | public string Name { get; private set; } = "CUSTOM";
8 |
9 | public Map Map { get; set; }
10 |
11 | public GameMode GameMode { get; set; }
12 |
13 | public Difficulty Difficulty { get; set; }
14 |
15 | public int StartingCoin { get; set; }
16 |
17 | public TimeSpan ParTime => throw new NotImplementedException();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Model/GameInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace SingleplayerLauncher.Model
4 | {
5 | public sealed class GameInfo
6 | {
7 | private static readonly GameInfo instance = new();
8 |
9 | // Explicit static constructor to tell C# compiler
10 | // not to mark type as beforefieldinit /Singleton
11 | static GameInfo() { }
12 | private GameInfo() { }
13 |
14 | public static GameInfo Instance => instance;
15 |
16 |
17 | public static SurvivalLoadout SurvivalLoadout { get; set; } = new SurvivalLoadout();
18 | public static SiegeLoadout SiegeLoadout { get; set; } = new SiegeLoadout();
19 | public IBattleground SurvivalBattleground { get; set; } = new Survival();
20 | public IBattleground SiegeBattleground { get; set; } = new Siege();
21 |
22 | public int PlayerCount { get; set; } = 1;
23 | public List AllyBotsLoadouts { get; set; } = [];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Model/Language.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace SingleplayerLauncher.Model
4 | {
5 | public class Language
6 | {
7 | public static readonly Dictionary survivalLanguageMap = new()
8 | {
9 | { "Chinese" , "CHN" },
10 | { "Deutsch" , "DEU" },
11 | { "Español" , "ESN" },
12 | { "Français" , "FRA" },
13 | { "English" , "INT" },
14 | { "Italiano" , "ITA" },
15 | { "Polski" , "POL" },
16 | { "Português" , "PTB" },
17 | { "Русский" , "RUS" },
18 | { "Türkçe" , "TUR" }
19 | };
20 |
21 | public static readonly Dictionary siegeLanguageMap = new()
22 | {
23 | { "Chinese" , "CHN" },
24 | { "Deutsch" , "DEU" },
25 | { "Español" , "ESN" },
26 | { "Français" , "FRA" },
27 | { "English" , "INT" },
28 | { "Italiano" , "ITA" },
29 | { "Polski" , "POL" },
30 | { "Türkçe" , "TUR" }
31 | };
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Model/LoadoutItem.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Model
2 | {
3 | public abstract class LoadoutItem
4 | {
5 | public int Id { get; protected set; }
6 | public string Name { get; protected set; }
7 | public string SiegeName { get; protected set; }
8 | public string Description { get; protected set; }
9 | public string SiegeDescription { get; protected set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Model/SiegeLoadout.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Utils;
2 | using System.Text;
3 |
4 | namespace SingleplayerLauncher.Model
5 | {
6 | public class SiegeLoadout : BaseLoadout
7 | {
8 | public const int WAVES_SLOT_COUNT = 10;
9 | public Wave[] Waves { get; set; } = new Wave[WAVES_SLOT_COUNT];
10 | public SiegeRole Role { get; set; } = SiegeRole.Defender;
11 | public int BotTrapLevel { get; set; }
12 |
13 | public SiegeLoadout()
14 | {
15 | SlotItems = [Gear.MendingRoot, Gear.MagesClover, Trap.Barricade, Trap.ViscousTar, Trap.FlipTrap, Trap.WallBlades, Trap.ArrowWall, Trap.ConcussivePounder, Trap.CeilingBallista];
16 | Traits = new SiegeTrait[TRAIT_SLOT_COUNT];
17 | }
18 |
19 |
20 | public override string Encode()
21 | {
22 | var encodedString = new StringBuilder(EncodeBase());
23 |
24 | // Encode Role
25 | encodedString.Append(Role != null ? Base62Converter.Encode(Role.Id) : "0");
26 | encodedString.Append('-');
27 |
28 | // Encode SlotItems
29 | foreach (var slotItem in SlotItems)
30 | {
31 | encodedString.Append(slotItem != null ? Base62Converter.Encode(slotItem.Id, FORCE_LENGTH_TWO) : "00");
32 | }
33 | encodedString.Append('-');
34 |
35 | // Encode Waves
36 | for (int i = 0; i < WAVES_SLOT_COUNT; i++)
37 | {
38 | Wave wave = Waves[i];
39 | encodedString.Append(wave != null ? Base62Converter.Encode(wave.Id) : "0");
40 | }
41 | encodedString.Append('-');
42 |
43 | // Encode Traits
44 | foreach (var trait in Traits)
45 | {
46 | encodedString.Append(trait != null ? Base62Converter.Encode(trait.Id) : "0");
47 | }
48 |
49 | return encodedString.ToString();
50 | }
51 |
52 | public override BaseLoadout Decode(string encodedString)
53 | {
54 | DecodeBase(encodedString);
55 | var code = encodedString.Split('-');
56 | int codeIdx = 2;
57 |
58 | // Decode Role
59 | var role = code[codeIdx++];
60 | Role = SiegeRole.GetById(Base62Converter.Decode(role));
61 |
62 | // Decode SlotItems
63 | var slotItemParts = code[codeIdx++];
64 | SlotItems = new SlotItem[SLOT_ITEMS_COUNT];
65 | for (int i = 0; i < SLOT_ITEMS_COUNT; i++)
66 | {
67 | var slotItem = slotItemParts.Substring(i * 2, 2);
68 | SlotItems[i] = slotItem != "00" ? SlotItem.GetById(Base62Converter.Decode(slotItem)) : null; // Forced to be 2 chars
69 | }
70 |
71 | // Decode Waves
72 | var waveParts = code[codeIdx++];
73 | Waves = new Wave[WAVES_SLOT_COUNT];
74 | for (int i = 0; i < WAVES_SLOT_COUNT; i++)
75 | {
76 | var wave = waveParts.Substring(i, 1);
77 | Waves[i] = wave != "0" ? Wave.GetById(Base62Converter.Decode(wave)) : null;
78 | }
79 |
80 | // Decode Traits
81 | var traitParts = code[codeIdx++];
82 | Traits = new SiegeTrait[TRAIT_SLOT_COUNT];
83 | for (int i = 0; i < TRAIT_SLOT_COUNT; i++)
84 | {
85 | var trait = traitParts.Substring(i, 1);
86 | Traits[i] = trait != "0" ? SiegeTrait.GetById(Base62Converter.Decode(trait)) : null;
87 | }
88 |
89 | return this;
90 | }
91 | }
92 | }
93 |
94 |
95 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Model/SlotItem.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace SingleplayerLauncher.Model
5 | {
6 | public abstract class SlotItem : LoadoutItem
7 | {
8 | public string ItemTemplateName { get; protected set; }
9 |
10 | public static Dictionary SlotItemsById = [];
11 |
12 | public static SlotItem GetById(int id)
13 | {
14 | SlotItemsById = Trap.TrapsById
15 | .Concat(Gear.GearsById)
16 | .ToDictionary(pair => pair.Key, pair => pair.Value);
17 |
18 | if (SlotItemsById.TryGetValue(id, out var slotItem))
19 | {
20 | return slotItem;
21 | }
22 | return new Trap { Id = 0, ItemTemplateName = "Unknown", Description = "Unknown" };
23 | }
24 |
25 | public static Dictionary GetAllSlotItems()
26 | {
27 | return new List>() { Gear.Gears, Trap.Traps }
28 | .SelectMany(dict => dict)
29 | .ToDictionary(pair => pair.Key, pair => pair.Value);
30 | }
31 |
32 | public static Dictionary GetAllSiegeSlotItems()
33 | {
34 | return new List>() { Gear.Gears, Trap.SiegeTraps }
35 | .SelectMany(dict => dict)
36 | .ToDictionary(pair => pair.Key, pair => pair.Value);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Model/SurvivalLoadout.cs:
--------------------------------------------------------------------------------
1 | using SingleplayerLauncher.Utils;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace SingleplayerLauncher.Model
6 | {
7 | public class SurvivalLoadout : BaseLoadout
8 | {
9 | public const int GUARDIAN_SLOT_COUNT = 2;
10 | public const int CONSUMABLE_SLOT_COUNT = 2;
11 | public const int TRAP_PART_SLOT_COUNT = 3;
12 |
13 | public SurvivalLoadout()
14 | {
15 | SlotItems = new SlotItem[SLOT_ITEMS_COUNT] { Gear.MendingRoot, Gear.MagesClover, Trap.Barricade, Trap.ViscousTar, Trap.FlipTrap, Trap.WallBlades, Trap.ArrowWall, Trap.ConcussivePounder, Trap.CeilingBallista
16 | };
17 | }
18 |
19 | public Guardian[] Guardians { get; set; } = new Guardian[GUARDIAN_SLOT_COUNT] { Guardian.DragonGuardian, Guardian.MoonGuardian };
20 |
21 | public Consumable[] Consumables { get; set; } = new Consumable[CONSUMABLE_SLOT_COUNT] { Consumable.LuckPotion, Consumable.UnchainedScroll };
22 |
23 | public TrapPart[,] TrapParts { get; set; } = new TrapPart[SLOT_ITEMS_COUNT, TRAP_PART_SLOT_COUNT];
24 |
25 | public TrapPart[] GetTrapPartsForLoadout(int loadoutSlotIdx)
26 | {
27 | return Enumerable.Range(0, TRAP_PART_SLOT_COUNT)
28 | .Select(c => TrapParts[loadoutSlotIdx, c])
29 | .ToArray();
30 | }
31 |
32 | public override string Encode()
33 | {
34 | var encodedString = new StringBuilder(EncodeBase());
35 |
36 | // Encode SlotItems
37 | foreach (var slotItem in SlotItems)
38 | {
39 | encodedString.Append(slotItem != null ? Base62Converter.Encode(slotItem.Id, FORCE_LENGTH_TWO) : "00");
40 | }
41 | encodedString.Append("-");
42 |
43 | // Encode Guardians
44 | foreach (var guardian in Guardians)
45 | {
46 | encodedString.Append(guardian != null ? Base62Converter.Encode(guardian.Id) : "0");
47 | }
48 | encodedString.Append("-");
49 |
50 | // Encode Consumables
51 | foreach (var consumable in Consumables)
52 | {
53 | encodedString.Append(consumable != null ? Base62Converter.Encode(consumable.Id) : "0");
54 | }
55 | encodedString.Append("-");
56 |
57 | // Encode Traits
58 | foreach (var trait in Traits)
59 | {
60 | encodedString.Append(trait != null ? Base62Converter.Encode(trait.Id) : "0");
61 | }
62 | encodedString.Append("-");
63 |
64 | // Encode TrapParts
65 | for (int i = 0; i < SLOT_ITEMS_COUNT; i++)
66 | {
67 | for (int j = 0; j < TRAP_PART_SLOT_COUNT; j++)
68 | {
69 | var trapPart = TrapParts[i, j];
70 | encodedString.Append(trapPart != null ? Base62Converter.Encode(trapPart.Id) : "0");
71 | }
72 | }
73 |
74 | return encodedString.ToString();
75 | }
76 |
77 | public override BaseLoadout Decode(string encodedString)
78 | {
79 | DecodeBase(encodedString);
80 | var code = encodedString.Split('-');
81 | int codeIdx = 2;
82 |
83 | // Decode SlotItems
84 | var slotItemParts = code[codeIdx++];
85 | SlotItems = new SlotItem[SLOT_ITEMS_COUNT];
86 | for (int i = 0; i < SLOT_ITEMS_COUNT; i++)
87 | {
88 | var slotItem = slotItemParts.Substring(i * 2, 2);
89 | SlotItems[i] = slotItem != "00" ? SlotItem.GetById(Base62Converter.Decode(slotItem)) : null; // Forced to be 2 chars
90 | }
91 |
92 | // Decode Guardians
93 | var guardianParts = code[codeIdx++];
94 | Guardians = new Guardian[GUARDIAN_SLOT_COUNT];
95 | for (int i = 0; i < GUARDIAN_SLOT_COUNT; i++)
96 | {
97 | var guardian = guardianParts.Substring(i, 1);
98 | Guardians[i] = guardian != "0" ? Guardian.GetById(Base62Converter.Decode(guardian)) : null;
99 | }
100 |
101 | // Decode Consumables
102 | var consumableParts = code[codeIdx++];
103 | Consumables = new Consumable[CONSUMABLE_SLOT_COUNT];
104 | for (int i = 0; i < CONSUMABLE_SLOT_COUNT; i++)
105 | {
106 | var consumable = consumableParts.Substring(i, 1);
107 | Consumables[i] = consumable != "0" ? Consumable.GetById(Base62Converter.Decode(consumable)) : null;
108 | }
109 |
110 | // Decode Traits
111 | var traitParts = code[codeIdx++];
112 | Traits = new SurvivalTrait[TRAIT_SLOT_COUNT];
113 | for (int i = 0; i < TRAIT_SLOT_COUNT; i++)
114 | {
115 | var trait = traitParts.Substring(i, 1);
116 | Traits[i] = trait != "0" ? SurvivalTrait.GetById(Base62Converter.Decode(trait)) : null;
117 | }
118 |
119 | // Decode TrapParts
120 | var trapPartParts = code[codeIdx];
121 | TrapParts = new TrapPart[SLOT_ITEMS_COUNT, TRAP_PART_SLOT_COUNT];
122 | int trapPartIndex = 0;
123 | for (int i = 0; i < SLOT_ITEMS_COUNT; i++)
124 | {
125 | for (int j = 0; j < TRAP_PART_SLOT_COUNT; j++)
126 | {
127 | var trapPartChar = trapPartParts.Substring(trapPartIndex++, 1);
128 | TrapParts[i, j] = trapPartChar != "0" ? TrapPart.GetById(Base62Converter.Decode(trapPartChar)) : null;
129 | }
130 | }
131 |
132 | return this;
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/AccountLevelOverride.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class AccountLevelOverride : Mod
4 | {
5 | public override bool InstallMod()
6 | {
7 | return true;
8 | }
9 |
10 | public override bool UninstallMod()
11 | {
12 | return true;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/AdditionalheroWeapon.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class AdditionalHeroWeapon : Mod
4 | {
5 | public new string Value { get; set; }
6 |
7 | public override bool InstallMod()
8 | {
9 | return true;
10 | }
11 |
12 | public override bool UninstallMod()
13 | {
14 | return true;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/EnhancedTrapRotation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SingleplayerLauncher.Mods
4 | {
5 | public class EnhancedTrapRotation : Mod
6 | {
7 | readonly byte[] DEGREES_90_IN_BYTES = BitConverter.GetBytes(90);
8 | readonly byte[] DEGREES_10_IN_BYTES = BitConverter.GetBytes(10); // Actually seems to work as 22.5
9 |
10 | private const int CHANGE_INDEX = 0x1544729;
11 |
12 | readonly private byte[] UNIQUE_BYTES_REFERENCE = { 0x00, 0x00, 0x00, 0x00, 0xD6, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x42, 0x58, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
13 | readonly private int OFFSET_FROM_UNIQUE_BYTES = 0;
14 |
15 | public override bool InstallMod()
16 | {
17 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, DEGREES_10_IN_BYTES);
18 | return true;
19 | }
20 |
21 | public override bool UninstallMod()
22 | {
23 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, DEGREES_90_IN_BYTES);
24 | return true;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/GodMode.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class GodMode : Mod
4 | {
5 | public override bool InstallMod()
6 | {
7 | return true;
8 | }
9 |
10 | public override bool UninstallMod()
11 | {
12 | return true;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/Hardcore.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class Hardcore : Mod
4 | {
5 | readonly private byte[] UNIQUE_BYTES_REFERENCE = { 0x12, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6 | readonly private int OFFSET_FROM_UNIQUE_BYTES = 0;
7 |
8 | private const int CHANGE_INDEX = 0x1102F88;
9 |
10 | public override bool InstallMod()
11 | {
12 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 1);
13 | return true;
14 | }
15 |
16 | public override bool UninstallMod()
17 | {
18 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 0);
19 | return true;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/InvincibleBarricades.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class InvincibleBarricades : Mod
4 | {
5 | readonly private byte[] UNIQUE_BYTES_REFERENCE = { 0x00, 0x00, 0x00, 0x00, 0x6E, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xCB, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6 | readonly private int OFFSET_FROM_UNIQUE_BYTES = 0;
7 |
8 | private const int CHANGE_INDEX = 0x184631A; // ( CB 15 00 00 ): CanBeDamaged
9 |
10 | public override bool InstallMod()
11 | {
12 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 0);
13 | return true;
14 | }
15 |
16 | public override bool UninstallMod()
17 | {
18 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 1);
19 | return true;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/Mod.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public abstract class Mod
4 | {
5 | public string Name { get; private set; }
6 |
7 | internal static UPKFile UPKFile { get; set; }
8 |
9 | public bool IsEnabled { get; set; } = false;
10 |
11 | public int Value { get; set; } = 0;
12 |
13 | public abstract bool InstallMod();
14 | public abstract bool UninstallMod();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/Mods.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using SingleplayerLauncher.Names;
3 |
4 | namespace SingleplayerLauncher.Mods
5 | {
6 | static class Mods
7 | {
8 | // static members
9 | public static Mod NoTrapCap = new NoTrapCap();
10 | public static Mod NoLimitUniqueTraps = new NoLimitUniqueTraps();
11 | public static Mod InvincibleBarricades = new InvincibleBarricades();
12 | public static Mod TrapsAnySurface = new TrapsAnySurface();
13 | public static Mod TrapsInTraps = new TrapsInTraps();
14 | public static Mod NoTrapGrid = new NoTrapGrid();
15 | public static Mod GodMode = new GodMode();
16 | public static Mod ShowTrapDamageFlyoffs = new ShowTrapDamageFlyoffs();
17 | public static Mod Hardcore = new Hardcore();
18 | public static Mod EnhancedTrapRotation = new EnhancedTrapRotation();
19 | public static Mod SellTrapsAnytime = new SellTrapsAnytime();
20 | public static Mod StartingCoinOverride = new StartingCoinOverride();
21 | public static Mod AccountLevelOverride = new AccountLevelOverride();
22 | public static Mod TrapTierOverride = new TrapTierOverride();
23 | public static AdditionalHeroWeapon AdditionalHeroWeapon = new();
24 |
25 | public readonly static List ModList =
26 | [
27 | NoTrapCap,
28 | NoLimitUniqueTraps,
29 | InvincibleBarricades,
30 | TrapsAnySurface,
31 | TrapsInTraps,
32 | NoTrapGrid,
33 | GodMode,
34 | ShowTrapDamageFlyoffs,
35 | Hardcore,
36 | EnhancedTrapRotation,
37 | SellTrapsAnytime,
38 | StartingCoinOverride,
39 | AccountLevelOverride,
40 | TrapTierOverride,
41 | AdditionalHeroWeapon
42 | ];
43 |
44 | public readonly static Dictionary ModsByName = new()
45 | {
46 | { Names.Mod.NO_TRAP_CAP, NoTrapCap },
47 | { Names.Mod.NO_LIMIT_UNIQUE_TRAPS, NoLimitUniqueTraps },
48 | { Names.Mod.INVINCIBLE_BARRICADES, InvincibleBarricades },
49 | { Names.Mod.TRAPS_ANY_SURFACE, TrapsAnySurface },
50 | { Names.Mod.TRAPS_IN_TRAPS, TrapsInTraps },
51 | { Names.Mod.NO_TRAP_GRID, NoTrapGrid },
52 | { Names.Mod.GOD_MODE, GodMode },
53 | { Names.Mod.SHOW_TRAP_DAMAGE_FLYOFFS, ShowTrapDamageFlyoffs },
54 | { Names.Mod.HARDCORE, Hardcore },
55 | { Names.Mod.ENHANCED_TRAP_ROTATION, EnhancedTrapRotation },
56 | { Names.Mod.SELL_TRAPS_ANYTIME, SellTrapsAnytime },
57 | { Names.Mod.STARTING_COIN_OVERRIDE, StartingCoinOverride },
58 | { Names.Mod.ACCOUNT_LEVEL_OVERRIDE, AccountLevelOverride },
59 | { Names.Mod.TRAP_TIER_OVERRIDE, TrapTierOverride },
60 | { Names.Mod.ADDITIONAL_HERO_WEAPON, AdditionalHeroWeapon }
61 | };
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/NoLimitUniqueTraps.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class NoLimitUniqueTraps : Mod
4 | {
5 | readonly private byte[] UNIQUE_BYTES_REFERENCE = { 0x37, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0xC4, 0x00, 0x00, 0x37, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7, 0xC4, 0x00, 0x00, 0x5F, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6 | readonly private int OFFSET_FROM_UNIQUE_BYTES = 0;
7 |
8 | private const int CHANGE_INDEX = 0x16157FE; // ( 5F B0 00 00 ): UseCountLimiter
9 |
10 | public override bool InstallMod()
11 | {
12 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 0);
13 | return true;
14 | }
15 |
16 | public override bool UninstallMod()
17 | {
18 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 1);
19 | return true;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/NoTrapCap.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class NoTrapCap : Mod
4 | {
5 | readonly private byte[] UNIQUE_BYTES_REFERENCE = { 0x00, 0x00, 0x00, 0x6B, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xEE, 0x00, 0x00, 0x35, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
6 | readonly private int OFFSET_FROM_UNIQUE_BYTES = 74;
7 |
8 | private const int CHANGE_INDEX = 0x15444A3;
9 |
10 | public override bool InstallMod()
11 | {
12 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 0);
13 | return true;
14 | }
15 |
16 | public override bool UninstallMod()
17 | {
18 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 1);
19 | return true;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/NoTrapGrid.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class NoTrapGrid : Mod
4 | {
5 | readonly private byte[] UNIQUE_BYTES_REFERENCE = { 0x00, 0x00, 0x00, 0x6B, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xEE, 0x00, 0x00, 0x35, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
6 | readonly private int OFFSET_FROM_UNIQUE_BYTES = 49;
7 |
8 | private const int CHANGE_INDEX = 0x154448A;
9 |
10 | public override bool InstallMod()
11 | {
12 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 0);
13 | return true;
14 | }
15 |
16 | public override bool UninstallMod()
17 | {
18 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 1);
19 | return true;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/SellTrapsAnytime.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class SellTrapsAnytime : Mod
4 | {
5 | readonly private byte[] UNIQUE_BYTES_REFERENCE = { 0x00, 0x04, 0x27, 0x07, 0x33, 0x02, 0x9A, 0x38, 0x3A, 0x1A, 0x38, 0x3A, 0x1B, 0x79, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x19, 0x00, 0xED, 0x34, 0x00, 0x00, 0x09, 0x00, 0xB2, 0x9D, 0x00, 0x00, 0x00, 0x01, 0xB2, 0x9D, 0x00, 0x00, 0x38, 0x3A, 0x24, 0x01, 0x16, 0x04, 0x27, 0x04 };
6 | readonly private int OFFSET_FROM_UNIQUE_BYTES = 0;
7 |
8 | private const int CHANGE_INDEX = 0x0F172B8;
9 | public override bool InstallMod()
10 | {
11 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 39);
12 | return true;
13 | }
14 |
15 | public override bool UninstallMod()
16 | {
17 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 40);
18 | return true;
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/ShowTrapDamageFlyoffs.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class ShowTrapDamageFlyoffs : Mod
4 | {
5 | public override bool InstallMod()
6 | {
7 | return true;
8 | }
9 |
10 | public override bool UninstallMod()
11 | {
12 | return true;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/StartingCoinOverride.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class StartingCoinOverride : Mod
4 | {
5 | public override bool InstallMod()
6 | {
7 | return true;
8 | }
9 |
10 | public override bool UninstallMod()
11 | {
12 | return true;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/TrapTierOverride.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class TrapTierOverride : Mod
4 | {
5 | public override bool InstallMod()
6 | {
7 | return true;
8 | }
9 |
10 | public override bool UninstallMod()
11 | {
12 | return true;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/TrapsAnySurface.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class TrapsAnySurface : Mod
4 | {
5 | readonly private byte[] UNIQUE_BYTES_REFERENCE_1 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xDF, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
6 | readonly private byte[] UNIQUE_BYTES_REFERENCE_2 = { 0x00, 0x00, 0x00, 0x6B, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xEE, 0x00, 0x00, 0x35, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
7 | readonly private byte[] UNIQUE_BYTES_REFERENCE_3 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
8 |
9 | readonly private int OFFSET_FROM_UNIQUE_BYTES_1 = 0;
10 | readonly private int OFFSET_FROM_UNIQUE_BYTES_2 = 0;
11 | readonly private int OFFSET_FROM_UNIQUE_BYTES_3 = 0;
12 |
13 |
14 | private const int CHANGE_INDEX_1 = 0x154453A;
15 | private const int CHANGE_INDEX_2 = 0x1544459;
16 | private const int CHANGE_INDEX_3 = 0x1544D37;
17 |
18 | public override bool InstallMod()
19 | {
20 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE_1, CHANGE_INDEX_1, OFFSET_FROM_UNIQUE_BYTES_1, new byte[] { 0xF1, 0x6F });
21 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE_2, CHANGE_INDEX_2, OFFSET_FROM_UNIQUE_BYTES_2, new byte[] { 0xFA, 0x6F });
22 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE_3, CHANGE_INDEX_3, OFFSET_FROM_UNIQUE_BYTES_3, new byte[] { 0x16, 0x70 });
23 |
24 | return true;
25 | }
26 |
27 | public override bool UninstallMod()
28 | {
29 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE_1, CHANGE_INDEX_1, OFFSET_FROM_UNIQUE_BYTES_1, new byte[] { 0x32, 0x65 });
30 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE_2, CHANGE_INDEX_2, OFFSET_FROM_UNIQUE_BYTES_2, new byte[] { 0x3B, 0x57 });
31 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE_3, CHANGE_INDEX_3, OFFSET_FROM_UNIQUE_BYTES_3, new byte[] { 0xE4, 0x0F });
32 |
33 | return true;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Mods/TrapsInTraps.cs:
--------------------------------------------------------------------------------
1 | namespace SingleplayerLauncher.Mods
2 | {
3 | public class TrapsInTraps : Mod
4 | {
5 | readonly private byte[] UNIQUE_BYTES_REFERENCE = { 0x00, 0x00, 0x00, 0x6B, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xEE, 0x00, 0x00, 0x35, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
6 | readonly private int OFFSET_FROM_UNIQUE_BYTES = 99;
7 |
8 | private const int CHANGE_INDEX = 0x15444BC;
9 |
10 | public override bool InstallMod()
11 | {
12 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 0);
13 | return true;
14 | }
15 |
16 | public override bool UninstallMod()
17 | {
18 | UPKFile.ApplyModification(UNIQUE_BYTES_REFERENCE, CHANGE_INDEX, OFFSET_FROM_UNIQUE_BYTES, 1);
19 | return true;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/ProjectRechained.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Version 17
3 | VisualStudioVersion = 17.10.35013.160
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectRechainedLauncher", "ProjectRechainedLauncher.csproj", "{5902F0C2-713E-4BF0-8037-5094AF55CEEB}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|Any CPU = Debug|Any CPU
10 | Release|Any CPU = Release|Any CPU
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {5902F0C2-713E-4BF0-8037-5094AF55CEEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
14 | {5902F0C2-713E-4BF0-8037-5094AF55CEEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
15 | {5902F0C2-713E-4BF0-8037-5094AF55CEEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | {5902F0C2-713E-4BF0-8037-5094AF55CEEB}.Release|Any CPU.Build.0 = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(SolutionProperties) = preSolution
19 | HideSolutionNode = FALSE
20 | EndGlobalSection
21 | GlobalSection(ExtensibilityGlobals) = postSolution
22 | SolutionGuid = {CDA9A273-8962-4D45-865D-05A895A7E77D}
23 | EndGlobalSection
24 | EndGlobal
25 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/ProjectRechainedLauncher.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows
4 | WinExe
5 | ProjectRechained
6 | false
7 | publish\
8 | true
9 | Disk
10 | false
11 | Foreground
12 | 7
13 | Days
14 | false
15 | false
16 | true
17 | 0
18 | 1.0.0.%2a
19 | false
20 | true
21 | false
22 | true
23 | true
24 | omdu-U1_512.png
25 | omdu-U1_512.ico
26 |
27 |
28 | true
29 | bin\Debug Siege\
30 |
31 |
32 |
33 | False
34 | .NET Framework 3.5 SP1
35 | true
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | True
49 | True
50 | Settings.settings
51 |
52 |
53 |
54 |
55 | SettingsSingleFileGenerator
56 | Settings.Designer.cs
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ProjectRechained.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ProjectRechained.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ProjectRechained.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 |
26 | [global::System.Configuration.UserScopedSettingAttribute()]
27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
28 | [global::System.Configuration.DefaultSettingValueAttribute("True")]
29 | public bool FirstRun {
30 | get {
31 | return ((bool)(this["FirstRun"]));
32 | }
33 | set {
34 | this["FirstRun"] = value;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | True
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Utils/Base62Converter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SingleplayerLauncher.Utils
4 | {
5 | ///
6 | /// Provides methods for encoding integers to Base62 strings and decoding Base62 strings to integers.
7 | ///
8 | public static class Base62Converter
9 | {
10 | ///
11 | /// The character set used for Base62 encoding, consisting of digits, uppercase letters, and lowercase letters.
12 | ///
13 | private const string Base62Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
14 |
15 | ///
16 | /// Encodes an integer value to a Base62 string.
17 | ///
18 | /// The integer value to encode. Must be between 0 and 3843, inclusive.
19 | /// If true, always returns a two-character string. If false, returns a one-character string for values less than 62.
20 | /// A Base62 encoded string representation of the input value.
21 | /// Thrown when the input value is less than 0 or greater than or equal to 3844.
22 | public static string Encode(int value, bool forceLengthTwo = false)
23 | {
24 | if (value < 0 || value >= 3844) // 62 * 62 = 3844
25 | {
26 | throw new ArgumentOutOfRangeException(nameof(value), "Value must be in the range 0-3843.");
27 | }
28 |
29 | if (value < 62 && !forceLengthTwo)
30 | {
31 | return Base62Chars[value].ToString();
32 | }
33 |
34 | int high = value / 62;
35 | int low = value % 62;
36 | return $"{Base62Chars[high]}{Base62Chars[low]}";
37 | }
38 |
39 | ///
40 | /// Decodes a Base62 encoded string to its integer value.
41 | ///
42 | /// The Base62 encoded string to decode. Must be either one or two characters long.
43 | /// The integer value represented by the Base62 encoded string.
44 | /// Thrown when the input string length is not 1 or 2.
45 | public static int Decode(string str)
46 | {
47 | if (str.Length == 1)
48 | {
49 | return Base62Chars.IndexOf(str[0]);
50 | }
51 | else if (str.Length == 2)
52 | {
53 | int high = Base62Chars.IndexOf(str[0]);
54 | int low = Base62Chars.IndexOf(str[1]);
55 | return high * 62 + low;
56 | }
57 | else
58 | {
59 | throw new ArgumentException("Invalid Base62 encoded string length.", nameof(str));
60 | }
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/SingleplayerLauncher/Utils/Comment.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace SingleplayerLauncher.Utils
3 | {
4 | public class Comment : IniElement
5 | {
6 | public Comment(string rawText) : base(rawText)
7 | {
8 | }
9 | }
10 |
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Utils/Entry.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace SingleplayerLauncher.Utils
3 | {
4 | public class Entry : IniElement
5 | {
6 | public string Key { get; private set; }
7 | public string Value { get; private set; }
8 |
9 | public Entry(string line) : base(line)
10 | {
11 | var index = line.IndexOf('=');
12 | if (index >= 0)
13 | {
14 | Key = line.Substring(0, index).Trim();
15 | Value = line.Substring(index + 1).Trim();
16 | }
17 | else
18 | {
19 | Key = line.Trim();
20 | Value = string.Empty;
21 | }
22 | }
23 |
24 | public void UpdateValue(string newValue)
25 | {
26 | Value = newValue;
27 | RawText = $"{Key}={Value}";
28 | }
29 | }
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Utils/IniElement.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace SingleplayerLauncher.Utils
3 | {
4 | public abstract class IniElement
5 | {
6 | public string RawText { get; set; }
7 |
8 | protected IniElement(string rawText)
9 | {
10 | RawText = rawText;
11 | }
12 |
13 | public override string ToString()
14 | {
15 | return RawText;
16 | }
17 | }
18 |
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Utils/IniFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 |
6 | namespace SingleplayerLauncher.Utils
7 | {
8 |
9 | public class IniFile
10 | {
11 | public Dictionary Sections { get; private set; }
12 |
13 | public IniFile()
14 | {
15 | Sections = [];
16 | }
17 |
18 | public void Load(string filePath)
19 | {
20 | var lines = File.ReadAllLines(filePath);
21 | Section currentSection = null;
22 |
23 | foreach (var line in lines)
24 | {
25 | var trimmedLine = line.Trim();
26 | if (trimmedLine.Length == 0)
27 | {
28 | continue;
29 | }
30 |
31 | if (trimmedLine.StartsWith(";"))
32 | {
33 | // Comment line
34 | var comment = new Comment(line);
35 | if (currentSection != null)
36 | {
37 | currentSection.AddElement(comment);
38 | }
39 | else
40 | {
41 | // Add comment outside of any section
42 | var sectionKey = Guid.NewGuid().ToString();
43 | Sections[sectionKey] = new Section(null, line);
44 | Sections[sectionKey].AddElement(comment);
45 | }
46 | }
47 | else if (trimmedLine.StartsWith("[") && trimmedLine.EndsWith("]"))
48 | {
49 | // New section
50 | var sectionName = trimmedLine.Substring(1, trimmedLine.Length - 2);
51 | currentSection = new Section(sectionName, line);
52 | Sections[sectionName] = currentSection;
53 | }
54 | else
55 | {
56 | // Entry in section
57 | var entry = new Entry(line);
58 | if (currentSection != null)
59 | {
60 | currentSection.AddElement(entry);
61 | }
62 | else
63 | {
64 | // Add entry outside of any section
65 | var sectionKey = Guid.NewGuid().ToString();
66 | Sections[sectionKey] = new Section(null, line);
67 | Sections[sectionKey].AddElement(entry);
68 | }
69 | }
70 | }
71 | }
72 |
73 | public void Save(string filePath)
74 | {
75 | var sb = new StringBuilder();
76 |
77 | foreach (var section in Sections.Values)
78 | {
79 | if (section.Name != null)
80 | {
81 | sb.AppendLine($"[{section.Name}]");
82 | }
83 |
84 | foreach (var element in section.Elements)
85 | {
86 | sb.AppendLine(element.ToString());
87 | }
88 |
89 | sb.AppendLine(); // Add an extra line between sections for readability
90 | }
91 |
92 | File.WriteAllText(filePath, sb.ToString());
93 | }
94 |
95 | public List FindEntries(string sectionName, string key)
96 | {
97 | if (Sections.TryGetValue(sectionName, out var section))
98 | {
99 | return section.FindEntries(key);
100 | }
101 | return null;
102 | }
103 |
104 | public void UpdateEntry(string sectionName, string key, string newValue, int index = 0)
105 | {
106 | if (!Sections.TryGetValue(sectionName, out var section))
107 | {
108 | section = new Section(sectionName, $"[{sectionName}]");
109 | Sections[sectionName] = section;
110 | }
111 |
112 | var entries = section.FindEntries(key);
113 | if (entries != null && index < entries.Count)
114 | {
115 | entries[index].UpdateValue(newValue);
116 | }
117 | else
118 | {
119 | var newEntry = new Entry($"{key}={newValue}");
120 | section.AddElement(newEntry);
121 | }
122 | }
123 |
124 | public void RemoveSection(string sectionName)
125 | {
126 | if (!Sections.TryGetValue(sectionName, out var section))
127 | {
128 | // Section not found; nothing to remove
129 | return;
130 | }
131 |
132 | Sections.Remove(sectionName);
133 | }
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/Utils/Section.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace SingleplayerLauncher.Utils
3 | {
4 | using System.Collections.Generic;
5 |
6 | public class Section : IniElement
7 | {
8 | public string Name { get; private set; }
9 | public List Elements { get; private set; }
10 | private readonly Dictionary> entries;
11 |
12 | public Section(string name, string rawText) : base(rawText)
13 | {
14 | Name = name;
15 | Elements = [];
16 | entries = [];
17 | }
18 |
19 | public void AddElement(IniElement element)
20 | {
21 | Elements.Add(element);
22 | if (element is Entry entry)
23 | {
24 | if (!entries.TryGetValue(entry.Key, out var entryList))
25 | {
26 | entryList = [];
27 | entries[entry.Key] = entryList;
28 | }
29 | entryList.Add(entry);
30 | }
31 | }
32 |
33 | public List FindEntries(string key)
34 | {
35 | if (entries.TryGetValue(key, out var entryList))
36 | {
37 | return entryList;
38 | }
39 | return [];
40 | }
41 | }
42 |
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | True
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/SingleplayerLauncher/omdu-U1_512.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TimeMaster18/Project-Rechained/07e60a4a377128522a41f00606ac2942bc3addde/SingleplayerLauncher/omdu-U1_512.ico
--------------------------------------------------------------------------------