mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-04-10 11:20:05 +03:00
1102 lines
44 KiB
C#
1102 lines
44 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using Syroot.BinaryData;
|
|
using Syroot.Worms.Core;
|
|
|
|
namespace Syroot.Worms.Armageddon
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public class Scheme : ILoadableFile, ISaveableFile
|
|
{
|
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
|
|
|
private const string _signature = "SCHM";
|
|
|
|
// Lookup tables.
|
|
private static readonly Dictionary<byte, byte> _mapWaterRiseToRaw = new Dictionary<byte, byte>() { [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 ------------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Scheme"/> class.
|
|
/// </summary>
|
|
public Scheme()
|
|
{
|
|
Version = SchemeVersion.Extended;
|
|
Weapons = new SchemeWeaponSetting[GetWeaponCount()];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Scheme"/> class, loading the data from the given
|
|
/// <see cref="Stream"/>.
|
|
/// </summary>
|
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
|
public Scheme(Stream stream)
|
|
{
|
|
Load(stream);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Scheme"/> class, loading the data from the given file.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
|
public Scheme(string fileName)
|
|
{
|
|
Load(fileName);
|
|
}
|
|
|
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Gets or sets the <see cref="SchemeVersion"/> of this scheme, controlling whether super weapon settings
|
|
/// are stored or not.
|
|
/// </summary>
|
|
public SchemeVersion Version { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the delay in seconds between each team's turn to allow relaxed switching of seats.
|
|
/// </summary>
|
|
public byte HotSeatDelay { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public byte RetreatTime { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public byte RetreatTimeRope { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the total round time until sudden death will be displayed in the
|
|
/// turn timer.
|
|
/// </summary>
|
|
public bool ShowRoundTime { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether significant turns will be replayed in offline games.
|
|
/// </summary>
|
|
public bool AutomaticReplays { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the percentual amount of fall damage applied, relative to normal fall damage being 100%.
|
|
/// </summary>
|
|
public SchemeFallDamage FallDamage { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether worms cannot walk and are mostly stuck at their current position
|
|
/// without using any utilities.
|
|
/// </summary>
|
|
public bool ArtilleryMode { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating the scheme editor used to modify this scheme. Originally used to indicate
|
|
/// the unimplemented Bounty Mode.
|
|
/// </summary>
|
|
public SchemeEditor SchemeEditor { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating the stockpiling of armory between game rounds.
|
|
/// </summary>
|
|
public SchemeStockpiling StockpilingMode { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating the worm selection order determining the next worm to be played.
|
|
/// </summary>
|
|
public SchemeWormSelect WormSelectMode { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating the action triggered when Sudden Death starts.
|
|
/// </summary>
|
|
public SchemeSuddenDeathEvent SuddenDeathEvent { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the amount in pixels which the water will rise between turns after Sudden Death was triggered.
|
|
/// </summary>
|
|
public SchemeWaterRise WaterRiseRate { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the percentual probability of a weapon crate to drop between turns. Negative values might crash
|
|
/// the game.
|
|
/// </summary>
|
|
public sbyte WeaponCrateProbability { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether donor cards can spawn upon a worm's death.
|
|
/// </summary>
|
|
public bool DonorCards { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the percentual probability of a health crate to drop between turns. Negative values might crash
|
|
/// the game.
|
|
/// </summary>
|
|
public sbyte HealthCrateProbability { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the amount of health included in a health crate added to the collecting worm's energy.
|
|
/// </summary>
|
|
public byte HealthCrateEnergy { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the percentual probability of a utility crate to drop between turns. Negative values might
|
|
/// crash the game.
|
|
/// </summary>
|
|
public sbyte UtilityCrateProbability { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the type of objects which can be placed on the map.
|
|
/// </summary>
|
|
public SchemeObjectType ObjectTypes { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the maximum number of objects (mines or oil drums) on the map.
|
|
/// </summary>
|
|
public SchemeObjectCount ObjectCount { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the number of seconds a mine requires to explode. Can be 1-3 and 5-127 seconds.
|
|
/// </summary>
|
|
public byte MineDelay
|
|
{
|
|
get
|
|
{
|
|
return _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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the mine fuse will be randomly chosen between fractions of 1 to 3
|
|
/// seconds. If <c>true</c>, the <see cref="MineDelay"/> setting will be ignored.
|
|
/// </summary>
|
|
public bool MineDelayRandom { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether mines can refuse to explode after their count down.
|
|
/// </summary>
|
|
public bool DudMines { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether worms are placed manually by the players for their initial position
|
|
/// at round start.
|
|
/// </summary>
|
|
public bool ManualWormPlacement { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the initial worm energy at round start.
|
|
/// </summary>
|
|
public byte WormEnergy { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the turn time in seconds available for the player to move. Must be in the range of 0-127.
|
|
/// </summary>
|
|
public byte TurnTime
|
|
{
|
|
get
|
|
{
|
|
return _turnTime;
|
|
}
|
|
set
|
|
{
|
|
if (value > 0x7F)
|
|
{
|
|
throw new ArgumentException("Turn time must be between 0-127.", nameof(value));
|
|
}
|
|
_turnTime = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the turn time is unlimited. If <c>true</c>, the
|
|
/// <see cref="TurnTime"/> setting will be ignored.
|
|
/// </summary>
|
|
public bool TurnTimeInfinite { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the round time before sudden death is triggered between 0-127 minutes.
|
|
/// </summary>
|
|
public byte RoundTimeMinutes
|
|
{
|
|
get
|
|
{
|
|
return _roundTimeMinutes;
|
|
}
|
|
set
|
|
{
|
|
if (value > 0x7F)
|
|
{
|
|
throw new ArgumentException("Round time must be between 0-127 minutes.", nameof(value));
|
|
}
|
|
_roundTimeMinutes = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the round time before sudden death is triggered in between 0-128 seconds. When 0,
|
|
/// <see cref="RoundTimeMinutes"/> is used instead.
|
|
/// </summary>
|
|
public byte RoundTimeSeconds
|
|
{
|
|
get
|
|
{
|
|
return _roundTimeSeconds;
|
|
}
|
|
set
|
|
{
|
|
if (value > 0x80)
|
|
{
|
|
throw new ArgumentException("Round time must be between 0-128 seconds.", nameof(value));
|
|
}
|
|
_roundTimeSeconds = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the number of round wins required to win the game. Must not be 0.
|
|
/// </summary>
|
|
public byte NumberOfWins
|
|
{
|
|
get
|
|
{
|
|
return _numberOfWins;
|
|
}
|
|
set
|
|
{
|
|
if (value == 0)
|
|
{
|
|
throw new ArgumentException("Number of wins must not be 0.", nameof(value));
|
|
}
|
|
_numberOfWins = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether blood effects are enabled.
|
|
/// </summary>
|
|
public bool Blood { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the Super Sheep weapon gets upgraded to the Aqua Sheep, which can
|
|
/// fly underwater.
|
|
/// </summary>
|
|
public bool AquaSheep { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether sheeps will jump out of exploding crates.
|
|
/// </summary>
|
|
public bool SheepHeaven { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether worms have infinity energy, killable only by drowning them.
|
|
/// </summary>
|
|
public bool GodWorms { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether terrain cannot be destroyed by explosions.
|
|
/// </summary>
|
|
public bool IndestructibleLand { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the Grenade weapon is more powerful.
|
|
/// </summary>
|
|
public bool UpgradedGrenade { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the Shotgun weapon shoots twice for each of the two shots.
|
|
/// </summary>
|
|
public bool UpgradedShotgun { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether cluster weapon explode into more clusters.
|
|
/// </summary>
|
|
public bool UpgradedCluster { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the Longbow weapon is more powerful.
|
|
/// </summary>
|
|
public bool UpgradedLongbow { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether team weapons will be given to the teams, overriding the default
|
|
/// weapon settings for them.
|
|
/// </summary>
|
|
public bool EnableTeamWeapons { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether super weapons can be collected from crates.
|
|
/// </summary>
|
|
public bool EnableSuperWeapons { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets the array of <see cref="SchemeWeaponSetting"/> instances, each mapping to one weapon at the index of
|
|
/// the <see cref="SchemeWeapon"/> enumeration. Depending on the scheme <see cref="Version"/>, super weapons
|
|
/// might not be stored in this array.
|
|
/// </summary>
|
|
public SchemeWeaponSetting[] Weapons { get; set; }
|
|
|
|
// ---- RubberWorm Settings ----
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value whether power is unlocked like in TestStuff. Configurable with the /alp command.
|
|
/// </summary>
|
|
public bool RwAntiLockPower { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public bool RwAntiSink { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the aim direction of a weapon will be reset at turn start.
|
|
/// Configurable with the /reaim command.
|
|
/// </summary>
|
|
public bool RwAutoReaim { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value whether the aim of a weapon loops in full rather than half circles. Configurable with
|
|
/// the /cira command.
|
|
/// </summary>
|
|
public bool RwCircularAim { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public byte RwCrateLimit { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the maximum number of crates spawning per turn and enables the crate counter. Configurable with
|
|
/// the /craterate command.
|
|
/// </summary>
|
|
public byte RwCrateRate { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether crate shower is enabled throughout a turn. Configurable with the
|
|
/// /crateshower command.
|
|
/// </summary>
|
|
public bool RwCrateShower { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public bool RwExtendedFuse { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the turn timer is paused while launching a weapon. Configurable with
|
|
/// the /fdpt or /nopause commands.
|
|
/// </summary>
|
|
public bool RwFireDoesntPauseTimer { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the maximum number of flame particles active at the same time in the scale of 100x.
|
|
/// Configurable with the /flames command.
|
|
/// </summary>
|
|
public byte RwFlameLimit { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public byte RwFriction { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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,
|
|
/// <see cref="RwGravityConstBlackHole"/> and <see cref="RwGravityPropBlackHole"/> are reset.
|
|
/// </summary>
|
|
public sbyte RwGravity
|
|
{
|
|
get
|
|
{
|
|
return _rwGravity;
|
|
}
|
|
set
|
|
{
|
|
if (value != 0)
|
|
{
|
|
RwGravityConstBlackHole = 0;
|
|
RwGravityPropBlackHole = 0;
|
|
}
|
|
_rwGravity = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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, <see cref="RwGravity"/> and <see cref="RwGravityPropBlackHole"/> are reset.
|
|
/// </summary>
|
|
public sbyte RwGravityConstBlackHole
|
|
{
|
|
get
|
|
{
|
|
return _rwGravityConstBlackHole;
|
|
}
|
|
set
|
|
{
|
|
if (value != 0)
|
|
{
|
|
RwGravity = 0;
|
|
RwGravityPropBlackHole = 0;
|
|
}
|
|
_rwGravityConstBlackHole = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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, <see cref="RwGravity"/> and <see cref="RwGravityConstBlackHole"/> are
|
|
/// reset.
|
|
/// </summary>
|
|
public sbyte RwGravityPropBlackHole
|
|
{
|
|
get
|
|
{
|
|
return _rwGravityPropBlackHole;
|
|
}
|
|
set
|
|
{
|
|
if (value != 0)
|
|
{
|
|
RwGravity = 0;
|
|
RwGravityConstBlackHole = 0;
|
|
}
|
|
_rwGravityPropBlackHole = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Kaos game scheme mod. 0 for none, 1-5 for the corresponding mod. Configurable with the
|
|
/// /kaosmod command.
|
|
/// </summary>
|
|
public byte RwKaosMod
|
|
{
|
|
get
|
|
{
|
|
return _rwKaosMod;
|
|
}
|
|
set
|
|
{
|
|
if (value > 0xF)
|
|
{
|
|
throw new ArgumentException("Kaos mod must not be greater than 15.");
|
|
}
|
|
_rwKaosMod = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the rope knocking power in percent, where 100 is the default power. Configurable with the
|
|
/// /knock command.
|
|
/// </summary>
|
|
public byte RwKnockForce { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public bool RwLoseControlDoesntEndTurn { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public byte RwMaxRopeSpeed { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public bool RwShotDoesntEndTurn { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the weapons Armageddon, Earthquake and Indian Nuke Test are affected
|
|
/// by <see cref="RwShotDoesntEndTurn"/> aswell. Configurable with the /usw command.
|
|
/// </summary>
|
|
public bool RwShotDoesntEndTurnAll { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether all objects are pushed by explosions near them. Configurable with
|
|
/// the /ope command.
|
|
/// </summary>
|
|
public bool RwObjectPushByExplosion { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public bool RwSelectWormAnytime { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether the Ninja Rope is more powerful. Configurable with the /ir or /rope+
|
|
/// commands.
|
|
/// </summary>
|
|
public bool RwUpgradedRope { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public ushort RwVersionOverride { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the amount of air viscosity affecting objects. Odd numbers affect worms too. Configurable with
|
|
/// the /visc command.
|
|
/// </summary>
|
|
public byte RwViscosity { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public bool RwWeaponsDontChange { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public byte RwWindPower { get; set; }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public byte RwWormBouncyness { get; set; }
|
|
|
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Loads the data from the given <see cref="Stream"/>.
|
|
/// </summary>
|
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
|
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<SchemeVersion>(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<SchemeEditor>(false);
|
|
StockpilingMode = reader.ReadEnum<SchemeStockpiling>(true);
|
|
WormSelectMode = reader.ReadEnum<SchemeWormSelect>(true);
|
|
SuddenDeathEvent = reader.ReadEnum<SchemeSuddenDeathEvent>(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, (int)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<SchemeWeaponSetting>();
|
|
}
|
|
|
|
// Ignore possible unknown WWP trash at the end of the file.
|
|
|
|
// Parse the RubberWorm settings.
|
|
LoadRubberWormSettings();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads the data from the given file.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
|
public void Load(string fileName)
|
|
{
|
|
using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
|
|
{
|
|
Load(stream);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the data into the given <paramref name="stream"/>.
|
|
/// </summary>
|
|
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
|
public void Save(Stream stream)
|
|
{
|
|
Save(stream, SchemeSaveFormat.ExtendedWithObjectCount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the data into the given <paramref name="stream"/> with the specified <paramref name="format"/>.
|
|
/// </summary>
|
|
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
|
/// <param name="format">The <see cref="SchemeSaveFormat"/> to respect when storing the settings.</param>
|
|
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.Write(weapon);
|
|
}
|
|
|
|
// Ignore possible unknown WWP trash at the end of the file.
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the data in the given file.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to save the data in.</param>
|
|
public void Save(string fileName)
|
|
{
|
|
Save(fileName, SchemeSaveFormat.ExtendedWithObjectCount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves the data in the given file with the specified <paramref name="format"/>.
|
|
/// </summary>
|
|
/// <param name="fileName">The name of the file to save the data in.</param>
|
|
/// <param name="format">The <see cref="SchemeSaveFormat"/> to respect when storing the settings.</param>
|
|
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
|
|
}
|
|
}
|
|
}
|