@working on decorate support

This commit is contained in:
codeimp 2009-01-22 15:10:41 +00:00
parent 76ec8a8c98
commit 54940886fd
19 changed files with 608 additions and 142 deletions

View file

@ -24,6 +24,9 @@ testparameters = "-iwad \"%WP\" -skill \"%S\" -file \"%AP\" \"%F\" +map %L %NM";
defaultsavecompiler = "zdbsp_normal";
defaulttestcompiler = "zdbsp_fast";
// Decorate actors to include depending on actor game property
decorategames = "doom";
// Skill levels
skills
{

View file

@ -24,6 +24,9 @@ testparameters = "-iwad \"%WP\" -skill \"%S\" -file \"%AP\" \"%F\" +map %L %NM";
defaultsavecompiler = "zdbsp_normal";
defaulttestcompiler = "zdbsp_fast";
// Decorate actors to include depending on actor game property
decorategames = "doom";
// Skill levels
skills
{

View file

@ -24,6 +24,9 @@ testparameters = "-iwad \"%WP\" -skill \"%S\" -file \"%AP\" \"%F\" +map %L %NM";
defaultsavecompiler = "zdbsp_normal";
defaulttestcompiler = "zdbsp_fast";
// Decorate actors to include depending on actor game property
decorategames = "doom";
// Skill levels
skills
{

View file

@ -24,6 +24,9 @@ testparameters = "-iwad \"%WP\" -skill \"%S\" -file \"%AP\" \"%F\" +map %L %NM";
defaultsavecompiler = "zdbsp_normal";
defaulttestcompiler = "zdbsp_fast";
// Decorate actors to include depending on actor game property
decorategames = "doom";
// Skill levels
skills
{

View file

@ -24,6 +24,9 @@ testparameters = "-iwad \"%WP\" -skill \"%S\" -file \"%AP\" \"%F\" +map %L %NM";
defaultsavecompiler = "zdbsp_normal";
defaulttestcompiler = "zdbsp_fast";
// Decorate actors to include depending on actor game property
decorategames = "doom";
// Skill levels
skills
{

View file

@ -24,6 +24,9 @@ testparameters = "-iwad \"%WP\" -skill \"%S\" -file \"%AP\" \"%F\" +map %L %NM";
defaultsavecompiler = "zdbsp_normal";
defaulttestcompiler = "zdbsp_fast";
// Decorate actors to include depending on actor game property
decorategames = "heretic raven";
// Skill levels
skills
{

View file

@ -24,6 +24,9 @@ testparameters = "-iwad \"%WP\" -skill \"%S\" -file \"%AP\" \"%F\" +map %L %NM";
defaultsavecompiler = "zdbsp_normal";
defaulttestcompiler = "zdbsp_fast";
// Decorate actors to include depending on actor game property
decorategames = "hexen raven";
// Skill levels
skills
{

View file

@ -24,6 +24,9 @@ testparameters = "-iwad \"%WP\" -skill \"%S\" -file \"%AP\" \"%F\" +map %L %NM";
defaultsavecompiler = "zdbsp_normal";
defaulttestcompiler = "zdbsp_fast";
// Decorate actors to include depending on actor game property
decorategames = "strife";
// Skill levels
skills
{

View file

@ -68,6 +68,7 @@ namespace CodeImp.DoomBuilder.Config
private int makedooraction;
private int[] makedoorargs;
private bool linetagindicatesectors;
private string decorategames;
// Skills
private List<SkillInfo> skills;
@ -143,6 +144,7 @@ namespace CodeImp.DoomBuilder.Config
public int MakeDoorAction { get { return makedooraction; } }
public int[] MakeDoorArgs { get { return makedoorargs; } }
public bool LineTagIndicatesSectors { get { return linetagindicatesectors ; } }
public string DecorateGames { get { return decorategames; } }
// Skills
public List<SkillInfo> Skills { get { return skills; } }
@ -238,6 +240,7 @@ namespace CodeImp.DoomBuilder.Config
makedoortrack = cfg.ReadSetting("makedoortrack", "-");
makedooraction = cfg.ReadSetting("makedooraction", 0);
linetagindicatesectors = cfg.ReadSetting("linetagindicatesectors", false);
decorategames = cfg.ReadSetting("decorategames", "");
for(int i = 0; i < Linedef.NUM_ARGS; i++) makedoorargs[i] = cfg.ReadSetting("makedoorarg" + i.ToString(CultureInfo.InvariantCulture), 0);
// Flags have special (invariant culture) conversion

View file

@ -84,7 +84,31 @@ namespace CodeImp.DoomBuilder.Config
#endregion
#region ================== Constructor / Disposer
// Constructor
internal ThingCategory(string name, string title)
{
// Initialize
this.name = name;
this.title = title;
this.things = new List<ThingTypeInfo>();
// Set default properties
this.sprite = "";
this.sorted = true;
this.color = 18;
this.arrow = 1;
this.width = 10;
this.height = 20;
this.hangs = 0;
this.blocking = 0;
this.errorcheck = 1;
this.fixedsize = false;
// We have no destructor
GC.SuppressFinalize(this);
}
// Constructor
internal ThingCategory(Configuration cfg, string name)
{

View file

@ -26,6 +26,7 @@ using CodeImp.DoomBuilder.Data;
using System.IO;
using System.Diagnostics;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Decorate;
#endregion
@ -170,10 +171,65 @@ namespace CodeImp.DoomBuilder.Config
GC.SuppressFinalize(this);
}
// Constructor
internal ThingTypeInfo(ThingCategory cat, ActorStructure actor)
{
// Initialize
this.index = actor.DoomEdNum;
this.category = cat;
this.title = "Unnamed";
// Read properties
this.sprite = cat.Sprite;
this.color = cat.Color;
this.arrow = (cat.Arrow != 0);
this.width = cat.Width;
this.height = cat.Height;
this.hangs = (cat.Hangs != 0);
this.blocking = cat.Blocking;
this.errorcheck = cat.ErrorCheck;
this.fixedsize = cat.FixedSize;
// Apply settings from actor
ModifyByDecorateActor(actor);
// We have no destructor
GC.SuppressFinalize(this);
}
#endregion
#region ================== Methods
// This updates the properties from a decorate actor
internal void ModifyByDecorateActor(ActorStructure actor)
{
// Set the title
if(actor.Tag != null)
title = actor.Tag;
else
title = actor.ClassName;
// Set sprite
sprite = actor.FindSuitableSprite();
if(this.sprite.Length <= 8)
this.spritelongname = Lump.MakeLongName(this.sprite);
else
this.spritelongname = long.MaxValue;
// Size
width = actor.Radius;
height = actor.Height;
// Safety
if(this.width < 8f) this.width = 8f;
// Options
hangs = actor.GetFlagValue("spawnceiling", false);
blocking = actor.GetFlagValue("solid", false) ? 2 : 0;
}
#endregion
}
}

View file

@ -915,6 +915,23 @@ namespace CodeImp.DoomBuilder.Data
// No such patch found
return null;
}
// This tests if a given sprite can be found
internal bool GetSpriteExists(string pname)
{
if(!string.IsNullOrEmpty(pname))
{
// Go for all opened containers
for(int i = containers.Count - 1; i >= 0; i--)
{
// This contain provides this patch?
if(containers[i].GetSpriteExists(pname)) return true;
}
}
// No such patch found
return false;
}
// This loads the internal sprites
private void LoadInternalSprites()
@ -1036,40 +1053,83 @@ namespace CodeImp.DoomBuilder.Data
DecorateParser parser;
int counter = 0;
// Create the parser
parser = new DecorateParser();
parser.OnInclude = LoadDecorateFromLocation;
// Go for all opened containers
foreach(DataReader dr in containers)
// Only load these when the game configuration supports the use of decorate
if(!string.IsNullOrEmpty(General.Map.Config.DecorateGames))
{
// Load Decorate info cumulatively (the last Decorate is added to the previous)
// I'm not sure if this is the right thing to do though.
currentreader = dr;
Stream decodata = dr.GetDecorateData("DECORATE");
if(decodata != null)
// Create the parser
parser = new DecorateParser();
parser.OnInclude = LoadDecorateFromLocation;
// Go for all opened containers
foreach(DataReader dr in containers)
{
// Parse the data
decodata.Seek(0, SeekOrigin.Begin);
parser.Parse(decodata, "DECORATE");
// Check for errors
if(parser.HasError)
// Load Decorate info cumulatively (the last Decorate is added to the previous)
// I'm not sure if this is the right thing to do though.
currentreader = dr;
Stream decodata = dr.GetDecorateData("DECORATE");
if(decodata != null)
{
General.WriteLogLine("ERROR: Unable to parse DECORATE data from location " + dr.Location.location + "!");
General.WriteLogLine("ERROR: " + parser.ErrorDescription + " on line " + parser.ErrorLine + " in '" + parser.ErrorSource + "'");
break;
// Parse the data
decodata.Seek(0, SeekOrigin.Begin);
parser.Parse(decodata, "DECORATE");
// Check for errors
if(parser.HasError)
{
General.WriteLogLine("ERROR: Unable to parse DECORATE data from location " + dr.Location.location + "!");
General.WriteLogLine("ERROR: " + parser.ErrorDescription + " on line " + parser.ErrorLine + " in '" + parser.ErrorSource + "'");
break;
}
}
}
currentreader = null;
if(!parser.HasError)
{
// Find the decorate category
ThingCategory cat = null;
foreach(ThingCategory c in thingcategories)
{
if(c.Name == "decorate")
{
cat = c;
break;
}
}
// Go for all actors in the decorate to make things or update things
foreach(ActorStructure actor in parser.Actors)
{
// Check if we want to add this actor
if(actor.DoomEdNum > 0)
{
// Check if we can find this thing in our existing collection
if(thingtypes.ContainsKey(actor.DoomEdNum))
{
// Update the thing
thingtypes[actor.DoomEdNum].ModifyByDecorateActor(actor);
}
else
{
// Make the decorate category if needed
if(cat == null)
{
cat = new ThingCategory("decorate", "Decorate");
thingcategories.Add(cat);
}
// Add new thing
ThingTypeInfo t = new ThingTypeInfo(cat, actor);
cat.AddThing(t);
thingtypes.Add(t.Index, t);
}
// Count
counter++;
}
}
}
}
currentreader = null;
if(!parser.HasError)
{
// Go for all actors in the decorate
// TODO
counter = parser.Actors.Count;
}
// Output info
@ -1079,7 +1139,7 @@ namespace CodeImp.DoomBuilder.Data
// This loads Decorate data from a specific file or lump name
private void LoadDecorateFromLocation(DecorateParser parser, string location)
{
General.WriteLogLine("Including DECORATE resource '" + location + "'...");
//General.WriteLogLine("Including DECORATE resource '" + location + "'...");
Stream decodata = currentreader.GetDecorateData(location);
if(decodata != null)
{

View file

@ -118,9 +118,12 @@ namespace CodeImp.DoomBuilder.Data
#endregion
#region ================== Sprites
// When implemented, this returns the sprite lump
public virtual Stream GetSpriteData(string pname) { return null; }
// When implemented, this checks if the given sprite lump exists
public virtual bool GetSpriteExists(string pname) { return false; }
#endregion

View file

@ -76,27 +76,37 @@ namespace CodeImp.DoomBuilder.Data
}
// This returns all files in a given directory
protected override string[] GetAllFiles(string path)
protected override string[] GetAllFiles(string path, bool subfolders)
{
if(Directory.Exists(path))
return Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly);
{
if(subfolders)
return Directory.GetFiles(path, "*", SearchOption.AllDirectories);
else
return Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly);
}
else
return new string[0];
}
// This returns all files in a given directory that match the given extension
protected override string[] GetFilesWithExt(string path, string extension)
protected override string[] GetFilesWithExt(string path, string extension, bool subfolders)
{
if(Directory.Exists(path))
return Directory.GetFiles(path, "*." + extension, SearchOption.TopDirectoryOnly);
{
if(subfolders)
return Directory.GetFiles(path, "*." + extension, SearchOption.AllDirectories);
else
return Directory.GetFiles(path, "*." + extension, SearchOption.TopDirectoryOnly);
}
else
return new string[0];
}
// This finds the first file that has the specific name, regardless of file extension
protected override string FindFirstFile(string path, string beginswith)
protected override string FindFirstFile(string path, string beginswith, bool subfolders)
{
string[] files = GetAllFiles(path);
string[] files = GetAllFiles(path, subfolders);
foreach(string f in files)
{
if(string.Compare(Path.GetFileNameWithoutExtension(f), beginswith, true) == 0)
@ -107,9 +117,9 @@ namespace CodeImp.DoomBuilder.Data
}
// This finds the first file that has the specific name
protected override string FindFirstFileWithExt(string path, string beginswith)
protected override string FindFirstFileWithExt(string path, string beginswith, bool subfolders)
{
string[] files = GetAllFiles(path);
string[] files = GetAllFiles(path, subfolders);
foreach(string f in files)
{
if(string.Compare(Path.GetFileName(f), beginswith, true) == 0)

View file

@ -119,60 +119,107 @@ namespace CodeImp.DoomBuilder.Data
}
// This returns all files in a given directory
protected override string[] GetAllFiles(string path)
protected override string[] GetAllFiles(string path, bool subfolders)
{
List<string> matches = new List<string>();
string lowpath = path.ToLowerInvariant();
foreach(string f in fileslist)
if(subfolders)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) &&
(Path.GetFileName(f).Length > 0))
matches.Add(f);
foreach(string f in fileslist)
{
if(Path.GetDirectoryName(f).StartsWith(lowpath, true, CultureInfo.InvariantCulture) &&
(Path.GetFileName(f).Length > 0))
matches.Add(f);
}
}
else
{
foreach(string f in fileslist)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) &&
(Path.GetFileName(f).Length > 0))
matches.Add(f);
}
}
return matches.ToArray();
}
// This returns all files in a given directory that match the given extension
protected override string[] GetFilesWithExt(string path, string extension)
protected override string[] GetFilesWithExt(string path, string extension, bool subfolders)
{
List<string> matches = new List<string>();
string lowpath = path.ToLowerInvariant();
string lowext = "." + extension.ToLowerInvariant();
foreach(string f in fileslist)
if(subfolders)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) && f.EndsWith(lowext))
matches.Add(f);
foreach(string f in fileslist)
{
if(Path.GetDirectoryName(f).StartsWith(lowpath, true, CultureInfo.InvariantCulture) && f.EndsWith(lowext))
matches.Add(f);
}
}
else
{
foreach(string f in fileslist)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) && f.EndsWith(lowext))
matches.Add(f);
}
}
return matches.ToArray();
}
// This finds the first file that has the specific name, regardless of file extension
protected override string FindFirstFile(string path, string beginswith)
protected override string FindFirstFile(string path, string beginswith, bool subfolders)
{
string lowpath = path.ToLowerInvariant();
string lowbegin = beginswith.ToLowerInvariant();
foreach(string f in fileslist)
if(subfolders)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) &&
(string.Compare(Path.GetFileNameWithoutExtension(f), lowbegin) == 0))
return f;
foreach(string f in fileslist)
{
if(Path.GetDirectoryName(f).StartsWith(lowpath, true, CultureInfo.InvariantCulture) &&
(string.Compare(Path.GetFileNameWithoutExtension(f), lowbegin) == 0))
return f;
}
}
else
{
foreach(string f in fileslist)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) &&
(string.Compare(Path.GetFileNameWithoutExtension(f), lowbegin) == 0))
return f;
}
}
return null;
}
// This finds the first file that has the specific name
protected override string FindFirstFileWithExt(string path, string beginswith)
protected override string FindFirstFileWithExt(string path, string beginswith, bool subfolders)
{
string lowpath = path.ToLowerInvariant();
string lowbegin = beginswith.ToLowerInvariant();
foreach(string f in fileslist)
if(subfolders)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) &&
(string.Compare(Path.GetFileName(f), lowbegin) == 0))
return f;
foreach(string f in fileslist)
{
if(Path.GetDirectoryName(f).StartsWith(lowpath, true, CultureInfo.InvariantCulture) &&
(string.Compare(Path.GetFileName(f), lowbegin) == 0))
return f;
}
}
else
{
foreach(string f in fileslist)
{
if((string.Compare(Path.GetDirectoryName(f), lowpath) == 0) &&
(string.Compare(Path.GetFileName(f), lowbegin) == 0))
return f;
}
}
return null;

