Added support for Worms 2 Option schemes.

This commit is contained in:
Ray Koopa 2017-04-21 17:59:04 +02:00
parent a70ac954c5
commit e629788c7c
9 changed files with 397 additions and 29 deletions

View File

@ -26,15 +26,15 @@ Formats of the second generation 2D games are mostly focused right now.
| Mission | WAM | WA, WWP | No | No |
| Monochrome Map | LEV | W2 | No | No |
| Monochrome Map | LEV, BIT | WA, WWP | No | No |
| Option Scheme | OPT | W2 | No | No |
| Palette | PAL | W2, WA, WWP | Yes | Yes |
| Project X Library | PXL | WA+PX | No | No |
| Project X Scheme | PXS | WA+PX | No | No |
| Replay | WAGAME | WA | No | No |
| Scheme | WSC | WA, WWP | Yes | Yes |
| Scheme Options | OPT | W2 | Yes | Yes |
| Scheme Weapons | WEP | W2 | No | No |
| Team Container | ST1 | W2 | No | No |
| Team Container | WGT | WA, WWP | No | No |
| Weapon Scheme | WEP | W2 | No | No |
## Third Generation (3D)
Third generation file formats used by the 3D games have not yet been looked into.

View File

@ -43,8 +43,7 @@ namespace Syroot.Worms.Core
using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII))
{
// Read the file header.
string signature = reader.ReadString(4);
if (signature != _signature)
if (reader.ReadString(_signature.Length) != _signature)
{
throw new InvalidDataException("Invalid RIFF file signature.");
}

View File

@ -61,8 +61,7 @@ namespace Syroot.Worms.Gen2
using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII))
{
// Read the header.
int signature = reader.ReadInt32();
if (signature != _signature)
if (reader.ReadInt32() != _signature)
{
throw new InvalidDataException("Invalid DIR file signature.");
}

View File

@ -1,26 +1,22 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Syroot.IO;
using Syroot.Maths;
using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.Armageddon
{
/// <summary>
/// Represents a high level view on a scheme stored in a WSC file which contains game settings and armory
/// configuration.
/// 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 int _signature = 0x4D484353; // "SCHM"
private const string _signature = "SCHM";
// Lookup tables.
private static readonly Dictionary<byte, byte> _mapWaterRiseToRaw = new Dictionary<byte, byte>() { [0] = 0,
@ -606,8 +602,7 @@ namespace Syroot.Worms.Gen2.Armageddon
using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII))
{
// Read the header.
int signature = reader.ReadInt32();
if (signature != _signature)
if (reader.ReadString(_signature.Length) != _signature)
{
throw new InvalidDataException("Invalid WSC file signature.");
}
@ -700,7 +695,7 @@ namespace Syroot.Worms.Gen2.Armageddon
using (BinaryDataWriter writer = new BinaryDataWriter(stream, Encoding.ASCII))
{
// Write the header.
writer.Write(_signature);
writer.Write(_signature, BinaryStringFormat.NoPrefixOrTermination);
writer.Write((byte)Version);
// Write the options.

View File

@ -711,8 +711,19 @@ namespace Syroot.Worms.Gen2.Armageddon
/// </summary>
public enum SchemeWormSelect : byte
{
Ordered = 0,
/// <summary>
/// Worms are selected in the order in which they appear in the team.
/// </summary>
Sequential = 0,
/// <summary>
/// Worms are selected by the player.
/// </summary>
Manual = 1,
/// <summary>
/// Worms are selected randomly.
/// </summary>
Random = 2
}
@ -721,8 +732,19 @@ namespace Syroot.Worms.Gen2.Armageddon
/// </summary>
public enum SchemeSuddenDeathEvent : byte
{
/// <summary>
/// The round ends and is drawn.
/// </summary>
RoundEnd = 0,
/// <summary>
/// A nuklear test is triggered.
/// </summary>
NuclearStrike = 1,
/// <summary>
/// The worms health is reduced to 1.
/// </summary>
HealthDrop = 2
}

View File

@ -81,8 +81,7 @@ namespace Syroot.Worms.Gen2
using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII))
{
// Read the header.
int signature = reader.ReadInt32();
if (signature != _signature)
if (reader.ReadInt32() != _signature)
{
throw new InvalidDataException("Invalid IMG file signature.");
}

View File

@ -1,10 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Syroot.Worms.Gen2.Worms2
{
class OptionsScheme
{
}
}

View File

@ -0,0 +1,364 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Syroot.IO;
using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.Worms2
{
/// <summary>
/// Represents scheme options stored in an OPT file which contains game settings.
/// S. https://worms2d.info/Options_file.
/// </summary>
public class SchemeOptions : ILoadableFile, ISaveableFile
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const string _signature = "OPTFILE";
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="SchemeOptions"/> 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 SchemeOptions(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 SchemeOptions(string fileName)
{
Load(fileName);
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the round time before sudden death is triggered in minutes.
/// </summary>
public int RoundTime { get; set; }
/// <summary>
/// Gets or sets the turn time in seconds available for the player to move.
/// </summary>
public int TurnTime { 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 int 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 int RetreatTimeRope { get; set; }
/// <summary>
/// Gets or sets the maximum number of objects (mines or oil drums) on the map.
/// </summary>
public int ObjectCount { get; set; }
/// <summary>
/// Gets or sets the number of seconds a mine requires to explode. -1 is random between 0-3 seconds.
/// </summary>
public int MineDelay { 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 the influence power of the wind affecting weapons in percent.
/// </summary>
public int WindPower { get; set; }
/// <summary>
/// Gets or sets the friction deaccelerating objects touching solid ground between 0-5. 0 is default, 1 is low
/// friction, 5 is high friction.
/// </summary>
public int Friction { get; set; }
/// <summary>
/// Gets or sets the number of kills which have to be done to replay the turn.
/// </summary>
public int ReplayRequiredKills { get; set; }
/// <summary>
/// Gets or sets the number of damage in health points which has to be done to replay the turn.
/// </summary>
public int ReplayRequiredDamage { 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 maximum fall damage applied in health points.
/// </summary>
public int FallDamage { get; set; }
/// <summary>
/// Gets or sets the number of rope swings allowed with one Ninja Rope.
/// </summary>
public int RopeSwings { 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 the amount in pixels which the water will rise between turns after Sudden Death was triggered.
/// </summary>
public int WaterRiseRate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the worms health is reduced to 1 when Sudden Death is triggered.
/// </summary>
public bool SuddenDeathHealthDrop { get; set; }
/// <summary>
/// Gets or sets a value indicating whether an indestructible border will be placed around the map.
/// </summary>
public bool IndestructibleBorder { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the radius in which girders can be placed around the active worm is
/// no longer unlimited.
/// </summary>
public bool RestrictGirders { 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 whether the chat box will be closed upon starting to move a worm or stays
/// open.
/// </summary>
public bool ExtendedChatControls { get; set; }
/// <summary>
/// Gets or sets the delay in seconds between each team's turn to allow relaxed switching of seats.
/// </summary>
public int HotSeatDelay { get; set; }
/// <summary>
/// Gets or sets a value indicating whether weapons collected in previous round will be carried over to the next
/// round.
/// </summary>
public bool EnableStockpiling { get; set; }
/// <summary>
/// Gets or sets the percentual probability of a weapon or health crate to drop between turns.
/// </summary>
public int CrateProbability { get; set; }
/// <summary>
/// Gets or sets the percentual probability of a crate dropping closer to weak teams.
/// </summary>
public int CrateIntelligence { get; set; }
/// <summary>
/// Gets or sets the amount of health included in a health crate added to the collecting worm's energy.
/// </summary>
public int HealthCrateEnergy { get; set; }
/// <summary>
/// Gets or sets a value indicating whether crates can explode upon trying to collect them.
/// </summary>
public bool BoobyTraps { 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 or sets the initial worm energy at round start.
/// </summary>
public int WormEnergy { 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 whether worm selection is disabled after sudden death.
/// </summary>
public bool SuddenDeathDisableWormSelect { get; set; }
/// <summary>
/// Gets or sets a value indicating whether oil drums will be placed instead of mines.
/// </summary>
public bool UseOilDrums { 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 (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII))
{
// Read the header.
if (reader.ReadString(_signature.Length) != _signature)
{
throw new InvalidDataException("Invalid OPT file signature.");
}
// Read the options.
RoundTime = reader.ReadInt32();
TurnTime = reader.ReadInt32();
RetreatTime = reader.ReadInt32();
RetreatTimeRope = reader.ReadInt32();
ObjectCount = reader.ReadInt32();
MineDelay = reader.ReadInt32();
DudMines = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
WindPower = reader.ReadInt32();
Friction = reader.ReadInt32();
ReplayRequiredKills = reader.ReadInt32();
ReplayRequiredDamage = reader.ReadInt32();
AutomaticReplays = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
FallDamage = reader.ReadInt32();
RopeSwings = reader.ReadInt32();
ShowRoundTime = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
WaterRiseRate = reader.ReadInt32();
SuddenDeathHealthDrop = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
IndestructibleBorder = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
RestrictGirders = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
WormSelectMode = reader.ReadEnum<SchemeWormSelect>(true);
ExtendedChatControls = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
HotSeatDelay = reader.ReadInt32();
EnableStockpiling = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
CrateProbability = reader.ReadInt32();
CrateIntelligence = reader.ReadInt32();
HealthCrateEnergy = reader.ReadInt32();
BoobyTraps = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
EnableSuperWeapons = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
WormEnergy = reader.ReadInt32();
ArtilleryMode = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
SuddenDeathDisableWormSelect = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
// The following option does not exist in all schemes.
if (!reader.EndOfStream)
{
UseOilDrums = reader.ReadBoolean(BinaryBooleanFormat.NonZeroDword);
}
}
}
/// <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"/> with the specified <paramref name="format"/>.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
public void Save(Stream stream)
{
using (BinaryDataWriter writer = new BinaryDataWriter(stream, Encoding.ASCII))
{
// Write the header.
writer.Write(_signature, BinaryStringFormat.NoPrefixOrTermination);
// Write the options.
writer.Write(RoundTime);
writer.Write(TurnTime);
writer.Write(RetreatTime);
writer.Write(RetreatTimeRope);
writer.Write(ObjectCount);
writer.Write(MineDelay);
writer.Write(DudMines, BinaryBooleanFormat.NonZeroDword);
writer.Write(WindPower);
writer.Write(Friction);
writer.Write(ReplayRequiredKills);
writer.Write(ReplayRequiredDamage);
writer.Write(AutomaticReplays, BinaryBooleanFormat.NonZeroDword);
writer.Write(FallDamage);
writer.Write(RopeSwings);
writer.Write(ShowRoundTime, BinaryBooleanFormat.NonZeroDword);
writer.Write(WaterRiseRate);
writer.Write(SuddenDeathHealthDrop, BinaryBooleanFormat.NonZeroDword);
writer.Write(IndestructibleBorder, BinaryBooleanFormat.NonZeroDword);
writer.Write(RestrictGirders, BinaryBooleanFormat.NonZeroDword);
writer.Write(WormSelectMode, true);
writer.Write(ExtendedChatControls, BinaryBooleanFormat.NonZeroDword);
writer.Write(HotSeatDelay);
writer.Write(EnableStockpiling, BinaryBooleanFormat.NonZeroDword);
writer.Write(CrateProbability);
writer.Write(CrateIntelligence);
writer.Write(HealthCrateEnergy);
writer.Write(BoobyTraps, BinaryBooleanFormat.NonZeroDword);
writer.Write(EnableSuperWeapons, BinaryBooleanFormat.NonZeroDword);
writer.Write(WormEnergy);
writer.Write(ArtilleryMode, BinaryBooleanFormat.NonZeroDword);
writer.Write(SuddenDeathDisableWormSelect, BinaryBooleanFormat.NonZeroDword);
writer.Write(UseOilDrums, BinaryBooleanFormat.NonZeroDword);
}
}
/// <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)
{
using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
Save(stream);
}
}
}
/// <summary>
/// Represents the method to determine the next turn's worm.
/// </summary>
public enum SchemeWormSelect : int
{
/// <summary>
/// Worms are selected in the order in which they appear in the team.
/// </summary>
Sequential = 0,
/// <summary>
/// Worms are selected randomly.
/// </summary>
Random = 1,
/// <summary>
/// Worms are selected by a computed rating system.
/// </summary>
Intelligent = 2,
/// <summary>
/// Worms are selected by the player.
/// </summary>
Manual = 3
}
}