Rewrite logic of parsing Scheme object count and type.

Mirrors frontend logic more closely now without the need for static lookup arrays.
This commit is contained in:
Ray Koopa 2020-06-26 19:00:55 +02:00
parent 57815e9072
commit 26a9125f0a
3 changed files with 2448 additions and 2756 deletions

View File

@ -19,6 +19,10 @@ namespace Syroot.Worms.Armageddon
private const string _signature = "SCHM"; private const string _signature = "SCHM";
private const int _objectCount5Step = 30;
private const int _objectCount10Step = 44;
private const int _objectCountLastStep = 59;
// Lookup tables. // Lookup tables.
private static readonly Dictionary<byte, byte> _mapWaterRiseToRaw = new Dictionary<byte, byte>() private static readonly Dictionary<byte, byte> _mapWaterRiseToRaw = new Dictionary<byte, byte>()
{ {
@ -67,12 +71,10 @@ namespace Syroot.Worms.Armageddon
[245] = 7, [245] = 7,
[253] = 59 [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 ------------------------------------------------------------------------------------------------ // ---- MEMBERS ------------------------------------------------------------------------------------------------
private byte _objectCount;
private byte _mineDelay; private byte _mineDelay;
private byte _turnTime; private byte _turnTime;
private byte _roundTimeMinutes; private byte _roundTimeMinutes;
@ -214,9 +216,24 @@ 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. /// Gets or sets the maximum number of objects (mines or oil drums) on the map. Note that setting this value
/// rounds it valid count.
/// </summary> /// </summary>
public SchemeObjectCount ObjectCount { get; set; } 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;
}
}
/// <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.
@ -685,7 +702,7 @@ namespace Syroot.Worms.Armageddon
/// <param name="format">The <see cref="SchemeSaveFormat"/> to respect when storing the settings.</param> /// <param name="format">The <see cref="SchemeSaveFormat"/> to respect when storing the settings.</param>
public void Save(Stream stream, SchemeSaveFormat format) public void Save(Stream stream, SchemeSaveFormat format)
{ {
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII); using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
// Write the header. // Write the header.
writer.Write(_signature, StringCoding.Raw); writer.Write(_signature, StringCoding.Raw);
@ -759,45 +776,25 @@ namespace Syroot.Worms.Armageddon
private void LoadObjectTypesAndCount(BinaryStream reader) private void LoadObjectTypesAndCount(BinaryStream reader)
{ {
// Invalid values default to 8 mines.
ObjectTypes = SchemeObjectType.Mines;
ObjectCount = SchemeObjectCount.Count8;
byte raw = reader.Read1Byte(); byte raw = reader.Read1Byte();
if (raw < 12) switch (raw)
{ {
// WA before 3.6.28.0 and WWP only store object type. case 3: case 4: raw = 1; break;
switch (raw) case 6: case 7: raw = 0; break;
{
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];
} }
// 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) private void LoadMineDelayConfig(BinaryStream reader)
@ -874,17 +871,12 @@ namespace Syroot.Worms.Armageddon
// 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)SchemeWeapon.MailStrike].Probability;
if (mailStrikeProb.GetBit(7)) if (!mailStrikeProb.GetBit(7))
{
if (mailStrikeProb.GetBit(6))
RwGravityPropBlackHole = mailStrikeProb.DecodeSByte(6);
else
RwGravityConstBlackHole = mailStrikeProb.DecodeSByte(6);
}
else
{
RwGravity = mailStrikeProb.DecodeSByte(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; RwKnockForce = (byte)Weapons[(int)SchemeWeapon.SuperBananaBomb].Probability;
RwMaxRopeSpeed = (byte)Weapons[(int)SchemeWeapon.MineStrike].Probability; RwMaxRopeSpeed = (byte)Weapons[(int)SchemeWeapon.MineStrike].Probability;
@ -908,41 +900,23 @@ namespace Syroot.Worms.Armageddon
private void SaveObjectTypesAndCount(BinaryStream writer, SchemeSaveFormat format) private void SaveObjectTypesAndCount(BinaryStream writer, SchemeSaveFormat format)
{ {
byte raw = 0; byte raw;
if (format == SchemeSaveFormat.ExtendedWithObjectCount) if (format == SchemeSaveFormat.ExtendedWithObjectCount && ObjectCount != 8)
{ {
// WA since 3.6.28.0 encodes object type and count in one byte. // WA since 3.6.28.0 encodes object type and count in one byte.
switch (ObjectTypes) raw = (byte)ObjectTypes;
{
case SchemeObjectType.Mines: int step = ObjectCount;
raw = 0x01; if (ObjectCount >= 100)
break; step = _objectCount10Step + (ObjectCount - 100) / 10;
case SchemeObjectType.OilDrums: else if (ObjectCount >= 30)
raw = 0x02; step = _objectCount5Step + (ObjectCount - 30) / 5;
break; raw |= (byte)(4 * step + 8);
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 else
{ {
// WA before 3.6.28.0 and WWP only store object type. // WA before 3.6.28.0 and WWP only store object type.
switch (ObjectTypes) raw = ObjectTypes == SchemeObjectType.Both ? (byte)5 : (byte)ObjectTypes;
{
case SchemeObjectType.Mines:
raw = 0x01;
break;
case SchemeObjectType.OilDrums:
raw = 0x02;
break;
case SchemeObjectType.Mines | SchemeObjectType.OilDrums:
raw = 0x05;
break;
}
} }
writer.Write(raw); writer.Write(raw);
} }

View File

@ -985,315 +985,10 @@ namespace Syroot.Worms.Armageddon
[Flags] [Flags]
public enum SchemeObjectType public enum SchemeObjectType
{ {
None = 0, None,
Mines = 1 << 0, Mines = 1 << 0,
OilDrums = 1 << 1 OilDrums = 1 << 1,
} Both = Mines | OilDrums
/// <summary>
/// Represents the possible maximum number of objects which can appear on the map.
/// </summary>
public enum SchemeObjectCount
{
/// <summary>
/// No objects.
/// </summary>
None = 0,
/// <summary>
/// Up to 1 object.
/// </summary>
Count1 = 1,
/// <summary>
/// Up to 2 objects.
/// </summary>
Count2 = 2,
/// <summary>
/// Up to 3 objects.
/// </summary>
Count3 = 3,
/// <summary>
/// Up to 4 objects.
/// </summary>
Count4 = 4,
/// <summary>
/// Up to 5 objects.
/// </summary>
Count5 = 5,
/// <summary>
/// Up to 6 objects.
/// </summary>
Count6 = 6,
/// <summary>
/// Up to 7 objects.
/// </summary>
Count7 = 7,
/// <summary>
/// Up to 8 objects.
/// </summary>
Count8 = 8,
/// <summary>
/// Up to 9 objects.
/// </summary>
Count9 = 9,
/// <summary>
/// Up to 10 objects.
/// </summary>
Count10 = 10,
/// <summary>
/// Up to 11 objects.
/// </summary>
Count11 = 11,
/// <summary>
/// Up to 12 objects.
/// </summary>
Count12 = 12,
/// <summary>
/// Up to 13 objects.
/// </summary>
Count13 = 13,
/// <summary>
/// Up to 14 objects.
/// </summary>
Count14 = 14,
/// <summary>
/// Up to 15 objects.
/// </summary>
Count15 = 15,
/// <summary>
/// Up to 16 objects.
/// </summary>
Count16 = 16,
/// <summary>
/// Up to 17 objects.
/// </summary>
Count17 = 17,
/// <summary>
/// Up to 18 objects.
/// </summary>
Count18 = 18,
/// <summary>
/// Up to 19 objects.
/// </summary>
Count19 = 19,
/// <summary>
/// Up to 20 objects.
/// </summary>
Count20 = 20,
/// <summary>
/// Up to 21 objects.
/// </summary>
Count21 = 21,
/// <summary>
/// Up to 22 objects.
/// </summary>
Count22 = 22,
/// <summary>
/// Up to 23 objects.
/// </summary>
Count23 = 23,
/// <summary>
/// Up to 24 objects.
/// </summary>
Count24 = 24,
/// <summary>
/// Up to 25 objects.
/// </summary>
Count25 = 25,
/// <summary>
/// Up to 26 objects.
/// </summary>
Count26 = 26,
/// <summary>
/// Up to 27 objects.
/// </summary>
Count27 = 27,
/// <summary>
/// Up to 28 objects.
/// </summary>
Count28 = 28,
/// <summary>
/// Up to 29 objects.
/// </summary>
Count29 = 29,
/// <summary>
/// Up to 30 objects.
/// </summary>
Count30 = 30,
/// <summary>
/// Up to 35 objects.
/// </summary>
Count35 = 35,
/// <summary>
/// Up to 40 objects.
/// </summary>
Count40 = 40,
/// <summary>
/// Up to 45 objects.
/// </summary>
Count45 = 45,
/// <summary>
/// Up to 50 objects.
/// </summary>
Count50 = 50,
/// <summary>
/// Up to 55 objects.
/// </summary>
Count55 = 55,
/// <summary>
/// Up to 60 objects.
/// </summary>
Count60 = 60,
/// <summary>
/// Up to 65 objects.
/// </summary>
Count65 = 65,
/// <summary>
/// Up to 70 objects.
/// </summary>
Count70 = 70,
/// <summary>
/// Up to 75 objects.
/// </summary>
Count75 = 75,
/// <summary>
/// Up to 80 objects.
/// </summary>
Count80 = 80,
/// <summary>
/// Up to 85 objects.
/// </summary>
Count85 = 85,
/// <summary>
/// Up to 90 objects.
/// </summary>
Count90 = 90,
/// <summary>
/// Up to 95 objects.
/// </summary>
Count95 = 95,
/// <summary>
/// Up to 100 objects.
/// </summary>
Count100 = 100,
/// <summary>
/// Up to 110 objects.
/// </summary>
Count110 = 110,
/// <summary>
/// Up to 120 objects.
/// </summary>
Count120 = 120,
/// <summary>
/// Up to 130 objects.
/// </summary>
Count130 = 130,
/// <summary>
/// Up to 140 objects.
/// </summary>
Count140 = 140,
/// <summary>
/// Up to 150 objects.
/// </summary>
Count150 = 150,
/// <summary>
/// Up to 160 objects.
/// </summary>
Count160 = 160,
/// <summary>
/// Up to 170 objects.
/// </summary>
Count170 = 170,
/// <summary>
/// Up to 180 objects.
/// </summary>
Count180 = 180,
/// <summary>
/// Up to 190 objects.
/// </summary>
Count190 = 190,
/// <summary>
/// Up to 200 objects.
/// </summary>
Count200 = 200,
/// <summary>
/// Up to 210 objects.
/// </summary>
Count210 = 210,
/// <summary>
/// Up to 220 objects.
/// </summary>
Count220 = 220,
/// <summary>
/// Up to 230 objects.
/// </summary>
Count230 = 230,
/// <summary>
/// Up to 240 objects.
/// </summary>
Count240 = 240,
/// <summary>
/// Up to 250 objects.
/// </summary>
Count250 = 250
} }
/// <summary> /// <summary>

View File

@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using Syroot.Worms.Armageddon;
using Syroot.Worms.Mgame; using Syroot.Worms.Mgame;
namespace Syroot.Worms.Scratchpad namespace Syroot.Worms.Scratchpad
@ -9,8 +11,30 @@ namespace Syroot.Worms.Scratchpad
{ {
// ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
private static void Main(string[] args) private static void Main()
{ {
IEnumerable<string> 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() private static void ConvertIgdImages()
@ -21,7 +45,6 @@ namespace Syroot.Worms.Scratchpad
foreach (string igdFilePath in Directory.GetFiles(igdFolder, "*.igd", SearchOption.AllDirectories)) foreach (string igdFilePath in Directory.GetFiles(igdFolder, "*.igd", SearchOption.AllDirectories))
{ {
// Load the IGD and let it convert the images. // Load the IGD and let it convert the images.
Igd igd = new Igd(igdFilePath); Igd igd = new Igd(igdFilePath);