diff --git a/README.md b/README.md index 0ae11b1..45b52ec 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This project is still in its early stages, much more will hopefully come soon :) ### Features: - Launch the game in offline mode, single player. - Play any map, survival or endless modes -- Play with Max +- Play with Maximilian, Gabriella, Smolder, Ivy, Hogarth and Bionka - Choose traps and gear - Choose your skin and your dye - Choose Guardians @@ -35,4 +35,3 @@ This project is still in its early stages, much more will hopefully come soon :) ### Known Problems - Hero HP doesn't scale as expected - UI of the launcher is lacking - diff --git a/SingleplayerLauncher/ConfigFile.cs b/SingleplayerLauncher/ConfigFile.cs index 0f4a9a7..46c77b7 100644 --- a/SingleplayerLauncher/ConfigFile.cs +++ b/SingleplayerLauncher/ConfigFile.cs @@ -1,13 +1,13 @@ -using System.IO; -using IniParser; +using IniParser; using IniParser.Model; +using System.IO; namespace SingleplayerLauncher { - class ConfigFile + internal class ConfigFile { - private FileIniDataParser parser; - private string path; // (filename too) + private readonly FileIniDataParser parser; + private readonly string path; // (filename too) public IniData data; //public Dictionary> configDict; diff --git a/SingleplayerLauncher/Hero.cs b/SingleplayerLauncher/Hero.cs index 52d8eb4..ec253c7 100644 --- a/SingleplayerLauncher/Hero.cs +++ b/SingleplayerLauncher/Hero.cs @@ -1,6 +1,6 @@ -using System; +using SingleplayerLauncher.Resources; +using System; using System.Linq; -using System.Windows.Forms; namespace SingleplayerLauncher { @@ -11,107 +11,65 @@ public sealed class Hero // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit /Singleton - static Hero() {} - private Hero() {} + static Hero() { } + private Hero() { } - public static Hero Instance - { - get - { - return instance; - } - } + public static Hero Instance => instance; public UPKFile UPKFile { get; set; } public string Skin { get; set; } public string[] Loadout { get; set; } // Could be it's own class if we get Guardians and Traits to work. public string[] Guardians { get; set; } + public string Name { get; set; } private const int LoadoutSlotByteSize = 4; private const int LoadoutSlotsNumber = 9; - // Where the actual array of the loadout starts is + 12 bytes from the loadout header. - // Array Size in bytes + Array (start?) index + Array number of elements (4 + 4 + 4 ) - private const int LoadoutOffsetFromHeader = 12; + // Where the actual array of the loadout starts is + 28 bytes from the start of the loadout header. + // Header + Field Type + Field Size in bytes + Array (start?) index + Array number of elements (8 + 8 + 4 + 4 + 4) + private const int LoadoutArraySizeOffset = 16; + private const int LoadoutArrayElementCountOffset = 24; + private const int LoadoutSlotsOffset = 28; + private const int LoadoutSectionLength = LoadoutSlotsOffset + 4 * LoadoutSlotsNumber; private const int GuardianSlotsNumber = 2; - // Where the actual array of the guardians starts is + 12 bytes from the loadout header. - // Array Size in bytes + Array (start?) index + Array number of elements (4 + 4 + 4 ) - private const int GuardiansOffsetFromHeader = 12; - - // TODO Add resource file with the rest of heroes and remove this (and use above ones only, the rest to resource files) - private const string NameMaximilian = "Maximilian"; - private const int HeroObjectOffsetMaximillian = 0x28AB495; // Within the file offset where it starts (Maximillian currently default) - private const int HeroObjectSizeMaximillian = 1542; - // private const string SpitfireGameUPKMaximilian = "..//SpitfireGame//CookedPCConsole//SpitfireGame.upk"; - private static readonly byte[] LoadoutHeaderMaximilian = new byte[] { 0xC6, 0x2C, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xC5, 0x07, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - private static readonly byte[] GuardiansHeaderMaximillian = new byte[] { 0xBA, 0x2C, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xC5, 0x07, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - private static readonly int DefaultWaveClassesSectionLength = 64; - private static readonly byte[] DefaultWaveClassesHeaderMaximillian = new byte[] { - 0xF3, 0x2c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - private static readonly int HeroDamageTypeSectionLength = 40; - private static readonly byte[] HeroDamageTypeHeaderMaximillian = new byte[] { - 0x0E, 0x46, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - private static readonly int StrategicRoleSectionLength = 40; - private static readonly byte[] StrategicRoleHeaderMaximillian = new byte[] { - 0x74, 0xA3, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - private static readonly int DefaultRoleClassSectionLength = 28; - private static readonly byte[] DefaultRoleClassHeaderMaximillian = new byte[] { - 0xE4, 0x2C, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - - - private static readonly byte[] SkinPatternMaximilian = new byte[] { 0x52, 0x1E, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x6B, 0x66, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - private static readonly byte[] IconToRemoveFromFileBytes = new byte[] { 0x30, 0x6E, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x6B, 0x66, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, - 0x41, 0xCC, 0x02, 0x00 - }; - - private static readonly byte[] WeaverTreeDefaultHeaderMaximillian = new byte[] { 0x00, 0xB4, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - private static readonly byte[] StartHeaderAfterGuardiansMaximillian = WeaverTreeDefaultHeaderMaximillian; - + // Where the actual array of the guardians starts is + 28 bytes from the start of the guardians header. + // Header + Field type + Field Size in bytes + Array (start?) index + Array number of elements (8 + 8 + 4 + 4 + 4) + private const int GuardiansArraySizeOffset = 16; + private const int GuardiansArrayElementCountOffset = 24; + private const int GuardiansOffset = 28; + + + // Header + Field type + Size in bytes + (start?) index (8 + 8 + 4 + 4) + private const int SkinOffsetFromHeader = 24; + + private const string nameMaximilian = "Maximilian"; + + + private static readonly byte[] StartHeaderAfterGuardians = SpitfireGameUPK.HeroObjectWeaverTreeDefaultHeader; + public void ApplyLoadoutChanges() { - RemoveByteSection(DefaultWaveClassesHeaderMaximillian, DefaultWaveClassesSectionLength); - RemoveByteSection(HeroDamageTypeHeaderMaximillian, HeroDamageTypeSectionLength); - RemoveByteSection(StrategicRoleHeaderMaximillian, StrategicRoleSectionLength); - RemoveByteSection(DefaultRoleClassHeaderMaximillian, DefaultRoleClassSectionLength); + RemoveByteSection(SpitfireGameUPK.HeroObjectDefaultRoleClassHeader, SpitfireGameUPK.HeroObjectDefaultRoleClassSectionLength); + RemoveByteSection(SpitfireGameUPK.HeroObjectStrategicRoleHeader, SpitfireGameUPK.HeroObjectStrategicRoleSectionLength); + RemoveByteSection(SpitfireGameUPK.HeroObjectmAIResponsiveBehaviorsHeader, SpitfireGameUPK.HeroObjectmAIResponsiveBehaviorsSectionLength); + RemoveByteSection(SpitfireGameUPK.HeroObjectActionManagerHeader, SpitfireGameUPK.HeroObjectActionManagerSectionLength); + RemoveByteSection(SpitfireGameUPK.HeroObjectNetRelComponentHeader, SpitfireGameUPK.HeroObjectNetRelComponentSectionLength); + + if (!Name.Equals(nameMaximilian)) // Maximilian has already part of the bytes we have to insert for the rest. Extra bytes would need to code the size of extra space going past 255, using more than 1 byte for size of guardians. + { + RemoveByteSection(SpitfireGameUPK.HeroObjectBeginStealthClientSegmentHeader, SpitfireGameUPK.HeroObjectBeginStealthClientSegmentSectionLength); + RemoveByteSection(SpitfireGameUPK.HeroObjectEndStealthClientSegmentHeader, SpitfireGameUPK.HeroObjectEndStealthClientSegmentSectionLength); + RemoveByteSection(SpitfireGameUPK.HeroObjectTeamStealthSegmentHeader, SpitfireGameUPK.HeroObjectTeamStealthSegmentSectionLength); + } ApplyTrapsGear(); //ApplyTraits(); ApplySkin(); - // FillRemovedBytes should be run after all the removing if (UPKFile.nBytesRemoved > 0) { - int positionToFillRemovedBytesWithZeros = UPKFile.FindBytesKMP(StartHeaderAfterGuardiansMaximillian, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian); + int positionToFillRemovedBytesWithZeros = UPKFile.FindBytesKMP(StartHeaderAfterGuardians, SpitfireGameUPK.HeroObjects[Name].Offset, SpitfireGameUPK.HeroObjects[Name].Size); FillRemovedBytes(positionToFillRemovedBytesWithZeros); } @@ -120,26 +78,42 @@ public void ApplyLoadoutChanges() private void ApplyTrapsGear() { - if (Loadout == null || Loadout.Length != 9) + if (Loadout == null || Loadout.Length != LoadoutSlotsNumber) throw new Exception("9 traps/gear must be used"); - int startIndex = UPKFile.FindBytesKMP(LoadoutHeaderMaximilian, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian) + LoadoutHeaderMaximilian.Length; - int arrayElementCountIndex = startIndex + 8; - int arraySizeIndex = startIndex; + int startIndexLoadout = UPKFile.FindBytesKMP(SpitfireGameUPK.HeroObjectLoadoutHeader, SpitfireGameUPK.HeroObjects[Name].Offset, SpitfireGameUPK.HeroObjects[Name].Size - UPKFile.nBytesRemoved); + + if (startIndexLoadout == -1) + { + // Get position after Archetype and Add Header and Field Type + startIndexLoadout = UPKFile.FindBytesKMP(SpitfireGameUPK.HeroObjectDefaultInventoryArchetypesHeader, SpitfireGameUPK.HeroObjects[Name].Offset, SpitfireGameUPK.HeroObjects[Name].Size) + SpitfireGameUPK.HeroObjectDefaultInventoryArchetypesSectionLength; + + UPKFile.InsertZeroedBytes(startIndexLoadout, LoadoutSectionLength); - // There aren't 9 slots set up so we create them and insert necessary bytes - if (UPKFile.getByte(arrayElementCountIndex) != 9) + UPKFile.OverrideBytes(SpitfireGameUPK.HeroObjectLoadoutHeader, startIndexLoadout); + UPKFile.OverrideBytes(SpitfireGameUPK.HeroObjectLoadoutFieldType, startIndexLoadout + SpitfireGameUPK.HeroObjectLoadoutHeader.Length); + } + + int arraySizeIndex = startIndexLoadout + LoadoutArraySizeOffset; + int arrayElementCountIndex = startIndexLoadout + LoadoutArrayElementCountOffset; + int loadoutSlotsIndex = startIndexLoadout + LoadoutSlotsOffset; + + // IF there aren't 9 slots set up we create them and insert necessary bytes + int loadoutSlotsUsed = UPKFile.GetByte(arrayElementCountIndex); + if (loadoutSlotsUsed != LoadoutSlotsNumber) { - UPKFile.OverrideSingleByte((byte)(LoadoutSlotsNumber + 1) * LoadoutSlotByteSize, arraySizeIndex); // Array Size - UPKFile.OverrideSingleByte((byte)LoadoutSlotsNumber, arrayElementCountIndex); // Array Element Count ( the 4 bytes inbetween are "index 0") + UPKFile.OverrideSingleByte((LoadoutSlotsNumber + 1) * LoadoutSlotByteSize, arraySizeIndex); // Array Size (+1 to count the size field itself too) + UPKFile.OverrideSingleByte(LoadoutSlotsNumber, arrayElementCountIndex); // Array Element Count ( the 4 bytes inbetween are "index 0") + + // Add new slots (as many as missing) + int indexAfterUsedSlots = loadoutSlotsIndex + (loadoutSlotsUsed * LoadoutSlotByteSize); - // Add new slots (2 slots) - UPKFile.InsertZeroedBytes(startIndex + LoadoutOffsetFromHeader + 8, 2 * LoadoutSlotByteSize); + UPKFile.InsertZeroedBytes(indexAfterUsedSlots, (LoadoutSlotsNumber - loadoutSlotsUsed) * LoadoutSlotByteSize); } // Convert and apply Loadout byte[] loadoutBytes = ConvertLoadoutToBytes(Loadout); - UPKFile.OverrideBytes(loadoutBytes, startIndex + LoadoutOffsetFromHeader); + UPKFile.OverrideBytes(loadoutBytes, loadoutSlotsIndex); } private void ApplyGuardians() @@ -147,40 +121,54 @@ private void ApplyGuardians() if (Guardians == null || Guardians.Length != 2) throw new Exception("2 guardians must be used"); - int startIndex = UPKFile.FindBytesKMP(GuardiansHeaderMaximillian, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian) + GuardiansHeaderMaximillian.Length; - int endIndex = UPKFile.FindBytesKMP(StartHeaderAfterGuardiansMaximillian, startIndex + GuardiansOffsetFromHeader, HeroObjectSizeMaximillian); - int totalSize = endIndex - startIndex; // Everything after header - - byte[] firstGuardian = Resources.guardians[Guardians[0]].First; // Extra space after each guardian is already included - byte[] secondGuardian = Resources.guardians[Guardians[1]].First; + int startIndexGuardians = UPKFile.FindBytesKMP(SpitfireGameUPK.HeroObjectGuardiansHeader, SpitfireGameUPK.HeroObjects[Name].Offset, SpitfireGameUPK.HeroObjects[Name].Size); - byte[] sizeFirstGuardian = new byte[] {Resources.guardians[Guardians[0]].Second, 0x00, 0x00, 0x00 }; // Add the 0x00 to complete the 4 bytes field + if (startIndexGuardians == -1) + { + // Get position after Loadout and Add Header and Field Type + startIndexGuardians = UPKFile.FindBytesKMP(SpitfireGameUPK.HeroObjectLoadoutHeader, SpitfireGameUPK.HeroObjects[Name].Offset, SpitfireGameUPK.HeroObjects[Name].Size) + LoadoutSectionLength; + + UPKFile.OverrideBytes(SpitfireGameUPK.HeroObjectGuardiansHeader, startIndexGuardians); + UPKFile.OverrideBytes(SpitfireGameUPK.HeroObjectGuardiansFieldType, startIndexGuardians + SpitfireGameUPK.HeroObjectGuardiansHeader.Length); + } - int secondGuardianOffset = firstGuardian.Length + sizeFirstGuardian.Length + GuardiansOffsetFromHeader + 4; // 4 from second guardian size itself - byte[] sizeSecondGuardian = new byte[] {(byte) (totalSize - secondGuardianOffset), 0x00, 0x00, 0x00 }; // Counting extra space + int endIndex = UPKFile.FindBytesKMP(StartHeaderAfterGuardians, startIndexGuardians + GuardiansOffset, SpitfireGameUPK.HeroObjects[Name].Size); + int totalSize = endIndex - (startIndexGuardians + GuardiansArrayElementCountOffset); // Everything after header - int emptySpaceOffset = secondGuardianOffset + Resources.guardians[Guardians[1]].Second; - byte[] emptySpace = Enumerable.Repeat((byte)0x00, totalSize - emptySpaceOffset).ToArray(); + int guardiansArraySizeIndex = startIndexGuardians + GuardiansArraySizeOffset; + int guardiansArrayElementCountIndex = startIndexGuardians + GuardiansArrayElementCountOffset; + int guardiansIndex = startIndexGuardians + GuardiansOffset; + byte[] firstGuardian = Resources.Loadout.Guardians[Guardians[0]].First; // Extra space after each guardian is already included + byte[] secondGuardian = Resources.Loadout.Guardians[Guardians[1]].First; + + byte[] sizeFirstGuardian = new byte[] { Resources.Loadout.Guardians[Guardians[0]].Second, 0x00, 0x00, 0x00 }; // Add the 0x00 to complete the 4 bytes field + + int secondGuardianOffset = firstGuardian.Length + sizeFirstGuardian.Length + GuardiansOffset + 4; // 4 from second guardian size itself + byte[] sizeSecondGuardian = new byte[] { (byte)(totalSize - secondGuardianOffset), 0x00, 0x00, 0x00 }; // Counting extra space + + int emptySpaceOffset = secondGuardianOffset + Resources.Loadout.Guardians[Guardians[1]].Second; + + byte[] emptySpace = Enumerable.Repeat((byte)0x00, totalSize - emptySpaceOffset).ToArray(); // Combining arrays to SizeGuardian1 + Guardian1 + SizeGuardian2 + Guardian2 + emptySpace byte[] guardiansBytes = sizeFirstGuardian.Concat(firstGuardian).Concat(sizeSecondGuardian).Concat(secondGuardian).Concat(emptySpace).ToArray(); - UPKFile.OverrideBytes(guardiansBytes, startIndex + GuardiansOffsetFromHeader); + UPKFile.OverrideBytes(guardiansBytes, guardiansIndex); - UPKFile.OverrideSingleByte((byte) (totalSize - 8), startIndex); // Size (counts array element count and both guardians and their sizes but not itself or index so -8) + UPKFile.OverrideSingleByte((byte)totalSize, guardiansArraySizeIndex); // Size (counts array element count and both guardians and their sizes but not itself or index so -8) //TODO check single byte and avoid override? - UPKFile.OverrideSingleByte(GuardianSlotsNumber, startIndex + 8); // Array Element Count + UPKFile.OverrideSingleByte(GuardianSlotsNumber, guardiansArrayElementCountIndex); // Array Element Count } - + private void ApplySkin() { - int skinIndex = UPKFile.FindBytesKMP(SkinPatternMaximilian, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian) + SkinPatternMaximilian.Length; - UPKFile.OverrideBytes(Resources.skins[NameMaximilian][Skin], skinIndex); + int startIndexSkin = UPKFile.FindBytesKMP(SpitfireGameUPK.HeroObjectCurrentSkinClassHeader, SpitfireGameUPK.HeroObjects[Name].Offset, SpitfireGameUPK.HeroObjects[Name].Size); + UPKFile.OverrideBytes(GameInfo.Heroes[Name].GetSkinHex(Skin), startIndexSkin + SkinOffsetFromHeader); } private void RemoveByteSection(byte[] sectionHeaderByteArray, int length) { - int removeIndex = UPKFile.FindBytesKMP(sectionHeaderByteArray, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian); + int removeIndex = UPKFile.FindBytesKMP(sectionHeaderByteArray, SpitfireGameUPK.HeroObjects[Name].Offset, SpitfireGameUPK.HeroObjects[Name].Size); if (removeIndex != -1) { @@ -189,7 +177,7 @@ private void RemoveByteSection(byte[] sectionHeaderByteArray, int length) } private void FillRemovedBytes(int insertIndex) - { + { UPKFile.InsertZeroedBytes(insertIndex, 0); } @@ -201,13 +189,13 @@ private byte[] ConvertLoadoutToBytes(string[] loadout) { byte[] slotBytes; - if (Resources.traps.ContainsKey(loadout[i])) + if (Resources.Loadout.Traps.ContainsKey(loadout[i])) { - slotBytes = Resources.traps[loadout[i]]; + slotBytes = Resources.Loadout.Traps[loadout[i]]; } else { - slotBytes = Resources.gear[loadout[i]]; + slotBytes = Resources.Loadout.Gear[loadout[i]]; } for (int j = 0; j < LoadoutSlotByteSize; j++) @@ -218,6 +206,6 @@ private byte[] ConvertLoadoutToBytes(string[] loadout) return loadoutBytes; } - + } } diff --git a/SingleplayerLauncher/LauncherMainForm.Designer.cs b/SingleplayerLauncher/LauncherMainForm.Designer.cs index 036b59e..aee3160 100644 --- a/SingleplayerLauncher/LauncherMainForm.Designer.cs +++ b/SingleplayerLauncher/LauncherMainForm.Designer.cs @@ -173,7 +173,6 @@ private void InitializeComponent() this.chkLog.TabIndex = 6; this.chkLog.Text = "Log"; this.chkLog.UseVisualStyleBackColor = true; - this.chkLog.CheckedChanged += new System.EventHandler(this.ChkLog_CheckedChanged); // // btnLoadoutEditor // diff --git a/SingleplayerLauncher/LauncherMainForm.cs b/SingleplayerLauncher/LauncherMainForm.cs index 8137ba7..8939a6e 100644 --- a/SingleplayerLauncher/LauncherMainForm.cs +++ b/SingleplayerLauncher/LauncherMainForm.cs @@ -1,5 +1,7 @@ -using Newtonsoft.Json.Linq; +using IniParser.Model; +using Newtonsoft.Json.Linq; using SingleplayerLauncher.Mods; +using SingleplayerLauncher.Resources; using System; using System.Collections.Generic; using System.Diagnostics; @@ -28,16 +30,7 @@ public partial class LauncherMainForm : Form private const string spitfireGameUPKDecompressedPath = ".//UE Extractor//unpacked//SpitfireGame.upk"; private const string loggingArgument = " -log -ABSLOG=log.txt"; - private const string defaultArguments = " -seekfreeloadingpcconsole -writepid -Language=INT -Region=us"; - private const string defaultSelectedHero = "Maximilian"; // Default selected Hero "Maximillian" Main Hero of the OrcsMustDie! Saga - private const string defaultSelectedSkin = "Default"; // Default selected skin - private const string defaultSelectedDye = "Normal"; // Default selected Normal dye - - private const string defaultSelectedMap = "The Baths"; // Default Selected "The Baths" because it's well optimised and Iconic Level - private const string defaultSelectedGameMode = "Survival"; // Default selected Game Mode "Survival" - - private const bool defaultCustomIniSetting = true; private const string valueTrue = "TRUE"; private const string RCharacterDataSection = "RCharacterData_0 RCharacterData"; @@ -47,16 +40,6 @@ public partial class LauncherMainForm : Form private const string characterDataKeyDye = "HeroicDyeIdx"; private const string characterDataKeyBonusStartingCoin = "BonusStartingCoin"; - private readonly Dictionary defaultCharacterDataSection = new Dictionary - { - { "PlayerName", "Savitar" }, - { "GuildTag", "~(^-^)~" }, - { "GuildName", "ComboCalypse" }, - { "PawnTemplateName", "PawnWeapon_Warmage.Pawn_Warmage" }, - { "Team" , "1" }, - { "HeroicDyeIdx", "Normal" }, - { "GodMode", "FALSE" } - }; private const string RGameReplicationInfoSection = "SpitfireGame.RGameReplicationInfo"; @@ -69,17 +52,9 @@ public partial class LauncherMainForm : Form private const string gameModeEndless = "Endless"; private const string noExtraDifficulty = "No"; - private readonly Dictionary defaultGameReplicationInfoSection = new Dictionary - { - { "DefaultOfflineDifficulty", "1" }, - { "PlayerCountOverride", "1" }, - { "DefaultOfflineSuggestedLevel", "1" }, - { "DefaultOfflinePlayerLevel", "1" }, - { "DefaultOfflinePauseTimerDurationInSeconds", "999999" } // Can be applied only once - }; private const string RDisplayColorInfoSection = "SpitfireGame.RDisplayColorInfo"; - + private readonly Hero hero = Hero.Instance; @@ -89,7 +64,7 @@ public LauncherMainForm() { FirstRunInitialization(); } - InitializeComponent(); + InitializeComponent(); } private void FirstRunInitialization() @@ -98,23 +73,23 @@ private void FirstRunInitialization() CreateBackup(characterDataIniFileName, characterDataIniPath); ConfigFile characterDataConfigFile = new ConfigFile(characterDataIniPath, true); - var characterData = characterDataConfigFile.data; + IniData characterData = characterDataConfigFile.data; characterData.Sections.AddSection(RCharacterDataSection); - foreach (KeyValuePair entry in defaultCharacterDataSection) + foreach (KeyValuePair entry in DefaultValues.CharacterDataSection) characterData[RCharacterDataSection].AddKey(entry.Key, entry.Value); characterDataConfigFile.Write(characterData); // DefaultGame.ini Initialization CreateBackup(defaultGameIniFileName, defaultGameIniPath); - + ConfigFile defaultGame = new ConfigFile(defaultGameIniPath); - var defaultGameData = defaultGame.data; + IniData defaultGameData = defaultGame.data; - foreach (KeyValuePair entry in defaultGameReplicationInfoSection) - defaultGameData[RGameReplicationInfoSection][entry.Key] = entry.Value; + foreach (KeyValuePair entry in DefaultValues.GameReplicationInfoSection) + defaultGameData[RGameReplicationInfoSection][entry.Key] = entry.Value; defaultGameData.Sections.RemoveSection(RDisplayColorInfoSection); @@ -130,10 +105,12 @@ private void FirstRunInitialization() File.Copy(spitfireGameUPKPath, spitfireGameUPKDecompressPath); // Decompress - ProcessStartInfo psi = new ProcessStartInfo(); - psi.FileName = Path.GetFileName(decompressorPath); - psi.WorkingDirectory = Path.GetDirectoryName(decompressorPath); - psi.Arguments = "\"" + Path.GetFileName(spitfireGameUPKDecompressPath) + "\""; + ProcessStartInfo psi = new ProcessStartInfo + { + FileName = Path.GetFileName(decompressorPath), + WorkingDirectory = Path.GetDirectoryName(decompressorPath), + Arguments = "\"" + Path.GetFileName(spitfireGameUPKDecompressPath) + "\"" + }; Process process = Process.Start(psi); process.WaitForExit(); File.Delete(spitfireGameUPKPath); @@ -158,31 +135,31 @@ private void CreateBackup(string fileName, string path) private void Form1_Load(object sender, EventArgs e) { - foreach (string m in Resources.maps.Keys) + foreach (string m in GameInfo.Maps.Keys) comBoxMap.Items.Add(m); - foreach (string h in Resources.heroes.Keys) + foreach (string h in GameInfo.Heroes.Keys) comBoxHero.Items.Add(h); - foreach (string d in Resources.dyes.Keys) + foreach (string d in IniConfig.Dyes.Keys) comBoxDye.Items.Add(d); - foreach (string gm in Resources.gameModes.Keys) + foreach (string gm in IniConfig.GameModes.Keys) comBoxGameMode.Items.Add(gm); - foreach (string s in Resources.skins[defaultSelectedHero].Keys) + foreach (string s in GameInfo.Heroes[DefaultValues.SelectedHero].Skins.Keys) comBoxSkin.Items.Add(s); - comBoxHero.SelectedItem = defaultSelectedHero; - comBoxSkin.SelectedItem = defaultSelectedSkin; - comBoxDye.SelectedItem = defaultSelectedDye; + comBoxHero.SelectedItem = DefaultValues.SelectedHero; + comBoxSkin.SelectedItem = DefaultValues.SelectedSkin; + comBoxDye.SelectedItem = DefaultValues.SelectedDye; - comBoxMap.SelectedItem = defaultSelectedMap; - comBoxGameMode.SelectedItem = defaultSelectedGameMode; + comBoxMap.SelectedItem = DefaultValues.SelectedMap; + comBoxGameMode.SelectedItem = DefaultValues.SelectedGameMode; - chkCustomIni.Checked = defaultCustomIniSetting; + chkCustomIni.Checked = DefaultValues.CustomIniSetting; - hero.Loadout = Resources.defaultLoadout; + hero.Loadout = DefaultValues.Loadout; if (Settings.Instance.ContainsKey("hero")) comBoxHero.SelectedItem = Settings.Instance["hero"]; if (Settings.Instance.ContainsKey("skin")) @@ -203,7 +180,7 @@ private void Form1_Load(object sender, EventArgs e) chkLog.Checked = (bool)Settings.Instance["log"]; if (Settings.Instance.ContainsKey("loadout")) { - var savedLoadOut = ((JArray)Settings.Instance["loadout"]).ToObject(); + string[] savedLoadOut = ((JArray)Settings.Instance["loadout"]).ToObject(); hero.Loadout = savedLoadOut; } } @@ -218,24 +195,25 @@ private void btnLaunch_Click(object sender, EventArgs e) UpdateCharacterDataIni(); UpdateDefaultGameIni(); } + if (comBoxSkin.SelectedItem != null) - { hero.Skin = comBoxSkin.SelectedItem.ToString(); - } - + + + if (comBoxHero.SelectedItem != null) + hero.Name = comBoxHero.SelectedItem.ToString(); + MessageBox.Show("Saving your changes. Please wait."); hero.ApplyLoadoutChanges(); - ApplyMods(spitfireGameUPKFile); - spitfireGameUPKFile.Save(); MessageBox.Show("Finished"); + SaveSettings(); StartGame(); } public void SaveSettings() { - Settings.Instance["hero"] = comBoxHero.SelectedItem; Settings.Instance["skin"] = comBoxSkin.SelectedItem; Settings.Instance["dye"] = comBoxDye.SelectedItem; @@ -258,7 +236,7 @@ private static void ApplyMods(UPKFile spitfireGameUPKFile) { ntp.UninstallMod(); } - + TrapsInTraps tit = new TrapsInTraps(spitfireGameUPKFile); if (Settings.Instance.ContainsKey("TrapsInTraps") && (bool)Settings.Instance["TrapsInTraps"]) { @@ -282,10 +260,10 @@ private void StartGame() private void UpdateCharacterDataIni() { ConfigFile characterData = new ConfigFile(characterDataIniPath); - var data = characterData.data; + IniData data = characterData.data; - data[RCharacterDataSection][characterDataKeyHero] = Resources.heroes[comBoxHero.Text]; - data[RCharacterDataSection][characterDataKeyDye] = Resources.dyes[comBoxDye.Text]; + data[RCharacterDataSection][characterDataKeyHero] = IniConfig.Heroes[comBoxHero.Text]; + data[RCharacterDataSection][characterDataKeyDye] = IniConfig.Dyes[comBoxDye.Text]; if (Settings.Instance.ContainsKey("GodMode") && (bool)Settings.Instance["GodMode"]) data[RCharacterDataSection][characterDataKeyGodMode] = valueTrue; @@ -312,12 +290,12 @@ private string calculateMultiplierStartingCoin(string mapName, int startingCoin) else { int baseStartingCoins = 9000; - if (Resources.startingCoin6000Maps.Contains(mapName)) + if (GameInfo.startingCoin6000Maps.Contains(mapName)) { baseStartingCoins = 6000; } - double startingCoinsMultiplier = (double) startingCoin / baseStartingCoins; + double startingCoinsMultiplier = (double)startingCoin / baseStartingCoins; // it's a multiplier, so it needs an offset of -1 startingCoinsMultiplier--; @@ -329,20 +307,17 @@ private string calculateMultiplierStartingCoin(string mapName, int startingCoin) private void UpdateDefaultGameIni() { ConfigFile defaultGame = new ConfigFile(defaultGameIniPath); - var data = defaultGame.data; + IniData data = defaultGame.data; string selectedGameMode = comBoxGameMode.SelectedItem.ToString(); string selectedExtraDifficulty = comBoxExtraDifficulty.SelectedItem.ToString(); - foreach (KeyValuePair entry in defaultGameReplicationInfoSection) // TODO improve default/set handling to prevent rewriting same key + foreach (KeyValuePair entry in Resources.DefaultValues.GameReplicationInfoSection) // TODO improve default/set handling to prevent rewriting same key data[RGameReplicationInfoSection][entry.Key] = entry.Value; - data[RGameReplicationInfoSection][GameReplicationInfoKeyGameMode] = Resources.gameModes[selectedGameMode]; - - bool extraDifficulty = false; - if (!selectedExtraDifficulty.Equals(noExtraDifficulty)) - extraDifficulty = true; + data[RGameReplicationInfoSection][GameReplicationInfoKeyGameMode] = IniConfig.GameModes[selectedGameMode]; + bool extraDifficulty = !selectedExtraDifficulty.Equals(noExtraDifficulty); if (selectedGameMode.Equals(gameModeSurvival)) { @@ -350,21 +325,21 @@ private void UpdateDefaultGameIni() if (extraDifficulty) { - data[RGameReplicationInfoSection][GameReplicationInfoKeyPlayerLevel] = Resources.survivalExtraDifficulties[selectedDifficulty][selectedExtraDifficulty][0].ToString(); - data[RGameReplicationInfoSection][GameReplicationInfoKeyMapLevel] = Resources.survivalExtraDifficulties[selectedDifficulty][selectedExtraDifficulty][1].ToString(); + data[RGameReplicationInfoSection][GameReplicationInfoKeyMapLevel] = IniConfig.survivalExtraDifficulties[selectedDifficulty][selectedExtraDifficulty][1].ToString(); + data[RGameReplicationInfoSection][GameReplicationInfoKeyPlayerLevel] = IniConfig.survivalExtraDifficulties[selectedDifficulty][selectedExtraDifficulty][0].ToString(); data[RGameReplicationInfoSection][GameReplicationInfoKeyPlayerCount] = "3"; } else { - data[RGameReplicationInfoSection][GameReplicationInfoKeyMapLevel] = Resources.survivalDifficulties[selectedDifficulty]; - data[RGameReplicationInfoSection][GameReplicationInfoKeyPlayerLevel] = Resources.survivalDifficulties[selectedDifficulty]; + data[RGameReplicationInfoSection][GameReplicationInfoKeyMapLevel] = IniConfig.survivalDifficulties[selectedDifficulty]; + data[RGameReplicationInfoSection][GameReplicationInfoKeyPlayerLevel] = IniConfig.survivalDifficulties[selectedDifficulty]; } - } + } else if (selectedGameMode.Equals(gameModeEndless)) { if (extraDifficulty) { - data[RGameReplicationInfoSection][GameReplicationInfoKeyMapLevel] = Resources.endlessDifficulties[selectedExtraDifficulty]; + data[RGameReplicationInfoSection][GameReplicationInfoKeyMapLevel] = IniConfig.endlessDifficulties[selectedExtraDifficulty]; data[RGameReplicationInfoSection][GameReplicationInfoKeyPlayerCount] = "3"; } } @@ -376,8 +351,8 @@ private string CreateExeArguments() { string arguments = ""; - string map = Resources.maps[comBoxMap.Text]; - string defaultArgs = defaultArguments; + string map = IniConfig.Maps[comBoxMap.Text]; + string defaultArgs = DefaultValues.ExeArguments; arguments += map; arguments += defaultArgs; @@ -427,7 +402,7 @@ private void comBoxGameMode_SelectedIndexChanged(object sender, EventArgs e) case gameModeEndless: comBoxDifficulty.Enabled = false; - foreach (string ed in Resources.endlessDifficulties.Keys) + foreach (string ed in IniConfig.endlessDifficulties.Keys) comBoxExtraDifficulty.Items.Add(ed); break; @@ -437,7 +412,7 @@ private void comBoxGameMode_SelectedIndexChanged(object sender, EventArgs e) string selectedMap = comBoxMap.SelectedItem.ToString(); - foreach (string md in Resources.mapSurvivalDifficulties[selectedMap]) + foreach (string md in GameInfo.Maps[selectedMap].SurvivalDifficulties) comBoxDifficulty.Items.Add(md); comBoxDifficulty.SelectedIndex = 0; @@ -465,11 +440,11 @@ private void comBoxMap_SelectedIndexChanged(object sender, EventArgs e) comBoxExtraDifficulty.Enabled = true; // Manual refresh of possible difficulties - var modeSelected = comBoxGameMode.SelectedItem; + object modeSelected = comBoxGameMode.SelectedItem; comBoxGameMode.SelectedIndex = -1; comBoxGameMode.SelectedItem = modeSelected; - if (Resources.startingCoin6000Maps.Contains(selectedMap)) + if (GameInfo.startingCoin6000Maps.Contains(selectedMap)) Settings.Instance["StartingCoin"] = "6000"; else Settings.Instance["StartingCoin"] = "9000"; @@ -486,7 +461,7 @@ private void comBoxDifficulty_SelectedIndexChanged(object sender, EventArgs e) string selectedDifficulty = comBoxDifficulty.SelectedItem.ToString(); - foreach (string ef in Resources.survivalExtraDifficulties[selectedDifficulty].Keys) + foreach (string ef in IniConfig.survivalExtraDifficulties[selectedDifficulty].Keys) { comBoxExtraDifficulty.Items.Add(ef); } @@ -495,9 +470,16 @@ private void comBoxDifficulty_SelectedIndexChanged(object sender, EventArgs e) // TODO to change when more heroes are playable private void comBoxHero_SelectedIndexChanged(object sender, EventArgs e) { - if (comBoxHero.SelectedItem.Equals(defaultSelectedHero)) + comBoxSkin.Items.Clear(); + string selectedHero = comBoxHero.SelectedItem.ToString(); + if (GameInfo.Heroes[selectedHero].Skins != null) { comBoxSkin.Enabled = true; + + foreach (string s in GameInfo.Heroes[selectedHero].Skins.Keys) + comBoxSkin.Items.Add(s); + + comBoxSkin.SelectedItem = DefaultValues.SelectedSkin; } else { @@ -520,11 +502,6 @@ private void btnMods_Click(object sender, EventArgs e) ModLoaderForm mlf = new ModLoaderForm(); mlf.Show(); } - - private void ChkLog_CheckedChanged(object sender, EventArgs e) - { - } } } - \ No newline at end of file diff --git a/SingleplayerLauncher/LoadoutEditorForm.cs b/SingleplayerLauncher/LoadoutEditorForm.cs index a10f45b..8b8d725 100644 --- a/SingleplayerLauncher/LoadoutEditorForm.cs +++ b/SingleplayerLauncher/LoadoutEditorForm.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using SingleplayerLauncher.Resources; using System; using System.Collections.Generic; using System.Linq; @@ -8,29 +8,16 @@ namespace SingleplayerLauncher { public partial class LoadoutEditorForm : Form { - readonly List comBoxLoadoutSlots; - readonly List comBoxGuardianSlots; + private readonly List comBoxLoadoutSlots; + private readonly List comBoxGuardianSlots; public static List bytes = new List(); private readonly Hero hero = Hero.Instance; private const int nLoadoutSlots = 9; private const int nGuardianSlots = 2; - //TODO move defaults to a resource file - readonly string[] defaultLoadout = - { - "Mending Root", "Mage's Clover", "Barricade", - "Viscous Tar", "Flip Trap", "Wall Blades", - "Arrow Wall", "Concussive Pounder", "Ceiling Ballista" - }; - - readonly string[] defaultGuardians = - { - "Dragon Guardian", "Serpent Guardian" - }; - public LoadoutEditorForm() - { + { InitializeComponent(); comBoxLoadoutSlots = new List() @@ -44,34 +31,34 @@ public LoadoutEditorForm() { comBoxGuardianSlot1, comBoxGuardianSlot2 }; - } + } private void LoadoutEditor_Load(object sender, EventArgs e) { - PopulateSlots(this.comBoxLoadoutSlots, Resources.traps.Keys.ToList()); - PopulateSlots(this.comBoxLoadoutSlots, Resources.gear.Keys.ToList()); - PopulateSlots(this.comBoxGuardianSlots, Resources.guardians.Keys.ToList()); + PopulateSlots(comBoxLoadoutSlots, Resources.Loadout.Traps.Keys.ToList()); + PopulateSlots(comBoxLoadoutSlots, Resources.Loadout.Gear.Keys.ToList()); + PopulateSlots(comBoxGuardianSlots, Resources.Loadout.Guardians.Keys.ToList()); // TODO implement a way of loading previous loadout used // Placeholder -> Default loadout - SetDefaultSlots(this.comBoxLoadoutSlots, this.defaultLoadout); - SetDefaultSlots(this.comBoxGuardianSlots, this.defaultGuardians); + SetDefaultSlots(comBoxLoadoutSlots, DefaultValues.Loadout); + SetDefaultSlots(comBoxGuardianSlots, DefaultValues.Guardians); } private void btnSave_Click(object sender, EventArgs e) { SetDefaultLoadoutInForm(); - Settings.Instance["loadout"] = hero.Loadout; + Settings.Instance["loadout"] = hero.Loadout; Settings.Save(); - this.Close(); + Close(); } private void SetDefaultLoadoutInForm() - { + { hero.Loadout = SaveLoadout(); hero.Guardians = SaveGuardians(); - this.Close(); + Close(); } private void PopulateSlots(List comBoxSlotList, List entryList) diff --git a/SingleplayerLauncher/ModLoaderForm.cs b/SingleplayerLauncher/ModLoaderForm.cs index fef421a..2e37674 100644 --- a/SingleplayerLauncher/ModLoaderForm.cs +++ b/SingleplayerLauncher/ModLoaderForm.cs @@ -1,10 +1,4 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; using System.Windows.Forms; namespace SingleplayerLauncher @@ -14,7 +8,7 @@ public partial class ModLoaderForm : Form public ModLoaderForm() { InitializeComponent(); - } + } private void ModLoaderForm_Load(object sender, EventArgs e) { diff --git a/SingleplayerLauncher/Mods/NoTrapCap.cs b/SingleplayerLauncher/Mods/NoTrapCap.cs index 3c6501a..a28477b 100644 --- a/SingleplayerLauncher/Mods/NoTrapCap.cs +++ b/SingleplayerLauncher/Mods/NoTrapCap.cs @@ -1,6 +1,6 @@ namespace SingleplayerLauncher.Mods { - public class NoTrapCap:Mod + public class NoTrapCap : Mod { public NoTrapCap(UPKFile UPKFile) : base(UPKFile) { diff --git a/SingleplayerLauncher/Program.cs b/SingleplayerLauncher/Program.cs index 55d2b11..16d225f 100644 --- a/SingleplayerLauncher/Program.cs +++ b/SingleplayerLauncher/Program.cs @@ -1,17 +1,15 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Windows.Forms; namespace SingleplayerLauncher { - static class Program + internal static class Program { /// /// The main entry point for the application. /// [STAThread] - static void Main() + private static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); diff --git a/SingleplayerLauncher/Properties/AssemblyInfo.cs b/SingleplayerLauncher/Properties/AssemblyInfo.cs index 1a0d5f9..0888ab4 100644 --- a/SingleplayerLauncher/Properties/AssemblyInfo.cs +++ b/SingleplayerLauncher/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/SingleplayerLauncher/Resources.cs b/SingleplayerLauncher/Resources.cs deleted file mode 100644 index 69b896c..0000000 --- a/SingleplayerLauncher/Resources.cs +++ /dev/null @@ -1,543 +0,0 @@ -using System.Collections.Generic; - -namespace SingleplayerLauncher -{ - public class Tuple - { - public T1 First { get; private set; } - public T2 Second { get; private set; } - internal Tuple(T1 first, T2 second) - { - First = first; - Second = second; - } - } - - public static class Tuple - { - public static Tuple New(T1 first, T2 second) - { - var tuple = new Tuple(first, second); - return tuple; - } - } - - internal static class Resources - { - //Initialize Dictionaries - - //Traps (Name, Hex) - public static Dictionary traps = new Dictionary - { - { "Acid Sprayer", new byte[] { 0x5A, 0xC4, 0x00, 0x00, } }, - { "Arcane Bowling Ball", new byte[] { 0x62, 0xC4, 0x00, 0x00 } }, - { "Arcane Phaser", new byte[] { 0x64, 0xC4, 0x00, 0x00 } }, - { "Arrow Wall", new byte[] { 0x66, 0xC4, 0x00, 0x00 } }, - { "BGH Arrow Wall", new byte[] { 0x68, 0xC4, 0x00, 0x00 } }, - { "Ceiling Ballista", new byte[] { 0x6A, 0xC4, 0x00, 0x00 } }, - { "Dragons Lance", new byte[] { 0x6C, 0xC4, 0x00, 0x00 } }, - { "BGH Ceiling Ballista", new byte[] { 0x6E, 0xC4, 0x00, 0x00 } }, - { "Barricade", new byte[] { 0x70, 0xC4, 0x00, 0x00 } }, - { "Great Wall Barricade", new byte[] { 0x72, 0xC4, 0x00, 0x00 } }, - { "Boom Barrel", new byte[] { 0x74, 0xC4, 0x00, 0x00 } }, - { "Boom Barrel Roller", new byte[] { 0x76, 0xC4, 0x00, 0x00 } }, - { "Boulder Chute", new byte[] { 0x78, 0xC4, 0x00, 0x00 } }, - { "Icicle Impaler", new byte[] { 0x7A, 0xC4, 0x00, 0x00 } }, - { "Brimstone", new byte[] { 0x7C, 0xC4, 0x00, 0x00 } }, - { "Coin Forge", new byte[] { 0x7E, 0xC4, 0x00, 0x00 } }, - { "Cursed Ground", new byte[] { 0x80, 0xC4, 0x00, 0x00 } }, - { "Decoy", new byte[] { 0x82, 0xC4, 0x00, 0x00 } }, - { "Spitfire Wall", new byte[] { 0x84, 0xC4, 0x00, 0x00 } }, - { "Fire Cracker", new byte[] { 0x86, 0xC4, 0x00, 0x00 } }, - { "Flip Trap", new byte[] { 0x88, 0xC4, 0x00, 0x00 } }, - { "Floor Scorcher", new byte[] { 0x8A, 0xC4, 0x00, 0x00 } }, - { "Temple Alarm Gong", new byte[] { 0x8C, 0xC4, 0x00, 0x00 } }, - { "Grinder", new byte[] { 0x8E, 0xC4, 0x00, 0x00 } }, - { "Quarter Pounder", new byte[] { 0x90, 0xC4, 0x00, 0x00 } }, - { "Haymaker", new byte[] { 0x92, 0xC4, 0x00, 0x00 } }, - { "Healing Well", new byte[] { 0x94, 0xC4, 0x00, 0x00 } }, - { "Ice Shard", new byte[] { 0x96, 0xC4, 0x00, 0x00 } }, - { "Ice Vent", new byte[] { 0x98, 0xC4, 0x00, 0x00 } }, - { "Lightning Rod", new byte[] { 0x9A, 0xC4, 0x00, 0x00 } }, - { "Mana Well", new byte[] { 0x9C, 0xC4, 0x00, 0x00 } }, - { "Summoner Trap", new byte[] { 0x9E, 0xC4, 0x00, 0x00 } }, - { "Naphtha Sprayer", new byte[] { 0xA0, 0xC4, 0x00, 0x00 } }, - { "Overload Trap", new byte[] { 0xA2, 0xC4, 0x00, 0x00 } }, - { "Powerup Damage", new byte[] { 0xA4, 0xC4, 0x00, 0x00 } }, - { "Pounder", new byte[] { 0xA6, 0xC4, 0x00, 0x00 } }, - { "Concussive Pounder", new byte[] { 0xA8, 0xC4, 0x00, 0x00 } }, - { "Power Generator", new byte[] { 0xAA, 0xC4, 0x00, 0x00 } }, - { "Projectile Shield", new byte[] { 0xAC, 0xC4, 0x00, 0x00 } }, - { "Push Trap", new byte[] { 0xAE, 0xC4, 0x00, 0x00 } }, - { "Saw Of Arctos", new byte[] { 0xB0, 0xC4, 0x00, 0x00 } }, - { "Shield Powerup", new byte[] { 0xB2, 0xC4, 0x00, 0x00 } }, - { "Speed Pad", new byte[] { 0xB4, 0xC4, 0x00, 0x00 } }, - { "Floor Spikes", new byte[] { 0xB6, 0xC4, 0x00, 0x00 } }, - { "Spike Wall", new byte[] { 0xB8, 0xC4, 0x00, 0x00 } }, - { "Steam Vent", new byte[] { 0xBA, 0xC4, 0x00, 0x00 } }, - { "Swinging Mace", new byte[] { 0xBC, 0xC4, 0x00, 0x00 } }, - { "Tar Trap", new byte[] { 0xBE, 0xC4, 0x00, 0x00 } }, - { "Viscous Tar", new byte[] { 0xC0, 0xC4, 0x00, 0x00 } }, - { "Shock Zapper", new byte[] { 0xC2, 0xC4, 0x00, 0x00 } }, - { "BGH Shock Zapper", new byte[] { 0xC4, 0xC4, 0x00, 0x00 } }, - { "Trip Wire", new byte[] { 0xC6, 0xC4, 0x00, 0x00 } }, - { "Wall Blades", new byte[] { 0xC8, 0xC4, 0x00, 0x00 } }, - { "Wall Charger", new byte[] { 0xCA, 0xC4, 0x00, 0x00 } }, - { "Web Spinner", new byte[] { 0xCC, 0xC4, 0x00, 0x00 } } - }; - - // Gear (Name, Hex) - public static Dictionary gear = new Dictionary - { - { "Freedom Trinket", new byte[] { 0xCE, 0xC4, 0x00, 0x00 } }, - { "Greater Freedom Trinket", new byte[] { 0xD0, 0xC4, 0x00, 0x00 } }, - { "Mending Root", new byte[] { 0xD2, 0xC4, 0x00, 0x00 } }, - { "Hobgoblin Charm", new byte[] { 0xD4, 0xC4, 0x00, 0x00 } }, - { "Ring of Last Stand", new byte[] { 0xD6, 0xC4, 0x00, 0x00 } }, - { "Mage's Picnic", new byte[] { 0xD8, 0xC4, 0x00, 0x00 } }, - { "Mage's Clover", new byte[] { 0xDA, 0xC4, 0x00, 0x00 } }, - { "Gnomish Repair Kit", new byte[] { 0xDC, 0xC4, 0x00, 0x00 } }, - { "Teleportation Ring", new byte[] { 0xDE, 0xC4, 0x00, 0x00 } }, - { "Arcane Bubble Blower", new byte[] { 0xE3, 0xC4, 0x00, 0x00 } }, - { "Fire Wall Bracers", new byte[] { 0xE5, 0xC4, 0x00, 0x00 } }, - { "Ice Amulet", new byte[] { 0xE7, 0xC4, 0x00, 0x00 } }, - { "Ring of Storms", new byte[] { 0xE9, 0xC4, 0x00, 0x00 } }, - { "Lightning Ring", new byte[] { 0xEB, 0xC4, 0x00, 0x00 } }, - { "AntiTrap Vambrace", new byte[] { 0x02, 0xC4, 0x00, 0x00 } } - }; - - // Guardians (Name (Hex String code, Length in Hex)) - public static Dictionary> guardians = new Dictionary> - { - { "Bartender Guardian", new Tuple - ( - new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x62, 0x61, 0x72, 0x6d, 0x61, 0x69, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x42, 0x61, 0x72, 0x6d, 0x61, 0x69, 0x64, 0x00 }, 47 - ) - }, - { "Blacksmith Guardian",new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x42, 0x65, 0x61, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x42, 0x65, 0x61, 0x72, 0x00 }, 33 - ) - }, - { "Cook Guardian", new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x6f, 0x6f, 0x6b, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x43, 0x6f, 0x6f, 0x6b, 0x00 }, 33 - ) - }, - { "Deckhand Guardian", new Tuple - ( - new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x64, 0x65, 0x63, 0x6b, 0x68, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x44, 0x65, 0x63, 0x6b, 0x68, 0x61, 0x6e, 0x64, 0x00 }, 49 - ) - }, - { "Dragon Guardian", new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x00 }, 37 - ) - }, - { "Friar Guardian", new Tuple - ( - new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x6f, 0x72, 0x63, 0x70, 0x72, 0x69, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4f, 0x72, 0x63, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74, 0x00 }, 51 - ) - }, - { "Headhunter Guardian", new Tuple - ( - new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x54, 0x72, 0x6f, 0x6c, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x00 }, 61 - ) - }, - { "Jade Empire", new Tuple - ( - new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x6a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x4a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x00 }, 59 - ) - }, - { "Jailer Guardian", new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x00 }, 45 - ) - }, - { "Lion Guardian", new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4c, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x4c, 0x69, 0x6f, 0x6e, 0x00 }, 33 - ) - }, - { "Moon Guardian", new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4d, 0x6f, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x4d, 0x6f, 0x6f, 0x6e, 0x00 }, 33 - ) - }, - { "Priest Guardian", new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74, 0x00 }, 45 - ) - }, - { "Quartermaster Guardian", new Tuple - ( - new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x63, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x00 }, 47 - ) - }, - { "Ranch Hand Guardian", new Tuple - ( - new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x6d, 0x69, 0x6e, 0x6f, 0x74, 0x61, 0x75, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x4d, 0x69, 0x6e, 0x6f, 0x74, 0x61, 0x75, 0x72, 0x00 }, 69 - ) - }, - { "Rumrudder Guardian", new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x63, 0x75, 0x72, 0x76, 0x79, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x63, 0x75, 0x72, 0x76, 0x79, 0x00 }, 45 - ) - }, - { "Serpent Guardian", new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x52, 0x61, 0x7a, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x52, 0x61, 0x7a, 0x65, 0x72, 0x00 }, 35 - ) - }, - { "Stablehand Guardian", new Tuple - ( - new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x00 }, 53 - ) - }, - { "Sun Guardian", new Tuple - ( - new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x75, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x53, 0x75, 0x6e, 0x00 }, 31 - ) - }, - { "WeaponWright Guardian", new Tuple - ( - new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x77, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x77, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x57, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x77, 0x72, 0x69, 0x67, 0x68, 0x74, 0x00 }, 57 - ) - } - }; - - // Initialize usable variables within CharactersData.ini - - // Heroes (Name, pawnweapon) - public static Dictionary heroes = new Dictionary - { - { "Bionka", "PawnWeapon_Bionka.Pawn_Bionka" }, - { "Blackpaw", "PawnWeapon_Blackpaw.Pawn_Blackpaw" }, - { "Bloodspike", "PawnWeapon_Bloodspike.Pawn_Bloodspike" }, - { "Brass", "PawnWeapon_Brass.Pawn_Brass" }, - { "Cygnus", "PawnWeapon_Cygnus.Pawn_Cygnus" }, - { "Deadeye", "PawnWeapon_Deadeye.Pawn_Deadeye" }, - { "Dobbin", "PawnWeapon_Dobbin.Pawn_Dobbin" }, - { "Gabriella", "PawnWeapon_Sorceress.Pawn_Sorceress" }, - { "Hogarth", "PawnWeapon_Hogarth.Pawn_Hogarth" }, - { "Ivy", "PawnWeapon_Ivy.Pawn_Ivy" }, - { "Maximilian", "PawnWeapon_Warmage.Pawn_Warmage" }, - { "Midnight", "PawnWeapon_Midnight.Pawn_Midnight" }, - { "Oziel", "PawnWeapon_Oziel.Pawn_Oziel" }, - { "Smolder", "PawnWeapon_Smolder.Pawn_Smolder" }, - { "Stinkeye", "PawnWeapon_Stinkeye.Pawn_Stinkeye" }, - { "Temper", "PawnWeapon_Temper.Pawn_Temper" }, - { "Tundra", "PawnWeapon_Tundra.Pawn_Tundra" }, - { "Yi-Lin", "PawnWeapon_hooksword.Pawn_hooksword" }, - { "Zoey", "PawnWeapon_Zoey.Pawn_Zoey" } - }; - - - // Maps { Name, umap } - public static Dictionary maps = new Dictionary - { - { "Academy Sewers", "PvE_Sewers.umap" }, - { "Archmage Library", "PvE_AcademyLibrary.umap" }, - { "Avalanche", "PvE_Avalanche.umap" }, - { "Banquet Hall", "PvE_BanquetHall.umap" }, - { "Castle Gates", "PvE_ASN_CastleGates.umap" }, - { "Cliffside Clash", "PvE_2Lane.umap" }, - { "Confluence", "PvE_AcademyCanals.umap" }, - { "Crogon Keep", "PvE_CrogonKeep.umap" }, - { "Docks at Eventide", "PvE_SUR_Pirates.umap" }, - { "Eventide Fortress", "PvE_Surrounded.umap" }, - { "Eventide Ramparts", "PvE_SUR_NorthernClans.umap" }, - { "Frostbite", "PvE_FrostBite.umap" }, - { "Gates of Thuricvod", "PvE_Corridors.umap" }, - { "Highlands", "PvE_Highlands.umap" }, - { "Maximum Security", "PvE_AcademyDungeon.umap" }, - { "Midnight Market", "PvE_ASN_NightMarket.umap" }, - { "Orcatraz", "PvE_Orcatraz.umap" }, - { "Orcri-La", "PvE_OrcVil_Temple.umap" }, - { "Restricted Section", "PvE_RestrictedSection.umap" }, - { "Riftmaker's Temple", "PvE_AcademyTemple.umap" }, - { "Shark Island", "PvE_SharkIsle.umap" }, - { "Stables at Eventide", "PvE_SUR_JungleTribe.umap" }, - { "Storm Drain", "PvE_Flushed.umap" }, - { "Temple Graveyard", "PvE_Mausoleum.umap" }, - { "The Baths", "PvE_Baths.umap" }, - { "The Falling Folly", "PvE_Towering.umap" }, - { "The Wall", "PvE_TheWall.umap" }, - { "Throne Room", "PvE_ThroneRoom.umap" }, - { "Thuricvod Village", "PvE_Gap.umap" }, - { "Training Grounds", "PvE_TrainingGrounds.umap" }, - { "Unchained Fortress", "PvE_OneWay.umap" }, - { "Water Garden", "PvE_ASN_WaterGarden.umap" }, - { "Prologue 1 (Grand Foyer)", "NPE_1.umap" }, - { "Prologue 2 (Archmage Library)", "NPE_2.umap" }, - { "Prologue 3 (Dungeon)", "NPE_3.umap" }, - { "Prologue 4 (Canals)", "NPE_4.umap" }, - { "Prologue 5 (Riftmaker's Temple)", "NPE_5.umap" }, - //{ "SpitfireFrontEndMap", "SpitfireFrontEndMap.umap" }, - { "Survival Tutorial", "TutorialSurvival.umap" }, - { "Basics Tutorial", "NewbieTutorial.umap" } - }; - - // GameModes { Name, DefaultOfflineDifficulty } (DefaultGame.ini) - public static Dictionary gameModes = new Dictionary - { - { "Survival", "1" }, - { "Endless", "5" } - //{ "Weekly Challenge", "" } - //{ "Chaos Trials", "" } - }; - - // Maps { Name, umap } - public static Dictionary> mapSurvivalDifficulties = new Dictionary> - { - { "Academy Sewers", new HashSet { "Rift Lord" } }, - { "Archmage Library", new HashSet {"Apprentice" } }, - { "Avalanche", new HashSet { "Master", "Rift Lord" } }, - { "Banquet Hall", new HashSet { "Apprentice", "War Mage", "Rift Lord" } }, - { "Castle Gates", new HashSet { "Master", "Rift Lord" } }, - { "Cliffside Clash", new HashSet { "Apprentice", "Master" } }, - { "Confluence", new HashSet { "Rift Lord" } }, - { "Crogon Keep", new HashSet { "War Mage", "Master", "Rift Lord" } }, - { "Docks at Eventide", new HashSet { "Master" } }, - { "Eventide Fortress", new HashSet { "Rift Lord" } }, - { "Eventide Ramparts", new HashSet {"Apprentice", "War Mage" } }, - { "Frostbite", new HashSet { "Master", "Rift Lord" } }, - { "Gates of Thuricvod", new HashSet { "War Mage", "Rift Lord" } }, - { "Highlands", new HashSet { "Apprentice", "War Mage", "Rift Lord" } }, - { "Maximum Security", new HashSet { "Rift Lord" } }, - { "Midnight Market", new HashSet { "War Mage", "Rift Lord" } }, - { "Orcatraz", new HashSet { "Master" } }, - { "Orcri-La", new HashSet { "Master" } }, - { "Restricted Section", new HashSet { "War Mage", "Rift Lord" } }, - { "Riftmaker's Temple", new HashSet { "Apprentice" } }, - { "Shark Island", new HashSet { "War Mage", "Master" } }, - { "Stables at Eventide", new HashSet { "War Mage", "Master" } }, - { "Storm Drain", new HashSet { "Master" } }, - { "Temple Graveyard", new HashSet { "War Mage", "Rift Lord" } }, - { "The Baths", new HashSet { "Apprentice", "Rift Lord" } }, - { "The Falling Folly", new HashSet { "Master" } }, - { "The Wall", new HashSet { "War Mage", "Master" } }, - { "Throne Room", new HashSet { "Apprentice", "War Mage", "Rift Lord" } }, - { "Thuricvod Village", new HashSet { "War Mage" } }, - { "Training Grounds", new HashSet { "Apprentice", "War Mage", "Master" } }, - { "Unchained Fortress", new HashSet { "Apprentice", "Master" } }, - { "Water Garden", new HashSet { "Apprentice", "Master" } } - }; - - // Difficulty { Name, DefaultOfflineDifficulty } (DefaultGame.ini) - public static Dictionary survivalDifficulties = new Dictionary - { - { "Apprentice", "1" }, - { "War Mage", "11" }, - { "Master", "26" }, - { "Rift Lord", "46" } - }; - - // Endless Difficulty { Name, DefaultOfflineDifficulty } (DefaultGame.ini) - public static Dictionary endlessDifficulties = new Dictionary - { - { "Endless+", "20" }, - { "Endless++", "40" }, - { "Endless+3", "60" }, - { "Endless+4", "80" }, - { "Endless+5", "100" }, - { "Endless+6", "120" }, - { "Endless+7", "140" }, - { "Endless+8", "160" }, - { "Endless+9", "180" }, - { "Endless+10", "200" } - }; - - // Survival Difficulty { Name, {DefaultOfflinePlayerLevel, DefaultOfflineDifficulty } (DefaultGame.ini) - public static Dictionary> survivalExtraDifficulties = new Dictionary> - { - { "Apprentice", new Dictionary - { - { "Apprentice+" , new int[] { 1, 10 } } - } - }, - { "War Mage", new Dictionary - { - { "War Mage+" , new int[] { 1, 25 } } - } - }, - { "Master", new Dictionary - { - { "Master+" , new int[] { 6, 26 } }, - { "Master++" , new int[] { 1, 45 } } - } - }, - { "Rift Lord", new Dictionary - { - { "Rift Lord+" , new int[] { 26, 46 } }, - { "Rift Lord++" , new int[] { 6, 46 } }, - { "Rift Lord+3" , new int[] { 1, 61 } }, - { "Rift Lord+4" , new int[] { 1, 75 } } - } - } - }; - - // Dyes (Name, IdxDye) - public static Dictionary dyes = new Dictionary - { - { "Normal", "0" }, - { "Heroic", "1" }, - { "Legendary", "2" } - }; - - - // Skins - public static Dictionary> skins = new Dictionary> - { - { "Maximilian", new Dictionary - { - { "Lucky Tunic" , new byte[] { 0x4A, 0xE0, 0x00, 0x00 } }, - { "Enchanted Armor" , new byte[] { 0x4C, 0xE0, 0x00, 0x00 } }, - { "Knight's Watch" , new byte[] { 0x4E, 0xE0, 0x00, 0x00 } }, - { "Default" , new byte[] { 0x50, 0xE0, 0x00, 0x00 } }, - { "Orc Slayer" , new byte[] { 0x52, 0xE0, 0x00, 0x00 } }, - { "Cardboard Samurai" , new byte[] { 0x54, 0xE0, 0x00, 0x00 } }, - { "Scared-Crow" , new byte[] { 0x56, 0xE0, 0x00, 0x00 } }, - { "Lion Heart" , new byte[] { 0x58, 0xE0, 0x00, 0x00 } }, - { "Robin Hood" , new byte[] { 0x5A, 0xE0, 0x00, 0x00 } }, - { "Paximillian" , new byte[] { 0x5C, 0xE0, 0x00, 0x00 } }, - { "Legendary (default)" , new byte[] { 0x5E, 0xE0, 0x00, 0x00 } }, - { "Champion of the Order" , new byte[] { 0x60, 0xE0, 0x00, 0x00 } }, - { "Jamez Ripher" , new byte[] { 0x62, 0xE0, 0x00, 0x00 } }, - { "Winter Warrior" , new byte[] { 0x64, 0xE0, 0x00, 0x00 } }, - { "Boomstick" , new byte[] { 0x66, 0xE0, 0x00, 0x00 } }, - { "Backdraft" , new byte[] { 0x68, 0xE0, 0x00, 0x00 } }, - { "Dragon Slayer" , new byte[] { 0x6A, 0xE0, 0x00, 0x00 } }, - { "Summer of Stunning" , new byte[] { 0x6C, 0xE0, 0x00, 0x00 } }, - { "Red Scarf (China Ad)" , new byte[] { 0x6E, 0xE0, 0x00, 0x00 } }, - { "Yellow Scarf (China Ad)" , new byte[] { 0x70, 0xE0, 0x00, 0x00 } }, - { "Blue Scarf (China Ad)" , new byte[] { 0x72, 0xE0, 0x00, 0x00 } } - } - }, - { "Bionka", new Dictionary - { - { "Default", new byte[] { 0x54, 0xDF, 0x00, 0x00 } }, - { "Lizard Queen", new byte[] { 0x56, 0xDF, 0x00, 0x00 } }, - { "Fluffalump", new byte[] { 0x58, 0xDF, 0x00, 0x00 } }, - { "Bionka Bunny", new byte[] { 0x5A, 0xDF, 0x00, 0x00 } } - } - }, - { "Brass", new Dictionary - { - { "Default", new byte[] { 0x7A, 0xDF, 0x00, 0x00 } }, - { "Guns, Gears, 'n Lace", new byte[] { 0x7C, 0xDF, 0x00, 0x00 } }, - { "Bombshell Battalion", new byte[] { 0x7E, 0xDF, 0x00, 0x00 } } - } - }, - { "Hogarth", new Dictionary - { - { "Enchanted Armor", new byte[] { 0x8C, 0xDF, 0x00, 0x00 } }, - { "God of Plunder", new byte[] { 0x8E, 0xDF, 0x00, 0x00 } }, - { "Default", new byte[] { 0x90, 0xDF, 0x00, 0x00 } }, - { "Lumbering Jack", new byte[] { 0x92, 0xDF, 0x00, 0x00 } }, - { "Default (with some gold)", new byte[] { 0x94, 0xDF, 0x00, 0x00 } }, - { "Ice Armor", new byte[] { 0x96, 0xDF, 0x00, 0x00 } }, - { "Imperial Golden Warrior", new byte[] { 0x98, 0xDF, 0x00, 0x00 } }, - { "Beached Bod", new byte[] { 0x9A, 0xDF, 0x00, 0x00 } }, - { "*crashed*", new byte[] { 0x9C, 0xDF, 0x00, 0x00 } }, - { "Black Thane", new byte[] { 0x9E, 0xDF, 0x00, 0x00 } }, - { "The Schling", new byte[] { 0xA0, 0xDF, 0x00, 0x00 } }, - { "Dragon Ward", new byte[] { 0xA2, 0xDF, 0x00, 0x00 } } - } - }, - { "Ivy", new Dictionary - { - { "Enchanted Armor", new byte[] { 0xDA, 0xDF, 0x00, 0x00 } }, - { "Valkyrie", new byte[] { 0xDC, 0xDF, 0x00, 0x00 } }, - { "Default", new byte[] { 0xDE, 0xDF, 0x00, 0x00 } }, - { "Flower Friend", new byte[] { 0xE0, 0xDF, 0x00, 0x00 } }, - { "Default (with some gold)", new byte[] { 0xE2, 0xDF, 0x00, 0x00 } }, - { "Imperial Ruby Archer", new byte[] { 0xE4, 0xDF, 0x00, 0x00 } }, - { "*crashed*", new byte[] { 0xE6, 0xDF, 0x00, 0x00 } }, - { "Wicked Warden", new byte[] { 0xE8, 0xDF, 0x00, 0x00 } }, - { "Grovewatch", new byte[] { 0xEA, 0xDF, 0x00, 0x00 } }, - { "Dragon Tamer", new byte[] { 0xEC, 0xDF, 0x00, 0x00 } } - } - }, - { "Smolder", new Dictionary - { - { "Default", new byte[] { 0xF2, 0xDF, 0x00, 0x00 } }, - { "Helter Swelter", new byte[] { 0xF4, 0xDF, 0x00, 0x00 } }, - { "Default (White hair)", new byte[] { 0xF6, 0xDF, 0x00, 0x00 } }, - { "Elite", new byte[] { 0xF8, 0xDF, 0x00, 0x00 } }, - { "Kill-auea", new byte[] { 0xFA, 0xDF, 0x00, 0x00 } }, - { "Fire-Alarm Femme", new byte[] { 0xFC, 0xDF, 0x00, 0x00 } }, - { "Firestarter", new byte[] { 0xFE, 0xDF, 0x00, 0x00 } }, - { "Wu Xing Dragon Mage", new byte[] { 0x00, 0xE0, 0x00, 0x00 } } - } - }, - { "Gabriella", new Dictionary - { - { "Violent Vintage", new byte[] { 0x02, 0xE0, 0x00, 0x00 } }, - { "Enchanted Armor", new byte[] { 0x04, 0xE0, 0x00, 0x00 } }, - { "Blood Queen", new byte[] { 0x06, 0xE0, 0x00, 0x00 } }, - { "Default", new byte[] { 0x08, 0xE0, 0x00, 0x00 } }, - { "Frightfully Delightful", new byte[] { 0x0A, 0xE0, 0x00, 0x00 } }, - { "Default (Red)", new byte[] { 0x0C, 0xE0, 0x00, 0x00 } }, - { "Life In Plastic", new byte[] { 0x0E, 0xE0, 0x00, 0x00 } }, - { "Archmage of the Order", new byte[] { 0x10, 0xE0, 0x00, 0x00 } }, - { "Mistress of Illusion", new byte[] { 0x12, 0xE0, 0x00, 0x00 } }, - { "Promising Prodigy", new byte[] { 0x14, 0xE0, 0x00, 0x00 } }, - { "I Dream of Gabby", new byte[] { 0x16, 0xE0, 0x00, 0x00 } }, - { "Miracle Worker", new byte[] { 0x18, 0xE0, 0x00, 0x00 } }, - { "Winter Witch", new byte[] { 0x1A, 0xE0, 0x00, 0x00 } }, - { "Skull Ninja", new byte[] { 0x1C, 0xE0, 0x00, 0x00 } }, - { "Evil Ways", new byte[] { 0x1E, 0xE0, 0x00, 0x00 } }, - { "Dragon Charmer", new byte[] { 0x20, 0xE0, 0x00, 0x00 } } - } - } - }; - - public static string[] defaultLoadout = - { - "Mending Root", "Mage's Clover", "Barricade", - "Viscous Tar", "Flip Trap", "Wall Blades", - "Arrow Wall", "Concussive Pounder", "Ceiling Ballista" - }; - - public static HashSet startingCoin9000Maps = new HashSet - { - "Academy Sewers", - "Archmage Library", - "Avalanche", - "Banquet Hall", - "Cliffside Clash", - "Confluence", - "Crogon Keep", - "Docks at Eventide", - "Eventide Fortress", - "Eventide Ramparts", - "Frostbite", - "Gates of Thuricvod", - "Highlands", - "Maximum Security", - "Orcatraz", - "Orcri-La", - "Restricted Section", - "Shark Island", - "Stables at Eventide", - "Storm Drain", - "Temple Graveyard", - "The Falling Folly", - "The Wall", - "Throne Room", - "Thuricvod Village", - "Training Grounds", - "Unchained Fortress", - }; - - public static HashSet startingCoin6000Maps = new HashSet - { - "Castle Gates", - "Midnight Market", - "Riftmaker's Temple", - "The Baths", - "Water Garden" - }; - } -} diff --git a/SingleplayerLauncher/Resources/DefaultValues.cs b/SingleplayerLauncher/Resources/DefaultValues.cs new file mode 100644 index 0000000..1f4a403 --- /dev/null +++ b/SingleplayerLauncher/Resources/DefaultValues.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; + +namespace SingleplayerLauncher.Resources +{ + internal class DefaultValues + { + // Game Running + public const string ExeArguments = " -seekfreeloadingpcconsole -writepid -Language=INT -Region=us"; + + // Form Defaults + public const string SelectedHero = "Maximilian"; // Default selected Hero "Maximilian" Main Hero of the OrcsMustDie! Saga + public const string SelectedSkin = "Default"; // Default selected skin + public const string SelectedDye = "Normal"; // Default selected Normal dye + + public const string SelectedMap = "The Baths"; // Default Selected "The Baths" because it's well optimised and Iconic Level + public const string SelectedGameMode = "Survival"; // Default selected Game Mode "Survival" + + public const bool CustomIniSetting = true; + + // Config Defaults + public static readonly Dictionary CharacterDataSection = new Dictionary + { + { "PlayerName", "Savitar" }, + { "GuildTag", "~(^-^)~" }, + { "GuildName", "ComboCalypse" }, + { "PawnTemplateName", "PawnWeapon_Warmage.Pawn_Warmage" }, + { "Team" , "1" }, + { "HeroicDyeIdx", "Normal" }, + { "GodMode", "FALSE" } + }; + + public static readonly Dictionary GameReplicationInfoSection = new Dictionary + { + { "DefaultOfflineDifficulty", "1" }, + { "PlayerCountOverride", "1" }, + { "DefaultOfflineSuggestedLevel", "1" }, + { "DefaultOfflinePlayerLevel", "1" }, + { "DefaultOfflinePauseTimerDurationInSeconds", "999999" } + }; + + // Loadout defaults + public static readonly string[] Loadout = + { + "Mending Root", "Mage's Clover", "Barricade", + "Viscous Tar", "Flip Trap", "Wall Blades", + "Arrow Wall", "Concussive Pounder", "Ceiling Ballista" + }; + + public static readonly string[] Guardians = + { + "Dragon Guardian", "Serpent Guardian" + }; + } +} diff --git a/SingleplayerLauncher/Resources/GameInfo.cs b/SingleplayerLauncher/Resources/GameInfo.cs new file mode 100644 index 0000000..c1c269e --- /dev/null +++ b/SingleplayerLauncher/Resources/GameInfo.cs @@ -0,0 +1,253 @@ +using System.Collections.Generic; + +namespace SingleplayerLauncher.Resources +{ + public class Tuple + { + public T1 First { get; private set; } + public T2 Second { get; private set; } + internal Tuple(T1 first, T2 second) + { + First = first; + Second = second; + } + } + + public static class Tuple + { + public static Tuple New(T1 first, T2 second) + { + Tuple tuple = new Tuple(first, second); + return tuple; + } + } + + + + + + internal static class GameInfo + { + + public class Map + { + public string Name; + public string[] SurvivalDifficulties; + public Map(string name, string[] survivalDifficulties) + { + Name = name; + SurvivalDifficulties = survivalDifficulties; + } + } + + public class Hero + { + public string Name; + public Dictionary Skins; + public Hero(string name, Dictionary skins) + { + Name = name; + Skins = skins; + } + + public byte[] GetSkinHex(string skinName) + { + return Skins[skinName].HexSpitfireGameUPK; + } + } + + public class Skin + { + public string Name; + public byte[] HexSpitfireGameUPK; + public Skin(string name, byte[] hexSpitfireGameUPK) + { + Name = name; + HexSpitfireGameUPK = hexSpitfireGameUPK; + } + } + + public static Dictionary Heroes = new Dictionary + { + { "Bionka", new Hero( "Bionka", new Dictionary { + { "Default", new Skin("Default", new byte[] { 0x54, 0xDF, 0x00, 0x00 } ) }, + { "Lizard Queen", new Skin("Lizard Queen", new byte[] { 0x56, 0xDF, 0x00, 0x00 } ) }, + { "Fluffalump", new Skin("Fluffalump", new byte[] { 0x58, 0xDF, 0x00, 0x00 } ) }, + { "Bionka Bunny", new Skin("Bionka Bunny", new byte[] { 0x5A, 0xDF, 0x00, 0x00 } ) } + }) + }, + { "Blackpaw", new Hero("Blackpaw", null) }, + { "Bloodspike", new Hero("Bloodspike", null) }, + { "Brass", new Hero("Brass", null) }, + { "Cygnus", new Hero("Cygnus", null) }, + { "Deadeye", new Hero("Deadeye", null) }, + { "Dobbin", new Hero("Dobbin", null) }, + { "Gabriella", new Hero( "Gabriella", new Dictionary { + { "Violent Vintage", new Skin("Violent Vintage", new byte[] { 0x02, 0xE0, 0x00, 0x00 } ) }, + { "Enchanted Armor", new Skin("Enchanted Armor", new byte[] { 0x04, 0xE0, 0x00, 0x00 } ) }, + { "Blood Queen", new Skin("Blood Queen", new byte[] { 0x06, 0xE0, 0x00, 0x00 } ) }, + { "Default", new Skin("Default", new byte[] { 0x08, 0xE0, 0x00, 0x00 } ) }, + { "Frightfully Delightful", new Skin("Frightfully Delightful", new byte[] { 0x0A, 0xE0, 0x00, 0x00 } ) }, + { "Default (Red)", new Skin("Default (Red)", new byte[] { 0x0C, 0xE0, 0x00, 0x00 } ) }, + { "Life In Plastic", new Skin("Life In Plastic", new byte[] { 0x0E, 0xE0, 0x00, 0x00 } ) }, + { "Archmage of the Order", new Skin("Archmage of the Order", new byte[] { 0x10, 0xE0, 0x00, 0x00 } ) }, + { "Mistress of Illusion", new Skin("Mistress of Illusion", new byte[] { 0x12, 0xE0, 0x00, 0x00 } ) }, + { "Promising Prodigy", new Skin("Promising Prodigy", new byte[] { 0x14, 0xE0, 0x00, 0x00 } ) }, + { "I Dream of Gabby", new Skin("I Dream of Gabby", new byte[] { 0x16, 0xE0, 0x00, 0x00 } ) }, + { "Miracle Worker", new Skin("Miracle Worker", new byte[] { 0x18, 0xE0, 0x00, 0x00 } ) }, + { "Winter Witch", new Skin("Winter Witch", new byte[] { 0x1A, 0xE0, 0x00, 0x00 } ) }, + { "Skull Ninja", new Skin("Skull Ninja", new byte[] { 0x1C, 0xE0, 0x00, 0x00 } ) }, + { "Evil Ways", new Skin("Evil Ways", new byte[] { 0x1E, 0xE0, 0x00, 0x00 } ) }, + { "Dragon Charmer", new Skin("Dragon Charmer", new byte[] { 0x20, 0xE0, 0x00, 0x00 } ) } + }) + }, + { "Hogarth", new Hero( "Hogarth", new Dictionary { + { "Enchanted Armor", new Skin("Enchanted Armor", new byte[] { 0x8C, 0xDF, 0x00, 0x00 } ) }, + { "God of Plunder", new Skin("God of Plunder", new byte[] { 0x8E, 0xDF, 0x00, 0x00 } ) }, + { "Default", new Skin("Default", new byte[] { 0x90, 0xDF, 0x00, 0x00 } ) }, + { "Lumbering Jack", new Skin("Lumbering Jack", new byte[] { 0x92, 0xDF, 0x00, 0x00 } ) }, + { "Default (with some gold)", new Skin("Default (with some gold)", new byte[] { 0x94, 0xDF, 0x00, 0x00 } ) }, + { "Ice Armor", new Skin("Ice Armor", new byte[] { 0x96, 0xDF, 0x00, 0x00 } ) }, + { "Imperial Golden Warrior", new Skin("Imperial Golden Warrior", new byte[] { 0x98, 0xDF, 0x00, 0x00 } ) }, + { "Beached Bod", new Skin("Beached Bod", new byte[] { 0x9A, 0xDF, 0x00, 0x00 } ) }, + //{ "*crashed*", new Skin("*crashed*", new byte[] { 0x9C, 0xDF, 0x00, 0x00 } ) }, + { "Black Thane", new Skin("Black Thane", new byte[] { 0x9E, 0xDF, 0x00, 0x00 } ) }, + { "The Schling", new Skin("The Schling", new byte[] { 0xA0, 0xDF, 0x00, 0x00 } ) }, + { "Dragon Ward", new Skin("Dragon Ward", new byte[] { 0xA2, 0xDF, 0x00, 0x00 } ) } + }) + }, + { "Ivy", new Hero( "Ivy", new Dictionary { + { "Enchanted Armor", new Skin("Enchanted Armor", new byte[] { 0xDA, 0xDF, 0x00, 0x00 } ) }, + { "Valkyrie", new Skin("Valkyrie", new byte[] { 0xDC, 0xDF, 0x00, 0x00 } ) }, + { "Default", new Skin("Default", new byte[] { 0xDE, 0xDF, 0x00, 0x00 } ) }, + { "Flower Friend", new Skin("Flower Friend", new byte[] { 0xE0, 0xDF, 0x00, 0x00 } ) }, + { "Default (with some gold)", new Skin("Default (with some gold)", new byte[] { 0xE2, 0xDF, 0x00, 0x00 } ) }, + { "Imperial Ruby Archer", new Skin("Imperial Ruby Archer", new byte[] { 0xE4, 0xDF, 0x00, 0x00 } ) }, + //{ "*crashed*", new Skin("*crashed*", new byte[] { 0xE6, 0xDF, 0x00, 0x00 } ) }, + { "Wicked Warden", new Skin("Wicked Warden", new byte[] { 0xE8, 0xDF, 0x00, 0x00 } ) }, + { "Grovewatch", new Skin("Grovewatch", new byte[] { 0xEA, 0xDF, 0x00, 0x00 } ) }, + { "Dragon Tamer", new Skin("Dragon Tamer", new byte[] { 0xEC, 0xDF, 0x00, 0x00 } ) } + }) + }, + { "Maximilian", new Hero("Maximilian", new Dictionary { + { "Lucky Tunic" , new Skin("Lucky Tunic" , new byte[] { 0x4A, 0xE0, 0x00, 0x00 } ) }, + { "Enchanted Armor" , new Skin("Enchanted Armor" , new byte[] { 0x4C, 0xE0, 0x00, 0x00 } ) }, + { "Knight's Watch" , new Skin("Knight's Watch" , new byte[] { 0x4E, 0xE0, 0x00, 0x00 } ) }, + { "Default" , new Skin("Default" , new byte[] { 0x50, 0xE0, 0x00, 0x00 } ) }, + { "Orc Slayer" , new Skin("Orc Slayer" , new byte[] { 0x52, 0xE0, 0x00, 0x00 } ) }, + { "Cardboard Samurai" , new Skin("Cardboard Samurai" , new byte[] { 0x54, 0xE0, 0x00, 0x00 } ) }, + { "Scared-Crow" , new Skin("Scared-Crow" , new byte[] { 0x56, 0xE0, 0x00, 0x00 } ) }, + { "Lion Heart" , new Skin("Lion Heart" , new byte[] { 0x58, 0xE0, 0x00, 0x00 } ) }, + { "Robin Hood" , new Skin("Robin Hood" , new byte[] { 0x5A, 0xE0, 0x00, 0x00 } ) }, + { "Paximillian" , new Skin("Paximillian" , new byte[] { 0x5C, 0xE0, 0x00, 0x00 } ) }, + { "Legendary (default)" , new Skin("Legendary (default)" , new byte[] { 0x5E, 0xE0, 0x00, 0x00 } ) }, + { "Champion of the Order" , new Skin("Champion of the Order" , new byte[] { 0x60, 0xE0, 0x00, 0x00 } ) }, + { "Jamez Ripher" , new Skin("Jamez Ripher" , new byte[] { 0x62, 0xE0, 0x00, 0x00 } ) }, + { "Winter Warrior" , new Skin("Winter Warrior" , new byte[] { 0x64, 0xE0, 0x00, 0x00 } ) }, + { "Boomstick" , new Skin("Boomstick" , new byte[] { 0x66, 0xE0, 0x00, 0x00 } ) }, + { "Backdraft" , new Skin("Backdraft" , new byte[] { 0x68, 0xE0, 0x00, 0x00 } ) }, + { "Dragon Slayer" , new Skin("Dragon Slayer" , new byte[] { 0x6A, 0xE0, 0x00, 0x00 } ) }, + { "Summer of Stunning" , new Skin("Summer of Stunning" , new byte[] { 0x6C, 0xE0, 0x00, 0x00 } ) }, + { "Red Scarf (China Ad)" , new Skin("Red Scarf (China Ad)" , new byte[] { 0x6E, 0xE0, 0x00, 0x00 } ) }, + { "Yellow Scarf (China Ad)" , new Skin("Yellow Scarf (China Ad)" , new byte[] { 0x70, 0xE0, 0x00, 0x00 } ) }, + { "Blue Scarf (China Ad)" , new Skin("Blue Scarf (China Ad)" , new byte[] { 0x72, 0xE0, 0x00, 0x00 } ) } + }) + }, + { "Midnight", new Hero("Midnight", null) }, + { "Oziel", new Hero("Oziel", null) }, + { "Smolder", new Hero( "Smolder", new Dictionary { + { "Default", new Skin("Default", new byte[] { 0xF2, 0xDF, 0x00, 0x00 } ) }, + { "Helter Swelter", new Skin("Helter Swelter", new byte[] { 0xF4, 0xDF, 0x00, 0x00 } ) }, + { "Default (White hair)", new Skin("Default (White hair)", new byte[] { 0xF6, 0xDF, 0x00, 0x00 } ) }, + { "Elite", new Skin("Elite", new byte[] { 0xF8, 0xDF, 0x00, 0x00 } ) }, + { "Kill-auea", new Skin("Kill-auea", new byte[] { 0xFA, 0xDF, 0x00, 0x00 } ) }, + { "Fire-Alarm Femme", new Skin("Fire-Alarm Femme", new byte[] { 0xFC, 0xDF, 0x00, 0x00 } ) }, + { "Firestarter", new Skin("Firestarter", new byte[] { 0xFE, 0xDF, 0x00, 0x00 } ) }, + { "Wu Xing Dragon Mage", new Skin("Wu Xing Dragon Mage", new byte[] { 0x00, 0xE0, 0x00, 0x00 } ) } + }) + }, + { "Stinkeye", new Hero("Stinkeye", null) }, + { "Temper", new Hero("Temper", null) }, + { "Tundra", new Hero("Tundra", null) }, + { "Yi-Lin", new Hero("Yi-Lin", null) }, + { "Zoey", new Hero("Zoey", null) } + }; + + // Maps { Name, umap } + public static Dictionary Maps = new Dictionary + { + { "Academy Sewers", new Map("Academy Sewers", new string[] { "Rift Lord" } ) }, + { "Archmage Library", new Map("Archmage Library", new string[] {"Apprentice" } ) }, + { "Avalanche", new Map("Avalanche", new string[] { "Master", "Rift Lord" } ) }, + { "Banquet Hall", new Map("Banquet Hall", new string[] { "Apprentice", "War Mage", "Rift Lord" } ) }, + { "Castle Gates", new Map("Castle Gates", new string[] { "Master", "Rift Lord" } ) }, + { "Cliffside Clash", new Map("Cliffside Clash", new string[] { "Apprentice", "Master" } ) }, + { "Confluence", new Map("Confluence", new string[] { "Rift Lord" } ) }, + { "Crogon Keep", new Map("Crogon Keep", new string[] { "War Mage", "Master", "Rift Lord" } ) }, + { "Docks at Eventide", new Map("Docks at Eventide", new string[] { "Master" } ) }, + { "Eventide Fortress", new Map("Eventide Fortress", new string[] { "Rift Lord" } ) }, + { "Eventide Ramparts", new Map("Eventide Ramparts", new string[] {"Apprentice", "War Mage" } ) }, + { "Frostbite", new Map("Frostbite", new string[] { "Master", "Rift Lord" } ) }, + { "Gates of Thuricvod", new Map("Gates of Thuricvod", new string[] { "War Mage", "Rift Lord" } ) }, + { "Highlands", new Map("Highlands", new string[] { "Apprentice", "War Mage", "Rift Lord" } ) }, + { "Maximum Security", new Map("Maximum Security", new string[] { "Rift Lord" } ) }, + { "Midnight Market", new Map("Midnight Market", new string[] { "War Mage", "Rift Lord" } ) }, + { "Orcatraz", new Map("Orcatraz", new string[] { "Master" } ) }, + { "Orcri-La", new Map("Orcri-La", new string[] { "Master" } ) }, + { "Restricted Section", new Map("Restricted Section", new string[] { "War Mage", "Rift Lord" } ) }, + { "Riftmaker's Temple", new Map("Riftmaker's Temple", new string[] { "Apprentice" } ) }, + { "Shark Island", new Map("Shark Island", new string[] { "War Mage", "Master" } ) }, + { "Stables at Eventide", new Map("Stables at Eventide", new string[] { "War Mage", "Master" } ) }, + { "Storm Drain", new Map("Storm Drain", new string[] { "Master" } ) }, + { "Temple Graveyard", new Map("Temple Graveyard", new string[] { "War Mage", "Rift Lord" } ) }, + { "The Baths", new Map("The Baths", new string[] { "Apprentice", "Rift Lord" } ) }, + { "The Falling Folly", new Map("The Falling Folly", new string[] { "Master" } ) }, + { "The Wall", new Map("The Wall", new string[] { "War Mage", "Master" } ) }, + { "Throne Room", new Map("Throne Room", new string[] { "Apprentice", "War Mage", "Rift Lord" } ) }, + { "Thuricvod Village", new Map("Thuricvod Village", new string[] { "War Mage" } ) }, + { "Training Grounds", new Map("Training Grounds", new string[] { "Apprentice", "War Mage", "Master" } ) }, + { "Unchained Fortress", new Map("Unchained Fortress", new string[] { "Apprentice", "Master" } ) }, + { "Water Garden", new Map("Water Garden", new string[] { "Apprentice", "Master" }) } + }; + + public static HashSet startingCoin9000Maps = new HashSet + { + "Academy Sewers", + "Archmage Library", + "Avalanche", + "Banquet Hall", + "Cliffside Clash", + "Confluence", + "Crogon Keep", + "Docks at Eventide", + "Eventide Fortress", + "Eventide Ramparts", + "Frostbite", + "Gates of Thuricvod", + "Highlands", + "Maximum Security", + "Orcatraz", + "Orcri-La", + "Restricted Section", + "Shark Island", + "Stables at Eventide", + "Storm Drain", + "Temple Graveyard", + "The Falling Folly", + "The Wall", + "Throne Room", + "Thuricvod Village", + "Training Grounds", + "Unchained Fortress", + }; + + public static HashSet startingCoin6000Maps = new HashSet + { + "Castle Gates", + "Midnight Market", + "Riftmaker's Temple", + "The Baths", + "Water Garden" + }; + } +} diff --git a/SingleplayerLauncher/Resources/IniConfig.cs b/SingleplayerLauncher/Resources/IniConfig.cs new file mode 100644 index 0000000..0944f3a --- /dev/null +++ b/SingleplayerLauncher/Resources/IniConfig.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; + +namespace SingleplayerLauncher.Resources +{ + internal class IniConfig + { + // Usable variables within CharactersData.ini + // Heroes (Name, pawnweapon) + public static Dictionary Heroes = new Dictionary + { + { "Bionka", "PawnWeapon_Bionka.Pawn_Bionka" }, + { "Blackpaw", "PawnWeapon_Blackpaw.Pawn_Blackpaw" }, + { "Bloodspike", "PawnWeapon_Bloodspike.Pawn_Bloodspike" }, + { "Brass", "PawnWeapon_Brass.Pawn_Brass" }, + { "Cygnus", "PawnWeapon_Cygnus.Pawn_Cygnus" }, + { "Deadeye", "PawnWeapon_Deadeye.Pawn_Deadeye" }, + { "Dobbin", "PawnWeapon_Dobbin.Pawn_Dobbin" }, + { "Gabriella", "PawnWeapon_Sorceress.Pawn_Sorceress" }, + { "Hogarth", "PawnWeapon_Hogarth.Pawn_Hogarth" }, + { "Ivy", "PawnWeapon_Ivy.Pawn_Ivy" }, + { "Maximilian", "PawnWeapon_Warmage.Pawn_Warmage" }, + { "Midnight", "PawnWeapon_Midnight.Pawn_Midnight" }, + { "Oziel", "PawnWeapon_Oziel.Pawn_Oziel" }, + { "Smolder", "PawnWeapon_Smolder.Pawn_Smolder" }, + { "Stinkeye", "PawnWeapon_Stinkeye.Pawn_Stinkeye" }, + { "Temper", "PawnWeapon_Temper.Pawn_Temper" }, + { "Tundra", "PawnWeapon_Tundra.Pawn_Tundra" }, + { "Yi-Lin", "PawnWeapon_hooksword.Pawn_hooksword" }, + { "Zoey", "PawnWeapon_Zoey.Pawn_Zoey" } + }; + + + // Maps { Name, umap } + public static Dictionary Maps = new Dictionary + { + { "Academy Sewers", "PvE_Sewers.umap" }, + { "Archmage Library", "PvE_AcademyLibrary.umap" }, + { "Avalanche", "PvE_Avalanche.umap" }, + { "Banquet Hall", "PvE_BanquetHall.umap" }, + { "Castle Gates", "PvE_ASN_CastleGates.umap" }, + { "Cliffside Clash", "PvE_2Lane.umap" }, + { "Confluence", "PvE_AcademyCanals.umap" }, + { "Crogon Keep", "PvE_CrogonKeep.umap" }, + { "Docks at Eventide", "PvE_SUR_Pirates.umap" }, + { "Eventide Fortress", "PvE_Surrounded.umap" }, + { "Eventide Ramparts", "PvE_SUR_NorthernClans.umap" }, + { "Frostbite", "PvE_FrostBite.umap" }, + { "Gates of Thuricvod", "PvE_Corridors.umap" }, + { "Highlands", "PvE_Highlands.umap" }, + { "Maximum Security", "PvE_AcademyDungeon.umap" }, + { "Midnight Market", "PvE_ASN_NightMarket.umap" }, + { "Orcatraz", "PvE_Orcatraz.umap" }, + { "Orcri-La", "PvE_OrcVil_Temple.umap" }, + { "Restricted Section", "PvE_RestrictedSection.umap" }, + { "Riftmaker's Temple", "PvE_AcademyTemple.umap" }, + { "Shark Island", "PvE_SharkIsle.umap" }, + { "Stables at Eventide", "PvE_SUR_JungleTribe.umap" }, + { "Storm Drain", "PvE_Flushed.umap" }, + { "Temple Graveyard", "PvE_Mausoleum.umap" }, + { "The Baths", "PvE_Baths.umap" }, + { "The Falling Folly", "PvE_Towering.umap" }, + { "The Wall", "PvE_TheWall.umap" }, + { "Throne Room", "PvE_ThroneRoom.umap" }, + { "Thuricvod Village", "PvE_Gap.umap" }, + { "Training Grounds", "PvE_TrainingGrounds.umap" }, + { "Unchained Fortress", "PvE_OneWay.umap" }, + { "Water Garden", "PvE_ASN_WaterGarden.umap" }, + { "Prologue 1 (Grand Foyer)", "NPE_1.umap" }, + { "Prologue 2 (Archmage Library)", "NPE_2.umap" }, + { "Prologue 3 (Dungeon)", "NPE_3.umap" }, + { "Prologue 4 (Canals)", "NPE_4.umap" }, + { "Prologue 5 (Riftmaker's Temple)", "NPE_5.umap" }, + //{ "SpitfireFrontEndMap", "SpitfireFrontEndMap.umap" }, + { "Survival Tutorial", "TutorialSurvival.umap" }, + { "Basics Tutorial", "NewbieTutorial.umap" } + }; + + // GameModes { Name, DefaultOfflineDifficulty } (DefaultGame.ini) + public static Dictionary GameModes = new Dictionary + { + { "Survival", "1" }, + { "Endless", "5" } + //{ "Weekly Challenge", "" } + //{ "Chaos Trials", "" } + }; + + + // Difficulty { Name, DefaultOfflineDifficulty } (DefaultGame.ini) + public static Dictionary survivalDifficulties = new Dictionary + { + { "Apprentice", "1" }, + { "War Mage", "11" }, + { "Master", "26" }, + { "Rift Lord", "46" } + }; + + // Endless Difficulty { Name, DefaultOfflineDifficulty } (DefaultGame.ini) + public static Dictionary endlessDifficulties = new Dictionary + { + { "Endless+", "20" }, + { "Endless++", "40" }, + { "Endless+3", "60" }, + { "Endless+4", "80" }, + { "Endless+5", "100" }, + { "Endless+6", "120" }, + { "Endless+7", "140" }, + { "Endless+8", "160" }, + { "Endless+9", "180" }, + { "Endless+10", "200" } + }; + + // Survival Difficulty { Name, {DefaultOfflinePlayerLevel, DefaultOfflineDifficulty } (DefaultGame.ini) + public static Dictionary> survivalExtraDifficulties = new Dictionary> + { + { "Apprentice", new Dictionary + { + { "Apprentice+" , new int[] { 1, 10 } } + } + }, + { "War Mage", new Dictionary + { + { "War Mage+" , new int[] { 1, 25 } } + } + }, + { "Master", new Dictionary + { + { "Master+" , new int[] { 6, 26 } }, + { "Master++" , new int[] { 1, 45 } } + } + }, + { "Rift Lord", new Dictionary + { + { "Rift Lord+" , new int[] { 26, 46 } }, + { "Rift Lord++" , new int[] { 6, 46 } }, + { "Rift Lord+3" , new int[] { 1, 61 } }, + { "Rift Lord+4" , new int[] { 1, 75 } } + } + } + }; + + // Dyes (Name, IdxDye) + public static Dictionary Dyes = new Dictionary + { + { "Normal", "0" }, + { "Heroic", "1" }, + { "Legendary", "2" } + }; + + } +} diff --git a/SingleplayerLauncher/Resources/Loadout.cs b/SingleplayerLauncher/Resources/Loadout.cs new file mode 100644 index 0000000..270656e --- /dev/null +++ b/SingleplayerLauncher/Resources/Loadout.cs @@ -0,0 +1,190 @@ +using System.Collections.Generic; + +namespace SingleplayerLauncher.Resources +{ + internal static class Loadout + { + //Traps (Name, Hex) + // Hex representation in SpitfireGame.upk + public static Dictionary Traps = new Dictionary + { + { "Acid Sprayer", new byte[] { 0x5A, 0xC4, 0x00, 0x00, } }, + { "Arcane Bowling Ball", new byte[] { 0x62, 0xC4, 0x00, 0x00 } }, + { "Arcane Phaser", new byte[] { 0x64, 0xC4, 0x00, 0x00 } }, + { "Arrow Wall", new byte[] { 0x66, 0xC4, 0x00, 0x00 } }, + { "BGH Arrow Wall", new byte[] { 0x68, 0xC4, 0x00, 0x00 } }, + { "Ceiling Ballista", new byte[] { 0x6A, 0xC4, 0x00, 0x00 } }, + { "Dragons Lance", new byte[] { 0x6C, 0xC4, 0x00, 0x00 } }, + { "BGH Ceiling Ballista", new byte[] { 0x6E, 0xC4, 0x00, 0x00 } }, + { "Barricade", new byte[] { 0x70, 0xC4, 0x00, 0x00 } }, + { "Great Wall Barricade", new byte[] { 0x72, 0xC4, 0x00, 0x00 } }, + { "Boom Barrel", new byte[] { 0x74, 0xC4, 0x00, 0x00 } }, + { "Boom Barrel Roller", new byte[] { 0x76, 0xC4, 0x00, 0x00 } }, + { "Boulder Chute", new byte[] { 0x78, 0xC4, 0x00, 0x00 } }, + { "Icicle Impaler", new byte[] { 0x7A, 0xC4, 0x00, 0x00 } }, + { "Brimstone", new byte[] { 0x7C, 0xC4, 0x00, 0x00 } }, + { "Coin Forge", new byte[] { 0x7E, 0xC4, 0x00, 0x00 } }, + { "Cursed Ground", new byte[] { 0x80, 0xC4, 0x00, 0x00 } }, + { "Decoy", new byte[] { 0x82, 0xC4, 0x00, 0x00 } }, + { "Spitfire Wall", new byte[] { 0x84, 0xC4, 0x00, 0x00 } }, + { "Fire Cracker", new byte[] { 0x86, 0xC4, 0x00, 0x00 } }, + { "Flip Trap", new byte[] { 0x88, 0xC4, 0x00, 0x00 } }, + { "Floor Scorcher", new byte[] { 0x8A, 0xC4, 0x00, 0x00 } }, + { "Temple Alarm Gong", new byte[] { 0x8C, 0xC4, 0x00, 0x00 } }, + { "Grinder", new byte[] { 0x8E, 0xC4, 0x00, 0x00 } }, + { "Quarter Pounder", new byte[] { 0x90, 0xC4, 0x00, 0x00 } }, + { "Haymaker", new byte[] { 0x92, 0xC4, 0x00, 0x00 } }, + { "Healing Well", new byte[] { 0x94, 0xC4, 0x00, 0x00 } }, + { "Ice Shard", new byte[] { 0x96, 0xC4, 0x00, 0x00 } }, + { "Ice Vent", new byte[] { 0x98, 0xC4, 0x00, 0x00 } }, + { "Lightning Rod", new byte[] { 0x9A, 0xC4, 0x00, 0x00 } }, + { "Mana Well", new byte[] { 0x9C, 0xC4, 0x00, 0x00 } }, + { "Summoner Trap", new byte[] { 0x9E, 0xC4, 0x00, 0x00 } }, + { "Naphtha Sprayer", new byte[] { 0xA0, 0xC4, 0x00, 0x00 } }, + { "Overload Trap", new byte[] { 0xA2, 0xC4, 0x00, 0x00 } }, + { "Powerup Damage", new byte[] { 0xA4, 0xC4, 0x00, 0x00 } }, + { "Pounder", new byte[] { 0xA6, 0xC4, 0x00, 0x00 } }, + { "Concussive Pounder", new byte[] { 0xA8, 0xC4, 0x00, 0x00 } }, + { "Power Generator", new byte[] { 0xAA, 0xC4, 0x00, 0x00 } }, + { "Projectile Shield", new byte[] { 0xAC, 0xC4, 0x00, 0x00 } }, + { "Push Trap", new byte[] { 0xAE, 0xC4, 0x00, 0x00 } }, + { "Saw Of Arctos", new byte[] { 0xB0, 0xC4, 0x00, 0x00 } }, + { "Shield Powerup", new byte[] { 0xB2, 0xC4, 0x00, 0x00 } }, + { "Speed Pad", new byte[] { 0xB4, 0xC4, 0x00, 0x00 } }, + { "Floor Spikes", new byte[] { 0xB6, 0xC4, 0x00, 0x00 } }, + { "Spike Wall", new byte[] { 0xB8, 0xC4, 0x00, 0x00 } }, + { "Steam Vent", new byte[] { 0xBA, 0xC4, 0x00, 0x00 } }, + { "Swinging Mace", new byte[] { 0xBC, 0xC4, 0x00, 0x00 } }, + { "Tar Trap", new byte[] { 0xBE, 0xC4, 0x00, 0x00 } }, + { "Viscous Tar", new byte[] { 0xC0, 0xC4, 0x00, 0x00 } }, + { "Shock Zapper", new byte[] { 0xC2, 0xC4, 0x00, 0x00 } }, + { "BGH Shock Zapper", new byte[] { 0xC4, 0xC4, 0x00, 0x00 } }, + { "Trip Wire", new byte[] { 0xC6, 0xC4, 0x00, 0x00 } }, + { "Wall Blades", new byte[] { 0xC8, 0xC4, 0x00, 0x00 } }, + { "Wall Charger", new byte[] { 0xCA, 0xC4, 0x00, 0x00 } }, + { "Web Spinner", new byte[] { 0xCC, 0xC4, 0x00, 0x00 } } + }; + + // Gear (Name, Hex) + // Hex representation in SpitfireGame.upk + public static Dictionary Gear = new Dictionary + { + { "Freedom Trinket", new byte[] { 0xCE, 0xC4, 0x00, 0x00 } }, + { "Greater Freedom Trinket", new byte[] { 0xD0, 0xC4, 0x00, 0x00 } }, + { "Mending Root", new byte[] { 0xD2, 0xC4, 0x00, 0x00 } }, + { "Hobgoblin Charm", new byte[] { 0xD4, 0xC4, 0x00, 0x00 } }, + { "Ring of Last Stand", new byte[] { 0xD6, 0xC4, 0x00, 0x00 } }, + { "Mage's Picnic", new byte[] { 0xD8, 0xC4, 0x00, 0x00 } }, + { "Mage's Clover", new byte[] { 0xDA, 0xC4, 0x00, 0x00 } }, + { "Gnomish Repair Kit", new byte[] { 0xDC, 0xC4, 0x00, 0x00 } }, + { "Teleportation Ring", new byte[] { 0xDE, 0xC4, 0x00, 0x00 } }, + { "Arcane Bubble Blower", new byte[] { 0xE3, 0xC4, 0x00, 0x00 } }, + { "Fire Wall Bracers", new byte[] { 0xE5, 0xC4, 0x00, 0x00 } }, + { "Ice Amulet", new byte[] { 0xE7, 0xC4, 0x00, 0x00 } }, + { "Ring of Storms", new byte[] { 0xE9, 0xC4, 0x00, 0x00 } }, + { "Lightning Ring", new byte[] { 0xEB, 0xC4, 0x00, 0x00 } }, + { "AntiTrap Vambrace", new byte[] { 0x02, 0xC4, 0x00, 0x00 } } + }; + + // Guardians (Name (Hex String code, Length)) + // Hex string representation in SpitfireGame.upk + public static Dictionary> Guardians = new Dictionary> + { + { "Bartender Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x62, 0x61, 0x72, 0x6d, 0x61, 0x69, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x42, 0x61, 0x72, 0x6d, 0x61, 0x69, 0x64, 0x00 }, 47 + ) + }, + { "Blacksmith Guardian",new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x42, 0x65, 0x61, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x42, 0x65, 0x61, 0x72, 0x00 }, 33 + ) + }, + { "Cook Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x6f, 0x6f, 0x6b, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x43, 0x6f, 0x6f, 0x6b, 0x00 }, 33 + ) + }, + { "Deckhand Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x64, 0x65, 0x63, 0x6b, 0x68, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x44, 0x65, 0x63, 0x6b, 0x68, 0x61, 0x6e, 0x64, 0x00 }, 49 + ) + }, + { "Dragon Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x00 }, 37 + ) + }, + { "Friar Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x6f, 0x72, 0x63, 0x70, 0x72, 0x69, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4f, 0x72, 0x63, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74, 0x00 }, 51 + ) + }, + { "Headhunter Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x54, 0x72, 0x6f, 0x6c, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x00 }, 61 + ) + }, + { "Jade Empire", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x6a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x4a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x00 }, 59 + ) + }, + { "Jailer Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x00 }, 45 + ) + }, + { "Lion Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4c, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x4c, 0x69, 0x6f, 0x6e, 0x00 }, 33 + ) + }, + { "Moon Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4d, 0x6f, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x4d, 0x6f, 0x6f, 0x6e, 0x00 }, 33 + ) + }, + { "Priest Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74, 0x00 }, 45 + ) + }, + { "Quartermaster Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x63, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x00 }, 47 + ) + }, + { "Ranch Hand Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x6d, 0x69, 0x6e, 0x6f, 0x74, 0x61, 0x75, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x4d, 0x69, 0x6e, 0x6f, 0x74, 0x61, 0x75, 0x72, 0x00 }, 69 + ) + }, + { "Rumrudder Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x63, 0x75, 0x72, 0x76, 0x79, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x63, 0x75, 0x72, 0x76, 0x79, 0x00 }, 45 + ) + }, + { "Serpent Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x52, 0x61, 0x7a, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x52, 0x61, 0x7a, 0x65, 0x72, 0x00 }, 35 + ) + }, + { "Stablehand Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x00 }, 53 + ) + }, + { "Sun Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x75, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x53, 0x75, 0x6e, 0x00 }, 31 + ) + }, + { "WeaponWright Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x77, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x77, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x57, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x77, 0x72, 0x69, 0x67, 0x68, 0x74, 0x00 }, 57 + ) + } + }; + } +} diff --git a/SingleplayerLauncher/Resources/SpitfireGameUPK.cs b/SingleplayerLauncher/Resources/SpitfireGameUPK.cs new file mode 100644 index 0000000..eec26fb --- /dev/null +++ b/SingleplayerLauncher/Resources/SpitfireGameUPK.cs @@ -0,0 +1,138 @@ +using System.Collections.Generic; + +namespace SingleplayerLauncher.Resources +{ + internal class SpitfireGameUPK + { + + public class HeroObject + { + public string Name; + public int Offset; // index of offset from start of SpitfireGame.upk + public int Size; // n of Bytes + public HeroObject(string name, int offset, int size) + { + Name = name; + Offset = offset; + Size = size; + } + } + + public static readonly Dictionary HeroObjects = new Dictionary + { + {"Maximilian", new HeroObject("Maximilian", 0x28AB495, 1542) }, + {"Bionka", new HeroObject("Bionka", 0x28a992b, 1388) }, + {"Hogarth", new HeroObject("Hogarth", 0x28a9e97, 1505) }, + {"Ivy", new HeroObject("Ivy", 0x28aa478, 1372) }, + {"Smolder", new HeroObject("Smolder", 0x28aa9d4, 1397) }, + {"Gabriella", new HeroObject("Gabriella", 0x28aaf49, 1356) } + }; + + // Archetype (Weapon/Skill set) (DefaultInventoryArchetypes(0)=RItem'PawnWeapon_Warmage.Weapon_Warmage') + public static readonly int HeroObjectDefaultInventoryArchetypesSectionLength = 32; + public static readonly byte[] HeroObjectDefaultInventoryArchetypesHeader = new byte[] { 0xC5, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + // Loadout (DefaultInventoryClasses(0)=class'RItemTrinketHealing') + // Size is variable, full loadout makes it length = + public static readonly byte[] HeroObjectLoadoutHeader = new byte[] { 0xC6, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + public static readonly byte[] HeroObjectLoadoutFieldType = new byte[] { 0xC5, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // Guardians (DefaultGuardianArchetypes(0)="Pawn_GuardianDragon.Placeable_Dragon") + // Size is variable + public static readonly byte[] HeroObjectGuardiansHeader = new byte[] { 0xBA, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + public static readonly byte[] HeroObjectGuardiansFieldType = new byte[] { 0xC5, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // Hero Skin + public static readonly int HeroObjectCurrentSkinClassSectionLength = 28; + public static readonly byte[] HeroObjectCurrentSkinClassHeader = new byte[] { 0x52, 0x1E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + // Hero weaver tree (is after Inventory/Guardians) + public static readonly int HeroObjectWeaverTreeDefaultSectionLength = 28; + public static readonly byte[] HeroObjectWeaverTreeDefaultHeader = new byte[] { 0x00, 0xB4, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + // Default WaveClasses (only in Maximilian) + public static readonly int HeroObjectDefaultWaveClassesSectionLength = 64; + public static readonly byte[] HeroObjectDefaultWaveClassesHeader = new byte[] { 0xF3, 0x2c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + public static readonly int HeroObjectHeroDamageTypeSectionLength = 40; + public static readonly byte[] HeroObjectHeroDamageTypeHeader = new byte[] { 0x0E, 0x46, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + // Strategic Role, probably from siege + public static readonly int HeroObjectStrategicRoleSectionLength = 40; + public static readonly byte[] HeroObjectStrategicRoleHeader = new byte[] { 0x74, 0xA3, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // Controls what role bots will pick in siege + public static readonly int HeroObjectDefaultRoleClassSectionLength = 28; + public static readonly byte[] HeroObjectDefaultRoleClassHeader = new byte[] { 0xE4, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + // EndGameDanceSegment + public static readonly int HeroObjectEndGameDanceSegmentSectionLength = 28; + public static readonly byte[] HeroObjectEndGameDanceSegmentHeader = new byte[] { 0x78, 0x33, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // EndGameLockMovementSegment + public static readonly int HeroObjectEndGameLockMovementSegmentSectionLength = 28; + public static readonly byte[] HeroObjectEndGameLockMovementSegmentHeader = new byte[] { 0x7E, 0x33, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // EndGameDefeatedSegment + public static readonly int HeroObjectEndGameDefeatedSegmentSectionLength = 28; + public static readonly byte[] HeroObjectEndGameDefeatedSegmentHeader = new byte[] { 0x7C, 0x33, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + + // BeginStealthClientSegment + public static readonly int HeroObjectBeginStealthClientSegmentSectionLength = 28; + public static readonly byte[] HeroObjectBeginStealthClientSegmentHeader = new byte[] { 0x5F, 0x0D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // EndStealthClientSegment + public static readonly int HeroObjectEndStealthClientSegmentSectionLength = 28; + public static readonly byte[] HeroObjectEndStealthClientSegmentHeader = new byte[] { 0xA6, 0x33, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // TeamStealthSegment + public static readonly int HeroObjectTeamStealthSegmentSectionLength = 28; + public static readonly byte[] HeroObjectTeamStealthSegmentHeader = new byte[] { 0x09, 0xA7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + + // NetRelComponent + public static readonly int HeroObjectNetRelComponentSectionLength = 28; + public static readonly byte[] HeroObjectNetRelComponentHeader = new byte[] { 0xF9, 0x63, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + // ActionManager + public static readonly int HeroObjectActionManagerSectionLength = 28; + public static readonly byte[] HeroObjectActionManagerHeader = new byte[] { 0x8D, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + // mAIResponsiveBehaviors + public static readonly int HeroObjectmAIResponsiveBehaviorsSectionLength = 52; + public static readonly byte[] HeroObjectmAIResponsiveBehaviorsHeader = new byte[] { 0x82, 0x54, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + } +} diff --git a/SingleplayerLauncher/Settings.cs b/SingleplayerLauncher/Settings.cs index c008d97..13f5bcd 100644 --- a/SingleplayerLauncher/Settings.cs +++ b/SingleplayerLauncher/Settings.cs @@ -1,11 +1,11 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; using System.IO; using System.Windows.Forms; -using Newtonsoft.Json; namespace SingleplayerLauncher { - class Settings + internal class Settings { private static readonly string SettingsFile = "settings.txt"; public static Dictionary Instance = new Dictionary(); @@ -15,7 +15,7 @@ static Settings() { Load(); } - catch(Exception e) + catch (Exception e) { MessageBox.Show(e.ToString()); } diff --git a/SingleplayerLauncher/SingleplayerLauncher.csproj b/SingleplayerLauncher/SingleplayerLauncher.csproj index 71f2acc..dc72058 100644 --- a/SingleplayerLauncher/SingleplayerLauncher.csproj +++ b/SingleplayerLauncher/SingleplayerLauncher.csproj @@ -11,6 +11,21 @@ v3.5 512 true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true AnyCPU @@ -74,7 +89,11 @@ - + + + + + @@ -107,5 +126,12 @@ True + + + False + .NET Framework 3.5 SP1 + true + + \ No newline at end of file diff --git a/SingleplayerLauncher/UPKFile.cs b/SingleplayerLauncher/UPKFile.cs index 747f60f..e7177b4 100644 --- a/SingleplayerLauncher/UPKFile.cs +++ b/SingleplayerLauncher/UPKFile.cs @@ -1,15 +1,14 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; +using System.Windows.Forms; namespace SingleplayerLauncher { public class UPKFile { private byte[] bytes; - private string filePath; + private readonly string filePath; private int fileLength = 0; public int nBytesRemoved = 0; @@ -23,7 +22,7 @@ public UPKFile(string path) { filePath = path; bytes = ReadAllBytes(path); - } + } protected byte[] Bytes { @@ -33,10 +32,7 @@ protected byte[] Bytes bytes = ReadAllBytes(filePath); return bytes; } - set - { - bytes = value; - } + set => bytes = value; } private byte[] ReadAllBytes(string fileName) @@ -69,11 +65,11 @@ public byte[] CreateZeroedByteArray(int length) /// Saves the file to disk. /// public void Save() - { + { File.WriteAllBytes(filePath, Bytes); - } + } - public byte getByte(int index) + public byte GetByte(int index) { return Bytes[index]; } @@ -85,7 +81,7 @@ public void OverrideBytes(byte[] bytesToWrite, int startPosition) { int length = bytesToWrite.Length; - for(int i = 0; i < length; i++) + for (int i = 0; i < length; i++) { Bytes[startPosition + i] = bytesToWrite[i]; } @@ -118,7 +114,7 @@ public void FindAndOverrideBytes(byte[] bytesToWrite, byte[] bytesToFind, int st /// Number of bytes to remove. public void RemoveBytes(int index, int numberBytes) { - var tmpBytes = new List(Bytes); + List tmpBytes = new List(Bytes); tmpBytes.RemoveRange(index, numberBytes); Bytes = tmpBytes.ToArray(); @@ -137,7 +133,7 @@ public void InsertZeroedBytes(int index, int numberBytes) { numberBytes = nBytesRemoved; } - + byte[] zeroedBytes = CreateZeroedByteArray(numberBytes); InsertBytes(zeroedBytes, index); @@ -149,7 +145,7 @@ public void InsertZeroedBytes(int index, int numberBytes) /// public void InsertBytes(byte[] bytesToInsert, int position) { - var tmpBytes = new List(Bytes); + List tmpBytes = new List(Bytes); tmpBytes.InsertRange(position, bytesToInsert); Bytes = tmpBytes.ToArray(); @@ -170,7 +166,7 @@ public int FindBytesNaive(byte[] needle, int start = 0) for (int i = start; i <= haystack.Length - needle.Length; i++) { - if (Match( needle, i)) + if (Match(needle, i)) { return i; } @@ -289,6 +285,19 @@ private void ComputeLPSArray(byte[] pat, int M, int[] lps) } } } + + /// + /// For Debugging purposes. + /// Prints in a Mesage Box the indicated range, from stated index. + /// + /// Offset index of where to start the print. + /// Number of bytes to print from the start index. + internal void PrintBytes(int offset, int size) + { + byte[] arrToPrint = new byte[size]; + Array.Copy(Bytes, offset, arrToPrint, 0, size); + MessageBox.Show(BitConverter.ToString(arrToPrint)); + } } }