2021-09-05 12:59:31 +00:00
|
|
|
|
#region ================== Copyright (c) 2021 Boris Iwanski
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
*
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
*
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
*
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
|
|
|
|
*
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program.If not, see<http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Namespaces
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
namespace CodeImp.DoomBuilder.Dehacked
|
|
|
|
|
{
|
2021-09-11 20:11:07 +00:00
|
|
|
|
public enum ThingAngled
|
|
|
|
|
{
|
|
|
|
|
UNCHANGED,
|
|
|
|
|
YES,
|
|
|
|
|
NO
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-05 12:59:31 +00:00
|
|
|
|
public class DehackedThing
|
|
|
|
|
{
|
|
|
|
|
#region ================== Variables
|
|
|
|
|
|
|
|
|
|
private int number;
|
|
|
|
|
private string name;
|
|
|
|
|
private Dictionary<string, string> props;
|
|
|
|
|
private int doomednum;
|
|
|
|
|
private int initialframe;
|
|
|
|
|
private string sprite;
|
|
|
|
|
private int height;
|
|
|
|
|
private int width;
|
|
|
|
|
private List<string> bits;
|
|
|
|
|
private string category;
|
|
|
|
|
private int color;
|
|
|
|
|
private bool bright;
|
2021-09-11 20:11:07 +00:00
|
|
|
|
private ThingAngled angled;
|
2021-09-05 12:59:31 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Properties
|
|
|
|
|
|
|
|
|
|
public int Number { get { return number; } }
|
|
|
|
|
public Dictionary<string, string> Props { get { return props; } }
|
|
|
|
|
public int DoomEdNum { get { return doomednum; } internal set { doomednum = value; } }
|
|
|
|
|
public string Name { get { return name; } internal set { name = value; } }
|
|
|
|
|
public int InitialFrame { get { return initialframe; } internal set { initialframe = value; } }
|
|
|
|
|
public string Sprite { get { return sprite; } internal set { sprite = value; } }
|
|
|
|
|
public int Height { get { return height; } internal set { height = value; } }
|
|
|
|
|
public int Width { get { return width; } internal set { width = value; } }
|
|
|
|
|
public List<string> Bits { get { return bits; } }
|
|
|
|
|
public string Category { get { return category; } }
|
|
|
|
|
public int Color { get { return color; } }
|
|
|
|
|
public bool Bright { get { return bright; } }
|
2021-09-11 20:11:07 +00:00
|
|
|
|
public ThingAngled Angled { get { return angled; } }
|
2021-09-05 12:59:31 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Constructor
|
|
|
|
|
|
|
|
|
|
internal DehackedThing(int number, string name)
|
|
|
|
|
{
|
|
|
|
|
this.number = number;
|
|
|
|
|
this.name = name;
|
|
|
|
|
color = -1;
|
2021-09-11 20:11:07 +00:00
|
|
|
|
sprite = null;
|
|
|
|
|
angled = ThingAngled.UNCHANGED;
|
2021-09-05 12:59:31 +00:00
|
|
|
|
|
|
|
|
|
props = new Dictionary<string, string>();
|
|
|
|
|
bits = new List<string>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal DehackedThing(int number, string name, Dictionary<string, string> props) : this(number, name)
|
|
|
|
|
{
|
|
|
|
|
foreach(string key in props.Keys)
|
|
|
|
|
{
|
|
|
|
|
this.props[key.ToLowerInvariant()] = props[key];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Processes the thing, setting it up according to the properties defined in the Dehacked patch.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="frames">Dehacked frames the thing could use</param>
|
|
|
|
|
/// <param name="bitmnemonics">Bit mnemonics</param>
|
|
|
|
|
/// <param name="basething">The base thing to copy properties from</param>
|
|
|
|
|
/// <param name="availablesprites">All sprites available in the resources</param>
|
|
|
|
|
internal void Process(Dictionary<int, DehackedFrame> frames, Dictionary<long, string> bitmnemonics, DehackedThing basething, HashSet<string> availablesprites)
|
|
|
|
|
{
|
|
|
|
|
// Copy all missing properties from the base thing
|
|
|
|
|
if(basething != null)
|
|
|
|
|
{
|
|
|
|
|
doomednum = basething.DoomEdNum;
|
|
|
|
|
foreach (string key in basething.Props.Keys)
|
|
|
|
|
if (!props.ContainsKey(key))
|
|
|
|
|
props[key] = basething.props[key];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (KeyValuePair<string, string> kvp in props)
|
|
|
|
|
{
|
|
|
|
|
string prop = kvp.Key.ToLowerInvariant();
|
|
|
|
|
string value = kvp.Value;
|
|
|
|
|
|
|
|
|
|
switch (prop)
|
|
|
|
|
{
|
|
|
|
|
case "id #":
|
|
|
|
|
int.TryParse(value, out doomednum);
|
|
|
|
|
break;
|
|
|
|
|
case "initial frame":
|
2021-09-11 20:11:07 +00:00
|
|
|
|
if (sprite == null && int.TryParse(value, out initialframe))
|
2021-09-05 12:59:31 +00:00
|
|
|
|
{
|
|
|
|
|
if (frames.ContainsKey(initialframe))
|
|
|
|
|
{
|
|
|
|
|
// It doesn't seem to matter which rotation we select, UDB will automagically
|
|
|
|
|
// find the correct sprites later. We just try to find a sprite that's available
|
|
|
|
|
// in the loaded resources, either xxxxA0 (i.e. without rotations) or xxxxA1 (i.e. with rotations)
|
|
|
|
|
if (!string.IsNullOrEmpty(frames[initialframe].Sprite))
|
|
|
|
|
{
|
|
|
|
|
string spritename = frames[initialframe].Sprite + Convert.ToChar(frames[initialframe].SpriteSubNumber + 'A');
|
|
|
|
|
if (availablesprites.Contains(spritename + "0"))
|
|
|
|
|
sprite = spritename + "0";
|
|
|
|
|
else
|
|
|
|
|
sprite = spritename + "1";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
sprite = null;
|
|
|
|
|
|
|
|
|
|
bright = frames[initialframe].Bright;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
General.ErrorLogger.Add(ErrorType.Error, "Dehacked thing " + number + " is referencing initial frame " + initialframe + " that is not defined.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "width":
|
|
|
|
|
if(int.TryParse(value, out width))
|
|
|
|
|
{
|
|
|
|
|
// Value is in 16.16 fixed point, so shift it
|
|
|
|
|
width >>= 16;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "height":
|
|
|
|
|
if(int.TryParse(value, out height))
|
|
|
|
|
{
|
|
|
|
|
// Value is in 16.16 fixed point, so shift it
|
|
|
|
|
height >>= 16;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "bits":
|
|
|
|
|
long allbits;
|
|
|
|
|
// Try to parse the value as an number, if that works it's an old-school bit set and not mnemonics
|
|
|
|
|
if(long.TryParse(value, out allbits))
|
|
|
|
|
{
|
|
|
|
|
// Go through all given mnemonics and translate the bits to them
|
|
|
|
|
foreach (long mask in bitmnemonics.Keys)
|
|
|
|
|
if ((mask & allbits) == mask)
|
|
|
|
|
bits.Add(bitmnemonics[mask]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// The bits are mnemonics, so split them and turn them into a list
|
|
|
|
|
foreach (string mnemonic in value.Split('+'))
|
|
|
|
|
bits.Add(mnemonic.Trim().ToLowerInvariant());
|
|
|
|
|
}
|
|
|
|
|
break;
|
2021-09-11 20:11:07 +00:00
|
|
|
|
case "$editor category":
|
|
|
|
|
category = value;
|
|
|
|
|
break;
|
|
|
|
|
case "$editor color id":
|
|
|
|
|
if (!int.TryParse(value, out color))
|
|
|
|
|
color = 18; // Default light brown
|
|
|
|
|
break;
|
|
|
|
|
case "$editor sprite":
|
|
|
|
|
sprite = value;
|
|
|
|
|
break;
|
|
|
|
|
case "$editor angled":
|
|
|
|
|
if (value.ToLowerInvariant() == "true")
|
|
|
|
|
angled = ThingAngled.YES;
|
|
|
|
|
else
|
|
|
|
|
angled = ThingAngled.NO;
|
|
|
|
|
break;
|
2021-09-05 12:59:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|