View file

@ -87,7 +87,7 @@ namespace CodeImp.DoomBuilder.Data
this.spritespath = Path.Combine(rootpath, SPRITES_DIR);
// Load all WAD files in the root as WAD resources
string[] wadfiles = GetFilesWithExt(rootpath, "wad");
string[] wadfiles = GetFilesWithExt(rootpath, "wad", false);
wads = new List<WADReader>(wadfiles.Length);
foreach(string w in wadfiles)
{
@ -193,7 +193,7 @@ namespace CodeImp.DoomBuilder.Data
// Load TEXTURE1 lump file
List<ImageData> imgset = new List<ImageData>();
string texture1file = FindFirstFile(rootpath, "TEXTURE1");
string texture1file = FindFirstFile(rootpath, "TEXTURE1", false);
if((texture1file != null) && FileExists(texture1file))
{
MemoryStream filedata = LoadFile(texture1file);
@ -202,7 +202,7 @@ namespace CodeImp.DoomBuilder.Data
}
// Load TEXTURE2 lump file
string texture2file = FindFirstFile(rootpath, "TEXTURE2");
string texture2file = FindFirstFile(rootpath, "TEXTURE2", false);
if((texture2file != null) && FileExists(texture2file))
{
MemoryStream filedata = LoadFile(texture2file);
@ -234,7 +234,7 @@ namespace CodeImp.DoomBuilder.Data
}
// If none of the wads provides patch names, let's see if we can
string pnamesfile = FindFirstFile(rootpath, "PNAMES");
string pnamesfile = FindFirstFile(rootpath, "PNAMES", false);
if((pnamesfile != null) && FileExists(pnamesfile))
{
MemoryStream pnamesdata = LoadFile(pnamesfile);
@ -261,7 +261,7 @@ namespace CodeImp.DoomBuilder.Data
}
// Find in patches directory
string filename = FindFirstFile(patchespath, pname);
string filename = FindFirstFile(patchespath, pname, true);
if((filename != null) && FileExists(filename))
{
return LoadFile(filename);
@ -326,7 +326,7 @@ namespace CodeImp.DoomBuilder.Data
}
// Find in sprites directory
string filename = FindFirstFile(spritespath, pfilename);
string filename = FindFirstFile(spritespath, pfilename, true);
if((filename != null) && FileExists(filename))
{
return LoadFile(filename);
@ -335,6 +335,31 @@ namespace CodeImp.DoomBuilder.Data
// Nothing found
return null;
}
// This checks if the given sprite exists
public override bool GetSpriteExists(string pname)
{
string pfilename = pname.Replace('\\', '^');
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Find in any of the wad files
for(int i = wads.Count - 1; i >= 0; i--)
{
if(wads[i].GetSpriteExists(pname)) return true;
}
// Find in sprites directory
string filename = FindFirstFile(spritespath, pfilename, true);
if((filename != null) && FileExists(filename))
{
return true;
}
// Nothing found
return false;
}
#endregion
@ -357,7 +382,7 @@ namespace CodeImp.DoomBuilder.Data
string filename = Path.GetFileName(pname);
string pathname = Path.GetDirectoryName(pname);
string fullpath = Path.Combine(rootpath, pathname);
string foundfile = filename.IndexOf('.') > -1 ? FindFirstFileWithExt(fullpath, filename) : FindFirstFile(fullpath, filename);
string foundfile = filename.IndexOf('.') > -1 ? FindFirstFileWithExt(fullpath, filename, false) : FindFirstFile(fullpath, filename, false);
if((foundfile != null) && FileExists(foundfile))
{
return LoadFile(foundfile);
@ -377,9 +402,9 @@ namespace CodeImp.DoomBuilder.Data
List<ImageData> images = new List<ImageData>();
string[] files;
string name;
// Go for all files
files = GetAllFiles(path);
files = GetAllFiles(path, false);
foreach(string f in files)
{
// Make the texture name from filename without extension
@ -423,16 +448,16 @@ namespace CodeImp.DoomBuilder.Data
protected abstract bool FileExists(string filename);
// This must return all files in a given directory
protected abstract string[] GetAllFiles(string path);
protected abstract string[] GetAllFiles(string path, bool subfolders);
// This must return all files in a given directory that match the given extension
protected abstract string[] GetFilesWithExt(string path, string extension);
protected abstract string[] GetFilesWithExt(string path, string extension, bool subfolders);
// This must find the first file that has the specific name, regardless of file extension
protected abstract string FindFirstFile(string path, string beginswith);
protected abstract string FindFirstFile(string path, string beginswith, bool subfolders);
// This must find the first file that has the specific name
protected abstract string FindFirstFileWithExt(string path, string beginswith);
protected abstract string FindFirstFileWithExt(string path, string beginswith, bool subfolders);
// This must load an entire file in memory and returns the stream
// NOTE: Callers are responsible for disposing the stream!

View file

@ -421,10 +421,23 @@ namespace CodeImp.DoomBuilder.Data
lump = file.FindLump(pname);
if(lump != null) return lump.Stream; else return null;
}
// This checks if the given sprite exists
public override bool GetSpriteExists(string pname)
{
Lump lump;
// Error when suspended
if(issuspended) throw new Exception("Data reader is suspended");
// Find the lump
lump = file.FindLump(pname);
return (lump != null);
}
#endregion
#region ================== Sprite
#region ================== Things
// This finds and returns a sprite stream
public override Stream GetDecorateData(string pname)

View file

@ -35,6 +35,8 @@ namespace CodeImp.DoomBuilder.Decorate
{
#region ================== Constants
private readonly string[] SPRITE_POSTFIXES = new string[] {"2C8", "2D8", "2A8", "2B8", "1C1", "1D1", "1A1", "1B1", "A2", "A1", "A0", "2", "1", "0" };
#endregion
#region ================== Variables
@ -43,6 +45,7 @@ namespace CodeImp.DoomBuilder.Decorate
private string classname;
private string inheritclass;
private string replaceclass;
private List<string> games;
private int doomednum = -1;
// Flags
@ -55,19 +58,24 @@ namespace CodeImp.DoomBuilder.Decorate
// nothing that tells you if it is a value or another property)
private int radius;
private int height;
private string tag;
// States
private List<StateStructure> states;
private Dictionary<string, StateStructure> states;
#endregion
#region ================== Properties
public Dictionary<string, bool> Flags { get { return flags; } }
public string Name { get { return classname; } }
public string ClassName { get { return classname; } }
public string InheritsClass { get { return inheritclass; } }
public string ReplacesClass { get { return replaceclass; } }
public List<string> Games { get { return games; } }
public int DoomEdNum { get { return doomednum; } }
public int Radius { get { return radius; } }
public int Height { get { return height; } }
public int DoomEdNum { get { return doomednum; } }
public string Tag { get { return tag; } }
#endregion
@ -78,7 +86,11 @@ namespace CodeImp.DoomBuilder.Decorate
{
// Initialize
flags = new Dictionary<string, bool>();
states = new List<StateStructure>();
states = new Dictionary<string, StateStructure>();
inheritclass = "actor";
replaceclass = null;
games = new List<string>();
tag = null;
// First next token is the class name
parser.SkipWhitespace(true);
@ -88,7 +100,7 @@ namespace CodeImp.DoomBuilder.Decorate
parser.ReportError("Expected actor class name");
return;
}
// Parse tokens before entering the actor scope
while(parser.SkipWhitespace(true))
{
@ -106,6 +118,15 @@ namespace CodeImp.DoomBuilder.Decorate
parser.ReportError("Expected class name to inherit from");
return;
}
else
{
// Find the actor to inherit from
ActorStructure other = parser.GetArchivedActorByName(inheritclass);
if(other != null)
InheritFrom(other);
else
General.WriteLogLine("WARNING: Unable to find the DECORATE class '" + inheritclass + "' to inherit from, while parsing '" + classname + "'");
}
}
else if(token == "replaces")
{
@ -118,6 +139,10 @@ namespace CodeImp.DoomBuilder.Decorate
return;
}
}
else if(token == "native")
{
// Igore this token
}
else if(token == "{")
{
// Actor scope begins here,
@ -143,6 +168,7 @@ namespace CodeImp.DoomBuilder.Decorate
}
// Now parse the contents of actor structure
string previoustoken = "";
while(parser.SkipWhitespace(true))
{
string token = parser.ReadToken();
@ -157,7 +183,7 @@ namespace CodeImp.DoomBuilder.Decorate
{
// Add the flag with its value
flagname = flagname.ToLowerInvariant();
flags.Add(flagname, flagvalue);
flags[flagname] = flagvalue;
}
else
{
@ -165,45 +191,44 @@ namespace CodeImp.DoomBuilder.Decorate
return;
}
}
else if((token == "action") || (token == "native"))
{
// We don't need this, ignore up to the first next ;
while(parser.SkipWhitespace(true))
{
string t = parser.ReadToken();
if((t == ";") || (t == null)) break;
}
}
else if(token == "states")
{
// Now parse actor states until we reach the end of the states structure
while(parser.SkipWhitespace(true))
{
string statename = parser.ReadToken();
if(!string.IsNullOrEmpty(statename))
string statetoken = parser.ReadToken();
if(!string.IsNullOrEmpty(statetoken))
{
// Start of scope?
if(statename == "{")
if(statetoken == "{")
{
// This is fine
}
// End of scope?
else if(statename == "}")
else if(statetoken == "}")
{
// Done with the states,
// break out of this parse loop
break;
}
else
// State label?
else if(statetoken == ":")
{
// Next token must be a :
parser.SkipWhitespace(true);
string labeltoken = parser.ReadToken();
if(!string.IsNullOrEmpty(statename))
if(!string.IsNullOrEmpty(previoustoken))
{
if(labeltoken == ":")
{
// Parse actor state
StateStructure st = new StateStructure(parser, statename);
states.Add(st);
if(parser.HasError) return;
}
else
{
parser.ReportError("Expected state label colon");
return;
}
// Parse actor state
StateStructure st = new StateStructure(parser, previoustoken);
if(parser.HasError) return;
states[previoustoken.ToLowerInvariant()] = st;
}
else
{
@ -211,6 +236,11 @@ namespace CodeImp.DoomBuilder.Decorate
return;
}
}
else
{
// Keep token
previoustoken = statetoken;
}
}
else
{
@ -225,36 +255,59 @@ namespace CodeImp.DoomBuilder.Decorate
// break out of this parse loop
break;
}
else
// Known property with a single value?
else if((token == "radius") || (token == "height") || (token == "tag"))
{
// This must be a property
// Is this a known property?
if((token == "radius") || (token == "height"))
// Next token is the property value to set
parser.SkipWhitespace(true);
string value = parser.ReadToken();
if(!string.IsNullOrEmpty(value))
{
// Next token is the property value to set
parser.SkipWhitespace(true);
string value = parser.ReadToken();
if(!string.IsNullOrEmpty(value))
// Try parsing as integer value
int intvalue;
int.TryParse(value, out intvalue);
// Set the property
switch(token)
{
// Try parsing as integer value
int intvalue;
int.TryParse(value, out intvalue);
// Set the property
if(token == "radius")
radius = intvalue;
else if(token == "height")
height = intvalue;
}
else
{
// Can't find the property value!
parser.ReportError("Expected a value for property '" + token + "'");
return;
case "radius": radius = intvalue; break;
case "height": height = intvalue; break;
case "tag": tag = value; break;
}
}
else
{
// Can't find the property value!
parser.ReportError("Expected a value for property '" + token + "'");
return;
}
}
// Monster property?
else if(token == "monster")
{
// This sets certain flags we are interested in
flags["solid"] = true;
}
// Game property?
else if(token == "game")
{
// Include all tokens on the same line
games.Clear();
while(parser.SkipWhitespace(false))
{
string v = parser.ReadToken();
if(v == null)
{
parser.ReportError("Unexpected end of structure");
return;
}
if(v == "\n") break;
games.Add(v.ToLowerInvariant());
}
}
// Keep token
previoustoken = token;
}
}
@ -262,6 +315,94 @@ namespace CodeImp.DoomBuilder.Decorate
#region ================== Methods
// This is called to inherit properties from another actor
private void InheritFrom(ActorStructure baseactor)
{
this.flags = new Dictionary<string, bool>(baseactor.flags);
this.height = baseactor.height;
this.radius = baseactor.radius;
this.games = new List<string>(baseactor.games);
this.states = new Dictionary<string, StateStructure>(baseactor.states);
}
// This returns the status of a flag
public bool GetFlagValue(string flag, bool defaultvalue)
{
if(flags.ContainsKey(flag))
return flags[flag];
else
return defaultvalue;
}
// This checks if this actor is meant for the current decorate game support
public bool CheckActorSupported()
{
// Check if we want to include this actor
string includegames = General.Map.Config.DecorateGames.ToLowerInvariant();
bool includeactor = (games.Count == 0);
foreach(string g in games)
includeactor |= includegames.Contains(g);
return includeactor;
}
// This finds the best suitable sprite to use
public string FindSuitableSprite()
{
string sprite = "";
// Try the idle state
if(states.ContainsKey("idle"))
{
StateStructure s = states["idle"];
if(!string.IsNullOrEmpty(s.FirstSprite))
sprite = s.FirstSprite;
}
// Try the see state
if(string.IsNullOrEmpty(sprite) && states.ContainsKey("see"))
{
StateStructure s = states["see"];
if(!string.IsNullOrEmpty(s.FirstSprite))
sprite = s.FirstSprite;
}
// Try the inactive state
if(string.IsNullOrEmpty(sprite) && states.ContainsKey("inactive"))
{
StateStructure s = states["inactive"];
if(!string.IsNullOrEmpty(s.FirstSprite))
sprite = s.FirstSprite;
}
// Still no sprite found? then just pick the first we can find
if(string.IsNullOrEmpty(sprite))
{
foreach(StateStructure s in states.Values)
{
if(!string.IsNullOrEmpty(s.FirstSprite))
{
sprite = s.FirstSprite;
break;
}
}
}
if(!string.IsNullOrEmpty(sprite))
{
// The sprite name is not actually complete, we still have to append
// the direction characters to it. Find an existing sprite with direction.
foreach(string postfix in SPRITE_POSTFIXES)
{
if(General.Map.Data.GetSpriteExists(sprite + postfix))
return sprite + postfix;
}
}
// No sprite found
return "";
}
#endregion
}
}

