Update to ZDoom r1246:

- Used the one unused byte in the state structure as a flag to tell what type
  the NextState parameter is. The code did some rather unsafe checks with it
  to determine its type.
- moved all state related code into a new file: p_states.cpp.
- merged all FindState functions. All the different variations are now inlined
  and call the same function to do the real work.
- did some code cleanup and reorganization in thingdef.cpp.
- Replaced the translation parser for TEXTURES with FRemapTable::AddToTranslation.
- To get the game name the screenshot code might as well use the globally
  available GameNames array instead of creating its own list.
- Moved backpack names for cheat into gameinfo.
- Fixed: SNDINFO must be loaded before the textures. However, this required
  some changes to the MAPINFO parser which tried to access the texture manager
  to check if the level name patches exist. That check had to be moved to
  where the intermission screen is set up.
- Fixed: 'bloodcolor' ignored the first parameter value when given a list
  of integers.
  Please note that this creates an incompatibility between old and new 
  versions so if you want to create something that works with both 2.2.0
  and current versions better use the string format version for the color
  parameter!
- Rewrote the DECORATE property parser so that the parser is completely
  separated from the property handlers. This should allow reuse of all 
  the handler code for a new format if Doomscript requires one.
- Fixed: PClass::InitializeActorInfo copied too many bytes if a subclass's
  defaults were larger than the parent's.
- Moved A_ChangeFlag to thingdef_codeptr.cpp.
- Moved translation related code from thingdef_properties.cpp to r_translate.cpp
  and rewrote the translation parser to use FScanner instead of strtol.
- replaced DECORATE's 'alpha default' by 'defaultalpha' for consistency.
  Since this was never used outside zdoom.pk3 it's not critical.
- Removed support for game specific pickup messages because the only thing
  this was ever used for - Raven's invulnerability item - has already been
  split up into a Heretic and Hexen version.
- Fixed: The Timidity config parser always tried to process the note number,
  even if it wasn't specified.
- Fixed: When UpdateJoystickMenu() modifies the menu items for different
  controllers, the joystick axis selectors need to NULL the d.graycheck
  field, since this is shared by the axis sensitivity sliders' step values.
- Fixed: The crosshair must be initialized after the texture manager because
  on the fly texture creation for graphics patches is no longer supported.
- Fixed a few Linux compile errors.
- Changed: Replaced weapons should not be given by generic cheats, only
  when explicitly giving them.
- Changed 'give weapon' cheat so that in single player it only gives weapons
  belonging to the current game or are placed in a weapon slot to avoid
  giving the Chex Quest weapons in Doom and vice versa.
- Fixed: The texture manager must be the first thing to be initialized
  because MAPINFO and DECORATE both can reference textures and letting them
  create their own textures is not safe.


git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@181 b0f79afe-0144-0410-b225-9a4edf0717df
This commit is contained in:
Christoph Oelckers 2008-09-23 21:41:49 +00:00
parent 8302107238
commit 9d46cdadbe
67 changed files with 4610 additions and 3412 deletions

View file

@ -1,4 +1,80 @@
September 22, 2008 (Changes by Graf Zahl)
- Used the one unused byte in the state structure as a flag to tell what type
the NextState parameter is. The code did some rather unsafe checks with it
to determine its type.
- moved all state related code into a new file: p_states.cpp.
- merged all FindState functions. All the different variations are now inlined
and call the same function to do the real work.
September 21, 2008 (Changes by Graf Zahl)
- did some code cleanup and reorganization in thingdef.cpp.
- Replaced the translation parser for TEXTURES with FRemapTable::AddToTranslation.
- To get the game name the screenshot code might as well use the globally
available GameNames array instead of creating its own list.
- Moved backpack names for cheat into gameinfo.
- Fixed: SNDINFO must be loaded before the textures. However, this required
some changes to the MAPINFO parser which tried to access the texture manager
to check if the level name patches exist. That check had to be moved to
where the intermission screen is set up.
- Fixed: 'bloodcolor' ignored the first parameter value when given a list
of integers.
Please note that this creates an incompatibility between old and new
versions so if you want to create something that works with both 2.2.0
and current versions better use the string format version for the color
parameter!
- Rewrote the DECORATE property parser so that the parser is completely
separated from the property handlers. This should allow reuse of all
the handler code for a new format if Doomscript requires one.
- Fixed: PClass::InitializeActorInfo copied too many bytes if a subclass's
defaults were larger than the parent's.
- Moved A_ChangeFlag to thingdef_codeptr.cpp.
- Moved translation related code from thingdef_properties.cpp to r_translate.cpp
and rewrote the translation parser to use FScanner instead of strtol.
- replaced DECORATE's 'alpha default' by 'defaultalpha' for consistency.
Since this was never used outside zdoom.pk3 it's not critical.
- Removed support for game specific pickup messages because the only thing
this was ever used for - Raven's invulnerability item - has already been
split up into a Heretic and Hexen version.
September 20, 2008
- Fixed: The Timidity config parser always tried to process the note number,
even if it wasn't specified.
September 19, 2008
- Fixed: When UpdateJoystickMenu() modifies the menu items for different
controllers, the joystick axis selectors need to NULL the d.graycheck
field, since this is shared by the axis sensitivity sliders' step values.
September 19, 2008 (Changes by Graf Zahl)
- Fixed: The crosshair must be initialized after the texture manager because
on the fly texture creation for graphics patches is no longer supported.
- Fixed a few Linux compile errors.
September 16, 2008 (Changes by Graf Zahl)
- Changed: Replaced weapons should not be given by generic cheats, only
when explicitly giving them.
- Changed 'give weapon' cheat so that in single player it only gives weapons
belonging to the current game or are placed in a weapon slot to avoid
giving the Chex Quest weapons in Doom and vice versa.
- Fixed: The texture manager must be the first thing to be initialized
because MAPINFO and DECORATE both can reference textures and letting them
create their own textures is not safe.
September 15, 2008 (Changes by Graf Zahl)
- Separated low level sound code from all high level dependencies.
- Separated low level sound channel class from high level class which now
is just a subclass of the low level class.
- Moved some more high level sound logic out of FMODSoundRenderer:
The rolloff and channel ended callbacks now call functions in s_sound.cpp
instead of working on the data itself and GSnd->StopSound has been replaced
with S_StopChannel.
- Changed compilation for g_doom, g_heretic, g_hexen and g_strife folders so
that all files are included by a central one instead of compiling each one
separately. This speeds up the compilation process by 25% when doing a
complete rebuild in Visual C.
September 14, 2008 (Changes by Graf Zahl)
- Cleaned up some include dependencies.
- fixed: For Chex Quest some CVars were initialized to Heretic's default.
- fixed pickup message for LargeZorchPack.

File diff suppressed because it is too large Load diff

View file

@ -198,21 +198,21 @@ if( NOT NO_ASM )
find_program( NASM_PATH NAMES ${NASM_NAMES} )
find_program( YASM_PATH yasm )
if( YASM_PATH )
set( ASSEMBLER ${YASM_PATH} )
else( YASM_PATH )
if( X64 )
if( X64 )
if( YASM_PATH )
set( ASSEMBLER ${YASM_PATH} )
else( YASM_PATH )
message( STATUS "Could not find YASM. Disabling assembly code." )
set( NO_ASM ON )
else( X64 )
if( NOT NASM_PATH )
message( STATUS "Could not find YASM or NASM. Disabling assembly code." )
set( NO_ASM ON )
else( NOT NASM_PATH )
set( ASSEMBLER ${NASM_PATH} )
endif( NOT NASM_PATH )
endif( X64 )
endif( YASM_PATH )
endif( YASM_PATH )
else( X64 )
if( NASM_PATH )
set( ASSEMBLER ${NASM_PATH} )
else( NASM_PATH )
message( STATUS "Could not find NASM. Disabling assembly code." )
set( NO_ASM ON )
endif( NASM_PATH )
endif( X64 )
endif( UNIX AND X64 )
# I think the only reason there was a version requirement was because the
@ -397,7 +397,7 @@ else( NO_ASM )
endif( X64 )
if( WIN32 )
if( NOT X64 )
ADD_ASM_FILE( win32/wrappers.asm )
ADD_ASM_FILE( win32 wrappers )
endif( NOT X64 )
endif( WIN32 )
endif( NO_ASM )
@ -506,6 +506,7 @@ add_executable( zdoom WIN32
p_sight.cpp
p_slopes.cpp
p_spec.cpp
p_states.cpp
p_switch.cpp
p_teleport.cpp
p_terrain.cpp
@ -685,6 +686,7 @@ add_executable( zdoom WIN32
thingdef/thingdef_codeptr.cpp
thingdef/thingdef_exp.cpp
thingdef/thingdef_main.cpp
thingdef/thingdef_parse.cpp
thingdef/thingdef_properties.cpp
thingdef/thingdef_states.cpp
timidity/common.cpp

View file

@ -430,6 +430,27 @@ enum
AMETA_BloodType3, // AxeBlood replacement type
};
struct FDropItem
{
FName Name;
int probability;
int amount;
FDropItem * Next;
};
class FDropItemPtrArray : public TArray<FDropItem *>
{
public:
~FDropItemPtrArray();
};
extern FDropItemPtrArray DropItemList;
void FreeDropItemChain(FDropItem *chain);
int StoreDropItemChain(FDropItem *chain);
// Map Object definition.
class AActor : public DThinker
{
@ -451,6 +472,7 @@ public:
return (AActor *)(RUNTIME_TYPE(this)->Defaults);
}
FDropItem *GetDropItems();
// Return true if the monster should use a missile attack, false for melee
bool SuggestMissileAttack (fixed_t dist);
@ -766,14 +788,20 @@ public:
bool isFast();
void SetIdle();
FState *FindState (FName label) const;
FState *FindState (FName label, FName sublabel, bool exact = false) const;
FState *FindState (FName label) const
{
return GetClass()->ActorInfo->FindState(1, &label);
}
FState *FindState (FName label, FName sublabel, bool exact = false) const
{
FName names[]={label, sublabel};
return GetClass()->ActorInfo->FindState(2, &label, exact);
}
bool HasSpecialDeathStates () const;
static FState States[];
enum { S_NULL = 2, S_GENERICFREEZEDEATH = 3 };
TArray<TObjPtr<AActor> > dynamiclights;
void * lightassociations;
bool hasmodel;

View file

@ -50,7 +50,7 @@
#define REGMARKER(x) (x)
typedef void *REGINFO;
// List of ActorInfos and the TypeInfos they belong to
// List of Action functons
extern REGINFO ARegHead;
extern REGINFO ARegTail;
@ -58,6 +58,10 @@ extern REGINFO ARegTail;
extern REGINFO CRegHead;
extern REGINFO CRegTail;
// List of properties
extern REGINFO GRegHead;
extern REGINFO GRegTail;
template<class T, REGINFO *_head, REGINFO *_tail>
class TAutoSegIteratorNoArrow
{

View file

@ -682,6 +682,7 @@ static int PatchThing (int thingy)
bool hadHeight = false;
bool hadTranslucency = false;
bool hadStyle = false;
FStateDefinitions statedef;
bool patchedStates = false;
int oldflags;
const PClass *type;
@ -813,36 +814,36 @@ static int PatchThing (int thingy)
if (type != NULL && !patchedStates)
{
MakeStateDefines(type->ActorInfo->StateList);
statedef.MakeStateDefines(type);
patchedStates = true;
}
if (!strnicmp (Line1, "Initial", 7))
AddState("Spawn", state ? state : GetDefault<AActor>()->SpawnState);
statedef.AddState("Spawn", state ? state : GetDefault<AActor>()->SpawnState);
else if (!strnicmp (Line1, "First moving", 12))
AddState("See", state);
statedef.AddState("See", state);
else if (!strnicmp (Line1, "Injury", 6))
AddState("Pain", state);
statedef.AddState("Pain", state);
else if (!strnicmp (Line1, "Close attack", 12))
{
if (thingy != 1) // Not for players!
{
AddState("Melee", state);
statedef.AddState("Melee", state);
}
}
else if (!strnicmp (Line1, "Far attack", 10))
{
if (thingy != 1) // Not for players!
{
AddState("Missile", state);
statedef.AddState("Missile", state);
}
}
else if (!strnicmp (Line1, "Death", 5))
AddState("Death", state);
statedef.AddState("Death", state);
else if (!strnicmp (Line1, "Exploding", 9))
AddState("XDeath", state);
statedef.AddState("XDeath", state);
else if (!strnicmp (Line1, "Respawn", 7))
AddState("Raise", state);
statedef.AddState("Raise", state);
}
else if (stricmp (Line1 + linelen - 6, " sound") == 0)
{
@ -1048,7 +1049,7 @@ static int PatchThing (int thingy)
}
if (patchedStates)
{
InstallStates(type->ActorInfo, info);
statedef.InstallStates(type->ActorInfo, info);
}
}
@ -1359,6 +1360,7 @@ static int PatchWeapon (int weapNum)
AWeapon *info;
BYTE dummy[sizeof(AWeapon)];
bool patchedStates = false;
FStateDefinitions statedef;
if (weapNum >= 0 && weapNum < 9)
{
@ -1385,20 +1387,20 @@ static int PatchWeapon (int weapNum)
if (type != NULL && !patchedStates)
{
MakeStateDefines(type->ActorInfo->StateList);
statedef.MakeStateDefines(type);
patchedStates = true;
}
if (strnicmp (Line1, "Deselect", 8) == 0)
AddState("Select", state);
statedef.AddState("Select", state);
else if (strnicmp (Line1, "Select", 6) == 0)
AddState("Deselect", state);
statedef.AddState("Deselect", state);
else if (strnicmp (Line1, "Bobbing", 7) == 0)
AddState("Ready", state);
statedef.AddState("Ready", state);
else if (strnicmp (Line1, "Shooting", 8) == 0)
AddState("Fire", state);
statedef.AddState("Fire", state);
else if (strnicmp (Line1, "Firing", 6) == 0)
AddState("Flash", state);
statedef.AddState("Flash", state);
}
else if (stricmp (Line1, "Ammo type") == 0)
{
@ -1455,7 +1457,7 @@ static int PatchWeapon (int weapNum)
if (patchedStates)
{
InstallStates(type->ActorInfo, info);
statedef.InstallStates(type->ActorInfo, info);
}
return result;
@ -1697,18 +1699,22 @@ static int PatchMisc (int dummy)
{
player->health = deh.StartHealth;
FDropItem * di = GetDropItems(PClass::FindClass(NAME_DoomPlayer));
while (di != NULL)
// Hm... I'm not sure that this is the right way to change this info...
unsigned int index = PClass::FindClass(NAME_DoomPlayer)->Meta.GetMetaInt (ACMETA_DropItems) - 1;
if (index >= 0 && index < DropItemList.Size())
{
if (di->Name == NAME_Clip)
FDropItem * di = DropItemList[index];
while (di != NULL)
{
di->amount = deh.StartBullets;
if (di->Name == NAME_Clip)
{
di->amount = deh.StartBullets;
}
di = di->Next;
}
di = di->Next;
}
}
// 0xDD means "enable infighting"
if (infighting == 0xDD)
{
@ -2195,14 +2201,6 @@ void DoDehPatch (const char *patchfile, bool autoloading, int lump)
PatchFile[filelen] = 0;
dversion = pversion = -1;
/*
if (gameinfo.gametype != GAME_Doom)
{
Printf ("DeHackEd/BEX patches are only supported for DOOM mode\n");
delete[] PatchFile;
return;
}
*/
cont = 0;
if (0 == strncmp (PatchFile, "Patch File for DeHackEd v", 25))
{
@ -2586,13 +2584,14 @@ void FinishDehPatch ()
memcpy (defaults2, defaults1, sizeof(AActor));
// Make a copy the state labels
MakeStateDefines(type->ActorInfo->StateList);
if (!type->IsDescendantOf(RUNTIME_CLASS(AInventory)))
{
// If this is a hacked non-inventory item we must also copy AInventory's special states
AddStateDefines(RUNTIME_CLASS(AInventory)->ActorInfo->StateList);
FStateDefinitions statedef;
statedef.MakeStateDefines(type);
statedef.AddStateDefines(RUNTIME_CLASS(AInventory)->ActorInfo->StateList);
statedef.InstallStates(subclass->ActorInfo, defaults2);
}
InstallStates(subclass->ActorInfo, defaults2);
// Use the DECORATE replacement feature to redirect all spawns
// of the original class to the new one.

View file

@ -2489,6 +2489,13 @@ void D_DoomMain (void)
Printf ("S_InitData: Load sound definitions.\n");
S_InitData ();
Printf ("Texman.Init: Init texture manager.\n");
TexMan.Init();
// Now that all textues have been loaded the crosshair can be initialized.
crosshair.Callback ();
// [CW] Parse any TEAMINFO lumps
Printf ("TEAMINFO_Init: Load team definitions.\n");
TEAMINFO_Init ();

View file

@ -297,7 +297,7 @@ void PClass::InitializeActorInfo ()
Defaults = new BYTE[Size];
if (ParentClass->Defaults != NULL)
{
memcpy (Defaults, ParentClass->Defaults, Size);
memcpy (Defaults, ParentClass->Defaults, ParentClass->Size);
if (Size > ParentClass->Size)
{
memset (Defaults + ParentClass->Size, 0, Size - ParentClass->Size);

View file

@ -170,6 +170,14 @@ struct PalEntry
#endif
};
// Screenshot buffer image data types
enum ESSType
{
SS_PAL,
SS_RGB,
SS_BGRA
};
#ifndef M_PI
#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h
#endif

View file

@ -165,7 +165,7 @@ static void SpawnFly(AActor *self, const PClass *spawntype, FSoundID sound)
FDropItem *drop; // while drop stays as the reference point.
int n=0;
drop = di = GetDropItems(self->master->GetClass());
drop = di = self->master->GetDropItems();
if (di != NULL)
{
while (di != NULL)

View file

@ -848,13 +848,15 @@ static void G_DoParseMapInfo (int lump)
strcpy (levelinfo->skypic2, levelinfo->skypic1);
}
SetLevelNum (levelinfo, levelinfo->levelnum); // Wipe out matching levelnums from other maps.
/* can't do this here.
if (levelinfo->pname[0] != 0)
{
if (!TexMan.AddPatch(levelinfo->pname).Exists())
if (!TexMan.CheckForTexture(levelinfo->pname, FTexture::TEX_MiscPatch).Exists())
{
levelinfo->pname[0] = 0;
}
}
*/
break;
}
@ -1722,7 +1724,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
{
int cstype = SBarInfoScript->GetGameType();
if(cstype == GAME_Doom || cstype == GAME_Chex) //Did the user specify a "base"
if(cstype & GAME_DoomChex) //Did the user specify a "base"
{
StatusBar = CreateDoomStatusBar ();
}
@ -1854,7 +1856,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
//
// G_DoCompleted
//
static char nextlevel[9];
static FString nextlevel;
static int startpos; // [RH] Support for multiple starts per level
extern int NoWipe; // [RH] Don't wipe when travelling in hubs
static bool startkeepfacing; // [RH] Support for keeping your facing angle
@ -1866,7 +1868,7 @@ static bool g_nomonsters;
// match the first parameter of the single player start spots
// that should appear in the next map.
void G_ChangeLevel(const char * levelname, int position, bool keepFacing, int nextSkill,
void G_ChangeLevel(const char *levelname, int position, bool keepFacing, int nextSkill,
bool nointermission, bool resetinv, bool nomonsters)
{
if (unloading)
@ -1875,15 +1877,14 @@ void G_ChangeLevel(const char * levelname, int position, bool keepFacing, int ne
return;
}
strncpy (nextlevel, levelname, 8);
nextlevel[8] = 0;
nextlevel = levelname;
if (strncmp(nextlevel, "enDSeQ", 6))
if (strncmp(levelname, "enDSeQ", 6))
{
level_info_t *nextinfo = CheckLevelRedirect (FindLevelInfo (nextlevel));
if (nextinfo)
{
strncpy(nextlevel, nextinfo->mapname, 8);
nextlevel = nextinfo->mapname;
}
}
@ -1983,7 +1984,7 @@ void G_DoCompleted (void)
if (gamestate == GS_TITLELEVEL)
{
strncpy (level.mapname, nextlevel, 8);
strncpy (level.mapname, nextlevel, 255);
G_DoLoadLevel (startpos, false);
startpos = 0;
viewactive = true;
@ -1998,7 +1999,7 @@ void G_DoCompleted (void)
AM_Stop ();
wminfo.finished_ep = level.cluster - 1;
wminfo.lname0 = level.info->pname;
wminfo.LName0 = TexMan[TexMan.CheckForTexture(level.info->pname, FTexture::TEX_MiscPatch)];
wminfo.current = level.mapname;
if (deathmatch &&
@ -2006,26 +2007,27 @@ void G_DoCompleted (void)
!(level.flags & LEVEL_CHANGEMAPCHEAT))
{
wminfo.next = level.mapname;
wminfo.lname1 = level.info->pname;
wminfo.LName1 = wminfo.LName0;
}
else
{
if (strncmp (nextlevel, "enDSeQ", 6) == 0)
{
wminfo.next = FString(nextlevel, 8);
wminfo.lname1 = "";
wminfo.LName1 = NULL;
}
else
{
level_info_t *nextinfo = FindLevelInfo (nextlevel);
wminfo.next = nextinfo->mapname;
wminfo.lname1 = nextinfo->pname;
wminfo.LName1 = TexMan[TexMan.CheckForTexture(nextinfo->pname, FTexture::TEX_MiscPatch)];
}
}
CheckWarpTransMap (wminfo.next, true);
nextlevel = wminfo.next;
wminfo.next_ep = FindLevelInfo (nextlevel)->cluster - 1;
wminfo.next_ep = FindLevelInfo (wminfo.next)->cluster - 1;
wminfo.maxkills = level.total_monsters;
wminfo.maxitems = level.total_items;
wminfo.maxsecret = level.total_secrets;
@ -2357,7 +2359,7 @@ void G_DoWorldDone (void)
}
else
{
strncpy (level.mapname, nextlevel, 8);
strncpy (level.mapname, nextlevel, 255);
}
G_StartTravel ();
G_DoLoadLevel (startpos, true);

View file

@ -81,7 +81,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_NoBlocking)
// If the self has attached metadata for items to drop, drop those.
if (!self->IsKindOf (RUNTIME_CLASS (APlayerPawn))) // [GRB]
{
FDropItem *di = GetDropItems(RUNTIME_TYPE(self));
FDropItem *di = self->GetDropItems();
if (di != NULL)
{

View file

@ -394,6 +394,7 @@ bool P_CheckKeys (AActor *owner, int keynum, bool remote)
const char *failtext = NULL;
FSoundID failsound;
if (owner == NULL) return false;
if (keynum<=0 || keynum>255) return true;
// Just a safety precaution. The messages should have been initialized upon game start.
if (!keysdone) P_InitKeyMessages();

View file

@ -23,14 +23,14 @@ class ARandomSpawner : public AActor
void PostBeginPlay()
{
AActor *newmobj;
AActor *newmobj = NULL;
FDropItem *di; // di will be our drop item list iterator
FDropItem *drop; // while drop stays as the reference point.
int n=0;
Super::PostBeginPlay();
drop = di = GetDropItems(RUNTIME_TYPE(this));
drop = di = GetDropItems();
if (di != NULL)
{
while (di != NULL)

View file

@ -587,7 +587,7 @@ IMPLEMENT_CLASS(AWeaponGiver)
bool AWeaponGiver::TryPickup(AActor *&toucher)
{
FDropItem *di = GetDropItems(GetClass());
FDropItem *di = GetDropItems();
if (di != NULL)
{
@ -737,8 +737,8 @@ bool FWeaponSlots::LocateWeapon (const PClass *type, int *const slot, int *const
{
if (Slots[i].Weapons[j] == type)
{
*slot = i;
*index = j;
if (slot != NULL) *slot = i;
if (index != NULL) *index = j;
return true;
}
else if (Slots[i].Weapons[j] == NULL)

View file

@ -94,7 +94,7 @@ CUSTOM_CVAR (Bool, st_scale, true, CVAR_ARCHIVE)
}
}
CUSTOM_CVAR (Int, crosshair, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CUSTOM_CVAR (Int, crosshair, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
{
int num = self;
char name[16], size;
@ -123,7 +123,7 @@ CUSTOM_CVAR (Int, crosshair, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
strcpy (name, "XHAIRS1");
}
}
CrosshairImage = TexMan[TexMan.AddPatch (name)];
CrosshairImage = TexMan[TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch)];
}
CVAR (Color, crosshaircolor, 0xff0000, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);

View file

@ -100,6 +100,7 @@ gameinfo_t HexenGameInfo =
{ "mapinfo/hexen.txt", NULL },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"BagOfHolding", // Hexen doesn't have a backpack so use Heretic's.
};
gameinfo_t HexenDKGameInfo =
@ -132,6 +133,7 @@ gameinfo_t HexenDKGameInfo =
{ "mapinfo/hexen.txt", NULL },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"BagOfHolding",
};
gameinfo_t HereticGameInfo =
@ -164,6 +166,7 @@ gameinfo_t HereticGameInfo =
{ "mapinfo/heretic.txt", NULL },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"BagOfHolding",
};
gameinfo_t HereticSWGameInfo =
@ -196,6 +199,7 @@ gameinfo_t HereticSWGameInfo =
{ "mapinfo/heretic.txt", NULL },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"BagOfHolding",
};
gameinfo_t SharewareGameInfo =
@ -228,6 +232,7 @@ gameinfo_t SharewareGameInfo =
{ "mapinfo/doomcommon.txt", "mapinfo/doom1.txt" },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"Backpack",
};
gameinfo_t RegisteredGameInfo =
@ -260,6 +265,7 @@ gameinfo_t RegisteredGameInfo =
{ "mapinfo/doomcommon.txt", "mapinfo/doom1.txt" },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"Backpack",
};
gameinfo_t ChexGameInfo =
@ -292,6 +298,7 @@ gameinfo_t ChexGameInfo =
{ "mapinfo/chex.txt", NULL },
MAKERGB(63,125,57),
MAKERGB(95,175,87),
"ZorchPack",
};
gameinfo_t Chex3GameInfo =
@ -324,6 +331,7 @@ gameinfo_t Chex3GameInfo =
{ "mapinfo/chex.txt", NULL },
MAKERGB(63,125,57),
MAKERGB(95,175,87),
"ZorchPack",
};
gameinfo_t RetailGameInfo =
@ -356,6 +364,7 @@ gameinfo_t RetailGameInfo =
{ "mapinfo/doomcommon.txt", "mapinfo/doom1.txt" },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"Backpack",
};
gameinfo_t CommercialGameInfo =
@ -388,6 +397,7 @@ gameinfo_t CommercialGameInfo =
{ "mapinfo/doomcommon.txt", "mapinfo/doom2.txt" },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"Backpack",
};
gameinfo_t PlutoniaGameInfo =
@ -420,6 +430,7 @@ gameinfo_t PlutoniaGameInfo =
{ "mapinfo/doomcommon.txt", "mapinfo/plutonia.txt" },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"Backpack",
};
gameinfo_t TNTGameInfo =
@ -452,6 +463,7 @@ gameinfo_t TNTGameInfo =
{ "mapinfo/doomcommon.txt", "mapinfo/tnt.txt" },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"Backpack",
};
gameinfo_t StrifeGameInfo =
@ -484,6 +496,7 @@ gameinfo_t StrifeGameInfo =
{ "mapinfo/strife.txt", NULL },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"AmmoSatchel",
};
gameinfo_t StrifeTeaserGameInfo =
@ -516,6 +529,7 @@ gameinfo_t StrifeTeaserGameInfo =
{ "mapinfo/strife.txt", NULL },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"AmmoSatchel",
};
gameinfo_t StrifeTeaser2GameInfo =
@ -548,4 +562,5 @@ gameinfo_t StrifeTeaser2GameInfo =
{ "mapinfo/strife.txt", NULL },
MAKERGB(104,0,0),
MAKERGB(255,0,0),
"AmmoSatchel",
};

