/*
** info.h
**
**---------------------------------------------------------------------------
** Copyright 1998-2005 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.
**
** 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.
**---------------------------------------------------------------------------
**
** Important restrictions because of the way FState is structured:
**
** The range of Frame is [0,63]. Since sprite naming conventions
** are even more restrictive than this, this isn't something to
** really worry about.
**
** The range of Tics is [-1,65534]. If Misc1 is important, then
** the range of Tics is reduced to [-1,254], because Misc1 also
** doubles as the high byte of the tic.
**
** The range of Misc1 is [-128,127] and Misc2's range is [0,255].
**
** When compiled with Visual C++, this struct is 16 bytes. With
** any other compiler (assuming a 32-bit architecture), it is 20 bytes.
** This is because with VC++, I can use the charizing operator to
** initialize the name array to exactly 4 chars. If GCC would
** compile something like char t = "PLYR"[0]; as char t = 'P'; then GCC
** could also use the 16-byte version. Unfortunately, GCC compiles it
** more like:
**
** char t;
** void initializer () {
**     static const char str[]="PLYR";
**     t = str[0];
** }
**
** While this does allow the use of a 16-byte FState, the additional
** code amounts to more than 4 bytes.
**
** If C++ would allow char name[4] = "PLYR"; without an error (as C does),
** I could just initialize the name as a regular string and be done with it.
*/

#ifndef __INFO_H__
#define __INFO_H__

#include "dobject.h"
#include "dthinker.h"
#include "farchive.h"
#include "doomdef.h"
#include "name.h"

const BYTE SF_STATEPARAM = 0x20;
const BYTE SF_FULLBRIGHT = 0x40;
const BYTE SF_BIGTIC	 = 0x80;


// All state parameters are stored in this array now.
// The first 2 parameters for each function call represent 
// the old misc1/misc2 values, even for non-weapons

extern TArray<int> StateParameters;

struct FState
{
	union
	{
#if _MSC_VER
		char name[4];
#else
		char name[8];	// 4 for name, 1 for '\0', 3 for pad
#endif
		int index;
	} sprite;
	BYTE		Tics;
	SBYTE		Misc1;
	BYTE		Misc2;
	BYTE		Frame;
	actionf_p	Action;
	FState		*NextState;

	inline int GetFrame() const
	{
		return Frame & ~(SF_FULLBRIGHT|SF_BIGTIC|SF_STATEPARAM);
	}
	inline int GetFullbright() const
	{
		return Frame & SF_FULLBRIGHT ? 0x10 /*RF_FULLBRIGHT*/ : 0;
	}
	inline int GetTics() const
	{
		int tics;
#ifdef WORDS_BIGENDIAN
		tics = Frame & SF_BIGTIC ? (Tics|((BYTE)Misc1<<8))-1 : Tics-1;
#else
		// Use some trickery to help the compiler create this without
		// using any jumps.
		tics = ((*(SDWORD *)&Tics) & ((*(SDWORD *)&Tics) < 0 ? 0xffff : 0xff)) - 1;
#endif
#if TICRATE == 35
		return tics;
#else
		return tics > 0 ? tics * TICRATE / 35 : tics;
#endif
	}
	inline int GetMisc1() const
	{
		return Frame & SF_BIGTIC ? 0 : Misc1;
	}
	inline int GetMisc2() const
	{
		return Misc2;
	}
	inline int GetMisc1_2() const
	{
		return ((*(DWORD *)&Tics) >> 8) & 0xFFFF;
	}
	inline void SetMisc1_2 (WORD val)
	{
		DWORD x = *(DWORD *)&Tics;
		x &= 0xFF0000FF;
		x |= (DWORD)val << 8;
		*(DWORD *)&Tics = x;
	}
	inline FState *GetNextState() const
	{
		return NextState;
	}
	inline actionf_p GetAction() const
	{
		return Action;
	}
	inline void SetFrame(BYTE frame)
	{
		Frame = (Frame & (SF_FULLBRIGHT|SF_BIGTIC)) | (frame-'A');
	}
};

// A truly awful hack to get to the state that called an action function
// without knowing whether it has been called from a weapon or actor.
extern FState * CallingState;
int CheckIndex(int paramsize, FState ** pcallstate=NULL);

// This is used when an inventory item's use state sequence is executed.
bool CallStateChain(AActor * actor, FState * State);


FArchive &operator<< (FArchive &arc, FState *&state);

