-std=gmqcc \
-O3 -flno \
-DSTUFFTO_ENABLED \
+ -DMONSTERS_EXTRA \
-Werror -fno-bail-on-werror -Wall \
-fftepp -fftepp-predefs -Wcpp -futf8 \
$(QCCFLAGS_WTFS) \
if(self.isplayermodel) // this checks if it's a player MODEL!
{
- CSQCPlayer_ModelAppearance_Apply(self.entnum == player_localnum + 1);
- CSQCPlayer_LOD_Apply();
+ if(isplayer)
+ {
+ CSQCPlayer_ModelAppearance_Apply(self.entnum == player_localnum + 1);
+ CSQCPlayer_LOD_Apply();
+ }
+
if(!isplayer)
{
skeleton_loadinfo(self);
void CSQCModel_Hook_PostUpdate(bool isnew, bool isplayer, bool islocalplayer)
{
// is it a player model? (shared state)
- self.isplayermodel = (substring(self.model, 0, 14) == "models/player/" || substring(self.model, 0, 17) == "models/ok_player/");
+ self.isplayermodel = (substring(self.model, 0, 14) == "models/player/" || substring(self.model, 0, 17) == "models/ok_player/" || substring(self.model, 0, 16) == "models/monsters/");
// save values set by server
if(self.isplayermodel)
// needs to be done so early because of the constants they create
CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
+ CALL_ACCUMULATED_FUNCTION(RegisterMonsters);
CALL_ACCUMULATED_FUNCTION(RegisterTurrets);
CALL_ACCUMULATED_FUNCTION(RegisterVehicles);
CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
float eventchase_current_distance;
float eventchase_running;
-float WantEventchase()
+bool WantEventchase()
{
if(autocvar_cl_orthoview)
return false;
{
if(hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0))
return true;
+ if(substring(self.model, 0, 16) == "models/monsters/")
+ return true;
if(autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(WepSet_GetFromStat() & WepSet_FromWeapon(WEP_PORTO)))
return true;
if(autocvar_cl_eventchase_death && (getstati(STAT_HEALTH) <= 0))
#include "../server/defs.qh"
#endif
-// player animation data for this model
-// each vector is as follows:
-// _x = startframe
-// _y = numframes
-// _z = framerate
-.vector anim_die1; // player dies
-.vector anim_die2; // player dies differently
-.vector anim_draw; // player pulls out a weapon
-.vector anim_duckwalk; // player walking while crouching
-.vector anim_duckjump; // player jumping from a crouch
-.vector anim_duckidle; // player idling while crouching
-.vector anim_idle; // player standing
-.vector anim_jump; // player jump
-.vector anim_pain1; // player flinches from pain
-.vector anim_pain2; // player flinches from pain, differently
-.vector anim_shoot; // player shoots
-.vector anim_taunt; // player taunts others (FIXME: no code references this)
-.vector anim_run; // player running forward
-.vector anim_runbackwards; // player running backward
-.vector anim_strafeleft; // player shuffling left quickly
-.vector anim_straferight; // player shuffling right quickly
-.vector anim_forwardright; // player running forward and right
-.vector anim_forwardleft; // player running forward and left
-.vector anim_backright; // player running backward and right
-.vector anim_backleft; // player running back and left
-.vector anim_melee; // player doing the melee action
-.vector anim_duck; // player doing the melee action
-.vector anim_duckwalkbackwards;
-.vector anim_duckwalkstrafeleft;
-.vector anim_duckwalkstraferight;
-.vector anim_duckwalkforwardright;
-.vector anim_duckwalkforwardleft;
-.vector anim_duckwalkbackright;
-.vector anim_duckwalkbackleft;
-.float animdecide_modelindex;
+bool monsters_animoverride(entity e)
+{
+ int monster_id = 0;
+ for(int i = MON_FIRST; i <= MON_LAST; ++i)
+ {
+ entity mon = get_monsterinfo(i);
+
+ //if(substring(e.model, 0, strlen(mon.model) - 4) == substring(mon.model, 0, strlen(mon.model) - 4))
+ if(e.model == mon.model)
+ {
+ monster_id = i;
+ break;
+ }
+ }
+
+ if(!monster_id) { return false; }
+
+ MON_ACTION(monster_id, MR_ANIM);
+
+ vector none = '0 0 0';
+ e.anim_duckwalk = e.anim_walk;
+ e.anim_duckjump = animfixfps(e, '5 1 10', none);
+ e.anim_duckidle = e.anim_idle;
+ e.anim_jump = animfixfps(e, '8 1 10', none);
+ e.anim_taunt = animfixfps(e, '12 1 0.33', none);
+ e.anim_runbackwards = e.anim_run;
+ e.anim_strafeleft = e.anim_run;
+ e.anim_straferight = e.anim_run;
+ e.anim_forwardright = e.anim_run;
+ e.anim_forwardleft = e.anim_run;
+ e.anim_backright = e.anim_run;
+ e.anim_backleft = e.anim_run;
+ e.anim_duckwalkbackwards = e.anim_walk;
+ e.anim_duckwalkstrafeleft = e.anim_walk;
+ e.anim_duckwalkstraferight = e.anim_walk;
+ e.anim_duckwalkforwardright = e.anim_walk;
+ e.anim_duckwalkforwardleft = e.anim_walk;
+ e.anim_duckwalkbackright = e.anim_walk;
+ e.anim_duckwalkbackleft = e.anim_walk;
+
+ // these anims ought to stay until stopped explicitly by weaponsystem
+ e.anim_shoot_z = 0.001;
+ e.anim_melee_z = 0.001;
+
+ return true;
+}
void animdecide_load_if_needed(entity e)
{
return;
e.animdecide_modelindex = e.modelindex;
+ if(substring(e.model, 0, 16) == "models/monsters/")
+ {
+ if(monsters_animoverride(e))
+ return;
+ }
+
vector none = '0 0 0';
e.anim_die1 = animfixfps(e, '0 1 0.5', none); // 2 seconds
e.anim_die2 = animfixfps(e, '1 1 0.5', none); // 2 seconds
.float anim_upper_implicit_action;
.float anim_upper_implicit_time;
+// player animation data for this model
+// each vector is as follows:
+// _x = startframe
+// _y = numframes
+// _z = framerate
+.vector anim_die1; // player dies
+.vector anim_die2; // player dies differently
+.vector anim_draw; // player pulls out a weapon
+.vector anim_duckwalk; // player walking while crouching
+.vector anim_duckjump; // player jumping from a crouch
+.vector anim_duckidle; // player idling while crouching
+.vector anim_idle; // player standing
+.vector anim_jump; // player jump
+.vector anim_pain1; // player flinches from pain
+.vector anim_pain2; // player flinches from pain, differently
+.vector anim_shoot; // player shoots
+.vector anim_taunt; // player taunts others (FIXME: no code references this)
+.vector anim_run; // player running forward
+.vector anim_runbackwards; // player running backward
+.vector anim_strafeleft; // player shuffling left quickly
+.vector anim_straferight; // player shuffling right quickly
+.vector anim_forwardright; // player running forward and right
+.vector anim_forwardleft; // player running forward and left
+.vector anim_backright; // player running backward and right
+.vector anim_backleft; // player running back and left
+.vector anim_melee; // player doing the melee action
+.vector anim_duck; // player doing the melee action
+.vector anim_duckwalkbackwards;
+.vector anim_duckwalkstrafeleft;
+.vector anim_duckwalkstraferight;
+.vector anim_duckwalkforwardright;
+.vector anim_duckwalkforwardleft;
+.vector anim_duckwalkbackright;
+.vector anim_duckwalkbackleft;
+.float animdecide_modelindex;
+
// explicit anim states (networked)
void animdecide_setstate(entity e, int newstate, float restart);
const int ANIMSTATE_DEAD1 = 1; // base frames: die1
#include "nmm.qc"
#include "ttt.qc"
-//#include "c4.qc"
+#include "c4.qc"
#include "pong.qc"
/**
MINIGAME(nmm, "Nine Men's Morris") \
MINIGAME(ttt, "Tic Tac Toe") \
MINIGAME(pong, "Pong") \
+ MINIGAME(c4, "Connect Four") \
/*empty line*/
-//MINIGAME(c4, "Connect Four") \
-
/**
* Set up automatic entity read/write functionality
* To ensure that everything is handled automatically, spawn on the server using msle_spawn
const float C4_TURN_TEAM2 = 0x0002;
const float C4_TURN_TEAM = 0x000f; // turn team mask
+const int C4_LET_CNT = 7;
+const int C4_NUM_CNT = 6;
+const int C4_WIN_CNT = 4;
+
+const int C4_MAX_TILES = 42;
+
+const int C4_TILE_SIZE = 8;
+
+const int C4_TEAMS = 2;
+
// send flags
const float C4_SF_PLAYERSCORE = MINIG_SF_CUSTOM; // send minigame_player scores (won matches)
entity left = piece;
entity topleft = piece;
entity botleft = piece;
- for(i = number; i < 6; ++i)
+ for(i = number; i < C4_NUM_CNT; ++i)
{
entity p = c4_find_piece(piece.owner,minigame_tile_buildname(letter, i));
if(p.team == piece.team)
botleft = p;
else break;
}
- for(i = letter, j = number; i >= 0, j < 6; --i, ++j)
+ for(i = letter, j = number; i >= 0, j < C4_NUM_CNT; --i, ++j)
{
entity p = c4_find_piece(piece.owner,minigame_tile_buildname(i, j));
if(p.team == piece.team)
else break;
}
- if(found >= 4)
+ if(found >= C4_WIN_CNT)
return true;
// right
found = 0;
- for(i = minigame_tile_letter(left.netname); i < 7; ++i)
+ for(i = minigame_tile_letter(left.netname); i < C4_LET_CNT; ++i)
{
if(c4_find_piece(piece.owner,minigame_tile_buildname(i, number)).team == piece.team)
++found;
else break;
}
- if(found >= 4)
+ if(found >= C4_WIN_CNT)
return true;
// diagright down
found = 0;
- for(i = minigame_tile_letter(topleft.netname), j = minigame_tile_number(topleft.netname); i < 7, j >= 0; ++i, --j)
+ for(i = minigame_tile_letter(topleft.netname), j = minigame_tile_number(topleft.netname); i < C4_LET_CNT, j >= 0; ++i, --j)
{
if(c4_find_piece(piece.owner,minigame_tile_buildname(i, j)).team == piece.team)
++found;
else break;
}
- if(found >= 4)
+ if(found >= C4_WIN_CNT)
return true;
// diagright up
found = 0;
- for(i = minigame_tile_letter(botleft.netname), j = minigame_tile_number(botleft.netname); i < 7, j < 6; ++i, ++j)
+ for(i = minigame_tile_letter(botleft.netname), j = minigame_tile_number(botleft.netname); i < C4_LET_CNT, j < C4_NUM_CNT; ++i, ++j)
{
if(c4_find_piece(piece.owner,minigame_tile_buildname(i, j)).team == piece.team)
++found;
else break;
}
- if(found >= 4)
+ if(found >= C4_WIN_CNT)
return true;
return false;
return 0;
float number = minigame_tile_number(tile);
float letter = minigame_tile_letter(tile);
- return 0 <= number && number < 6 && 0 <= letter && letter < 7;
+ return 0 <= number && number < C4_NUM_CNT && 0 <= letter && letter < C4_LET_CNT;
}
// make a move
minigame_server_sendflags(piece,MINIG_SF_ALL);
minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
minigame.c4_npieces++;
- minigame.c4_nexteam = minigame_next_team(player.team,2);
+ minigame.c4_nexteam = minigame_next_team(player.team,C4_TEAMS);
if ( c4_winning_piece(piece) )
{
player.minigame_flags++;
minigame_server_sendflags(player, C4_SF_PLAYERSCORE);
minigame.minigame_flags = C4_TURN_WIN | player.team;
}
- else if ( minigame.c4_npieces >= 21 )
+ else if ( minigame.c4_npieces >= C4_MAX_TILES )
minigame.minigame_flags = C4_TURN_DRAW;
else
minigame.minigame_flags = C4_TURN_PLACE | minigame.c4_nexteam;
// required function, handle server side events
-float minigame_event_c4(entity minigame, string event, ...)
+float c4_server_event(entity minigame, string event, ...)
{
switch(event)
{
float pl_num = minigame_count_players(minigame);
// Don't allow more than 2 players
- if(pl_num >= 2) { return false; }
+ if(pl_num >= C4_TEAMS) { return false; }
// Get the right team
if(minigame.minigame_players)
- return minigame_next_team(minigame.minigame_players.team, 2);
+ return minigame_next_team(minigame.minigame_players.team, C4_TEAMS);
// Team 1 by default
return 1;
.float c4_checkwin; // Used to optimize checks to display a win
// Required function, draw the game board
-void minigame_hud_board_c4(vector pos, vector mySize)
+void c4_hud_board(vector pos, vector mySize)
{
minigame_hud_fitsqare(pos, mySize);
c4_boardpos = pos;
minigame_hud_simpleboard(pos,mySize,minigame_texture("c4/board"));
- vector tile_size = minigame_hud_denormalize_size(('1 0 0' / 6) + ('0 1 0' / 6),pos,mySize);
+ vector tile_size = minigame_hud_denormalize_size('1 1 0' / C4_TILE_SIZE,pos,mySize);
vector tile_pos;
if ( (active_minigame.minigame_flags & C4_TURN_TEAM) == minigame_self.team )
if ( c4_valid_tile(c4_curr_pos) )
{
- tile_pos = minigame_tile_pos(c4_curr_pos,6,7);
+ tile_pos = minigame_tile_pos(c4_curr_pos,C4_NUM_CNT,C4_LET_CNT);
tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
minigame_drawpic_centered( tile_pos,
minigame_texture(strcat("c4/piece",ftos(minigame_self.team))),
{
if ( e.classname == "minigame_board_piece" )
{
- tile_pos = minigame_tile_pos(e.netname,6,7);
+ tile_pos = minigame_tile_pos(e.netname,C4_NUM_CNT,C4_LET_CNT);
tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
if ( active_minigame.minigame_flags & C4_TURN_WIN )
// Required function, draw the game status panel
-void minigame_hud_status_c4(vector pos, vector mySize)
+void c4_hud_status(vector pos, vector mySize)
{
HUD_Panel_DrawBg(1);
vector ts;
if ( e.team == 2 )
mypos_y += player_fontsize_y + ts_y;
minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
- (e.minigame_playerslot ? GetPlayerName(e.minigame_playerslot-1) : _("AI")),
+ GetPlayerName(e.minigame_playerslot-1),
player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
mypos_y += player_fontsize_y;
{
int i;
int end = 0;
- for(i = 6; i >= 0; --i)
+ for(i = C4_NUM_CNT; i >= 0; --i)
{
if(c4_find_piece(minigame,minigame_tile_buildname(minigame_tile_letter(s), i - 1)).team)
{
}
// Required function, handle client events
-float minigame_event_c4(entity minigame, string event, ...)
+float c4_client_event(entity minigame, string event, ...)
{
switch(event)
{
if ( ! c4_curr_pos )
c4_set_curr_pos(c4_get_lowest_tile(minigame, "a3"));
else
- c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,1,0,6,7)));
+ c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,1,0,C4_NUM_CNT,C4_LET_CNT)));
return true;
case K_LEFTARROW:
case K_KP_LEFTARROW:
if ( ! c4_curr_pos )
c4_set_curr_pos(c4_get_lowest_tile(minigame, "c3"));
else
- c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,-1,0,6,7)));
+ c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,-1,0,C4_NUM_CNT,C4_LET_CNT)));
return true;
/*case K_UPARROW:
case K_KP_UPARROW:
vector mouse_pos = minigame_hud_normalize(mousepos,c4_boardpos,c4_boardsize);
if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) )
{
- c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_tile_name(mouse_pos,6,7)));
+ c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_tile_name(mouse_pos,C4_NUM_CNT,C4_LET_CNT)));
}
if ( ! c4_valid_tile(c4_curr_pos) )
c4_set_curr_pos("");
#include "sv_monsters.qh"
#endif
+#include "../animdecide.qh"
+vector animfixfps(entity e, vector a, vector b);
+
#include "monster/zombie.qc"
#include "monster/spider.qc"
#include "monster/mage.qc"
}
void spawnfunc_monster_afrit() { Monster_Spawn(MON_AFRIT); }
+#endif // SVQC
-float M_Afrit(float req)
+bool M_Afrit(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
if(self.target_range < 0)
self.superweapons_finished = time + 20;
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_afrit_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
}
void spawnfunc_monster_creeper() { Monster_Spawn(MON_CREEPER); }
+#endif // SVQC
-float M_Creeper(float req)
+bool M_Creeper(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
if(self.creeper_primed)
setanim(self, self.anim_die1, false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_creeper_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
void spawnfunc_monster_demon() { Monster_Spawn(MON_DEMON); }
void spawnfunc_monster_demon1() { spawnfunc_monster_demon(); }
void spawnfunc_monster_animus() { spawnfunc_monster_demon(); }
+#endif // SVQC
-float M_Demon(float req)
+bool M_Demon(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
setanim(self, self.anim_die1, false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_demon_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
}
void spawnfunc_monster_enforcer() { Monster_Spawn(MON_ENFORCER); }
+#endif // SVQC
-float M_Enforcer(float req)
+bool M_Enforcer(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
setanim(self, ((random() > 0.5) ? self.anim_die1 : self.anim_die2), false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_enforcer_health);
precache_sound(W_Sound("lasergun_fire"));
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
REGISTER_MONSTER(
/* MON_##id */ GOOMBA,
/* functions */ M_Goomba, M_Goomba_Attack,
-/* spawnflags */ MON_FLAG_MELEE,
+/* spawnflags */ MON_FLAG_MELEE | MON_FLAG_CRUSH,
/* mins,maxs */ '-18 -18 -20', '18 18 20',
/* model */ "goomba.md3",
/* netname */ "goomba",
}
void spawnfunc_monster_goomba() { Monster_Spawn(MON_GOOMBA); }
+#endif // SVQC
bool M_Goomba(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
Monster_Move_2D(self.speed, autocvar_g_monster_goomba_allow_jumpoff);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
// compatibility with old spawns
void spawnfunc_monster_shalrath() { spawnfunc_monster_mage(); }
#endif
+#endif // SVQC
-float M_Mage(float req)
+bool M_Mage(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
entity head;
- float need_help = false;
+ bool need_help = false;
for(head = world; (head = findfloat(head, iscreature, true)); )
if(head != self)
setanim(self, self.anim_die1, false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_mage_health);
precache_sound (W_Sound("tagexp1"));
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
}
void spawnfunc_monster_ogre() { Monster_Spawn(MON_OGRE); }
+#endif // SVQC
-float M_Ogre(float req)
+bool M_Ogre(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
setanim(self, ((random() > 0.5) ? self.anim_die1 : self.anim_die2), false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_ogre_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
void spawnfunc_monster_rotfish() { Monster_Spawn(MON_ROTFISH); }
void spawnfunc_monster_fish() { spawnfunc_monster_rotfish(); }
+#endif // SVQC
-float M_Rotfish(float req)
+bool M_Rotfish(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
setanim(self, self.anim_die1, false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_rotfish_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
}
void spawnfunc_monster_rottweiler() { Monster_Spawn(MON_ROTTWEILER); }
+#endif // SVQC
-float M_Rottweiler(float req)
+bool M_Rottweiler(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
setanim(self, ((random() > 0.5) ? self.anim_die1 : self.anim_die2), false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_rottweiler_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
// compatibility with old spawns
void spawnfunc_monster_wizard() { spawnfunc_monster_scrag(); }
void spawnfunc_monster_wyvern() { spawnfunc_monster_scrag(); }
+#endif // SVQC
-float M_Scrag(float req)
+bool M_Scrag(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
self.velocity_z = 100 + 100 * random();
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_scrag_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
}
void spawnfunc_monster_shambler() { Monster_Spawn(MON_SHAMBLER); }
+#endif // SVQC
-float M_Shambler(float req)
+bool M_Shambler(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
setanim(self, self.anim_die1, false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_shambler_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
}
void spawnfunc_monster_spawn() { Monster_Spawn(MON_SPAWN); }
+#endif // SVQC
-float M_Spawn(float req)
+bool M_Spawn(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
// prevent standard code from breaking everything
defer(0.05, M_Spawn_Attack_Explode); // simply defer to prevent recursion
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_spawn_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
}
void spawnfunc_monster_spider() { Monster_Spawn(MON_SPIDER); }
+#endif // SVQC
-float M_Spider(float req)
+bool M_Spider(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
self.angles_x = 180;
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_spider_health);
precache_sound (W_Sound("electro_fire2"));
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
// compatibility with old spawns
void spawnfunc_monster_shalrath() { spawnfunc_monster_vore(); }
+#endif // SVQC
-float M_Vore(float req)
+int M_Vore(bool req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
setanim(self, self.anim_die1, false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_vore_health);
precache_sound (W_Sound("tagexp1"));
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
// compatibility with old spawns
void spawnfunc_monster_wizard() { spawnfunc_monster_wyvern(); }
#endif // extra monsters include original wizard
+#endif // SVQC
-float M_Wyvern(float req)
+bool M_Wyvern(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
return true;
self.velocity_z = 100 + 100 * random();
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_wyvern_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
}
void spawnfunc_monster_zombie() { Monster_Spawn(MON_ZOMBIE); }
+#endif // SVQC
-float M_Zombie(float req)
+bool M_Zombie(int req)
{
switch(req)
{
+ #ifdef SVQC
case MR_THINK:
{
if(time >= self.spawn_time)
setanim(self, ((random() > 0.5) ? self.anim_die1 : self.anim_die2), false, true, true);
return true;
}
+ #endif
case MR_ANIM:
{
vector none = '0 0 0';
return true;
}
+ #ifdef SVQC
case MR_SETUP:
{
if(!self.health) self.health = (autocvar_g_monster_zombie_health);
{
return true;
}
+ #endif
}
return true;
}
-#endif // SVQC
#endif // REGISTER_MONSTER
const int MON_FLAG_RANGED = 512; // monster shoots projectiles
const int MON_FLAG_MELEE = 1024;
const int MON_FLAG_RIDE = 2048; // monster can be mounted
+const int MON_FLAG_CRUSH = 4096; // monster can be stomped
// entity properties of monsterinfo
.float monsterid; // MON_...
.int spawnflags;
.vector mins, maxs; // monster hitbox size
+// animations
+.vector anim_blockend;
+.vector anim_blockstart;
+.vector anim_melee1;
+.vector anim_melee2;
+.vector anim_melee3;
+.vector anim_pain3;
+.vector anim_pain4;
+.vector anim_pain5;
+.vector anim_walk;
+.vector anim_spawn;
+
// other useful macros
#define MON_ACTION(monstertype,mrequest) (get_monsterinfo(monstertype)).monster_func(mrequest)
register_monster(id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname); \
} \
ACCUMULATE_FUNCTION(RegisterMonsters, RegisterMonsters_##id)
-#ifdef SVQC
+#ifndef MENUQC
#define REGISTER_MONSTER(id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname) \
REGISTER_MONSTER_2(MON_##id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname)
-#elif defined(CSQC)
- #define REGISTER_MONSTER(id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname) \
- REGISTER_MONSTER_2(MON_##id,m_null,m_null,monsterflags,min_s,max_s,modelname,shortname,mname)
#else
#define REGISTER_MONSTER(id,func,attackfunc,monsterflags,min_s,max_s,modelname,shortname,mname) \
REGISTER_MONSTER_2(MON_##id,m_null,m_null,monsterflags,min_s,max_s,modelname,shortname,mname)
void monster_makevectors(entity e)
{
- vector v;
+ if(IS_MONSTER(self))
+ {
+ vector v;
- v = e.origin + (e.mins + e.maxs) * 0.5;
- self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
- self.v_angle_x = -self.v_angle_x;
+ v = e.origin + (e.mins + e.maxs) * 0.5;
+ self.v_angle = vectoangles(v - (self.origin + self.view_ofs));
+ self.v_angle_x = -self.v_angle_x;
+ }
makevectors(self.v_angle);
}
float Monster_Attack_Melee(entity targ, float damg, vector anim, float er, float animtime, int deathtype, float dostop)
{
- if(dostop) { self.state = MONSTER_ATTACK_MELEE; }
+ if(dostop && IS_MONSTER(self)) { self.state = MONSTER_ATTACK_MELEE; }
- setanim(self, anim, true, true, true);
+ setanim(self, anim, false, true, false);
- if(self.animstate_endtime > time)
+ if(self.animstate_endtime > time && IS_MONSTER(self))
self.attack_finished_single = self.anim_finished = self.animstate_endtime;
else
self.attack_finished_single = self.anim_finished = time + animtime;
float Monster_Attack_Leap_Check(vector vel)
{
- if(self.state)
+ if(self.state && IS_MONSTER(self))
return false; // already attacking
if(!(self.flags & FL_ONGROUND))
return false; // not on the ground
if(!Monster_Attack_Leap_Check(vel))
return false;
- setanim(self, anm, true, true, true);
+ setanim(self, anm, false, true, false);
- if(self.animstate_endtime > time)
+ if(self.animstate_endtime > time && IS_MONSTER(self))
self.attack_finished_single = self.anim_finished = self.animstate_endtime;
else
self.attack_finished_single = self.anim_finished = time + animtime;
- self.state = MONSTER_ATTACK_RANGED;
+ if(IS_MONSTER(self))
+ self.state = MONSTER_ATTACK_RANGED;
self.touch = touchfunc;
self.origin_z += 1;
self.velocity = vel;
.int oldskin;
.string mdl_dead; // dead model for goombas
-.vector anim_blockend;
-.vector anim_blockstart;
-.vector anim_melee1;
-.vector anim_melee2;
-.vector anim_melee3;
-.vector anim_pain3;
-.vector anim_pain4;
-.vector anim_pain5;
-.vector anim_walk;
-.vector anim_spawn;
-
#define MONSTER_SKILLMOD(mon) (0.5 + mon.monster_skill * ((1.2 - 0.3) / 10))
// other properties
bool autocvar_g_overkill_powerups_replace;
bool autocvar_g_za;
int autocvar_g_za_max_monsters;
+bool autocvar_g_za_infect;
string autocvar_g_za_spawnmonster;
float autocvar_g_za_spawn_delay;
float autocvar_g_overkill_superguns_respawn_time;
if(substring(plyermodel,-4,4) != ".dpm")
if(substring(plyermodel,-4,4) != ".iqm")
if(substring(plyermodel,-4,4) != ".md3")
+ if(substring(plyermodel,-4,4) != ".mdl")
if(substring(plyermodel,-4,4) != ".psk")
return FallbackPlayerModel;
// forbid the LOD models
if(other == world)
return;
- if(!autocvar_g_player_crush && !IS_MONSTER(other))
+ bool and_monster = IS_MONSTER(other);
+ if(and_monster && !((get_monsterinfo(other.monsterid)).spawnflags & MON_FLAG_CRUSH))
+ and_monster = false;
+
+ if(!autocvar_g_player_crush && !and_monster)
return;
if(!IS_PLAYER(self))
return;
- if(!IS_PLAYER(other) && !IS_MONSTER(other))
+ if(!IS_PLAYER(other) && !and_monster)
return;
if(self.deadflag != DEAD_NO || other.deadflag != DEAD_NO)
e.nextthink = time + 0.1;
}
+.vector za_viewofs;
+
+void za_SetEnemy(entity player)
+{
+ entity head;
+ entity closest_targ = world;
+ FOR_EACH_PLAYER(head) if(head != player)
+ if(vlen(head.origin - player.origin) < autocvar_g_monsters_target_range)
+ if(!closest_targ || vlen(closest_targ.origin - player.origin) > vlen(head.origin - player.origin))
+ closest_targ = head;
+
+ if(!closest_targ)
+ {
+ FOR_EACH_MONSTER(head)
+ if(vlen(head.origin - player.origin) < autocvar_g_monsters_target_range)
+ if(!closest_targ || vlen(closest_targ.origin - player.origin) > vlen(head.origin - player.origin))
+ closest_targ = head;
+ }
+
+ if(closest_targ)
+ player.enemy = closest_targ;
+}
+
+MUTATOR_HOOKFUNCTION(za_FixPlayermodel)
+{
+ if(self.monsterid)
+ ret_string = (get_monsterinfo(self.monsterid)).model;
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(za_PlayerPhysics)
+{
+ if(!self.monsterid) { return false; }
+
+ entity mon = get_monsterinfo(self.monsterid);
+
+ self.weapons = '0 0 0';
+ self.switchweapon = 0;
+ self.stat_pl_min = mon.mins;
+ self.stat_pl_max = mon.maxs;
+ self.stat_pl_crouch_min = mon.mins;
+ self.stat_pl_crouch_max = mon.maxs;
+ self.stat_pl_crouch_view_ofs = self.za_viewofs;
+ self.stat_pl_view_ofs = self.za_viewofs;
+ self.stat_sv_jumpvelocity = 0;
+
+ self.stat_sv_maxspeed = self.speed2;
+ self.stat_sv_maxairspeed = self.speed2;
+ self.BUTTON_CROUCH = false;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(za_PlayerThink)
+{
+ if(!self.monsterid || time < self.attack_finished_single) { return false; }
+
+ MON_ACTION(self.monsterid, MR_THINK);
+
+ if(self.BUTTON_ATCK)
+ if(self.monster_attackfunc)
+ {
+ za_SetEnemy(self);
+ if(self.monster_attackfunc(MONSTER_ATTACK_MELEE))
+ animdecide_setaction(self, ANIMACTION_MELEE, false);
+ }
+
+ if(self.BUTTON_ATCK2)
+ if(self.monster_attackfunc)
+ {
+ za_SetEnemy(self);
+ if(self.monster_attackfunc(MONSTER_ATTACK_RANGED))
+ animdecide_setaction(self, ANIMACTION_SHOOT, false);
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(za_ItemTouch)
+{
+ if(other.monsterid)
+ return MUT_ITEMTOUCH_RETURN;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(za_PlayerDies)
+{
+ if(!autocvar_g_za_infect) { return false; }
+
+ if(!IS_MONSTER(frag_attacker))
+ {
+ self.monsterid = 0;
+ return false;
+ }
+
+ entity mon = get_monsterinfo(frag_attacker.monsterid);
+
+ self.monsterid = frag_attacker.monsterid;
+ self.health = frag_attacker.max_health;
+ self.weapons = '0 0 0';
+ self.switchweapon = 0;
+ self.stat_pl_min = mon.mins;
+ self.stat_pl_max = mon.maxs;
+ self.stat_pl_crouch_min = mon.mins;
+ self.stat_pl_crouch_max = mon.maxs;
+ self.monster_attack = false;
+ self.speed = frag_attacker.speed;
+ self.speed2 = frag_attacker.speed2;
+ self.stopspeed = frag_attacker.stopspeed;
+ self.view_ofs = self.za_viewofs = frag_attacker.view_ofs;
+ self.attack_range = ((frag_attacker.attack_range) ? frag_attacker.attack_range : 150);
+ //self.attack_range = frag_attacker.attack_range;
+ self.monster_attackfunc = mon.monster_attackfunc;
+ setsize(self, frag_attacker.mins, frag_attacker.maxs);
+
+ return false;
+}
+
MUTATOR_HOOKFUNCTION(za_StartFrame)
{
if(time < za_spawn_delay || autocvar_g_za_max_monsters <= 0 || !autocvar_g_za)
MUTATOR_DEFINITION(mutator_zombie_apocalypse)
{
+ MUTATOR_HOOK(FixPlayermodel, za_FixPlayermodel, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPhysics, za_PlayerPhysics, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPreThink, za_PlayerThink, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ItemTouch, za_ItemTouch, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDies, za_PlayerDies, CBC_ORDER_ANY);
MUTATOR_HOOK(SV_StartFrame, za_StartFrame, CBC_ORDER_ANY);
MUTATOR_ONADD