#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.
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections.Generic;
#endregion
namespace CodeImp.DoomBuilder.Dehacked
{
public enum ThingAngled
{
UNCHANGED,
YES,
NO
}
public class DehackedThing
{
#region ================== Variables
private int number;
private string name;
private Dictionary props;
private int doomednum;
private int initialframe;
private string sprite;
private int height;
private int width;
private List bits;
private string category;
private int color;
private bool bright;
private ThingAngled angled;
#endregion
#region ================== Properties
public int Number { get { return number; } }
public Dictionary 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 Bits { get { return bits; } }
public string Category { get { return category; } }
public int Color { get { return color; } }
public bool Bright { get { return bright; } }
public ThingAngled Angled { get { return angled; } }
#endregion
#region ================== Constructor
internal DehackedThing(int number, string name)
{
this.number = number;
this.name = name;
color = -1;
sprite = null;
angled = ThingAngled.UNCHANGED;
props = new Dictionary();
bits = new List();
}
internal DehackedThing(int number, string name, Dictionary props) : this(number, name)
{
foreach(string key in props.Keys)
{
this.props[key.ToLowerInvariant()] = props[key];
}
}
#endregion
///
/// Processes the thing, setting it up according to the properties defined in the Dehacked patch.
///
/// Dehacked frames the thing could use
/// Bit mnemonics
/// The base thing to copy properties from
/// All sprites available in the resources
internal void Process(Dictionary frames, Dictionary bitmnemonics, DehackedThing basething, HashSet 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 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":
if (sprite == null && int.TryParse(value, out initialframe))
{
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;
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;
}
}
}
}
}