diff --git a/src/library/Syroot.Worms.Armageddon/Scheme.cs b/src/library/Syroot.Worms.Armageddon/Scheme.cs index 40a7396..cc660c0 100644 --- a/src/library/Syroot.Worms.Armageddon/Scheme.cs +++ b/src/library/Syroot.Worms.Armageddon/Scheme.cs @@ -1,1062 +1,1036 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using Syroot.BinaryData; -using Syroot.Worms.Core; -using Syroot.Worms.Core.IO; - -namespace Syroot.Worms.Armageddon -{ - /// - /// Represents a scheme stored in a WSC file which contains game settings and armory configuration, including - /// RubberWorm settings encoded in those. - /// Used by WA and WWP. S. https://worms2d.info/Game_scheme_file. - /// - public class Scheme : ILoadableFile, ISaveableFile - { - // ---- CONSTANTS ---------------------------------------------------------------------------------------------- - - private const string _signature = "SCHM"; - - // Lookup tables. - private static readonly Dictionary _mapWaterRiseToRaw = new Dictionary() - { - [0] = 0, - [5] = 1, - [13] = 19, - [20] = 2, - [21] = 55, - [29] = 43, - [37] = 47, - [45] = 3, - [52] = 26, - [53] = 25, - [61] = 27, - [64] = 8, - [69] = 33, - [77] = 13, - [80] = 4, - [84] = 18, - [85] = 23, - [93] = 11, - [101] = 15, - [109] = 29, - [116] = 22, - [117] = 57, - [125] = 5, - [133] = 63, - [141] = 45, - [148] = 30, - [149] = 9, - [157] = 21, - [165] = 17, - [173] = 61, - [180] = 6, - [181] = 39, - [189] = 37, - [197] = 31, - [205] = 51, - [208] = 12, - [212] = 14, - [213] = 41, - [221] = 53, - [229] = 49, - [237] = 35, - [244] = 10, - [245] = 7, - [253] = 59 - }; - private static readonly byte[] _objectCounts = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, - 90, 95, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250 }; - - // ---- MEMBERS ------------------------------------------------------------------------------------------------ - - private byte _mineDelay; - private byte _turnTime; - private byte _roundTimeMinutes; - private byte _roundTimeSeconds; - private byte _numberOfWins; - private sbyte _rwGravity; - private sbyte _rwGravityConstBlackHole; - private sbyte _rwGravityPropBlackHole; - private byte _rwKaosMod; - - // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ - - /// - /// Initializes a new instance of the class. - /// - public Scheme() - { - Version = SchemeVersion.Extended; - Weapons = new SchemeWeaponSetting[GetWeaponCount()]; - } - - /// - /// Initializes a new instance of the class, loading the data from the given - /// . - /// - /// The to load the data from. - public Scheme(Stream stream) => Load(stream); - - /// - /// Initializes a new instance of the class, loading the data from the given file. - /// - /// The name of the file to load the data from. - public Scheme(string fileName) => Load(fileName); - - // ---- PROPERTIES --------------------------------------------------------------------------------------------- - - /// - /// Gets or sets the of this scheme, controlling whether super weapon settings - /// are stored or not. - /// - public SchemeVersion Version { get; set; } - - /// - /// Gets or sets the delay in seconds between each team's turn to allow relaxed switching of seats. - /// - public byte HotSeatDelay { get; set; } - - /// - /// Gets or sets the time in seconds available for a worm to retreat after using a weapon which ends the turn - /// while standing on land. - /// - public byte RetreatTime { get; set; } - - /// - /// Gets or sets the time in seconds available for a worm to retreat after using a weapon which ends the turn - /// while on a rope. - /// - public byte RetreatTimeRope { get; set; } - - /// - /// Gets or sets a value indicating whether the total round time until sudden death will be displayed in the - /// turn timer. - /// - public bool ShowRoundTime { get; set; } - - /// - /// Gets or sets a value indicating whether significant turns will be replayed in offline games. - /// - public bool AutomaticReplays { get; set; } - - /// - /// Gets or sets the percentual amount of fall damage applied, relative to normal fall damage being 100%. - /// - public SchemeFallDamage FallDamage { get; set; } - - /// - /// Gets or sets a value indicating whether worms cannot walk and are mostly stuck at their current position - /// without using any utilities. - /// - public bool ArtilleryMode { get; set; } - - /// - /// Gets or sets a value indicating the scheme editor used to modify this scheme. Originally used to indicate - /// the unimplemented Bounty Mode. - /// - public SchemeEditor SchemeEditor { get; set; } - - /// - /// Gets or sets a value indicating the stockpiling of armory between game rounds. - /// - public SchemeStockpiling StockpilingMode { get; set; } - - /// - /// Gets or sets a value indicating the worm selection order determining the next worm to be played. - /// - public SchemeWormSelect WormSelectMode { get; set; } - - /// - /// Gets or sets a value indicating the action triggered when Sudden Death starts. - /// - public SchemeSuddenDeathEvent SuddenDeathEvent { get; set; } - - /// - /// Gets or sets the amount in pixels which the water will rise between turns after Sudden Death was triggered. - /// - public SchemeWaterRise WaterRiseRate { get; set; } - - /// - /// Gets or sets the percentual probability of a weapon crate to drop between turns. Negative values might crash - /// the game. - /// - public sbyte WeaponCrateProbability { get; set; } - - /// - /// Gets or sets a value indicating whether donor cards can spawn upon a worm's death. - /// - public bool DonorCards { get; set; } - - /// - /// Gets or sets the percentual probability of a health crate to drop between turns. Negative values might crash - /// the game. - /// - public sbyte HealthCrateProbability { get; set; } - - /// - /// Gets or sets the amount of health included in a health crate added to the collecting worm's energy. - /// - public byte HealthCrateEnergy { get; set; } - - /// - /// Gets or sets the percentual probability of a utility crate to drop between turns. Negative values might - /// crash the game. - /// - public sbyte UtilityCrateProbability { get; set; } - - /// - /// Gets or sets the type of objects which can be placed on the map. - /// - public SchemeObjectType ObjectTypes { get; set; } - - /// - /// Gets or sets the maximum number of objects (mines or oil drums) on the map. - /// - public SchemeObjectCount ObjectCount { get; set; } - - /// - /// Gets or sets the number of seconds a mine requires to explode. Can be 1-3 and 5-127 seconds. - /// - public byte MineDelay - { - get => _mineDelay; - set - { - if (value == 4 || value > 0x7F) - throw new ArgumentException("Mine delay must be between 0-127 and not be 4.", nameof(value)); - _mineDelay = value; - } - } - - /// - /// Gets or sets a value indicating whether the mine fuse will be randomly chosen between fractions of 1 to 3 - /// seconds. If true, the setting will be ignored. - /// - public bool MineDelayRandom { get; set; } - - /// - /// Gets or sets a value indicating whether mines can refuse to explode after their count down. - /// - public bool DudMines { get; set; } - - /// - /// Gets or sets a value indicating whether worms are placed manually by the players for their initial position - /// at round start. - /// - public bool ManualWormPlacement { get; set; } - - /// - /// Gets or sets the initial worm energy at round start. - /// - public byte WormEnergy { get; set; } - - /// - /// Gets or sets the turn time in seconds available for the player to move. Must be in the range of 0-127. - /// - public byte TurnTime - { - get => _turnTime; - set - { - if (value > 0x7F) - throw new ArgumentException("Turn time must be between 0-127.", nameof(value)); - _turnTime = value; - } - } - - /// - /// Gets or sets a value indicating whether the turn time is unlimited. If true, the - /// setting will be ignored. - /// - public bool TurnTimeInfinite { get; set; } - - /// - /// Gets or sets the round time before sudden death is triggered between 0-127 minutes. - /// - public byte RoundTimeMinutes - { - get => _roundTimeMinutes; - set - { - if (value > 0x7F) - throw new ArgumentException("Round time must be between 0-127 minutes.", nameof(value)); - _roundTimeMinutes = value; - } - } - - /// - /// Gets or sets the round time before sudden death is triggered in between 0-128 seconds. When 0, - /// is used instead. - /// - public byte RoundTimeSeconds - { - get => _roundTimeSeconds; - set - { - if (value > 0x80) - throw new ArgumentException("Round time must be between 0-128 seconds.", nameof(value)); - _roundTimeSeconds = value; - } - } - - /// - /// Gets or sets the number of round wins required to win the game. Must not be 0. - /// - public byte NumberOfWins - { - get => _numberOfWins; - set - { - if (value == 0) - throw new ArgumentException("Number of wins must not be 0.", nameof(value)); - _numberOfWins = value; - } - } - - /// - /// Gets or sets a value indicating whether blood effects are enabled. - /// - public bool Blood { get; set; } - - /// - /// Gets or sets a value indicating whether the Super Sheep weapon gets upgraded to the Aqua Sheep, which can - /// fly underwater. - /// - public bool AquaSheep { get; set; } - - /// - /// Gets or sets a value indicating whether sheeps will jump out of exploding crates. - /// - public bool SheepHeaven { get; set; } - - /// - /// Gets or sets a value indicating whether worms have infinity energy, killable only by drowning them. - /// - public bool GodWorms { get; set; } - - /// - /// Gets or sets a value indicating whether terrain cannot be destroyed by explosions. - /// - public bool IndestructibleLand { get; set; } - - /// - /// Gets or sets a value indicating whether the Grenade weapon is more powerful. - /// - public bool UpgradedGrenade { get; set; } - - /// - /// Gets or sets a value indicating whether the Shotgun weapon shoots twice for each of the two shots. - /// - public bool UpgradedShotgun { get; set; } - - /// - /// Gets or sets a value indicating whether cluster weapon explode into more clusters. - /// - public bool UpgradedCluster { get; set; } - - /// - /// Gets or sets a value indicating whether the Longbow weapon is more powerful. - /// - public bool UpgradedLongbow { get; set; } - - /// - /// Gets or sets a value indicating whether team weapons will be given to the teams, overriding the default - /// weapon settings for them. - /// - public bool EnableTeamWeapons { get; set; } - - /// - /// Gets or sets a value indicating whether super weapons can be collected from crates. - /// - public bool EnableSuperWeapons { get; set; } - - /// - /// Gets the array of instances, each mapping to one weapon at the index of - /// the enumeration. Depending on the scheme , super weapons - /// might not be stored in this array. - /// - public SchemeWeaponSetting[] Weapons { get; set; } - - // ---- RubberWorm Settings ---- - - /// - /// Gets or sets a value whether power is unlocked like in TestStuff. Configurable with the /alp command. - /// - public bool RwAntiLockPower { get; set; } - - /// - /// Gets or sets a value indicating whether worms falling into water will be reset to the last solid location - /// they stood on. Configurable with the /antisink command. - /// - public bool RwAntiSink { get; set; } - - /// - /// Gets or sets a value indicating whether the aim direction of a weapon will be reset at turn start. - /// Configurable with the /reaim command. - /// - public bool RwAutoReaim { get; set; } - - /// - /// Gets or sets a value whether the aim of a weapon loops in full rather than half circles. Configurable with - /// the /cira command. - /// - public bool RwCircularAim { get; set; } - - /// - /// Gets or sets the maximum number of crates existing on the map at the same time. 0 disables this feature, - /// default limit is 5. Configurable with the /cratelimit command. - /// - public byte RwCrateLimit { get; set; } - - /// - /// Gets or sets the maximum number of crates spawning per turn and enables the crate counter. Configurable with - /// the /craterate command. - /// - public byte RwCrateRate { get; set; } - - /// - /// Gets or sets a value indicating whether crate shower is enabled throughout a turn. Configurable with the - /// /crateshower command. - /// - public bool RwCrateShower { get; set; } - - /// - /// Gets or sets a value indicating whether weapon fuses can be selected between 0-9 seconds and herd counts - /// between 1-10 animals. Configurable with the /fuseex command. - /// - public bool RwExtendedFuse { get; set; } - - /// - /// Gets or sets a value indicating whether the turn timer is paused while launching a weapon. Configurable with - /// the /fdpt or /nopause commands. - /// - public bool RwFireDoesntPauseTimer { get; set; } - - /// - /// Gets or sets the maximum number of flame particles active at the same time in the scale of 100x. - /// Configurable with the /flames command. - /// - public byte RwFlameLimit { get; set; } - - /// - /// Gets or sets the friction deaccelerating objects touching solid ground. 96 is default, 100 is no friction, - /// higher values accelerate objects. Configurable with the /friction command. - /// - public byte RwFriction { get; set; } - - /// - /// Gets or sets the amount of gravity. Ranges from -64-63 where 0 and 12 are default gravity and negative - /// values pull objects upwards. Configurable with the /gravity command. When set, - /// and are reset. - /// - public sbyte RwGravity - { - get => _rwGravity; - set - { - if (value != 0) - { - RwGravityConstBlackHole = 0; - RwGravityPropBlackHole = 0; - } - _rwGravity = value; - } - } - - /// - /// gets or sets the amount of gravity acting as a constant black hole. Ranges from -32 to 31. Configurable with - /// the /cbh command. When set, and are reset. - /// - public sbyte RwGravityConstBlackHole - { - get => _rwGravityConstBlackHole; - set - { - if (value != 0) - { - RwGravity = 0; - RwGravityPropBlackHole = 0; - } - _rwGravityConstBlackHole = value; - } - } - - /// - /// Gets or sets the amount of gravity acting as a proportional black hole. Ranges from -32 to 31. Configurable - /// with the /pbh command. When set, and are - /// reset. - /// - public sbyte RwGravityPropBlackHole - { - get => _rwGravityPropBlackHole; - set - { - if (value != 0) - { - RwGravity = 0; - RwGravityConstBlackHole = 0; - } - _rwGravityPropBlackHole = value; - } - } - - /// - /// Gets or sets the Kaos game scheme mod. 0 for none, 1-5 for the corresponding mod. Configurable with the - /// /kaosmod command. - /// - public byte RwKaosMod - { - get => _rwKaosMod; - set - { - if (value > 0xF) - throw new ArgumentException("Kaos mod must not be greater than 15."); - _rwKaosMod = value; - } - } - - /// - /// Gets or sets the rope knocking power in percent, where 100 is the default power. Configurable with the - /// /knock command. - /// - public byte RwKnockForce { get; set; } - - /// - /// Gets or sets a value indicating whether the loss of control while moving a worm ends does no longer end the - /// turn and allows the player to continue moving. Configurable with the /ldet or /stoicworm commands. - /// - public bool RwLoseControlDoesntEndTurn { get; set; } - - /// - /// Gets or sets the maximum speed a player can reach while roping. 16 is default, 32 is like in TestStuff, 255 - /// is unlimited. Configurable with the /speed command. - /// - public byte RwMaxRopeSpeed { get; set; } - - /// - /// Gets or sets a value indicating whether using a weapon does no longer end the turn and allows to shoot - /// multiple weapons in one turn. Configurable with the /sdet or /multishot commands. - /// - public bool RwShotDoesntEndTurn { get; set; } - - /// - /// Gets or sets a value indicating whether the weapons Armageddon, Earthquake and Indian Nuke Test are affected - /// by aswell. Configurable with the /usw command. - /// - public bool RwShotDoesntEndTurnAll { get; set; } - - /// - /// Gets or sets a value indicating whether all objects are pushed by explosions near them. Configurable with - /// the /ope command. - /// - public bool RwObjectPushByExplosion { get; set; } - - /// - /// Gets or sets a value indicating whether worms can be selected at any time during the turn, as long as worm - /// selection is activated. Configurable with the /swat command. - /// - public bool RwSelectWormAnytime { get; set; } - - /// - /// Gets or sets a value indicating whether the Ninja Rope is more powerful. Configurable with the /ir or /rope+ - /// commands. - /// - public bool RwUpgradedRope { get; set; } - - /// - /// Gets or sets the (special) game version to be forced. Configurable with the /version command. - /// S. http://worms2d.info/List_of_Worms_Armageddon_logic_versions. - /// - public ushort RwVersionOverride { get; set; } - - /// - /// Gets or sets the amount of air viscosity affecting objects. Odd numbers affect worms too. Configurable with - /// the /visc command. - /// - public byte RwViscosity { get; set; } - - /// - /// Gets or sets a value indicating whether the selected weapon no longer resets to a remembered one upon - /// shooting it. Configurable with the /wdca command. - /// - public bool RwWeaponsDontChange { get; set; } - - /// - /// Gets or sets the influence power of the wind affecting all weapons. 255 is the same influence as the - /// Bazooka. Odd numbers affect worms too. Configurable with the /wind command. - /// - public byte RwWindPower { get; set; } - - /// - /// Gets or sets the power with which worms bounce off terrain, where 0 disables this feature and 255 fully - /// bounces worms back without speed loss. Configurable with the /rubber command. - /// - public byte RwWormBouncyness { get; set; } - - // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- - - /// - /// Loads the data from the given . - /// - /// The to load the data from. - public void Load(Stream stream) - { - using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true); - - // Read the header. - if (reader.ReadString(_signature.Length) != _signature) - throw new InvalidDataException("Invalid WSC file signature."); - Version = reader.ReadEnum(true); - - // Read the options. - HotSeatDelay = reader.Read1Byte(); - RetreatTime = reader.Read1Byte(); - RetreatTimeRope = reader.Read1Byte(); - ShowRoundTime = reader.ReadBoolean(); - AutomaticReplays = reader.ReadBoolean(); - FallDamage = (SchemeFallDamage)(reader.ReadByte() * 50 % 0x100 * 2); - ArtilleryMode = reader.ReadBoolean(); - SchemeEditor = reader.ReadEnum(false); - StockpilingMode = reader.ReadEnum(true); - WormSelectMode = reader.ReadEnum(true); - SuddenDeathEvent = reader.ReadEnum(true); - WaterRiseRate = (SchemeWaterRise)(Math.Pow(reader.ReadByte(), 2) * 5 % 0x100); - WeaponCrateProbability = reader.ReadSByte(); - DonorCards = reader.ReadBoolean(); - HealthCrateProbability = reader.ReadSByte(); - HealthCrateEnergy = reader.Read1Byte(); - UtilityCrateProbability = reader.ReadSByte(); - LoadObjectTypesAndCount(reader); - LoadMineDelayConfig(reader); - DudMines = reader.ReadBoolean(); - ManualWormPlacement = reader.ReadBoolean(); - WormEnergy = reader.Read1Byte(); - LoadTurnTimeConfig(reader); - LoadRoundTimeConfig(reader); - NumberOfWins = (byte)Math.Max(1, reader.ReadByte()); - Blood = reader.ReadBoolean(); - AquaSheep = reader.ReadBoolean(); - SheepHeaven = reader.ReadBoolean(); - GodWorms = reader.ReadBoolean(); - IndestructibleLand = reader.ReadBoolean(); - UpgradedGrenade = reader.ReadBoolean(); - UpgradedShotgun = reader.ReadBoolean(); - UpgradedCluster = reader.ReadBoolean(); - UpgradedLongbow = reader.ReadBoolean(); - EnableTeamWeapons = reader.ReadBoolean(); - EnableSuperWeapons = reader.ReadBoolean(); - - // Read the weapon settings. Old versions do not store super weapon settings. - Weapons = new SchemeWeaponSetting[64]; - int weaponCount = GetWeaponCount(); - for (int i = 0; i < weaponCount; i++) - Weapons[i] = reader.ReadStruct(); - - // Ignore possible unknown WWP trash at the end of the file. - - // Parse the RubberWorm settings. - LoadRubberWormSettings(); - } - - /// - /// Loads the data from the given file. - /// - /// The name of the file to load the data from. - public void Load(string fileName) - { - using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); - Load(stream); - } - - /// - /// Saves the data into the given . - /// - /// The to save the data to. - public void Save(Stream stream) => Save(stream, SchemeSaveFormat.ExtendedWithObjectCount); - - /// - /// Saves the data into the given with the specified . - /// - /// The to save the data to. - /// The to respect when storing the settings. - public void Save(Stream stream, SchemeSaveFormat format) - { - using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII); - - // Write the header. - writer.Write(_signature, StringCoding.Raw); - writer.Write((byte)Version); - - // Write the options. - writer.Write(HotSeatDelay); - writer.Write(RetreatTime); - writer.Write(RetreatTimeRope); - writer.Write(ShowRoundTime); - writer.Write(AutomaticReplays); - writer.Write((byte)((int)FallDamage / 4 * 41 % 0x80)); - writer.Write(ArtilleryMode); - writer.WriteEnum(SchemeEditor, false); - writer.WriteEnum(StockpilingMode, true); - writer.WriteEnum(WormSelectMode, true); - writer.WriteEnum(SuddenDeathEvent, true); - writer.Write(_mapWaterRiseToRaw[(byte)WaterRiseRate]); - writer.Write(WeaponCrateProbability); - writer.Write(DonorCards); - writer.Write(HealthCrateProbability); - writer.Write(HealthCrateEnergy); - writer.Write(UtilityCrateProbability); - SaveObjectTypesAndCount(writer, format); - SaveMineDelayConfig(writer); - writer.Write(DudMines); - writer.Write(ManualWormPlacement); - writer.Write(WormEnergy); - SaveTurnTimeConfig(writer); - SaveRoundTimeConfig(writer); - writer.Write(NumberOfWins); - writer.Write(Blood); - writer.Write(AquaSheep); - writer.Write(SheepHeaven); - writer.Write(GodWorms); - writer.Write(IndestructibleLand); - writer.Write(UpgradedGrenade); - writer.Write(UpgradedShotgun); - writer.Write(UpgradedCluster); - writer.Write(UpgradedLongbow); - writer.Write(EnableTeamWeapons); - writer.Write(EnableSuperWeapons); - - // Transfer the RubberWorm settings to unused weapon configuration. - SaveRubberWormSettings(); - - // Write the weapon settings. Old versions do not store super weapon settings. - int weaponCount = GetWeaponCount(); - foreach (SchemeWeaponSetting weapon in Weapons) - writer.WriteStruct(weapon); - } - - /// - /// Saves the data in the given file. - /// - /// The name of the file to save the data in. - public void Save(string fileName) => Save(fileName, SchemeSaveFormat.ExtendedWithObjectCount); - - /// - /// Saves the data in the given file with the specified . - /// - /// The name of the file to save the data in. - /// The to respect when storing the settings. - public void Save(string fileName, SchemeSaveFormat format) - { - using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); - Save(stream, format); - } - - // ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- - - private void LoadObjectTypesAndCount(BinaryStream reader) - { - // Invalid values default to 8 mines. - ObjectTypes = SchemeObjectType.Mines; - ObjectCount = SchemeObjectCount.Count8; - - byte raw = reader.Read1Byte(); - if (raw < 12) - { - // WA before 3.6.28.0 and WWP only store object type. - switch (raw) - { - case 0x00: - ObjectTypes = SchemeObjectType.None; - break; - case 0x02: - ObjectTypes = SchemeObjectType.OilDrums; - break; - case 0x05: - ObjectTypes = SchemeObjectType.Mines | SchemeObjectType.OilDrums; - break; - } - } - else - { - // WA since 3.6.28.0 encodes object type and count in one byte. - int modulo = raw % 4; - switch (modulo) - { - case 0x00: - ObjectTypes = SchemeObjectType.None; - break; - case 0x02: - ObjectTypes = SchemeObjectType.OilDrums; - break; - case 0x03: - ObjectTypes = SchemeObjectType.Mines | SchemeObjectType.OilDrums; - break; - } - ObjectCount = (SchemeObjectCount)_objectCounts[(raw - (8 + modulo)) / 4]; - } - } - - private void LoadMineDelayConfig(BinaryStream reader) - { - byte raw = reader.Read1Byte(); - if (raw == 4 || raw > 0x7F) - { - MineDelay = 0; - MineDelayRandom = true; - } - else - { - MineDelay = raw; - MineDelayRandom = false; - } - } - - private void LoadTurnTimeConfig(BinaryStream reader) - { - byte raw = reader.Read1Byte(); - if (raw > 0x7F) - { - TurnTime = 0; - TurnTimeInfinite = true; - } - else - { - TurnTime = raw; - TurnTimeInfinite = false; - } - } - - private void LoadRoundTimeConfig(BinaryStream reader) - { - byte raw = reader.Read1Byte(); - if (raw > 0x7F) - { - RoundTimeMinutes = 0; - RoundTimeSeconds = (byte)(raw - 0x7F); - } - else - { - RoundTimeMinutes = raw; - RoundTimeSeconds = 0; - } - } - - private void LoadRubberWormSettings() - { - RwEarthquakeProb earthquakeProb = (RwEarthquakeProb)Weapons[(int)SchemeWeapon.Earthquake].Probability; - RwAntiLockPower = earthquakeProb.HasFlag(RwEarthquakeProb.AntiLockPower); - RwAutoReaim = earthquakeProb.HasFlag(RwEarthquakeProb.AutoReaim); - RwCircularAim = earthquakeProb.HasFlag(RwEarthquakeProb.CircularAim); - RwShotDoesntEndTurnAll = earthquakeProb.HasFlag(RwEarthquakeProb.ShotDoesntEndTurnAll); - RwKaosMod = ((byte)earthquakeProb).DecodeByte(4, 4); - - RwAntiSink = Weapons[(int)SchemeWeapon.SheepStrike].Probability != 0; - RwCrateLimit = (byte)Weapons[(int)SchemeWeapon.MagicBullet].Probability; - RwCrateRate = (byte)Weapons[(int)SchemeWeapon.NuclearTest].Probability; - - RwMoleSquadronProb moleSquadronProb = (RwMoleSquadronProb)Weapons[(int)SchemeWeapon.MoleSquadron] - .Probability; - RwCrateShower = moleSquadronProb.HasFlag(RwMoleSquadronProb.CrateShower); - RwExtendedFuse = moleSquadronProb.HasFlag(RwMoleSquadronProb.ExtendedFuse); - RwFireDoesntPauseTimer = moleSquadronProb.HasFlag(RwMoleSquadronProb.FireDoesntPauseTimer); - RwLoseControlDoesntEndTurn = moleSquadronProb.HasFlag(RwMoleSquadronProb.LoseControlDoesntEndTurn); - RwObjectPushByExplosion = moleSquadronProb.HasFlag(RwMoleSquadronProb.ObjectPushByExplosion); - RwShotDoesntEndTurn = moleSquadronProb.HasFlag(RwMoleSquadronProb.ShotDoesntEndTurn); - RwUpgradedRope = moleSquadronProb.HasFlag(RwMoleSquadronProb.UpgradedRope); - RwWeaponsDontChange = moleSquadronProb.HasFlag(RwMoleSquadronProb.WeaponsDontChange); - - RwFlameLimit = (byte)Weapons[(int)SchemeWeapon.ScalesOfJustice].Probability; - RwFriction = (byte)Weapons[(int)SchemeWeapon.SalvationArmy].Probability; - - // 8th and 7th bit control constant / proportional black hole gravity, otherwise normal gravity. - byte mailStrikeProb = (byte)Weapons[(int)SchemeWeapon.MailStrike].Probability; - if (mailStrikeProb.GetBit(7)) - { - if (mailStrikeProb.GetBit(6)) - RwGravityPropBlackHole = mailStrikeProb.DecodeSByte(6); - else - RwGravityConstBlackHole = mailStrikeProb.DecodeSByte(6); - } - else - { - RwGravity = mailStrikeProb.DecodeSByte(7); - } - - RwKnockForce = (byte)Weapons[(int)SchemeWeapon.SuperBananaBomb].Probability; - RwMaxRopeSpeed = (byte)Weapons[(int)SchemeWeapon.MineStrike].Probability; - RwSelectWormAnytime = ((byte)Weapons[(int)SchemeWeapon.MBBomb].Probability).GetBit(1); - - byte[] versionBytes = new byte[2]; - versionBytes[0] = (byte)Weapons[(int)SchemeWeapon.SelectWorm].Probability; - versionBytes[1] = (byte)Weapons[(int)SchemeWeapon.Freeze].Probability; - RwVersionOverride = BitConverter.ToUInt16(versionBytes, 0); - - RwViscosity = (byte)Weapons[(int)SchemeWeapon.ConcreteDonkey].Probability; - RwWindPower = (byte)Weapons[(int)SchemeWeapon.SuicideBomber].Probability; - RwWormBouncyness = (byte)Weapons[(int)SchemeWeapon.Armageddon].Probability; - } - - private int GetWeaponCount() - { - // Old versions do not store super weapon settings. - return Version == SchemeVersion.Extended ? 64 : 45; - } - - private void SaveObjectTypesAndCount(BinaryStream writer, SchemeSaveFormat format) - { - byte raw = 0; - if (format == SchemeSaveFormat.ExtendedWithObjectCount) - { - // WA since 3.6.28.0 encodes object type and count in one byte. - switch (ObjectTypes) - { - case SchemeObjectType.Mines: - raw = 0x01; - break; - case SchemeObjectType.OilDrums: - raw = 0x02; - break; - case SchemeObjectType.Mines | SchemeObjectType.OilDrums: - raw = 0x03; - break; - } - // Get the index of the object count and compute the raw value from that. - int index = Array.IndexOf(_objectCounts, (byte)ObjectCount); - raw = (byte)(index * 4 + 8 + raw); - } - else - { - // WA before 3.6.28.0 and WWP only store object type. - switch (ObjectTypes) - { - case SchemeObjectType.Mines: - raw = 0x01; - break; - case SchemeObjectType.OilDrums: - raw = 0x02; - break; - case SchemeObjectType.Mines | SchemeObjectType.OilDrums: - raw = 0x05; - break; - } - } - writer.Write(raw); - } - - private void SaveMineDelayConfig(BinaryStream writer) - { - if (MineDelayRandom) - writer.Write((byte)4); - else - writer.Write(MineDelay); - } - - private void SaveTurnTimeConfig(BinaryStream writer) - { - if (TurnTimeInfinite) - writer.Write((byte)0xFF); - else - writer.Write(TurnTime); - } - - private void SaveRoundTimeConfig(BinaryStream writer) - { - if (RoundTimeSeconds > 0) - writer.Write((byte)(0xFF - (RoundTimeSeconds - 1))); - else - writer.Write(RoundTimeMinutes); - } - - private void SaveRubberWormSettings() - { - byte earthquakeProb = 0; - if (RwAntiLockPower) earthquakeProb |= (byte)RwEarthquakeProb.AntiLockPower; - if (RwAutoReaim) earthquakeProb |= (byte)RwEarthquakeProb.AutoReaim; - if (RwCircularAim) earthquakeProb |= (byte)RwEarthquakeProb.CircularAim; - if (RwShotDoesntEndTurnAll) earthquakeProb |= (byte)RwEarthquakeProb.ShotDoesntEndTurnAll; - earthquakeProb = earthquakeProb.Encode(RwKaosMod, 4); - Weapons[(int)SchemeWeapon.Earthquake].Probability = (sbyte)earthquakeProb; - - Weapons[(int)SchemeWeapon.SheepStrike].Probability = (sbyte)(RwAntiSink ? 1 : 0); - Weapons[(int)SchemeWeapon.MagicBullet].Probability = (sbyte)RwCrateLimit; - Weapons[(int)SchemeWeapon.NuclearTest].Probability = (sbyte)RwCrateRate; - - RwMoleSquadronProb moleSquadronProb = RwMoleSquadronProb.None; - if (RwCrateShower) moleSquadronProb |= RwMoleSquadronProb.CrateShower; - if (RwExtendedFuse) moleSquadronProb |= RwMoleSquadronProb.ExtendedFuse; - if (RwFireDoesntPauseTimer) moleSquadronProb |= RwMoleSquadronProb.FireDoesntPauseTimer; - if (RwLoseControlDoesntEndTurn) moleSquadronProb |= RwMoleSquadronProb.LoseControlDoesntEndTurn; - if (RwObjectPushByExplosion) moleSquadronProb |= RwMoleSquadronProb.ObjectPushByExplosion; - if (RwShotDoesntEndTurn) moleSquadronProb |= RwMoleSquadronProb.ShotDoesntEndTurn; - if (RwUpgradedRope) moleSquadronProb |= RwMoleSquadronProb.UpgradedRope; - if (RwWeaponsDontChange) moleSquadronProb |= RwMoleSquadronProb.WeaponsDontChange; - Weapons[(int)SchemeWeapon.MoleSquadron].Probability = (sbyte)moleSquadronProb; - - Weapons[(int)SchemeWeapon.ScalesOfJustice].Probability = (sbyte)RwFlameLimit; - Weapons[(int)SchemeWeapon.SalvationArmy].Probability = (sbyte)RwFriction; - - // 8th and 7th bit control constant / proportional black hole gravity, otherwise normal gravity. - byte mailStrikeProb = 0; - if (RwGravity != 0) - { - mailStrikeProb = mailStrikeProb.Encode(RwGravity, 7); - } - else if (RwGravityConstBlackHole != 0) - { - mailStrikeProb = mailStrikeProb.EnableBit(7); - mailStrikeProb = mailStrikeProb.Encode(RwGravityConstBlackHole, 6); - } - else if (RwGravityPropBlackHole != 0) - { - mailStrikeProb = mailStrikeProb.EnableBit(7); - mailStrikeProb = mailStrikeProb.EnableBit(6); - mailStrikeProb = mailStrikeProb.Encode(RwGravityPropBlackHole, 6); - } - Weapons[(int)SchemeWeapon.MailStrike].Probability = (sbyte)mailStrikeProb; - - Weapons[(int)SchemeWeapon.SuperBananaBomb].Probability = (sbyte)RwKnockForce; - Weapons[(int)SchemeWeapon.MineStrike].Probability = (sbyte)RwMaxRopeSpeed; - byte mbBombProb = ((byte)Weapons[(int)SchemeWeapon.MBBomb].Probability).SetBit(0, RwSelectWormAnytime); - Weapons[(int)SchemeWeapon.MBBomb].Probability = (sbyte)mbBombProb; - - byte[] versionBytes = BitConverter.GetBytes(RwVersionOverride); - Weapons[(int)SchemeWeapon.SelectWorm].Probability = (sbyte)versionBytes[0]; - Weapons[(int)SchemeWeapon.Freeze].Probability = (sbyte)versionBytes[1]; - - Weapons[(int)SchemeWeapon.ConcreteDonkey].Probability = (sbyte)RwViscosity; - Weapons[(int)SchemeWeapon.SuicideBomber].Probability = (sbyte)RwWindPower; - Weapons[(int)SchemeWeapon.Armageddon].Probability = (sbyte)RwWormBouncyness; - } - - // ---- ENUMERATIONS ------------------------------------------------------------------------------------------- - - [Flags] - private enum RwEarthquakeProb : byte - { - None = 0, - AutoReaim = 1 << 0, - CircularAim = 1 << 1, - AntiLockPower = 1 << 2, - ShotDoesntEndTurnAll = 1 << 3 - // Remaining bits represent kaosmod version. - } - - [Flags] - private enum RwMoleSquadronProb : byte - { - None = 0, - ShotDoesntEndTurn = 1 << 0, - LoseControlDoesntEndTurn = 1 << 1, - FireDoesntPauseTimer = 1 << 2, - UpgradedRope = 1 << 3, - CrateShower = 1 << 4, - ObjectPushByExplosion = 1 << 5, - WeaponsDontChange = 1 << 6, - ExtendedFuse = 1 << 7 - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Syroot.BinaryData; +using Syroot.Worms.Core; +using Syroot.Worms.Core.IO; + +namespace Syroot.Worms.Armageddon +{ + /// + /// Represents a scheme stored in a WSC file which contains game settings and armory configuration, including + /// RubberWorm settings encoded in those. + /// Used by WA and WWP. S. https://worms2d.info/Game_scheme_file. + /// + public class Scheme : ILoadableFile, ISaveableFile + { + // ---- CONSTANTS ---------------------------------------------------------------------------------------------- + + private const string _signature = "SCHM"; + + private const int _objectCount5Step = 30; + private const int _objectCount10Step = 44; + private const int _objectCountLastStep = 59; + + // Lookup tables. + private static readonly Dictionary _mapWaterRiseToRaw = new Dictionary() + { + [0] = 0, + [5] = 1, + [13] = 19, + [20] = 2, + [21] = 55, + [29] = 43, + [37] = 47, + [45] = 3, + [52] = 26, + [53] = 25, + [61] = 27, + [64] = 8, + [69] = 33, + [77] = 13, + [80] = 4, + [84] = 18, + [85] = 23, + [93] = 11, + [101] = 15, + [109] = 29, + [116] = 22, + [117] = 57, + [125] = 5, + [133] = 63, + [141] = 45, + [148] = 30, + [149] = 9, + [157] = 21, + [165] = 17, + [173] = 61, + [180] = 6, + [181] = 39, + [189] = 37, + [197] = 31, + [205] = 51, + [208] = 12, + [212] = 14, + [213] = 41, + [221] = 53, + [229] = 49, + [237] = 35, + [244] = 10, + [245] = 7, + [253] = 59 + }; + + // ---- MEMBERS ------------------------------------------------------------------------------------------------ + + private byte _objectCount; + private byte _mineDelay; + private byte _turnTime; + private byte _roundTimeMinutes; + private byte _roundTimeSeconds; + private byte _numberOfWins; + private sbyte _rwGravity; + private sbyte _rwGravityConstBlackHole; + private sbyte _rwGravityPropBlackHole; + private byte _rwKaosMod; + + // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ + + /// + /// Initializes a new instance of the class. + /// + public Scheme() + { + Version = SchemeVersion.Extended; + Weapons = new SchemeWeaponSetting[GetWeaponCount()]; + } + + /// + /// Initializes a new instance of the class, loading the data from the given + /// . + /// + /// The to load the data from. + public Scheme(Stream stream) => Load(stream); + + /// + /// Initializes a new instance of the class, loading the data from the given file. + /// + /// The name of the file to load the data from. + public Scheme(string fileName) => Load(fileName); + + // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + /// + /// Gets or sets the of this scheme, controlling whether super weapon settings + /// are stored or not. + /// + public SchemeVersion Version { get; set; } + + /// + /// Gets or sets the delay in seconds between each team's turn to allow relaxed switching of seats. + /// + public byte HotSeatDelay { get; set; } + + /// + /// Gets or sets the time in seconds available for a worm to retreat after using a weapon which ends the turn + /// while standing on land. + /// + public byte RetreatTime { get; set; } + + /// + /// Gets or sets the time in seconds available for a worm to retreat after using a weapon which ends the turn + /// while on a rope. + /// + public byte RetreatTimeRope { get; set; } + + /// + /// Gets or sets a value indicating whether the total round time until sudden death will be displayed in the + /// turn timer. + /// + public bool ShowRoundTime { get; set; } + + /// + /// Gets or sets a value indicating whether significant turns will be replayed in offline games. + /// + public bool AutomaticReplays { get; set; } + + /// + /// Gets or sets the percentual amount of fall damage applied, relative to normal fall damage being 100%. + /// + public SchemeFallDamage FallDamage { get; set; } + + /// + /// Gets or sets a value indicating whether worms cannot walk and are mostly stuck at their current position + /// without using any utilities. + /// + public bool ArtilleryMode { get; set; } + + /// + /// Gets or sets a value indicating the scheme editor used to modify this scheme. Originally used to indicate + /// the unimplemented Bounty Mode. + /// + public SchemeEditor SchemeEditor { get; set; } + + /// + /// Gets or sets a value indicating the stockpiling of armory between game rounds. + /// + public SchemeStockpiling StockpilingMode { get; set; } + + /// + /// Gets or sets a value indicating the worm selection order determining the next worm to be played. + /// + public SchemeWormSelect WormSelectMode { get; set; } + + /// + /// Gets or sets a value indicating the action triggered when Sudden Death starts. + /// + public SchemeSuddenDeathEvent SuddenDeathEvent { get; set; } + + /// + /// Gets or sets the amount in pixels which the water will rise between turns after Sudden Death was triggered. + /// + public SchemeWaterRise WaterRiseRate { get; set; } + + /// + /// Gets or sets the percentual probability of a weapon crate to drop between turns. Negative values might crash + /// the game. + /// + public sbyte WeaponCrateProbability { get; set; } + + /// + /// Gets or sets a value indicating whether donor cards can spawn upon a worm's death. + /// + public bool DonorCards { get; set; } + + /// + /// Gets or sets the percentual probability of a health crate to drop between turns. Negative values might crash + /// the game. + /// + public sbyte HealthCrateProbability { get; set; } + + /// + /// Gets or sets the amount of health included in a health crate added to the collecting worm's energy. + /// + public byte HealthCrateEnergy { get; set; } + + /// + /// Gets or sets the percentual probability of a utility crate to drop between turns. Negative values might + /// crash the game. + /// + public sbyte UtilityCrateProbability { get; set; } + + /// + /// Gets or sets the type of objects which can be placed on the map. + /// + public SchemeObjectType ObjectTypes { get; set; } + + /// + /// Gets or sets the maximum number of objects (mines or oil drums) on the map. Note that setting this value + /// rounds it valid count. + /// + public byte ObjectCount + { + get => _objectCount; + set + { + if (value > 100) + _objectCount = (byte)(value - value % 10); + else if (value > 30) + _objectCount = (byte)(value - value % 5); + else if (value == 0) + _objectCount = 1; + else + _objectCount = value; + } + } + + /// + /// Gets or sets the number of seconds a mine requires to explode. Can be 1-3 and 5-127 seconds. + /// + public byte MineDelay + { + get => _mineDelay; + set + { + if (value == 4 || value > 0x7F) + throw new ArgumentException("Mine delay must be between 0-127 and not be 4.", nameof(value)); + _mineDelay = value; + } + } + + /// + /// Gets or sets a value indicating whether the mine fuse will be randomly chosen between fractions of 1 to 3 + /// seconds. If true, the setting will be ignored. + /// + public bool MineDelayRandom { get; set; } + + /// + /// Gets or sets a value indicating whether mines can refuse to explode after their count down. + /// + public bool DudMines { get; set; } + + /// + /// Gets or sets a value indicating whether worms are placed manually by the players for their initial position + /// at round start. + /// + public bool ManualWormPlacement { get; set; } + + /// + /// Gets or sets the initial worm energy at round start. + /// + public byte WormEnergy { get; set; } + + /// + /// Gets or sets the turn time in seconds available for the player to move. Must be in the range of 0-127. + /// + public byte TurnTime + { + get => _turnTime; + set + { + if (value > 0x7F) + throw new ArgumentException("Turn time must be between 0-127.", nameof(value)); + _turnTime = value; + } + } + + /// + /// Gets or sets a value indicating whether the turn time is unlimited. If true, the + /// setting will be ignored. + /// + public bool TurnTimeInfinite { get; set; } + + /// + /// Gets or sets the round time before sudden death is triggered between 0-127 minutes. + /// + public byte RoundTimeMinutes + { + get => _roundTimeMinutes; + set + { + if (value > 0x7F) + throw new ArgumentException("Round time must be between 0-127 minutes.", nameof(value)); + _roundTimeMinutes = value; + } + } + + /// + /// Gets or sets the round time before sudden death is triggered in between 0-128 seconds. When 0, + /// is used instead. + /// + public byte RoundTimeSeconds + { + get => _roundTimeSeconds; + set + { + if (value > 0x80) + throw new ArgumentException("Round time must be between 0-128 seconds.", nameof(value)); + _roundTimeSeconds = value; + } + } + + /// + /// Gets or sets the number of round wins required to win the game. Must not be 0. + /// + public byte NumberOfWins + { + get => _numberOfWins; + set + { + if (value == 0) + throw new ArgumentException("Number of wins must not be 0.", nameof(value)); + _numberOfWins = value; + } + } + + /// + /// Gets or sets a value indicating whether blood effects are enabled. + /// + public bool Blood { get; set; } + + /// + /// Gets or sets a value indicating whether the Super Sheep weapon gets upgraded to the Aqua Sheep, which can + /// fly underwater. + /// + public bool AquaSheep { get; set; } + + /// + /// Gets or sets a value indicating whether sheeps will jump out of exploding crates. + /// + public bool SheepHeaven { get; set; } + + /// + /// Gets or sets a value indicating whether worms have infinity energy, killable only by drowning them. + /// + public bool GodWorms { get; set; } + + /// + /// Gets or sets a value indicating whether terrain cannot be destroyed by explosions. + /// + public bool IndestructibleLand { get; set; } + + /// + /// Gets or sets a value indicating whether the Grenade weapon is more powerful. + /// + public bool UpgradedGrenade { get; set; } + + /// + /// Gets or sets a value indicating whether the Shotgun weapon shoots twice for each of the two shots. + /// + public bool UpgradedShotgun { get; set; } + + /// + /// Gets or sets a value indicating whether cluster weapon explode into more clusters. + /// + public bool UpgradedCluster { get; set; } + + /// + /// Gets or sets a value indicating whether the Longbow weapon is more powerful. + /// + public bool UpgradedLongbow { get; set; } + + /// + /// Gets or sets a value indicating whether team weapons will be given to the teams, overriding the default + /// weapon settings for them. + /// + public bool EnableTeamWeapons { get; set; } + + /// + /// Gets or sets a value indicating whether super weapons can be collected from crates. + /// + public bool EnableSuperWeapons { get; set; } + + /// + /// Gets the array of instances, each mapping to one weapon at the index of + /// the enumeration. Depending on the scheme , super weapons + /// might not be stored in this array. + /// + public SchemeWeaponSetting[] Weapons { get; set; } + + // ---- RubberWorm Settings ---- + + /// + /// Gets or sets a value whether power is unlocked like in TestStuff. Configurable with the /alp command. + /// + public bool RwAntiLockPower { get; set; } + + /// + /// Gets or sets a value indicating whether worms falling into water will be reset to the last solid location + /// they stood on. Configurable with the /antisink command. + /// + public bool RwAntiSink { get; set; } + + /// + /// Gets or sets a value indicating whether the aim direction of a weapon will be reset at turn start. + /// Configurable with the /reaim command. + /// + public bool RwAutoReaim { get; set; } + + /// + /// Gets or sets a value whether the aim of a weapon loops in full rather than half circles. Configurable with + /// the /cira command. + /// + public bool RwCircularAim { get; set; } + + /// + /// Gets or sets the maximum number of crates existing on the map at the same time. 0 disables this feature, + /// default limit is 5. Configurable with the /cratelimit command. + /// + public byte RwCrateLimit { get; set; } + + /// + /// Gets or sets the maximum number of crates spawning per turn and enables the crate counter. Configurable with + /// the /craterate command. + /// + public byte RwCrateRate { get; set; } + + /// + /// Gets or sets a value indicating whether crate shower is enabled throughout a turn. Configurable with the + /// /crateshower command. + /// + public bool RwCrateShower { get; set; } + + /// + /// Gets or sets a value indicating whether weapon fuses can be selected between 0-9 seconds and herd counts + /// between 1-10 animals. Configurable with the /fuseex command. + /// + public bool RwExtendedFuse { get; set; } + + /// + /// Gets or sets a value indicating whether the turn timer is paused while launching a weapon. Configurable with + /// the /fdpt or /nopause commands. + /// + public bool RwFireDoesntPauseTimer { get; set; } + + /// + /// Gets or sets the maximum number of flame particles active at the same time in the scale of 100x. + /// Configurable with the /flames command. + /// + public byte RwFlameLimit { get; set; } + + /// + /// Gets or sets the friction deaccelerating objects touching solid ground. 96 is default, 100 is no friction, + /// higher values accelerate objects. Configurable with the /friction command. + /// + public byte RwFriction { get; set; } + + /// + /// Gets or sets the amount of gravity. Ranges from -64-63 where 0 and 12 are default gravity and negative + /// values pull objects upwards. Configurable with the /gravity command. When set, + /// and are reset. + /// + public sbyte RwGravity + { + get => _rwGravity; + set + { + if (value != 0) + { + RwGravityConstBlackHole = 0; + RwGravityPropBlackHole = 0; + } + _rwGravity = value; + } + } + + /// + /// gets or sets the amount of gravity acting as a constant black hole. Ranges from -32 to 31. Configurable with + /// the /cbh command. When set, and are reset. + /// + public sbyte RwGravityConstBlackHole + { + get => _rwGravityConstBlackHole; + set + { + if (value != 0) + { + RwGravity = 0; + RwGravityPropBlackHole = 0; + } + _rwGravityConstBlackHole = value; + } + } + + /// + /// Gets or sets the amount of gravity acting as a proportional black hole. Ranges from -32 to 31. Configurable + /// with the /pbh command. When set, and are + /// reset. + /// + public sbyte RwGravityPropBlackHole + { + get => _rwGravityPropBlackHole; + set + { + if (value != 0) + { + RwGravity = 0; + RwGravityConstBlackHole = 0; + } + _rwGravityPropBlackHole = value; + } + } + + /// + /// Gets or sets the Kaos game scheme mod. 0 for none, 1-5 for the corresponding mod. Configurable with the + /// /kaosmod command. + /// + public byte RwKaosMod + { + get => _rwKaosMod; + set + { + if (value > 0xF) + throw new ArgumentException("Kaos mod must not be greater than 15."); + _rwKaosMod = value; + } + } + + /// + /// Gets or sets the rope knocking power in percent, where 100 is the default power. Configurable with the + /// /knock command. + /// + public byte RwKnockForce { get; set; } + + /// + /// Gets or sets a value indicating whether the loss of control while moving a worm ends does no longer end the + /// turn and allows the player to continue moving. Configurable with the /ldet or /stoicworm commands. + /// + public bool RwLoseControlDoesntEndTurn { get; set; } + + /// + /// Gets or sets the maximum speed a player can reach while roping. 16 is default, 32 is like in TestStuff, 255 + /// is unlimited. Configurable with the /speed command. + /// + public byte RwMaxRopeSpeed { get; set; } + + /// + /// Gets or sets a value indicating whether using a weapon does no longer end the turn and allows to shoot + /// multiple weapons in one turn. Configurable with the /sdet or /multishot commands. + /// + public bool RwShotDoesntEndTurn { get; set; } + + /// + /// Gets or sets a value indicating whether the weapons Armageddon, Earthquake and Indian Nuke Test are affected + /// by aswell. Configurable with the /usw command. + /// + public bool RwShotDoesntEndTurnAll { get; set; } + + /// + /// Gets or sets a value indicating whether all objects are pushed by explosions near them. Configurable with + /// the /ope command. + /// + public bool RwObjectPushByExplosion { get; set; } + + /// + /// Gets or sets a value indicating whether worms can be selected at any time during the turn, as long as worm + /// selection is activated. Configurable with the /swat command. + /// + public bool RwSelectWormAnytime { get; set; } + + /// + /// Gets or sets a value indicating whether the Ninja Rope is more powerful. Configurable with the /ir or /rope+ + /// commands. + /// + public bool RwUpgradedRope { get; set; } + + /// + /// Gets or sets the (special) game version to be forced. Configurable with the /version command. + /// S. http://worms2d.info/List_of_Worms_Armageddon_logic_versions. + /// + public ushort RwVersionOverride { get; set; } + + /// + /// Gets or sets the amount of air viscosity affecting objects. Odd numbers affect worms too. Configurable with + /// the /visc command. + /// + public byte RwViscosity { get; set; } + + /// + /// Gets or sets a value indicating whether the selected weapon no longer resets to a remembered one upon + /// shooting it. Configurable with the /wdca command. + /// + public bool RwWeaponsDontChange { get; set; } + + /// + /// Gets or sets the influence power of the wind affecting all weapons. 255 is the same influence as the + /// Bazooka. Odd numbers affect worms too. Configurable with the /wind command. + /// + public byte RwWindPower { get; set; } + + /// + /// Gets or sets the power with which worms bounce off terrain, where 0 disables this feature and 255 fully + /// bounces worms back without speed loss. Configurable with the /rubber command. + /// + public byte RwWormBouncyness { get; set; } + + // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- + + /// + /// Loads the data from the given . + /// + /// The to load the data from. + public void Load(Stream stream) + { + using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true); + + // Read the header. + if (reader.ReadString(_signature.Length) != _signature) + throw new InvalidDataException("Invalid WSC file signature."); + Version = reader.ReadEnum(true); + + // Read the options. + HotSeatDelay = reader.Read1Byte(); + RetreatTime = reader.Read1Byte(); + RetreatTimeRope = reader.Read1Byte(); + ShowRoundTime = reader.ReadBoolean(); + AutomaticReplays = reader.ReadBoolean(); + FallDamage = (SchemeFallDamage)(reader.ReadByte() * 50 % 0x100 * 2); + ArtilleryMode = reader.ReadBoolean(); + SchemeEditor = reader.ReadEnum(false); + StockpilingMode = reader.ReadEnum(true); + WormSelectMode = reader.ReadEnum(true); + SuddenDeathEvent = reader.ReadEnum(true); + WaterRiseRate = (SchemeWaterRise)(Math.Pow(reader.ReadByte(), 2) * 5 % 0x100); + WeaponCrateProbability = reader.ReadSByte(); + DonorCards = reader.ReadBoolean(); + HealthCrateProbability = reader.ReadSByte(); + HealthCrateEnergy = reader.Read1Byte(); + UtilityCrateProbability = reader.ReadSByte(); + LoadObjectTypesAndCount(reader); + LoadMineDelayConfig(reader); + DudMines = reader.ReadBoolean(); + ManualWormPlacement = reader.ReadBoolean(); + WormEnergy = reader.Read1Byte(); + LoadTurnTimeConfig(reader); + LoadRoundTimeConfig(reader); + NumberOfWins = (byte)Math.Max(1, reader.ReadByte()); + Blood = reader.ReadBoolean(); + AquaSheep = reader.ReadBoolean(); + SheepHeaven = reader.ReadBoolean(); + GodWorms = reader.ReadBoolean(); + IndestructibleLand = reader.ReadBoolean(); + UpgradedGrenade = reader.ReadBoolean(); + UpgradedShotgun = reader.ReadBoolean(); + UpgradedCluster = reader.ReadBoolean(); + UpgradedLongbow = reader.ReadBoolean(); + EnableTeamWeapons = reader.ReadBoolean(); + EnableSuperWeapons = reader.ReadBoolean(); + + // Read the weapon settings. Old versions do not store super weapon settings. + Weapons = new SchemeWeaponSetting[64]; + int weaponCount = GetWeaponCount(); + for (int i = 0; i < weaponCount; i++) + Weapons[i] = reader.ReadStruct(); + + // Ignore possible unknown WWP trash at the end of the file. + + // Parse the RubberWorm settings. + LoadRubberWormSettings(); + } + + /// + /// Loads the data from the given file. + /// + /// The name of the file to load the data from. + public void Load(string fileName) + { + using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + Load(stream); + } + + /// + /// Saves the data into the given . + /// + /// The to save the data to. + public void Save(Stream stream) => Save(stream, SchemeSaveFormat.ExtendedWithObjectCount); + + /// + /// Saves the data into the given with the specified . + /// + /// The to save the data to. + /// The to respect when storing the settings. + public void Save(Stream stream, SchemeSaveFormat format) + { + using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true); + + // Write the header. + writer.Write(_signature, StringCoding.Raw); + writer.Write((byte)Version); + + // Write the options. + writer.Write(HotSeatDelay); + writer.Write(RetreatTime); + writer.Write(RetreatTimeRope); + writer.Write(ShowRoundTime); + writer.Write(AutomaticReplays); + writer.Write((byte)((int)FallDamage / 4 * 41 % 0x80)); + writer.Write(ArtilleryMode); + writer.WriteEnum(SchemeEditor, false); + writer.WriteEnum(StockpilingMode, true); + writer.WriteEnum(WormSelectMode, true); + writer.WriteEnum(SuddenDeathEvent, true); + writer.Write(_mapWaterRiseToRaw[(byte)WaterRiseRate]); + writer.Write(WeaponCrateProbability); + writer.Write(DonorCards); + writer.Write(HealthCrateProbability); + writer.Write(HealthCrateEnergy); + writer.Write(UtilityCrateProbability); + SaveObjectTypesAndCount(writer, format); + SaveMineDelayConfig(writer); + writer.Write(DudMines); + writer.Write(ManualWormPlacement); + writer.Write(WormEnergy); + SaveTurnTimeConfig(writer); + SaveRoundTimeConfig(writer); + writer.Write(NumberOfWins); + writer.Write(Blood); + writer.Write(AquaSheep); + writer.Write(SheepHeaven); + writer.Write(GodWorms); + writer.Write(IndestructibleLand); + writer.Write(UpgradedGrenade); + writer.Write(UpgradedShotgun); + writer.Write(UpgradedCluster); + writer.Write(UpgradedLongbow); + writer.Write(EnableTeamWeapons); + writer.Write(EnableSuperWeapons); + + // Transfer the RubberWorm settings to unused weapon configuration. + SaveRubberWormSettings(); + + // Write the weapon settings. Old versions do not store super weapon settings. + int weaponCount = GetWeaponCount(); + foreach (SchemeWeaponSetting weapon in Weapons) + writer.WriteStruct(weapon); + } + + /// + /// Saves the data in the given file. + /// + /// The name of the file to save the data in. + public void Save(string fileName) => Save(fileName, SchemeSaveFormat.ExtendedWithObjectCount); + + /// + /// Saves the data in the given file with the specified . + /// + /// The name of the file to save the data in. + /// The to respect when storing the settings. + public void Save(string fileName, SchemeSaveFormat format) + { + using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); + Save(stream, format); + } + + // ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- + + private void LoadObjectTypesAndCount(BinaryStream reader) + { + byte raw = reader.Read1Byte(); + switch (raw) + { + case 3: case 4: raw = 1; break; + case 6: case 7: raw = 0; break; + } + + // Determine object count. + int step = Math.Min(raw < 8 ? 8 : raw / 4 - 2, _objectCountLastStep); + if (step >= _objectCount10Step) + step = 100 + 10 * (step - _objectCount10Step); + else if (step >= _objectCount5Step) + step = 30 + 5 * (step - _objectCount5Step); + ObjectCount = (byte)step; + + // Determine object types. + ObjectTypes = raw == 5 // old "both" value + ? SchemeObjectType.Both + : (SchemeObjectType)(raw & (byte)SchemeObjectType.Both); + } + + private void LoadMineDelayConfig(BinaryStream reader) + { + byte raw = reader.Read1Byte(); + if (raw == 4 || raw > 0x7F) + { + MineDelay = 0; + MineDelayRandom = true; + } + else + { + MineDelay = raw; + MineDelayRandom = false; + } + } + + private void LoadTurnTimeConfig(BinaryStream reader) + { + byte raw = reader.Read1Byte(); + if (raw > 0x7F) + { + TurnTime = 0; + TurnTimeInfinite = true; + } + else + { + TurnTime = raw; + TurnTimeInfinite = false; + } + } + + private void LoadRoundTimeConfig(BinaryStream reader) + { + byte raw = reader.Read1Byte(); + if (raw > 0x7F) + { + RoundTimeMinutes = 0; + RoundTimeSeconds = (byte)(raw - 0x7F); + } + else + { + RoundTimeMinutes = raw; + RoundTimeSeconds = 0; + } + } + + private void LoadRubberWormSettings() + { + RwEarthquakeProb earthquakeProb = (RwEarthquakeProb)Weapons[(int)SchemeWeapon.Earthquake].Probability; + RwAntiLockPower = earthquakeProb.HasFlag(RwEarthquakeProb.AntiLockPower); + RwAutoReaim = earthquakeProb.HasFlag(RwEarthquakeProb.AutoReaim); + RwCircularAim = earthquakeProb.HasFlag(RwEarthquakeProb.CircularAim); + RwShotDoesntEndTurnAll = earthquakeProb.HasFlag(RwEarthquakeProb.ShotDoesntEndTurnAll); + RwKaosMod = ((byte)earthquakeProb).DecodeByte(4, 4); + + RwAntiSink = Weapons[(int)SchemeWeapon.SheepStrike].Probability != 0; + RwCrateLimit = (byte)Weapons[(int)SchemeWeapon.MagicBullet].Probability; + RwCrateRate = (byte)Weapons[(int)SchemeWeapon.NuclearTest].Probability; + + RwMoleSquadronProb moleSquadronProb = (RwMoleSquadronProb)Weapons[(int)SchemeWeapon.MoleSquadron] + .Probability; + RwCrateShower = moleSquadronProb.HasFlag(RwMoleSquadronProb.CrateShower); + RwExtendedFuse = moleSquadronProb.HasFlag(RwMoleSquadronProb.ExtendedFuse); + RwFireDoesntPauseTimer = moleSquadronProb.HasFlag(RwMoleSquadronProb.FireDoesntPauseTimer); + RwLoseControlDoesntEndTurn = moleSquadronProb.HasFlag(RwMoleSquadronProb.LoseControlDoesntEndTurn); + RwObjectPushByExplosion = moleSquadronProb.HasFlag(RwMoleSquadronProb.ObjectPushByExplosion); + RwShotDoesntEndTurn = moleSquadronProb.HasFlag(RwMoleSquadronProb.ShotDoesntEndTurn); + RwUpgradedRope = moleSquadronProb.HasFlag(RwMoleSquadronProb.UpgradedRope); + RwWeaponsDontChange = moleSquadronProb.HasFlag(RwMoleSquadronProb.WeaponsDontChange); + + RwFlameLimit = (byte)Weapons[(int)SchemeWeapon.ScalesOfJustice].Probability; + RwFriction = (byte)Weapons[(int)SchemeWeapon.SalvationArmy].Probability; + + // 8th and 7th bit control constant / proportional black hole gravity, otherwise normal gravity. + byte mailStrikeProb = (byte)Weapons[(int)SchemeWeapon.MailStrike].Probability; + if (!mailStrikeProb.GetBit(7)) + RwGravity = mailStrikeProb.DecodeSByte(7); + else if (mailStrikeProb.GetBit(6)) + RwGravityPropBlackHole = mailStrikeProb.DecodeSByte(6); + else + RwGravityConstBlackHole = mailStrikeProb.DecodeSByte(6); + + RwKnockForce = (byte)Weapons[(int)SchemeWeapon.SuperBananaBomb].Probability; + RwMaxRopeSpeed = (byte)Weapons[(int)SchemeWeapon.MineStrike].Probability; + RwSelectWormAnytime = ((byte)Weapons[(int)SchemeWeapon.MBBomb].Probability).GetBit(1); + + byte[] versionBytes = new byte[2]; + versionBytes[0] = (byte)Weapons[(int)SchemeWeapon.SelectWorm].Probability; + versionBytes[1] = (byte)Weapons[(int)SchemeWeapon.Freeze].Probability; + RwVersionOverride = BitConverter.ToUInt16(versionBytes, 0); + + RwViscosity = (byte)Weapons[(int)SchemeWeapon.ConcreteDonkey].Probability; + RwWindPower = (byte)Weapons[(int)SchemeWeapon.SuicideBomber].Probability; + RwWormBouncyness = (byte)Weapons[(int)SchemeWeapon.Armageddon].Probability; + } + + private int GetWeaponCount() + { + // Old versions do not store super weapon settings. + return Version == SchemeVersion.Extended ? 64 : 45; + } + + private void SaveObjectTypesAndCount(BinaryStream writer, SchemeSaveFormat format) + { + byte raw; + if (format == SchemeSaveFormat.ExtendedWithObjectCount && ObjectCount != 8) + { + // WA since 3.6.28.0 encodes object type and count in one byte. + raw = (byte)ObjectTypes; + + int step = ObjectCount; + if (ObjectCount >= 100) + step = _objectCount10Step + (ObjectCount - 100) / 10; + else if (ObjectCount >= 30) + step = _objectCount5Step + (ObjectCount - 30) / 5; + raw |= (byte)(4 * step + 8); + } + else + { + // WA before 3.6.28.0 and WWP only store object type. + raw = ObjectTypes == SchemeObjectType.Both ? (byte)5 : (byte)ObjectTypes; + } + writer.Write(raw); + } + + private void SaveMineDelayConfig(BinaryStream writer) + { + if (MineDelayRandom) + writer.Write((byte)4); + else + writer.Write(MineDelay); + } + + private void SaveTurnTimeConfig(BinaryStream writer) + { + if (TurnTimeInfinite) + writer.Write((byte)0xFF); + else + writer.Write(TurnTime); + } + + private void SaveRoundTimeConfig(BinaryStream writer) + { + if (RoundTimeSeconds > 0) + writer.Write((byte)(0xFF - (RoundTimeSeconds - 1))); + else + writer.Write(RoundTimeMinutes); + } + + private void SaveRubberWormSettings() + { + byte earthquakeProb = 0; + if (RwAntiLockPower) earthquakeProb |= (byte)RwEarthquakeProb.AntiLockPower; + if (RwAutoReaim) earthquakeProb |= (byte)RwEarthquakeProb.AutoReaim; + if (RwCircularAim) earthquakeProb |= (byte)RwEarthquakeProb.CircularAim; + if (RwShotDoesntEndTurnAll) earthquakeProb |= (byte)RwEarthquakeProb.ShotDoesntEndTurnAll; + earthquakeProb = earthquakeProb.Encode(RwKaosMod, 4); + Weapons[(int)SchemeWeapon.Earthquake].Probability = (sbyte)earthquakeProb; + + Weapons[(int)SchemeWeapon.SheepStrike].Probability = (sbyte)(RwAntiSink ? 1 : 0); + Weapons[(int)SchemeWeapon.MagicBullet].Probability = (sbyte)RwCrateLimit; + Weapons[(int)SchemeWeapon.NuclearTest].Probability = (sbyte)RwCrateRate; + + RwMoleSquadronProb moleSquadronProb = RwMoleSquadronProb.None; + if (RwCrateShower) moleSquadronProb |= RwMoleSquadronProb.CrateShower; + if (RwExtendedFuse) moleSquadronProb |= RwMoleSquadronProb.ExtendedFuse; + if (RwFireDoesntPauseTimer) moleSquadronProb |= RwMoleSquadronProb.FireDoesntPauseTimer; + if (RwLoseControlDoesntEndTurn) moleSquadronProb |= RwMoleSquadronProb.LoseControlDoesntEndTurn; + if (RwObjectPushByExplosion) moleSquadronProb |= RwMoleSquadronProb.ObjectPushByExplosion; + if (RwShotDoesntEndTurn) moleSquadronProb |= RwMoleSquadronProb.ShotDoesntEndTurn; + if (RwUpgradedRope) moleSquadronProb |= RwMoleSquadronProb.UpgradedRope; + if (RwWeaponsDontChange) moleSquadronProb |= RwMoleSquadronProb.WeaponsDontChange; + Weapons[(int)SchemeWeapon.MoleSquadron].Probability = (sbyte)moleSquadronProb; + + Weapons[(int)SchemeWeapon.ScalesOfJustice].Probability = (sbyte)RwFlameLimit; + Weapons[(int)SchemeWeapon.SalvationArmy].Probability = (sbyte)RwFriction; + + // 8th and 7th bit control constant / proportional black hole gravity, otherwise normal gravity. + byte mailStrikeProb = 0; + if (RwGravity != 0) + { + mailStrikeProb = mailStrikeProb.Encode(RwGravity, 7); + } + else if (RwGravityConstBlackHole != 0) + { + mailStrikeProb = mailStrikeProb.EnableBit(7); + mailStrikeProb = mailStrikeProb.Encode(RwGravityConstBlackHole, 6); + } + else if (RwGravityPropBlackHole != 0) + { + mailStrikeProb = mailStrikeProb.EnableBit(7); + mailStrikeProb = mailStrikeProb.EnableBit(6); + mailStrikeProb = mailStrikeProb.Encode(RwGravityPropBlackHole, 6); + } + Weapons[(int)SchemeWeapon.MailStrike].Probability = (sbyte)mailStrikeProb; + + Weapons[(int)SchemeWeapon.SuperBananaBomb].Probability = (sbyte)RwKnockForce; + Weapons[(int)SchemeWeapon.MineStrike].Probability = (sbyte)RwMaxRopeSpeed; + byte mbBombProb = ((byte)Weapons[(int)SchemeWeapon.MBBomb].Probability).SetBit(0, RwSelectWormAnytime); + Weapons[(int)SchemeWeapon.MBBomb].Probability = (sbyte)mbBombProb; + + byte[] versionBytes = BitConverter.GetBytes(RwVersionOverride); + Weapons[(int)SchemeWeapon.SelectWorm].Probability = (sbyte)versionBytes[0]; + Weapons[(int)SchemeWeapon.Freeze].Probability = (sbyte)versionBytes[1]; + + Weapons[(int)SchemeWeapon.ConcreteDonkey].Probability = (sbyte)RwViscosity; + Weapons[(int)SchemeWeapon.SuicideBomber].Probability = (sbyte)RwWindPower; + Weapons[(int)SchemeWeapon.Armageddon].Probability = (sbyte)RwWormBouncyness; + } + + // ---- ENUMERATIONS ------------------------------------------------------------------------------------------- + + [Flags] + private enum RwEarthquakeProb : byte + { + None = 0, + AutoReaim = 1 << 0, + CircularAim = 1 << 1, + AntiLockPower = 1 << 2, + ShotDoesntEndTurnAll = 1 << 3 + // Remaining bits represent kaosmod version. + } + + [Flags] + private enum RwMoleSquadronProb : byte + { + None = 0, + ShotDoesntEndTurn = 1 << 0, + LoseControlDoesntEndTurn = 1 << 1, + FireDoesntPauseTimer = 1 << 2, + UpgradedRope = 1 << 3, + CrateShower = 1 << 4, + ObjectPushByExplosion = 1 << 5, + WeaponsDontChange = 1 << 6, + ExtendedFuse = 1 << 7 + } + } +} diff --git a/src/library/Syroot.Worms.Armageddon/SchemeEnums.cs b/src/library/Syroot.Worms.Armageddon/SchemeEnums.cs index 78e4a54..295b74a 100644 --- a/src/library/Syroot.Worms.Armageddon/SchemeEnums.cs +++ b/src/library/Syroot.Worms.Armageddon/SchemeEnums.cs @@ -1,1624 +1,1319 @@ -using System; - -namespace Syroot.Worms.Armageddon -{ - /// - /// Represents the known versions of scheme file formats. - /// - public enum SchemeVersion : byte - { - /// - /// The standard format used by WA before version 3.6.28.0 and WWP. It does not store super weapon settings. - /// - Standard = 1, - - /// - /// The extended format used by WA since version 3.6.28.0. - /// - Extended = 2 - } - - /// - /// Represents the possible variations of the options stored in a scheme file. - /// - public enum SchemeSaveFormat - { - /// - /// Represents the format. - /// - Standard, - - /// - /// Represents the format, and does not store object counts. - /// - Extended, - - /// - /// Represents the format, and stores object counts (supported since WA - /// 3.6.28.0). - /// - ExtendedWithObjectCount - } - - /// - /// Represents the fall damage in percent, relative to the normal fall damage being 100%. - /// - public enum SchemeFallDamage - { - /// - /// No fall damage. - /// - None = 0, - - /// - /// 4% fall damage. - /// - Percent4 = 4, - - /// - /// 8% fall damage. - /// - Percent8 = 8, - - /// - /// 12% fall damage. - /// - Percent12 = 12, - - /// - /// 16% fall damage. - /// - Percent16 = 16, - - /// - /// 20% fall damage. - /// - Percent20 = 20, - - /// - /// 24% fall damage. - /// - Percent24 = 24, - - /// - /// 28% fall damage. - /// - Percent28 = 28, - - /// - /// 32% fall damage. - /// - Percent32 = 32, - - /// - /// 36% fall damage. - /// - Percent36 = 36, - - /// - /// 40% fall damage. - /// - Percent40 = 40, - - /// - /// 44% fall damage. - /// - Percent44 = 44, - - /// - /// 48% fall damage. - /// - Percent48 = 48, - - /// - /// 52% fall damage. - /// - Percent52 = 52, - - /// - /// 56% fall damage. - /// - Percent56 = 56, - - /// - /// 60% fall damage. - /// - Percent60 = 60, - - /// - /// 64% fall damage. - /// - Percent64 = 64, - - /// - /// 68% fall damage. - /// - Percent68 = 68, - - /// - /// 72% fall damage. - /// - Percent72 = 72, - - /// - /// 76% fall damage. - /// - Percent76 = 76, - - /// - /// 80% fall damage. - /// - Percent80 = 80, - - /// - /// 84% fall damage. - /// - Percent84 = 84, - - /// - /// 88% fall damage. - /// - Percent88 = 88, - - /// - /// 92% fall damage. - /// - Percent92 = 92, - - /// - /// 96% fall damage. - /// - Percent96 = 96, - - /// - /// 100% fall damage. - /// - Percent100 = 100, - - /// - /// 104% fall damage. - /// - Percent104 = 104, - - /// - /// 108% fall damage. - /// - Percent108 = 108, - - /// - /// 112% fall damage. - /// - Percent112 = 112, - - /// - /// 116% fall damage. - /// - Percent116 = 116, - - /// - /// 120% fall damage. - /// - Percent120 = 120, - - /// - /// 124% fall damage. - /// - Percent124 = 124, - - /// - /// 128% fall damage. - /// - Percent128 = 128, - - /// - /// 132% fall damage. - /// - Percent132 = 132, - - /// - /// 136% fall damage. - /// - Percent136 = 136, - - /// - /// 140% fall damage. - /// - Percent140 = 140, - - /// - /// 144% fall damage. - /// - Percent144 = 144, - - /// - /// 148% fall damage. - /// - Percent148 = 148, - - /// - /// 152% fall damage. - /// - Percent152 = 152, - - /// - /// 156% fall damage. - /// - Percent156 = 156, - - /// - /// 160% fall damage. - /// - Percent160 = 160, - - /// - /// 164% fall damage. - /// - Percent164 = 164, - - /// - /// 168% fall damage. - /// - Percent168 = 168, - - /// - /// 172% fall damage. - /// - Percent172 = 172, - - /// - /// 176% fall damage. - /// - Percent176 = 176, - - /// - /// 180% fall damage. - /// - Percent180 = 180, - - /// - /// 184% fall damage. - /// - Percent184 = 184, - - /// - /// 188% fall damage. - /// - Percent188 = 188, - - /// - /// 192% fall damage. - /// - Percent192 = 192, - - /// - /// 196% fall damage. - /// - Percent196 = 196, - - /// - /// 200% fall damage. - /// - Percent200 = 200, - - /// - /// 204% fall damage. - /// - Percent204 = 204, - - /// - /// 208% fall damage. - /// - Percent208 = 208, - - /// - /// 212% fall damage. - /// - Percent212 = 212, - - /// - /// 216% fall damage. - /// - Percent216 = 216, - - /// - /// 220% fall damage. - /// - Percent220 = 220, - - /// - /// 224% fall damage. - /// - Percent224 = 224, - - /// - /// 228% fall damage. - /// - Percent228 = 228, - - /// - /// 232% fall damage. - /// - Percent232 = 232, - - /// - /// 236% fall damage. - /// - Percent236 = 236, - - /// - /// 240% fall damage. - /// - Percent240 = 240, - - /// - /// 244% fall damage. - /// - Percent244 = 244, - - /// - /// 248% fall damage. - /// - Percent248 = 248, - - /// - /// 252% fall damage. - /// - Percent252 = 252, - - /// - /// 256% fall damage. - /// - Percent256 = 256, - - /// - /// 260% fall damage. - /// - Percent260 = 260, - - /// - /// 264% fall damage. - /// - Percent264 = 264, - - /// - /// 268% fall damage. - /// - Percent268 = 268, - - /// - /// 272% fall damage. - /// - Percent272 = 272, - - /// - /// 276% fall damage. - /// - Percent276 = 276, - - /// - /// 280% fall damage. - /// - Percent280 = 280, - - /// - /// 284% fall damage. - /// - Percent284 = 284, - - /// - /// 288% fall damage. - /// - Percent288 = 288, - - /// - /// 292% fall damage. - /// - Percent292 = 292, - - /// - /// 296% fall damage. - /// - Percent296 = 296, - - /// - /// 300% fall damage. - /// - Percent300 = 300, - - /// - /// 304% fall damage. - /// - Percent304 = 304, - - /// - /// 308% fall damage. - /// - Percent308 = 308, - - /// - /// 312% fall damage. - /// - Percent312 = 312, - - /// - /// 316% fall damage. - /// - Percent316 = 316, - - /// - /// 320% fall damage. - /// - Percent320 = 320, - - /// - /// 324% fall damage. - /// - Percent324 = 324, - - /// - /// 328% fall damage. - /// - Percent328 = 328, - - /// - /// 332% fall damage. - /// - Percent332 = 332, - - /// - /// 336% fall damage. - /// - Percent336 = 336, - - /// - /// 340% fall damage. - /// - Percent340 = 340, - - /// - /// 344% fall damage. - /// - Percent344 = 344, - - /// - /// 348% fall damage. - /// - Percent348 = 348, - - /// - /// 352% fall damage. - /// - Percent352 = 352, - - /// - /// 356% fall damage. - /// - Percent356 = 356, - - /// - /// 360% fall damage. - /// - Percent360 = 360, - - /// - /// 364% fall damage. - /// - Percent364 = 364, - - /// - /// 368% fall damage. - /// - Percent368 = 368, - - /// - /// 372% fall damage. - /// - Percent372 = 372, - - /// - /// 376% fall damage. - /// - Percent376 = 376, - - /// - /// 380% fall damage. - /// - Percent380 = 380, - - /// - /// 384% fall damage. - /// - Percent384 = 384, - - /// - /// 388% fall damage. - /// - Percent388 = 388, - - /// - /// 392% fall damage. - /// - Percent392 = 392, - - /// - /// 396% fall damage. - /// - Percent396 = 396, - - /// - /// 400% fall damage. - /// - Percent400 = 400, - - /// - /// 404% fall damage. - /// - Percent404 = 404, - - /// - /// 408% fall damage. - /// - Percent408 = 408, - - /// - /// 412% fall damage. - /// - Percent412 = 412, - - /// - /// 416% fall damage. - /// - Percent416 = 416, - - /// - /// 420% fall damage. - /// - Percent420 = 420, - - /// - /// 424% fall damage. - /// - Percent424 = 424, - - /// - /// 428% fall damage. - /// - Percent428 = 428, - - /// - /// 432% fall damage. - /// - Percent432 = 432, - - /// - /// 436% fall damage. - /// - Percent436 = 436, - - /// - /// 440% fall damage. - /// - Percent440 = 440, - - /// - /// 444% fall damage. - /// - Percent444 = 444, - - /// - /// 448% fall damage. - /// - Percent448 = 448, - - /// - /// 452% fall damage. - /// - Percent452 = 452, - - /// - /// 456% fall damage. - /// - Percent456 = 456, - - /// - /// 460% fall damage. - /// - Percent460 = 460, - - /// - /// 464% fall damage. - /// - Percent464 = 464, - - /// - /// 468% fall damage. - /// - Percent468 = 468, - - /// - /// 472% fall damage. - /// - Percent472 = 472, - - /// - /// 476% fall damage. - /// - Percent476 = 476, - - /// - /// 480% fall damage. - /// - Percent480 = 480, - - /// - /// 484% fall damage. - /// - Percent484 = 484, - - /// - /// 488% fall damage. - /// - Percent488 = 488, - - /// - /// 492% fall damage. - /// - Percent492 = 492, - - /// - /// 496% fall damage. - /// - Percent496 = 496, - - /// - /// 500% fall damage. - /// - Percent500 = 500, - - /// - /// 504% fall damage. - /// - Percent504 = 504, - - /// - /// 508% fall damage. - /// - Percent508 = 508 - } - - /// - /// Represents the known identifiers of scheme editors stored in schemes modified by them. - /// - public enum SchemeEditor : byte - { - None = 0, - LeTotalKiller = 0x5F, - SchemeEddy = 0x89 - } - - /// - /// Represents the stockpiling mode of weapon armory between rounds. - /// - public enum SchemeStockpiling : byte - { - Off = 0, - On = 1, - Anti = 2 - } - - /// - /// Represents the method to determine the next turn's worm. - /// - public enum SchemeWormSelect : byte - { - /// - /// Worms are selected in the order in which they appear in the team. - /// - Sequential = 0, - - /// - /// Worms are selected by the player. - /// - Manual = 1, - - /// - /// Worms are selected randomly. - /// - Random = 2 - } - - /// - /// Represents the event triggered when the round timer runs out. - /// - public enum SchemeSuddenDeathEvent : byte - { - /// - /// The round ends and is drawn. - /// - RoundEnd = 0, - - /// - /// A nuklear test is triggered. - /// - NuclearStrike = 1, - - /// - /// The worms health is reduced to 1. - /// - HealthDrop = 2, - - /// - /// No special event is triggered and the water only rises in the rate specified in the scheme. - /// - WaterRise = 3 - } - - /// - /// Represents the water rise in pixels after sudden death was triggered. - /// - public enum SchemeWaterRise : byte - { - /// - /// No water rise. - /// - None = 0, - - /// - /// 5 pixels water rise. - /// - Pixels5 = 5, - - /// - /// 13 pixels water rise. - /// - Pixels13 = 13, - - /// - /// 20 pixels water rise. - /// - Pixels20 = 20, - - /// - /// 21 pixels water rise. - /// - Pixels21 = 21, - - /// - /// 29 pixels water rise. - /// - Pixels29 = 29, - - /// - /// 37 pixels water rise. - /// - Pixels37 = 37, - - /// - /// 45 pixels water rise. - /// - Pixels45 = 45, - - /// - /// 52 pixels water rise. - /// - Pixels52 = 52, - - /// - /// 53 pixels water rise. - /// - Pixels53 = 53, - - /// - /// 61 pixels water rise. - /// - Pixels61 = 61, - - /// - /// 64 pixels water rise. - /// - Pixels64 = 64, - - /// - /// 69 pixels water rise. - /// - Pixels69 = 69, - - /// - /// 77 pixels water rise. - /// - Pixels77 = 77, - - /// - /// 80 pixels water rise. - /// - Pixels80 = 80, - - /// - /// 84 pixels water rise. - /// - Pixels84 = 84, - - /// - /// 85 pixels water rise. - /// - Pixels85 = 85, - - /// - /// 93 pixels water rise. - /// - Pixels93 = 93, - - /// - /// 101 pixels water rise. - /// - Pixels101 = 101, - - /// - /// 109 pixels water rise. - /// - Pixels109 = 109, - - /// - /// 116 pixels water rise. - /// - Pixels116 = 116, - - /// - /// 117 pixels water rise. - /// - Pixels117 = 117, - - /// - /// 125 pixels water rise. - /// - Pixels125 = 125, - - /// - /// 133 pixels water rise. - /// - Pixels133 = 133, - - /// - /// 141 pixels water rise. - /// - Pixels141 = 141, - - /// - /// 148 pixels water rise. - /// - Pixels148 = 148, - - /// - /// 149 pixels water rise. - /// - Pixels149 = 149, - - /// - /// 157 pixels water rise. - /// - Pixels157 = 157, - - /// - /// 165 pixels water rise. - /// - Pixels165 = 165, - - /// - /// 173 pixels water rise. - /// - Pixels173 = 173, - - /// - /// 180 pixels water rise. - /// - Pixels180 = 180, - - /// - /// 181 pixels water rise. - /// - Pixels181 = 181, - - /// - /// 189 pixels water rise. - /// - Pixels189 = 189, - - /// - /// 197 pixels water rise. - /// - Pixels197 = 197, - - /// - /// 205 pixels water rise. - /// - Pixels205 = 205, - - /// - /// 208 pixels water rise. - /// - Pixels208 = 208, - - /// - /// 212 pixels water rise. - /// - Pixels212 = 212, - - /// - /// 213 pixels water rise. - /// - Pixels213 = 213, - - /// - /// 221 pixels water rise. - /// - Pixels221 = 221, - - /// - /// 229 pixels water rise. - /// - Pixels229 = 229, - - /// - /// 237 pixels water rise. - /// - Pixels237 = 237, - - /// - /// 244 pixels water rise. - /// - Pixels244 = 244, - - /// - /// 245 pixels water rise. - /// - Pixels245 = 245, - - /// - /// 253 pixels water rise. - /// - Pixels253 = 253 - } - - /// - /// Represents the types of objects which can appear on the map. - /// - [Flags] - public enum SchemeObjectType - { - None = 0, - Mines = 1 << 0, - OilDrums = 1 << 1 - } - - /// - /// Represents the possible maximum number of objects which can appear on the map. - /// - public enum SchemeObjectCount - { - /// - /// No objects. - /// - None = 0, - - /// - /// Up to 1 object. - /// - Count1 = 1, - - /// - /// Up to 2 objects. - /// - Count2 = 2, - - /// - /// Up to 3 objects. - /// - Count3 = 3, - - /// - /// Up to 4 objects. - /// - Count4 = 4, - - /// - /// Up to 5 objects. - /// - Count5 = 5, - - /// - /// Up to 6 objects. - /// - Count6 = 6, - - /// - /// Up to 7 objects. - /// - Count7 = 7, - - /// - /// Up to 8 objects. - /// - Count8 = 8, - - /// - /// Up to 9 objects. - /// - Count9 = 9, - - /// - /// Up to 10 objects. - /// - Count10 = 10, - - /// - /// Up to 11 objects. - /// - Count11 = 11, - - /// - /// Up to 12 objects. - /// - Count12 = 12, - - /// - /// Up to 13 objects. - /// - Count13 = 13, - - /// - /// Up to 14 objects. - /// - Count14 = 14, - - /// - /// Up to 15 objects. - /// - Count15 = 15, - - /// - /// Up to 16 objects. - /// - Count16 = 16, - - /// - /// Up to 17 objects. - /// - Count17 = 17, - - /// - /// Up to 18 objects. - /// - Count18 = 18, - - /// - /// Up to 19 objects. - /// - Count19 = 19, - - /// - /// Up to 20 objects. - /// - Count20 = 20, - - /// - /// Up to 21 objects. - /// - Count21 = 21, - - /// - /// Up to 22 objects. - /// - Count22 = 22, - - /// - /// Up to 23 objects. - /// - Count23 = 23, - - /// - /// Up to 24 objects. - /// - Count24 = 24, - - /// - /// Up to 25 objects. - /// - Count25 = 25, - - /// - /// Up to 26 objects. - /// - Count26 = 26, - - /// - /// Up to 27 objects. - /// - Count27 = 27, - - /// - /// Up to 28 objects. - /// - Count28 = 28, - - /// - /// Up to 29 objects. - /// - Count29 = 29, - - /// - /// Up to 30 objects. - /// - Count30 = 30, - - /// - /// Up to 35 objects. - /// - Count35 = 35, - - /// - /// Up to 40 objects. - /// - Count40 = 40, - - /// - /// Up to 45 objects. - /// - Count45 = 45, - - /// - /// Up to 50 objects. - /// - Count50 = 50, - - /// - /// Up to 55 objects. - /// - Count55 = 55, - - /// - /// Up to 60 objects. - /// - Count60 = 60, - - /// - /// Up to 65 objects. - /// - Count65 = 65, - - /// - /// Up to 70 objects. - /// - Count70 = 70, - - /// - /// Up to 75 objects. - /// - Count75 = 75, - - /// - /// Up to 80 objects. - /// - Count80 = 80, - - /// - /// Up to 85 objects. - /// - Count85 = 85, - - /// - /// Up to 90 objects. - /// - Count90 = 90, - - /// - /// Up to 95 objects. - /// - Count95 = 95, - - /// - /// Up to 100 objects. - /// - Count100 = 100, - - /// - /// Up to 110 objects. - /// - Count110 = 110, - - /// - /// Up to 120 objects. - /// - Count120 = 120, - - /// - /// Up to 130 objects. - /// - Count130 = 130, - - /// - /// Up to 140 objects. - /// - Count140 = 140, - - /// - /// Up to 150 objects. - /// - Count150 = 150, - - /// - /// Up to 160 objects. - /// - Count160 = 160, - - /// - /// Up to 170 objects. - /// - Count170 = 170, - - /// - /// Up to 180 objects. - /// - Count180 = 180, - - /// - /// Up to 190 objects. - /// - Count190 = 190, - - /// - /// Up to 200 objects. - /// - Count200 = 200, - - /// - /// Up to 210 objects. - /// - Count210 = 210, - - /// - /// Up to 220 objects. - /// - Count220 = 220, - - /// - /// Up to 230 objects. - /// - Count230 = 230, - - /// - /// Up to 240 objects. - /// - Count240 = 240, - - /// - /// Up to 250 objects. - /// - Count250 = 250 - } - - /// - /// Represents the weapons in the game. - /// - public enum SchemeWeapon - { - /// - /// The Bazooka weapon. - /// - Bazooka, - - /// - /// The Homing Missile weapon. - /// - HomingMissile, - - /// - /// The Mortar weapon. - /// - Mortar, - - /// - /// The Grenade weapon. - /// - Grenade, - - /// - /// The Cluster Bomb weapon. - /// - ClusterBomb, - - /// - /// The Skunk weapon. - /// - Skunk, - - /// - /// The Petrol Bomb weapon. - /// - PetrolBomb, - - /// - /// The Banana Bomb weapon. - /// - BananaBomb, - - /// - /// The Handgun weapon. - /// - Handgun, - - /// - /// The Shotgun weapon. - /// - Shotgun, - - /// - /// The Uzi weapon. - /// - Uzi, - - /// - /// The Minigun weapon. - /// - Minigun, - - /// - /// The Longbow weapon. - /// - Longbow, - - /// - /// The Airstrike weapon. - /// - Airstrike, - - /// - /// The Napalm Strike weapon. - /// - NapalmStrike, - - /// - /// The Mine weapon. - /// - Mine, - - /// - /// The Firepunch weapon. - /// - Firepunch, - - /// - /// The Dragonball weapon. - /// - Dragonball, - - /// - /// The Kamikaze weapon. - /// - Kamikaze, - - /// - /// The Prod weapon. - /// - Prod, - - /// - /// The Battle Axe weapon. - /// - BattleAxe, - - /// - /// The Blowtorch weapon. - /// - Blowtorch, - - /// - /// The Pneumatic Drill weapon. - /// - PneumaticDrill, - - /// - /// The Girder weapon. - /// - Girder, - - /// - /// The Ninja Rope weapon. - /// - NinjaRope, - - /// - /// The Parachute weapon. - /// - Parachute, - - /// - /// The Bungee weapon. - /// - Bungee, - - /// - /// The Teleport weapon. - /// - Teleport, - - /// - /// The Dynamite weapon. - /// - Dynamite, - - /// - /// The Sheep weapon. - /// - Sheep, - - /// - /// The Baseball Bat weapon. - /// - BaseballBat, - - /// - /// The Flame Thrower weapon. - /// - Flamethrower, - - /// - /// The Homing Pigeon weapon. - /// - HomingPigeon, - - /// - /// The Mad Cow weapon. - /// - MadCow, - - /// - /// The Holy Hand Grenade weapon. - /// - HolyHandGrenade, - - /// - /// The Old Woman weapon. - /// - OldWoman, - - /// - /// The Sheep Launcher weapon. - /// - SheepLauncher, - - /// - /// The Super Sheep or Aqua Sheep weapon. - /// - SuperSheep, - - /// - /// The Mole Bomb weapon. - /// - MoleBomb, - - /// - /// The Jetpack utility. - /// - Jetpack, - - /// - /// The Low Gravity utility. - /// - LowGravity, - - /// - /// The Laser Sight utility. - /// - LaserSight, - - /// - /// The Fast Walk utility. - /// - FastWalk, - - /// - /// The Invisibility utility. - /// - Invisibility, - - /// - /// The Damage x2 utility. - /// - DamageX2, - - /// - /// The Freeze super weapon. - /// - Freeze, - - /// - /// The Super Banana Bomb super weapon. - /// - SuperBananaBomb, - - /// - /// The Mine Strike super weapon. - /// - MineStrike, - - /// - /// The Girder Starter Pack super weapon. - /// - GirderStarterPack, - - /// - /// The Earthquake super weapon. - /// - Earthquake, - - /// - /// The Scales Of Justice super weapon. - /// - ScalesOfJustice, - - /// - /// The Ming Vase super weapon. - /// - MingVase, - - /// - /// The Mike's Carpet Bomb super weapon. - /// - MikesCarpetBomb, - - /// - /// The Patsy's Magic Bullet super weapon. - /// - MagicBullet, - - /// - /// The Indian Nuclear Test super weapon. - /// - NuclearTest, - - /// - /// The Select Worm super weapon. - /// - SelectWorm, - - /// - /// The Salvation Army super weapon. - /// - SalvationArmy, - - /// - /// The Mole Squadron super weapon. - /// - MoleSquadron, - - /// - /// The MB Bomb super weapon. - /// - MBBomb, - - /// - /// The Concrete Donkey super weapon. - /// - ConcreteDonkey, - - /// - /// The Suicide Bomber super weapon. - /// - SuicideBomber, - - /// - /// The Sheep Strike super weapon. - /// - SheepStrike, - - /// - /// The Mail Strike super weapon. - /// - MailStrike, - - /// - /// The Armageddon super weapon. - /// - Armageddon - } -} +using System; + +namespace Syroot.Worms.Armageddon +{ + /// + /// Represents the known versions of scheme file formats. + /// + public enum SchemeVersion : byte + { + /// + /// The standard format used by WA before version 3.6.28.0 and WWP. It does not store super weapon settings. + /// + Standard = 1, + + /// + /// The extended format used by WA since version 3.6.28.0. + /// + Extended = 2 + } + + /// + /// Represents the possible variations of the options stored in a scheme file. + /// + public enum SchemeSaveFormat + { + /// + /// Represents the format. + /// + Standard, + + /// + /// Represents the format, and does not store object counts. + /// + Extended, + + /// + /// Represents the format, and stores object counts (supported since WA + /// 3.6.28.0). + /// + ExtendedWithObjectCount + } + + /// + /// Represents the fall damage in percent, relative to the normal fall damage being 100%. + /// + public enum SchemeFallDamage + { + /// + /// No fall damage. + /// + None = 0, + + /// + /// 4% fall damage. + /// + Percent4 = 4, + + /// + /// 8% fall damage. + /// + Percent8 = 8, + + /// + /// 12% fall damage. + /// + Percent12 = 12, + + /// + /// 16% fall damage. + /// + Percent16 = 16, + + /// + /// 20% fall damage. + /// + Percent20 = 20, + + /// + /// 24% fall damage. + /// + Percent24 = 24, + + /// + /// 28% fall damage. + /// + Percent28 = 28, + + /// + /// 32% fall damage. + /// + Percent32 = 32, + + /// + /// 36% fall damage. + /// + Percent36 = 36, + + /// + /// 40% fall damage. + /// + Percent40 = 40, + + /// + /// 44% fall damage. + /// + Percent44 = 44, + + /// + /// 48% fall damage. + /// + Percent48 = 48, + + /// + /// 52% fall damage. + /// + Percent52 = 52, + + /// + /// 56% fall damage. + /// + Percent56 = 56, + + /// + /// 60% fall damage. + /// + Percent60 = 60, + + /// + /// 64% fall damage. + /// + Percent64 = 64, + + /// + /// 68% fall damage. + /// + Percent68 = 68, + + /// + /// 72% fall damage. + /// + Percent72 = 72, + + /// + /// 76% fall damage. + /// + Percent76 = 76, + + /// + /// 80% fall damage. + /// + Percent80 = 80, + + /// + /// 84% fall damage. + /// + Percent84 = 84, + + /// + /// 88% fall damage. + /// + Percent88 = 88, + + /// + /// 92% fall damage. + /// + Percent92 = 92, + + /// + /// 96% fall damage. + /// + Percent96 = 96, + + /// + /// 100% fall damage. + /// + Percent100 = 100, + + /// + /// 104% fall damage. + /// + Percent104 = 104, + + /// + /// 108% fall damage. + /// + Percent108 = 108, + + /// + /// 112% fall damage. + /// + Percent112 = 112, + + /// + /// 116% fall damage. + /// + Percent116 = 116, + + /// + /// 120% fall damage. + /// + Percent120 = 120, + + /// + /// 124% fall damage. + /// + Percent124 = 124, + + /// + /// 128% fall damage. + /// + Percent128 = 128, + + /// + /// 132% fall damage. + /// + Percent132 = 132, + + /// + /// 136% fall damage. + /// + Percent136 = 136, + + /// + /// 140% fall damage. + /// + Percent140 = 140, + + /// + /// 144% fall damage. + /// + Percent144 = 144, + + /// + /// 148% fall damage. + /// + Percent148 = 148, + + /// + /// 152% fall damage. + /// + Percent152 = 152, + + /// + /// 156% fall damage. + /// + Percent156 = 156, + + /// + /// 160% fall damage. + /// + Percent160 = 160, + + /// + /// 164% fall damage. + /// + Percent164 = 164, + + /// + /// 168% fall damage. + /// + Percent168 = 168, + + /// + /// 172% fall damage. + /// + Percent172 = 172, + + /// + /// 176% fall damage. + /// + Percent176 = 176, + + /// + /// 180% fall damage. + /// + Percent180 = 180, + + /// + /// 184% fall damage. + /// + Percent184 = 184, + + /// + /// 188% fall damage. + /// + Percent188 = 188, + + /// + /// 192% fall damage. + /// + Percent192 = 192, + + /// + /// 196% fall damage. + /// + Percent196 = 196, + + /// + /// 200% fall damage. + /// + Percent200 = 200, + + /// + /// 204% fall damage. + /// + Percent204 = 204, + + /// + /// 208% fall damage. + /// + Percent208 = 208, + + /// + /// 212% fall damage. + /// + Percent212 = 212, + + /// + /// 216% fall damage. + /// + Percent216 = 216, + + /// + /// 220% fall damage. + /// + Percent220 = 220, + + /// + /// 224% fall damage. + /// + Percent224 = 224, + + /// + /// 228% fall damage. + /// + Percent228 = 228, + + /// + /// 232% fall damage. + /// + Percent232 = 232, + + /// + /// 236% fall damage. + /// + Percent236 = 236, + + /// + /// 240% fall damage. + /// + Percent240 = 240, + + /// + /// 244% fall damage. + /// + Percent244 = 244, + + /// + /// 248% fall damage. + /// + Percent248 = 248, + + /// + /// 252% fall damage. + /// + Percent252 = 252, + + /// + /// 256% fall damage. + /// + Percent256 = 256, + + /// + /// 260% fall damage. + /// + Percent260 = 260, + + /// + /// 264% fall damage. + /// + Percent264 = 264, + + /// + /// 268% fall damage. + /// + Percent268 = 268, + + /// + /// 272% fall damage. + /// + Percent272 = 272, + + /// + /// 276% fall damage. + /// + Percent276 = 276, + + /// + /// 280% fall damage. + /// + Percent280 = 280, + + /// + /// 284% fall damage. + /// + Percent284 = 284, + + /// + /// 288% fall damage. + /// + Percent288 = 288, + + /// + /// 292% fall damage. + /// + Percent292 = 292, + + /// + /// 296% fall damage. + /// + Percent296 = 296, + + /// + /// 300% fall damage. + /// + Percent300 = 300, + + /// + /// 304% fall damage. + /// + Percent304 = 304, + + /// + /// 308% fall damage. + /// + Percent308 = 308, + + /// + /// 312% fall damage. + /// + Percent312 = 312, + + /// + /// 316% fall damage. + /// + Percent316 = 316, + + /// + /// 320% fall damage. + /// + Percent320 = 320, + + /// + /// 324% fall damage. + /// + Percent324 = 324, + + /// + /// 328% fall damage. + /// + Percent328 = 328, + + /// + /// 332% fall damage. + /// + Percent332 = 332, + + /// + /// 336% fall damage. + /// + Percent336 = 336, + + /// + /// 340% fall damage. + /// + Percent340 = 340, + + /// + /// 344% fall damage. + /// + Percent344 = 344, + + /// + /// 348% fall damage. + /// + Percent348 = 348, + + /// + /// 352% fall damage. + /// + Percent352 = 352, + + /// + /// 356% fall damage. + /// + Percent356 = 356, + + /// + /// 360% fall damage. + /// + Percent360 = 360, + + /// + /// 364% fall damage. + /// + Percent364 = 364, + + /// + /// 368% fall damage. + /// + Percent368 = 368, + + /// + /// 372% fall damage. + /// + Percent372 = 372, + + /// + /// 376% fall damage. + /// + Percent376 = 376, + + /// + /// 380% fall damage. + /// + Percent380 = 380, + + /// + /// 384% fall damage. + /// + Percent384 = 384, + + /// + /// 388% fall damage. + /// + Percent388 = 388, + + /// + /// 392% fall damage. + /// + Percent392 = 392, + + /// + /// 396% fall damage. + /// + Percent396 = 396, + + /// + /// 400% fall damage. + /// + Percent400 = 400, + + /// + /// 404% fall damage. + /// + Percent404 = 404, + + /// + /// 408% fall damage. + /// + Percent408 = 408, + + /// + /// 412% fall damage. + /// + Percent412 = 412, + + /// + /// 416% fall damage. + /// + Percent416 = 416, + + /// + /// 420% fall damage. + /// + Percent420 = 420, + + /// + /// 424% fall damage. + /// + Percent424 = 424, + + /// + /// 428% fall damage. + /// + Percent428 = 428, + + /// + /// 432% fall damage. + /// + Percent432 = 432, + + /// + /// 436% fall damage. + /// + Percent436 = 436, + + /// + /// 440% fall damage. + /// + Percent440 = 440, + + /// + /// 444% fall damage. + /// + Percent444 = 444, + + /// + /// 448% fall damage. + /// + Percent448 = 448, + + /// + /// 452% fall damage. + /// + Percent452 = 452, + + /// + /// 456% fall damage. + /// + Percent456 = 456, + + /// + /// 460% fall damage. + /// + Percent460 = 460, + + /// + /// 464% fall damage. + /// + Percent464 = 464, + + /// + /// 468% fall damage. + /// + Percent468 = 468, + + /// + /// 472% fall damage. + /// + Percent472 = 472, + + /// + /// 476% fall damage. + /// + Percent476 = 476, + + /// + /// 480% fall damage. + /// + Percent480 = 480, + + /// + /// 484% fall damage. + /// + Percent484 = 484, + + /// + /// 488% fall damage. + /// + Percent488 = 488, + + /// + /// 492% fall damage. + /// + Percent492 = 492, + + /// + /// 496% fall damage. + /// + Percent496 = 496, + + /// + /// 500% fall damage. + /// + Percent500 = 500, + + /// + /// 504% fall damage. + /// + Percent504 = 504, + + /// + /// 508% fall damage. + /// + Percent508 = 508 + } + + /// + /// Represents the known identifiers of scheme editors stored in schemes modified by them. + /// + public enum SchemeEditor : byte + { + None = 0, + LeTotalKiller = 0x5F, + SchemeEddy = 0x89 + } + + /// + /// Represents the stockpiling mode of weapon armory between rounds. + /// + public enum SchemeStockpiling : byte + { + Off = 0, + On = 1, + Anti = 2 + } + + /// + /// Represents the method to determine the next turn's worm. + /// + public enum SchemeWormSelect : byte + { + /// + /// Worms are selected in the order in which they appear in the team. + /// + Sequential = 0, + + /// + /// Worms are selected by the player. + /// + Manual = 1, + + /// + /// Worms are selected randomly. + /// + Random = 2 + } + + /// + /// Represents the event triggered when the round timer runs out. + /// + public enum SchemeSuddenDeathEvent : byte + { + /// + /// The round ends and is drawn. + /// + RoundEnd = 0, + + /// + /// A nuklear test is triggered. + /// + NuclearStrike = 1, + + /// + /// The worms health is reduced to 1. + /// + HealthDrop = 2, + + /// + /// No special event is triggered and the water only rises in the rate specified in the scheme. + /// + WaterRise = 3 + } + + /// + /// Represents the water rise in pixels after sudden death was triggered. + /// + public enum SchemeWaterRise : byte + { + /// + /// No water rise. + /// + None = 0, + + /// + /// 5 pixels water rise. + /// + Pixels5 = 5, + + /// + /// 13 pixels water rise. + /// + Pixels13 = 13, + + /// + /// 20 pixels water rise. + /// + Pixels20 = 20, + + /// + /// 21 pixels water rise. + /// + Pixels21 = 21, + + /// + /// 29 pixels water rise. + /// + Pixels29 = 29, + + /// + /// 37 pixels water rise. + /// + Pixels37 = 37, + + /// + /// 45 pixels water rise. + /// + Pixels45 = 45, + + /// + /// 52 pixels water rise. + /// + Pixels52 = 52, + + /// + /// 53 pixels water rise. + /// + Pixels53 = 53, + + /// + /// 61 pixels water rise. + /// + Pixels61 = 61, + + /// + /// 64 pixels water rise. + /// + Pixels64 = 64, + + /// + /// 69 pixels water rise. + /// + Pixels69 = 69, + + /// + /// 77 pixels water rise. + /// + Pixels77 = 77, + + /// + /// 80 pixels water rise. + /// + Pixels80 = 80, + + /// + /// 84 pixels water rise. + /// + Pixels84 = 84, + + /// + /// 85 pixels water rise. + /// + Pixels85 = 85, + + /// + /// 93 pixels water rise. + /// + Pixels93 = 93, + + /// + /// 101 pixels water rise. + /// + Pixels101 = 101, + + /// + /// 109 pixels water rise. + /// + Pixels109 = 109, + + /// + /// 116 pixels water rise. + /// + Pixels116 = 116, + + /// + /// 117 pixels water rise. + /// + Pixels117 = 117, + + /// + /// 125 pixels water rise. + /// + Pixels125 = 125, + + /// + /// 133 pixels water rise. + /// + Pixels133 = 133, + + /// + /// 141 pixels water rise. + /// + Pixels141 = 141, + + /// + /// 148 pixels water rise. + /// + Pixels148 = 148, + + /// + /// 149 pixels water rise. + /// + Pixels149 = 149, + + /// + /// 157 pixels water rise. + /// + Pixels157 = 157, + + /// + /// 165 pixels water rise. + /// + Pixels165 = 165, + + /// + /// 173 pixels water rise. + /// + Pixels173 = 173, + + /// + /// 180 pixels water rise. + /// + Pixels180 = 180, + + /// + /// 181 pixels water rise. + /// + Pixels181 = 181, + + /// + /// 189 pixels water rise. + /// + Pixels189 = 189, + + /// + /// 197 pixels water rise. + /// + Pixels197 = 197, + + /// + /// 205 pixels water rise. + /// + Pixels205 = 205, + + /// + /// 208 pixels water rise. + /// + Pixels208 = 208, + + /// + /// 212 pixels water rise. + /// + Pixels212 = 212, + + /// + /// 213 pixels water rise. + /// + Pixels213 = 213, + + /// + /// 221 pixels water rise. + /// + Pixels221 = 221, + + /// + /// 229 pixels water rise. + /// + Pixels229 = 229, + + /// + /// 237 pixels water rise. + /// + Pixels237 = 237, + + /// + /// 244 pixels water rise. + /// + Pixels244 = 244, + + /// + /// 245 pixels water rise. + /// + Pixels245 = 245, + + /// + /// 253 pixels water rise. + /// + Pixels253 = 253 + } + + /// + /// Represents the types of objects which can appear on the map. + /// + [Flags] + public enum SchemeObjectType + { + None, + Mines = 1 << 0, + OilDrums = 1 << 1, + Both = Mines | OilDrums + } + + /// + /// Represents the weapons in the game. + /// + public enum SchemeWeapon + { + /// + /// The Bazooka weapon. + /// + Bazooka, + + /// + /// The Homing Missile weapon. + /// + HomingMissile, + + /// + /// The Mortar weapon. + /// + Mortar, + + /// + /// The Grenade weapon. + /// + Grenade, + + /// + /// The Cluster Bomb weapon. + /// + ClusterBomb, + + /// + /// The Skunk weapon. + /// + Skunk, + + /// + /// The Petrol Bomb weapon. + /// + PetrolBomb, + + /// + /// The Banana Bomb weapon. + /// + BananaBomb, + + /// + /// The Handgun weapon. + /// + Handgun, + + /// + /// The Shotgun weapon. + /// + Shotgun, + + /// + /// The Uzi weapon. + /// + Uzi, + + /// + /// The Minigun weapon. + /// + Minigun, + + /// + /// The Longbow weapon. + /// + Longbow, + + /// + /// The Airstrike weapon. + /// + Airstrike, + + /// + /// The Napalm Strike weapon. + /// + NapalmStrike, + + /// + /// The Mine weapon. + /// + Mine, + + /// + /// The Firepunch weapon. + /// + Firepunch, + + /// + /// The Dragonball weapon. + /// + Dragonball, + + /// + /// The Kamikaze weapon. + /// + Kamikaze, + + /// + /// The Prod weapon. + /// + Prod, + + /// + /// The Battle Axe weapon. + /// + BattleAxe, + + /// + /// The Blowtorch weapon. + /// + Blowtorch, + + /// + /// The Pneumatic Drill weapon. + /// + PneumaticDrill, + + /// + /// The Girder weapon. + /// + Girder, + + /// + /// The Ninja Rope weapon. + /// + NinjaRope, + + /// + /// The Parachute weapon. + /// + Parachute, + + /// + /// The Bungee weapon. + /// + Bungee, + + /// + /// The Teleport weapon. + /// + Teleport, + + /// + /// The Dynamite weapon. + /// + Dynamite, + + /// + /// The Sheep weapon. + /// + Sheep, + + /// + /// The Baseball Bat weapon. + /// + BaseballBat, + + /// + /// The Flame Thrower weapon. + /// + Flamethrower, + + /// + /// The Homing Pigeon weapon. + /// + HomingPigeon, + + /// + /// The Mad Cow weapon. + /// + MadCow, + + /// + /// The Holy Hand Grenade weapon. + /// + HolyHandGrenade, + + /// + /// The Old Woman weapon. + /// + OldWoman, + + /// + /// The Sheep Launcher weapon. + /// + SheepLauncher, + + /// + /// The Super Sheep or Aqua Sheep weapon. + /// + SuperSheep, + + /// + /// The Mole Bomb weapon. + /// + MoleBomb, + + /// + /// The Jetpack utility. + /// + Jetpack, + + /// + /// The Low Gravity utility. + /// + LowGravity, + + /// + /// The Laser Sight utility. + /// + LaserSight, + + /// + /// The Fast Walk utility. + /// + FastWalk, + + /// + /// The Invisibility utility. + /// + Invisibility, + + /// + /// The Damage x2 utility. + /// + DamageX2, + + /// + /// The Freeze super weapon. + /// + Freeze, + + /// + /// The Super Banana Bomb super weapon. + /// + SuperBananaBomb, + + /// + /// The Mine Strike super weapon. + /// + MineStrike, + + /// + /// The Girder Starter Pack super weapon. + /// + GirderStarterPack, + + /// + /// The Earthquake super weapon. + /// + Earthquake, + + /// + /// The Scales Of Justice super weapon. + /// + ScalesOfJustice, + + /// + /// The Ming Vase super weapon. + /// + MingVase, + + /// + /// The Mike's Carpet Bomb super weapon. + /// + MikesCarpetBomb, + + /// + /// The Patsy's Magic Bullet super weapon. + /// + MagicBullet, + + /// + /// The Indian Nuclear Test super weapon. + /// + NuclearTest, + + /// + /// The Select Worm super weapon. + /// + SelectWorm, + + /// + /// The Salvation Army super weapon. + /// + SalvationArmy, + + /// + /// The Mole Squadron super weapon. + /// + MoleSquadron, + + /// + /// The MB Bomb super weapon. + /// + MBBomb, + + /// + /// The Concrete Donkey super weapon. + /// + ConcreteDonkey, + + /// + /// The Suicide Bomber super weapon. + /// + SuicideBomber, + + /// + /// The Sheep Strike super weapon. + /// + SheepStrike, + + /// + /// The Mail Strike super weapon. + /// + MailStrike, + + /// + /// The Armageddon super weapon. + /// + Armageddon + } +} diff --git a/src/tool/Syroot.Worms.Scratchpad/Program.cs b/src/tool/Syroot.Worms.Scratchpad/Program.cs index 9346265..6a7775d 100644 --- a/src/tool/Syroot.Worms.Scratchpad/Program.cs +++ b/src/tool/Syroot.Worms.Scratchpad/Program.cs @@ -1,71 +1,94 @@ -using System; -using System.Drawing.Imaging; -using System.IO; -using Syroot.Worms.Mgame; - -namespace Syroot.Worms.Scratchpad -{ - internal class Program - { - // ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- - - private static void Main(string[] args) - { - } - - private static void ConvertIgdImages() - { - string igdFolder = @"C:\Games\WWP Aqua\Images"; - string pngFolder = @"D:\Pictures\IGD"; - Directory.CreateDirectory(pngFolder); - - foreach (string igdFilePath in Directory.GetFiles(igdFolder, "*.igd", SearchOption.AllDirectories)) - { - - // Load the IGD and let it convert the images. - Igd igd = new Igd(igdFilePath); - - // Save the images in the output folder under a relative path. - string igdFileFolder = Path.GetDirectoryName(igdFilePath); - string relativePath = igdFileFolder.Substring(igdFolder.Length).TrimStart(Path.DirectorySeparatorChar); - string pngIgdFolder = Path.Combine(pngFolder, relativePath, Path.GetFileName(igdFilePath)); - Directory.CreateDirectory(pngIgdFolder); - for (int i = 0; i < igd.Images.Count; i++) - { - string pngFileName = Path.ChangeExtension(i.ToString(), "png"); - IgdImage image = igd.Images[i]; - image.RawBitmap.ToBitmap().Save(Path.Combine(pngIgdFolder, pngFileName), ImageFormat.Png); - } - } - } - - private static void ConvertKsfImages() - { - string ksfFolder = @"C:\Games\Online Worms\Ksf"; - string palFolder = @"C:\Games\Online Worms\Palette"; - string pngFolder = @"D:\Pictures\KSF"; - Directory.CreateDirectory(pngFolder); - Palette fallbackPalette = new Palette(Path.Combine(palFolder, "StaticImage.pal")); - - foreach (string ksfFilePath in Directory.GetFiles(ksfFolder, "*.ksf")) - { - // Try to find a palette for every KSF, otherwise use fallback palette. - string ksfFileName = Path.GetFileName(ksfFilePath); - string palFilePath = Path.Combine(palFolder, Path.ChangeExtension(ksfFileName, "pal")); - Palette palette = File.Exists(palFilePath) ? new Palette(palFilePath) : fallbackPalette; - - // Load the KSF and let it convert the images. - Ksf ksf = new Ksf(ksfFilePath, palette); - - // Save the images in the output folder. - string pngKsfFolder = Path.Combine(pngFolder, ksfFileName); - Directory.CreateDirectory(pngKsfFolder); - for (int i = 0; i < ksf.Images.Count; i++) - { - string pngFileName = Path.ChangeExtension(i.ToString(), "png"); - ksf.Images[i].RawBitmap?.ToBitmap().Save(Path.Combine(pngKsfFolder, pngFileName), ImageFormat.Png); - } - } - } - } +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.IO; +using Syroot.Worms.Armageddon; +using Syroot.Worms.Mgame; + +namespace Syroot.Worms.Scratchpad +{ + internal class Program + { + // ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- + + private static void Main() + { + IEnumerable fileNames = Directory.EnumerateFiles( + @"C:\Games\Worms Armageddon 3.7.2.1\User\Schemes", "*.wsc", SearchOption.AllDirectories); + + foreach (string fileName in fileNames) + { + string tempFile = Path.GetTempFileName(); + using (Stream tempStream = new FileStream(tempFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) + { + // Load the scheme and save it to temp file. + Scheme origScheme = new Scheme(fileName); + tempStream.Position = 0; + origScheme.Save(tempStream); + + // Load the temp file. + tempStream.Position = 0; + Scheme newScheme = new Scheme(tempStream); + + if (origScheme.ObjectCount != newScheme.ObjectCount || origScheme.ObjectTypes != newScheme.ObjectTypes) + throw new InvalidOperationException("mismatch"); + } + File.Delete(tempFile); + } + } + + private static void ConvertIgdImages() + { + string igdFolder = @"C:\Games\WWP Aqua\Images"; + string pngFolder = @"D:\Pictures\IGD"; + Directory.CreateDirectory(pngFolder); + + foreach (string igdFilePath in Directory.GetFiles(igdFolder, "*.igd", SearchOption.AllDirectories)) + { + // Load the IGD and let it convert the images. + Igd igd = new Igd(igdFilePath); + + // Save the images in the output folder under a relative path. + string igdFileFolder = Path.GetDirectoryName(igdFilePath); + string relativePath = igdFileFolder.Substring(igdFolder.Length).TrimStart(Path.DirectorySeparatorChar); + string pngIgdFolder = Path.Combine(pngFolder, relativePath, Path.GetFileName(igdFilePath)); + Directory.CreateDirectory(pngIgdFolder); + for (int i = 0; i < igd.Images.Count; i++) + { + string pngFileName = Path.ChangeExtension(i.ToString(), "png"); + IgdImage image = igd.Images[i]; + image.RawBitmap.ToBitmap().Save(Path.Combine(pngIgdFolder, pngFileName), ImageFormat.Png); + } + } + } + + private static void ConvertKsfImages() + { + string ksfFolder = @"C:\Games\Online Worms\Ksf"; + string palFolder = @"C:\Games\Online Worms\Palette"; + string pngFolder = @"D:\Pictures\KSF"; + Directory.CreateDirectory(pngFolder); + Palette fallbackPalette = new Palette(Path.Combine(palFolder, "StaticImage.pal")); + + foreach (string ksfFilePath in Directory.GetFiles(ksfFolder, "*.ksf")) + { + // Try to find a palette for every KSF, otherwise use fallback palette. + string ksfFileName = Path.GetFileName(ksfFilePath); + string palFilePath = Path.Combine(palFolder, Path.ChangeExtension(ksfFileName, "pal")); + Palette palette = File.Exists(palFilePath) ? new Palette(palFilePath) : fallbackPalette; + + // Load the KSF and let it convert the images. + Ksf ksf = new Ksf(ksfFilePath, palette); + + // Save the images in the output folder. + string pngKsfFolder = Path.Combine(pngFolder, ksfFileName); + Directory.CreateDirectory(pngKsfFolder); + for (int i = 0; i < ksf.Images.Count; i++) + { + string pngFileName = Path.ChangeExtension(i.ToString(), "png"); + ksf.Images[i].RawBitmap?.ToBitmap().Save(Path.Combine(pngKsfFolder, pngFileName), ImageFormat.Png); + } + } + } + } } \ No newline at end of file