View file

@ -42,17 +42,20 @@ namespace CodeImp.DoomBuilder.Decorate
#endregion
#region ================== Constants
// Parsing
private const string WHITESPACE = "\n \t\r";
private const string SPECIALTOKEN = ":{}+-\n";
private const string SPECIALTOKEN = ":{}+-\n;";
#endregion
#region ================== Variables
// Objects
private List<ActorStructure> actors;
// These are actors we want to keep
private Dictionary<string, ActorStructure> actors;
// These are all parsed actors, also those from other games
private Dictionary<string, ActorStructure> archivedactors;
// Input data stream
private Stream datastream;
@ -70,7 +73,7 @@ namespace CodeImp.DoomBuilder.Decorate
internal Stream DataStream { get { return datastream; } }
internal BinaryReader DataReader { get { return datareader; } }
public ICollection<ActorStructure> Actors { get { return actors; } }
public ICollection<ActorStructure> Actors { get { return actors.Values; } }
public int ErrorLine { get { return errorline; } }
public string ErrorDescription { get { return errordesc; } }
public string ErrorSource { get { return errorsource; } }
@ -84,14 +87,15 @@ namespace CodeImp.DoomBuilder.Decorate
public DecorateParser()
{
// Initialize
actors = new List<ActorStructure>();
actors = new Dictionary<string, ActorStructure>();
archivedactors = new Dictionary<string, ActorStructure>();
errordesc = null;
}
#endregion
#region ================== Methods
#region ================== Parsing
// This parses the given decorate stream
// Returns false on errors
public bool Parse(Stream stream, string sourcefilename)
@ -117,8 +121,26 @@ namespace CodeImp.DoomBuilder.Decorate
// Read actor structure
ActorStructure actor = new ActorStructure(this);
if(this.HasError) break;
General.WriteLogLine("Added actor '" + actor.Name + "' from '" + localsourcename + "'");
actors.Add(actor);
// Add the actor
archivedactors[actor.ClassName.ToLowerInvariant()] = actor;
if(actor.CheckActorSupported())
actors[actor.ClassName.ToLowerInvariant()] = actor;
// Replace an actor?
if(actor.ReplacesClass != null)
{
if(GetArchivedActorByName(actor.ReplacesClass) != null)
archivedactors[actor.ReplacesClass.ToLowerInvariant()] = actor;
else
General.WriteLogLine("WARNING: Unable to find the DECORATE class '" + actor.ReplacesClass + "' to replace, while parsing '" + actor.ClassName + "'");
if(actor.CheckActorSupported())
{
if(GetActorByName(actor.ReplacesClass) != null)
actors[actor.ReplacesClass.ToLowerInvariant()] = actor;
}
}
}
else if(objdeclaration == "#include")
{
@ -145,22 +167,34 @@ namespace CodeImp.DoomBuilder.Decorate
break;
}
}
else if((objdeclaration == "const") || (objdeclaration == "native"))
{
// We don't need this, ignore up to the first next ;
while(SkipWhitespace(true))
{
string t = ReadToken();
if((t == ";") || (t == null)) break;
}
}
else
{
// Unknown structure!
// Best we can do now is just find the first { and the follow the scopes until the matching } is found
// Best we can do now is just find the first { and then
// follow the scopes until the matching } is found
string token2;
do
{
SkipWhitespace(true);
if(!SkipWhitespace(true)) break;
token2 = ReadToken();
if(token2 == null) break;
}
while(token2 != "{");
int scopelevel = 1;
do
{
SkipWhitespace(true);
if(!SkipWhitespace(true)) break;
token2 = ReadToken();
if(token2 == null) break;
if(token2 == "{") scopelevel++;
if(token2 == "}") scopelevel--;
}
@ -325,5 +359,31 @@ namespace CodeImp.DoomBuilder.Decorate
}
#endregion
#region ================== Methods
// This returns an actor by name
// Returns null when actor cannot be found
public ActorStructure GetActorByName(string name)
{
name = name.ToLowerInvariant();
if(actors.ContainsKey(name))
return actors[name];
else
return null;
}
// This returns an actor by name
// Returns null when actor cannot be found
internal ActorStructure GetArchivedActorByName(string name)
{
name = name.ToLowerInvariant();
if(archivedactors.ContainsKey(name))
return archivedactors[name];
else
return null;
}
#endregion
}
}