View file

@ -121,6 +121,7 @@ typedef struct
const char *mapinfo[2];
DWORD defaultbloodcolor;
DWORD defaultbloodparticlecolor;
const char *backpacktype;
} gameinfo_t;
extern gameinfo_t gameinfo;

View file

@ -51,122 +51,6 @@
extern void LoadDecorations ();
// Each state is owned by an actor. Actors can own any number of
// states, but a single state cannot be owned by more than one
// actor. States are archived by recording the actor they belong
// to and the index into that actor's list of states.
// For NULL states, which aren't owned by any actor, the owner
// is recorded as AActor with the following state. AActor should
// never actually have this many states of its own, so this
// is (relatively) safe.
#define NULL_STATE_INDEX 127
//==========================================================================
//
//
//==========================================================================
FArchive &operator<< (FArchive &arc, FState *&state)
{
const PClass *info;
if (arc.IsStoring ())
{
if (state == NULL)
{
arc.UserWriteClass (RUNTIME_CLASS(AActor));
arc.WriteCount (NULL_STATE_INDEX);
return arc;
}
info = FState::StaticFindStateOwner (state);
if (info != NULL)
{
arc.UserWriteClass (info);
arc.WriteCount ((DWORD)(state - info->ActorInfo->OwnedStates));
}
else
{
/* this was never working as intended.
I_Error ("Cannot find owner for state %p:\n"
"%s %c%c %3d [%p] -> %p", state,
sprites[state->sprite].name,
state->GetFrame() + 'A',
state->GetFullbright() ? '*' : ' ',
state->GetTics(),
state->GetAction(),
state->GetNextState());
*/
}
}
else
{
const PClass *info;
DWORD ofs;
arc.UserReadClass (info);
ofs = arc.ReadCount ();
if (ofs == NULL_STATE_INDEX && info == RUNTIME_CLASS(AActor))
{
state = NULL;
}
else if (info->ActorInfo != NULL)
{
state = info->ActorInfo->OwnedStates + ofs;
}
else
{
state = NULL;
}
}
return arc;
}
//==========================================================================
//
// Find the actor that a state belongs to.
//
//==========================================================================
const PClass *FState::StaticFindStateOwner (const FState *state)
{
for (unsigned int i = 0; i < PClass::m_RuntimeActors.Size(); ++i)
{
FActorInfo *info = PClass::m_RuntimeActors[i]->ActorInfo;
if (state >= info->OwnedStates &&
state < info->OwnedStates + info->NumOwnedStates)
{
return info->Class;
}
}
return NULL;
}
//==========================================================================
//
// Find the actor that a state belongs to, but restrict the search to
// the specified type and its ancestors.
//
//==========================================================================
const PClass *FState::StaticFindStateOwner (const FState *state, const FActorInfo *info)
{
while (info != NULL)
{
if (state >= info->OwnedStates &&
state < info->OwnedStates + info->NumOwnedStates)
{
return info->Class;
}
info = info->Class->ParentClass->ActorInfo;
}
return NULL;
}
//==========================================================================
//
//
@ -174,16 +58,23 @@ const PClass *FState::StaticFindStateOwner (const FState *state, const FActorInf
int GetSpriteIndex(const char * spritename)
{
// Make sure that the string is upper case and 4 characters long
char upper[5];
for (int i = 0; spritename[i] != 0 && i < 4; i++)
{
upper[i] = toupper (spritename[i]);
}
upper[4] = 0;
for (unsigned i = 0; i < sprites.Size (); ++i)
{
if (strncmp (sprites[i].name, spritename, 4) == 0)
if (strcmp (sprites[i].name, spritename) == 0)
{
return (int)i;
}
}
spritedef_t temp;
strncpy (temp.name, spritename, 4);
temp.name[4] = 0;
strcpy (temp.name, upper);
temp.numframes = 0;
temp.spriteframes = 0;
return (int)sprites.Push (temp);
@ -337,209 +228,6 @@ void FActorInfo::SetPainChance(FName type, int chance)
}
}
//==========================================================================
//
//
//==========================================================================
FStateLabel *FStateLabels::FindLabel (FName label)
{
return const_cast<FStateLabel *>(BinarySearch<FStateLabel, FName> (Labels, NumLabels, &FStateLabel::Label, label));
}
void FStateLabels::Destroy ()
{
for(int i=0; i<NumLabels;i++)
{
if (Labels[i].Children != NULL)
{
Labels[i].Children->Destroy();
free (Labels[i].Children); // These are malloc'd, not new'd!
Labels[i].Children=NULL;
}
}
}
//===========================================================================
//
// HasStates
//
// Checks whether the actor has special death states.
//
//===========================================================================
bool AActor::HasSpecialDeathStates () const
{
const FActorInfo *info = GetClass()->ActorInfo;
if (info->StateList != NULL)
{
FStateLabel *slabel = info->StateList->FindLabel (NAME_Death);
if (slabel != NULL && slabel->Children != NULL)
{
for(int i=0;i<slabel->Children->NumLabels;i++)
{
if (slabel->Children->Labels[i].State != NULL) return true;
}
}
}
return false;
}
//===========================================================================
//
// FindState (one name version)
//
// Finds a state with the exact specified name.
//
//===========================================================================
FState *AActor::FindState (FName label) const
{
const FActorInfo *info = GetClass()->ActorInfo;
if (info->StateList != NULL)
{
FStateLabel *slabel = info->StateList->FindLabel (label);
if (slabel != NULL)
{
return slabel->State;
}
}
return NULL;
}
//===========================================================================
//
// FindState (two name version)
//
//===========================================================================
FState *AActor::FindState (FName label, FName sublabel, bool exact) const
{
const FActorInfo *info = GetClass()->ActorInfo;
if (info->StateList != NULL)
{
FStateLabel *slabel = info->StateList->FindLabel (label);
if (slabel != NULL)
{
if (slabel->Children != NULL)
{
FStateLabel *slabel2 = slabel->Children->FindLabel(sublabel);
if (slabel2 != NULL)
{
return slabel2->State;
}
}
if (!exact) return slabel->State;
}
}
return NULL;
}
//===========================================================================
//
// FindState (multiple names version)
//
// Finds a state that matches as many of the supplied names as possible.
// A state with more names than those provided does not match.
// A state with fewer names can match if there are no states with the exact
// same number of names.
//
// The search proceeds like this. For the current class, keeping matching
// names until there are no more. If both the argument list and the state
// are out of names, it's an exact match, so return it. If the state still
// has names, ignore it. If the argument list still has names, remember it.
//
//===========================================================================
FState *FActorInfo::FindState (FName name) const
{
return FindState(1, &name);
}
FState *FActorInfo::FindState (int numnames, FName *names, bool exact) const
{
FStateLabels *labels = StateList;
FState *best = NULL;
if (labels != NULL)
{
int count = 0;
FStateLabel *slabel = NULL;
FName label;
// Find the best-matching label for this class.
while (labels != NULL && count < numnames)
{
label = *names++;
slabel = labels->FindLabel (label);
if (slabel != NULL)
{
count++;
labels = slabel->Children;
best = slabel->State;
}
else
{
break;
}
}
if (count < numnames && exact) return NULL;
}
return best;
}
//==========================================================================
//
// Creates a list of names from a string. Dots are used as separator
//
//==========================================================================
void MakeStateNameList(const char * fname, TArray<FName> * out)
{
FName firstpart, secondpart;
char * c;
// Handle the old names for the existing death states
char * name = copystring(fname);
firstpart = strtok(name, ".");
switch (firstpart)
{
case NAME_Burn:
firstpart = NAME_Death;
secondpart = NAME_Fire;
break;
case NAME_Ice:
firstpart = NAME_Death;
secondpart = NAME_Ice;
break;
case NAME_Disintegrate:
firstpart = NAME_Death;
secondpart = NAME_Disintegrate;
break;
case NAME_XDeath:
firstpart = NAME_Death;
secondpart = NAME_Extreme;
break;
}
out->Clear();
out->Push(firstpart);
if (secondpart!=NAME_None) out->Push(secondpart);
while ((c = strtok(NULL, "."))!=NULL)
{
FName cc = c;
out->Push(cc);
}
delete [] name;
}
//==========================================================================
//
//

View file

@ -89,6 +89,7 @@ struct FState
SBYTE Misc1;
BYTE Misc2;
BYTE Frame;
BYTE DefineFlags; // Unused byte so let's use it during state creation.
FState *NextState;
actionf_p ActionFunc;
int ParameterIndex;
@ -204,8 +205,12 @@ struct FActorInfo
void SetDamageFactor(FName type, fixed_t factor);
void SetPainChance(FName type, int chance);
FState *FindState (FName name) const;
FState *FindState (int numnames, FName *names, bool exact=false) const;
FState *FindStateByString(const char *name, bool exact=false);
FState *FindState (FName name) const
{
return FindState(1, &name);
}
FActorInfo *GetReplacement ();
FActorInfo *GetReplacee ();
@ -252,6 +257,6 @@ private:
extern FDoomEdMap DoomEdMap;
int GetSpriteIndex(const char * spritename);
void MakeStateNameList(const char * fname, TArray<FName> * out);
TArray<FName> &MakeStateNameList(const char * fname);
#endif // __INFO_H__

View file

