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 | | Mission | WAM | WA, WWP | No | No |
| Monochrome Map | LEV | W2 | No | No | | Monochrome Map | LEV | W2 | No | No |
| Monochrome Map | LEV, BIT | WA, WWP | No | No | | Monochrome Map | LEV, BIT | WA, WWP | No | No |
| Option Scheme | OPT | W2 | No | No |
| Palette | PAL | W2, WA, WWP | Yes | Yes | | Palette | PAL | W2, WA, WWP | Yes | Yes |
| Project X Library | PXL | WA+PX | No | No | | Project X Library | PXL | WA+PX | No | No |
| Project X Scheme | PXS | WA+PX | No | No | | Project X Scheme | PXS | WA+PX | No | No |
| Replay | WAGAME | WA | No | No | | Replay | WAGAME | WA | No | No |
| Scheme | WSC | WA, WWP | Yes | Yes | | 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 | ST1 | W2 | No | No |
| Team Container | WGT | WA, WWP | No | No | | Team Container | WGT | WA, WWP | No | No |
| Weapon Scheme | WEP | W2 | No | No |
## Third Generation (3D) ## Third Generation (3D)
Third generation file formats used by the 3D games have not yet been looked into. 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)) using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII))
{ {
// Read the file header. // Read the file header.
string signature = reader.ReadString(4); if (reader.ReadString(_signature.Length) != _signature)
if (signature != _signature)
{ {
throw new InvalidDataException("Invalid RIFF file 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)) using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII))
{ {
// Read the header. // Read the header.
int signature = reader.ReadInt32(); if (reader.ReadInt32() != _signature)
if (signature != _signature)
{ {
throw new InvalidDataException("Invalid DIR file signature."); throw new InvalidDataException("Invalid DIR file signature.");
} }

View File

@ -1,26 +1,22 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Syroot.IO; using Syroot.IO;
using Syroot.Maths;
using Syroot.Worms.Core; using Syroot.Worms.Core;
namespace Syroot.Worms.Gen2.Armageddon namespace Syroot.Worms.Gen2.Armageddon
{ {
/// <summary> /// <summary>
/// Represents a high level view on a scheme stored in a WSC file which contains game settings and armory /// Represents a scheme stored in a WSC file which contains game settings and armory configuration, including
/// configuration. /// RubberWorm settings encoded in those.
/// Used by WA and WWP. S. https://worms2d.info/Game_scheme_file. /// Used by WA and WWP. S. https://worms2d.info/Game_scheme_file.
/// </summary> /// </summary>
public class Scheme : ILoadableFile, ISaveableFile public class Scheme : ILoadableFile, ISaveableFile
{ {
// ---- CONSTANTS ---------------------------------------------------------------------------------------------- // ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const int _signature = 0x4D484353; // "SCHM" private const string _signature = "SCHM";
// Lookup tables. // Lookup tables.
private static readonly Dictionary<byte, byte> _mapWaterRiseToRaw = new Dictionary<byte, byte>() { [0] = 0, 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)) using (BinaryDataReader reader = new BinaryDataReader(stream, Encoding.ASCII))
{ {
// Read the header. // Read the header.
int signature = reader.ReadInt32(); if (reader.ReadString(_signature.Length) != _signature)
if (signature != _signature)
{ {
throw new InvalidDataException("Invalid WSC file 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)) using (BinaryDataWriter writer = new BinaryDataWriter(stream, Encoding.ASCII))
{ {
// Write the header. // Write the header.
writer.Write(_signature); writer.Write(_signature, BinaryStringFormat.NoPrefixOrTermination);
writer.Write((byte)Version); writer.Write((byte)Version);
// Write the options. // Write the options.

View File

@ -711,8 +711,19 @@ namespace Syroot.Worms.Gen2.Armageddon
/// </summary> /// </summary>
public enum SchemeWormSelect : byte 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, Manual = 1,
/// <summary>
/// Worms are selected randomly.
/// </summary>
Random = 2 Random = 2
} }
@ -721,8 +732,19 @@ namespace Syroot.Worms.Gen2.Armageddon
/// </summary> /// </summary>
public enum SchemeSuddenDeathEvent : byte public enum SchemeSuddenDeathEvent : byte
{ {
/// <summary>
/// The round ends and is drawn.
/// </summary>
RoundEnd = 0, RoundEnd = 0,
/// <summary>
/// A nuklear test is triggered.
/// </summary>
NuclearStrike = 1, NuclearStrike = 1,
/// <summary>
/// The worms health is reduced to 1.
/// </summary>
HealthDrop = 2 HealthDrop = 2
} }

View File

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