#if _MSC_VER
#define _S__SPRITE_(spr) \
	{ {{(char)(#@spr>>24),(char)(#@spr>>16),(char)(#@spr>>8),(char)#@spr}}
#else
#define _S__SPRITE_(spr) \
	{ {{#spr}}
#endif

#define _S__FR_TIC_(spr,frm,tic,m1,m2,cmd,next) \
	_S__SPRITE_(spr), (tic+1)&255, m1|((tic+1)>>8), m2, (tic>254)?SF_BIGTIC|(frm):(frm), cmd, next }

#define S_NORMAL2(spr,frm,tic,cmd,next,m1,m2) \
	_S__FR_TIC_(spr, (frm) - 'A', tic, m1, m2, cmd, next)

#define S_BRIGHT2(spr,frm,tic,cmd,next,m1,m2) \
	_S__FR_TIC_(spr, (frm) - 'A' | SF_FULLBRIGHT, tic, m1, m2, cmd, next)

/* <winbase.h> #defines its own, completely unrelated S_NORMAL.
 * Since winbase.h will only be included in Win32-specific files that
 * don't define any actors, we can safely avoid defining it here.
 */

#ifndef S_NORMAL
#define S_NORMAL(spr,frm,tic,cmd,next)	S_NORMAL2(spr,frm,tic,cmd,next,0,0)
#endif
#define S_BRIGHT(spr,frm,tic,cmd,next)	S_BRIGHT2(spr,frm,tic,cmd,next,0,0)


#ifndef EGAMETYPE
#define EGAMETYPE
enum EGameType
{
	GAME_Any	 = 0,
	GAME_Doom	 = 1,
	GAME_Heretic = 2,
	GAME_Hexen	 = 4,
	GAME_Strife	 = 8,

	GAME_Raven		= GAME_Heretic|GAME_Hexen,
	GAME_DoomStrife	= GAME_Doom|GAME_Strife
};
#endif

enum
{
	ADEFTYPE_Byte		= 0x0000,
	ADEFTYPE_FixedMul	= 0x4000,		// one byte, multiplied by FRACUNIT
	ADEFTYPE_Word		= 0x8000,
	ADEFTYPE_Long		= 0xC000,
	ADEFTYPE_MASK		= 0xC000,

	// These first properties are always strings
	ADEF_SeeSound = 1,
	ADEF_AttackSound,
	ADEF_PainSound,
	ADEF_DeathSound,
	ADEF_ActiveSound,
	ADEF_UseSound,
	ADEF_Weapon_UpSound,
	ADEF_Weapon_ReadySound,
	ADEF_Inventory_PickupSound,
	ADEF_Tag,			// Used by Strife to name items
	ADEF_Weapon_AmmoType1,
	ADEF_Weapon_AmmoType2,
	ADEF_Weapon_SisterType,
	ADEF_Weapon_ProjectileType,
	ADEF_PowerupGiver_Powerup,
	ADEF_Inventory_Icon,
	ADEF_LastString = ADEF_Inventory_Icon,

	// The rest of the properties use their type field (upper 2 bits)
	ADEF_XScale,
	ADEF_YScale,
	ADEF_SpawnHealth,
	ADEF_ReactionTime,
	ADEF_PainChance,
	ADEF_Speed,
	ADEF_Radius,
	ADEF_Height,
	ADEF_Mass,
	ADEF_Damage,
	ADEF_DamageType,
	ADEF_Flags,			// Use these flags exactly
	ADEF_Flags2,		// "
	ADEF_Flags3,		// "
	ADEF_Flags4,		// "
	ADEF_FlagsSet,		// Or these flags with previous
	ADEF_Flags2Set,		// "
	ADEF_Flags3Set,		// "
	ADEF_Flags4Set,		// "
	ADEF_FlagsClear,	// Clear these flags from previous
	ADEF_Flags2Clear,	// "
	ADEF_Flags3Clear,	// "
	ADEF_Flags4Clear,	// "
	ADEF_Alpha,
	ADEF_RenderStyle,
	ADEF_RenderFlags,
	ADEF_Translation,
	ADEF_MinMissileChance,
	ADEF_MeleeRange,

	ADEF_SpawnState,
	ADEF_SeeState,
	ADEF_PainState,
	ADEF_MeleeState,
	ADEF_MissileState,
	ADEF_CrashState,
	ADEF_DeathState,
	ADEF_XDeathState,
	ADEF_BDeathState,
	ADEF_IDeathState,
	ADEF_EDeathState,
	ADEF_RaiseState,
	ADEF_WoundState,
	ADEF_YesState,
	ADEF_NoState,
	ADEF_GreetingsState,

	ADEF_User1,
	ADEF_User2,
	ADEF_User3,
	ADEF_User4,
	ADEF_User5,
	ADEF_User6,
	ADEF_User7,
	ADEF_User8,

	ADEF_StrifeType,	// Not really a property. Used to init StrifeTypes[] in p_conversation.h.
	ADEF_StrifeTeaserType,
	ADEF_StrifeTeaserType2,

	ADEF_Inventory_Amount,
	ADEF_Inventory_MaxAmount,
	ADEF_Inventory_DefMaxAmount,
	ADEF_Inventory_RespawnTics,
	ADEF_Inventory_FlagsSet,
	ADEF_Inventory_FlagsClear,

	ADEF_PuzzleItem_Number,	// Identifies the puzzle item for UsePuzzleItem

	ADEF_BasicArmorPickup_SavePercent,
	ADEF_BasicArmorPickup_SaveAmount,
	ADEF_BasicArmorBonus_SavePercent,
	ADEF_BasicArmorBonus_SaveAmount,
	ADEF_BasicArmorBonus_MaxSaveAmount,
	ADEF_HexenArmor_ArmorAmount,

	ADEF_Powerup_EffectTics,
	ADEF_Powerup_Color,
	ADEF_PowerupGiver_EffectTics,

	ADEF_Key_KeyNumber,
	ADEF_Key_AltKeyNumber,

	ADEF_Ammo_BackpackAmount,
	ADEF_Ammo_BackpackMaxAmount,

	ADEF_Weapon_Flags,
	ADEF_Weapon_AmmoGive1,
	ADEF_Weapon_AmmoGive2,
	ADEF_Weapon_AmmoUse1,
	ADEF_Weapon_AmmoUse2,
	ADEF_Weapon_Kickback,
	ADEF_Weapon_YAdjust,
	ADEF_Weapon_SelectionOrder,
	ADEF_Weapon_MoveCombatDist,
	ADEF_Weapon_UpState,
	ADEF_Weapon_DownState,
	ADEF_Weapon_ReadyState,
	ADEF_Weapon_AtkState,
	ADEF_Weapon_HoldAtkState,
	ADEF_Weapon_AltAtkState,
	ADEF_Weapon_AltHoldAtkState,
	ADEF_Weapon_FlashState,
	ADEF_Sigil_NumPieces,

	// The following are not properties but effect how the list is parsed
	ADEF_FirstCommand,
	ADEF_SkipSuper = ADEF_FirstCommand,	// Take defaults from AActor instead of superclass(es)

	ADEF_EOL = 0xED5E		// End Of List
};

struct FStateName
{
	FState *AActor::*State;
	name Name;
};

#if _MSC_VER
// nonstandard extension used : zero-sized array in struct/union
#pragma warning(disable:4200)
#endif

struct FActorInfo
{
	static void StaticInit ();
	static void StaticGameSet ();
	static void StaticSetActorNums ();
	static void StaticSpeedSet ();

	void BuildDefaults ();
	void ApplyDefaults (BYTE *defaults);
	void RegisterIDs ();

	TypeInfo *Class;
	FState *OwnedStates;
	BYTE *Defaults;
	FStateName *StateNames;
	int NumOwnedStates;
	BYTE GameFilter;
	BYTE SpawnID;
	SWORD DoomEdNum;

#if _MSC_VER
	// A 0-terminated list of default properties
	BYTE DefaultList[];
#else
	// A function to initialize the defaults for this actor
	void (*DefaultsConstructor)(); 
#endif
};

#if _MSC_VER
#pragma warning(default:4200)
#endif

class FDoomEdMap
{
public:
	const TypeInfo *FindType (int doomednum) const;
	void AddType (int doomednum, const TypeInfo *type);
	void DelType (int doomednum);
	void Empty ();

	static void DumpMapThings ();

private:
	enum { DOOMED_HASHSIZE = 256 };

	struct FDoomEdEntry
	{
		FDoomEdEntry *HashNext;
		const TypeInfo *Type;
		int DoomEdNum;
	};

	static FDoomEdEntry *DoomEdHash[DOOMED_HASHSIZE];
};

extern FDoomEdMap DoomEdMap;

#include "infomacros.h"

#endif	// __INFO_H__