@ -43,6 +43,8 @@
#include "r_translate.h"
#include "g_level.h"
#include "d_net.h"
#include "d_dehacked.h"
#include "gi.h"
// [RH] Actually handle the cheat. The cheat code in st_stuff.c now just
// writes some bytes to the network data stream, and the network code
@ -629,26 +631,7 @@ void cht_Give (player_t *player, const char *name, int amount)
if (giveall || stricmp (name, "backpack") == 0)
{
// Select the correct type of backpack based on the game
if (gameinfo.gametype == GAME_Heretic)
{
type = PClass::FindClass ("BagOfHolding");
}
else if (gameinfo.gametype == GAME_Strife)
{
type = PClass::FindClass ("AmmoSatchel");
}
else if (gameinfo.gametype == GAME_Doom)
{
type = PClass::FindClass ("Backpack");
}
else if (gameinfo.gametype == GAME_Chex)
{
type = PClass::FindClass ("Zorchpack");
}
else
{ // Hexen doesn't have a backpack, foo!
type = NULL;
}
type = PClass::FindClass(gameinfo.backpacktype);
if (type != NULL)
{
GiveSpawner (player, type, 1);
@ -743,13 +726,26 @@ void cht_Give (player_t *player, const char *name, int amount)
for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
{
type = PClass::m_Types[i];
// Don't give replaced weapons unless the replacement was done by Dehacked.
if (type != RUNTIME_CLASS(AWeapon) &&
type->IsDescendantOf (RUNTIME_CLASS(AWeapon)))
type->IsDescendantOf (RUNTIME_CLASS(AWeapon)) &&
(type->ActorInfo->GetReplacement() == type->ActorInfo ||
type->ActorInfo->GetReplacement()->Class->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup))))
{
AWeapon *def = (AWeapon*)GetDefaultByType (type);
if (!(def->WeaponFlags & WIF_CHEATNOTWEAPON))
// Give the weapon only if it belongs to the current game or
// is in a weapon slot. Unfortunately this check only works in
// singleplayer games because the weapon slots are stored locally.
// In multiplayer games all weapons must be given.
if (multiplayer || type->ActorInfo->GameFilter == GAME_Any ||
(type->ActorInfo->GameFilter & gameinfo.gametype) ||
LocalWeapons.LocateWeapon(type, NULL, NULL))
{
GiveSpawner (player, type, 1);
AWeapon *def = (AWeapon*)GetDefaultByType (type);
if (!(def->WeaponFlags & WIF_CHEATNOTWEAPON))
{
GiveSpawner (player, type, 1);
}
}
}
}
@ -860,33 +856,17 @@ void cht_Take (player_t *player, const char *name, int amount)
if (takeall || stricmp (name, "backpack") == 0)
{
// Select the correct type of backpack based on the game
if (gameinfo.gametype == GAME_Heretic)
// Take away all types of backpacks the player might own.
for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
{
type = PClass::FindClass ("BagOfHolding");
}
else if (gameinfo.gametype == GAME_Strife)
{
type = PClass::FindClass ("AmmoSatchel");
}
else if (gameinfo.gametype == GAME_Doom)
{
type = PClass::FindClass ("Backpack");
}
else if (gameinfo.gametype == GAME_Chex)
{
type = PClass::FindClass ("Zorchpack");
}
else
{ // Hexen doesn't have a backpack, foo!
type = NULL;
}
if (type != NULL)
{
AActor *backpack = player->mo->FindInventory (type);
const PClass *type = PClass::m_Types[i];
if (backpack)
backpack->Destroy ();
if (type->IsDescendantOf(RUNTIME_CLASS (ABackpackItem)))
{
AInventory *pack = player->mo->FindInventory (type);
if (pack) pack->Destroy();
}
}
if (!takeall)

View file

@ -596,17 +596,7 @@ static bool FindFreeName (FString &fullname, const char *extension)
for (i = 0; i <= 9999; i++)
{
const char *gamename;
switch (gameinfo.gametype)
{
case GAME_Doom: gamename = "Doom_"; break;
case GAME_Heretic: gamename = "Heretic_"; break;
case GAME_Hexen: gamename = "Hexen_"; break;
case GAME_Strife: gamename = "Strife_"; break;
case GAME_Chex: gamename = "Chex_"; break;
default: gamename = ""; break;
}
const char *gamename = GameNames[gameinfo.gametype];
time_t now;
tm *tm;
@ -616,18 +606,18 @@ static bool FindFreeName (FString &fullname, const char *extension)
if (tm == NULL)
{
lbmname.Format ("%sScreenshot_%s%04d.%s", fullname.GetChars(), gamename, i, extension);
lbmname.Format ("%sScreenshot_%s_%04d.%s", fullname.GetChars(), gamename, i, extension);
}
else if (i == 0)
{
lbmname.Format ("%sScreenshot_%s%04d%02d%02d_%02d%02d%02d.%s", fullname.GetChars(), gamename,
lbmname.Format ("%sScreenshot_%s_%04d%02d%02d_%02d%02d%02d.%s", fullname.GetChars(), gamename,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
extension);
}
else
{
lbmname.Format ("%sScreenshot_%s%04d%02d%02d_%02d%02d%02d_%02d.%s", fullname.GetChars(), gamename,
lbmname.Format ("%sScreenshot_%s_%04d%02d%02d_%02d%02d%02d_%02d.%s", fullname.GetChars(), gamename,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
i, extension);

View file

@ -1392,7 +1392,7 @@ static BYTE BitTranslate[32];
void M_OptInit (void)
{
if (gameinfo.gametype == GAME_Doom)
if (gameinfo.gametype & GAME_DoomChex)
{
LabelColor = CR_UNTRANSLATED;
ValueColor = CR_GRAY;
@ -2999,6 +2999,7 @@ void UpdateJoystickMenu ()
JoystickItems[line].type = discrete;
JoystickItems[line].a.intcvar = cvars[i];
JoystickItems[line].b.numvalues = 6.f;
JoystickItems[line].d.graycheck = NULL;
JoystickItems[line].e.values = JoyAxisMapNames;
line++;
}

View file

@ -35,8 +35,7 @@
#include <stdio.h>
#include "doomtype.h"
enum ESSType;
#include "v_video.h"
// PNG Writing --------------------------------------------------------------
@ -116,4 +115,4 @@ class FTexture;
FTexture *PNGTexture_CreateFromFile(PNGHandle *png, const FString &filename);
#endif
#endif

View file

@ -5230,16 +5230,13 @@ int DLevelScript::RunScript ()
case PCD_SETACTORSTATE:
{
const char *statename = FBehavior::StaticLookupString (STACK(2));
TArray<FName> statelist;
FState *state;
MakeStateNameList(statename, &statelist);
if (STACK(3) == 0)
{
if (activator != NULL)
{
state = activator->GetClass()->ActorInfo->FindState (statelist.Size(), &statelist[0], !!STACK(1));
state = activator->GetClass()->ActorInfo->FindStateByString (statename, !!STACK(1));
if (state != NULL)
{
activator->SetState (state);
@ -5259,7 +5256,7 @@ int DLevelScript::RunScript ()
while ( (actor = iterator.Next ()) )
{
state = actor->GetClass()->ActorInfo->FindState (statelist.Size(), &statelist[0], !!STACK(1));
state = actor->GetClass()->ActorInfo->FindStateByString (statename, !!STACK(1));
if (state != NULL)
{
actor->SetState (state);

View file

@ -4968,10 +4968,6 @@ int AActor::DoSpecialDamage (AActor *target, int damage)
int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype)
{
// If the actor does not have a corresponding death state, then it does not take damage.
// Note that DeathState matches every kind of damagetype, so if an actor has that, it can
// be hurt with any type of damage. Exception: Massacre damage always succeeds, because
// it needs to work.
FState *death;
if (flags5 & MF5_NODAMAGE)
@ -4985,7 +4981,7 @@ int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FN
// it needs to work.
// Always kill if there is a regular death state or no death states at all.
if (FindState (NAME_Death) != NULL || !HasSpecialDeathStates())
if (FindState (NAME_Death) != NULL || !HasSpecialDeathStates() || damagetype == NAME_Massacre)
{
return damage;
}
@ -5044,3 +5040,45 @@ void AActor::SetIdle()
if (idle == NULL) idle = SpawnState;
SetState(idle);
}
FDropItem *AActor::GetDropItems()
{
unsigned int index = GetClass()->Meta.GetMetaInt (ACMETA_DropItems) - 1;
if (index >= 0 && index < DropItemList.Size())
{
return DropItemList[index];
}
return NULL;
}
//----------------------------------------------------------------------------
//
// DropItem handling
//
//----------------------------------------------------------------------------
FDropItemPtrArray DropItemList;
void FreeDropItemChain(FDropItem *chain)
{
while (chain != NULL)
{
FDropItem *next = chain->Next;
delete chain;
chain = next;
}
}
FDropItemPtrArray::~FDropItemPtrArray()
{
for (unsigned int i = 0; i < Size(); ++i)
{
FreeDropItemChain ((*this)[i]);
}
}
int StoreDropItemChain(FDropItem *chain)
{
return DropItemList.Push (chain) + 1;
}

814
src/p_states.cpp Normal file
View file

@ -0,0 +1,814 @@
/*
** p_states.cpp
** state management
**
**---------------------------------------------------------------------------
** Copyright 1998-2008 Randy Heit
** Copyright 2006-2008 Christoph Oelckers
** 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.
**
** 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 "actor.h"
#include "farchive.h"
#include "templates.h"
#include "cmdlib.h"
#include "i_system.h"
#include "thingdef/thingdef.h"
// Each state is owned by an actor. Actors can own any number of
// states, but a single state cannot be owned by more than one
// actor. States are archived by recording the actor they belong
// to and the index into that actor's list of states.
// For NULL states, which aren't owned by any actor, the owner
// is recorded as AActor with the following state. AActor should
// never actually have this many states of its own, so this
// is (relatively) safe.
#define NULL_STATE_INDEX 127
TArray<FName> JumpParameters;
//==========================================================================
//
//
//==========================================================================
FArchive &operator<< (FArchive &arc, FState *&state)
{
const PClass *info;
if (arc.IsStoring ())
{
if (state == NULL)
{
arc.UserWriteClass (RUNTIME_CLASS(AActor));
arc.WriteCount (NULL_STATE_INDEX);
return arc;
}
info = FState::StaticFindStateOwner (state);
if (info != NULL)
{
arc.UserWriteClass (info);
arc.WriteCount ((DWORD)(state - info->ActorInfo->OwnedStates));
}
else
{
/* this was never working as intended.
I_Error ("Cannot find owner for state %p:\n"
"%s %c%c %3d [%p] -> %p", state,
sprites[state->sprite].name,
state->GetFrame() + 'A',
state->GetFullbright() ? '*' : ' ',
state->GetTics(),
state->GetAction(),
state->GetNextState());
*/
}
}
else
{
const PClass *info;
DWORD ofs;
arc.UserReadClass (info);
ofs = arc.ReadCount ();
if (ofs == NULL_STATE_INDEX && info == RUNTIME_CLASS(AActor))
{
state = NULL;
}
else if (info->ActorInfo != NULL)
{
state = info->ActorInfo->OwnedStates + ofs;
}
else
{
state = NULL;
}
}
return arc;
}
//==========================================================================
//
// Find the actor that a state belongs to.
//
//==========================================================================
const PClass *FState::StaticFindStateOwner (const FState *state)
{
for (unsigned int i = 0; i < PClass::m_RuntimeActors.Size(); ++i)
{
FActorInfo *info = PClass::m_RuntimeActors[i]->ActorInfo;
if (state >= info->OwnedStates &&
state < info->OwnedStates + info->NumOwnedStates)
{
return info->Class;
}
}
return NULL;
}
//==========================================================================
//
// Find the actor that a state belongs to, but restrict the search to
// the specified type and its ancestors.
//
//==========================================================================
const PClass *FState::StaticFindStateOwner (const FState *state, const FActorInfo *info)
{
while (info != NULL)
{
if (state >= info->OwnedStates &&
state < info->OwnedStates + info->NumOwnedStates)
{
return info->Class;
}
info = info->Class->ParentClass->ActorInfo;
}
return NULL;
}
//==========================================================================
//
//
//==========================================================================
FStateLabel *FStateLabels::FindLabel (FName label)
{
return const_cast<FStateLabel *>(BinarySearch<FStateLabel, FName> (Labels, NumLabels, &FStateLabel::Label, label));
}
void FStateLabels::Destroy ()
{
for(int i=0; i<NumLabels;i++)
{
if (Labels[i].Children != NULL)
{
Labels[i].Children->Destroy();
free (Labels[i].Children); // These are malloc'd, not new'd!
Labels[i].Children=NULL;
}
}
}
//===========================================================================
//
// HasStates
//
// Checks whether the actor has special death states.
//
//===========================================================================
bool AActor::HasSpecialDeathStates () const
{
const FActorInfo *info = GetClass()->ActorInfo;
if (info->StateList != NULL)
{
FStateLabel *slabel = info->StateList->FindLabel (NAME_Death);
if (slabel != NULL && slabel->Children != NULL)
{
for(int i=0;i<slabel->Children->NumLabels;i++)
{
if (slabel->Children->Labels[i].State != NULL) return true;
}
}
}
return false;
}
//==========================================================================
//
// Resolves a label parameter
//
//==========================================================================
FState *P_GetState(AActor *self, FState *CallingState, int offset)
{
if (offset == 0 || offset == INT_MIN)
{
return NULL; // 0 means 'no state'
}
else if (offset>0)
{
if (CallingState == NULL) return NULL;
return CallingState + offset;
}
else if (self != NULL)
{
FName *params = &JumpParameters[-offset];
FName classname = params[0];
const PClass *cls;
cls = classname==NAME_None? RUNTIME_TYPE(self) : PClass::FindClass(classname);
if (cls==NULL || cls->ActorInfo==NULL) return NULL; // shouldn't happen
int numnames = (int)params[1];
FState *jumpto = cls->ActorInfo->FindState(numnames, &params[2]);
if (jumpto == NULL)
{
const char *dot="";
Printf("Jump target '");
if (classname != NAME_None) Printf("%s::", classname.GetChars());
for (int i=0;i<numnames;i++)
{
Printf("%s%s", dot, params[2+i].GetChars());
dot = ".";
}
Printf("' not found in %s\n", self->GetClass()->TypeName.GetChars());
}
return jumpto;
}
else return NULL;
}
//==========================================================================
//
// Creates a list of names from a string. Dots are used as separator
//
//==========================================================================
TArray<FName> &MakeStateNameList(const char * fname)
{
static TArray<FName> namelist(3);
FName firstpart, secondpart;
char * c;
// Handle the old names for the existing death states
char * name = copystring(fname);
firstpart = strtok(name, ".");
switch (firstpart)
{
case NAME_Burn:
firstpart = NAME_Death;
secondpart = NAME_Fire;
break;
case NAME_Ice:
firstpart = NAME_Death;
secondpart = NAME_Ice;
break;
case NAME_Disintegrate:
firstpart = NAME_Death;
secondpart = NAME_Disintegrate;
break;
case NAME_XDeath:
firstpart = NAME_Death;
secondpart = NAME_Extreme;
break;
}
namelist.Clear();
namelist.Push(firstpart);
if (secondpart!=NAME_None) namelist.Push(secondpart);
while ((c = strtok(NULL, "."))!=NULL)
{
FName cc = c;
namelist.Push(cc);
}
delete [] name;
return namelist;
}
//===========================================================================
//
// FindState (multiple names version)
//
// Finds a state that matches as many of the supplied names as possible.
// A state with more names than those provided does not match.
// A state with fewer names can match if there are no states with the exact
// same number of names.
//
// The search proceeds like this. For the current class, keeping matching
// names until there are no more. If both the argument list and the state
// are out of names, it's an exact match, so return it. If the state still
// has names, ignore it. If the argument list still has names, remember it.
//
//===========================================================================
FState *FActorInfo::FindState (int numnames, FName *names, bool exact) const
{
FStateLabels *labels = StateList;
FState *best = NULL;
if (labels != NULL)
{
int count = 0;
FStateLabel *slabel = NULL;
FName label;
// Find the best-matching label for this class.
while (labels != NULL && count < numnames)
{
label = *names++;
slabel = labels->FindLabel (label);
if (slabel != NULL)
{
count++;
labels = slabel->Children;
best = slabel->State;
}
else
{
break;
}
}
if (count < numnames && exact) return NULL;
}
return best;
}
//==========================================================================
//
// Finds the state associated with the given string
//
//==========================================================================
FState *FActorInfo::FindStateByString(const char *name, bool exact)
{
TArray<FName> &namelist = MakeStateNameList(name);
return FindState(namelist.Size(), &namelist[0], exact);
}
//==========================================================================
//
// Search one list of state definitions for the given name
//
//==========================================================================
FStateDefine *FStateDefinitions::FindStateLabelInList(TArray<FStateDefine> & list, FName name, bool create)
{
for(unsigned i = 0; i<list.Size(); i++)
{
if (list[i].Label == name) return &list[i];
}
if (create)
{
FStateDefine def;
def.Label=name;
def.State=NULL;
def.DefineFlags = SDF_NEXT;
return &list[list.Push(def)];
}
return NULL;
}
//==========================================================================
//
// Finds the address of a state label given by name.
// Adds the state label if it doesn't exist
//
//==========================================================================
FStateDefine * FStateDefinitions::FindStateAddress(const char *name)
{
FStateDefine * statedef=NULL;
TArray<FName> &namelist = MakeStateNameList(name);
TArray<FStateDefine> * statelist = &StateLabels;
for(unsigned i=0;i<namelist.Size();i++)
{
statedef = FindStateLabelInList(*statelist, namelist[i], true);
statelist = &statedef->Children;
}
return statedef;
}
//==========================================================================
//
// Adds a new state tp the curremt list
//
//==========================================================================
void FStateDefinitions::AddState (const char *statename, FState *state, BYTE defflags)
{
FStateDefine *std = FindStateAddress(statename);
std->State = state;
std->DefineFlags = defflags;
}
//==========================================================================
//
// Finds the state associated with the given name
// returns NULL if none found
//
//==========================================================================
FState * FStateDefinitions::FindState(const char * name)
{
FStateDefine * statedef=NULL;
TArray<FName> &namelist = MakeStateNameList(name);
TArray<FStateDefine> * statelist = &StateLabels;
for(unsigned i=0;i<namelist.Size();i++)
{
statedef = FindStateLabelInList(*statelist, namelist[i], false);
if (statedef == NULL) return NULL;
statelist = &statedef->Children;
}
return statedef? statedef->State : NULL;
}
//==========================================================================
//
// Creates the final list of states from the state definitions
//
//==========================================================================
static int STACK_ARGS labelcmp(const void * a, const void * b)
{
FStateLabel * A = (FStateLabel *)a;
FStateLabel * B = (FStateLabel *)b;
return ((int)A->Label - (int)B->Label);
}
FStateLabels * FStateDefinitions::CreateStateLabelList(TArray<FStateDefine> & statelist)
{
// First delete all empty labels from the list
for (int i=statelist.Size()-1;i>=0;i--)
{
if (statelist[i].Label == NAME_None || (statelist[i].State == NULL && statelist[i].Children.Size() == 0))
{
statelist.Delete(i);
}
}
int count=statelist.Size();
if (count == 0) return NULL;
FStateLabels * list = (FStateLabels*)M_Malloc(sizeof(FStateLabels)+(count-1)*sizeof(FStateLabel));
list->NumLabels = count;
for (int i=0;i<count;i++)
{
list->Labels[i].Label = statelist[i].Label;
list->Labels[i].State = statelist[i].State;
list->Labels[i].Children = CreateStateLabelList(statelist[i].Children);
}
qsort(list->Labels, count, sizeof(FStateLabel), labelcmp);
return list;
}
//===========================================================================
//
// InstallStates
//
// Creates the actor's state list from the current definition
//
//===========================================================================
void FStateDefinitions::InstallStates(FActorInfo *info, AActor *defaults)
{
// First ensure we have a valid spawn state.
FState *state = FindState("Spawn");
if (state == NULL)
{
// A NULL spawn state will crash the engine so set it to something valid.
AddState("Spawn", GetDefault<AActor>()->SpawnState);
}
if (info->StateList != NULL)
{
info->StateList->Destroy();
M_Free(info->StateList);
}
info->StateList = CreateStateLabelList(StateLabels);
// Cache these states as member veriables.
defaults->SpawnState = info->FindState(NAME_Spawn);
defaults->SeeState = info->FindState(NAME_See);
// Melee and Missile states are manipulated by the scripted marines so they
// have to be stored locally
defaults->MeleeState = info->FindState(NAME_Melee);
defaults->MissileState = info->FindState(NAME_Missile);
}
//===========================================================================
//
// MakeStateDefines
//
// Creates a list of state definitions from an existing actor
// Used by Dehacked to modify an actor's state list
//
//===========================================================================
void FStateDefinitions::MakeStateList(const FStateLabels *list, TArray<FStateDefine> &dest)
{
dest.Clear();
if (list != NULL) for(int i=0;i<list->NumLabels;i++)
{
FStateDefine def;
def.Label = list->Labels[i].Label;
def.State = list->Labels[i].State;
dest.Push(def);
if (list->Labels[i].Children != NULL)
{
MakeStateList(list->Labels[i].Children, dest[dest.Size()-1].Children);
}
}
}
void FStateDefinitions::MakeStateDefines(const PClass *cls)
{
if (cls->ActorInfo && cls->ActorInfo->StateList)
{
MakeStateList(cls->ActorInfo->StateList, StateLabels);
}
else
{
ClearStateLabels();
}
}
//===========================================================================
//
// AddStateDefines
//
// Adds a list of states to the current definitions
//
//===========================================================================
void FStateDefinitions::AddStateDefines(const FStateLabels *list)
{
if (list != NULL) for(int i=0;i<list->NumLabels;i++)
{
if (list->Labels[i].Children == NULL)
{
if (!FindStateLabelInList(StateLabels, list->Labels[i].Label, false))
{
FStateDefine def;
def.Label = list->Labels[i].Label;
def.State = list->Labels[i].State;
StateLabels.Push(def);
}
}
}
}
//==========================================================================
//
// RetargetState(Pointer)s
//
// These functions are used when a goto follows one or more labels.
// Because multiple labels are permitted to occur consecutively with no
// intervening states, it is not enough to remember the last label defined
// and adjust it. So these functions search for all labels that point to
// the current position in the state array and give them a copy of the
// target string instead.
//
//==========================================================================
void FStateDefinitions::RetargetStatePointers (intptr_t count, const char *target, TArray<FStateDefine> & statelist)
{
for(unsigned i = 0;i<statelist.Size(); i++)
{
if (statelist[i].State == (FState*)count)
{
if (target == NULL)
{
statelist[i].State = NULL;
statelist[i].DefineFlags = SDF_STOP;
}
else
{
statelist[i].State = (FState *)copystring (target);
statelist[i].DefineFlags = SDF_LABEL;
}
}
if (statelist[i].Children.Size() > 0)
{
RetargetStatePointers(count, target, statelist[i].Children);
}
}
}
void FStateDefinitions::RetargetStates (intptr_t count, const char *target)
{
RetargetStatePointers(count, target, StateLabels);
}
//==========================================================================
//
// ResolveGotoLabel
//
// Resolves any strings being stored in a state's NextState field
//
//==========================================================================
FState *FStateDefinitions::ResolveGotoLabel (AActor *actor, const PClass *mytype, char *name)
{
const PClass *type=mytype;
FState *state;
char *namestart = name;
char *label, *offset, *pt;
int v;
// Check for classname
if ((pt = strstr (name, "::")) != NULL)
{
const char *classname = name;
*pt = '\0';
name = pt + 2;
// The classname may either be "Super" to identify this class's immediate
// superclass, or it may be the name of any class that this one derives from.
if (stricmp (classname, "Super") == 0)
{
type = type->ParentClass;
actor = GetDefaultByType (type);
}
else
{
// first check whether a state of the desired name exists
const PClass *stype = PClass::FindClass (classname);
if (stype == NULL)
{
I_Error ("%s is an unknown class.", classname);
}
if (!stype->IsDescendantOf (RUNTIME_CLASS(AActor)))
{
I_Error ("%s is not an actor class, so it has no states.", stype->TypeName.GetChars());
}
if (!stype->IsAncestorOf (type))
{
I_Error ("%s is not derived from %s so cannot access its states.",
type->TypeName.GetChars(), stype->TypeName.GetChars());
}
if (type != stype)
{
type = stype;
actor = GetDefaultByType (type);
}
}
}
label = name;
// Check for offset
offset = NULL;
if ((pt = strchr (name, '+')) != NULL)
{
*pt = '\0';
offset = pt + 1;
}
v = offset ? strtol (offset, NULL, 0) : 0;
// Get the state's address.
if (type==mytype) state = FindState (label);
else state = type->ActorInfo->FindStateByString(label, true);
if (state != NULL)
{
state += v;
}
else if (v != 0)
{
I_Error ("Attempt to get invalid state %s from actor %s.", label, type->TypeName.GetChars());
}
delete[] namestart; // free the allocated string buffer
return state;
}
//==========================================================================
//
// FixStatePointers
//
// Fixes an actor's default state pointers.
//
//==========================================================================
void FStateDefinitions::FixStatePointers (FActorInfo *actor, TArray<FStateDefine> & list)
{
for(unsigned i=0;i<list.Size(); i++)
{
size_t v=(size_t)list[i].State;
if (v >= 1 && v < 0x10000)
{
list[i].State = actor->OwnedStates + v - 1;
}
if (list[i].Children.Size() > 0) FixStatePointers(actor, list[i].Children);
}
}
//==========================================================================
//
// ResolveGotoLabels
//
// Resolves an actor's state pointers that were specified as jumps.
//
//==========================================================================
void FStateDefinitions::ResolveGotoLabels (FActorInfo *actor, AActor *defaults, TArray<FStateDefine> & list)
{
for(unsigned i=0;i<list.Size(); i++)
{
if (list[i].State != NULL && list[i].DefineFlags == SDF_LABEL)
{ // It's not a valid state, so it must be a label string. Resolve it.
list[i].State = ResolveGotoLabel (defaults, actor->Class, (char *)list[i].State);
list[i].DefineFlags = SDF_STATE;
}
if (list[i].Children.Size() > 0) ResolveGotoLabels(actor, defaults, list[i].Children);
}
}
//==========================================================================
//
// FinishStates
// copies a state block and fixes all state links using the current list of labels
//
//==========================================================================
int FStateDefinitions::FinishStates (FActorInfo *actor, AActor *defaults, TArray<FState> &StateArray)
{
static int c=0;
int count = StateArray.Size();
if (count > 0)
{
FState *realstates = new FState[count];
int i;
int currange;
memcpy(realstates, &StateArray[0], count*sizeof(FState));
actor->OwnedStates = realstates;
actor->NumOwnedStates = count;
// adjust the state pointers
// In the case new states are added these must be adjusted, too!
FixStatePointers (actor, StateLabels);
for(i = currange = 0; i < count; i++)
{
// resolve labels and jumps
switch(realstates[i].DefineFlags)
{
case SDF_STOP: // stop
realstates[i].NextState = NULL;
break;
case SDF_WAIT: // wait
realstates[i].NextState = &realstates[i];
break;
case SDF_NEXT: // next
realstates[i].NextState = (i < count-1 ? &realstates[i+1] : &realstates[0]);
break;
case SDF_INDEX: // loop
realstates[i].NextState = &realstates[(size_t)realstates[i].NextState-1];
break;
case SDF_LABEL:
realstates[i].NextState = ResolveGotoLabel (defaults, actor->Class, (char *)realstates[i].NextState);
break;
}
}
}
// Fix state pointers that are gotos
ResolveGotoLabels (actor, defaults, StateLabels);
return count;
}

View file

@ -80,47 +80,15 @@ void P_SpawnTeleportFog(fixed_t x, fixed_t y, fixed_t z, int spawnid)
{
fog = SpawnableThings[spawnid];
}
/* if (fog != NULL && level.info->teleportfog != NAME_None)
{
fog = PClass::FindClass(level.info->teleportfog);
}
*/
if (fog == NULL)
{
AActor *mo = Spawn ("TeleportFog", x, y, z + TELEFOGHEIGHT, ALLOW_REPLACE);
if (mo != NULL)
{
FState * state = NULL;
switch(gameinfo.gametype)
{
default:
case GAME_Doom:
state = mo->FindState(NAME_Doom);
break;
case GAME_Heretic:
state = mo->FindState(NAME_Heretic);
break;
case GAME_Hexen:
state = mo->FindState(NAME_Hexen);
break;
case GAME_Strife:
state = mo->FindState(NAME_Strife);
break;
}
if (state == NULL) state = mo->SpawnState; // allow execution of code pointers in the spawn state
mo->SetState(state);
}
}
else
{
AActor *mo = Spawn (fog, x, y, z, ALLOW_REPLACE);
if (mo != NULL) mo->SetState(mo->SpawnState);
if (mo != NULL) S_Sound(mo, CHAN_BODY, mo->SeeSound, 1.f, ATTN_NORM);
}
}

View file

@ -1124,6 +1124,7 @@ struct UDMFParser
{
default: // Shh, GCC
case GAME_Doom:
case GAME_Chex:
namespace_bits = Dm;
P_LoadTranslator("xlat/doom_base.txt");
break;

View file

@ -227,8 +227,8 @@ player_t::player_t()
momy(0),
centering(0),
turnticks(0),
oldbuttons(0),
attackdown(0),
oldbuttons(0),
health(0),
inventorytics(0),
CurrentPlayerClass(0),
@ -988,7 +988,7 @@ void APlayerPawn::GiveDefaultInventory ()
AddInventory (barmor);
// Now add the items from the DECORATE definition
FDropItem *di = GetDropItems(RUNTIME_TYPE(this));
FDropItem *di = GetDropItems();
while (di)
{

View file

@ -265,9 +265,7 @@ DWORD R_BlendForColormap (DWORD map)
void R_InitData ()
{
FTexture::InitGrayMap();
StartScreen->Progress();
TexMan.Init();
V_InitFonts();
StartScreen->Progress();

View file

@ -54,8 +54,8 @@ extern bool LocalKeyboardTurner; // [RH] The local player used the keyboard t
extern float WallTMapScale;
extern float WallTMapScale2;
extern int viewwidth;
extern int viewheight;
extern "C" int viewwidth;
extern "C" int viewheight;
extern int viewwindowx;
extern int viewwindowy;

View file

@ -43,6 +43,9 @@
#include "colormatcher.h"
#include "d_netinf.h"
#include "v_palette.h"
#include "sc_man.h"
#include "doomerrors.h"
#include "i_system.h"
#include "gi.h"
#include "stats.h"
@ -82,11 +85,23 @@ FRemapTable::FRemapTable(int count)
// the caller will do that next, if only by calling MakeIdentity().
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
FRemapTable::~FRemapTable()
{
Free();
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FRemapTable::Alloc(int count)
{
Remap = (BYTE *)M_Malloc(count*sizeof(*Remap) + count*sizeof(*Palette));
@ -96,6 +111,12 @@ void FRemapTable::Alloc(int count)
NumEntries = count;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FRemapTable::Free()
{
KillNative();
@ -108,6 +129,12 @@ void FRemapTable::Free()
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
FRemapTable::FRemapTable(const FRemapTable &o)
{
Remap = NULL;
@ -116,6 +143,12 @@ FRemapTable::FRemapTable(const FRemapTable &o)
operator= (o);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
FRemapTable &FRemapTable::operator=(const FRemapTable &o)
{
if (&o == this)
@ -134,6 +167,12 @@ FRemapTable &FRemapTable::operator=(const FRemapTable &o)
return *this;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
bool FRemapTable::operator==(const FRemapTable &o)
{
// Two translations are identical when they have the same amount of colors
@ -143,6 +182,12 @@ bool FRemapTable::operator==(const FRemapTable &o)
return !memcmp(o.Palette, Palette, NumEntries * sizeof(*Palette));
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FRemapTable::Serialize(FArchive &arc)
{
int n = NumEntries;
@ -167,6 +212,11 @@ void FRemapTable::Serialize(FArchive &arc)
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FRemapTable::MakeIdentity()
{
@ -186,6 +236,12 @@ void FRemapTable::MakeIdentity()
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
bool FRemapTable::IsIdentity() const
{
for (int j = 0; j < 256; ++j)
@ -198,6 +254,12 @@ bool FRemapTable::IsIdentity() const
return true;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FRemapTable::KillNative()
{
if (Native != NULL)
@ -207,6 +269,12 @@ void FRemapTable::KillNative()
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FRemapTable::UpdateNative()
{
if (Native != NULL)
@ -215,6 +283,12 @@ void FRemapTable::UpdateNative()
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
FNativePalette *FRemapTable::GetNative()
{
if (Native == NULL)
@ -224,6 +298,12 @@ FNativePalette *FRemapTable::GetNative()
return Native;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FRemapTable::AddIndexRange(int start, int end, int pal1, int pal2)
{
fixed_t palcol, palstep;
@ -250,6 +330,12 @@ void FRemapTable::AddIndexRange(int start, int end, int pal1, int pal2)
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FRemapTable::AddColorRange(int start, int end, int _r1,int _g1, int _b1, int _r2, int _g2, int _b2)
{
fixed_t r1 = _r1 << FRACBITS;
@ -304,6 +390,160 @@ void FRemapTable::AddColorRange(int start, int end, int _r1,int _g1, int _b1, in
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FRemapTable::AddToTranslation(const char * range)
{
int start,end;
FScanner sc;
sc.OpenMem("translation", range, int(strlen(range)));
sc.SetCMode(true);
try
{
sc.MustGetToken(TK_IntConst);
start = sc.Number;
sc.MustGetToken(':');
sc.MustGetToken(TK_IntConst);
end = sc.Number;
sc.MustGetToken('=');
if (!sc.CheckToken('['))
{
int pal1,pal2;
sc.MustGetToken(TK_IntConst);
pal1 = sc.Number;
sc.MustGetToken(':');
sc.MustGetToken(TK_IntConst);
pal2 = sc.Number;
AddIndexRange(start, end, pal1, pal2);
}
else
{
// translation using RGB values
int r1,g1,b1,r2,g2,b2;
sc.MustGetToken(TK_IntConst);
r1 = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
g1 = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
b1 = sc.Number;
sc.MustGetToken(']');
sc.MustGetToken(':');
sc.MustGetToken('[');
sc.MustGetToken(TK_IntConst);
r2 = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
g2 = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
b2 = sc.Number;
sc.MustGetToken(']');
AddColorRange(start, end, r1, g1, b1, r2, g2, b2);
}
}
catch (CRecoverableError &err)
{
Printf("Error in translation '%s':\n%s\n", err.GetMessage());
}
}
//----------------------------------------------------------------------------
//
// Stores a copy of this translation in the DECORATE translation table
//
//----------------------------------------------------------------------------
int FRemapTable::StoreTranslation()
{
unsigned int i;
for (i = 0; i < translationtables[TRANSLATION_Decorate].Size(); i++)
{
if (*this == *translationtables[TRANSLATION_Decorate][i])
{
// A duplicate of this translation already exists
return TRANSLATION(TRANSLATION_Decorate, i);
}
}
if (translationtables[TRANSLATION_Decorate].Size() >= MAX_DECORATE_TRANSLATIONS)
{
I_Error("Too many DECORATE translations");
}
FRemapTable *newtrans = new FRemapTable;
*newtrans = *this;
i = translationtables[TRANSLATION_Decorate].Push(newtrans);
return TRANSLATION(TRANSLATION_Decorate, i);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
TArray<PalEntry> BloodTranslationColors;
int CreateBloodTranslation(PalEntry color)
{
unsigned int i;
if (BloodTranslationColors.Size() == 0)
{
// Don't use the first slot.
translationtables[TRANSLATION_Blood].Push(NULL);
BloodTranslationColors.Push(0);
}
for (i = 1; i < BloodTranslationColors.Size(); i++)
{
if (color.r == BloodTranslationColors[i].r &&
color.g == BloodTranslationColors[i].g &&
color.b == BloodTranslationColors[i].b)
{
// A duplicate of this translation already exists
return i;
}
}
if (BloodTranslationColors.Size() >= MAX_DECORATE_TRANSLATIONS)
{
I_Error("Too many blood colors");
}
FRemapTable *trans = new FRemapTable;
for (i = 0; i < 256; i++)
{
int bright = MAX(MAX(GPalette.BaseColors[i].r, GPalette.BaseColors[i].g), GPalette.BaseColors[i].b);
PalEntry pe = PalEntry(color.r*bright/255, color.g*bright/255, color.b*bright/255);
int entry = ColorMatcher.Pick(pe.r, pe.g, pe.b);
trans->Palette[i] = pe;
trans->Remap[i] = entry;
}
translationtables[TRANSLATION_Blood].Push(trans);
return BloodTranslationColors.Push(color);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
FRemapTable *TranslationToTable(int translation)
{
@ -323,6 +563,12 @@ FRemapTable *TranslationToTable(int translation)
return slots->operator[](index);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
static void PushIdentityTable(int slot)
{
FRemapTable *table = new FRemapTable;
@ -330,11 +576,14 @@ static void PushIdentityTable(int slot)
translationtables[slot].Push(table);
}
//----------------------------------------------------------------------------
//
// R_InitTranslationTables
// Creates the translation tables to map the green color ramp to gray,
// brown, red. Assumes a given structure of the PLAYPAL.
//
//----------------------------------------------------------------------------
void R_InitTranslationTables ()
{
int i, j;
@ -508,6 +757,12 @@ void R_InitTranslationTables ()
}
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void R_DeinitTranslationTables()
{
for (int i = 0; i < NUM_TRANSLATION_TABLES; ++i)
@ -523,8 +778,13 @@ void R_DeinitTranslationTables()
}
}
//----------------------------------------------------------------------------
//
// [RH] Create a player's translation table based on a given mid-range color.
// [GRB] Split to 2 functions (because of player setup menu)
//
//----------------------------------------------------------------------------
static void SetRemap(FRemapTable *table, int i, float r, float g, float b)
{
int ir = clamp (int(r * 255.f), 0, 255);
@ -534,6 +794,12 @@ static void SetRemap(FRemapTable *table, int i, float r, float g, float b)
table->Palette[i] = PalEntry(255, ir, ig, ib);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
static void R_CreatePlayerTranslation (float h, float s, float v, FPlayerSkin *skin, FRemapTable *table, FRemapTable *alttable)
{
int i;
@ -675,6 +941,12 @@ static void R_CreatePlayerTranslation (float h, float s, float v, FPlayerSkin *s
table->UpdateNative();
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void R_BuildPlayerTranslation (int player)
{
float h, s, v;
@ -687,6 +959,12 @@ void R_BuildPlayerTranslation (int player)
translationtables[TRANSLATION_PlayersExtra][player]);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void R_GetPlayerTranslation (int color, FPlayerSkin *skin, FRemapTable *table)
{
float h, s, v;
@ -696,3 +974,4 @@ void R_GetPlayerTranslation (int color, FPlayerSkin *skin, FRemapTable *table)
R_CreatePlayerTranslation (h, s, v, skin, table, NULL);
}

View file

@ -38,6 +38,8 @@ struct FRemapTable
void Serialize(FArchive &ar);
void AddIndexRange(int start, int end, int pal1, int pal2);
void AddColorRange(int start, int end, int r1,int g1, int b1, int r2, int g2, int b2);
void AddToTranslation(const char * range);
int StoreTranslation();
BYTE *Remap; // For the software renderer
PalEntry *Palette; // The ideal palette this maps to
@ -99,6 +101,10 @@ void R_BuildPlayerTranslation (int player);
extern const BYTE IcePalette[16][3];
extern TArray<PalEntry> BloodTranslationColors;
int CreateBloodTranslation(PalEntry color);
#endif // __R_TRANSLATE_H

View file

@ -342,7 +342,10 @@ int S_PickReplacement (int refid)
unsigned int S_GetMSLength(FSoundID sound)
{
if (sound < 0 || sound >= S_sfx.Size()) return 0;
if ((unsigned int)sound >= S_sfx.Size())
{
return 0;
}
sfxinfo_t *sfx = &S_sfx[sound];

View file

@ -1478,6 +1478,7 @@ void S_RelinkSound (AActor *from, AActor *to)
}
else
{
chan->Actor = NULL;
S_StopChannel(chan);
}
}
@ -1778,7 +1779,7 @@ static void S_SetListener(SoundListener &listener, AActor *listenactor)
//
//==========================================================================
float S_GetRolloff(FRolloffInfo *rolloff, float distance)
float S_GetRolloff(FRolloffInfo *rolloff, float distance, bool logarithmic)
{
if (rolloff == NULL)
{
@ -1803,13 +1804,27 @@ float S_GetRolloff(FRolloffInfo *rolloff, float distance)
{
volume = S_SoundCurve[int(S_SoundCurveSize * (1 - volume))] / 127.f;
}
if (rolloff->RolloffType == ROLLOFF_Linear)
if (logarithmic)
{
return volume;
if (rolloff->RolloffType == ROLLOFF_Linear)
{
return volume;
}
else
{
return float((powf(10.f, volume) - 1.) / 9.);
}
}
else
{
return (powf(10.f, volume) - 1.f) / 9.f;
if (rolloff->RolloffType == ROLLOFF_Linear)
{
return float(log10(9. * volume + 1.));
}
else
{
return volume;
}
}
}

View file

@ -192,7 +192,7 @@ void FScanner::OpenFile (const char *name)
//
//==========================================================================
void FScanner::OpenMem (const char *name, char *buffer, int size)
void FScanner::OpenMem (const char *name, const char *buffer, int size)
{
Close ();
ScriptBuffer = FString(buffer, size);

View file

@ -20,7 +20,7 @@ public:
void Open(const char *lumpname);
void OpenFile(const char *filename);
void OpenMem(const char *name, char *buffer, int size);
void OpenMem(const char *name, const char *buffer, int size);
void OpenLumpNum(int lump);
void Close();

View file

@ -9,6 +9,7 @@
#include "v_video.h"
#include "d_main.h"
#include "d_event.h"
#include "d_gui.h"
#include "c_console.h"
#include "c_cvars.h"

View file

@ -54,6 +54,11 @@
#include "errors.h"
#include "version.h"
#include "w_wad.h"
#include "g_level.h"
#include "r_state.h"
#include "cmdlib.h"
#include "r_main.h"
#include "doomstat.h"
// MACROS ------------------------------------------------------------------
@ -180,7 +185,7 @@ static int DoomSpecificInfo (char *buffer, char *end)
if (!viewactive)
{
buffer += snprintf (buffer+p, size-p, "\n\nView not active.");
p += snprintf (buffer+p, size-p, "\n\nView not active.");
}
else
{

View file

@ -40,6 +40,7 @@
#include "SDL.h"
#include "doomtype.h"
#include "doomstat.h"
#include "version.h"
#include "doomdef.h"
#include "cmdlib.h"
@ -61,6 +62,9 @@
#include "hardware.h"
#include "gameconfigfile.h"
#include "m_fixed.h"
#include "g_level.h"
EXTERN_CVAR (String, language)
extern "C"

View file

@ -9,7 +9,7 @@
#include "v_video.h"
#include "v_pfx.h"
#include "stats.h"
#include "v_palette.h"
#include "sdlvideo.h"
#include <SDL.h>

View file

@ -42,6 +42,7 @@
#include "st_start.h"
#include "doomdef.h"
#include "i_system.h"
#include "c_cvars.h"
// MACROS ------------------------------------------------------------------

View file

@ -44,6 +44,7 @@ extern HWND Window;
#define FALSE 0
#define TRUE 1
#endif
#include <malloc.h>
#include "templates.h"
#include "fmodsound.h"
@ -2039,11 +2040,11 @@ float F_CALLBACK FMODSoundRenderer::RolloffCallback(FMOD_CHANNEL *channel, float
if (GRolloff != NULL)
{
return S_GetRolloff(GRolloff, distance * GDistScale);
return S_GetRolloff(GRolloff, distance * GDistScale, true);
}
else if (chan->getUserData((void **)&schan) == FMOD_OK && schan != NULL)
{
return S_GetRolloff(&schan->Rolloff, distance * schan->DistanceScale);
return S_GetRolloff(&schan->Rolloff, distance * schan->DistanceScale, true);
}
else
{

View file

@ -141,7 +141,7 @@ void I_InitSound ();
void I_ShutdownSound ();
void S_ChannelEnded(FISoundChannel *schan);
float S_GetRolloff(FRolloffInfo *rolloff, float distance);
float S_GetRolloff(FRolloffInfo *rolloff, float distance, bool logarithmic);
FISoundChannel *S_GetChannel(void *syschan);
extern ReverbContainer *DefaultEnvironments[26];

View file

@ -33,6 +33,7 @@
// HEADER FILES ------------------------------------------------------------
#include <malloc.h>
#include "i_musicinterns.h"
#include "templates.h"
#include "doomdef.h"

View file

@ -346,7 +346,7 @@ bool ST_Responder (event_t *ev)
static int counts[] = { countof(DoomCheats), countof(HereticCheats), countof(HexenCheats),
countof(StrifeCheats), countof(ChexCheats), countof(SpecialCheats) };
for (int i=0; i<countof(cheatlists); i++)
for (size_t i=0; i<countof(cheatlists); i++)
{
if (CheatCheckList(ev, cheatlists[i], counts[i])) return true;
}

View file

@ -3,5 +3,5 @@
// This file was automatically generated by the
// updaterevision tool. Do not edit by hand.
#define ZD_SVN_REVISION_STRING "1229"
#define ZD_SVN_REVISION_NUMBER 1229
#define ZD_SVN_REVISION_STRING "1246"
#define ZD_SVN_REVISION_NUMBER 1246

View file

@ -953,16 +953,11 @@ void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump)
}
static bool Check(char *& range, char c)
{
while (isspace(*range)) range++;
if (*range==c)
{
range++;
return true;
}
return false;
}
//==========================================================================
//
//
//
//==========================================================================
void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part)
{
@ -1056,46 +1051,7 @@ void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part)
do
{
sc.MustGetString();
char *range = sc.String;
int start,end;
start=strtol(range, &range, 10);
if (!Check(range, ':')) return;
end=strtol(range, &range, 10);
if (!Check(range, '=')) return;
if (!Check(range, '['))
{
int pal1,pal2;
pal1=strtol(range, &range, 10);
if (!Check(range, ':')) return;
pal2=strtol(range, &range, 10);
part.Translation->AddIndexRange(start, end, pal1, pal2);
}
else
{
// translation using RGB values
int r1,g1,b1,r2,g2,b2;
r1=strtol(range, &range, 10);
if (!Check(range, ',')) return;
g1=strtol(range, &range, 10);
if (!Check(range, ',')) return;
b1=strtol(range, &range, 10);
if (!Check(range, ']')) return;
if (!Check(range, ':')) return;
if (!Check(range, '[')) return;
r2=strtol(range, &range, 10);
if (!Check(range, ',')) return;
g2=strtol(range, &range, 10);
if (!Check(range, ',')) return;
b2=strtol(range, &range, 10);
if (!Check(range, ']')) return;
part.Translation->AddColorRange(start, end, r1, g1, b1, r2, g2, b2);
}
part.Translation->AddToTranslation(sc.String);
}
while (sc.CheckString(","));
}
@ -1161,6 +1117,11 @@ void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part)
}
}
//==========================================================================
//
// Constructor for text based multipatch definitions
//
//==========================================================================
FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype)
: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false)

View file

@ -330,33 +330,6 @@ void FTextureManager::ReplaceTexture (FTextureID picnum, FTexture *newtexture, b
}
}
//==========================================================================
//
// FTextureManager :: AddPatch
//
//==========================================================================
FTextureID FTextureManager::AddPatch (const char *patchname, int namespc, bool tryany)
{
if (patchname == NULL)
{
return FTextureID(-1);
}
FTextureID texnum = CheckForTexture (patchname, FTexture::TEX_MiscPatch, tryany);
if (texnum.Exists())
{
return texnum;
}
int lumpnum = Wads.CheckNumForName (patchname, namespc==ns_global? ns_graphics:namespc);
if (lumpnum < 0)
{
return FTextureID(-1);
}
return CreateTexture (lumpnum, FTexture::TEX_MiscPatch);
}
//==========================================================================
//
// FTextureManager :: AddGroup
@ -440,11 +413,6 @@ void FTextureManager::AddHiresTextures (int wadnum)
{
int amount = ListTextures(name, tlist);
if (amount == 0)
{
FTextureID oldtex = AddPatch(name);
if (oldtex.Exists()) tlist.Push(oldtex);
}
if (tlist.Size() == 0)
{
// A texture with this name does not yet exist
FTexture * newtex = FTexture::CreateTexture (firsttx, FTexture::TEX_Any);
@ -519,11 +487,6 @@ void FTextureManager::LoadTextureDefs(int wadnum, const char *lumpname)
tlist.Clear();
int amount = ListTextures(sc.String, tlist);
if (amount == 0)
{
FTextureID oldtex = AddPatch(sc.String);
if (oldtex.Exists()) tlist.Push(FTextureID(oldtex));
}
FName texname = sc.String;
sc.MustGetString();
@ -624,6 +587,10 @@ void FTextureManager::LoadTextureDefs(int wadnum, const char *lumpname)
{
ParseXTexture(sc, FTexture::TEX_Flat);
}
else if (sc.Compare("graphic"))
{
ParseXTexture(sc, FTexture::TEX_MiscPatch);
}
}
}
}
@ -843,6 +810,8 @@ void FTextureManager::SortTexturesByType(int start, int end)
void FTextureManager::Init()
{
FTexture::InitGrayMap();
int wadcnt = Wads.GetNumWads();
for(int i = 0; i< wadcnt; i++)
{

View file

@ -41,7 +41,7 @@
FWarpTexture::FWarpTexture (FTexture *source)
: SourcePic (source), Pixels (0), Spans (0), GenTime (0), Speed (1.f)
: GenTime (0), SourcePic (source), Pixels (0), Spans (0), Speed (1.f)
{
CopySize(source);
bNoDecals = source->bNoDecals;

View file

@ -103,8 +103,8 @@ PSymbolActionFunction *FindGlobalActionFunction(const char *name);
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
TArray<FState> &states, FExtraInfo &extra, EDefinitionType def, FScanner &sc);
static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
FExtraInfo &extra, EDefinitionType def, FScanner &sc);
static void ParseSpriteFrames (FActorInfo *info, TArray<FState> &states, FScanner &sc);
// PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -123,97 +123,6 @@ static const char *RenderStyles[] =
NULL
};
static const char *FlagNames1[] =
{
"*",
"Solid",
"*",
"NoSector",
"NoBlockmap",
"*",
"*",
"*",
"SpawnCeiling",
"NoGravity",
"*",
"*",
"*",
"*",
"*",
"*",
"*",
"*",
"Shadow",
"NoBlood",
"*",
"*",
"*",
"CountItem",
NULL
};
static const char *FlagNames2[] =
{
"LowGravity",
"WindThrust",
"*",
"*",
"*",
"FloorClip",
"SpawnFloat",
"NoTeleport",
"Ripper",
"Pushable",
"SlidesOnWalls",
"*",
"CanPass",
"CannotPush",
"ThruGhost",
"*",
"FireDamage",
"NoDamageThrust",
"Telestomp",
"FloatBob",
"*",
"ActivateImpact",
"CanPushWalls",
"ActivateMCross",
"ActivatePCross",
"*",
"*",
"*",
"*",
"*",
"*",
"Reflective",
NULL
};
static const char *FlagNames3[] =
{
"FloorHugger",
"CeilingHugger",
"*",
"*",
"*",
"*",
"DontSplash",
NULL
};
// CODE --------------------------------------------------------------------
//==========================================================================
@ -226,11 +135,11 @@ static const char *FlagNames3[] =
void ParseOldDecoration(FScanner &sc, EDefinitionType def)
{
TArray<FState> states;
Baggage bag;
FExtraInfo extra;
FActorInfo *info;
PClass *type;
PClass *parent;
FActorInfo *info;
FName typeName;
if (def == DEF_Pickup) parent = RUNTIME_CLASS(AFakeInventory);
@ -239,76 +148,17 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
sc.MustGetString();
typeName = FName(sc.String);
type = parent->CreateDerivedClass (typeName, parent->Size);
info = type->ActorInfo;
info->GameFilter = 0x80;
MakeStateDefines(parent->ActorInfo->StateList);
ResetBaggage(&bag, parent);
info = bag.Info = type->ActorInfo;
// There isn't a single WAD out there which uses game filters with old style
// decorations so this may as well be disabled. Without this option is is much
// easier to detect incorrect declarations
#if 0
sc.MustGetString ();
while (!sc.Compare ("{"))
{
if (sc.Compare ("Doom"))
{
info->GameFilter |= GAME_Doom;
}
else if (sc.Compare ("Heretic"))
{
info->GameFilter |= GAME_Heretic;
}
else if (sc.Compare ("Hexen"))
{
info->GameFilter |= GAME_Hexen;
}
else if (sc.Compare ("Raven"))
{
info->GameFilter |= GAME_Raven;
}
else if (sc.Compare ("Strife"))
{
info->GameFilter |= GAME_Strife;
}
else if (sc.Compare ("Any"))
{
info->GameFilter = GAME_Any;
}
else
{
if (def != DEF_Decoration || info->GameFilter != 0x80)
{
sc.ScriptError ("Unknown game type %s in %s", sc.String, typeName.GetChars());
}
else
{
// If this is a regular decoration (without preceding keyword) and no game
// filters defined this is more likely a general syntax error so output a
// more meaningful message.
sc.ScriptError ("Syntax error: Unknown identifier '%s'", typeName.GetChars());
}
}
sc.MustGetString ();
}
if (info->GameFilter == 0x80)
{
info->GameFilter = GAME_Any;
}
else
{
info->GameFilter &= ~0x80;
}
#else
info->GameFilter = GAME_Any;
sc.MustGetStringName("{");
#endif
states.Clear ();
memset (&extra, 0, sizeof(extra));
ParseInsideDecoration (info, (AActor *)(type->Defaults), states, extra, def, sc);
ParseInsideDecoration (bag, (AActor *)(type->Defaults), extra, def, sc);
info->NumOwnedStates = states.Size();
if (info->NumOwnedStates == 0)
bag.Info->NumOwnedStates = bag.StateArray.Size();
if (bag.Info->NumOwnedStates == 0)
{
sc.ScriptError ("%s does not define any animation frames", typeName.GetChars() );
}
@ -328,13 +178,13 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
if (extra.IceDeathEnd != 0)
{
// Make a copy of the final frozen frame for A_FreezeDeathChunks
FState icecopy = states[extra.IceDeathEnd-1];
states.Push (icecopy);
FState icecopy = bag.StateArray[extra.IceDeathEnd-1];
bag.StateArray.Push (icecopy);
info->NumOwnedStates += 1;
}
info->OwnedStates = new FState[info->NumOwnedStates];
memcpy (info->OwnedStates, &states[0], info->NumOwnedStates * sizeof(info->OwnedStates[0]));
memcpy (info->OwnedStates, &bag.StateArray[0], info->NumOwnedStates * sizeof(info->OwnedStates[0]));
if (info->NumOwnedStates == 1)
{
info->OwnedStates->Tics = -1;
@ -395,7 +245,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
if (extra.DeathHeight == 0) extra.DeathHeight = ((AActor*)(type->Defaults))->height;
info->Class->Meta.SetMetaFixed (AMETA_DeathHeight, extra.DeathHeight);
}
AddState("Death", &info->OwnedStates[extra.DeathStart]);
bag.statedef.AddState("Death", &info->OwnedStates[extra.DeathStart]);
}
// Burn states are the same as death states, except they can optionally terminate
@ -433,7 +283,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
if (extra.BurnHeight == 0) extra.BurnHeight = ((AActor*)(type->Defaults))->height;
type->Meta.SetMetaFixed (AMETA_BurnHeight, extra.BurnHeight);
AddState("Burn", &info->OwnedStates[extra.FireDeathStart]);
bag.statedef.AddState("Burn", &info->OwnedStates[extra.FireDeathStart]);
}
// Ice states are similar to burn and death, except their final frame enters
@ -454,11 +304,11 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
info->OwnedStates[i].Tics = 1;
info->OwnedStates[i].Misc1 = 0;
info->OwnedStates[i].SetAction(FindGlobalActionFunction("A_FreezeDeathChunks"));
AddState("Ice", &info->OwnedStates[extra.IceDeathStart]);
bag.statedef.AddState("Ice", &info->OwnedStates[extra.IceDeathStart]);
}
else if (extra.bGenericIceDeath)
{
AddState("Ice", RUNTIME_CLASS(AActor)->ActorInfo->FindState(NAME_GenericFreezeDeath));
bag.statedef.AddState("Ice", RUNTIME_CLASS(AActor)->ActorInfo->FindState(NAME_GenericFreezeDeath));
}
}
if (def == DEF_BreakableDecoration)
@ -469,8 +319,8 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
{
((AActor *)(type->Defaults))->flags |= MF_DROPOFF|MF_MISSILE;
}
AddState("Spawn", &info->OwnedStates[extra.SpawnStart]);
InstallStates (info, ((AActor *)(type->Defaults)));
bag.statedef.AddState("Spawn", &info->OwnedStates[extra.SpawnStart]);
bag.statedef.InstallStates (info, ((AActor *)(type->Defaults)));
}
//==========================================================================
@ -481,8 +331,8 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def)
//
//==========================================================================
static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
TArray<FState> &states, FExtraInfo &extra, EDefinitionType def, FScanner &sc)
static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
FExtraInfo &extra, EDefinitionType def, FScanner &sc)
{
AFakeInventory *const inv = static_cast<AFakeInventory *>(defaults);
char sprite[5] = "TNT1";
@ -497,7 +347,7 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
{
sc.ScriptError ("DoomEdNum must be in the range [-1,32767]");
}
info->DoomEdNum = (SWORD)sc.Number;
bag.Info->DoomEdNum = (SWORD)sc.Number;
}
else if (sc.Compare ("SpawnNum"))
{
@ -506,7 +356,7 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
{
sc.ScriptError ("SpawnNum must be in the range [0,255]");
}
info->SpawnID = (BYTE)sc.Number;
bag.Info->SpawnID = (BYTE)sc.Number;
}
else if (sc.Compare ("Sprite") || (
(def == DEF_BreakableDecoration || def == DEF_Projectile) &&
@ -531,31 +381,31 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
else if (sc.Compare ("Frames"))
{
sc.MustGetString ();
extra.SpawnStart = states.Size();
ParseSpriteFrames (info, states, sc);
extra.SpawnEnd = states.Size();
extra.SpawnStart = bag.StateArray.Size();
ParseSpriteFrames (bag.Info, bag.StateArray, sc);
extra.SpawnEnd = bag.StateArray.Size();
}
else if ((def == DEF_BreakableDecoration || def == DEF_Projectile) &&
sc.Compare ("DeathFrames"))
{
sc.MustGetString ();
extra.DeathStart = states.Size();
ParseSpriteFrames (info, states, sc);
extra.DeathEnd = states.Size();
extra.DeathStart = bag.StateArray.Size();
ParseSpriteFrames (bag.Info, bag.StateArray, sc);
extra.DeathEnd = bag.StateArray.Size();
}
else if (def == DEF_BreakableDecoration && sc.Compare ("IceDeathFrames"))
{
sc.MustGetString ();
extra.IceDeathStart = states.Size();
ParseSpriteFrames (info, states, sc);
extra.IceDeathEnd = states.Size();
extra.IceDeathStart = bag.StateArray.Size();
ParseSpriteFrames (bag.Info, bag.StateArray, sc);
extra.IceDeathEnd = bag.StateArray.Size();
}
else if (def == DEF_BreakableDecoration && sc.Compare ("BurnDeathFrames"))
{
sc.MustGetString ();
extra.FireDeathStart = states.Size();
ParseSpriteFrames (info, states, sc);
extra.FireDeathEnd = states.Size();
extra.FireDeathStart = bag.StateArray.Size();
ParseSpriteFrames (bag.Info, bag.StateArray, sc);
extra.FireDeathEnd = bag.StateArray.Size();
}
else if (def == DEF_BreakableDecoration && sc.Compare ("GenericIceDeath"))
{
@ -612,18 +462,18 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
else if (def == DEF_Projectile && sc.Compare ("ExplosionRadius"))
{
sc.MustGetNumber ();
info->Class->Meta.SetMetaInt(ACMETA_ExplosionRadius, sc.Number);
bag.Info->Class->Meta.SetMetaInt(ACMETA_ExplosionRadius, sc.Number);
extra.bExplosive = true;
}
else if (def == DEF_Projectile && sc.Compare ("ExplosionDamage"))
{
sc.MustGetNumber ();
info->Class->Meta.SetMetaInt(ACMETA_ExplosionDamage, sc.Number);
bag.Info->Class->Meta.SetMetaInt(ACMETA_ExplosionDamage, sc.Number);
extra.bExplosive = true;
}
else if (def == DEF_Projectile && sc.Compare ("DoNotHurtShooter"))
{
info->Class->Meta.SetMetaInt(ACMETA_DontHurtShooter, true);
bag.Info->Class->Meta.SetMetaInt(ACMETA_DontHurtShooter, true);
}
else if (def == DEF_Projectile && sc.Compare ("Damage"))
{
@ -708,7 +558,7 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
else if (def == DEF_Pickup && sc.Compare ("PickupMessage"))
{
sc.MustGetString ();
info->Class->Meta.SetMetaString(AIMETA_PickupMessage, sc.String);
bag.Info->Class->Meta.SetMetaString(AIMETA_PickupMessage, sc.String);
}
else if (def == DEF_Pickup && sc.Compare ("Respawns"))
{
@ -724,23 +574,7 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
}
else if (sc.String[0] != '*')
{
int bit = sc.MatchString (FlagNames1);
if (bit != -1)
{
defaults->flags |= 1 << bit;
}
else if ((bit = sc.MatchString (FlagNames2)) != -1)
{
defaults->flags2 |= 1 << bit;
}
else if ((bit = sc.MatchString (FlagNames3)) != -1)
{
defaults->flags3 |= 1 << bit;
}
else
{
sc.ScriptError (NULL);
}
HandleActorFlag(sc, bag, sc.String, NULL, '+');
}
else
{
@ -752,16 +586,16 @@ static void ParseInsideDecoration (FActorInfo *info, AActor *defaults,
unsigned int i;
int spr = GetSpriteIndex(sprite);
for (i = 0; i < states.Size(); ++i)
for (i = 0; i < bag.StateArray.Size(); ++i)
{
states[i].sprite = spr;
bag.StateArray[i].sprite = spr;
}
if (extra.DeathSprite[0] && extra.DeathEnd != 0)
{
int spr = GetSpriteIndex(extra.DeathSprite);
for (i = extra.DeathStart; i < extra.DeathEnd; ++i)
{
states[i].sprite = spr;
bag.StateArray[i].sprite = spr;
}
}
}

View file

@ -68,82 +68,6 @@
const PClass *QuestItemClasses[31];
//==========================================================================
//
// ActorConstDef
//
// Parses a constant definition.
//
//==========================================================================
void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls)
{
// 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 (sc, false, cls);
sc.MustGetToken(';');
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());
}
}
//==========================================================================
//
// ActorEnumDef
//
// Parses an enum definition.
//
//==========================================================================
void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls)
{
int currvalue = 0;
sc.MustGetToken('{');
while (!sc.CheckToken('}'))
{
sc.MustGetToken(TK_Identifier);
FName symname = sc.String;
if (sc.CheckToken('='))
{
int expr = ParseExpression(sc, false, cls);
currvalue = EvalExpressionI(expr, NULL, cls);
}
PSymbolConst *sym = new PSymbolConst;
sym->SymbolName = symname;
sym->SymbolType = SYM_Const;
sym->Value = currvalue;
if (symt->AddSymbol (sym) == NULL)
{
delete sym;
sc.ScriptError ("'%s' is already defined in class '%s'.",
symname.GetChars(), cls->TypeName.GetChars());
}
// This allows a comma after the last value but doesn't enforce it.
if (sc.CheckToken('}')) break;
sc.MustGetToken(',');
currvalue++;
}
sc.MustGetToken(';');
}
//==========================================================================
//
// ParseParameter
@ -245,8 +169,8 @@ int ParseParameter(FScanner &sc, PClass *cls, char type, bool constant)
// be checked here.
JumpParameters.Push(NAME_None);
}
TArray<FName> names;
MakeStateNameList(statestring, &names);
TArray<FName> &names = MakeStateNameList(statestring);
if (stype != NULL)
{
@ -409,7 +333,8 @@ static void ParseActionDef (FScanner &sc, PClass *cls)
if (hasdefaults)
{
sym->defaultparameterindex = StateParameters.Size();
for(int i = 0; i < DefaultParams.Size(); i++) StateParameters.Push(DefaultParams[i]);
for(unsigned int i = 0; i < DefaultParams.Size(); i++)
StateParameters.Push(DefaultParams[i]);
}
else
{
@ -428,123 +353,63 @@ static void ParseActionDef (FScanner &sc, PClass *cls)
// Starts a new actor definition
//
//==========================================================================
static FActorInfo *CreateNewActor(FScanner &sc, FActorInfo **parentc, Baggage *bag)
static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replaceName,
int DoomEdNum, bool native)
{
FName typeName;
const PClass *replacee = NULL;
int DoomEdNum = -1;
PClass *ti = NULL;
FActorInfo *info = NULL;
// Get actor name
sc.MustGetString();
char *colon = strchr(sc.String, ':');
if (colon != NULL)
{
*colon++ = 0;
}
/*
if (PClass::FindClass (sc.String) != NULL)
{
sc.ScriptError ("Actor %s is already defined.", sc.String);
}
*/
typeName = sc.String;
PClass *parent = RUNTIME_CLASS(AActor);
if (parentc)
{
*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)
{
if (colon[0] == 0)
{
sc.MustGetString ();
colon = sc.String;
}
}
if (colon != NULL)
{
parent = const_cast<PClass *> (PClass::FindClass (colon));
if (parent == NULL)
{
sc.ScriptError ("Parent type '%s' not found", colon);
}
else if (!parent->IsDescendantOf(RUNTIME_CLASS(AActor)))
{
sc.ScriptError ("Parent type '%s' is not an actor", colon);
}
else if (parent->ActorInfo == NULL)
{
sc.ScriptError ("uninitialized parent type '%s'", colon);
}
else
{
*parentc = parent->ActorInfo;
}
if (parentName != NAME_None)
{
parent = const_cast<PClass *> (PClass::FindClass (parentName));
if (parent == NULL)
{
I_Error ("Parent type '%s' not found in %s", parentName.GetChars(), typeName.GetChars());
}
else if (!parent->IsDescendantOf(RUNTIME_CLASS(AActor)))
{
I_Error ("Parent type '%s' is not an actor in %s", parentName.GetChars(), typeName.GetChars());
}
else if (parent->ActorInfo == NULL)
{
I_Error ("uninitialized parent type '%s' in %s", parentName.GetChars(), typeName.GetChars());
}
else sc.UnGet();
}
// Check for "replaces"
if (sc.CheckString ("replaces"))
if (replaceName != NAME_None)
{
// Get actor name
sc.MustGetString ();
replacee = PClass::FindClass (sc.String);
replacee = PClass::FindClass (replaceName);
if (replacee == NULL)
{
sc.ScriptError ("Replaced type '%s' not found", sc.String);
I_Error ("Replaced type '%s' not found in %s", replaceName.GetChars(), typeName.GetChars());
}
else if (replacee->ActorInfo == NULL)
{
sc.ScriptError ("Replaced type '%s' is not an actor", sc.String);
I_Error ("Replaced type '%s' is not an actor in %s", replaceName.GetChars(), typeName.GetChars());
}
}
// 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())
{
if (sc.Number>=-1 && sc.Number<32768) DoomEdNum = sc.Number;
else sc.ScriptError ("DoomEdNum must be in the range [-1,32767]");
}
if (sc.CheckString("native"))
if (native)
{
ti = (PClass*)PClass::FindClass(typeName);
if (ti == NULL)
{
sc.ScriptError("Unknown native class '%s'", typeName.GetChars());
I_Error("Unknown native class '%s'", typeName.GetChars());
}
else if (ti != RUNTIME_CLASS(AActor) && ti->ParentClass->NativeClass() != parent->NativeClass())
{
sc.ScriptError("Native class '%s' does not inherit from '%s'",
typeName.GetChars(),parent->TypeName.GetChars());
I_Error("Native class '%s' does not inherit from '%s'", typeName.GetChars(), parentName.GetChars());
}
else if (ti->ActorInfo != NULL)
{
sc.ScriptMessage("Redefinition of internal class '%s'", typeName.GetChars());
I_Error("Redefinition of internal class '%s'", typeName.GetChars());
}
ti->InitializeActorInfo();
info = ti->ActorInfo;
@ -555,11 +420,6 @@ static FActorInfo *CreateNewActor(FScanner &sc, FActorInfo **parentc, Baggage *b
info = ti->ActorInfo;
}
MakeStateDefines(parent->ActorInfo->StateList);
ResetBaggage (bag);
bag->Info = info;
info->DoomEdNum = -1;
if (parent->ActorInfo->DamageFactors != NULL)
{
@ -580,10 +440,98 @@ static FActorInfo *CreateNewActor(FScanner &sc, FActorInfo **parentc, Baggage *b
ti->ActorInfo->Replacee = replacee->ActorInfo;
}
info->DoomEdNum = DoomEdNum;
if (DoomEdNum > 0) info->DoomEdNum = DoomEdNum;
return info;
}
//==========================================================================
//
// Starts a new actor definition
//
//==========================================================================
static FActorInfo *ParseActorHeader(FScanner &sc, Baggage *bag)
{
FName typeName;
FName parentName;
FName replaceName;
bool native = false;
int DoomEdNum = -1;
// Get actor name
sc.MustGetString();
char *colon = strchr(sc.String, ':');
if (colon != NULL)
{
*colon++ = 0;
}
typeName = sc.String;
// 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)
{
if (colon[0] == 0)
{
sc.MustGetString ();
colon = sc.String;
}
}
if (colon == NULL)
{
sc.UnGet();
}
parentName = colon;
// Check for "replaces"
if (sc.CheckString ("replaces"))
{
// Get actor name
sc.MustGetString ();
replaceName = sc.String;
}
// 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())
{
if (sc.Number>=-1 && sc.Number<32768) DoomEdNum = sc.Number;
else sc.ScriptError ("DoomEdNum must be in the range [-1,32767]");
}
if (sc.CheckString("native"))
{
native = true;
}
try
{
FActorInfo *info = CreateNewActor(typeName, parentName, replaceName, DoomEdNum, native);
ResetBaggage (bag, info->Class->ParentClass);
bag->Info = info;
bag->Lumpnum = sc.LumpNum;
return info;
}
catch (CRecoverableError &err)
{
sc.ScriptError("%s", err.GetMessage());
return NULL;
}
}
//==========================================================================
//
// Reads an actor definition
@ -594,64 +542,41 @@ void ParseActor(FScanner &sc)
FActorInfo * info=NULL;
Baggage bag;
try
info = ParseActorHeader(sc, &bag);
sc.MustGetToken('{');
while (sc.MustGetAnyToken(), sc.TokenType != '}')
{
FActorInfo * parent;
info = CreateNewActor(sc, &parent, &bag);
sc.MustGetToken('{');
while (sc.MustGetAnyToken(), sc.TokenType != '}')
switch (sc.TokenType)
{
switch (sc.TokenType)
{
case TK_Action:
ParseActionDef (sc, info->Class);
break;
case TK_Action:
ParseActionDef (sc, info->Class);
break;
case TK_Const:
ParseConstant (sc, &info->Class->Symbols, info->Class);
break;
case TK_Const:
ParseConstant (sc, &info->Class->Symbols, info->Class);
break;
case TK_Enum:
ParseEnum (sc, &info->Class->Symbols, info->Class);
break;
case TK_Enum:
ParseEnum (sc, &info->Class->Symbols, info->Class);
break;
case TK_Identifier:
// other identifier related checks here
case TK_Projectile: // special case: both keyword and property name
ParseActorProperty(sc, bag);
break;
case TK_Identifier:
// other identifier related checks here
case TK_Projectile: // special case: both keyword and property name
ParseActorProperty(sc, bag);
break;
case '+':
case '-':
ParseActorFlag(sc, bag, sc.TokenType);
break;
default:
sc.ScriptError("Unexpected '%s' in definition of '%s'", sc.String, bag.Info->Class->TypeName.GetChars());
break;
}
case '+':
case '-':
ParseActorFlag(sc, bag, sc.TokenType);
break;
default:
sc.ScriptError("Unexpected '%s' in definition of '%s'", sc.String, bag.Info->Class->TypeName.GetChars());
break;
}
FinishActor(sc, info, bag);
}
catch(CRecoverableError & e)
{
throw e;
}
// I think this is better than a crash log.
#ifndef _DEBUG
catch (...)
{
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");
}
#endif
FinishActor(sc, info, bag);
sc.SetCMode (false);
}
@ -663,10 +588,33 @@ void ParseActor(FScanner &sc)
//
//==========================================================================
static int ResolvePointer(const PClass **pPtr, const PClass *owner, const PClass *destclass, const char *description)
{
fuglyname v;
v = *pPtr;
if (v != NAME_None && v.IsValidName())
{
*pPtr = PClass::FindClass(v);
if (!*pPtr)
{
Printf("Unknown %s '%s' in '%s'\n", description, v.GetChars(), owner->TypeName.GetChars());
return 1;
}
else if (!(*pPtr)->IsDescendantOf(destclass))
{
*pPtr = NULL;
Printf("Invalid %s '%s' in '%s'\n", description, v.GetChars(), owner->TypeName.GetChars());
return 1;
}
}
return 0;
}
void FinishThingdef()
{
unsigned int i;
bool isRuntimeActor=false;
int errorcount=0;
for (i = 0;i < PClass::m_Types.Size(); i++)
@ -676,13 +624,6 @@ void FinishThingdef()
// 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;
}
AActor *def = GetDefaultByType(ti);
if (!def)
@ -701,21 +642,7 @@ void FinishThingdef()
if (ti->IsDescendantOf(RUNTIME_CLASS(AInventory)))
{
AInventory * defaults=(AInventory *)def;
fuglyname v;
v = defaults->PickupFlash;
if (v != NAME_None && v.IsValidName())
{
defaults->PickupFlash = PClass::FindClass(v);
if (isRuntimeActor)
{
if (!defaults->PickupFlash)
{
Printf("Unknown pickup flash '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
}
}
errorcount += ResolvePointer(&defaults->PickupFlash, ti, RUNTIME_CLASS(AActor), "pickup flash");
}
if (ti->IsDescendantOf(RUNTIME_CLASS(APowerupGiver)) && ti != RUNTIME_CLASS(APowerupGiver))
@ -757,122 +684,47 @@ void FinishThingdef()
if (ti->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
AWeapon * defaults=(AWeapon *)def;
fuglyname v;
errorcount += ResolvePointer(&defaults->AmmoType1, ti, RUNTIME_CLASS(AAmmo), "ammo type");
errorcount += ResolvePointer(&defaults->AmmoType2, ti, RUNTIME_CLASS(AAmmo), "ammo type");
errorcount += ResolvePointer(&defaults->SisterWeaponType, ti, RUNTIME_CLASS(AWeapon), "sister weapon type");
v = defaults->AmmoType1;
if (v != NAME_None && v.IsValidName())
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)
{
defaults->AmmoType1 = PClass::FindClass(v);
if (isRuntimeActor)
// Do some consistency checks. If these states are undefined the weapon cannot work!
if (!ready)
{
if (!defaults->AmmoType1)
{
Printf("Unknown ammo type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
else if (defaults->AmmoType1->ParentClass != RUNTIME_CLASS(AAmmo))
{
Printf("Invalid ammo type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
Printf("Weapon %s doesn't define a ready state.\n", ti->TypeName.GetChars());
errorcount++;
}
if (!select)
{
Printf("Weapon %s doesn't define a select state.\n", ti->TypeName.GetChars());
errorcount++;
}
if (!deselect)
{
Printf("Weapon %s doesn't define a deselect state.\n", ti->TypeName.GetChars());
errorcount++;
}
if (!fire)
{
Printf("Weapon %s doesn't define a fire state.\n", ti->TypeName.GetChars());
errorcount++;
}
}
v = defaults->AmmoType2;
if (v != NAME_None && v.IsValidName())
{
defaults->AmmoType2 = PClass::FindClass(v);
if (isRuntimeActor)
{
if (!defaults->AmmoType2)
{
Printf("Unknown ammo type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
else if (defaults->AmmoType2->ParentClass != RUNTIME_CLASS(AAmmo))
{
Printf("Invalid ammo type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
}
}
v = defaults->SisterWeaponType;
if (v != NAME_None && v.IsValidName())
{
defaults->SisterWeaponType = PClass::FindClass(v);
if (isRuntimeActor)
{
if (!defaults->SisterWeaponType)
{
Printf("Unknown sister weapon type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
else if (!defaults->SisterWeaponType->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
Printf("Invalid sister weapon type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
}
}
if (isRuntimeActor)
{
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)
{
Printf("Weapon %s doesn't define a ready state.\n", ti->TypeName.GetChars());
errorcount++;
}
if (!select)
{
Printf("Weapon %s doesn't define a select state.\n", ti->TypeName.GetChars());
errorcount++;
}
if (!deselect)
{
Printf("Weapon %s doesn't define a deselect state.\n", ti->TypeName.GetChars());
errorcount++;
}
if (!fire)
{
Printf("Weapon %s doesn't define a fire state.\n", ti->TypeName.GetChars());
errorcount++;
}
}
}
}
// same for the weapon type of weapon pieces.
else if (ti->IsDescendantOf(RUNTIME_CLASS(AWeaponPiece)))
{
AWeaponPiece * defaults=(AWeaponPiece *)def;
fuglyname v;
v = defaults->WeaponClass;
if (v != NAME_None && v.IsValidName())
{
defaults->WeaponClass = PClass::FindClass(v);
if (!defaults->WeaponClass)
{
Printf("Unknown weapon type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
else if (!defaults->WeaponClass->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
Printf("Invalid weapon type '%s' in '%s'\n", v.GetChars(), ti->TypeName.GetChars());
errorcount++;
}
}
errorcount += ResolvePointer(&defaults->WeaponClass, ti, RUNTIME_CLASS(AWeapon), "weapon type");
}
}
if (errorcount > 0)

View file

@ -5,6 +5,24 @@
class FScanner;
//==========================================================================
//
// A flag descriptor
//
//==========================================================================
struct FFlagDef
{
int flagbit;
const char *name;
int structoffset;
};
FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2);
void HandleDeprecatedFlags(AActor *defaults, FActorInfo *info, bool set, int index);
//==========================================================================
//
// This class is for storing a name inside a const PClass* field without
@ -32,25 +50,72 @@ public:
//==========================================================================
//
// Dropitem list
// State parser
//
//==========================================================================
struct FDropItem
extern TArray<int> StateParameters;
extern TArray<FName> JumpParameters;
struct FStateLabels;
enum EStateDefineFlags
{
FName Name;
int probability;
int amount;
FDropItem * Next;
SDF_NEXT = 0,
SDF_STATE = 1,
SDF_STOP = 2,
SDF_WAIT = 3,
SDF_LABEL = 4,
SDF_INDEX = 5,
};
FDropItem *GetDropItems(const PClass * cls);
struct FStateDefine
{
FName Label;
TArray<FStateDefine> Children;
FState *State;
BYTE DefineFlags;
};
class FStateDefinitions
{
TArray<FStateDefine> StateLabels;
static FStateDefine *FindStateLabelInList(TArray<FStateDefine> &list, FName name, bool create);
static FStateLabels *CreateStateLabelList(TArray<FStateDefine> &statelist);
static void MakeStateList(const FStateLabels *list, TArray<FStateDefine> &dest);
static void RetargetStatePointers (intptr_t count, const char *target, TArray<FStateDefine> & statelist);
FStateDefine *FindStateAddress(const char *name);
FState *FindState(const char *name);
FState *ResolveGotoLabel (AActor *actor, const PClass *mytype, char *name);
static void FixStatePointers (FActorInfo *actor, TArray<FStateDefine> & list);
void ResolveGotoLabels (FActorInfo *actor, AActor *defaults, TArray<FStateDefine> & list);
public:
void ClearStateLabels()
{
StateLabels.Clear();
}
void AddState (const char * statename, FState * state, BYTE defflags = SDF_STATE);
void InstallStates(FActorInfo *info, AActor *defaults);
int FinishStates (FActorInfo *actor, AActor *defaults, TArray<FState> &StateArray);
void MakeStateDefines(const PClass *cls);
void AddStateDefines(const FStateLabels *list);
void RetargetStates (intptr_t count, const char *target);
};
//==========================================================================
//
// Extra info maintained while defining an actor.
//
//==========================================================================
struct FDropItem;
struct Baggage
{
@ -58,19 +123,23 @@ struct Baggage
bool DropItemSet;
bool StateSet;
int CurrentState;
int Lumpnum;
FStateDefinitions statedef;
TArray<FState> StateArray;
FDropItem *DropItemList;
};
inline void ResetBaggage (Baggage *bag)
inline void ResetBaggage (Baggage *bag, const PClass *stateclass)
{
bag->DropItemList = NULL;
bag->DropItemSet = false;
bag->CurrentState = 0;
bag->StateSet = false;
bag->StateArray.Clear();
bag->statedef.MakeStateDefines(stateclass);
}
//==========================================================================
//
// Action function lookup
@ -87,25 +156,8 @@ AFuncDesc * FindFunction(const char * string);
//==========================================================================
//
// State parser
//
//==========================================================================
extern TArray<int> StateParameters;
extern TArray<FName> JumpParameters;
void ClearStateLabels();
void AddState (const char * statename, FState * state);
FState * FindState(AActor * actor, const PClass * type, const char * name);
void InstallStates(FActorInfo *info, AActor *defaults);
void MakeStateDefines(const FStateLabels *list);
void AddStateDefines(const FStateLabels *list);
FState *P_GetState(AActor *self, FState *CallingState, int offset);
int FinishStates (FScanner &sc, FActorInfo *actor, AActor *defaults, Baggage &bag);
int ParseStates(FScanner &sc, FActorInfo *actor, AActor *defaults, Baggage &bag);
FState *CheckState(FScanner &sc, PClass *type);
//==========================================================================
@ -115,6 +167,7 @@ FState *CheckState(FScanner &sc, PClass *type);
//==========================================================================
void ParseActorProperty(FScanner &sc, Baggage &bag);
void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod);
void ParseActorFlag (FScanner &sc, Baggage &bag, int mod);
void FinishActor(FScanner &sc, FActorInfo *info, Baggage &bag);
@ -156,20 +209,91 @@ enum EDefinitionType
#if defined(_MSC_VER)
#pragma data_seg(".areg$u")
#pragma data_seg(".greg$u")
#pragma data_seg()
#define MSVC_ASEG __declspec(allocate(".areg$u"))
#define GCC_ASEG
#define MSVC_PSEG __declspec(allocate(".greg$u"))
#define GCC_PSEG
#else
#define MSVC_ASEG
#define GCC_ASEG __attribute__((section(AREG_SECTION)))
#define MSVC_PSEG
#define GCC_PSEG __attribute__((section(GREG_SECTION)))
#endif
union FPropParam
{
int i;
float f;
const char *s;
};
typedef void (*PropHandler)(AActor *defaults, Baggage &bag, FPropParam *params);
enum ECategory
{
CAT_PROPERTY, // Inheritable property
CAT_INFO // non-inheritable info (spawn ID, Doomednum, game filter, conversation ID)
};
struct FPropertyInfo
{
const char *name;
const char *params;
const PClass *cls;
PropHandler Handler;
int category;
};
FPropertyInfo *FindProperty(const char * string);
int MatchString (const char *in, const char **strings);
#define DEFINE_PROPERTY_BASE(name, paramlist, clas, cat) \
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, Baggage &bag, FPropParam *params); \
static FPropertyInfo Prop_##name##_##paramlist##_##clas = \
{ #name, #paramlist, RUNTIME_CLASS(A##clas), (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \
MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, Baggage &bag, FPropParam *params)
#define DEFINE_PREFIXED_PROPERTY_BASE(prefix, name, paramlist, clas, cat) \
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, Baggage &bag, FPropParam *params); \
static FPropertyInfo Prop_##name##_##paramlist##_##clas = \
{ #prefix"."#name, #paramlist, RUNTIME_CLASS(A##clas), (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \
MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, Baggage &bag, FPropParam *params)
#define DEFINE_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_PROPERTY)
#define DEFINE_INFO_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_INFO)
#define DEFINE_CLASS_PROPERTY(name, paramlist, clas) DEFINE_PREFIXED_PROPERTY_BASE(clas, name, paramlist, clas, CAT_PROPERTY)
#define DEFINE_CLASS_PROPERTY_PREFIX(prefix, name, paramlist, clas) DEFINE_PREFIXED_PROPERTY_BASE(prefix, name, paramlist, clas, CAT_PROPERTY)
#define PROP_PARM_COUNT (params[0].i)
#define PROP_STRING_PARM(var, no) \
const char *var = params[(no)+1].s;
#define PROP_INT_PARM(var, no) \
int var = params[(no)+1].i;
#define PROP_FLOAT_PARM(var, no) \
float var = params[(no)+1].f;
#define PROP_FIXED_PARM(var, no) \
fixed_t var = fixed_t(params[(no)+1].f * FRACUNIT);
#define PROP_COLOR_PARM(var, no) \
int var = params[(no)+1].i== 0? params[(no)+2].i : V_GetColor(NULL, params[(no)+2].s);
struct StateCallData
{
FState * State;
AActor * Item;
FState *State;
AActor *Item;
bool Result;
};

View file

@ -354,51 +354,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack)
}
//==========================================================================
//
// Resolves a label parameter
//
//==========================================================================
FState *P_GetState(AActor *self, FState *CallingState, int offset)
{
if (offset == 0 || offset == INT_MIN)
{
return NULL; // 0 means 'no state'
}
else if (offset>0)
{
if (CallingState == NULL) return NULL;
return CallingState + offset;
}
else if (self != NULL)
{
offset = -offset;
FName classname = JumpParameters[offset];
const PClass *cls;
cls = classname==NAME_None? RUNTIME_TYPE(self) : PClass::FindClass(classname);
if (cls==NULL || cls->ActorInfo==NULL) return NULL; // shouldn't happen
int numnames = (int)JumpParameters[offset+1];
FState *jumpto = cls->ActorInfo->FindState(numnames, &JumpParameters[offset+2]);
if (jumpto == NULL)
{
const char *dot="";
Printf("Jump target '");
if (classname != NAME_None) Printf("%s::", classname.GetChars());
for (int i=0;i<numnames;i++)
{
Printf("%s%s", dot, JumpParameters[offset+2+i].GetChars());
dot = ".";
}
Printf("' not found in %s\n", self->GetClass()->TypeName.GetChars());
}
return jumpto;
}
else return NULL;
}
//==========================================================================
//
// Do the state jump
@ -2236,3 +2191,49 @@ DEFINE_ACTION_FUNCTION(AActor, A_ResetReloadCounter)
AWeapon *weapon = self->player->ReadyWeapon;
weapon->ReloadCounter = 0;
}
//===========================================================================
//
// A_ChangeFlag
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag)
{
ACTION_PARAM_START(2);
ACTION_PARAM_STRING(flagname, 0);
ACTION_PARAM_BOOL(expression, 1);
const char *dot = strchr (flagname, '.');
FFlagDef *fd;
const PClass *cls = self->GetClass();
if (dot != NULL)
{
FString part1(flagname, dot-flagname);
fd = FindFlag (cls, part1, dot+1);
}
else
{
fd = FindFlag (cls, flagname, NULL);
}
if (fd != NULL)
{
if (fd->structoffset == -1)
{
HandleDeprecatedFlags(self, cls->ActorInfo, expression, fd->flagbit);
}
else
{
int * flagp = (int*) (((char*)self) + fd->structoffset);
if (expression) *flagp |= fd->flagbit;
else *flagp &= ~fd->flagbit;
}
}
else
{
Printf("Unknown flag '%s' in '%s'\n", flagname, cls->TypeName.GetChars());
}
}

View file

@ -47,9 +47,6 @@
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void ParseActor(FScanner &sc);
void ParseClass(FScanner &sc);
void ParseGlobalConst(FScanner &sc);
void ParseGlobalEnum(FScanner &sc);
void FinishThingdef();
void ParseOldDecoration(FScanner &sc, EDefinitionType def);

View file

@ -0,0 +1,559 @@
/*
** thingdef-parse.cpp
**
** Actor definitions - all parser related code
**
**---------------------------------------------------------------------------
** Copyright 2002-2007 Christoph Oelckers
** Copyright 2004-2007 Randy Heit
** 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 "doomtype.h"
#include "actor.h"
#include "a_pickups.h"
#include "sc_man.h"
#include "thingdef.h"
#include "a_morph.h"
#include "cmdlib.h"
#include "templates.h"
#include "v_palette.h"
#include "doomerrors.h"
#include "autosegs.h"
#include "i_system.h"
//==========================================================================
//
// ActorConstDef
//
// Parses a constant definition.
//
//==========================================================================
void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls)
{
// 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 (sc, false, cls);
sc.MustGetToken(';');
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());
}
}
//==========================================================================
//
// ActorEnumDef
//
// Parses an enum definition.
//
//==========================================================================
void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls)
{
int currvalue = 0;
sc.MustGetToken('{');
while (!sc.CheckToken('}'))
{
sc.MustGetToken(TK_Identifier);
FName symname = sc.String;
if (sc.CheckToken('='))
{
int expr = ParseExpression(sc, false, cls);
currvalue = EvalExpressionI(expr, NULL, cls);
}
PSymbolConst *sym = new PSymbolConst;
sym->SymbolName = symname;
sym->SymbolType = SYM_Const;
sym->Value = currvalue;
if (symt->AddSymbol (sym) == NULL)
{
delete sym;
sc.ScriptError ("'%s' is already defined in class '%s'.",
symname.GetChars(), cls->TypeName.GetChars());
}
// This allows a comma after the last value but doesn't enforce it.
if (sc.CheckToken('}')) break;
sc.MustGetToken(',');
currvalue++;
}
sc.MustGetToken(';');
}
//==========================================================================
//
// Parses a flag name
//
//==========================================================================
void ParseActorFlag (FScanner &sc, Baggage &bag, int mod)
{
sc.MustGetString ();
FString part1 = sc.String;
const char *part2 = NULL;
if (sc.CheckString ("."))
{
sc.MustGetString ();
part2 = sc.String;
}
HandleActorFlag(sc, bag, part1, part2, mod);
}
//==========================================================================
//
// Processes a flag. Also used by olddecorations.cpp
//
//==========================================================================
void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod)
{
FFlagDef *fd;
if ( (fd = FindFlag (bag.Info->Class, part1, part2)) )
{
AActor *defaults = (AActor*)bag.Info->Class->Defaults;
if (fd->structoffset == -1) // this is a deprecated flag that has been changed into a real property
{
HandleDeprecatedFlags(defaults, bag.Info, mod=='+', fd->flagbit);
}
else
{
DWORD * flagvar = (DWORD*) ((char*)defaults + fd->structoffset);
if (mod == '+')
{
*flagvar |= fd->flagbit;
}
else
{
*flagvar &= ~fd->flagbit;
}
}
}
else
{
if (part2 == NULL)
{
sc.ScriptError("\"%s\" is an unknown flag\n", part1);
}
else
{
sc.ScriptError("\"%s.%s\" is an unknown flag\n", part1, part2);
}
}
}
//==========================================================================
//
// [MH] parses a morph style expression
//
//==========================================================================
static int ParseMorphStyle (FScanner &sc)
{
static const char * morphstyles[]={
"MRF_ADDSTAMINA", "MRF_FULLHEALTH", "MRF_UNDOBYTOMEOFPOWER", "MRF_UNDOBYCHAOSDEVICE",
"MRF_FAILNOTELEFRAG", "MRF_FAILNOLAUGH", "MRF_WHENINVULNERABLE", "MRF_LOSEACTUALWEAPON",
"MRF_NEWTIDBEHAVIOUR", "MRF_UNDOBYDEATH", "MRF_UNDOBYDEATHFORCED", "MRF_UNDOBYDEATHSAVES", NULL};
static const int morphstyle_values[]={
MORPH_ADDSTAMINA, MORPH_FULLHEALTH, MORPH_UNDOBYTOMEOFPOWER, MORPH_UNDOBYCHAOSDEVICE,
MORPH_FAILNOTELEFRAG, MORPH_FAILNOLAUGH, MORPH_WHENINVULNERABLE, MORPH_LOSEACTUALWEAPON,
MORPH_NEWTIDBEHAVIOUR, MORPH_UNDOBYDEATH, MORPH_UNDOBYDEATHFORCED, MORPH_UNDOBYDEATHSAVES};
// May be given flags by number...
if (sc.CheckNumber())
{
sc.MustGetNumber();
return sc.Number;
}
// ... else should be flags by name.
// NOTE: Later this should be removed and a normal expression used.
// The current DECORATE parser can't handle this though.
bool gotparen = sc.CheckString("(");
int style = 0;
do
{
sc.MustGetString();
style |= morphstyle_values[sc.MustMatchString(morphstyles)];
}
while (sc.CheckString("|"));
if (gotparen)
{
sc.MustGetStringName(")");
}
return style;
}
//==========================================================================
//
// For getting a state address from the parent
// No attempts have been made to add new functionality here
// This is strictly for keeping compatibility with old WADs!
//
//==========================================================================
FState *CheckState(FScanner &sc, PClass *type)
{
int v=0;
if (sc.GetString() && !sc.Crossed)
{
if (sc.Compare("0")) return NULL;
else if (sc.Compare("PARENT"))
{
FState * state = NULL;
sc.MustGetString();
FActorInfo * info = type->ParentClass->ActorInfo;
if (info != NULL)
{
state = info->FindState(FName(sc.String));
}
if (sc.GetString ())
{
if (sc.Compare ("+"))
{
sc.MustGetNumber ();
v = sc.Number;
}
else
{
sc.UnGet ();
}
}
if (state == NULL && v==0) return NULL;
if (v!=0 && state==NULL)
{
sc.ScriptError("Attempt to get invalid state from actor %s\n", type->ParentClass->TypeName.GetChars());
return NULL;
}
state+=v;
return state;
}
else sc.ScriptError("Invalid state assignment");
}
return NULL;
}
//==========================================================================
//
// Parses an actor property's parameters and calls the handler
//
//==========================================================================
bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Baggage &bag)
{
static TArray<FPropParam> params;
static TArray<FString> strings;
params.Clear();
strings.Clear();
params.Reserve(1);
params[0].i = 0;
if (prop->params[0] != '0')
{
const char * p = prop->params;
bool nocomma;
bool optcomma;
while (*p)
{
FPropParam conv;
FPropParam pref;
nocomma = false;
conv.s = NULL;
pref.i = -1;
switch ((*p) & 223)
{
case 'X': // Expression in parentheses or number.
if (sc.CheckString ("("))
{
conv.i = 0x40000000 | ParseExpression (sc, false, bag.Info->Class);
params.Push(conv);
sc.MustGetStringName(")");
break;
}
// fall through
case 'I':
sc.MustGetNumber();
conv.i = sc.Number;
break;
case 'F':
sc.MustGetFloat();
conv.f = sc.Float;
break;
case 'Z': // an optional string. Does not allow any numerical value.
if (sc.CheckFloat())
{
nocomma = true;
sc.UnGet();
break;
}
// fall through
case 'S':
case 'T':
sc.MustGetString();
conv.s = strings[strings.Reserve(1)] = sc.String;
break;
case 'C':
if (sc.CheckNumber ())
{
int R, G, B;
R = clamp (sc.Number, 0, 255);
sc.CheckString (",");
sc.MustGetNumber ();
G = clamp (sc.Number, 0, 255);
sc.CheckString (",");
sc.MustGetNumber ();
B = clamp (sc.Number, 0, 255);
conv.i = MAKERGB(R, G, B);
pref.i = 0;
}
else
{
sc.MustGetString ();
conv.s = strings[strings.Reserve(1)] = sc.String;
pref.i = 1;
}
break;
case 'M': // special case. An expression-aware parser will not need this.
conv.i = ParseMorphStyle(sc);
break;
case 'L': // Either a number or a list of strings
if (sc.CheckNumber())
{
pref.i = 0;
conv.i = sc.Number;
}
else
{
pref.i = 1;
params.Push(pref);
params[0].i++;
do
{
sc.MustGetString ();
conv.s = strings[strings.Reserve(1)] = sc.String;
params.Push(conv);
params[0].i++;
}
while (sc.CheckString(","));
goto endofparm;
}
break;
default:
assert(false);
break;
}
if (pref.i != -1)
{
params.Push(pref);
params[0].i++;
}
params.Push(conv);
params[0].i++;
endofparm:
p++;
// Hack for some properties that have to allow comma less
// parameter lists for compatibility.
if ((optcomma = (*p == '_')))
p++;
if (nocomma)
{
continue;
}
else if (*p == 0)
{
break;
}
else if (*p >= 'a')
{
if (!sc.CheckString(","))
{
if (optcomma)
{
if (!sc.CheckFloat()) break;
else sc.UnGet();
}
else break;
}
}
else
{
if (!optcomma) sc.MustGetStringName(",");
else sc.CheckString(",");
}
}
}
// call the handler
try
{
prop->Handler(defaults, bag, &params[0]);
}
catch (CRecoverableError &error)
{
sc.ScriptError("%s", error.GetMessage());
}
return true;
}
//==========================================================================
//
// Parses an actor property
//
//==========================================================================
void ParseActorProperty(FScanner &sc, Baggage &bag)
{
static const char *statenames[] = {
"Spawn", "See", "Melee", "Missile", "Pain", "Death", "XDeath", "Burn",
"Ice", "Raise", "Crash", "Crush", "Wound", "Disintegrate", "Heal", NULL };
strlwr (sc.String);
FString propname = sc.String;
if (sc.CheckString ("."))
{
sc.MustGetString ();
propname += '.';
strlwr (sc.String);
propname += sc.String;
}
else
{
sc.UnGet ();
}
FPropertyInfo *prop = FindProperty(propname);
if (prop != NULL)
{
if (bag.Info->Class->IsDescendantOf(prop->cls))
{
ParsePropertyParams(sc, prop, (AActor *)bag.Info->Class->Defaults, bag);
}
else
{
sc.ScriptError("\"%s\" requires an actor of type \"%s\"\n", propname.GetChars(), prop->cls->TypeName.GetChars());
}
}
else if (!propname.CompareNoCase("States"))
{
if (!bag.StateSet) ParseStates(sc, bag.Info, (AActor *)bag.Info->Class->Defaults, bag);
else sc.ScriptError("Multiple state declarations not allowed");
bag.StateSet=true;
}
else if (MatchString(propname, statenames) != -1)
{
bag.statedef.AddState(propname, CheckState (sc, bag.Info->Class));
}
else
{
sc.ScriptError("\"%s\" is an unknown actor property\n", propname.GetChars());
}
}
//==========================================================================
//
// Finalizes an actor definition
//
//==========================================================================
void FinishActor(FScanner &sc, FActorInfo *info, Baggage &bag)
{
AActor *defaults = (AActor*)info->Class->Defaults;
try
{
bag.statedef.FinishStates (info, defaults, bag.StateArray);
}
catch (CRecoverableError &err)
{
sc.ScriptError(err.GetMessage());
}
bag.statedef.InstallStates (info, defaults);
bag.StateArray.Clear ();
if (bag.DropItemSet)
{
if (bag.DropItemList == NULL)
{
if (info->Class->Meta.GetMetaInt (ACMETA_DropItems) != 0)
{
info->Class->Meta.SetMetaInt (ACMETA_DropItems, 0);
}
}
else
{
info->Class->Meta.SetMetaInt (ACMETA_DropItems,
StoreDropItemChain(bag.DropItemList));
}
}
if (info->Class->IsDescendantOf (RUNTIME_CLASS(AInventory)))
{
defaults->flags |= MF_SPECIAL;
}
}

File diff suppressed because it is too large Load diff

View file

@ -57,9 +57,7 @@
#include "colormatcher.h"
TArray<int> StateParameters;
TArray<FName> JumpParameters;
static TArray<AFuncDesc> AFTable;
static TArray<FState> StateArray;
//==========================================================================
//
@ -132,261 +130,12 @@ PSymbolActionFunction *FindGlobalActionFunction(const char *name)
return NULL;
}
//==========================================================================
//
// Find a state address
//
//==========================================================================
struct FStateDefine
{
FName Label;
TArray<FStateDefine> Children;
FState *State;
};
static TArray<FStateDefine> StateLabels;
void ClearStateLabels()
{
StateLabels.Clear();
}
//==========================================================================
//
// Search one list of state definitions for the given name
//
//==========================================================================
static FStateDefine * FindStateLabelInList(TArray<FStateDefine> & list, FName name, bool create)
{
for(unsigned i = 0; i<list.Size(); i++)
{
if (list[i].Label == name) return &list[i];
}
if (create)
{
FStateDefine def;
def.Label=name;
def.State=NULL;
return &list[list.Push(def)];
}
return NULL;
}
//==========================================================================
//
// Finds the address of a state given by name.
// Adds the state if it doesn't exist
//
//==========================================================================
static FStateDefine * FindStateAddress(const char * name)
{
static TArray<FName> namelist(3);
FStateDefine * statedef=NULL;
MakeStateNameList(name, &namelist);
TArray<FStateDefine> * statelist = &StateLabels;
for(unsigned i=0;i<namelist.Size();i++)
{
statedef = FindStateLabelInList(*statelist, namelist[i], true);
statelist = &statedef->Children;
}
return statedef;
}
void AddState (const char * statename, FState * state)
{
FStateDefine * std = FindStateAddress(statename);
std->State = state;
}
//==========================================================================
//
// Finds the state associated with the given name
//
//==========================================================================
FState * FindState(AActor * actor, const PClass * type, const char * name)
{
static TArray<FName> namelist(3);
FStateDefine * statedef=NULL;
MakeStateNameList(name, &namelist);
TArray<FStateDefine> * statelist = &StateLabels;
for(unsigned i=0;i<namelist.Size();i++)
{
statedef = FindStateLabelInList(*statelist, namelist[i], false);
if (statedef == NULL) return NULL;
statelist = &statedef->Children;
}
return statedef? statedef->State : NULL;
}
//==========================================================================
//
// Finds the state associated with the given name
//
//==========================================================================
FState * FindStateInClass(AActor * actor, const PClass * type, const char * name)
{
static TArray<FName> namelist(3);
MakeStateNameList(name, &namelist);
FActorInfo * info = type->ActorInfo;
if (info) return info->FindState(namelist.Size(), &namelist[0], true);
return NULL;
}
//==========================================================================
//
// Checks if a state list is empty
// A list is empty if it doesn't contain any states and no children
// that contain any states
//
//==========================================================================
static bool IsStateListEmpty(TArray<FStateDefine> & statelist)
{
for(unsigned i=0;i<statelist.Size();i++)
{
if (statelist[i].State!=NULL || !IsStateListEmpty(statelist[i].Children)) return false;
}
return true;
}
//==========================================================================
//
// Creates the final list of states from the state definitions
//
//==========================================================================
static int STACK_ARGS labelcmp(const void * a, const void * b)
{
FStateLabel * A = (FStateLabel *)a;
FStateLabel * B = (FStateLabel *)b;
return ((int)A->Label - (int)B->Label);
}
static FStateLabels * CreateStateLabelList(TArray<FStateDefine> & statelist)
{
// First delete all empty labels from the list
for (int i=statelist.Size()-1;i>=0;i--)
{
if (statelist[i].Label == NAME_None || (statelist[i].State == NULL && statelist[i].Children.Size() == 0))
{
statelist.Delete(i);
}
}
int count=statelist.Size();
if (count == 0) return NULL;
FStateLabels * list = (FStateLabels*)M_Malloc(sizeof(FStateLabels)+(count-1)*sizeof(FStateLabel));
list->NumLabels = count;
for (int i=0;i<count;i++)
{
list->Labels[i].Label = statelist[i].Label;
list->Labels[i].State = statelist[i].State;
list->Labels[i].Children = CreateStateLabelList(statelist[i].Children);
}
qsort(list->Labels, count, sizeof(FStateLabel), labelcmp);
return list;
}
//===========================================================================
//
// InstallStates
//
// Creates the actor's state list from the current definition
//
//===========================================================================
void InstallStates(FActorInfo *info, AActor *defaults)
{
// First ensure we have a valid spawn state.
FState * state = FindState(defaults, info->Class, "Spawn");
if (state == NULL)
{
// A NULL spawn state will crash the engine so set it to something valid.
AddState("Spawn", GetDefault<AActor>()->SpawnState);
}
if (info->StateList != NULL)
{
info->StateList->Destroy();
M_Free(info->StateList);
}
info->StateList = CreateStateLabelList(StateLabels);
// Cache these states as member veriables.
defaults->SpawnState = info->FindState(NAME_Spawn);
defaults->SeeState = info->FindState(NAME_See);
// Melee and Missile states are manipulated by the scripted marines so they
// have to be stored locally
defaults->MeleeState = info->FindState(NAME_Melee);
defaults->MissileState = info->FindState(NAME_Missile);
}
//===========================================================================
//
// MakeStateDefines
//
// Creates a list of state definitions from an existing actor
// Used by Dehacked to modify an actor's state list
//
//===========================================================================
static void MakeStateList(const FStateLabels *list, TArray<FStateDefine> &dest)
{
dest.Clear();
if (list != NULL) for(int i=0;i<list->NumLabels;i++)
{
FStateDefine def;
def.Label = list->Labels[i].Label;
def.State = list->Labels[i].State;
dest.Push(def);
if (list->Labels[i].Children != NULL)
{
MakeStateList(list->Labels[i].Children, dest[dest.Size()-1].Children);
}
}
}
void MakeStateDefines(const FStateLabels *list)
{
MakeStateList(list, StateLabels);
}
void AddStateDefines(const FStateLabels *list)
{
if (list != NULL) for(int i=0;i<list->NumLabels;i++)
{
if (list->Labels[i].Children == NULL)
{
if (!FindStateLabelInList(StateLabels, list->Labels[i].Label, false))
{
FStateDefine def;
def.Label = list->Labels[i].Label;
def.State = list->Labels[i].State;
StateLabels.Push(def);
}
}
}
}
//==========================================================================
//
//***
// PrepareStateParameters
// creates an empty parameter list for a parameterized function call
//
@ -403,7 +152,7 @@ int PrepareStateParameters(FState * state, int numparams)
}
//==========================================================================
//
//***
// DoActionSpecials
// handles action specials as code pointers
//
@ -457,40 +206,7 @@ bool DoActionSpecials(FScanner &sc, FState & state, bool multistate, int * state
}
//==========================================================================
//
// RetargetState(Pointer)s
//
// These functions are used when a goto follows one or more labels.
// Because multiple labels are permitted to occur consecutively with no
// intervening states, it is not enough to remember the last label defined
// and adjust it. So these functions search for all labels that point to
// the current position in the state array and give them a copy of the
// target string instead.
//
//==========================================================================
static void RetargetStatePointers (intptr_t count, const char *target, TArray<FStateDefine> & statelist)
{
for(unsigned i = 0;i<statelist.Size(); i++)
{
if (statelist[i].State == (FState*)count)
{
statelist[i].State = target == NULL ? NULL : (FState *)copystring (target);
}
if (statelist[i].Children.Size() > 0)
{
RetargetStatePointers(count, target, statelist[i].Children);
}
}
}
static void RetargetStates (intptr_t count, const char *target)
{
RetargetStatePointers(count, target, StateLabels);
}
//==========================================================================
//
//***
// Reads a state label that may contain '.'s.
// processes a state block
//
@ -515,7 +231,7 @@ static FString ParseStateString(FScanner &sc)
}
//==========================================================================
//
//***
// ParseStates
// parses a state block
//
@ -551,10 +267,11 @@ do_goto:
if (laststate != NULL)
{ // Following a state definition: Modify it.
laststate->NextState = (FState*)copystring(statestring);
laststate->DefineFlags = SDF_LABEL;
}
else if (lastlabel >= 0)
{ // Following a label: Retarget it.
RetargetStates (count+1, statestring);
bag.statedef.RetargetStates (count+1, statestring);
}
else
{
@ -566,11 +283,11 @@ do_goto:
do_stop:
if (laststate!=NULL)
{
laststate->NextState=(FState*)-1;
laststate->DefineFlags = SDF_STOP;
}
else if (lastlabel >=0)
{
RetargetStates (count+1, NULL);
bag.statedef.RetargetStates (count+1, NULL);
}
else
{
@ -585,7 +302,7 @@ do_stop:
sc.ScriptError("%s before first state", sc.String);
continue;
}
laststate->NextState=(FState*)-2;
laststate->DefineFlags = SDF_WAIT;
}
else if (!statestring.CompareNoCase("LOOP"))
{
@ -595,6 +312,7 @@ do_stop:
continue;
}
laststate->NextState=(FState*)(lastlabel+1);
laststate->DefineFlags = SDF_INDEX;
}
else
{
@ -607,7 +325,7 @@ do_stop:
do
{
lastlabel = count;
AddState(statestring, (FState *) (count+1));
bag.statedef.AddState(statestring, (FState *) (count+1), SDF_INDEX);
statestring = ParseStateString(sc);
if (!statestring.CompareNoCase("GOTO"))
{
@ -788,7 +506,7 @@ do_stop:
}
sc.UnGet();
endofstate:
StateArray.Push(state);
bag.StateArray.Push(state);
while (*statestrp)
{
int frame=((*statestrp++)&223)-'A';
@ -800,10 +518,10 @@ endofstate:
}
state.Frame=(state.Frame&(SF_FULLBRIGHT))|frame;
StateArray.Push(state);
bag.StateArray.Push(state);
count++;
}
laststate=&StateArray[count];
laststate=&bag.StateArray[count];
count++;
}
}
@ -815,240 +533,3 @@ endofstate:
return count;
}
//==========================================================================
//
// ResolveGotoLabel
//
//==========================================================================
static FState *ResolveGotoLabel (FScanner &sc, AActor *actor, const PClass *mytype, char *name)
{
const PClass *type=mytype;
FState *state;
char *namestart = name;
char *label, *offset, *pt;
int v;
// Check for classname
if ((pt = strstr (name, "::")) != NULL)
{
const char *classname = name;
*pt = '\0';
name = pt + 2;
// The classname may either be "Super" to identify this class's immediate
// superclass, or it may be the name of any class that this one derives from.
if (stricmp (classname, "Super") == 0)
{
type = type->ParentClass;
actor = GetDefaultByType (type);
}
else
{
// first check whether a state of the desired name exists
const PClass *stype = PClass::FindClass (classname);
if (stype == NULL)
{
sc.ScriptError ("%s is an unknown class.", classname);
}
if (!stype->IsDescendantOf (RUNTIME_CLASS(AActor)))
{
sc.ScriptError ("%s is not an actor class, so it has no states.", stype->TypeName.GetChars());
}
if (!stype->IsAncestorOf (type))
{
sc.ScriptError ("%s is not derived from %s so cannot access its states.",
type->TypeName.GetChars(), stype->TypeName.GetChars());
}
if (type != stype)
{
type = stype;
actor = GetDefaultByType (type);
}
}
}
label = name;
// Check for offset
offset = NULL;
if ((pt = strchr (name, '+')) != NULL)
{
*pt = '\0';
offset = pt + 1;
}
v = offset ? strtol (offset, NULL, 0) : 0;
// Get the state's address.
if (type==mytype) state = FindState (actor, type, label);
else state = FindStateInClass (actor, type, label);
if (state != NULL)
{
state += v;
}
else if (v != 0)
{
sc.ScriptError ("Attempt to get invalid state %s from actor %s.", label, type->TypeName.GetChars());
}
delete[] namestart; // free the allocated string buffer
return state;
}
//==========================================================================
//
// FixStatePointers
//
// Fixes an actor's default state pointers.
//
//==========================================================================
static void FixStatePointers (FActorInfo *actor, TArray<FStateDefine> & list)
{
for(unsigned i=0;i<list.Size(); i++)
{
size_t v=(size_t)list[i].State;
if (v >= 1 && v < 0x10000)
{
list[i].State = actor->OwnedStates + v - 1;
}
if (list[i].Children.Size() > 0) FixStatePointers(actor, list[i].Children);
}
}
//==========================================================================
//
// FixStatePointersAgain
//
// Resolves an actor's state pointers that were specified as jumps.
//
//==========================================================================
static void FixStatePointersAgain (FScanner &sc, FActorInfo *actor, AActor *defaults, TArray<FStateDefine> & list)
{
for(unsigned i=0;i<list.Size(); i++)
{
if (list[i].State != NULL && FState::StaticFindStateOwner (list[i].State, actor) == NULL)
{ // It's not a valid state, so it must be a label string. Resolve it.
list[i].State = ResolveGotoLabel (sc, defaults, actor->Class, (char *)list[i].State);
}
if (list[i].Children.Size() > 0) FixStatePointersAgain(sc, actor, defaults, list[i].Children);
}
}
//==========================================================================
//
// FinishStates
// copies a state block and fixes all state links
//
//==========================================================================
int FinishStates (FScanner &sc, FActorInfo *actor, AActor *defaults, Baggage &bag)
{
static int c=0;
int count = StateArray.Size();
if (count > 0)
{
FState *realstates = new FState[count];
int i;
int currange;
memcpy(realstates, &StateArray[0], count*sizeof(FState));
actor->OwnedStates = realstates;
actor->NumOwnedStates = count;
// adjust the state pointers
// In the case new states are added these must be adjusted, too!
FixStatePointers (actor, StateLabels);
for(i = currange = 0; i < count; i++)
{
// resolve labels and jumps
switch((ptrdiff_t)realstates[i].NextState)
{
case 0: // next
realstates[i].NextState = (i < count-1 ? &realstates[i+1] : &realstates[0]);
break;
case -1: // stop
realstates[i].NextState = NULL;
break;
case -2: // wait
realstates[i].NextState = &realstates[i];
break;
default: // loop
if ((size_t)realstates[i].NextState < 0x10000)
{
realstates[i].NextState = &realstates[(size_t)realstates[i].NextState-1];
}
else // goto
{
realstates[i].NextState = ResolveGotoLabel (sc, defaults, bag.Info->Class, (char *)realstates[i].NextState);
}
}
}
}
StateArray.Clear ();
// Fix state pointers that are gotos
FixStatePointersAgain (sc, actor, defaults, StateLabels);
return count;
}
//==========================================================================
//
// For getting a state address from the parent
// No attempts have been made to add new functionality here
// This is strictly for keeping compatibility with old WADs!
//
//==========================================================================
FState *CheckState(FScanner &sc, PClass *type)
{
int v=0;
if (sc.GetString() && !sc.Crossed)
{
if (sc.Compare("0")) return NULL;
else if (sc.Compare("PARENT"))
{
FState * state = NULL;
sc.MustGetString();
FActorInfo * info = type->ParentClass->ActorInfo;
if (info != NULL)
{
state = info->FindState(FName(sc.String));
}
if (sc.GetString ())
{
if (sc.Compare ("+"))
{
sc.MustGetNumber ();
v = sc.Number;
}
else
{
sc.UnGet ();
}
}
if (state == NULL && v==0) return NULL;
if (v!=0 && state==NULL)
{
sc.ScriptError("Attempt to get invalid state from actor %s\n", type->ParentClass->TypeName.GetChars());
return NULL;
}
state+=v;
return state;
}
else sc.ScriptError("Invalid state assignment");
}
return NULL;
}

View file

@ -383,7 +383,7 @@ static int read_config_file(const char *name, bool ismain)
bank->tone[i].name = w[2];
bank->tone[i].fontbank = atoi(w[3]);
bank->tone[i].fontpreset = atoi(w[4]);
if (bank->tone[i].fontbank == 128 || (w[5][0] >= '0' && w[5][0] <= '9'))
if (words > 5 && (bank->tone[i].fontbank == 128 || (w[5][0] >= '0' && w[5][0] <= '9')))
{
bank->tone[i].fontnote = atoi(w[5]);
j = 6;

View file

@ -62,12 +62,7 @@ void FImageCollection::Add (const char **patchNames, int numPatches, int namespc
for (int i = 0; i < numPatches; ++i)
{
FTextureID picnum = TexMan.AddPatch (patchNames[i], namespc, true);
if (!picnum.Exists() && namespc != ns_sprites)
{
picnum = TexMan.AddPatch (patchNames[i], ns_sprites);
}
FTextureID picnum = TexMan.CheckForTexture(patchNames[i], namespc);
ImageMap[OldCount + i] = picnum;
}
}

View file

@ -1768,7 +1768,7 @@ void V_InitCustomFonts()
return;
wrong:
sc.ScriptError ("Invalid combination of properties in font '%s'", namebuffer);
sc.ScriptError ("Invalid combination of properties in font '%s'", namebuffer.GetChars());
}
//==========================================================================

View file

@ -114,13 +114,6 @@ enum
HUD_HorizCenter
};
// Screenshot buffer image data types
enum ESSType
{
SS_PAL,
SS_RGB,
SS_BGRA
};
class FFont;
struct FRemapTable;

View file

@ -763,7 +763,7 @@ void WI_drawLF ()
{
int y = WI_TITLEY;
FTexture * tex = wbs->lname0[0]? TexMan[wbs->lname0] : NULL;
FTexture * tex = wbs->LName0;
// draw <LevelName>
if (tex)
@ -825,7 +825,7 @@ void WI_drawEL ()
}
// draw <LevelName>
FTexture * tex = wbs->lname1[0]? TexMan[wbs->lname1] : NULL;
FTexture * tex = wbs->LName1;
if (tex)
{
screen->DrawTexture(tex, (SCREENWIDTH - tex->GetWidth() * CleanXfac) / 2, y * CleanYfac, DTA_CleanNoMove, true, TAG_DONE);

View file

@ -24,6 +24,8 @@
#include "doomdef.h"
class FTexture;
//
// INTERMISSION
// Structure passed e.g. to WI_Start(wb)
@ -50,8 +52,8 @@ struct wbstartstruct_t
FString current; // [RH] Name of map just finished
FString next; // next level, [RH] actual map name
FString lname0;
FString lname1;
FTexture *LName0;
FTexture *LName1;
int maxkills;
int maxitems;

View file

@ -13,7 +13,8 @@ const int SXF_ABSOLUTEMOMENTUM=8;
const int SXF_SETMASTER=16;
const int SXF_NOCHECKPOSITION = 32;
const int SXF_TELEFRAG=64;
const int SXF_TRANSFERAMBUSHFLAG=128;
// 128 was uses by Skulltag
const int SXF_TRANSFERAMBUSHFLAG=256;
// Flags for A_Chase
const int CHF_FASTCHASE = 1;
@ -28,3 +29,17 @@ const int LOF_NOSOUNDCHECK = 2;
const int LOF_DONTCHASEGOAL = 4;
const int LOF_NOSEESOUND = 8;
const int LOF_FULLVOLSEESOUND = 16;
// Morph constants
const int MRF_ADDSTAMINA = 1;
const int MRF_FULLHEALTH = 2;
const int MRF_UNDOBYTOMEOFPOWER = 4;
const int MRF_UNDOBYCHAOSDEVICE = 8;
const int MRF_FAILNOTELEFRAG = 16;
const int MRF_FAILNOLAUGH = 32;
const int MRF_WHENINVULNERABLE = 64;
const int MRF_LOSEACTUALWEAPON = 128;
const int MRF_NEWTIDBEHAVIOUR = 256;
const int MRF_UNDOBYDEATH = 512;
const int MRF_UNDOBYDEATHFORCED = 1024;
const int MRF_UNDOBYDEATHSAVES = 2048;

View file

@ -65,7 +65,7 @@ ACTOR LavaSmoke
+NOGRAVITY
+DONTSPLASH
RenderStyle Translucent
Alpha Default
DefaultAlpha
States
{
Spawn: