2006-02-24 04:48:15 +00:00
|
|
|
/*
|
|
|
|
** thingdef.cpp
|
|
|
|
**
|
|
|
|
** Actor definitions
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
2007-03-07 02:24:24 +00:00
|
|
|
** Copyright 2002-2007 Christoph Oelckers
|
|
|
|
** Copyright 2004-2007 Randy Heit
|
2006-02-24 04:48:15 +00:00
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
|
|
|
** covered by 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.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "gi.h"
|
|
|
|
#include "actor.h"
|
|
|
|
#include "info.h"
|
|
|
|
#include "sc_man.h"
|
|
|
|
#include "tarray.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "templates.h"
|
|
|
|
#include "r_defs.h"
|
|
|
|
#include "r_draw.h"
|
|
|
|
#include "a_pickups.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "p_lnspec.h"
|
|
|
|
#include "p_enemy.h"
|
|
|
|
#include "a_action.h"
|
|
|
|
#include "decallib.h"
|
|
|
|
#include "m_random.h"
|
|
|
|
#include "autosegs.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "p_effect.h"
|
|
|
|
#include "v_palette.h"
|
|
|
|
#include "doomerrors.h"
|
|
|
|
#include "a_doomglobal.h"
|
2006-03-12 22:04:49 +00:00
|
|
|
#include "a_hexenglobal.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
#include "a_weaponpiece.h"
|
|
|
|
#include "p_conversation.h"
|
2006-05-06 03:25:12 +00:00
|
|
|
#include "v_text.h"
|
2006-04-15 15:00:29 +00:00
|
|
|
#include "thingdef.h"
|
2006-08-17 09:54:42 +00:00
|
|
|
#include "a_sharedglobal.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
2006-05-10 02:40:43 +00:00
|
|
|
const PClass *QuestItemClasses[31];
|
2006-05-07 00:27:22 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
2007-05-28 22:18:51 +00:00
|
|
|
// ActorConstDef
|
|
|
|
//
|
|
|
|
// Parses a constant definition.
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
void ParseConstant (PSymbolTable * symt, PClass *cls)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
// Read the type and make sure it's int.
|
|
|
|
// (Maybe there will be other types later.)
|
|
|
|
SC_MustGetToken(TK_Int);
|
|
|
|
SC_MustGetToken(TK_Identifier);
|
|
|
|
FName symname = sc_String;
|
|
|
|
SC_MustGetToken('=');
|
|
|
|
int expr = ParseExpression (false, cls);
|
|
|
|
SC_MustGetToken(';');
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
int val = EvalExpressionI (expr, NULL, cls);
|
|
|
|
PSymbolConst *sym = new PSymbolConst;
|
|
|
|
sym->SymbolName = symname;
|
|
|
|
sym->SymbolType = SYM_Const;
|
|
|
|
sym->Value = val;
|
|
|
|
if (symt->AddSymbol (sym) == NULL)
|
|
|
|
{
|
|
|
|
delete sym;
|
|
|
|
SC_ScriptError ("'%s' is already defined in class '%s'.",
|
|
|
|
symname.GetChars(), cls->TypeName.GetChars());
|
|
|
|
}
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
2007-05-28 22:18:51 +00:00
|
|
|
// ActorEnumDef
|
|
|
|
//
|
|
|
|
// Parses an enum definition.
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
void ParseEnum (PSymbolTable * symt, PClass *cls)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
int currvalue = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_MustGetToken('{');
|
|
|
|
while (!SC_CheckToken('}'))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_MustGetToken(TK_Identifier);
|
|
|
|
FName symname = sc_String;
|
|
|
|
if (SC_CheckToken('='))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
int expr = ParseExpression(false, cls);
|
|
|
|
currvalue = EvalExpressionI (expr, NULL, cls);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
PSymbolConst *sym = new PSymbolConst;
|
|
|
|
sym->SymbolName = symname;
|
|
|
|
sym->SymbolType = SYM_Const;
|
|
|
|
sym->Value = currvalue;
|
|
|
|
if (symt->AddSymbol (sym) == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
delete sym;
|
|
|
|
SC_ScriptError ("'%s' is already defined in class '%s'.",
|
|
|
|
symname.GetChars(), cls->TypeName.GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
// This allows a comma after the last value but doesn't enforce it.
|
|
|
|
if (SC_CheckToken('}')) break;
|
|
|
|
SC_MustGetToken(',');
|
|
|
|
currvalue++;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_MustGetToken(';');
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// ActorActionDef
|
|
|
|
//
|
|
|
|
// Parses an action function definition. A lot of this is essentially
|
|
|
|
// documentation in the declaration for when I have a proper language
|
|
|
|
// ready.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static void ParseActionDef (PClass *cls)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
#define OPTIONAL 1
|
|
|
|
#define EVAL 2
|
|
|
|
#define EVALNOT 4
|
|
|
|
|
|
|
|
AFuncDesc *afd;
|
|
|
|
FName funcname;
|
|
|
|
FString args;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_MustGetToken(TK_Native);
|
|
|
|
SC_MustGetToken(TK_Identifier);
|
|
|
|
funcname = sc_String;
|
|
|
|
afd = FindFunction(sc_String);
|
|
|
|
if (afd == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_ScriptError ("The function '%s' has not been exported from the executable.", sc_String);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_MustGetToken('(');
|
|
|
|
if (!SC_CheckToken(')'))
|
|
|
|
{
|
|
|
|
while (sc_TokenType != ')')
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
int flags = 0;
|
|
|
|
char type = '@';
|
|
|
|
|
|
|
|
// Retrieve flags before type name
|
|
|
|
for (;;)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
if (SC_CheckToken(TK_Optional))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
flags |= OPTIONAL;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
else if (SC_CheckToken(TK_Eval))
|
|
|
|
{
|
|
|
|
flags |= EVAL;
|
|
|
|
}
|
|
|
|
else if (SC_CheckToken(TK_EvalNot))
|
|
|
|
{
|
|
|
|
flags |= EVALNOT;
|
|
|
|
}
|
|
|
|
else if (SC_CheckToken(TK_Coerce) || SC_CheckToken(TK_Native))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
break;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
// Read the variable type
|
|
|
|
SC_MustGetAnyToken();
|
|
|
|
switch (sc_TokenType)
|
|
|
|
{
|
|
|
|
case TK_Bool: type = 'i'; break;
|
|
|
|
case TK_Int: type = 'i'; break;
|
|
|
|
case TK_Float: type = 'f'; break;
|
|
|
|
case TK_Sound: type = 's'; break;
|
|
|
|
case TK_String: type = 't'; break;
|
|
|
|
case TK_Name: type = 't'; break;
|
|
|
|
case TK_State: type = 'l'; break;
|
|
|
|
case TK_Color: type = 'c'; break;
|
|
|
|
case TK_Class:
|
|
|
|
SC_MustGetToken('<');
|
|
|
|
SC_MustGetToken(TK_Identifier); // Skip class name, since the parser doesn't care
|
|
|
|
SC_MustGetToken('>');
|
|
|
|
type = 'm';
|
|
|
|
break;
|
|
|
|
case TK_Ellipsis:
|
|
|
|
type = '+';
|
|
|
|
SC_MustGetToken(')');
|
|
|
|
SC_UnGet();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SC_ScriptError ("Unknown variable type %s", SC_TokenName(sc_TokenType, sc_String).GetChars());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Read the optional variable name
|
|
|
|
if (!SC_CheckToken(',') && !SC_CheckToken(')'))
|
|
|
|
{
|
|
|
|
SC_MustGetToken(TK_Identifier);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SC_UnGet();
|
|
|
|
}
|
|
|
|
// If eval or evalnot were a flag, hey the decorate parser doesn't actually care about the type.
|
|
|
|
if (flags & EVALNOT)
|
|
|
|
{
|
|
|
|
type = 'y';
|
|
|
|
}
|
|
|
|
else if (flags & EVAL)
|
|
|
|
{
|
|
|
|
type = 'x';
|
|
|
|
}
|
|
|
|
if (!(flags & OPTIONAL) && type != '+')
|
|
|
|
{
|
|
|
|
type -= 'a' - 'A';
|
|
|
|
}
|
|
|
|
#undef OPTIONAL
|
|
|
|
#undef EVAL
|
|
|
|
#undef EVALNOT
|
|
|
|
args += type;
|
|
|
|
SC_MustGetAnyToken();
|
|
|
|
if (sc_TokenType != ',' && sc_TokenType != ')')
|
|
|
|
{
|
|
|
|
SC_ScriptError ("Expected ',' or ')' but got %s instead", SC_TokenName(sc_TokenType, sc_String).GetChars());
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_MustGetToken(';');
|
|
|
|
PSymbolActionFunction *sym = new PSymbolActionFunction;
|
|
|
|
sym->SymbolName = funcname;
|
|
|
|
sym->SymbolType = SYM_ActionFunction;
|
|
|
|
sym->Arguments = args;
|
|
|
|
sym->Function = afd->Function;
|
|
|
|
if (cls->Symbols.AddSymbol (sym) == NULL)
|
2007-04-22 21:01:35 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
delete sym;
|
|
|
|
SC_ScriptError ("'%s' is already defined in class '%s'.",
|
|
|
|
funcname.GetChars(), cls->TypeName.GetChars());
|
2007-04-22 21:01:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
//==========================================================================
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
2007-05-28 22:18:51 +00:00
|
|
|
// Starts a new actor definition
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
2007-05-28 22:18:51 +00:00
|
|
|
//==========================================================================
|
|
|
|
static FActorInfo * CreateNewActor(FActorInfo ** parentc, Baggage *bag)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
FName typeName;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
// Get actor name
|
|
|
|
SC_MustGetString();
|
|
|
|
|
|
|
|
char * colon = strchr(sc_String, ':');
|
|
|
|
if (colon != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
*colon++ = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
|
|
|
|
if (PClass::FindClass (sc_String) != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_ScriptError ("Actor %s is already defined.", sc_String);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
typeName = sc_String;
|
|
|
|
|
|
|
|
PClass * parent = RUNTIME_CLASS(AActor);
|
|
|
|
if (parentc)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
*parentc = NULL;
|
|
|
|
|
|
|
|
// Do some tweaking so that a definition like 'Actor:Parent' which is read as a single token is recognized as well
|
|
|
|
// without having resort to C-mode (which disallows periods in actor names.)
|
|
|
|
if (colon == NULL)
|
|
|
|
{
|
|
|
|
SC_MustGetString ();
|
|
|
|
if (sc_String[0]==':')
|
|
|
|
{
|
|
|
|
colon = sc_String + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (colon != NULL)
|
2007-04-22 21:01:35 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
if (colon[0] == 0)
|
|
|
|
{
|
|
|
|
SC_MustGetString ();
|
|
|
|
colon = sc_String;
|
|
|
|
}
|
2007-04-22 21:01:35 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
|
|
|
|
if (colon != NULL)
|
2007-04-22 21:01:35 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
parent = const_cast<PClass *> (PClass::FindClass (colon));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
if (parent == NULL)
|
|
|
|
{
|
|
|
|
SC_ScriptError ("Parent type '%s' not found", colon);
|
|
|
|
}
|
|
|
|
else if (parent->ActorInfo == NULL)
|
|
|
|
{
|
|
|
|
SC_ScriptError ("Parent type '%s' is not an actor", colon);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*parentc = parent->ActorInfo;
|
|
|
|
}
|
2007-04-22 21:01:35 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
else SC_UnGet();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
PClass * ti = parent->CreateDerivedClass (typeName, parent->Size);
|
|
|
|
FActorInfo * info = ti->ActorInfo;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
MakeStateDefines(parent->ActorInfo->StateList);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
ResetBaggage (bag);
|
|
|
|
bag->Info = info;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
info->DoomEdNum = -1;
|
|
|
|
if (parent->ActorInfo->DamageFactors != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
// copy damage factors from parent
|
|
|
|
info->DamageFactors = new DmgFactors;
|
|
|
|
*info->DamageFactors = *parent->ActorInfo->DamageFactors;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
if (parent->ActorInfo->PainChances != NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
// copy pain chances from parent
|
|
|
|
info->PainChances = new PainChanceList;
|
|
|
|
*info->PainChances = *parent->ActorInfo->PainChances;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
// Check for "replaces"
|
|
|
|
SC_MustGetString ();
|
|
|
|
if (SC_Compare ("replaces"))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
const PClass *replacee;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
// Get actor name
|
|
|
|
SC_MustGetString ();
|
|
|
|
replacee = PClass::FindClass (sc_String);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
if (replacee == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_ScriptError ("Replaced type '%s' not found", sc_String);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
else if (replacee->ActorInfo == NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_ScriptError ("Replaced type '%s' is not an actor", sc_String);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
replacee->ActorInfo->Replacement = ti->ActorInfo;
|
|
|
|
ti->ActorInfo->Replacee = replacee->ActorInfo;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_UnGet();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
// Now, after the actor names have been parsed, it is time to switch to C-mode
|
|
|
|
// for the rest of the actor definition.
|
|
|
|
SC_SetCMode (true);
|
|
|
|
if (SC_CheckNumber())
|
2006-10-31 14:53:21 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
if (sc_Number>=-1 && sc_Number<32768) info->DoomEdNum = sc_Number;
|
|
|
|
else SC_ScriptError ("DoomEdNum must be in the range [-1,32767]");
|
2006-10-31 14:53:21 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
if (parent == RUNTIME_CLASS(AWeapon))
|
2006-10-31 14:53:21 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
// preinitialize kickback to the default for the game
|
|
|
|
((AWeapon*)(info->Class->Defaults))->Kickback=gameinfo.defKickback;
|
2006-10-31 14:53:21 +00:00
|
|
|
}
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
return info;
|
2006-10-31 14:53:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
2007-05-28 22:18:51 +00:00
|
|
|
// Reads an actor definition
|
2006-10-31 14:53:21 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
2007-05-28 22:18:51 +00:00
|
|
|
void ParseActor()
|
2006-10-31 14:53:21 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
FActorInfo * info=NULL;
|
|
|
|
Baggage bag;
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
try
|
2006-10-31 14:53:21 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
FActorInfo * parent;
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
info=CreateNewActor(&parent, &bag);
|
|
|
|
SC_MustGetToken('{');
|
|
|
|
while (SC_MustGetAnyToken(), sc_TokenType != '}')
|
|
|
|
{
|
|
|
|
switch (sc_TokenType)
|
|
|
|
{
|
|
|
|
case TK_Action:
|
|
|
|
ParseActionDef (info->Class);
|
|
|
|
break;
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
case TK_Const:
|
|
|
|
ParseConstant (&info->Class->Symbols, info->Class);
|
|
|
|
break;
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
case TK_Enum:
|
|
|
|
ParseEnum (&info->Class->Symbols, info->Class);
|
|
|
|
break;
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
case TK_Identifier:
|
|
|
|
// other identifier related checks here
|
|
|
|
case TK_Projectile: // special case: both keyword and property name
|
|
|
|
ParseActorProperty(bag);
|
|
|
|
break;
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
case '+':
|
|
|
|
case '-':
|
|
|
|
ParseActorFlag(bag, sc_TokenType);
|
|
|
|
break;
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
default:
|
|
|
|
SC_ScriptError("Unexpected '%s' in definition of '%s'", sc_String, bag.Info->Class->TypeName.GetChars());
|
|
|
|
break;
|
|
|
|
}
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2006-12-16 12:50:36 +00:00
|
|
|
}
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
FinishActor(info, bag);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2006-12-16 12:50:36 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
catch(CRecoverableError & e)
|
2006-03-12 22:04:49 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
throw e;
|
2006-10-31 14:53:21 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
// I think this is better than a crash log.
|
|
|
|
#ifndef _DEBUG
|
|
|
|
catch (...)
|
2006-12-16 12:50:36 +00:00
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
if (info)
|
|
|
|
SC_ScriptError("Unexpected error during parsing of actor %s", info->Class->TypeName.GetChars());
|
|
|
|
else
|
|
|
|
SC_ScriptError("Unexpected error during parsing of actor definitions");
|
2006-12-16 12:50:36 +00:00
|
|
|
}
|
2007-05-28 22:18:51 +00:00
|
|
|
#endif
|
2006-10-31 14:53:21 +00:00
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
SC_SetCMode (false);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// Do some postprocessing after everything has been defined
|
2006-06-17 20:29:41 +00:00
|
|
|
// This also processes all the internal actors to adjust the type
|
|
|
|
// fields in the weapons
|
2006-02-24 04:48:15 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
void FinishThingdef()
|
|
|
|
{
|
2006-04-13 02:01:40 +00:00
|
|
|
unsigned int i;
|
2006-06-17 20:29:41 +00:00
|
|
|
bool isRuntimeActor=false;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2006-06-17 20:29:41 +00:00
|
|
|
for (i = 0;i < PClass::m_Types.Size(); i++)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-06-17 20:29:41 +00:00
|
|
|
PClass * ti = PClass::m_Types[i];
|
|
|
|
|
|
|
|
// Skip non-actors
|
|
|
|
if (!ti->IsDescendantOf(RUNTIME_CLASS(AActor))) continue;
|
|
|
|
|
|
|
|
// Everything coming after the first runtime actor is also a runtime actor
|
|
|
|
// so this check is sufficient
|
|
|
|
if (ti == PClass::m_RuntimeActors[0])
|
|
|
|
{
|
|
|
|
isRuntimeActor=true;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// Friendlies never count as kills!
|
|
|
|
if (GetDefaultByType(ti)->flags & MF_FRIENDLY)
|
|
|
|
{
|
|
|
|
GetDefaultByType(ti)->flags &=~MF_COUNTKILL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the typeinfo properties of weapons have to be fixed here after all actors have been declared
|
|
|
|
if (ti->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
AWeapon * defaults=(AWeapon *)ti->Defaults;
|
2006-02-24 04:48:15 +00:00
|
|
|
fuglyname v;
|
|
|
|
|
|
|
|
v = defaults->AmmoType1;
|
- Fixed: ActorFlagSetOrReset() wasn't receiving the + or - character from
ParseActorProperties().
- Fixed: The decorate FindFlag() function returned flags from ActorFlags
instead of the passed flags set.
- Fixed: The CHT_CHAINSAW, CHT_POWER, CHT_HEALTH, and CHT_RESSURECT needed
NULL player->mo checks.
- Fixed: The "give all" command didn't give the backpack in Doom, and it
must give the backpack before giving ammo.
- Fixed: P_SetPsprite() must not call the action function if the player is
not attached to an actor. This can happen, for instance, if the level is
destroyed while the player is holding a powered-up Phoenix Rod. As part
of its EndPowerup() function, it sets the psprite to the regular version,
but the player actor has already been destroyed.
- Fixed: FinishThingdef() needs to check for valid names, because weapons
could have inherited valid pointers from their superclass.
- Fixed: fuglyname didn't work.
- Fixed: Redefining $ambient sounds leaked memory.
- Added Jim's crashcatcher.c fix for better shell support.
- VC7.1 seems to have no trouble distinguishing between passing a (const
TypeInfo *) reference to operator<< and the generic, templated (object *)
version, so a few places that can benefit from it now use it. I believe
VC6 had problems with this, which is why I didn't do it all along. The
function's implementation was also moved out of dobject.cpp and into
farchive.cpp.
- Fixed: UnpackPixels() unpacked all chunks in a byte, which is wrong for the
last byte in a row if the image width is not an even multiple of the number
pixels per byte.
- Fixed: P_TranslateLineDef() should only clear monster activation for secret
useable lines, not crossable lines.
- Fixed: Some leftover P_IsHostile() calls still needed to be rewritten.
- Fixed: AWeaponHolder::Serialize() wrote the class type in all circumstances.
SVN r20 (trunk)
2006-03-14 06:11:39 +00:00
|
|
|
if (v != NAME_None && v.IsValidName())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-20 09:16:08 +00:00
|
|
|
defaults->AmmoType1 = PClass::FindClass(v);
|
2006-06-17 20:29:41 +00:00
|
|
|
if (isRuntimeActor)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-06-17 20:29:41 +00:00
|
|
|
if (!defaults->AmmoType1)
|
|
|
|
{
|
|
|
|
I_Error("Unknown ammo type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
|
|
|
|
}
|
|
|
|
else if (defaults->AmmoType1->ParentClass != RUNTIME_CLASS(AAmmo))
|
|
|
|
{
|
|
|
|
I_Error("Invalid ammo type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
v = defaults->AmmoType2;
|
- Fixed: ActorFlagSetOrReset() wasn't receiving the + or - character from
ParseActorProperties().
- Fixed: The decorate FindFlag() function returned flags from ActorFlags
instead of the passed flags set.
- Fixed: The CHT_CHAINSAW, CHT_POWER, CHT_HEALTH, and CHT_RESSURECT needed
NULL player->mo checks.
- Fixed: The "give all" command didn't give the backpack in Doom, and it
must give the backpack before giving ammo.
- Fixed: P_SetPsprite() must not call the action function if the player is
not attached to an actor. This can happen, for instance, if the level is
destroyed while the player is holding a powered-up Phoenix Rod. As part
of its EndPowerup() function, it sets the psprite to the regular version,
but the player actor has already been destroyed.
- Fixed: FinishThingdef() needs to check for valid names, because weapons
could have inherited valid pointers from their superclass.
- Fixed: fuglyname didn't work.
- Fixed: Redefining $ambient sounds leaked memory.
- Added Jim's crashcatcher.c fix for better shell support.
- VC7.1 seems to have no trouble distinguishing between passing a (const
TypeInfo *) reference to operator<< and the generic, templated (object *)
version, so a few places that can benefit from it now use it. I believe
VC6 had problems with this, which is why I didn't do it all along. The
function's implementation was also moved out of dobject.cpp and into
farchive.cpp.
- Fixed: UnpackPixels() unpacked all chunks in a byte, which is wrong for the
last byte in a row if the image width is not an even multiple of the number
pixels per byte.
- Fixed: P_TranslateLineDef() should only clear monster activation for secret
useable lines, not crossable lines.
- Fixed: Some leftover P_IsHostile() calls still needed to be rewritten.
- Fixed: AWeaponHolder::Serialize() wrote the class type in all circumstances.
SVN r20 (trunk)
2006-03-14 06:11:39 +00:00
|
|
|
if (v != NAME_None && v.IsValidName())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-20 09:16:08 +00:00
|
|
|
defaults->AmmoType2 = PClass::FindClass(v);
|
2006-06-17 20:29:41 +00:00
|
|
|
if (isRuntimeActor)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-06-17 20:29:41 +00:00
|
|
|
if (!defaults->AmmoType2)
|
|
|
|
{
|
|
|
|
I_Error("Unknown ammo type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
|
|
|
|
}
|
|
|
|
else if (defaults->AmmoType2->ParentClass != RUNTIME_CLASS(AAmmo))
|
|
|
|
{
|
|
|
|
I_Error("Invalid ammo type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
v = defaults->SisterWeaponType;
|
- Fixed: ActorFlagSetOrReset() wasn't receiving the + or - character from
ParseActorProperties().
- Fixed: The decorate FindFlag() function returned flags from ActorFlags
instead of the passed flags set.
- Fixed: The CHT_CHAINSAW, CHT_POWER, CHT_HEALTH, and CHT_RESSURECT needed
NULL player->mo checks.
- Fixed: The "give all" command didn't give the backpack in Doom, and it
must give the backpack before giving ammo.
- Fixed: P_SetPsprite() must not call the action function if the player is
not attached to an actor. This can happen, for instance, if the level is
destroyed while the player is holding a powered-up Phoenix Rod. As part
of its EndPowerup() function, it sets the psprite to the regular version,
but the player actor has already been destroyed.
- Fixed: FinishThingdef() needs to check for valid names, because weapons
could have inherited valid pointers from their superclass.
- Fixed: fuglyname didn't work.
- Fixed: Redefining $ambient sounds leaked memory.
- Added Jim's crashcatcher.c fix for better shell support.
- VC7.1 seems to have no trouble distinguishing between passing a (const
TypeInfo *) reference to operator<< and the generic, templated (object *)
version, so a few places that can benefit from it now use it. I believe
VC6 had problems with this, which is why I didn't do it all along. The
function's implementation was also moved out of dobject.cpp and into
farchive.cpp.
- Fixed: UnpackPixels() unpacked all chunks in a byte, which is wrong for the
last byte in a row if the image width is not an even multiple of the number
pixels per byte.
- Fixed: P_TranslateLineDef() should only clear monster activation for secret
useable lines, not crossable lines.
- Fixed: Some leftover P_IsHostile() calls still needed to be rewritten.
- Fixed: AWeaponHolder::Serialize() wrote the class type in all circumstances.
SVN r20 (trunk)
2006-03-14 06:11:39 +00:00
|
|
|
if (v != NAME_None && v.IsValidName())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-20 09:16:08 +00:00
|
|
|
defaults->SisterWeaponType = PClass::FindClass(v);
|
2006-06-17 20:29:41 +00:00
|
|
|
if (isRuntimeActor)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-06-17 20:29:41 +00:00
|
|
|
if (!defaults->SisterWeaponType)
|
|
|
|
{
|
|
|
|
I_Error("Unknown sister weapon type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
|
|
|
|
}
|
|
|
|
else if (!defaults->SisterWeaponType->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
|
|
|
|
{
|
|
|
|
I_Error("Invalid sister weapon type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-06-17 20:29:41 +00:00
|
|
|
if (isRuntimeActor)
|
|
|
|
{
|
2007-04-29 08:44:32 +00:00
|
|
|
FState * ready = ti->ActorInfo->FindState(NAME_Ready);
|
|
|
|
FState * select = ti->ActorInfo->FindState(NAME_Select);
|
|
|
|
FState * deselect = ti->ActorInfo->FindState(NAME_Deselect);
|
|
|
|
FState * fire = ti->ActorInfo->FindState(NAME_Fire);
|
|
|
|
|
|
|
|
// Consider any weapon without any valid state abstract and don't output a warning
|
|
|
|
// This is for creating base classes for weapon groups that only set up some properties.
|
|
|
|
if (ready || select || deselect || fire)
|
|
|
|
{
|
|
|
|
// Do some consistency checks. If these states are undefined the weapon cannot work!
|
|
|
|
if (!ready) I_Error("Weapon %s doesn't define a ready state.\n", ti->TypeName.GetChars());
|
|
|
|
if (!select) I_Error("Weapon %s doesn't define a select state.\n", ti->TypeName.GetChars());
|
|
|
|
if (!deselect) I_Error("Weapon %s doesn't define a deselect state.\n", ti->TypeName.GetChars());
|
|
|
|
if (!fire) I_Error("Weapon %s doesn't define a fire state.\n", ti->TypeName.GetChars());
|
|
|
|
}
|
2006-06-17 20:29:41 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
// same for the weapon type of weapon pieces.
|
|
|
|
else if (ti->IsDescendantOf(RUNTIME_CLASS(AWeaponPiece)))
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
AWeaponPiece * defaults=(AWeaponPiece *)ti->Defaults;
|
2006-02-24 04:48:15 +00:00
|
|
|
fuglyname v;
|
|
|
|
|
|
|
|
v = defaults->WeaponClass;
|
2006-05-20 09:16:08 +00:00
|
|
|
if (v != NAME_None && v.IsValidName())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-20 09:16:08 +00:00
|
|
|
defaults->WeaponClass = PClass::FindClass(v);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (!defaults->WeaponClass)
|
|
|
|
{
|
2006-06-17 20:29:41 +00:00
|
|
|
I_Error("Unknown weapon type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2006-05-20 09:16:08 +00:00
|
|
|
else if (!defaults->WeaponClass->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-06-17 20:29:41 +00:00
|
|
|
I_Error("Invalid weapon type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-05-07 00:27:22 +00:00
|
|
|
|
|
|
|
// Since these are defined in DECORATE now the table has to be initialized here.
|
|
|
|
for(int i=0;i<31;i++)
|
|
|
|
{
|
|
|
|
char fmt[20];
|
|
|
|
sprintf(fmt, "QuestItem%d", i+1);
|
2006-05-10 02:40:43 +00:00
|
|
|
QuestItemClasses[i]=PClass::FindClass(fmt);
|
2006-05-07 00:27:22 +00:00
|
|
|
}
|
2007-04-28 09:06:32 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2006-12-04 23:25:59 +00:00
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// ParseClass
|
|
|
|
//
|
|
|
|
// A minimal placeholder so that I can assign properties to some native
|
|
|
|
// classes. Please, no end users use this until it's finalized.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void ParseClass()
|
|
|
|
{
|
|
|
|
Baggage bag;
|
2007-05-28 22:18:51 +00:00
|
|
|
PClass *cls;
|
2006-12-04 23:25:59 +00:00
|
|
|
FName classname;
|
|
|
|
FName supername;
|
|
|
|
|
|
|
|
SC_MustGetToken(TK_Identifier); // class name
|
2007-05-28 14:46:49 +00:00
|
|
|
classname = sc_String;
|
2006-12-04 23:25:59 +00:00
|
|
|
SC_MustGetToken(TK_Extends); // because I'm not supporting Object
|
|
|
|
SC_MustGetToken(TK_Identifier); // superclass name
|
2007-05-28 14:46:49 +00:00
|
|
|
supername = sc_String;
|
2006-12-04 23:25:59 +00:00
|
|
|
SC_MustGetToken(TK_Native); // use actor definitions for your own stuff
|
|
|
|
SC_MustGetToken('{');
|
|
|
|
|
2007-05-28 22:18:51 +00:00
|
|
|
cls = const_cast<PClass*>(PClass::FindClass (classname));
|
2006-12-04 23:25:59 +00:00
|
|
|
if (cls == NULL)
|
|
|
|
{
|
|
|
|
SC_ScriptError ("'%s' is not a native class", classname.GetChars());
|
|
|
|
}
|
|
|
|
if (cls->ParentClass == NULL || cls->ParentClass->TypeName != supername)
|
|
|
|
{
|
|
|
|
SC_ScriptError ("'%s' does not extend '%s'", classname.GetChars(), supername.GetChars());
|
|
|
|
}
|
|
|
|
bag.Info = cls->ActorInfo;
|
|
|
|
|
|
|
|
SC_MustGetAnyToken();
|
|
|
|
while (sc_TokenType != '}')
|
|
|
|
{
|
|
|
|
if (sc_TokenType == TK_Action)
|
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
ParseActionDef(cls);
|
2006-12-04 23:25:59 +00:00
|
|
|
}
|
|
|
|
else if (sc_TokenType == TK_Const)
|
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
ParseConstant(&cls->Symbols, cls);
|
2006-12-04 23:25:59 +00:00
|
|
|
}
|
2007-05-28 14:46:49 +00:00
|
|
|
else if (sc_TokenType == TK_Enum)
|
|
|
|
{
|
2007-05-28 22:18:51 +00:00
|
|
|
ParseEnum(&cls->Symbols, cls);
|
2007-05-28 14:46:49 +00:00
|
|
|
}
|
2006-12-04 23:25:59 +00:00
|
|
|
else
|
|
|
|
{
|
2006-12-09 00:16:10 +00:00
|
|
|
FString tokname = SC_TokenName(sc_TokenType, sc_String);
|
2007-05-28 14:46:49 +00:00
|
|
|
SC_ScriptError ("Expected 'action', 'const' or 'enum' but got %s", tokname.GetChars());
|
2006-12-04 23:25:59 +00:00
|
|
|
}
|
|
|
|
SC_MustGetAnyToken();
|
|
|
|
}
|
|
|
|
}
|