mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-11-23 12:22:19 +00:00
53bb8dbf01
git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/trunk@106 8a3a26a2-13c4-0310-b231-cf6edde360e5
911 lines
27 KiB
C++
911 lines
27 KiB
C++
/*
|
|
Copyright (C) 2001-2006, William Joseph.
|
|
All Rights Reserved.
|
|
|
|
This file is part of GtkRadiant.
|
|
|
|
GtkRadiant 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 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "eclass_doom3.h"
|
|
|
|
#include "debugging/debugging.h"
|
|
|
|
#include <map>
|
|
|
|
#include "ifilesystem.h"
|
|
#include "iscriplib.h"
|
|
#include "iarchive.h"
|
|
#include "qerplugin.h"
|
|
|
|
#include "generic/callback.h"
|
|
#include "string/string.h"
|
|
#include "eclasslib.h"
|
|
#include "os/path.h"
|
|
#include "os/dir.h"
|
|
#include "stream/stringstream.h"
|
|
#include "moduleobservers.h"
|
|
#include "stringio.h"
|
|
|
|
class RawString
|
|
{
|
|
const char* m_value;
|
|
public:
|
|
RawString(const char* value) : m_value(value)
|
|
{
|
|
}
|
|
const char* c_str() const
|
|
{
|
|
return m_value;
|
|
}
|
|
};
|
|
|
|
inline bool operator<(const RawString& self, const RawString& other)
|
|
{
|
|
return string_less_nocase(self.c_str(), other.c_str());
|
|
}
|
|
|
|
typedef std::map<RawString, EntityClass*> EntityClasses;
|
|
EntityClasses g_EntityClassDoom3_classes;
|
|
EntityClass *g_EntityClassDoom3_bad = 0;
|
|
|
|
|
|
void EntityClassDoom3_clear()
|
|
{
|
|
for(EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i)
|
|
{
|
|
(*i).second->free((*i).second);
|
|
}
|
|
g_EntityClassDoom3_classes.clear();
|
|
}
|
|
|
|
// entityClass will be inserted only if another of the same name does not already exist.
|
|
// if entityClass was inserted, the same object is returned, otherwise the already-existing object is returned.
|
|
EntityClass* EntityClassDoom3_insertUnique(EntityClass* entityClass)
|
|
{
|
|
return (*g_EntityClassDoom3_classes.insert(EntityClasses::value_type(entityClass->name(), entityClass)).first).second;
|
|
}
|
|
|
|
void EntityClassDoom3_forEach(EntityClassVisitor& visitor)
|
|
{
|
|
for(EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i)
|
|
{
|
|
visitor.visit((*i).second);
|
|
}
|
|
}
|
|
|
|
inline void printParseError(const char* message)
|
|
{
|
|
globalErrorStream() << message;
|
|
}
|
|
|
|
#define PARSE_RETURN_FALSE_IF_FAIL(expression) if(!(expression)) { printParseError(FILE_LINE "\nparse failed: " #expression "\n"); return false; } else
|
|
|
|
bool EntityClassDoom3_parseToken(Tokeniser& tokeniser)
|
|
{
|
|
const char* token = tokeniser.getToken();
|
|
PARSE_RETURN_FALSE_IF_FAIL(token != 0);
|
|
return true;
|
|
}
|
|
|
|
bool EntityClassDoom3_parseToken(Tokeniser& tokeniser, const char* string)
|
|
{
|
|
const char* token = tokeniser.getToken();
|
|
PARSE_RETURN_FALSE_IF_FAIL(token != 0);
|
|
return string_equal(token, string);
|
|
}
|
|
|
|
bool EntityClassDoom3_parseString(Tokeniser& tokeniser, const char*& s)
|
|
{
|
|
const char* token = tokeniser.getToken();
|
|
PARSE_RETURN_FALSE_IF_FAIL(token != 0);
|
|
s = token;
|
|
return true;
|
|
}
|
|
|
|
bool EntityClassDoom3_parseString(Tokeniser& tokeniser, CopiedString& s)
|
|
{
|
|
const char* token = tokeniser.getToken();
|
|
PARSE_RETURN_FALSE_IF_FAIL(token != 0);
|
|
s = token;
|
|
return true;
|
|
}
|
|
|
|
bool EntityClassDoom3_parseString(Tokeniser& tokeniser, StringOutputStream& s)
|
|
{
|
|
const char* token = tokeniser.getToken();
|
|
PARSE_RETURN_FALSE_IF_FAIL(token != 0);
|
|
s << token;
|
|
return true;
|
|
}
|
|
|
|
bool EntityClassDoom3_parseUnknown(Tokeniser& tokeniser)
|
|
{
|
|
//const char* name =
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
|
|
//globalOutputStream() << "parsing unknown block " << makeQuoted(name) << "\n";
|
|
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "{"));
|
|
tokeniser.nextLine();
|
|
|
|
std::size_t depth = 1;
|
|
for(;;)
|
|
{
|
|
const char* token;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
|
|
if(string_equal(token, "}"))
|
|
{
|
|
if(--depth == 0)
|
|
{
|
|
tokeniser.nextLine();
|
|
break;
|
|
}
|
|
}
|
|
else if(string_equal(token, "{"))
|
|
{
|
|
++depth;
|
|
}
|
|
tokeniser.nextLine();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
class Model
|
|
{
|
|
public:
|
|
bool m_resolved;
|
|
CopiedString m_mesh;
|
|
CopiedString m_skin;
|
|
CopiedString m_parent;
|
|
typedef std::map<CopiedString, CopiedString> Anims;
|
|
Anims m_anims;
|
|
Model() : m_resolved(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
typedef std::map<CopiedString, Model> Models;
|
|
|
|
Models g_models;
|
|
|
|
void Model_resolveInheritance(const char* name, Model& model)
|
|
{
|
|
if(model.m_resolved == false)
|
|
{
|
|
model.m_resolved = true;
|
|
|
|
if(!string_empty(model.m_parent.c_str()))
|
|
{
|
|
Models::iterator i = g_models.find(model.m_parent);
|
|
if(i == g_models.end())
|
|
{
|
|
globalErrorStream() << "model " << name << " inherits unknown model " << model.m_parent.c_str() << "\n";
|
|
}
|
|
else
|
|
{
|
|
Model_resolveInheritance((*i).first.c_str(), (*i).second);
|
|
model.m_mesh = (*i).second.m_mesh;
|
|
model.m_skin = (*i).second.m_skin;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool EntityClassDoom3_parseModel(Tokeniser& tokeniser)
|
|
{
|
|
const char* name;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, name));
|
|
|
|
Model& model = g_models[name];
|
|
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "{"));
|
|
tokeniser.nextLine();
|
|
|
|
for(;;)
|
|
{
|
|
const char* parameter;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, parameter));
|
|
if(string_equal(parameter, "}"))
|
|
{
|
|
tokeniser.nextLine();
|
|
break;
|
|
}
|
|
else if(string_equal(parameter, "inherit"))
|
|
{
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, model.m_parent));
|
|
tokeniser.nextLine();
|
|
}
|
|
else if(string_equal(parameter, "remove"))
|
|
{
|
|
//const char* remove =
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
tokeniser.nextLine();
|
|
}
|
|
else if(string_equal(parameter, "mesh"))
|
|
{
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, model.m_mesh));
|
|
tokeniser.nextLine();
|
|
}
|
|
else if(string_equal(parameter, "skin"))
|
|
{
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, model.m_skin));
|
|
tokeniser.nextLine();
|
|
}
|
|
else if(string_equal(parameter, "offset"))
|
|
{
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "("));
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, ")"));
|
|
tokeniser.nextLine();
|
|
}
|
|
else if(string_equal(parameter, "channel"))
|
|
{
|
|
//const char* channelName =
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "("));
|
|
for(;;)
|
|
{
|
|
const char* end;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, end));
|
|
if(string_equal(end, ")"))
|
|
{
|
|
tokeniser.nextLine();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if(string_equal(parameter, "anim"))
|
|
{
|
|
CopiedString animName;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, animName));
|
|
const char* animFile;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, animFile));
|
|
model.m_anims.insert(Model::Anims::value_type(animName, animFile));
|
|
|
|
const char* token;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
|
|
|
|
while(string_equal(token, ","))
|
|
{
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, animFile));
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
|
|
}
|
|
|
|
if(string_equal(token, "{"))
|
|
{
|
|
for(;;)
|
|
{
|
|
const char* end;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, end));
|
|
if(string_equal(end, "}"))
|
|
{
|
|
tokeniser.nextLine();
|
|
break;
|
|
}
|
|
tokeniser.nextLine();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tokeniser.ungetToken();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
globalErrorStream() << "unknown model parameter: " << makeQuoted(parameter) << "\n";
|
|
return false;
|
|
}
|
|
tokeniser.nextLine();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline bool char_isSpaceOrTab(char c)
|
|
{
|
|
return c == ' ' || c == '\t';
|
|
}
|
|
|
|
inline bool char_isNotSpaceOrTab(char c)
|
|
{
|
|
return !char_isSpaceOrTab(c);
|
|
}
|
|
|
|
template<typename Predicate>
|
|
inline const char* string_find_if(const char* string, Predicate predicate)
|
|
{
|
|
for(; *string != 0; ++string)
|
|
{
|
|
if(predicate(*string))
|
|
{
|
|
return string;
|
|
}
|
|
}
|
|
return string;
|
|
}
|
|
|
|
inline const char* string_findFirstSpaceOrTab(const char* string)
|
|
{
|
|
return string_find_if(string, char_isSpaceOrTab);
|
|
}
|
|
|
|
inline const char* string_findFirstNonSpaceOrTab(const char* string)
|
|
{
|
|
return string_find_if(string, char_isNotSpaceOrTab);
|
|
}
|
|
|
|
|
|
static bool EntityClass_parse(EntityClass& entityClass, Tokeniser& tokeniser)
|
|
{
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, entityClass.m_name));
|
|
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser, "{"));
|
|
tokeniser.nextLine();
|
|
|
|
StringOutputStream usage(256);
|
|
StringOutputStream description(256);
|
|
CopiedString* currentDescription = 0;
|
|
StringOutputStream* currentString = 0;
|
|
|
|
for(;;)
|
|
{
|
|
const char* key;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, key));
|
|
|
|
const char* last = string_findFirstSpaceOrTab(key);
|
|
CopiedString first(StringRange(key, last));
|
|
|
|
if(!string_empty(last))
|
|
{
|
|
last = string_findFirstNonSpaceOrTab(last);
|
|
}
|
|
|
|
if(currentString != 0 && string_equal(key, "\\"))
|
|
{
|
|
tokeniser.nextLine();
|
|
*currentString << " ";
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, *currentString));
|
|
continue;
|
|
}
|
|
|
|
if(currentDescription != 0)
|
|
{
|
|
*currentDescription = description.c_str();
|
|
description.clear();
|
|
currentDescription = 0;
|
|
}
|
|
currentString = 0;
|
|
|
|
if(string_equal(key, "}"))
|
|
{
|
|
tokeniser.nextLine();
|
|
break;
|
|
}
|
|
else if(string_equal(key, "model"))
|
|
{
|
|
const char* token;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
|
|
entityClass.fixedsize = true;
|
|
StringOutputStream buffer(256);
|
|
buffer << PathCleaned(token);
|
|
entityClass.m_modelpath = buffer.c_str();
|
|
}
|
|
else if(string_equal(key, "editor_color"))
|
|
{
|
|
const char* value;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
|
|
if(!string_empty(value))
|
|
{
|
|
entityClass.colorSpecified = true;
|
|
bool success = string_parse_vector3(value, entityClass.color);
|
|
ASSERT_MESSAGE(success, "editor_color: parse error");
|
|
}
|
|
}
|
|
else if(string_equal(key, "editor_ragdoll"))
|
|
{
|
|
//bool ragdoll = atoi(tokeniser.getToken()) != 0;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
}
|
|
else if(string_equal(key, "editor_mins"))
|
|
{
|
|
entityClass.sizeSpecified = true;
|
|
const char* value;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
|
|
if(!string_empty(value) && !string_equal(value, "?"))
|
|
{
|
|
entityClass.fixedsize = true;
|
|
bool success = string_parse_vector3(value, entityClass.mins);
|
|
ASSERT_MESSAGE(success, "editor_mins: parse error");
|
|
}
|
|
}
|
|
else if(string_equal(key, "editor_maxs"))
|
|
{
|
|
entityClass.sizeSpecified = true;
|
|
const char* value;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
|
|
if(!string_empty(value) && !string_equal(value, "?"))
|
|
{
|
|
entityClass.fixedsize = true;
|
|
bool success = string_parse_vector3(value, entityClass.maxs);
|
|
ASSERT_MESSAGE(success, "editor_maxs: parse error");
|
|
}
|
|
}
|
|
else if(string_equal(key, "editor_usage"))
|
|
{
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, usage));
|
|
currentString = &usage;
|
|
}
|
|
else if(string_equal_n(key, "editor_usage", 12))
|
|
{
|
|
usage << "\n";
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, usage));
|
|
currentString = &usage;
|
|
}
|
|
else if(string_equal(key, "editor_rotatable")
|
|
|| string_equal(key, "editor_showangle")
|
|
|| string_equal(key, "editor_mover")
|
|
|| string_equal(key, "editor_model")
|
|
|| string_equal(key, "editor_material")
|
|
|| string_equal(key, "editor_combatnode")
|
|
|| (!string_empty(last) && string_equal(first.c_str(), "editor_gui"))
|
|
|| string_equal_n(key, "editor_copy", 11))
|
|
{
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
}
|
|
else if(!string_empty(last) && (string_equal(first.c_str(), "editor_var") || string_equal(first.c_str(), "editor_string")))
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
|
|
attribute.m_type = "string";
|
|
currentDescription = &attribute.m_description;
|
|
currentString = &description;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
|
|
}
|
|
else if(!string_empty(last) && string_equal(first.c_str(), "editor_float"))
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
|
|
attribute.m_type = "string";
|
|
currentDescription = &attribute.m_description;
|
|
currentString = &description;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
|
|
}
|
|
else if(!string_empty(last) && string_equal(first.c_str(), "editor_snd"))
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
|
|
attribute.m_type = "sound";
|
|
currentDescription = &attribute.m_description;
|
|
currentString = &description;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
|
|
}
|
|
else if(!string_empty(last) && string_equal(first.c_str(), "editor_bool"))
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
|
|
attribute.m_type = "boolean";
|
|
currentDescription = &attribute.m_description;
|
|
currentString = &description;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
|
|
}
|
|
else if(!string_empty(last) && string_equal(first.c_str(), "editor_int"))
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
|
|
attribute.m_type = "integer";
|
|
currentDescription = &attribute.m_description;
|
|
currentString = &description;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
|
|
}
|
|
else if(!string_empty(last) && string_equal(first.c_str(), "editor_model"))
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
|
|
attribute.m_type = "model";
|
|
currentDescription = &attribute.m_description;
|
|
currentString = &description;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
|
|
}
|
|
else if(!string_empty(last) && string_equal(first.c_str(), "editor_color"))
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
|
|
attribute.m_type = "color";
|
|
currentDescription = &attribute.m_description;
|
|
currentString = &description;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
|
|
}
|
|
else if(!string_empty(last) && (string_equal(first.c_str(), "editor_material") || string_equal(first.c_str(), "editor_mat")))
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, last).second;
|
|
attribute.m_type = "shader";
|
|
currentDescription = &attribute.m_description;
|
|
currentString = &description;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, description));
|
|
}
|
|
else if(string_equal(key, "inherit"))
|
|
{
|
|
entityClass.inheritanceResolved = false;
|
|
ASSERT_MESSAGE(entityClass.m_parent.empty(), "only one 'inherit' supported per entityDef");
|
|
const char* token;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, token));
|
|
entityClass.m_parent.push_back(token);
|
|
}
|
|
// begin quake4-specific keys
|
|
else if(string_equal(key, "editor_targetonsel"))
|
|
{
|
|
//const char* value =
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
}
|
|
else if(string_equal(key, "editor_menu"))
|
|
{
|
|
//const char* value =
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
}
|
|
else if(string_equal(key, "editor_ignore"))
|
|
{
|
|
//const char* value =
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseToken(tokeniser));
|
|
}
|
|
// end quake4-specific keys
|
|
else
|
|
{
|
|
CopiedString tmp(key);
|
|
ASSERT_MESSAGE(!string_equal_n(key, "editor_", 7), "unsupported editor key: " << makeQuoted(key));
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, key).second;
|
|
attribute.m_type = "string";
|
|
const char* value;
|
|
PARSE_RETURN_FALSE_IF_FAIL(EntityClassDoom3_parseString(tokeniser, value));
|
|
if(string_equal(value, "}")) // hack for quake4 powerups.def bug
|
|
{
|
|
globalErrorStream() << "entityDef " << makeQuoted(entityClass.m_name.c_str()) << " key " << makeQuoted(tmp.c_str()) << " has no value\n";
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
attribute.m_value = value;
|
|
}
|
|
}
|
|
tokeniser.nextLine();
|
|
}
|
|
|
|
entityClass.m_comments = usage.c_str();
|
|
|
|
if(string_equal(entityClass.m_name.c_str(), "light"))
|
|
{
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "light_radius").second;
|
|
attribute.m_type = "vector3";
|
|
attribute.m_value = "300 300 300";
|
|
}
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "light_center").second;
|
|
attribute.m_type = "vector3";
|
|
}
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "noshadows").second;
|
|
attribute.m_type = "boolean";
|
|
attribute.m_value = "0";
|
|
}
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "nospecular").second;
|
|
attribute.m_type = "boolean";
|
|
attribute.m_value = "0";
|
|
}
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "nodiffuse").second;
|
|
attribute.m_type = "boolean";
|
|
attribute.m_value = "0";
|
|
}
|
|
{
|
|
EntityClassAttribute& attribute = EntityClass_insertAttribute(entityClass, "falloff").second;
|
|
attribute.m_type = "real";
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EntityClassDoom3_parseEntityDef(Tokeniser& tokeniser)
|
|
{
|
|
EntityClass* entityClass = Eclass_Alloc();
|
|
entityClass->free = &Eclass_Free;
|
|
|
|
if(!EntityClass_parse(*entityClass, tokeniser))
|
|
{
|
|
eclass_capture_state(entityClass); // finish constructing the entity so that it can be destroyed cleanly.
|
|
entityClass->free(entityClass);
|
|
return false;
|
|
}
|
|
|
|
EntityClass* inserted = EntityClassDoom3_insertUnique(entityClass);
|
|
if(inserted != entityClass)
|
|
{
|
|
globalErrorStream() << "entityDef " << entityClass->name() << " is already defined, second definition ignored\n";
|
|
eclass_capture_state(entityClass); // finish constructing the entity so that it can be destroyed cleanly.
|
|
entityClass->free(entityClass);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool EntityClassDoom3_parseBlock(Tokeniser& tokeniser, const char* blockType)
|
|
{
|
|
if(string_equal(blockType, "entityDef"))
|
|
{
|
|
return EntityClassDoom3_parseEntityDef(tokeniser);
|
|
}
|
|
else if(string_equal(blockType, "model"))
|
|
{
|
|
return EntityClassDoom3_parseModel(tokeniser);
|
|
}
|
|
else
|
|
{
|
|
return EntityClassDoom3_parseUnknown(tokeniser);
|
|
}
|
|
}
|
|
|
|
bool EntityClassDoom3_parse(TextInputStream& inputStream, const char* filename)
|
|
{
|
|
Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(inputStream);
|
|
|
|
tokeniser.nextLine();
|
|
|
|
for(;;)
|
|
{
|
|
const char* blockType = tokeniser.getToken();
|
|
if(blockType == 0)
|
|
{
|
|
return true;
|
|
}
|
|
CopiedString tmp(blockType);
|
|
if(!EntityClassDoom3_parseBlock(tokeniser, tmp.c_str()))
|
|
{
|
|
globalErrorStream() << GlobalFileSystem().findFile(filename) << filename << ":" << (unsigned int)tokeniser.getLine() << ": " << tmp.c_str() << " parse failed, skipping rest of file\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
tokeniser.release();
|
|
}
|
|
|
|
|
|
void EntityClassDoom3_loadFile(const char* filename)
|
|
{
|
|
globalOutputStream() << "parsing entity classes from " << makeQuoted(filename) << "\n";
|
|
|
|
StringOutputStream fullname(256);
|
|
fullname << "def/" << filename;
|
|
|
|
ArchiveTextFile* file = GlobalFileSystem().openTextFile(fullname.c_str());
|
|
if(file != 0)
|
|
{
|
|
EntityClassDoom3_parse(file->getInputStream(), fullname.c_str());
|
|
file->release();
|
|
}
|
|
}
|
|
|
|
EntityClass* EntityClassDoom3_findOrInsert(const char *name, bool has_brushes)
|
|
{
|
|
ASSERT_NOTNULL(name);
|
|
|
|
if(string_empty(name))
|
|
{
|
|
return g_EntityClassDoom3_bad;
|
|
}
|
|
|
|
EntityClasses::iterator i = g_EntityClassDoom3_classes.find(name);
|
|
if(i != g_EntityClassDoom3_classes.end()
|
|
//&& string_equal((*i).first, name)
|
|
)
|
|
{
|
|
return (*i).second;
|
|
}
|
|
|
|
EntityClass* e = EntityClass_Create_Default(name, has_brushes);
|
|
EntityClass* inserted = EntityClassDoom3_insertUnique(e);
|
|
ASSERT_MESSAGE(inserted == e, "");
|
|
return inserted;
|
|
}
|
|
|
|
const ListAttributeType* EntityClassDoom3_findListType(const char* name)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
void EntityClass_resolveInheritance(EntityClass* derivedClass)
|
|
{
|
|
if(derivedClass->inheritanceResolved == false)
|
|
{
|
|
derivedClass->inheritanceResolved = true;
|
|
EntityClasses::iterator i = g_EntityClassDoom3_classes.find(derivedClass->m_parent.front().c_str());
|
|
if(i == g_EntityClassDoom3_classes.end())
|
|
{
|
|
globalErrorStream() << "failed to find entityDef " << makeQuoted(derivedClass->m_parent.front().c_str()) << " inherited by " << makeQuoted(derivedClass->m_name.c_str()) << "\n";
|
|
}
|
|
else
|
|
{
|
|
EntityClass* parentClass = (*i).second;
|
|
EntityClass_resolveInheritance(parentClass);
|
|
if(!derivedClass->colorSpecified)
|
|
{
|
|
derivedClass->colorSpecified = parentClass->colorSpecified;
|
|
derivedClass->color = parentClass->color;
|
|
}
|
|
if(!derivedClass->sizeSpecified)
|
|
{
|
|
derivedClass->sizeSpecified = parentClass->sizeSpecified;
|
|
derivedClass->mins = parentClass->mins;
|
|
derivedClass->maxs = parentClass->maxs;
|
|
derivedClass->fixedsize = parentClass->fixedsize;
|
|
}
|
|
|
|
for(EntityClassAttributes::iterator j = parentClass->m_attributes.begin(); j != parentClass->m_attributes.end(); ++j)
|
|
{
|
|
EntityClass_insertAttribute(*derivedClass, (*j).first.c_str(), (*j).second);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class EntityClassDoom3 : public ModuleObserver
|
|
{
|
|
std::size_t m_unrealised;
|
|
ModuleObservers m_observers;
|
|
public:
|
|
EntityClassDoom3() : m_unrealised(2)
|
|
{
|
|
}
|
|
void realise()
|
|
{
|
|
if(--m_unrealised == 0)
|
|
{
|
|
globalOutputStream() << "searching vfs directory " << makeQuoted("def") << " for *.def\n";
|
|
GlobalFileSystem().forEachFile("def/", "def", FreeCaller1<const char*, EntityClassDoom3_loadFile>());
|
|
|
|
{
|
|
for(Models::iterator i = g_models.begin(); i != g_models.end(); ++i)
|
|
{
|
|
Model_resolveInheritance((*i).first.c_str(), (*i).second);
|
|
}
|
|
}
|
|
{
|
|
for(EntityClasses::iterator i = g_EntityClassDoom3_classes.begin(); i != g_EntityClassDoom3_classes.end(); ++i)
|
|
{
|
|
EntityClass_resolveInheritance((*i).second);
|
|
if(!string_empty((*i).second->m_modelpath.c_str()))
|
|
{
|
|
Models::iterator j = g_models.find((*i).second->m_modelpath);
|
|
if(j != g_models.end())
|
|
{
|
|
(*i).second->m_modelpath = (*j).second.m_mesh;
|
|
(*i).second->m_skin = (*j).second.m_skin;
|
|
}
|
|
}
|
|
eclass_capture_state((*i).second);
|
|
|
|
StringOutputStream usage(256);
|
|
|
|
usage << "-------- KEYS --------\n";
|
|
|
|
for(EntityClassAttributes::iterator j = (*i).second->m_attributes.begin(); j != (*i).second->m_attributes.end(); ++j)
|
|
{
|
|
const char* name = EntityClassAttributePair_getName(*j);
|
|
const char* description = EntityClassAttributePair_getDescription(*j);
|
|
if(!string_equal(name, description))
|
|
{
|
|
usage << EntityClassAttributePair_getName(*j) << " : " << EntityClassAttributePair_getDescription(*j) << "\n";
|
|
}
|
|
}
|
|
|
|
(*i).second->m_comments = usage.c_str();
|
|
}
|
|
}
|
|
|
|
m_observers.realise();
|
|
}
|
|
}
|
|
void unrealise()
|
|
{
|
|
if(++m_unrealised == 1)
|
|
{
|
|
m_observers.unrealise();
|
|
EntityClassDoom3_clear();
|
|
}
|
|
}
|
|
void attach(ModuleObserver& observer)
|
|
{
|
|
m_observers.attach(observer);
|
|
}
|
|
void detach(ModuleObserver& observer)
|
|
{
|
|
m_observers.detach(observer);
|
|
}
|
|
};
|
|
|
|
EntityClassDoom3 g_EntityClassDoom3;
|
|
|
|
void EntityClassDoom3_attach(ModuleObserver& observer)
|
|
{
|
|
g_EntityClassDoom3.attach(observer);
|
|
}
|
|
void EntityClassDoom3_detach(ModuleObserver& observer)
|
|
{
|
|
g_EntityClassDoom3.detach(observer);
|
|
}
|
|
|
|
void EntityClassDoom3_realise()
|
|
{
|
|
g_EntityClassDoom3.realise();
|
|
}
|
|
void EntityClassDoom3_unrealise()
|
|
{
|
|
g_EntityClassDoom3.unrealise();
|
|
}
|
|
|
|
void EntityClassDoom3_construct()
|
|
{
|
|
GlobalFileSystem().attach(g_EntityClassDoom3);
|
|
|
|
// start by creating the default unknown eclass
|
|
g_EntityClassDoom3_bad = EClass_Create("UNKNOWN_CLASS", Vector3(0.0f, 0.5f, 0.0f), "");
|
|
|
|
EntityClassDoom3_realise();
|
|
}
|
|
|
|
void EntityClassDoom3_destroy()
|
|
{
|
|
EntityClassDoom3_unrealise();
|
|
|
|
g_EntityClassDoom3_bad->free(g_EntityClassDoom3_bad);
|
|
|
|
GlobalFileSystem().detach(g_EntityClassDoom3);
|
|
}
|
|
|
|
class EntityClassDoom3Dependencies : public GlobalFileSystemModuleRef, public GlobalShaderCacheModuleRef
|
|
{
|
|
};
|
|
|
|
class EntityClassDoom3API
|
|
{
|
|
EntityClassManager m_eclassmanager;
|
|
public:
|
|
typedef EntityClassManager Type;
|
|
STRING_CONSTANT(Name, "doom3");
|
|
|
|
EntityClassDoom3API()
|
|
{
|
|
EntityClassDoom3_construct();
|
|
|
|
m_eclassmanager.findOrInsert = &EntityClassDoom3_findOrInsert;
|
|
m_eclassmanager.findListType = &EntityClassDoom3_findListType;
|
|
m_eclassmanager.forEach = &EntityClassDoom3_forEach;
|
|
m_eclassmanager.attach = &EntityClassDoom3_attach;
|
|
m_eclassmanager.detach = &EntityClassDoom3_detach;
|
|
m_eclassmanager.realise = &EntityClassDoom3_realise;
|
|
m_eclassmanager.unrealise = &EntityClassDoom3_unrealise;
|
|
}
|
|
~EntityClassDoom3API()
|
|
{
|
|
EntityClassDoom3_destroy();
|
|
}
|
|
EntityClassManager* getTable()
|
|
{
|
|
return &m_eclassmanager;
|
|
}
|
|
};
|
|
|
|
#include "modulesystem/singletonmodule.h"
|
|
#include "modulesystem/moduleregistry.h"
|
|
|
|
typedef SingletonModule<EntityClassDoom3API, EntityClassDoom3Dependencies> EntityClassDoom3Module;
|
|
typedef Static<EntityClassDoom3Module> StaticEntityClassDoom3Module;
|
|
StaticRegisterModule staticRegisterEntityClassDoom3(StaticEntityClassDoom3Module::instance());
|