Simplify Scheme usage.

- SchemeWeapon enum has been renamed to SchemeWeaponName.
- SchemeWeaponSetting has been renamed to SchemeWeapon.
- Scheme.FallDamage and Scheme.WaterRiseRate are now integral values, rounding down to the nearest valid setting.
- Static Scheme.ValidXxx properties were added to retrieve a list of valid fall damage, water rise, and object count values.
- Scheme properties uniquely throw ArgumentOutOfRangeExceptions if a value is outside of the allowed bounds.
This commit is contained in:
Ray Koopa 2020-06-27 12:36:20 +02:00
parent e2b41157db
commit b46cdadb89
5 changed files with 282 additions and 1379 deletions

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using Syroot.BinaryData; using Syroot.BinaryData;
using Syroot.Worms.Core; using Syroot.Worms.Core;
@ -23,57 +24,12 @@ namespace Syroot.Worms.Armageddon
private const int _objectCount10Step = 44; private const int _objectCount10Step = 44;
private const int _objectCountLastStep = 59; private const int _objectCountLastStep = 59;
// Lookup tables. // ---- FIELDS -------------------------------------------------------------------------------------------------
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
};
// ---- MEMBERS ------------------------------------------------------------------------------------------------ private static readonly byte[] _waterRiseRates;
private byte _fallDamage;
private byte _waterRiseIndex;
private byte _objectCount; private byte _objectCount;
private byte _mineDelay; private byte _mineDelay;
private byte _turnTime; private byte _turnTime;
@ -87,13 +43,21 @@ namespace Syroot.Worms.Armageddon
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
static Scheme()
{
// Generate water rise rate lookup array.
_waterRiseRates = new byte[256];
for (int i = 0; i < 256; i++)
_waterRiseRates[i] = (byte)(Math.Pow(i, 2) * 5 % 256);
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Scheme"/> class. /// Initializes a new instance of the <see cref="Scheme"/> class.
/// </summary> /// </summary>
public Scheme() public Scheme()
{ {
Version = SchemeVersion.Extended; Version = SchemeVersion.Extended;
Weapons = new SchemeWeaponSetting[GetWeaponCount()]; Weapons = new SchemeWeapon[GetWeaponCount()];
} }
/// <summary> /// <summary>
@ -111,6 +75,39 @@ namespace Syroot.Worms.Armageddon
// ---- PROPERTIES --------------------------------------------------------------------------------------------- // ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets the available mine or oil drum object counts.
/// </summary>
public static IEnumerable<int> ValidObjectCounts
{
get
{
for (int i = 1; i < 30; i++)
yield return i;
for (int i = 30; i < 100; i += 5)
yield return i;
for (int i = 100; i <= 250; i += 10)
yield return i;
}
}
/// <summary>
/// Gets the available fall damage percentages.
/// </summary>
public static IEnumerable<int> ValidFallDamages
{
get
{
for (int i = 0; i <= 508; i += 4)
yield return i;
}
}
/// <summary>
/// Gets the available unique and ordered water rise rates in pixels.
/// </summary>
public static IEnumerable<byte> ValidWateRiseRates => _waterRiseRates.Distinct().OrderBy(x => x);
/// <summary> /// <summary>
/// Gets or sets the <see cref="SchemeVersion"/> of this scheme, controlling whether super weapon settings /// Gets or sets the <see cref="SchemeVersion"/> of this scheme, controlling whether super weapon settings
/// are stored or not. /// are stored or not.
@ -146,9 +143,22 @@ namespace Syroot.Worms.Armageddon
public bool AutomaticReplays { get; set; } public bool AutomaticReplays { get; set; }
/// <summary> /// <summary>
/// Gets or sets the percentual amount of fall damage applied, relative to normal fall damage being 100%. /// Gets or sets the percentual amount of fall damage applied, relative to normal fall damage being 100%. Valid
/// values lie between 0-508, at a granularity of 4. Note that changing this value rounds it down to a valid
/// granularity.
/// </summary> /// </summary>
public SchemeFallDamage FallDamage { get; set; } /// <remarks>Valid settings can be enumerated through <see cref="ValidFallDamages"/>.</remarks>
/// <exception cref="ArgumentOutOfRangeException">The value is not between 0-508.</exception>
public int FallDamage
{
get => _fallDamage;
set
{
if (value < 0 || value > 508)
throw new ArgumentOutOfRangeException(nameof(value), "Fall damage must be between 0-508.");
_fallDamage = (byte)(value >> 2 << 2);
}
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether worms cannot walk and are mostly stuck at their current position /// Gets or sets a value indicating whether worms cannot walk and are mostly stuck at their current position
@ -179,8 +189,26 @@ namespace Syroot.Worms.Armageddon
/// <summary> /// <summary>
/// Gets or sets the amount in pixels which the water will rise between turns after Sudden Death was triggered. /// Gets or sets the amount in pixels which the water will rise between turns after Sudden Death was triggered.
/// Note that changing this value rounds it down to a valid setting.
/// </summary> /// </summary>
public SchemeWaterRise WaterRiseRate { get; set; } /// <remarks>Valid settings can be enumerated through <see cref="ValidWateRiseRates"/>.</remarks>
public byte WaterRiseRate
{
get => _waterRiseRates[_waterRiseIndex];
set
{
int smallestDeviation = Int32.MaxValue;
for (int i = 0; i < _waterRiseRates.Length; i++)
{
int deviation = Math.Abs(value - _waterRiseRates[i]);
if (deviation >= 0 && smallestDeviation > deviation)
{
smallestDeviation = deviation;
_waterRiseIndex = (byte)i;
}
}
}
}
/// <summary> /// <summary>
/// Gets or sets the percentual probability of a weapon crate to drop between turns. Negative values might crash /// Gets or sets the percentual probability of a weapon crate to drop between turns. Negative values might crash
@ -216,9 +244,10 @@ namespace Syroot.Worms.Armageddon
public SchemeObjectType ObjectTypes { get; set; } public SchemeObjectType ObjectTypes { get; set; }
/// <summary> /// <summary>
/// Gets or sets the maximum number of objects (mines or oil drums) on the map. Note that setting this value /// Gets or sets the maximum number of objects (mines or oil drums) on the map. Note that changing this value
/// rounds it valid count. /// rounds it down to a valid setting.
/// </summary> /// </summary>
/// <remarks>Valid settings can be enumerated through <see cref="ValidObjectCounts"/>.</remarks>
public byte ObjectCount public byte ObjectCount
{ {
get => _objectCount; get => _objectCount;
@ -238,13 +267,14 @@ namespace Syroot.Worms.Armageddon
/// <summary> /// <summary>
/// Gets or sets the number of seconds a mine requires to explode. Can be 1-3 and 5-127 seconds. /// Gets or sets the number of seconds a mine requires to explode. Can be 1-3 and 5-127 seconds.
/// </summary> /// </summary>
/// <exception cref="ArgumentOutOfRangeException">Value is not between 0-127 or 4.</exception>
public byte MineDelay public byte MineDelay
{ {
get => _mineDelay; get => _mineDelay;
set set
{ {
if (value == 4 || value > 0x7F) if (value == 4 || value > 127)
throw new ArgumentException("Mine delay must be between 0-127 and not be 4.", nameof(value)); throw new ArgumentOutOfRangeException(nameof(value), "Mine delay must be between 0-127 and not be 4.");
_mineDelay = value; _mineDelay = value;
} }
} }
@ -274,13 +304,14 @@ namespace Syroot.Worms.Armageddon
/// <summary> /// <summary>
/// Gets or sets the turn time in seconds available for the player to move. Must be in the range of 0-127. /// Gets or sets the turn time in seconds available for the player to move. Must be in the range of 0-127.
/// </summary> /// </summary>
/// <exception cref="ArgumentOutOfRangeException">Value is not between 0-127.</exception>
public byte TurnTime public byte TurnTime
{ {
get => _turnTime; get => _turnTime;
set set
{ {
if (value > 0x7F) if (value > 127)
throw new ArgumentException("Turn time must be between 0-127.", nameof(value)); throw new ArgumentOutOfRangeException(nameof(value), "Turn time must be between 0-127.");
_turnTime = value; _turnTime = value;
} }
} }
@ -294,13 +325,14 @@ namespace Syroot.Worms.Armageddon
/// <summary> /// <summary>
/// Gets or sets the round time before sudden death is triggered between 0-127 minutes. /// Gets or sets the round time before sudden death is triggered between 0-127 minutes.
/// </summary> /// </summary>
/// <exception cref="ArgumentOutOfRangeException">Value is not between 0-127.</exception>
public byte RoundTimeMinutes public byte RoundTimeMinutes
{ {
get => _roundTimeMinutes; get => _roundTimeMinutes;
set set
{ {
if (value > 0x7F) if (value > 127)
throw new ArgumentException("Round time must be between 0-127 minutes.", nameof(value)); throw new ArgumentOutOfRangeException(nameof(value), "Round time must be between 0-127 minutes.");
_roundTimeMinutes = value; _roundTimeMinutes = value;
} }
} }
@ -309,13 +341,14 @@ namespace Syroot.Worms.Armageddon
/// Gets or sets the round time before sudden death is triggered in between 0-128 seconds. When 0, /// Gets or sets the round time before sudden death is triggered in between 0-128 seconds. When 0,
/// <see cref="RoundTimeMinutes"/> is used instead. /// <see cref="RoundTimeMinutes"/> is used instead.
/// </summary> /// </summary>
/// <exception cref="ArgumentOutOfRangeException">Value is not between 0-128.</exception>
public byte RoundTimeSeconds public byte RoundTimeSeconds
{ {
get => _roundTimeSeconds; get => _roundTimeSeconds;
set set
{ {
if (value > 0x80) if (value > 128)
throw new ArgumentException("Round time must be between 0-128 seconds.", nameof(value)); throw new ArgumentOutOfRangeException(nameof(value), "Round time must be between 0-128 seconds.");
_roundTimeSeconds = value; _roundTimeSeconds = value;
} }
} }
@ -323,13 +356,14 @@ namespace Syroot.Worms.Armageddon
/// <summary> /// <summary>
/// Gets or sets the number of round wins required to win the game. Must not be 0. /// Gets or sets the number of round wins required to win the game. Must not be 0.
/// </summary> /// </summary>
/// <exception cref="ArgumentOutOfRangeException">Value is 0.</exception>
public byte NumberOfWins public byte NumberOfWins
{ {
get => _numberOfWins; get => _numberOfWins;
set set
{ {
if (value == 0) if (value == 0)
throw new ArgumentException("Number of wins must not be 0.", nameof(value)); throw new ArgumentOutOfRangeException(nameof(value), "Number of wins must not be 0.");
_numberOfWins = value; _numberOfWins = value;
} }
} }
@ -392,11 +426,11 @@ namespace Syroot.Worms.Armageddon
public bool EnableSuperWeapons { get; set; } public bool EnableSuperWeapons { get; set; }
/// <summary> /// <summary>
/// Gets the array of <see cref="SchemeWeaponSetting"/> instances, each mapping to one weapon at the index of /// Gets the array of <see cref="SchemeWeapon"/> instances, each mapping to one weapon at the index of
/// the <see cref="SchemeWeapon"/> enumeration. Depending on the scheme <see cref="Version"/>, super weapons /// the <see cref="SchemeWeaponName"/> enumeration. Depending on the scheme <see cref="Version"/>, super weapons
/// might not be stored in this array. /// might not be stored in this array.
/// </summary> /// </summary>
public SchemeWeaponSetting[] Weapons { get; set; } public SchemeWeapon[] Weapons { get; set; }
// ---- RubberWorm Settings ---- // ---- RubberWorm Settings ----
@ -525,13 +559,14 @@ namespace Syroot.Worms.Armageddon
/// Gets or sets the Kaos game scheme mod. 0 for none, 1-5 for the corresponding mod. Configurable with the /// Gets or sets the Kaos game scheme mod. 0 for none, 1-5 for the corresponding mod. Configurable with the
/// /kaosmod command. /// /kaosmod command.
/// </summary> /// </summary>
/// <exception cref="ArgumentOutOfRangeException">Value is bigger than 15.</exception>
public byte RwKaosMod public byte RwKaosMod
{ {
get => _rwKaosMod; get => _rwKaosMod;
set set
{ {
if (value > 0xF) if (value > 15)
throw new ArgumentException("Kaos mod must not be greater than 15."); throw new ArgumentOutOfRangeException(nameof(value), "Kaos mod must not be greater than 15.");
_rwKaosMod = value; _rwKaosMod = value;
} }
} }
@ -635,13 +670,13 @@ namespace Syroot.Worms.Armageddon
RetreatTimeRope = reader.Read1Byte(); RetreatTimeRope = reader.Read1Byte();
ShowRoundTime = reader.ReadBoolean(); ShowRoundTime = reader.ReadBoolean();
AutomaticReplays = reader.ReadBoolean(); AutomaticReplays = reader.ReadBoolean();
FallDamage = (SchemeFallDamage)(reader.ReadByte() * 50 % 0x100 * 2); FallDamage = reader.ReadByte() * 50 % 256 * 2;
ArtilleryMode = reader.ReadBoolean(); ArtilleryMode = reader.ReadBoolean();
SchemeEditor = reader.ReadEnum<SchemeEditor>(false); SchemeEditor = reader.ReadEnum<SchemeEditor>(false);
StockpilingMode = reader.ReadEnum<SchemeStockpiling>(true); StockpilingMode = reader.ReadEnum<SchemeStockpiling>(true);
WormSelectMode = reader.ReadEnum<SchemeWormSelect>(true); WormSelectMode = reader.ReadEnum<SchemeWormSelect>(true);
SuddenDeathEvent = reader.ReadEnum<SchemeSuddenDeathEvent>(true); SuddenDeathEvent = reader.ReadEnum<SchemeSuddenDeathEvent>(true);
WaterRiseRate = (SchemeWaterRise)(Math.Pow(reader.ReadByte(), 2) * 5 % 0x100); _waterRiseIndex = reader.Read1Byte();
WeaponCrateProbability = reader.ReadSByte(); WeaponCrateProbability = reader.ReadSByte();
DonorCards = reader.ReadBoolean(); DonorCards = reader.ReadBoolean();
HealthCrateProbability = reader.ReadSByte(); HealthCrateProbability = reader.ReadSByte();
@ -668,10 +703,10 @@ namespace Syroot.Worms.Armageddon
EnableSuperWeapons = reader.ReadBoolean(); EnableSuperWeapons = reader.ReadBoolean();
// Read the weapon settings. Old versions do not store super weapon settings. // Read the weapon settings. Old versions do not store super weapon settings.
Weapons = new SchemeWeaponSetting[64]; Weapons = new SchemeWeapon[64];
int weaponCount = GetWeaponCount(); int weaponCount = GetWeaponCount();
for (int i = 0; i < weaponCount; i++) for (int i = 0; i < weaponCount; i++)
Weapons[i] = reader.ReadStruct<SchemeWeaponSetting>(); Weapons[i] = reader.ReadStruct<SchemeWeapon>();
// Ignore possible unknown WWP trash at the end of the file. // Ignore possible unknown WWP trash at the end of the file.
@ -714,13 +749,13 @@ namespace Syroot.Worms.Armageddon
writer.Write(RetreatTimeRope); writer.Write(RetreatTimeRope);
writer.Write(ShowRoundTime); writer.Write(ShowRoundTime);
writer.Write(AutomaticReplays); writer.Write(AutomaticReplays);
writer.Write((byte)((int)FallDamage / 4 * 41 % 0x80)); writer.Write((byte)(FallDamage / 4 * 41 % 128));
writer.Write(ArtilleryMode); writer.Write(ArtilleryMode);
writer.WriteEnum(SchemeEditor, false); writer.WriteEnum(SchemeEditor, false);
writer.WriteEnum(StockpilingMode, true); writer.WriteEnum(StockpilingMode, true);
writer.WriteEnum(WormSelectMode, true); writer.WriteEnum(WormSelectMode, true);
writer.WriteEnum(SuddenDeathEvent, true); writer.WriteEnum(SuddenDeathEvent, true);
writer.Write(_mapWaterRiseToRaw[(byte)WaterRiseRate]); writer.Write(_waterRiseIndex);
writer.Write(WeaponCrateProbability); writer.Write(WeaponCrateProbability);
writer.Write(DonorCards); writer.Write(DonorCards);
writer.Write(HealthCrateProbability); writer.Write(HealthCrateProbability);
@ -751,7 +786,7 @@ namespace Syroot.Worms.Armageddon
// Write the weapon settings. Old versions do not store super weapon settings. // Write the weapon settings. Old versions do not store super weapon settings.
int weaponCount = GetWeaponCount(); int weaponCount = GetWeaponCount();
foreach (SchemeWeaponSetting weapon in Weapons) foreach (SchemeWeapon weapon in Weapons)
writer.WriteStruct(weapon); writer.WriteStruct(weapon);
} }
@ -774,6 +809,8 @@ namespace Syroot.Worms.Armageddon
// ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
private int GetWeaponCount() => Version == SchemeVersion.Extended ? 64 : 45;
private void LoadObjectTypesAndCount(BinaryStream reader) private void LoadObjectTypesAndCount(BinaryStream reader)
{ {
byte raw = reader.Read1Byte(); byte raw = reader.Read1Byte();
@ -800,31 +837,15 @@ namespace Syroot.Worms.Armageddon
private void LoadMineDelayConfig(BinaryStream reader) private void LoadMineDelayConfig(BinaryStream reader)
{ {
byte raw = reader.Read1Byte(); byte raw = reader.Read1Byte();
if (raw == 4 || raw > 0x7F) MineDelayRandom = raw == 4 || raw > 0x7F;
{ MineDelay = MineDelayRandom ? (byte)0 : raw;
MineDelay = 0;
MineDelayRandom = true;
}
else
{
MineDelay = raw;
MineDelayRandom = false;
}
} }
private void LoadTurnTimeConfig(BinaryStream reader) private void LoadTurnTimeConfig(BinaryStream reader)
{ {
byte raw = reader.Read1Byte(); byte raw = reader.Read1Byte();
if (raw > 0x7F) TurnTimeInfinite = raw > 0x7F;
{ TurnTime = TurnTimeInfinite ? (byte)0 : raw;
TurnTime = 0;
TurnTimeInfinite = true;
}
else
{
TurnTime = raw;
TurnTimeInfinite = false;
}
} }
private void LoadRoundTimeConfig(BinaryStream reader) private void LoadRoundTimeConfig(BinaryStream reader)
@ -844,18 +865,18 @@ namespace Syroot.Worms.Armageddon
private void LoadRubberWormSettings() private void LoadRubberWormSettings()
{ {
RwEarthquakeProb earthquakeProb = (RwEarthquakeProb)Weapons[(int)SchemeWeapon.Earthquake].Probability; RwEarthquakeProb earthquakeProb = (RwEarthquakeProb)Weapons[(int)SchemeWeaponName.Earthquake].Probability;
RwAntiLockPower = earthquakeProb.HasFlag(RwEarthquakeProb.AntiLockPower); RwAntiLockPower = earthquakeProb.HasFlag(RwEarthquakeProb.AntiLockPower);
RwAutoReaim = earthquakeProb.HasFlag(RwEarthquakeProb.AutoReaim); RwAutoReaim = earthquakeProb.HasFlag(RwEarthquakeProb.AutoReaim);
RwCircularAim = earthquakeProb.HasFlag(RwEarthquakeProb.CircularAim); RwCircularAim = earthquakeProb.HasFlag(RwEarthquakeProb.CircularAim);
RwShotDoesntEndTurnAll = earthquakeProb.HasFlag(RwEarthquakeProb.ShotDoesntEndTurnAll); RwShotDoesntEndTurnAll = earthquakeProb.HasFlag(RwEarthquakeProb.ShotDoesntEndTurnAll);
RwKaosMod = ((byte)earthquakeProb).DecodeByte(4, 4); RwKaosMod = ((byte)earthquakeProb).DecodeByte(4, 4);
RwAntiSink = Weapons[(int)SchemeWeapon.SheepStrike].Probability != 0; RwAntiSink = Weapons[(int)SchemeWeaponName.SheepStrike].Probability != 0;
RwCrateLimit = (byte)Weapons[(int)SchemeWeapon.MagicBullet].Probability; RwCrateLimit = (byte)Weapons[(int)SchemeWeaponName.MagicBullet].Probability;
RwCrateRate = (byte)Weapons[(int)SchemeWeapon.NuclearTest].Probability; RwCrateRate = (byte)Weapons[(int)SchemeWeaponName.NuclearTest].Probability;
RwMoleSquadronProb moleSquadronProb = (RwMoleSquadronProb)Weapons[(int)SchemeWeapon.MoleSquadron] RwMoleSquadronProb moleSquadronProb = (RwMoleSquadronProb)Weapons[(int)SchemeWeaponName.MoleSquadron]
.Probability; .Probability;
RwCrateShower = moleSquadronProb.HasFlag(RwMoleSquadronProb.CrateShower); RwCrateShower = moleSquadronProb.HasFlag(RwMoleSquadronProb.CrateShower);
RwExtendedFuse = moleSquadronProb.HasFlag(RwMoleSquadronProb.ExtendedFuse); RwExtendedFuse = moleSquadronProb.HasFlag(RwMoleSquadronProb.ExtendedFuse);
@ -866,11 +887,11 @@ namespace Syroot.Worms.Armageddon
RwUpgradedRope = moleSquadronProb.HasFlag(RwMoleSquadronProb.UpgradedRope); RwUpgradedRope = moleSquadronProb.HasFlag(RwMoleSquadronProb.UpgradedRope);
RwWeaponsDontChange = moleSquadronProb.HasFlag(RwMoleSquadronProb.WeaponsDontChange); RwWeaponsDontChange = moleSquadronProb.HasFlag(RwMoleSquadronProb.WeaponsDontChange);
RwFlameLimit = (byte)Weapons[(int)SchemeWeapon.ScalesOfJustice].Probability; RwFlameLimit = (byte)Weapons[(int)SchemeWeaponName.ScalesOfJustice].Probability;
RwFriction = (byte)Weapons[(int)SchemeWeapon.SalvationArmy].Probability; RwFriction = (byte)Weapons[(int)SchemeWeaponName.SalvationArmy].Probability;
// 8th and 7th bit control constant / proportional black hole gravity, otherwise normal gravity. // 8th and 7th bit control constant / proportional black hole gravity, otherwise normal gravity.
byte mailStrikeProb = (byte)Weapons[(int)SchemeWeapon.MailStrike].Probability; byte mailStrikeProb = (byte)Weapons[(int)SchemeWeaponName.MailStrike].Probability;
if (!mailStrikeProb.GetBit(7)) if (!mailStrikeProb.GetBit(7))
RwGravity = mailStrikeProb.DecodeSByte(7); RwGravity = mailStrikeProb.DecodeSByte(7);
else if (mailStrikeProb.GetBit(6)) else if (mailStrikeProb.GetBit(6))
@ -878,24 +899,18 @@ namespace Syroot.Worms.Armageddon
else else
RwGravityConstBlackHole = mailStrikeProb.DecodeSByte(6); RwGravityConstBlackHole = mailStrikeProb.DecodeSByte(6);
RwKnockForce = (byte)Weapons[(int)SchemeWeapon.SuperBananaBomb].Probability; RwKnockForce = (byte)Weapons[(int)SchemeWeaponName.SuperBananaBomb].Probability;
RwMaxRopeSpeed = (byte)Weapons[(int)SchemeWeapon.MineStrike].Probability; RwMaxRopeSpeed = (byte)Weapons[(int)SchemeWeaponName.MineStrike].Probability;
RwSelectWormAnytime = ((byte)Weapons[(int)SchemeWeapon.MBBomb].Probability).GetBit(1); RwSelectWormAnytime = ((byte)Weapons[(int)SchemeWeaponName.MBBomb].Probability).GetBit(1);
byte[] versionBytes = new byte[2]; byte[] versionBytes = new byte[2];
versionBytes[0] = (byte)Weapons[(int)SchemeWeapon.SelectWorm].Probability; versionBytes[0] = (byte)Weapons[(int)SchemeWeaponName.SelectWorm].Probability;
versionBytes[1] = (byte)Weapons[(int)SchemeWeapon.Freeze].Probability; versionBytes[1] = (byte)Weapons[(int)SchemeWeaponName.Freeze].Probability;
RwVersionOverride = BitConverter.ToUInt16(versionBytes, 0); RwVersionOverride = BitConverter.ToUInt16(versionBytes, 0);
RwViscosity = (byte)Weapons[(int)SchemeWeapon.ConcreteDonkey].Probability; RwViscosity = (byte)Weapons[(int)SchemeWeaponName.ConcreteDonkey].Probability;
RwWindPower = (byte)Weapons[(int)SchemeWeapon.SuicideBomber].Probability; RwWindPower = (byte)Weapons[(int)SchemeWeaponName.SuicideBomber].Probability;
RwWormBouncyness = (byte)Weapons[(int)SchemeWeapon.Armageddon].Probability; RwWormBouncyness = (byte)Weapons[(int)SchemeWeaponName.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) private void SaveObjectTypesAndCount(BinaryStream writer, SchemeSaveFormat format)
@ -921,29 +936,13 @@ namespace Syroot.Worms.Armageddon
writer.Write(raw); writer.Write(raw);
} }
private void SaveMineDelayConfig(BinaryStream writer) private void SaveMineDelayConfig(BinaryStream writer) => writer.Write(MineDelayRandom ? (byte)4 : MineDelay);
{
if (MineDelayRandom)
writer.Write((byte)4);
else
writer.Write(MineDelay);
}
private void SaveTurnTimeConfig(BinaryStream writer) private void SaveTurnTimeConfig(BinaryStream writer) => writer.Write(TurnTimeInfinite ? (byte)0xFF : TurnTime);
{
if (TurnTimeInfinite)
writer.Write((byte)0xFF);
else
writer.Write(TurnTime);
}
private void SaveRoundTimeConfig(BinaryStream writer) private void SaveRoundTimeConfig(BinaryStream writer) => writer.Write(RoundTimeSeconds > 0
{ ? (byte)(0xFF - (RoundTimeSeconds - 1))
if (RoundTimeSeconds > 0) : RoundTimeMinutes);
writer.Write((byte)(0xFF - (RoundTimeSeconds - 1)));
else
writer.Write(RoundTimeMinutes);
}
private void SaveRubberWormSettings() private void SaveRubberWormSettings()
{ {
@ -953,11 +952,11 @@ namespace Syroot.Worms.Armageddon
if (RwCircularAim) earthquakeProb |= (byte)RwEarthquakeProb.CircularAim; if (RwCircularAim) earthquakeProb |= (byte)RwEarthquakeProb.CircularAim;
if (RwShotDoesntEndTurnAll) earthquakeProb |= (byte)RwEarthquakeProb.ShotDoesntEndTurnAll; if (RwShotDoesntEndTurnAll) earthquakeProb |= (byte)RwEarthquakeProb.ShotDoesntEndTurnAll;
earthquakeProb = earthquakeProb.Encode(RwKaosMod, 4); earthquakeProb = earthquakeProb.Encode(RwKaosMod, 4);
Weapons[(int)SchemeWeapon.Earthquake].Probability = (sbyte)earthquakeProb; Weapons[(int)SchemeWeaponName.Earthquake].Probability = (sbyte)earthquakeProb;
Weapons[(int)SchemeWeapon.SheepStrike].Probability = (sbyte)(RwAntiSink ? 1 : 0); Weapons[(int)SchemeWeaponName.SheepStrike].Probability = (sbyte)(RwAntiSink ? 1 : 0);
Weapons[(int)SchemeWeapon.MagicBullet].Probability = (sbyte)RwCrateLimit; Weapons[(int)SchemeWeaponName.MagicBullet].Probability = (sbyte)RwCrateLimit;
Weapons[(int)SchemeWeapon.NuclearTest].Probability = (sbyte)RwCrateRate; Weapons[(int)SchemeWeaponName.NuclearTest].Probability = (sbyte)RwCrateRate;
RwMoleSquadronProb moleSquadronProb = RwMoleSquadronProb.None; RwMoleSquadronProb moleSquadronProb = RwMoleSquadronProb.None;
if (RwCrateShower) moleSquadronProb |= RwMoleSquadronProb.CrateShower; if (RwCrateShower) moleSquadronProb |= RwMoleSquadronProb.CrateShower;
@ -968,10 +967,10 @@ namespace Syroot.Worms.Armageddon
if (RwShotDoesntEndTurn) moleSquadronProb |= RwMoleSquadronProb.ShotDoesntEndTurn; if (RwShotDoesntEndTurn) moleSquadronProb |= RwMoleSquadronProb.ShotDoesntEndTurn;
if (RwUpgradedRope) moleSquadronProb |= RwMoleSquadronProb.UpgradedRope; if (RwUpgradedRope) moleSquadronProb |= RwMoleSquadronProb.UpgradedRope;
if (RwWeaponsDontChange) moleSquadronProb |= RwMoleSquadronProb.WeaponsDontChange; if (RwWeaponsDontChange) moleSquadronProb |= RwMoleSquadronProb.WeaponsDontChange;
Weapons[(int)SchemeWeapon.MoleSquadron].Probability = (sbyte)moleSquadronProb; Weapons[(int)SchemeWeaponName.MoleSquadron].Probability = (sbyte)moleSquadronProb;
Weapons[(int)SchemeWeapon.ScalesOfJustice].Probability = (sbyte)RwFlameLimit; Weapons[(int)SchemeWeaponName.ScalesOfJustice].Probability = (sbyte)RwFlameLimit;
Weapons[(int)SchemeWeapon.SalvationArmy].Probability = (sbyte)RwFriction; Weapons[(int)SchemeWeaponName.SalvationArmy].Probability = (sbyte)RwFriction;
// 8th and 7th bit control constant / proportional black hole gravity, otherwise normal gravity. // 8th and 7th bit control constant / proportional black hole gravity, otherwise normal gravity.
byte mailStrikeProb = 0; byte mailStrikeProb = 0;
@ -990,28 +989,28 @@ namespace Syroot.Worms.Armageddon
mailStrikeProb = mailStrikeProb.EnableBit(6); mailStrikeProb = mailStrikeProb.EnableBit(6);
mailStrikeProb = mailStrikeProb.Encode(RwGravityPropBlackHole, 6); mailStrikeProb = mailStrikeProb.Encode(RwGravityPropBlackHole, 6);
} }
Weapons[(int)SchemeWeapon.MailStrike].Probability = (sbyte)mailStrikeProb; Weapons[(int)SchemeWeaponName.MailStrike].Probability = (sbyte)mailStrikeProb;
Weapons[(int)SchemeWeapon.SuperBananaBomb].Probability = (sbyte)RwKnockForce; Weapons[(int)SchemeWeaponName.SuperBananaBomb].Probability = (sbyte)RwKnockForce;
Weapons[(int)SchemeWeapon.MineStrike].Probability = (sbyte)RwMaxRopeSpeed; Weapons[(int)SchemeWeaponName.MineStrike].Probability = (sbyte)RwMaxRopeSpeed;
byte mbBombProb = ((byte)Weapons[(int)SchemeWeapon.MBBomb].Probability).SetBit(0, RwSelectWormAnytime); byte mbBombProb = ((byte)Weapons[(int)SchemeWeaponName.MBBomb].Probability).SetBit(0, RwSelectWormAnytime);
Weapons[(int)SchemeWeapon.MBBomb].Probability = (sbyte)mbBombProb; Weapons[(int)SchemeWeaponName.MBBomb].Probability = (sbyte)mbBombProb;
byte[] versionBytes = BitConverter.GetBytes(RwVersionOverride); byte[] versionBytes = BitConverter.GetBytes(RwVersionOverride);
Weapons[(int)SchemeWeapon.SelectWorm].Probability = (sbyte)versionBytes[0]; Weapons[(int)SchemeWeaponName.SelectWorm].Probability = (sbyte)versionBytes[0];
Weapons[(int)SchemeWeapon.Freeze].Probability = (sbyte)versionBytes[1]; Weapons[(int)SchemeWeaponName.Freeze].Probability = (sbyte)versionBytes[1];
Weapons[(int)SchemeWeapon.ConcreteDonkey].Probability = (sbyte)RwViscosity; Weapons[(int)SchemeWeaponName.ConcreteDonkey].Probability = (sbyte)RwViscosity;
Weapons[(int)SchemeWeapon.SuicideBomber].Probability = (sbyte)RwWindPower; Weapons[(int)SchemeWeaponName.SuicideBomber].Probability = (sbyte)RwWindPower;
Weapons[(int)SchemeWeapon.Armageddon].Probability = (sbyte)RwWormBouncyness; Weapons[(int)SchemeWeaponName.Armageddon].Probability = (sbyte)RwWormBouncyness;
} }
// ---- ENUMERATIONS ------------------------------------------------------------------------------------------- // ---- CLASSES, STRUCTS & ENUMS -------------------------------------------------------------------------------
[Flags] [Flags]
private enum RwEarthquakeProb : byte private enum RwEarthquakeProb : byte
{ {
None = 0, None,
AutoReaim = 1 << 0, AutoReaim = 1 << 0,
CircularAim = 1 << 1, CircularAim = 1 << 1,
AntiLockPower = 1 << 2, AntiLockPower = 1 << 2,
@ -1022,7 +1021,7 @@ namespace Syroot.Worms.Armageddon
[Flags] [Flags]
private enum RwMoleSquadronProb : byte private enum RwMoleSquadronProb : byte
{ {
None = 0, None,
ShotDoesntEndTurn = 1 << 0, ShotDoesntEndTurn = 1 << 0,
LoseControlDoesntEndTurn = 1 << 1, LoseControlDoesntEndTurn = 1 << 1,
FireDoesntPauseTimer = 1 << 2, FireDoesntPauseTimer = 1 << 2,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Syroot.Worms.Armageddon
{
/// <summary>
/// Represents the configuration of a weapon.
/// </summary>
[DebuggerDisplay("Ammo={Ammunition} Power={Power} Delay={Delay} Prob={Probability}")]
[StructLayout(LayoutKind.Sequential)]
public struct SchemeWeapon
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
/// <summary>Amount with which a team is equipped at game start. 10 and negative values represent infinity.</summary>
public sbyte Ammunition;
/// <summary>Power of this weapon.</summary>
public byte Power;
/// <summary>Number of turns required to be taken by each team before this weapon becomes available. Negative
/// values represent infinity.</summary>
public sbyte Delay;
/// <summary>Percentual chance of this weapon to appear in crates. Has no effect for super weapons.</summary>
public sbyte Probability;
}
}

View File

@ -1,35 +0,0 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Syroot.Worms.Armageddon
{
/// <summary>
/// Represents the configuration of a weapon.
/// </summary>
[DebuggerDisplay("Ammo={Ammunition} Power={Power} Delay={Delay} Prob={Probability}")]
[StructLayout(LayoutKind.Sequential)]
public struct SchemeWeaponSetting
{
/// <summary>
/// The amount of this weapon with which a team is equipped at game start. 10 and negative values represent
/// infinity.
/// </summary>
public sbyte Ammunition;
/// <summary>
/// The power of this weapon.
/// </summary>
public byte Power;
/// <summary>
/// The number of turns required to be taken by each team before this weapon becomes available. Negative values
/// represent infinity.
/// </summary>
public sbyte Delay;
/// <summary>
/// The percentual chance of this weapon to appear in crates. Has no effect for super weapons.
/// </summary>
public sbyte Probability;
}
}

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<AssemblyName>Syroot.Worms.Armageddon</AssemblyName> <AssemblyName>Syroot.Worms.Armageddon</AssemblyName>
<Description>.NET library for loading and modifying files of Team17's Worms Armageddon.</Description> <Description>.NET library for loading and modifying files of Team17's Worms Armageddon.</Description>
<PackageReleaseNotes>Simplify scheme object count usage. Fix saving files. Uniquely leave open Stream instances when saving into them.</PackageReleaseNotes> <PackageReleaseNotes>Simplify scheme fall damage and water rise usage.</PackageReleaseNotes>
<PackageTags>$(PackageTags);worms armageddon</PackageTags> <PackageTags>$(PackageTags);worms armageddon</PackageTags>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>