#define IS_GAMETYPE(NAME) \
(MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME)
- REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0")
+ REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0");
#define g_dm IS_GAMETYPE(DEATHMATCH)
- REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0")
+ REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0");
#define g_lms IS_GAMETYPE(LMS)
- REGISTER_GAMETYPE(_("Arena"),arena,g_arena,ARENA,"timelimit=20 pointlimit=10 leadlimit=0")
+ REGISTER_GAMETYPE(_("Arena"),arena,g_arena,ARENA,"timelimit=20 pointlimit=10 leadlimit=0");
#define g_arena IS_GAMETYPE(ARENA)
- REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0")
+ REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0");
#define g_race IS_GAMETYPE(RACE)
- REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1")
+ REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1");
#define g_cts IS_GAMETYPE(CTS)
- REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0")
+ REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0");
#define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH)
- REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0")
+ REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0");
#define g_ctf IS_GAMETYPE(CTF)
- REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0")
+ REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0");
#define g_ca IS_GAMETYPE(CA)
- REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0")
+ REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0");
#define g_domination IS_GAMETYPE(DOMINATION)
- REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0")
+ REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0");
#define g_keyhunt IS_GAMETYPE(KEYHUNT)
- REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20")
+ REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20");
#define g_assault IS_GAMETYPE(ASSAULT)
- REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"timelimit=20")
+ REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"timelimit=20");
#define g_onslaught IS_GAMETYPE(ONSLAUGHT)
- REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0")
+ REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0");
#define g_nexball IS_GAMETYPE(NEXBALL)
- REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0")
+ REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0");
#define g_freezetag IS_GAMETYPE(FREEZETAG)
- REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30")
+ REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30");
#define g_keepaway IS_GAMETYPE(KEEPAWAY)
- REGISTER_GAMETYPE(_("Invasion"),invasion,g_invasion,INVASION,"pointlimit=5")
++REGISTER_GAMETYPE(_("Invasion"),invasion,g_invasion,INVASION,"pointlimit=5");
+#define g_invasion IS_GAMETYPE(INVASION)
+
const float MAPINFO_FEATURE_WEAPONS = 1; // not defined for minstagib-only maps
const float MAPINFO_FEATURE_VEHICLES = 2;
const float MAPINFO_FEATURE_TURRETS = 4;
--- /dev/null
- ACCUMULATE_FUNCTION(RegisterMonsters, register_monsters_done)
+// monster requests
+#define MR_SETUP 1 // (SERVER) setup monster data
+#define MR_THINK 2 // (SERVER) logic to run every frame
+#define MR_DEATH 3 // (SERVER) called when monster dies
+#define MR_PRECACHE 4 // (BOTH) precaches models/sounds used by this monster
+#define MR_CONFIG 5 // (ALL)
+
+// functions:
+entity get_monsterinfo(float id);
+
+// special spawn flags
+const float MONSTER_RESPAWN_DEATHPOINT = 16; // re-spawn where we died
+const float MONSTER_TYPE_FLY = 32;
+const float MONSTER_TYPE_SWIM = 64;
+const float MONSTER_SIZE_BROKEN = 128; // TODO: remove when bad models are replaced
+const float MON_FLAG_SUPERMONSTER = 256; // incredibly powerful monster
+const float MON_FLAG_RANGED = 512; // monster shoots projectiles
+const float MON_FLAG_MELEE = 1024;
+
+// entity properties of monsterinfo:
+.float monsterid; // MON_...
+.string netname; // short name
+.string monster_name; // human readable name
+.float(float) monster_func; // m_...
+.string mdl; // currently a copy of the model
+.string model; // full name of model
+.float spawnflags;
+.vector mins, maxs; // monster hitbox size
+
+// other useful macros
+#define MON_ACTION(monstertype,mrequest) (get_monsterinfo(monstertype)).monster_func(mrequest)
+#define M_NAME(monstertype) (get_monsterinfo(monstertype)).monster_name
+
+// =====================
+// Monster Registration
+// =====================
+
+float m_null(float dummy);
+void register_monster(float id, float(float) func, float monsterflags, vector min_s, vector max_s, string modelname, string shortname, string mname);
+void register_monsters_done();
+
+const float MON_MAXCOUNT = 24;
+#define MON_FIRST 1
+float MON_COUNT;
+float MON_LAST;
+
+#define REGISTER_MONSTER_2(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \
+ float id; \
+ float func(float); \
+ void RegisterMonsters_##id() \
+ { \
+ MON_LAST = (id = MON_FIRST + MON_COUNT); \
+ ++MON_COUNT; \
+ register_monster(id,func,monsterflags,min_s,max_s,modelname,shortname,mname); \
+ } \
+ ACCUMULATE_FUNCTION(RegisterMonsters, RegisterMonsters_##id)
+#ifdef MENUQC
+#define REGISTER_MONSTER(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \
+ REGISTER_MONSTER_2(MON_##id,m_null,monsterflags,min_s,max_s,modelname,shortname,mname)
+#else
+#define REGISTER_MONSTER(id,func,monsterflags,min_s,max_s,modelname,shortname,mname) \
+ REGISTER_MONSTER_2(MON_##id,func,monsterflags,min_s,max_s,modelname,shortname,mname)
+#endif
+
+#define MON_DUPECHECK(dupecheck,cvar) \
+ #ifndef dupecheck \
+ #define dupecheck \
+ float cvar; \
+ #else \
+ #error DUPLICATE MONSTER CVAR: cvar \
+ #endif
+
+#define MON_ADD_CVAR(monster,name) \
+ MON_DUPECHECK(MON_CVAR_##monster##_##name, autocvar_g_monster_##monster##_##name)
+
+#define MON_CVAR(monster,name) autocvar_g_monster_##monster##_##name
+
+#include "all.qh"
+
+#undef MON_ADD_CVAR
+#undef REGISTER_MONSTER
++ACCUMULATE_FUNCTION(RegisterMonsters, register_monsters_done);