From: Mario Date: Sun, 26 Apr 2015 18:19:08 +0000 (+1000) Subject: Add an infection mode to zombie apocalypse, which allows playing as monsters X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=0f4beeaf8bd40cdf3dfd22bed9d5d0b5bb4735fb;p=xonotic%2Fxonotic-data.pk3dir.git Add an infection mode to zombie apocalypse, which allows playing as monsters --- diff --git a/qcsrc/Makefile b/qcsrc/Makefile index b7594c76e..04d8d85f0 100644 --- a/qcsrc/Makefile +++ b/qcsrc/Makefile @@ -13,6 +13,7 @@ QCCFLAGS ?= \ -std=gmqcc \ -O3 -flno \ -DSTUFFTO_ENABLED \ + -DMONSTERS_EXTRA \ -Werror -fno-bail-on-werror -Wall \ -fftepp -fftepp-predefs -Wcpp -futf8 \ $(QCCFLAGS_WTFS) \ diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index e63c7d68b..202263439 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -681,8 +681,12 @@ void CSQCModel_Hook_PreDraw(bool isplayer) 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); @@ -780,7 +784,7 @@ void CSQCModel_Hook_PreUpdate(bool isnew, bool isplayer, bool islocalplayer) 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) diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 3398066e8..8cd2acd7f 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -211,6 +211,7 @@ void CSQC_Init(void) // 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); diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index c72a3dd14..66228f441 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -475,7 +475,7 @@ vector liquidcolor_prev; float eventchase_current_distance; float eventchase_running; -float WantEventchase() +bool WantEventchase() { if(autocvar_cl_orthoview) return false; @@ -487,6 +487,8 @@ float WantEventchase() { 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)) diff --git a/qcsrc/common/animdecide.qc b/qcsrc/common/animdecide.qc index d5e8617d9..20e0c17a0 100644 --- a/qcsrc/common/animdecide.qc +++ b/qcsrc/common/animdecide.qc @@ -10,41 +10,52 @@ #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) { @@ -52,6 +63,12 @@ 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 diff --git a/qcsrc/common/animdecide.qh b/qcsrc/common/animdecide.qh index 5d441ef80..585fb1e67 100644 --- a/qcsrc/common/animdecide.qh +++ b/qcsrc/common/animdecide.qh @@ -24,6 +24,42 @@ void animdecide_setframes(entity e, bool support_blending, .int fld_frame, .int .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 diff --git a/qcsrc/common/minigames/minigame/all.qh b/qcsrc/common/minigames/minigame/all.qh index 2505b142b..f6c850107 100644 --- a/qcsrc/common/minigames/minigame/all.qh +++ b/qcsrc/common/minigames/minigame/all.qh @@ -63,7 +63,7 @@ that .owner is set to the minigame session entity and .minigame_autoclean is tru #include "nmm.qc" #include "ttt.qc" -//#include "c4.qc" +#include "c4.qc" #include "pong.qc" /** @@ -76,10 +76,9 @@ that .owner is set to the minigame session entity and .minigame_autoclean is tru 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 diff --git a/qcsrc/common/minigames/minigame/c4.qc b/qcsrc/common/minigames/minigame/c4.qc index 6e38afeb6..c4e06006f 100644 --- a/qcsrc/common/minigames/minigame/c4.qc +++ b/qcsrc/common/minigames/minigame/c4.qc @@ -8,6 +8,16 @@ const float C4_TURN_TEAM1 = 0x0001; 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) @@ -35,7 +45,7 @@ bool c4_winning_piece(entity piece) 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) @@ -59,7 +69,7 @@ bool c4_winning_piece(entity piece) 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) @@ -76,43 +86,43 @@ bool c4_winning_piece(entity piece) 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; @@ -125,7 +135,7 @@ float c4_valid_tile(string tile) 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 @@ -143,14 +153,14 @@ void c4_move(entity minigame, entity player, string pos ) 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; @@ -186,7 +196,7 @@ void c4_next_match(entity minigame, entity player) // required function, handle server side events -float minigame_event_c4(entity minigame, string event, ...) +float c4_server_event(entity minigame, string event, ...) { switch(event) { @@ -211,11 +221,11 @@ float minigame_event_c4(entity minigame, string 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; @@ -258,7 +268,7 @@ vector c4_boardsize;// HUD board size .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; @@ -266,13 +276,13 @@ void minigame_hud_board_c4(vector pos, vector mySize) 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))), @@ -284,7 +294,7 @@ void minigame_hud_board_c4(vector pos, vector mySize) { 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 ) @@ -310,7 +320,7 @@ void minigame_hud_board_c4(vector pos, vector mySize) // 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; @@ -335,7 +345,7 @@ void minigame_hud_status_c4(vector pos, vector mySize) 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; @@ -402,7 +412,7 @@ string c4_get_lowest_tile(entity minigame, string s) { 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) { @@ -414,7 +424,7 @@ string c4_get_lowest_tile(entity minigame, string s) } // Required function, handle client events -float minigame_event_c4(entity minigame, string event, ...) +float c4_client_event(entity minigame, string event, ...) { switch(event) { @@ -435,14 +445,14 @@ float minigame_event_c4(entity minigame, string 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: @@ -483,7 +493,7 @@ float minigame_event_c4(entity minigame, string event, ...) 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(""); diff --git a/qcsrc/common/monsters/all.qh b/qcsrc/common/monsters/all.qh index 2228dbbec..4a7592d94 100644 --- a/qcsrc/common/monsters/all.qh +++ b/qcsrc/common/monsters/all.qh @@ -6,6 +6,9 @@ #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" diff --git a/qcsrc/common/monsters/monster/afrit.qc b/qcsrc/common/monsters/monster/afrit.qc index 48bce9e2e..46cff9847 100644 --- a/qcsrc/common/monsters/monster/afrit.qc +++ b/qcsrc/common/monsters/monster/afrit.qc @@ -106,11 +106,13 @@ float M_Afrit_Attack(float attack_type) } 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) @@ -159,6 +161,7 @@ float M_Afrit(float req) self.superweapons_finished = time + 20; return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -173,6 +176,7 @@ float M_Afrit(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_afrit_health); @@ -196,10 +200,10 @@ float M_Afrit(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/creeper.qc b/qcsrc/common/monsters/monster/creeper.qc index a2cb40ac5..8aad49315 100644 --- a/qcsrc/common/monsters/monster/creeper.qc +++ b/qcsrc/common/monsters/monster/creeper.qc @@ -73,11 +73,13 @@ bool M_Creeper_Attack(int attack_type) } 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) @@ -95,6 +97,7 @@ float M_Creeper(float req) setanim(self, self.anim_die1, false, true, true); return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -105,6 +108,7 @@ float M_Creeper(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_creeper_health); @@ -124,10 +128,10 @@ float M_Creeper(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/demon.qc b/qcsrc/common/monsters/monster/demon.qc index 105bfa4fb..1b3aad794 100644 --- a/qcsrc/common/monsters/monster/demon.qc +++ b/qcsrc/common/monsters/monster/demon.qc @@ -79,11 +79,13 @@ float M_Demon_Attack(float attack_type) 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; @@ -99,6 +101,7 @@ float M_Demon(float req) setanim(self, self.anim_die1, false, true, true); return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -112,6 +115,7 @@ float M_Demon(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_demon_health); @@ -128,10 +132,10 @@ float M_Demon(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/enforcer.qc b/qcsrc/common/monsters/monster/enforcer.qc index 3560f9366..b431800fa 100644 --- a/qcsrc/common/monsters/monster/enforcer.qc +++ b/qcsrc/common/monsters/monster/enforcer.qc @@ -122,11 +122,13 @@ float M_Enforcer_Attack(float attack_type) } 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; @@ -150,6 +152,7 @@ float M_Enforcer(float req) 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'; @@ -166,6 +169,7 @@ float M_Enforcer(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_enforcer_health); @@ -185,10 +189,10 @@ float M_Enforcer(float req) precache_sound(W_Sound("lasergun_fire")); return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/goomba.qc b/qcsrc/common/monsters/monster/goomba.qc index 884453467..7dd75f4b1 100644 --- a/qcsrc/common/monsters/monster/goomba.qc +++ b/qcsrc/common/monsters/monster/goomba.qc @@ -2,7 +2,7 @@ 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", @@ -54,11 +54,13 @@ bool M_Goomba_Attack(int attack_type) } 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); @@ -89,10 +91,10 @@ bool M_Goomba(int req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index b1ec4e6af..a6f6608e3 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -345,15 +345,17 @@ void spawnfunc_monster_mage() { Monster_Spawn(MON_MAGE); } // 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) @@ -389,6 +391,7 @@ float M_Mage(float req) setanim(self, self.anim_die1, false, true, true); return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -401,6 +404,7 @@ float M_Mage(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_mage_health); @@ -419,10 +423,10 @@ float M_Mage(float req) precache_sound (W_Sound("tagexp1")); return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/ogre.qc b/qcsrc/common/monsters/monster/ogre.qc index cbcd98524..d038e1070 100644 --- a/qcsrc/common/monsters/monster/ogre.qc +++ b/qcsrc/common/monsters/monster/ogre.qc @@ -314,11 +314,13 @@ float M_Ogre_Attack(float attack_type) } 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; @@ -343,6 +345,7 @@ float M_Ogre(float req) 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'; @@ -363,6 +366,7 @@ float M_Ogre(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_ogre_health); @@ -383,10 +387,10 @@ float M_Ogre(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/rotfish.qc b/qcsrc/common/monsters/monster/rotfish.qc index fc8d3022c..61aea862f 100644 --- a/qcsrc/common/monsters/monster/rotfish.qc +++ b/qcsrc/common/monsters/monster/rotfish.qc @@ -48,11 +48,13 @@ float M_Rotfish_Attack(float attack_type) 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; @@ -68,6 +70,7 @@ float M_Rotfish(float req) setanim(self, self.anim_die1, false, true, true); return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -80,6 +83,7 @@ float M_Rotfish(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_rotfish_health); @@ -97,10 +101,10 @@ float M_Rotfish(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/rottweiler.qc b/qcsrc/common/monsters/monster/rottweiler.qc index 4149357de..b81cb5fc3 100644 --- a/qcsrc/common/monsters/monster/rottweiler.qc +++ b/qcsrc/common/monsters/monster/rottweiler.qc @@ -50,11 +50,13 @@ float M_Rottweiler_Attack(float attack_type) } 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; @@ -73,6 +75,7 @@ float M_Rottweiler(float req) 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'; @@ -88,6 +91,7 @@ float M_Rottweiler(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_rottweiler_health); @@ -104,10 +108,10 @@ float M_Rottweiler(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/scrag.qc b/qcsrc/common/monsters/monster/scrag.qc index cc6c6dde2..0247e1bc1 100644 --- a/qcsrc/common/monsters/monster/scrag.qc +++ b/qcsrc/common/monsters/monster/scrag.qc @@ -110,11 +110,13 @@ void spawnfunc_monster_scrag() { Monster_Spawn(MON_SCRAG); } // 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; @@ -133,6 +135,7 @@ float M_Scrag(float req) self.velocity_z = 100 + 100 * random(); return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -145,6 +148,7 @@ float M_Scrag(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_scrag_health); @@ -161,10 +165,10 @@ float M_Scrag(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/shambler.qc b/qcsrc/common/monsters/monster/shambler.qc index 8557c705d..fcff1722e 100644 --- a/qcsrc/common/monsters/monster/shambler.qc +++ b/qcsrc/common/monsters/monster/shambler.qc @@ -209,11 +209,13 @@ float M_Shambler_Attack(float attack_type) } 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; @@ -229,6 +231,7 @@ float M_Shambler(float req) setanim(self, self.anim_die1, false, true, true); return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -244,6 +247,7 @@ float M_Shambler(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_shambler_health); @@ -266,10 +270,10 @@ float M_Shambler(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/spawn.qc b/qcsrc/common/monsters/monster/spawn.qc index 2ef2c2e08..4f2cbc599 100644 --- a/qcsrc/common/monsters/monster/spawn.qc +++ b/qcsrc/common/monsters/monster/spawn.qc @@ -84,11 +84,13 @@ float M_Spawn_Attack(float attack_type) } 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 @@ -109,6 +111,7 @@ float M_Spawn(float req) defer(0.05, M_Spawn_Attack_Explode); // simply defer to prevent recursion return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -120,6 +123,7 @@ float M_Spawn(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_spawn_health); @@ -137,10 +141,10 @@ float M_Spawn(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/spider.qc b/qcsrc/common/monsters/monster/spider.qc index 3f8e5b62b..0c77ad813 100644 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@ -122,11 +122,13 @@ float M_Spider_Attack(float attack_type) } 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; @@ -141,6 +143,7 @@ float M_Spider(float req) self.angles_x = 180; return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -152,6 +155,7 @@ float M_Spider(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_spider_health); @@ -169,10 +173,10 @@ float M_Spider(float req) precache_sound (W_Sound("electro_fire2")); return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/vore.qc b/qcsrc/common/monsters/monster/vore.qc index 0c6b478ed..11e74aef5 100644 --- a/qcsrc/common/monsters/monster/vore.qc +++ b/qcsrc/common/monsters/monster/vore.qc @@ -183,11 +183,13 @@ void spawnfunc_monster_vore() { Monster_Spawn(MON_VORE); } // 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; @@ -203,6 +205,7 @@ float M_Vore(float req) setanim(self, self.anim_die1, false, true, true); return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -215,6 +218,7 @@ float M_Vore(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_vore_health); @@ -234,10 +238,10 @@ float M_Vore(float req) precache_sound (W_Sound("tagexp1")); return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/wyvern.qc b/qcsrc/common/monsters/monster/wyvern.qc index d931a5b2e..3e300b999 100644 --- a/qcsrc/common/monsters/monster/wyvern.qc +++ b/qcsrc/common/monsters/monster/wyvern.qc @@ -104,11 +104,13 @@ void spawnfunc_monster_wyvern() { Monster_Spawn(MON_WYVERN); } // 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; @@ -127,6 +129,7 @@ float M_Wyvern(float req) self.velocity_z = 100 + 100 * random(); return true; } + #endif case MR_ANIM: { vector none = '0 0 0'; @@ -139,6 +142,7 @@ float M_Wyvern(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_wyvern_health); @@ -155,10 +159,10 @@ float M_Wyvern(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monster/zombie.qc b/qcsrc/common/monsters/monster/zombie.qc index 966dc99a5..23a9d258a 100644 --- a/qcsrc/common/monsters/monster/zombie.qc +++ b/qcsrc/common/monsters/monster/zombie.qc @@ -134,11 +134,13 @@ float M_Zombie_Attack(float attack_type) } 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) @@ -158,6 +160,7 @@ float M_Zombie(float req) 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'; @@ -178,6 +181,7 @@ float M_Zombie(float req) return true; } + #ifdef SVQC case MR_SETUP: { if(!self.health) self.health = (autocvar_g_monster_zombie_health); @@ -204,10 +208,10 @@ float M_Zombie(float req) { return true; } + #endif } return true; } -#endif // SVQC #endif // REGISTER_MONSTER diff --git a/qcsrc/common/monsters/monsters.qh b/qcsrc/common/monsters/monsters.qh index 33068648e..649020f98 100644 --- a/qcsrc/common/monsters/monsters.qh +++ b/qcsrc/common/monsters/monsters.qh @@ -21,6 +21,7 @@ const int MON_FLAG_SUPERMONSTER = 256; // incredibly powerful 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_... @@ -33,6 +34,18 @@ const int MON_FLAG_RIDE = 2048; // monster can be mounted .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) @@ -63,12 +76,9 @@ int MON_LAST; 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) diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index 7d21678b4..f58c26342 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -65,11 +65,14 @@ void monster_dropitem() 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); } @@ -358,11 +361,11 @@ void Monster_Sound(.string samplefield, float sound_delay, float delaytoo, float 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; @@ -379,7 +382,7 @@ float Monster_Attack_Melee(entity targ, float damg, vector anim, float er, float 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 @@ -404,14 +407,15 @@ bool Monster_Attack_Leap(vector anm, void() touchfunc, vector vel, float animtim 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; diff --git a/qcsrc/common/monsters/sv_monsters.qh b/qcsrc/common/monsters/sv_monsters.qh index d5f04b765..d1d224363 100644 --- a/qcsrc/common/monsters/sv_monsters.qh +++ b/qcsrc/common/monsters/sv_monsters.qh @@ -29,17 +29,6 @@ int monsters_killed; .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 diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index afa682de9..0f3357c16 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -929,6 +929,7 @@ bool autocvar_g_jump_grunt; 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; diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 4b1c8c4ff..41b8e1792 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -182,6 +182,7 @@ string CheckPlayerModel(string plyermodel) { 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 @@ -488,13 +489,17 @@ void PlayerTouch (void) 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) diff --git a/qcsrc/server/mutators/mutator_zombie_apocalypse.qc b/qcsrc/server/mutators/mutator_zombie_apocalypse.qc index bd771b277..4f4c442e1 100644 --- a/qcsrc/server/mutators/mutator_zombie_apocalypse.qc +++ b/qcsrc/server/mutators/mutator_zombie_apocalypse.qc @@ -13,6 +13,125 @@ void za_SpawnMonster() 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) @@ -37,6 +156,11 @@ MUTATOR_HOOKFUNCTION(za_StartFrame) 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