From: Mario Date: Sat, 13 Apr 2013 07:01:21 +0000 (+1000) Subject: Big load of updates (attempted CSQC monsters) X-Git-Tag: xonotic-v0.8.0~241^2^2~422 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=4a2ad3c32f0acd52153dc8d1548841ca8df61bd8;p=xonotic%2Fxonotic-data.pk3dir.git Big load of updates (attempted CSQC monsters) --- diff --git a/commands.cfg b/commands.cfg index a01ffc9c6..0fd85a569 100644 --- a/commands.cfg +++ b/commands.cfg @@ -105,6 +105,7 @@ alias menu_showhudoptions "menu_cmd directpanelhudmenu ${* ?}" alias menu_showsandboxtools "menu_cmd directmenu SandboxTools" alias menu_showquitdialog "menu_cmd directmenu Quit" alias menu_showtdtools "menu_cmd directmenu TowerDefense" +alias menu_showmonstertools "menu_cmd directmenu MonsterTools" // command executed before loading a map by the menu // makes sure maxplayers is at least minplayers or bot_number + 1 @@ -181,7 +182,6 @@ alias spec "spectate" // mutator aliases alias sandbox "cmd g_sandbox ${* ?}" alias spawnturret "cmd turretspawn ${* ?}" -alias debugmonsters "cmd debugmonsters ${* ?}" alias upgradeturret "cmd buffturret ${* ?}" alias rmturret "cmd turretremove ${* ?}" alias repairturret "cmd repairturret ${* ?}" diff --git a/gamemodes.cfg b/gamemodes.cfg index d9705ad96..5519ce515 100644 --- a/gamemodes.cfg +++ b/gamemodes.cfg @@ -224,6 +224,7 @@ set g_ctf_pass_timelimit 2 "how long a flag can stay trying to pass before it gi set g_ctf_pass_velocity 750 "how fast or far a player can pass the flag" set g_ctf_allow_vehicle_touch 0 "allow flags to be picked up/captured/returned without even leaving the vehicle" set g_ctf_allow_vehicle_carry 1 "allow players to hold flags inside a vehicle" +set g_ctf_allow_monster_touch 1 "allow flags to be returned by monsters" set g_ctf_shield_max_ratio 0 "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)" set g_ctf_shield_min_negscore 20 "shield the player from the flag if he's got this negative amount of points or less" @@ -452,8 +453,8 @@ set g_td_turret_flac_cost 40 set g_td_turret_upgrade_cost 100 set g_td_turret_repair_cost 20 set g_td_barricade_damage 10 -set g_td_monsters_speed_walk 75 -set g_td_monsters_speed_run 110 +set g_td_monsters_speed_walk 110 +set g_td_monsters_speed_run 150 set g_td_monsters_spawn_delay 3 // ==================== diff --git a/models/monsters/hknight.mdl.framegroups b/models/monsters/hknight.mdl.framegroups index cc16b3089..c495edd8a 100644 --- a/models/monsters/hknight.mdl.framegroups +++ b/models/monsters/hknight.mdl.framegroups @@ -1 +1 @@ -1 8 10 1 // hellknight stand 10 19 10 1 // hellknight walk 30 7 10 1 // hellknight run 38 4 10 0 // hellknight pain 43 11 10 0 // hellknight death1 55 8 10 0 // hellknight death2 64 15 10 0 // hellknight charge1 80 13 10 0 // hellknight magic1 94 12 10 0 // hellknight magic2 107 5 10 0 // hellknight charge2 113 9 10 1 // hellknight slice 123 9 10 1 // hellknight smash 133 21 10 1 // hellknight weapon attack 155 10 10 0 //hellknight magic3 \ No newline at end of file +1 8 10 1 // hellknight stand 10 19 10 1 // hellknight walk 30 7 10 1 // hellknight run 38 4 10 0 // hellknight pain 43 11 10 0 // hellknight death1 55 8 10 0 // hellknight death2 64 15 10 1 // hellknight charge1 80 13 10 1 // hellknight magic1 94 12 10 1 // hellknight magic2 107 5 10 1 // hellknight charge2 113 9 10 1 // hellknight slice 123 9 10 1 // hellknight smash 133 21 10 1 // hellknight weapon attack 155 10 10 1 //hellknight magic3 \ No newline at end of file diff --git a/models/monsters/mage.dpm b/models/monsters/mage.dpm index 3686b74af..2e50fb5ca 100644 Binary files a/models/monsters/mage.dpm and b/models/monsters/mage.dpm differ diff --git a/models/monsters/ogre.dpm b/models/monsters/ogre.dpm index 0f9b30eba..c38482dcf 100644 Binary files a/models/monsters/ogre.dpm and b/models/monsters/ogre.dpm differ diff --git a/models/monsters/zombie.dpm.framegroups b/models/monsters/zombie.dpm.framegroups index 3d56cdb2f..3d38a2864 100644 --- a/models/monsters/zombie.dpm.framegroups +++ b/models/monsters/zombie.dpm.framegroups @@ -1,4 +1,4 @@ -1 56 30 0 // zombie attackleap 1 +1 56 30 1 // zombie attackleap 1 57 41 60 1 // zombie attackrun1 2 98 41 60 1 // zombie attackrun2 3 139 41 60 1 // zombie attackrun3 4 diff --git a/models/monsters/zombie.dpm_3.skin b/models/monsters/zombie.dpm_3.skin new file mode 100644 index 000000000..ad7e54552 --- /dev/null +++ b/models/monsters/zombie.dpm_3.skin @@ -0,0 +1,2 @@ +bloodyskull,cleanskull +meat,meat \ No newline at end of file diff --git a/monsters.cfg b/monsters.cfg index 78ca83fce..88772499c 100644 --- a/monsters.cfg +++ b/monsters.cfg @@ -168,6 +168,11 @@ set g_monster_shalrath_attack_spike_radius 60 "Vore homing spike explosion radiu set g_monster_shalrath_attack_spike_delay 2 "Delay between Vore homing spike attacks" set g_monster_shalrath_attack_melee_damage 30 "Vore magic attack damage" set g_monster_shalrath_attack_melee_delay 0.7 "Delay between Vore melee attacks" +set g_monster_shalrath_heal_self 50 "Amount of health Vore will regenerate every attack when its low on health" +set g_monster_shalrath_heal_friends 15 "Amount of health Vore will regenerate nearby friends" +set g_monster_shalrath_heal_minhealth 250 "Health limit below which Vore will try to heal itself" +set g_monster_shalrath_heal_range 200 "Maximum healing distance" +set g_monster_shalrath_heal_delay 1.5 "Delay between healing bursts" // Spawner set g_monster_spawner 1 "Enable Monster Spawner" diff --git a/qcsrc/client/Main.qc b/qcsrc/client/Main.qc index 173a93194..630e34fe2 100644 --- a/qcsrc/client/Main.qc +++ b/qcsrc/client/Main.qc @@ -779,6 +779,7 @@ void CSQC_Ent_Update(float bIsNewEntity) case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break; case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break; case ENT_CLIENT_TURRET: ent_turret(); break; + case ENT_CLIENT_MONSTER: ent_monster(); break; case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break; case ENT_CLIENT_ITEM: ItemRead(bIsNewEntity); break; case ENT_CLIENT_BUMBLE_RAYGUN: bumble_raygun_read(bIsNewEntity); break; diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index 150efb170..2d2515ad7 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -163,6 +163,7 @@ float autocvar_g_waypointsprite_spam; float autocvar_g_waypointsprite_timealphaexponent; var float autocvar_g_waypointsprite_turrets = TRUE; var float autocvar_g_waypointsprite_turrets_maxdist = 5000; +var float autocvar_g_waypointsprite_monsters_maxdist = 5000; var float autocvar_hud_cursormode = TRUE; float autocvar_hud_colorflash_alpha; diff --git a/qcsrc/client/monsters.qc b/qcsrc/client/monsters.qc new file mode 100644 index 000000000..8fc843871 --- /dev/null +++ b/qcsrc/client/monsters.qc @@ -0,0 +1,360 @@ +string mid2info_model; +string mid2info_name; +vector mid2info_min; +vector mid2info_max; + +float monster_precached[MONSTER_LAST]; +void monster_mid2info(float _mid); + +void monster_precache(float _mid) +{ + monster_mid2info(_mid); + if(turret_is_precache[_mid]) + return; + + switch(_mid) + { + case MONSTER_ZOMBIE: + { + precache_model(ZOMBIE_MODEL); + break; + } + case MONSTER_OGRE: + { + precache_model(OGRE_MODEL); + break; + } + case MONSTER_DEMON: + { + precache_model(DEMON_MODEL); + break; + } + case MONSTER_SHAMBLER: + { + precache_model(SHAMBLER_MODEL); + break; + } + case MONSTER_KNIGHT: + { + precache_model(KNIGHT_MODEL); + break; + } + case MONSTER_MARINE: + { + precache_model(SOLDIER_MODEL); + break; + } + case MONSTER_SCRAG: + { + precache_model(WIZARD_MODEL); + break; + } + case MONSTER_DOG: + { + precache_model(DOG_MODEL); + break; + } + case MONSTER_TARBABY: + { + precache_model(TARBABY_MODEL); + break; + } + case MONSTER_HELLKNIGHT: + { + precache_model(HELLKNIGHT_MODEL); + break; + } + case MONSTER_FISH: + { + precache_model(FISH_MODEL); + break; + } + case MONSTER_MAGE: + { + precache_model(SHALRATH_MODEL); + break; + } + case MONSTER_ENFORCER: + { + precache_model(ENFORCER_MODEL); + break; + } + case MONSTER_SPIDER: + { + precache_model(SPIDER_MODEL); + + break; + } + } + monster_precached[_mid] = TRUE; +} + +void monster_mid2info(float _mid) +{ + switch(_mid) + { + case MONSTER_ZOMBIE: + { + mid2info_model = ZOMBIE_MODEL; + mid2info_name = "Zombie"; + mid2info_min = ZOMBIE_MIN; + mid2info_max = ZOMBIE_MAX; + break; + } + case MONSTER_OGRE: + { + mid2info_model = OGRE_MODEL; + mid2info_name = "Ogre"; + mid2info_min = OGRE_MIN; + mid2info_max = OGRE_MAX; + break; + } + case MONSTER_DEMON: + { + mid2info_model = DEMON_MODEL; + mid2info_name = "Fiend"; + mid2info_min = DEMON_MIN; + mid2info_max = DEMON_MAX; + break; + } + case MONSTER_SHAMBLER: + { + mid2info_model = SHAMBLER_MODEL; + mid2info_name = "Shambler"; + mid2info_min = SHAMBLER_MIN; + mid2info_max = SHAMBLER_MAX; + break; + } + case MONSTER_KNIGHT: + { + mid2info_model = KNIGHT_MODEL; + mid2info_name = "Knight"; + mid2info_min = KNIGHT_MIN; + mid2info_max = KNIGHT_MAX; + break; + } + case MONSTER_MARINE: + { + mid2info_model = SOLDIER_MODEL; + mid2info_name = "Marine"; + mid2info_min = SOLDIER_MIN; + mid2info_max = SOLDIER_MAX; + break; + } + case MONSTER_SCRAG: + { + mid2info_model = WIZARD_MODEL; + mid2info_name = "Scrag"; + mid2info_min = WIZARD_MIN; + mid2info_max = WIZARD_MAX; + break; + } + case MONSTER_DOG: + { + mid2info_model = DOG_MODEL; + mid2info_name = "Cerberus"; + mid2info_min = DOG_MIN; + mid2info_max = DOG_MAX; + break; + } + case MONSTER_TARBABY: + { + mid2info_model = TARBABY_MODEL; + mid2info_name = "Spawn"; + mid2info_min = TARBABY_MIN; + mid2info_max = TARBABY_MAX; + break; + } + case MONSTER_HELLKNIGHT: + { + mid2info_model = HELLKNIGHT_MODEL; + mid2info_name = "Hell-Knight"; + mid2info_min = HELLKNIGHT_MIN; + mid2info_max = HELLKNIGHT_MAX; + break; + } + case MONSTER_FISH: + { + mid2info_model = FISH_MODEL; + mid2info_name = "Rotfish"; + mid2info_min = FISH_MIN; + mid2info_max = FISH_MAX; + break; + } + case MONSTER_MAGE: + { + mid2info_model = SHALRATH_MODEL; + mid2info_name = "Mage"; + mid2info_min = SHALRATH_MIN; + mid2info_max = SHALRATH_MAX; + break; + } + case MONSTER_ENFORCER: + { + mid2info_model = ENFORCER_MODEL; + mid2info_name = "Enforcer"; + mid2info_min = ENFORCER_MIN; + mid2info_max = ENFORCER_MAX; + break; + } + case MONSTER_SPIDER: + { + mid2info_model = SPIDER_MODEL; + mid2info_name = "Spider"; + mid2info_min = SPIDER_MIN; + mid2info_max = SPIDER_MAX; + break; + } + default: + { + dprint("WARNING: Unknown monster in CSQC\n"); + break; + } + } +} + +.vector glowmod; +void monster_changeteam() +{ + switch(self.team - 1) + { + case NUM_TEAM_1: // Red + { + self.glowmod = '2 0 0'; + self.teamradar_color = '1 0 0'; + break; + } + case NUM_TEAM_2: // Blue + { + self.glowmod = '0 0 2'; + self.teamradar_color = '0 0 1'; + break; + } + case NUM_TEAM_3: // Yellow + { + self.glowmod = '1 1 0'; + self.teamradar_color = '1 1 0'; + break; + } + case NUM_TEAM_4: // Pink + { + self.glowmod = '1 0 1'; + self.teamradar_color = '1 0 1'; + break; + } + } + + if(self.team) + self.colormap = 1024 + (self.team - 1) * 17; + +} + +void monster_construct() +{ + monster_mid2info(self.monsterid); + self.netname = mid2info_name; + + setorigin(self, self.origin); + setmodel(self, mid2info_model); + setsize(self, mid2info_min, mid2info_max); + + self.move_movetype = MOVETYPE_BOUNCE; + self.health = 255; + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_BOUNCE; + self.move_origin = self.origin; + self.move_time = time; + self.drawmask = MASK_NORMAL; + self.alpha = 1; +} + +void ent_monster() +{ + float sf; + sf = ReadByte(); + + if(sf & MSF_SETUP) + { + self.monsterid = ReadByte(); + + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.angles_x = ReadAngle(); + self.angles_y = ReadAngle(); + + self.scale = ReadByte(); + self.skin = ReadByte(); + + monster_precache(self.monsterid); + monster_construct(); + self.colormap = 1024; + self.glowmod = '0 1 1'; + } + + if(sf & MSF_SIZE) + { + self.scale = ReadByte(); + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + + setsize(self, self.mins, self.maxs); + } + + if(sf & MSF_ANG) + { + self.move_angles_x = ReadShort(); + self.move_angles_y = ReadShort(); + self.angles = self.move_angles; + } + + if(sf & MSF_MOVE) + { + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + setorigin(self, self.origin); + + self.velocity_x = ReadShort(); + self.velocity_y = ReadShort(); + self.velocity_z = ReadShort(); + + self.move_angles_y = ReadShort(); + + self.move_time = time; + self.move_velocity = self.velocity; + self.move_origin = self.origin; + } + + if(sf & MSF_ANIM) + { + self.frame1time = ReadCoord(); + self.frame = ReadByte(); + } + + if(sf & MSF_STATUS) + { + float _tmp; + _tmp = ReadByte(); + if(_tmp != self.team) + { + self.team = _tmp; + monster_changeteam(); + } + + _tmp = ReadByte(); + + if(_tmp == 0 && self.health != 0) + if(self.monsterid == MONSTER_SPIDER) + self.angles += '180 0 0'; + + self.health = _tmp; + } +} diff --git a/qcsrc/client/monsters.qh b/qcsrc/client/monsters.qh new file mode 100644 index 000000000..aa6fb4126 --- /dev/null +++ b/qcsrc/client/monsters.qh @@ -0,0 +1 @@ +void ent_monster(); diff --git a/qcsrc/client/progs.src b/qcsrc/client/progs.src index 3b8aa1bac..29e1143f6 100644 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@ -46,6 +46,8 @@ bgmscript.qh noise.qh tturrets.qh ../server/tturrets/include/turrets_early.qh +monsters.qh +../server/monsters/lib/monsters_early.qh ../server/movelib.qc main.qh vehicles/vehicles.qh @@ -117,6 +119,9 @@ command/cl_cmd.qc ../warpzonelib/client.qc tturrets.qc +../server/monsters/monsters.qh +monsters.qc + player_skeleton.qc ../common/animdecide.qc diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index 6b362fed9..fd894be1f 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -101,6 +101,7 @@ const float ENT_CLIENT_NOTIFICATION = 36; const float ENT_CLIENT_TURRET = 40; const float ENT_CLIENT_AUXILIARYXHAIR = 50; const float ENT_CLIENT_VEHICLE = 60; +const float ENT_CLIENT_MONSTER = 70; const float SPRITERULE_DEFAULT = 0; const float SPRITERULE_TEAMPLAY = 1; diff --git a/qcsrc/common/notifications.qh b/qcsrc/common/notifications.qh index a32cc2b42..f594ee76c 100644 --- a/qcsrc/common/notifications.qh +++ b/qcsrc/common/notifications.qh @@ -336,11 +336,12 @@ void Send_Notification_WOVA( MSG_INFO_NOTIF(1, INFO_TD_NOFUEL, 0, 0, "", "", "", _("^K1You don't have enough fuel to spawn that turret\n"), "") \ MSG_INFO_NOTIF(1, INFO_TD_NOFUEL_REPAIR, 0, 1, "f1", "", "", _("^K1You need %s fuel to repair this turret\n"), "") \ MSG_INFO_NOTIF(1, INFO_TD_NOFUEL_UPGRADE, 0, 1, "f1", "", "", _("^K1You need %s fuel to increase this turret's power\n"), "") \ - MSG_INFO_NOTIF(1, INFO_TD_PHASE_BUILD, 0, 3, "f1 f2 f3", "", "", _("^BGWave %s^BG build phase... Next monsters: ^F2%s^BG, wave starts in ^F2%s seconds\n"), "") \ + MSG_INFO_NOTIF(1, INFO_TD_PHASE_BUILD, 0, 3, "f1 f2 f3", "", "", _("^BGWave ^F2%s^BG build phase... Next monsters: ^F2%s^BG, wave starts in ^F2%s seconds\n"), "") \ MSG_INFO_NOTIF(1, INFO_TD_PHASE_COMBAT, 0, 0, "", "", "", _("^K1Combat phase!\n"), "") \ MSG_INFO_NOTIF(1, INFO_TD_REMOVE, 0, 0, "", "", "", _("^BGTurret removed\n"), "") \ MSG_INFO_NOTIF(1, INFO_TD_REPAIR, 0, 0, "", "", "", _("^F1Turret repaired by 100 health points!\n"), "") \ MSG_INFO_NOTIF(1, INFO_TD_SPAWN, 0, 0, "", "", "", _("^BGYou spawned a turret\n"), "") \ + MSG_INFO_NOTIF(1, INFO_TD_TURRETS_DISABLED, 0, 0, "", "", "", _("^BGTurrets are disabled on this map\n"), "") \ MSG_INFO_NOTIF(1, INFO_TD_UPGRADE, 0, 0, "", "", "", _("^F1Turret power increased by 20 percent!\n"), "") \ MSG_INFO_NOTIF(1, INFO_TD_VICTORY, 1, 0, "s1", "", "", _("^F1%s^F1 victory!\n"), "") \ MSG_INFO_NOTIF(2, INFO_VERSION_BETA, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have ^F2Xonotic %s\n"), "") \ diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 7c9177436..03cdc954b 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -730,6 +730,7 @@ float autocvar_g_chat_teamcolors; float autocvar_g_chat_tellprivacy; float autocvar_g_ctf_allow_vehicle_carry; float autocvar_g_ctf_allow_vehicle_touch; +float autocvar_g_ctf_allow_monster_touch; float autocvar_g_ctf_throw; float autocvar_g_ctf_throw_angle_max; float autocvar_g_ctf_throw_angle_min; diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index 6f22c5083..37292d56a 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -235,7 +235,7 @@ void ClientCommand_mobkill(float request) return; } sprint(self, strcat("Your pet '", trace_ent.netname, "' has been brutally mutilated.\n")); - Damage (trace_ent, world, world, trace_ent.health + trace_ent.max_health, DEATH_KILL, trace_ent.origin, '0 0 0'); + Damage (trace_ent, world, world, trace_ent.health + trace_ent.max_health + 200, DEATH_KILL, trace_ent.origin, '0 0 0'); return; } else @@ -276,8 +276,9 @@ void ClientCommand_mobspawn(float request, float argc) return; } - if(!IS_PLAYER(self)) { sprint(self, "You can't spawn monsters while spectating.\n"); } - else if not(autocvar_g_monsters) { sprint(self, "Monsters aren't enabled.\n"); } + if(autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { sprint(self, "Monster spawning is disabled.\n"); } + else if(!IS_PLAYER(self)) { sprint(self, "You can't spawn monsters while spectating.\n"); } + else if not(autocvar_g_monsters) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_MONSTERS_DISABLED); } else if(self.vehicle) { sprint(self, "You can't spawn monsters while driving a vehicle.\n"); } else if(g_td) { sprint(self, "You can't spawn monsters in Tower Defense mode.\n"); } else if(self.deadflag) { sprint(self, "You can't spawn monsters while dead.\n"); } diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index 2a967151d..f3264a40c 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -164,6 +164,9 @@ void GameCommand_butcher(float request) FOR_EACH_PLAYER(head) head.monstercount = 0; + monsters_total = 0; // reset stats? + monsters_killed = 0; + totalspawned = 0; if(removed_count <= 0) diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 39a8af1fd..d88da22fc 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -627,19 +627,3 @@ string modname; #define MISSILE_IS_CONFUSABLE(m) ((m.missile_flags & MIF_GUIDED_CONFUSABLE) ? TRUE : FALSE) #define MISSILE_IS_GUIDED(m) ((m.missile_flags & MIF_GUIDED_ALL) ? TRUE : FALSE) #define MISSILE_IS_TRACKING(m) ((m.missile_flags & MIF_GUIDED_TRACKING) ? TRUE : FALSE) - -.string spawnmob; -.float monster_attack; - -float monster_skill; -float spawncode_first_load; // used to tell the player the monster database is loading (TODO: fix this?) - -.entity monster_owner; // new monster owner entity, fixes non-solid monsters -.float monstercount; // per player monster count - -.float stat_monsters_killed; // stats -.float stat_monsters_total; -float monsters_total; -float monsters_killed; -void monsters_setstatus(); // monsters.qc -.float monster_moveflags; // checks where to move when not attacking diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index a66759066..7196c7e18 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -815,7 +815,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float mirrorforce *= g_weaponforcefactor; } - if(targ.frozen && attacker.classname != "monster_spider") + if(targ.frozen && attacker.monsterid != MONSTER_SPIDER) { damage = 0; force *= 0.2; diff --git a/qcsrc/server/monsters/lib/defs.qh b/qcsrc/server/monsters/lib/defs.qh index 422885af4..ca25bb982 100644 --- a/qcsrc/server/monsters/lib/defs.qh +++ b/qcsrc/server/monsters/lib/defs.qh @@ -6,7 +6,11 @@ .float candrop; -.float spawner_monstercount; +.string oldmessage; + +.float respawnflags; +const float MONSTER_RESPAWN_SPAWNPOINT = 0; // re-spawn at original spawn point +const float MONSTER_RESPAWN_DEATHPOINT = 1; // re-spawn where we died .float monster_respawned; // used to make sure we're not recounting respawned monster stats @@ -45,4 +49,5 @@ const float MONSTER_MOVE_SPAWNLOC = 3; // monster will move to its spawn locatio const float MONSTER_MOVE_NOMOVE = 4; // monster simply stands still const float MONSTER_MOVE_ENEMY = 5; // used only as a movestate -float MONSTER_STATE_ATTACK_LEAP = 1; // the start of something big? +const float MONSTER_STATE_ATTACK_LEAP = 1; +const float MONSTER_STATE_ATTACK_MELEE = 2; diff --git a/qcsrc/server/monsters/lib/monsters.qc b/qcsrc/server/monsters/lib/monsters.qc index 889aeb4fc..c335d8004 100644 --- a/qcsrc/server/monsters/lib/monsters.qc +++ b/qcsrc/server/monsters/lib/monsters.qc @@ -79,6 +79,16 @@ void Monster_DropItem (string itype, string itemsize) e.nextthink = time + 0.1; } +void monsters_setframe(float _frame) +{ + if(self.frame == _frame) + return; + + self.anim_start_time = time; + self.frame = _frame; + self.SendFlags |= MSF_ANIM; +} + float monster_isvalidtarget (entity targ, entity ent) { if(!targ || !ent) @@ -172,21 +182,30 @@ void monster_precachesounds(entity e) precache_sound(e.msound_pain); } -void monster_melee (entity targ, float damg, float er, float deathtype) +void monster_melee (entity targ, float damg, float er, float deathtype, float dostop) { - float bigdmg = 0, rdmg = damg * random(); + float bigdmg, dot, rdmg = damg * random(); if (self.health <= 0) return; if (targ == world) return; - - if (vlen(self.origin - targ.origin) > er * self.scale) - return; + if(dostop) + { + self.velocity_x = 0; + self.velocity_y = 0; + self.state = MONSTER_STATE_ATTACK_MELEE; + self.SendFlags |= MSF_MOVE; + } + + makevectors (self.angles); + dot = normalize (targ.origin - self.origin) * v_forward; + bigdmg = rdmg * self.scale; - Damage(targ, self, self, bigdmg * monster_skill, deathtype, targ.origin, normalize(targ.origin - self.origin)); + if(dot > er) + Damage(targ, self, self, bigdmg * monster_skill, deathtype, targ.origin, normalize(targ.origin - self.origin)); } void Monster_CheckDropCvars (string mon) @@ -214,11 +233,13 @@ void Monster_CheckDropCvars (string mon) Monster_DropItem("armor", "medium"); } -void ScaleMonster (float scle) +void ScaleMonster (float scle, vector min_s, vector max_s) { // this should prevent monster from falling through floor when scale changes self.scale = scle; + setsize(self, min_s * scle, max_s * scle); setorigin(self, self.origin + ('0 0 30' * scle)); + self.SendFlags |= MSF_SIZE | MSF_MOVE; } void Monster_CheckMinibossFlag () @@ -226,15 +247,19 @@ void Monster_CheckMinibossFlag () if(MUTATOR_CALLHOOK(MonsterCheckBossFlag)) return; + if(self.spawnflags & MONSTERFLAG_GIANT) + return; // 1 size modifier at a time + float r = random() * 4, chance = random() * 100; // g_monsters_miniboss_chance cvar or spawnflags 64 causes a monster to be a miniboss if ((self.spawnflags & MONSTERFLAG_MINIBOSS) || (chance < autocvar_g_monsters_miniboss_chance)) { self.health += autocvar_g_monsters_miniboss_healthboost; - ScaleMonster(1.5); + ScaleMonster(1.5, self.mins, self.maxs); self.flags |= MONSTERFLAG_MINIBOSS; - self.weapon = WEP_NEX; + if not(self.weapon) + self.weapon = WEP_NEX; if (r < 2 || self.team == NUM_TEAM_2) { @@ -249,11 +274,8 @@ void Monster_CheckMinibossFlag () else self.effects |= (EF_FULLBRIGHT | EF_RED | EF_BLUE); - if(teamplay) - if(self.team) - return; - - self.colormod = randomvec() * 4; + if not(self.team) + self.colormod = randomvec(); } } @@ -277,9 +299,14 @@ void Monster_Fade () if(Monster_CanRespawn(self)) { self.monster_respawned = TRUE; - setmodel(self, ""); + self.netname = ""; // needed to fix sprite (TODO: fix!) self.think = self.monster_spawnfunc; self.nextthink = time + self.respawntime; + if(self.respawnflags & MONSTER_RESPAWN_DEATHPOINT) + { + self.pos1 = self.origin; + self.pos2 = self.angles; + } setorigin(self, self.pos1); self.angles = self.pos2; self.health = self.max_health; // TODO: check if resetting to max_health is wise here @@ -292,7 +319,16 @@ void Monster_Fade () float Monster_CanJump (vector vel) { - local vector old = self.velocity; + if(self.state) + return FALSE; // already attacking + if not(self.flags & FL_ONGROUND) + return FALSE; // not on the ground + if(self.health <= 0) + return FALSE; // called when dead? + if(time < self.attack_finished_single) + return FALSE; // still attacking + + vector old = self.velocity; self.velocity = vel; tracetoss(self, self); @@ -305,20 +341,15 @@ float Monster_CanJump (vector vel) float monster_leap (float anm, void() touchfunc, vector vel, float anim_finished) { - if not(self.flags & FL_ONGROUND) - return FALSE; - if(self.health < 1) - return FALSE; // called when dead? - if not(Monster_CanJump(vel)) + if(!Monster_CanJump(vel)) return FALSE; - self.frame = anm; + monsters_setframe(anm); self.state = MONSTER_STATE_ATTACK_LEAP; self.touch = touchfunc; self.origin_z += 1; self.velocity = vel; - if (self.flags & FL_ONGROUND) - self.flags -= FL_ONGROUND; + self.flags &~= FL_ONGROUND; self.attack_finished_single = time + anim_finished; @@ -394,17 +425,20 @@ float trace_path(vector from, vector to) return ((trace1 < trace_fraction) ? trace1 : trace_fraction); } +.float last_trace; vector monster_pickmovetarget(entity targ) { // enemy is always preferred target if(self.enemy) { self.monster_movestate = MONSTER_MOVE_ENEMY; + self.last_trace = time + 0.1; return self.enemy.origin; } if(targ) { self.monster_movestate = MONSTER_MOVE_WANDER; + self.last_trace = time + 0.5; return targ.origin; } @@ -413,12 +447,14 @@ vector monster_pickmovetarget(entity targ) case MONSTER_MOVE_OWNER: { self.monster_movestate = MONSTER_MOVE_OWNER; + self.last_trace = time + 0.3; if(self.monster_owner && self.monster_owner.classname != "monster_swarm") return self.monster_owner.origin; } case MONSTER_MOVE_WANDER: { self.monster_movestate = MONSTER_MOVE_WANDER; + self.last_trace = time + 2; self.angles_y = random() * 500; makevectors(self.angles); @@ -427,18 +463,19 @@ vector monster_pickmovetarget(entity targ) case MONSTER_MOVE_SPAWNLOC: { self.monster_movestate = MONSTER_MOVE_SPAWNLOC; + self.last_trace = time + 2; return self.pos1; } default: case MONSTER_MOVE_NOMOVE: { self.monster_movestate = MONSTER_MOVE_NOMOVE; + self.last_trace = time + 2; return self.origin; } } } -.float last_trace; void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_run, float manim_walk, float manim_idle) { if(self.target) @@ -500,7 +537,7 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_ if(gameover || time < game_starttime) { runspeed = walkspeed = 0; - self.frame = manim_idle; + monsters_setframe(manim_idle); movelib_beak_simple(stopspeed); return; } @@ -527,15 +564,13 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_ if(self.enemy) monster_sound(self.msound_sight, 0, FALSE); } + + if(self.state == MONSTER_STATE_ATTACK_MELEE && time >= self.attack_finished_single) + self.state = 0; - if(time >= self.last_trace) - { - if(self.monster_movestate == MONSTER_MOVE_WANDER && self.goalentity.classname != "td_waypoint") - self.last_trace = time + 2; - else - self.last_trace = time + 0.5; + if(self.state != MONSTER_STATE_ATTACK_MELEE) // don't move if set + if(time >= self.last_trace || self.enemy) // update enemy instantly self.moveto = monster_pickmovetarget(targ); - } if not(self.enemy) monster_sound(self.msound_idle, 5, TRUE); @@ -543,7 +578,12 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_ vector angles_face = vectoangles(self.moveto - self.origin); vector owner_face = vectoangles(self.monster_owner.origin - self.origin); vector enemy_face = vectoangles(self.enemy.origin - self.origin); - self.angles_y = angles_face_y; + + if(self.state != MONSTER_STATE_ATTACK_LEAP) + { + self.angles_y = angles_face_y; + self.v_angle = self.angles; + } if(self.state == MONSTER_STATE_ATTACK_LEAP && (self.flags & FL_ONGROUND)) { @@ -561,15 +601,15 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_ if(self.flags & FL_ONGROUND) movelib_jump_simple(100); - if(vlen(self.origin - self.moveto) > 64) + if(vlen(self.origin - self.moveto) > 64 * self.scale) { - if(self.flags & FL_FLY) + if(self.flags & FL_FLY || self.flags & FL_SWIM) movelib_move_simple(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6); else movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6); if(time > self.pain_finished) if(time > self.attack_finished_single) - self.frame = ((self.enemy) ? manim_run : manim_walk); + monsters_setframe((self.enemy) ? manim_run : manim_walk); } else { @@ -578,7 +618,7 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_ if(time > self.pain_finished) if (vlen(self.velocity) <= 30) { - self.frame = manim_idle; + monsters_setframe(manim_idle); if(self.enemy) self.angles_y = enemy_face_y; else @@ -588,6 +628,10 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_ if(self.enemy && self.checkattack) self.checkattack(); + + self.SendFlags |= MSF_ANG; + if(vlen(self.velocity) > 0) + self.SendFlags |= MSF_MOVE; } void monsters_setstatus() @@ -596,13 +640,133 @@ void monsters_setstatus() self.stat_monsters_killed = monsters_killed; } -void Monster_Appear () +void Monster_Appear() { self.enemy = activator; self.spawnflags &~= MONSTERFLAG_APPEAR; self.monster_spawnfunc(); } +float Monster_CheckAppearFlags(entity ent) +{ + if not(ent.spawnflags & MONSTERFLAG_APPEAR) + return FALSE; + + ent.think = func_null; + ent.nextthink = -1; + ent.use = Monster_Appear; + ent.flags = FL_MONSTER; // set so this monster can get butchered + + return TRUE; +} + +void monsters_reset() +{ + setorigin(self, self.pos1); + self.angles = self.pos2; + + self.health = self.max_health; + self.velocity = '0 0 0'; + self.enemy = world; + self.goalentity = world; + self.attack_finished_single = 0; + self.moveto = self.origin; + + WaypointSprite_UpdateHealth(self.sprite, self.health); +} + +float monster_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_MONSTER); + WriteByte(MSG_ENTITY, sf); + if(sf & MSF_SETUP) + { + WriteByte(MSG_ENTITY, self.monsterid); + + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteAngle(MSG_ENTITY, self.angles_x); + WriteAngle(MSG_ENTITY, self.angles_y); + + WriteByte(MSG_ENTITY, self.scale); + WriteByte(MSG_ENTITY, self.skin); + } + + if(sf & MSF_SIZE) + { + WriteByte(MSG_ENTITY, self.scale); + + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + } + + if(sf & MSF_ANG) + { + WriteShort(MSG_ENTITY, rint(self.angles_x)); + WriteShort(MSG_ENTITY, rint(self.angles_y)); + } + + if(sf & MSF_MOVE) + { + WriteShort(MSG_ENTITY, rint(self.origin_x)); + WriteShort(MSG_ENTITY, rint(self.origin_y)); + WriteShort(MSG_ENTITY, rint(self.origin_z)); + + WriteShort(MSG_ENTITY, rint(self.velocity_x)); + WriteShort(MSG_ENTITY, rint(self.velocity_y)); + WriteShort(MSG_ENTITY, rint(self.velocity_z)); + + WriteShort(MSG_ENTITY, rint(self.angles_y)); + } + + if(sf & MSF_ANIM) + { + WriteCoord(MSG_ENTITY, self.anim_start_time); + WriteByte(MSG_ENTITY, self.frame); + } + + if(sf & MSF_STATUS) + { + WriteByte(MSG_ENTITY, self.team); + + if(self.health <= 0) + WriteByte(MSG_ENTITY, 0); + else + WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); + } + + return TRUE; +} + +void monster_link(void() spawnproc) +{ + Net_LinkEntity(self, TRUE, 0, monster_send); + self.think = spawnproc; + self.nextthink = time; +} + +void monsters_corpse_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + self.health -= damage; + + Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, self, attacker); + + if(self.health <= -100) // 100 health until gone? + { + Violence_GibSplash(self, 1, 0.5, attacker); + + self.think = SUB_Remove; + self.nextthink = time + 0.1; + } +} + void monsters_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) { if(self.frozen && deathtype != DEATH_KILL) @@ -615,7 +779,8 @@ void monsters_damage (entity inflictor, entity attacker, float damage, float dea if(monster_isvalidtarget(attacker, self)) self.enemy = attacker; - damage *= self.armorvalue; + if(deathtype != DEATH_KILL) + damage *= self.armorvalue; self.health -= damage; @@ -660,14 +825,23 @@ void monsters_damage (entity inflictor, entity attacker, float damage, float dea frag_attacker = attacker; frag_target = self; MUTATOR_CALLHOOK(MonsterDies); + + if(self.health <= -100) // check if we're already gibbed + { + Violence_GibSplash(self, 1, 0.5, attacker); + + self.think = SUB_Remove; + self.nextthink = time + 0.1; + } } + + self.SendFlags |= MSF_STATUS; } // used to hook into monster post death functions without a mutator void monster_hook_death() { - if(self.sprite) - WaypointSprite_Kill(self.sprite); + WaypointSprite_Kill(self.sprite); if(self.weaponentity) { @@ -683,26 +857,31 @@ void monster_hook_death() if(self.candrop && self.weapon) W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325'); - if(self.realowner.classname == "monster_spawner") - self.realowner.spawner_monstercount -= 1; - if(IS_CLIENT(self.realowner)) self.realowner.monstercount -= 1; + self.event_damage = monsters_corpse_damage; + self.solid = SOLID_CORPSE; + self.takedamage = DAMAGE_AIM; + self.enemy = world; + self.movetype = MOVETYPE_TOSS; + totalspawned -= 1; } // used to hook into monster post spawn functions without a mutator void monster_hook_spawn() { - Monster_CheckMinibossFlag(); + if not(self.monster_respawned) + Monster_CheckMinibossFlag(); self.max_health = self.health; self.pain_finished = self.nextthink; + self.anim_start_time = time; monster_precachesounds(self); - if(teamplay && self.team) + if(self.team) { //self.colormod = Team_ColorRGB(self.team); //self.glowmod = self.colormod; @@ -725,17 +904,21 @@ void monster_hook_spawn() if(autocvar_g_monsters_healthbars) { - WaypointSprite_Spawn(self.netname, 0, 600, self, '0 0 1' * self.sprite_height, world, 0, self, sprite, FALSE, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0')); + WaypointSprite_Spawn(self.message, 0, 600, self, '0 0 1' * self.sprite_height, world, 0, self, sprite, FALSE, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0')); WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); WaypointSprite_UpdateHealth(self.sprite, self.health); } + self.message = self.oldmessage; + monster_sound(self.msound_spawn, 0, FALSE); MUTATOR_CALLHOOK(MonsterSpawn); + + self.SendFlags = MSF_SETUP; } -float monster_initialize(string net_name, +float monster_initialize(string net_name, float mon_id, string bodymodel, vector min_s, vector max_s, @@ -753,35 +936,36 @@ float monster_initialize(string net_name, if(monster_skill == autocvar_g_monsters_skill_insane && (self.spawnflags & MONSTERSKILL_NOTINSANE)) { return FALSE; } if(monster_skill >= autocvar_g_monsters_skill_nightmare && (self.spawnflags & MONSTERSKILL_NOTNIGHTMARE)) { return FALSE; } - if(self.model == "") + if(self.model == "" || self.model == "null") if(bodymodel == "") error("monsters: missing bodymodel!"); if(self.netname == "") { if(net_name != "" && IS_PLAYER(self.realowner)) - net_name = strzone(strdecolorize(sprintf("%s's %s", self.realowner.netname, net_name))); + net_name = strzone(sprintf("%s^7's %s", self.realowner.netname, net_name)); self.netname = ((net_name == "") ? self.classname : net_name); + self.oldmessage = self.message; + self.message = strzone(strdecolorize(self.netname)); } + if(autocvar_g_monsters_giants_only) + self.spawnflags |= MONSTERFLAG_GIANT; + if not(self.scale) self.scale = 1; - - if(self.spawnflags & MONSTERFLAG_GIANT && !autocvar_g_monsters_nogiants) - ScaleMonster(5); - else - ScaleMonster(self.scale); - min_s *= self.scale; - max_s *= self.scale; - + if(self.spawnflags & MONSTERFLAG_GIANT && !autocvar_g_monsters_nogiants) + self.scale = 5; + + if(self.team && !teamplay) self.team = 0; self.flags = FL_MONSTER; - if(self.model != "") - bodymodel = self.model; + if(self.model && self.model != "null") + bodymodel = self.model; // TODO: find out why monsters can become invisible if not(self.spawnflags & MONSTERFLAG_SPAWNED) // naturally spawned monster if not(self.monster_respawned) @@ -790,14 +974,14 @@ float monster_initialize(string net_name, precache_model(bodymodel); setmodel(self, bodymodel); - - setsize(self, min_s, max_s); + ScaleMonster(self.scale, min_s, max_s); self.takedamage = DAMAGE_AIM; self.bot_attack = TRUE; self.iscreature = TRUE; self.teleportable = TRUE; self.damagedbycontents = TRUE; + self.monsterid = mon_id; self.damageforcescale = 0.003; self.monster_die = dieproc; self.event_damage = monsters_damage; @@ -807,17 +991,19 @@ float monster_initialize(string net_name, self.movetype = MOVETYPE_WALK; self.delay = -1; // used in attack delay code monsters_spawned += 1; - self.think = spawnproc; - self.nextthink = time; self.enemy = world; self.velocity = '0 0 0'; self.moveto = self.origin; - self.pos1 = self.origin; self.pos2 = self.angles; + self.reset = monsters_reset; self.candrop = TRUE; + self.view_ofs = '0 0 1' * self.maxs_z; + + if not(self.ticrate) + self.ticrate = 0.05; if not(self.armorvalue) - self.armorvalue = 1; + self.armorvalue = 1; // multiplier if not(self.target_range) self.target_range = autocvar_g_monsters_target_range; @@ -837,9 +1023,13 @@ float monster_initialize(string net_name, if not(nodrop) { setorigin(self, self.origin); - tracebox(self.origin + '0 0 100', min_s, max_s, self.origin - '0 0 10000', MOVE_WORLDONLY, self); + tracebox(self.origin + '0 0 100', self.mins, self.maxs, self.origin - '0 0 10000', MOVE_WORLDONLY, self); setorigin(self, trace_endpos); } + + self.pos1 = self.origin; + + monster_link(spawnproc); return TRUE; } diff --git a/qcsrc/server/monsters/lib/monsters_early.qh b/qcsrc/server/monsters/lib/monsters_early.qh new file mode 100644 index 000000000..5a2f2b7bc --- /dev/null +++ b/qcsrc/server/monsters/lib/monsters_early.qh @@ -0,0 +1,52 @@ +// for definitions used outside the monsters folder + +#ifdef SVQC +.string spawnmob; +.float monster_attack; + +float monster_skill; +float spawncode_first_load; // used to tell the player the monster database is loading (TODO: fix this?) + +.entity monster_owner; // new monster owner entity, fixes non-solid monsters +.float monstercount; // per player monster count + +.float stat_monsters_killed; // stats +.float stat_monsters_total; +float monsters_total; +float monsters_killed; +void monsters_setstatus(); // monsters.qc +.float monster_moveflags; // checks where to move when not attacking +#endif // SVQC + +#ifndef MENUQC +.float monsterid; +// Monster IDs +float MONSTER_FIRST = 1; +float MONSTER_ZOMBIE = 2; +float MONSTER_OGRE = 3; +float MONSTER_DEMON = 4; +float MONSTER_SHAMBLER = 5; +float MONSTER_KNIGHT = 6; +float MONSTER_MARINE = 7; +float MONSTER_SCRAG = 8; +float MONSTER_DOG = 9; +float MONSTER_TARBABY = 10; +float MONSTER_HELLKNIGHT = 11; +float MONSTER_FISH = 12; +float MONSTER_MAGE = 13; +float MONSTER_ENFORCER = 14; +float MONSTER_SPIDER = 15; +float MONSTER_LAST = 16; + +float MSF_UPDATE = 2; +float MSF_STATUS = 4; +float MSF_SETUP = 8; +float MSF_ANG = 16; +float MSF_MOVE = 32; +.float anim_start_time; +float MSF_ANIM = 64; +float MSF_SIZE = 128; + +float MSF_FULL_UPDATE = 16777215; + +#endif // CSQC/SVQC \ No newline at end of file diff --git a/qcsrc/server/monsters/lib/spawn.qc b/qcsrc/server/monsters/lib/spawn.qc index 87704d80f..40c2b7f6c 100644 --- a/qcsrc/server/monsters/lib/spawn.qc +++ b/qcsrc/server/monsters/lib/spawn.qc @@ -10,14 +10,6 @@ float spawnmonster_checkinlist(string monster, string list) entity spawnmonster (string monster, entity spawnedby, entity own, vector orig, float respwn, float moveflag) { - if not(autocvar_g_monsters) - { - if(IS_CLIENT(spawnedby)) - Send_Notification(NOTIF_ONE, spawnedby, MSG_INFO, INFO_MONSTERS_DISABLED); - - return world; - } - if(!spawncode_first_load) { initialize_field_db(); @@ -43,24 +35,21 @@ entity spawnmonster (string monster, entity spawnedby, entity own, vector orig, if (spawnedby.classname == "monster_swarm") e.monster_owner = own; - else if(IS_CLIENT(spawnedby)) + else if(IS_PLAYER(spawnedby)) { if(teamplay && autocvar_g_monsters_teams) e.team = spawnedby.team; // colors handled in spawn code - if(teamplay) + if(e.team) e.colormap = 1024; else e.colormap = spawnedby.colormap; if(autocvar_g_monsters_owners) - e.monster_owner = own; // using owner makes the monster non-solid for its master + e.monster_owner = own; // using .owner makes the monster non-solid for its master e.angles = spawnedby.angles; } - - if(autocvar_g_monsters_giants_only) - e.spawnflags |= MONSTERFLAG_GIANT; monster = strcat("$ spawnfunc_monster_", monster); diff --git a/qcsrc/server/monsters/monster/demon.qc b/qcsrc/server/monsters/monster/demon.qc index 55d189a4f..3e52b8cc2 100644 --- a/qcsrc/server/monsters/monster/demon.qc +++ b/qcsrc/server/monsters/monster/demon.qc @@ -1,3 +1,14 @@ +#ifndef MENUQC +// size +const vector DEMON_MIN = '-32 -32 -24'; +const vector DEMON_MAX = '32 32 24'; + +// model +string DEMON_MODEL = "models/monsters/demon.mdl"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_demon; float autocvar_g_monster_demon_health; @@ -6,35 +17,29 @@ float autocvar_g_monster_demon_damage; float autocvar_g_monster_demon_speed_walk; float autocvar_g_monster_demon_speed_run; -// size -const vector DEMON_MIN = '-32 -32 -24'; -const vector DEMON_MAX = '32 32 24'; - -// animation -#define demon_anim_stand 0 -#define demon_anim_walk 1 -#define demon_anim_run 2 -#define demon_anim_leap 3 -#define demon_anim_pain 4 -#define demon_anim_death 5 -#define demon_anim_attack 6 +// animations +const float demon_anim_stand = 0; +const float demon_anim_walk = 1; +const float demon_anim_run = 2; +const float demon_anim_leap = 3; +const float demon_anim_pain = 4; +const float demon_anim_death = 5; +const float demon_anim_attack = 6; void demon_think () { self.think = demon_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_demon_speed_run, autocvar_g_monster_demon_speed_walk, 100, demon_anim_run, demon_anim_walk, demon_anim_stand); } void demon_attack_melee () { - float bigdmg = autocvar_g_monster_demon_damage * self.scale; - - self.frame = demon_anim_attack; + monsters_setframe(demon_anim_attack); self.attack_finished_single = time + 1; - monster_melee(self.enemy, bigdmg * monster_skill, 120, DEATH_MONSTER_FIEND); + monster_melee(self.enemy, autocvar_g_monster_demon_damage, 0.3, DEATH_MONSTER_FIEND, TRUE); } void Demon_JumpTouch () @@ -70,14 +75,9 @@ void demon_die () { Monster_CheckDropCvars ("demon"); - self.frame = demon_anim_death; - self.think = Monster_Fade; - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.movetype = MOVETYPE_TOSS; - self.enemy = world; - self.nextthink = time + 3; + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe(demon_anim_death); monster_hook_death(); // for post-death mods } @@ -93,33 +93,28 @@ void demon_spawn () self.attack_melee = demon_attack_melee; self.attack_ranged = demon_jump; self.nextthink = time + random() * 0.5 + 0.1; - self.frame = demon_anim_stand; self.think = demon_think; self.sprite_height = 30; + monsters_setframe(demon_anim_stand); + monster_hook_spawn(); // for post-spawn mods } -/* QUAKED monster_demon (1 0 0) (-32 -32 -24) (32 32 64) Ambush */ void spawnfunc_monster_demon () { if not(autocvar_g_monster_demon) { remove(self); return; } self.monster_spawnfunc = spawnfunc_monster_demon; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } self.scale = 1.3; if not (monster_initialize( - "Fiend", - "models/monsters/demon.mdl", + "Fiend", MONSTER_DEMON, + DEMON_MODEL, DEMON_MIN, DEMON_MAX, FALSE, demon_die, demon_spawn)) @@ -131,3 +126,5 @@ void spawnfunc_monster_demon () // Compatibility with old spawns void spawnfunc_monster_demon1 () { spawnfunc_monster_demon(); } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/dog.qc b/qcsrc/server/monsters/monster/dog.qc index 686ad510f..0095a905b 100644 --- a/qcsrc/server/monsters/monster/dog.qc +++ b/qcsrc/server/monsters/monster/dog.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size const vector DOG_MAX = '16 16 12'; const vector DOG_MIN = '-16 -16 -24'; +// model +string DOG_MODEL = "models/monsters/dog.dpm"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_dog; float autocvar_g_monster_dog_health; @@ -11,12 +18,12 @@ float autocvar_g_monster_dog_speed_walk; float autocvar_g_monster_dog_speed_run; // animations -#define dog_anim_idle 0 -#define dog_anim_walk 1 -#define dog_anim_run 2 -#define dog_anim_attack 3 -#define dog_anim_die 4 -#define dog_anim_pain 5 +const float dog_anim_idle = 0; +const float dog_anim_walk = 1; +const float dog_anim_run = 2; +const float dog_anim_attack = 3; +const float dog_anim_die = 4; +const float dog_anim_pain = 5; void Dog_JumpTouch () { @@ -37,19 +44,17 @@ void Dog_JumpTouch () void dog_think () { self.think = dog_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_dog_speed_run, autocvar_g_monster_dog_speed_walk, 50, dog_anim_run, dog_anim_walk, dog_anim_idle); } void dog_attack () { - float bigdmg = autocvar_g_monster_dog_bite_damage * self.scale; - - self.frame = dog_anim_attack; + monsters_setframe(dog_anim_attack); self.attack_finished_single = time + 0.7; - monster_melee(self.enemy, bigdmg * monster_skill, 100, DEATH_MONSTER_DOG_BITE); + monster_melee(self.enemy, autocvar_g_monster_dog_bite_damage, 0.2, DEATH_MONSTER_DOG_BITE, TRUE); } float dog_jump () @@ -65,14 +70,9 @@ void dog_die () { Monster_CheckDropCvars ("dog"); - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.nextthink = time + 2.1; - self.think = Monster_Fade; - self.movetype = MOVETYPE_TOSS; - self.frame = dog_anim_die; + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe(dog_anim_die); monster_hook_death(); // for post-death mods } @@ -89,8 +89,8 @@ void dog_spawn () self.checkattack = GenericCheckAttack; self.nextthink = time + random() * 0.5 + 0.1; self.think = dog_think; - self.frame = dog_anim_idle; self.sprite_height = 20; + monsters_setframe(dog_anim_idle); monster_hook_spawn(); // for post-spawn mods } @@ -101,17 +101,12 @@ void spawnfunc_monster_dog () self.monster_spawnfunc = spawnfunc_monster_dog; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } if not (monster_initialize( - "Cerberus", - "models/monsters/dog.dpm", + "Cerberus", MONSTER_DOG, + DOG_MODEL, DOG_MIN, DOG_MAX, FALSE, dog_die, dog_spawn)) @@ -120,3 +115,5 @@ void spawnfunc_monster_dog () return; } } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/enforcer.qc b/qcsrc/server/monsters/monster/enforcer.qc index d52fdd046..88af41902 100644 --- a/qcsrc/server/monsters/monster/enforcer.qc +++ b/qcsrc/server/monsters/monster/enforcer.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size const vector ENFORCER_MIN = '-32 -32 0'; const vector ENFORCER_MAX = '32 32 64'; +// model +string ENFORCER_MODEL = "models/turrets/ewheel-base2.md3"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_enforcer; float autocvar_g_monster_enforcer_health; @@ -10,16 +17,16 @@ float autocvar_g_monster_enforcer_speed_run; float autocvar_g_monster_enforcer_attack_uzi_bullets; // animations -#define enforcer_anim_stop 0 -#define enforcer_anim_walk 1 -#define enforcer_anim_run 2 -#define enforcer_anim_walkback 3 -#define enforcer_anim_runback 4 +const float enforcer_anim_stop = 0; +const float enforcer_anim_walk = 1; +const float enforcer_anim_run = 2; +const float enforcer_anim_walkback = 3; +const float enforcer_anim_runback = 4; void enforcer_think () { self.think = enforcer_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; if(self.delay != -1) self.nextthink = self.delay; @@ -27,30 +34,6 @@ void enforcer_think () monster_move(autocvar_g_monster_enforcer_speed_run, autocvar_g_monster_enforcer_speed_walk, 100, enforcer_anim_run, enforcer_anim_walk, enforcer_anim_stop); } -void enforcer_laser () -{ - self.attack_finished_single = time + 0.8; - W_Laser_Attack(0); -} - -float enf_missile_laser () -{ - enforcer_laser(); - return TRUE; -} - -void enforcer_shotgun () -{ - self.attack_finished_single = time + 0.8; - W_Shotgun_Attack(); -} - -float enf_missile_shotgun () -{ - enforcer_shotgun(); - return TRUE; -} - .float enf_cycles; void enforcer_uzi_fire () { @@ -67,56 +50,56 @@ void enforcer_uzi_fire () self.monster_delayedattack = enforcer_uzi_fire; } -void enforcer_uzi () -{ - self.attack_finished_single = time + 0.8; - self.delay = time + 0.1; - self.monster_delayedattack = enforcer_uzi_fire; -} - -float enf_missile_uzi () -{ - self.enf_cycles = 0; - enforcer_uzi(); - return TRUE; -} - -void enforcer_rl () -{ - self.attack_finished_single = time + 0.8; - W_Rocket_Attack(); -} - -float enf_missile_rocket () -{ - enforcer_rl(); - return TRUE; -} - -void enforcer_electro () -{ - self.attack_finished_single = time + 0.8; - W_Electro_Attack(); -} - -float enf_missile_plasma () +float enforcer_attack() { - enforcer_electro(); - return TRUE; + makevectors(self.angles); + switch(self.weapon) + { + case WEP_ROCKET_LAUNCHER: + { + self.attack_finished_single = time + 0.8; + W_Rocket_Attack(); + return TRUE; + } + case WEP_ELECTRO: + { + self.attack_finished_single = time + 0.8; + W_Electro_Attack(); + return TRUE; + } + case WEP_SHOTGUN: + { + self.attack_finished_single = time + 0.8; + W_Shotgun_Attack(); + return TRUE; + } + case WEP_UZI: + { + self.enf_cycles = 0; + self.attack_finished_single = time + 0.8; + self.delay = time + 0.1; + self.monster_delayedattack = enforcer_uzi_fire; + return TRUE; + } + case WEP_LASER: + { + self.attack_finished_single = time + 0.8; + W_Laser_Attack(0); + } + default: + return FALSE; // no weapon? + } + + // never gets here } void enforcer_die () { Monster_CheckDropCvars ("enforcer"); - self.solid = SOLID_NOT; - self.movetype = MOVETYPE_TOSS; - self.think = Monster_Fade; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.nextthink = time + 2.1; - self.frame = enforcer_anim_stop; + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe(enforcer_anim_stop); monster_hook_death(); // for post-death mods } @@ -133,6 +116,8 @@ void enforcer_spawn () self.think = enforcer_think; self.items = (IT_SHELLS | IT_ROCKETS | IT_NAILS | IT_CELLS); self.sprite_height = 45; + self.attack_ranged = enforcer_attack; + self.view_ofs *= 0.5; self.weaponentity = spawn(); self.weaponentity.owner = self; @@ -143,32 +128,14 @@ void enforcer_spawn () setmodel(self.weaponentity, "models/turrets/ewheel-gun1.md3"); setattachment(self.weaponentity, self, "tag_head"); - local float r = random(); - if (r < 0.20) - { - self.attack_ranged = enf_missile_rocket; - self.weapon = WEP_ROCKET_LAUNCHER; - } - else if (r < 0.40) - { - self.attack_ranged = enf_missile_plasma; - self.weapon = WEP_ELECTRO; - } - else if (r < 0.60) - { - self.attack_ranged = enf_missile_shotgun; - self.weapon = WEP_SHOTGUN; - } - else if (r < 0.80) - { - self.attack_ranged = enf_missile_uzi; - self.weapon = WEP_UZI; - } - else - { - self.attack_ranged = enf_missile_laser; - self.weapon = WEP_LASER; - } + RandomSelection_Init(); + RandomSelection_Add(world, WEP_ROCKET_LAUNCHER, "", 1, 1); + RandomSelection_Add(world, WEP_ELECTRO, "", 1, 1); + RandomSelection_Add(world, WEP_SHOTGUN, "", 1, 1); + RandomSelection_Add(world, WEP_UZI, "", 1, 1); + RandomSelection_Add(world, WEP_LASER, "", 1, 1); + + self.weapon = RandomSelection_chosen_float; monster_hook_spawn(); // for post-spawn mods } @@ -179,17 +146,12 @@ void spawnfunc_monster_enforcer () self.monster_spawnfunc = spawnfunc_monster_enforcer; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } if not (monster_initialize( - "Enforcer", - "models/turrets/ewheel-base2.md3", + "Enforcer", MONSTER_ENFORCER, + ENFORCER_MODEL, ENFORCER_MIN, ENFORCER_MAX, FALSE, enforcer_die, enforcer_spawn)) @@ -198,3 +160,5 @@ void spawnfunc_monster_enforcer () return; } } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/fish.qc b/qcsrc/server/monsters/monster/fish.qc index 31e74c691..677a496f8 100644 --- a/qcsrc/server/monsters/monster/fish.qc +++ b/qcsrc/server/monsters/monster/fish.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size const vector FISH_MIN = '-16 -16 -24'; const vector FISH_MAX = '16 16 16'; +// model +string FISH_MODEL = "models/monsters/fish.mdl"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_fish; float autocvar_g_monster_fish_health; @@ -10,40 +17,34 @@ float autocvar_g_monster_fish_speed_walk; float autocvar_g_monster_fish_speed_run; // animations -#define fish_anim_attack 0 -#define fish_anim_death 1 -#define fish_anim_swim 2 -#define fish_anim_pain 3 +const float fish_anim_attack = 0; +const float fish_anim_death = 1; +const float fish_anim_swim = 2; +const float fish_anim_pain = 3; void fish_think () { self.think = fish_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_fish_speed_run, autocvar_g_monster_fish_speed_walk, 10, fish_anim_swim, fish_anim_swim, fish_anim_swim); } void fish_attack () { - float bigdmg = autocvar_g_monster_fish_damage * self.scale; - - self.frame = fish_anim_attack; + monsters_setframe(fish_anim_attack); self.attack_finished_single = time + 0.5; - monster_melee(self.enemy, bigdmg * monster_skill, 60, DEATH_MONSTER_FISH); + monster_melee(self.enemy, autocvar_g_monster_fish_damage, 0.1, DEATH_MONSTER_FISH, FALSE); } void fish_die () { Monster_CheckDropCvars ("fish"); - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.frame = fish_anim_death; - self.think = Monster_Fade; - self.nextthink = time + 2.1; + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe(fish_anim_death); monster_hook_death(); // for post-death mods } @@ -71,19 +72,14 @@ void spawnfunc_monster_fish () self.monster_spawnfunc = spawnfunc_monster_fish; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } self.scale = 1.3; if not (monster_initialize( - "Rotfish", - "models/monsters/fish.mdl", + "Rotfish", MONSTER_FISH, + FISH_MODEL, FISH_MIN, FISH_MAX, TRUE, fish_die, fish_spawn)) @@ -92,3 +88,5 @@ void spawnfunc_monster_fish () return; } } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/hknight.qc b/qcsrc/server/monsters/monster/hknight.qc index 22333f1ae..fca42d953 100644 --- a/qcsrc/server/monsters/monster/hknight.qc +++ b/qcsrc/server/monsters/monster/hknight.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size const vector HELLKNIGHT_MIN = '-16 -16 -24'; const vector HELLKNIGHT_MAX = '16 16 32'; +// model +string HELLKNIGHT_MODEL = "models/monsters/hknight.mdl"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_hellknight; float autocvar_g_monster_hellknight_health; @@ -26,20 +33,20 @@ float autocvar_g_monster_hellknight_jump_damage; float autocvar_g_monster_hellknight_jump_dist; // animations -#define hellknight_anim_stand 0 -#define hellknight_anim_walk 1 -#define hellknight_anim_run 2 -#define hellknight_anim_pain 3 -#define hellknight_anim_death1 4 -#define hellknight_anim_death2 5 -#define hellknight_anim_charge1 6 -#define hellknight_anim_magic1 7 -#define hellknight_anim_magic2 8 -#define hellknight_anim_charge2 9 -#define hellknight_anim_slice 10 -#define hellknight_anim_smash 11 -#define hellknight_anim_wattack 12 -#define hellknight_anim_magic3 13 +const float hellknight_anim_stand = 0; +const float hellknight_anim_walk = 1; +const float hellknight_anim_run = 2; +const float hellknight_anim_pain = 3; +const float hellknight_anim_death1 = 4; +const float hellknight_anim_death2 = 5; +const float hellknight_anim_charge1 = 6; +const float hellknight_anim_magic1 = 7; +const float hellknight_anim_magic2 = 8; +const float hellknight_anim_charge2 = 9; +const float hellknight_anim_slice = 10; +const float hellknight_anim_smash = 11; +const float hellknight_anim_wattack = 12; +const float hellknight_anim_magic3 = 13; void hknight_spike_think() { @@ -120,7 +127,7 @@ float hknight_checkmagic () local float dot = 0; // use magic to kill zombies as they heal too fast for sword - if (self.enemy.classname == "monster_zombie") + if (self.enemy.monsterid == MONSTER_ZOMBIE) { traceline((self.absmin + self.absmax) * 0.5, (self.enemy.absmin + self.enemy.absmax) * 0.5, FALSE, self); if (trace_ent == self.enemy) @@ -172,7 +179,7 @@ void CheckContinueCharge () void hellknight_think () { self.think = hellknight_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_hellknight_speed_run, autocvar_g_monster_hellknight_speed_walk, 100, hellknight_anim_run, hellknight_anim_walk, hellknight_anim_stand); } @@ -185,7 +192,7 @@ void hellknight_magic () if(self.hknight_cycles >= 5) { - self.frame = hellknight_anim_magic1; + monsters_setframe(hellknight_anim_magic1); self.attack_finished_single = time + 0.7; hknight_infernowarning(); self.think = hellknight_think; @@ -247,7 +254,7 @@ void hellknight_fireball () void hellknight_magic2 () { - self.frame = hellknight_anim_magic2; + monsters_setframe(hellknight_anim_magic2); self.attack_finished_single = time + 1.2; self.delay = time + 0.4; self.monster_delayedattack = hellknight_fireball; @@ -265,7 +272,7 @@ void hellknight_spikes () void hellknight_magic3 () { - self.frame = hellknight_anim_magic3; + monsters_setframe(hellknight_anim_magic3); self.attack_finished_single = time + 1; self.think = hellknight_spikes; self.nextthink = time + 0.4; @@ -273,42 +280,42 @@ void hellknight_magic3 () void hellknight_charge () { - self.frame = hellknight_anim_charge1; + monsters_setframe(hellknight_anim_charge1); self.attack_finished_single = time + 0.5; hknight_checkmagic(); - monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE); + monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, FALSE); hknight_checkmagic(); } void hellknight_charge2 () { - self.frame = hellknight_anim_charge2; + monsters_setframe(hellknight_anim_charge2); self.attack_finished_single = time + 0.5; CheckContinueCharge (); - monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE); + monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, FALSE); } void hellknight_slice () { - self.frame = hellknight_anim_slice; + monsters_setframe(hellknight_anim_slice); self.attack_finished_single = time + 0.7; - monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE); + monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, FALSE); } void hellknight_smash () { - self.frame = hellknight_anim_smash; + monsters_setframe(hellknight_anim_smash); self.attack_finished_single = time + 0.7; - monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE); + monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, TRUE); } void hellknight_weapon_attack () { - self.frame = hellknight_anim_wattack; + monsters_setframe(hellknight_anim_wattack); self.attack_finished_single = time + 0.7; - monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 70, DEATH_MONSTER_HKNIGHT_MELEE); + monster_melee(self.enemy, autocvar_g_monster_hellknight_melee_damage, 0.3, DEATH_MONSTER_HKNIGHT_MELEE, TRUE); } float hknight_type; @@ -340,7 +347,7 @@ float hknight_magic () self.hknight_cycles = 0; - if (self.enemy.classname == "monster_zombie") + if (self.enemy.monsterid == MONSTER_ZOMBIE) { // always use fireball to kill zombies hellknight_magic2(); @@ -397,13 +404,9 @@ void hellknight_die () float chance = random(); Monster_CheckDropCvars ("hellknight"); - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.movetype = MOVETYPE_TOSS; - self.think = Monster_Fade; - self.nextthink = time + 2.1; + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe((random() > 0.5) ? hellknight_anim_death1 : hellknight_anim_death2); if(chance < 0.10 || self.flags & MONSTERFLAG_MINIBOSS) if(self.candrop) @@ -411,11 +414,6 @@ void hellknight_die () self.superweapons_finished = time + autocvar_g_balance_superweapons_time + 5; // give the player a few seconds to find the weapon self.weapon = WEP_FIREBALL; } - - if (random() > 0.5) - self.frame = hellknight_anim_death1; - else - self.frame = hellknight_anim_death2; monster_hook_death(); // for post-death mods } @@ -433,7 +431,8 @@ void hellknight_spawn () self.nextthink = time + random() * 0.5 + 0.1; self.think = hellknight_think; self.sprite_height = 30; - self.frame = hellknight_anim_stand; + + monsters_setframe(hellknight_anim_stand); monster_hook_spawn(); // for post-spawn mods } @@ -444,19 +443,14 @@ void spawnfunc_monster_hell_knight () self.monster_spawnfunc = spawnfunc_monster_hell_knight; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } self.scale = 1.3; if not (monster_initialize( - "Hell-knight", - "models/monsters/hknight.mdl", + "Hell-knight", MONSTER_HELLKNIGHT, + HELLKNIGHT_MODEL, HELLKNIGHT_MIN, HELLKNIGHT_MAX, FALSE, hellknight_die, hellknight_spawn)) @@ -470,3 +464,5 @@ void spawnfunc_monster_hell_knight () // compatibility with old spawns void spawnfunc_monster_hellknight () { spawnfunc_monster_hell_knight(); } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/knight.qc b/qcsrc/server/monsters/monster/knight.qc index 35ded60dd..17ade040a 100644 --- a/qcsrc/server/monsters/monster/knight.qc +++ b/qcsrc/server/monsters/monster/knight.qc @@ -1,58 +1,62 @@ +#ifndef MENUQC // size const vector KNIGHT_MIN = '-16 -16 -24'; const vector KNIGHT_MAX = '16 16 32'; - + +// model +string KNIGHT_MODEL = "models/monsters/knight.mdl"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_knight; float autocvar_g_monster_knight_health; -float autocvar_g_monster_knight_melee_damage; +//float autocvar_g_monster_knight_melee_damage; float autocvar_g_monster_knight_speed_walk; float autocvar_g_monster_knight_speed_run; // animations -#define knight_anim_stand 0 -#define knight_anim_run 1 -#define knight_anim_runattack 2 -#define knight_anim_pain1 3 -#define knight_anim_pain2 4 -#define knight_anim_attack 5 -#define knight_anim_walk 6 -#define knight_anim_kneel 7 -#define knight_anim_standing 8 -#define knight_anim_death1 9 -#define knight_anim_death2 10 +const float knight_anim_stand = 0; +const float knight_anim_run = 1; +const float knight_anim_runattack = 2; +const float knight_anim_pain1 = 3; +const float knight_anim_pain2 = 4; +const float knight_anim_attack = 5; +const float knight_anim_walk = 6; +const float knight_anim_kneel = 7; +const float knight_anim_standing = 8; +const float knight_anim_death1 = 9; +const float knight_anim_death2 = 10; void knight_think () { self.think = knight_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_knight_speed_run, autocvar_g_monster_knight_speed_walk, 50, knight_anim_run, knight_anim_walk, knight_anim_stand); } void knight_attack () { - local float len = vlen(self.velocity); + float len = vlen(self.velocity); + + W_Shotgun_Attack2(); - self.frame = ((len < 50) ? knight_anim_attack : knight_anim_runattack); + monsters_setframe((len < 50) ? knight_anim_attack : knight_anim_runattack); - self.attack_finished_single = time + 0.9; + self.attack_finished_single = time + 1.25; - monster_melee(self.enemy, autocvar_g_monster_knight_melee_damage, 80, DEATH_MONSTER_KNIGHT); + //monster_melee(self.enemy, autocvar_g_monster_knight_melee_damage, 0.3, DEATH_MONSTER_KNIGHT); } void knight_die () { Monster_CheckDropCvars ("knight"); - - self.frame = ((random() > 0.5) ? knight_anim_death1 : knight_anim_death2); - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.think = Monster_Fade; - self.movetype = MOVETYPE_TOSS; - self.nextthink = time + 2.1; + + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe((random() > 0.5) ? knight_anim_death1 : knight_anim_death2); monster_hook_death(); // for post-death mods } @@ -69,7 +73,9 @@ void knight_spawn () self.nextthink = time + random() * 0.5 + 0.1; self.think = knight_think; self.sprite_height = 30; - self.frame = knight_anim_stand; + self.view_ofs *= 0.5; + + monsters_setframe(knight_anim_stand); monster_hook_spawn(); // for post-spawn mods } @@ -80,19 +86,14 @@ void spawnfunc_monster_knight () self.monster_spawnfunc = spawnfunc_monster_knight; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } self.scale = 1.3; if not (monster_initialize( - "Knight", - "models/monsters/knight.mdl", + "Knight", MONSTER_KNIGHT, + KNIGHT_MODEL, KNIGHT_MIN, KNIGHT_MAX, FALSE, knight_die, knight_spawn)) @@ -101,3 +102,5 @@ void spawnfunc_monster_knight () return; } } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/ogre.qc b/qcsrc/server/monsters/monster/ogre.qc index 6cbb2d371..653fcb619 100644 --- a/qcsrc/server/monsters/monster/ogre.qc +++ b/qcsrc/server/monsters/monster/ogre.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size -const vector OGRE_MIN = '-32 -32 -24'; -const vector OGRE_MAX = '32 32 32'; - +const vector OGRE_MIN = '-36 -36 -20'; +const vector OGRE_MAX = '36 36 50'; + +// model +string OGRE_MODEL = "models/monsters/ogre.dpm"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_ogre; float autocvar_g_monster_ogre_health; @@ -11,12 +18,12 @@ float autocvar_g_monster_ogre_speed_run; float autocvar_g_monster_ogre_attack_uzi_bullets; // animations -#define ogre_anim_idle 0 -#define ogre_anim_walk 1 -#define ogre_anim_run 2 -#define ogre_anim_pain 3 -#define ogre_anim_swing 4 -#define ogre_anim_die 5 +const float ogre_anim_idle = 0; +const float ogre_anim_walk = 1; +const float ogre_anim_run = 2; +const float ogre_anim_pain = 3; +const float ogre_anim_swing = 4; +const float ogre_anim_die = 5; void chainsaw (float side) { @@ -32,7 +39,7 @@ void chainsaw (float side) void ogre_think () { self.think = ogre_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; if(self.delay != -1) self.nextthink = self.delay; @@ -44,7 +51,7 @@ void ogre_think () void ogre_swing () { self.ogre_cycles += 1; - self.frame = ogre_anim_swing; + monsters_setframe(ogre_anim_swing); if(self.ogre_cycles == 1) self.attack_finished_single = time + 1.3; self.angles_y = self.angles_y + random()* 25; @@ -79,7 +86,7 @@ void ogre_uzi_fire () void ogre_uzi () { - self.frame = ogre_anim_pain; + monsters_setframe(ogre_anim_pain); self.attack_finished_single = time + 0.8; self.delay = time + 0.1; self.monster_delayedattack = ogre_uzi_fire; @@ -88,7 +95,7 @@ void ogre_uzi () void ogre_gl () { W_Grenade_Attack2(); - self.frame = ogre_anim_pain; + monsters_setframe(ogre_anim_pain); self.attack_finished_single = time + 0.8; } @@ -117,14 +124,9 @@ void ogre_die() { Monster_CheckDropCvars ("ogre"); - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.nextthink = time + 2.1; - self.movetype = MOVETYPE_TOSS; - self.think = Monster_Fade; - self.frame = ogre_anim_die; + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe(ogre_anim_die); monster_hook_death(); // for post-death mods } @@ -138,13 +140,14 @@ void ogre_spawn () self.classname = "monster_ogre"; self.checkattack = GenericCheckAttack; self.attack_melee = ogre_melee; - self.frame = ogre_anim_idle; self.attack_ranged = ogre_missile; self.nextthink = time + 0.1; self.think = ogre_think; - self.sprite_height = 50; + self.sprite_height = 65; self.weapon = WEP_GRENADE_LAUNCHER; + monsters_setframe(ogre_anim_idle); + monster_hook_spawn(); // for post-spawn mods } @@ -154,19 +157,12 @@ void spawnfunc_monster_ogre () self.monster_spawnfunc = spawnfunc_monster_ogre; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } - - self.scale = 1.3; if not (monster_initialize( - "Ogre", - "models/monsters/ogre.dpm", + "Ogre", MONSTER_OGRE, + OGRE_MODEL, OGRE_MIN, OGRE_MAX, FALSE, ogre_die, ogre_spawn)) @@ -177,3 +173,5 @@ void spawnfunc_monster_ogre () weapon_action(WEP_GRENADE_LAUNCHER, WR_PRECACHE); } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/shalrath.qc b/qcsrc/server/monsters/monster/shalrath.qc index 6dcfb6172..40d30979e 100644 --- a/qcsrc/server/monsters/monster/shalrath.qc +++ b/qcsrc/server/monsters/monster/shalrath.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size -const vector SHALRATH_MIN = '-32 -32 -24'; -const vector SHALRATH_MAX = '32 32 32'; +const vector SHALRATH_MIN = '-36 -36 -24'; +const vector SHALRATH_MAX = '36 36 50'; +// model +string SHALRATH_MODEL = "models/monsters/mage.dpm"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_shalrath; float autocvar_g_monster_shalrath_health; @@ -11,31 +18,56 @@ float autocvar_g_monster_shalrath_attack_spike_radius; float autocvar_g_monster_shalrath_attack_spike_delay; float autocvar_g_monster_shalrath_attack_melee_damage; float autocvar_g_monster_shalrath_attack_melee_delay; +float autocvar_g_monster_shalrath_heal_self; +float autocvar_g_monster_shalrath_heal_friends; +float autocvar_g_monster_shalrath_heal_minhealth; +float autocvar_g_monster_shalrath_heal_range; +float autocvar_g_monster_shalrath_heal_delay; // animations -#define shalrath_anim_idle 0 -#define shalrath_anim_walk 1 -#define shalrath_anim_attack 2 -#define shalrath_anim_pain 3 -#define shalrath_anim_death 4 -#define shalrath_anim_run 5 +const float shalrath_anim_idle = 0; +const float shalrath_anim_walk = 1; +const float shalrath_anim_attack = 2; +const float shalrath_anim_pain = 3; +const float shalrath_anim_death = 4; +const float shalrath_anim_run = 5; void() ShalMissile; +float() shal_missile; +void() shalrath_heal; void shalrath_think () { + entity head; + float friend_needshelp = FALSE; + + FOR_EACH_PLAYER(head) + { + if(vlen(head.origin - self.origin) < autocvar_g_monster_shalrath_heal_range * self.scale) + if((!g_minstagib && head.health < autocvar_g_balance_health_regenstable) || (g_minstagib && head.ammo_cells < start_ammo_cells)) + { + friend_needshelp = TRUE; + break; // found 1 player near us who is low on health + } + } + self.think = shalrath_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; if(self.delay != -1) self.nextthink = self.delay; + + if(self.health < autocvar_g_monster_shalrath_heal_minhealth || friend_needshelp) + if(time >= self.attack_finished_single) + if(random() < 0.5) + shalrath_heal(); monster_move(autocvar_g_monster_shalrath_speed, autocvar_g_monster_shalrath_speed, 50, shalrath_anim_walk, shalrath_anim_run, shalrath_anim_idle); } void shalrath_attack () { - self.frame = shalrath_anim_attack; + monsters_setframe(shalrath_anim_attack); self.delay = time + 0.2; self.attack_finished_single = time + autocvar_g_monster_shalrath_attack_spike_delay; self.monster_delayedattack = ShalMissile; @@ -43,18 +75,14 @@ void shalrath_attack () void shalrathattack_melee () { - float bigdmg = 0, rdmg = autocvar_g_monster_shalrath_attack_melee_damage * random(); - - bigdmg = rdmg * self.scale; - - monster_melee(self.enemy, bigdmg * monster_skill, 120, DEATH_MONSTER_MAGE); + monster_melee(self.enemy, autocvar_g_monster_shalrath_attack_melee_damage, 0.3, DEATH_MONSTER_MAGE, TRUE); } void shalrath_attack_melee () { self.monster_delayedattack = shalrathattack_melee; self.delay = time + 0.2; - self.frame = shalrath_anim_attack; + monsters_setframe(shalrath_anim_attack); self.attack_finished_single = time + autocvar_g_monster_shalrath_attack_melee_delay; } @@ -136,10 +164,9 @@ void ShalMissile () float ShalrathCheckAttack () { - local vector spot1 = '0 0 0', spot2 = '0 0 0'; - local entity targ = self.enemy; + vector spot1 = '0 0 0', spot2 = '0 0 0'; - if (self.health <= 0 || targ == world || targ.health < 1) + if (self.health <= 0 || self.enemy == world || self.enemy.health < 1) return FALSE; if(self.monster_delayedattack && self.delay != -1) @@ -167,35 +194,66 @@ float ShalrathCheckAttack () // see if any entities are in the way of the shot spot1 = self.origin + self.view_ofs; - spot2 = targ.origin + targ.view_ofs; + spot2 = self.enemy.origin + self.enemy.view_ofs; traceline (spot1, spot2, FALSE, self); - if (trace_ent != targ && trace_fraction < 1) + if (trace_ent != self.enemy && trace_fraction < 1) return FALSE; // don't have a clear shot //if (trace_inopen && trace_inwater) // return FALSE; // sight line crossed contents - if (random() < 0.2) if (self.attack_ranged()) return TRUE; return FALSE; } +void shalrath_heal() +{ + entity head; + if(self.health < self.max_health) // only show our effect if we are healing ourself too + pointparticles(particleeffectnum("healing_fx"), self.origin, '0 0 0', 1); + self.health = bound(0, self.health + autocvar_g_monster_shalrath_heal_self, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + monsters_setframe(shalrath_anim_attack); + self.attack_finished_single = time + autocvar_g_monster_shalrath_heal_delay; + + for(head = world; (head = findfloat(head, monster_attack, TRUE)); ) + { + if(head.health > 0) + if not(head.frozen || head.freezetag_frozen) + if(vlen(head.origin - self.origin) < autocvar_g_monster_shalrath_heal_range * self.scale) + if not(IsDifferentTeam(head, self)) + { + if(IS_PLAYER(head)) + { + if(head.ammo_cells < start_ammo_cells || head.health < g_pickup_healthmedium_max) + pointparticles(particleeffectnum(((g_minstagib) ? "ammoregen_fx" : "healing_fx")), head.origin, '0 0 0', 1); + if(g_minstagib) + head.ammo_cells = bound(0, head.ammo_cells + 1, start_ammo_cells); + else + head.health = bound(0, head.health + autocvar_g_monster_shalrath_heal_friends, g_pickup_healthmedium_max); + } + else + { + if(head.health < head.max_health) + pointparticles(particleeffectnum("healing_fx"), head.origin, '0 0 0', 1); + head.health = bound(0, head.health + autocvar_g_monster_shalrath_heal_friends, head.max_health); + WaypointSprite_UpdateHealth(head.sprite, head.health); + } + } + } +} + void shalrath_die () { Monster_CheckDropCvars ("shalrath"); - self.think = Monster_Fade; - self.frame = shalrath_anim_death; - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.nextthink = time + 2.1; - self.movetype = MOVETYPE_TOSS; + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe(shalrath_anim_death); monster_hook_death(); // for post-death mods } @@ -212,8 +270,9 @@ void shalrath_spawn () self.attack_melee = shalrath_attack_melee; self.nextthink = time + random() * 0.5 + 0.1; self.think = shalrath_think; - self.frame = shalrath_anim_walk; - self.sprite_height = 40; + self.sprite_height = 65; + + monsters_setframe(shalrath_anim_walk); monster_hook_spawn(); // for post-spawn mods } @@ -224,20 +283,12 @@ void spawnfunc_monster_shalrath () self.monster_spawnfunc = spawnfunc_monster_shalrath; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; - + if(Monster_CheckAppearFlags(self)) return; - } - - self.scale = 1.3; if not (monster_initialize( - "Mage", - "models/monsters/mage.dpm", + "Mage", MONSTER_MAGE, + SHALRATH_MODEL, SHALRATH_MIN, SHALRATH_MAX, FALSE, shalrath_die, shalrath_spawn)) @@ -249,3 +300,5 @@ void spawnfunc_monster_shalrath () // compatibility with old spawns void spawnfunc_monster_vore () { spawnfunc_monster_shalrath(); } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/shambler.qc b/qcsrc/server/monsters/monster/shambler.qc index de1ff35d2..afa983d22 100644 --- a/qcsrc/server/monsters/monster/shambler.qc +++ b/qcsrc/server/monsters/monster/shambler.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size const vector SHAMBLER_MIN = '-32 -32 -24'; const vector SHAMBLER_MAX = '32 32 64'; +// model +string SHAMBLER_MODEL = "models/monsters/shambler.mdl"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_shambler; float autocvar_g_monster_shambler_health; @@ -12,20 +19,20 @@ float autocvar_g_monster_shambler_speed_walk; float autocvar_g_monster_shambler_speed_run; // animations -#define shambler_anim_stand 0 -#define shambler_anim_walk 1 -#define shambler_anim_run 2 -#define shambler_anim_smash 3 -#define shambler_anim_swingr 4 -#define shambler_anim_swingl 5 -#define shambler_anim_magic 6 -#define shambler_anim_pain 7 -#define shambler_anim_death 8 +const float shambler_anim_stand = 0; +const float shambler_anim_walk = 1; +const float shambler_anim_run = 2; +const float shambler_anim_smash = 3; +const float shambler_anim_swingr = 4; +const float shambler_anim_swingl = 5; +const float shambler_anim_magic = 6; +const float shambler_anim_pain = 7; +const float shambler_anim_death = 8; void shambler_think () { self.think = shambler_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_shambler_speed_run, autocvar_g_monster_shambler_speed_walk, 300, shambler_anim_run, shambler_anim_walk, shambler_anim_stand); } @@ -49,22 +56,20 @@ void shambler_smash () void shambler_delayedsmash () { - self.frame = shambler_anim_smash; + monsters_setframe(shambler_anim_smash); self.think = shambler_smash; self.nextthink = time + 0.7; } void ShamClaw (float side) { - float bigdmg = autocvar_g_monster_shambler_attack_claw_damage * self.scale; - - monster_melee(self.enemy, bigdmg * monster_skill, 100, DEATH_MONSTER_SHAMBLER_CLAW); + monster_melee(self.enemy, autocvar_g_monster_shambler_attack_claw_damage, 0.3, DEATH_MONSTER_SHAMBLER_CLAW, TRUE); } void() shambler_swing_right; void shambler_swing_left () { - self.frame = shambler_anim_swingl; + monsters_setframe(shambler_anim_swingl); ShamClaw(250); self.attack_finished_single = time + 0.8; self.nextthink = self.attack_finished_single; @@ -75,7 +80,7 @@ void shambler_swing_left () void shambler_swing_right () { - self.frame = shambler_anim_swingr; + monsters_setframe(shambler_anim_swingr); ShamClaw(-250); self.attack_finished_single = time + 0.8; self.nextthink = self.attack_finished_single; @@ -98,9 +103,9 @@ void sham_melee () void CastLightning () { - self.nextthink = time + 0.4; - self.think = shambler_think; - + self.monster_delayedattack = func_null; + self.delay = -1; + local vector org = '0 0 0', dir = '0 0 0'; vector v = '0 0 0'; @@ -122,10 +127,10 @@ void CastLightning () void shambler_magic () { - self.frame = shambler_anim_magic; + monsters_setframe(shambler_anim_magic); self.attack_finished_single = time + 1.1; - self.nextthink = time + 0.6; - self.think = CastLightning; + self.monster_delayedattack = CastLightning; + self.delay = time + 0.6; } float sham_lightning () @@ -138,14 +143,9 @@ void shambler_die () { Monster_CheckDropCvars ("shambler"); - self.think = Monster_Fade; - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.nextthink = time + 2.1; - self.frame = shambler_anim_death; - self.movetype = MOVETYPE_TOSS; + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe(shambler_anim_death); monster_hook_death(); // for post-death mods } @@ -161,11 +161,12 @@ void shambler_spawn () self.checkattack = GenericCheckAttack; self.attack_ranged = sham_lightning; self.nextthink = time + random() * 0.5 + 0.1; - self.frame = shambler_anim_stand; self.think = shambler_think; self.sprite_height = 70; self.weapon = WEP_NEX; + monsters_setframe(shambler_anim_stand); + monster_hook_spawn(); // for post-spawn mods } @@ -175,19 +176,14 @@ void spawnfunc_monster_shambler () self.monster_spawnfunc = spawnfunc_monster_shambler; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } self.scale = 1.3; if not (monster_initialize( - "Shambler", - "models/monsters/shambler.mdl", + "Shambler", MONSTER_SHAMBLER, + SHAMBLER_MODEL, SHAMBLER_MIN, SHAMBLER_MAX, FALSE, shambler_die, shambler_spawn)) @@ -201,3 +197,5 @@ void spawnfunc_monster_shambler () precache_sound ("weapons/lgbeam_fire.wav"); } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/soldier.qc b/qcsrc/server/monsters/monster/soldier.qc index 480410f61..11c3663b1 100644 --- a/qcsrc/server/monsters/monster/soldier.qc +++ b/qcsrc/server/monsters/monster/soldier.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size const vector SOLDIER_MIN = '-16 -16 -30'; const vector SOLDIER_MAX = '16 16 32'; +// model +string SOLDIER_MODEL = "models/monsters/soldier.zym"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_soldier; float autocvar_g_monster_soldier_health; @@ -16,45 +23,34 @@ float autocvar_g_monster_soldier_weapon_rocketlauncher_chance; float autocvar_g_monster_soldier_attack_uzi_bullets; // animations -#define soldier_anim_die1 0 -#define soldier_anim_die2 1 -#define soldier_anim_draw 2 -#define soldier_anim_duck 3 -#define soldier_anim_duckwalk 4 -#define soldier_anim_duckjump 5 -#define soldier_anim_duckidle 6 -#define soldier_anim_idle 7 -#define soldier_anim_jump 8 -#define soldier_anim_pain1 9 -#define soldier_anim_pain2 10 -#define soldier_anim_shoot 11 -#define soldier_anim_taunt 12 -#define soldier_anim_run 13 -#define soldier_anim_runbackwards 14 -#define soldier_anim_strafeleft 15 -#define soldier_anim_straferight 16 -#define soldier_anim_dead1 17 -#define soldier_anim_dead2 18 -#define soldier_anim_forwardright 19 -#define soldier_anim_forwardleft 20 -#define soldier_anim_backright 21 -#define soldier_anim_backleft 22 - -//#define soldier_anim_stand 0 -//#define soldier_anim_death1 1 -//#define soldier_anim_death2 2 -//#define soldier_anim_reload 3 -//#define soldier_anim_pain1 4 -//#define soldier_anim_pain2 5 -//#define soldier_anim_pain3 6 -//#define soldier_anim_run 7 -//#define soldier_anim_shoot 8 -//#define soldier_anim_prowl 9 +const float soldier_anim_die1 = 0; +const float soldier_anim_die2 = 1; +const float soldier_anim_draw = 2; +const float soldier_anim_duck = 3; +const float soldier_anim_duckwalk = 4; +const float soldier_anim_duckjump = 5; +const float soldier_anim_duckidle = 6; +const float soldier_anim_idle = 7; +const float soldier_anim_jump = 8; +const float soldier_anim_pain1 = 9; +const float soldier_anim_pain2 = 10; +const float soldier_anim_shoot = 11; +const float soldier_anim_taunt = 12; +const float soldier_anim_run = 13; +const float soldier_anim_runbackwards = 14; +const float soldier_anim_strafeleft = 15; +const float soldier_anim_straferight = 16; +const float soldier_anim_dead1 = 17; +const float soldier_anim_dead2 = 18; +const float soldier_anim_forwardright = 19; +const float soldier_anim_forwardleft = 20; +const float soldier_anim_backright = 21; +const float soldier_anim_backleft = 22; void soldier_think () { self.think = soldier_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; if(self.delay != -1) self.nextthink = self.delay; @@ -67,101 +63,23 @@ void soldier_think () void soldier_reload () { - self.frame = soldier_anim_draw; + self.monster_delayedattack = func_null; // out of ammo, don't keep attacking + self.delay = -1; + monsters_setframe(soldier_anim_draw); self.attack_finished_single = time + 2; self.currentammo = autocvar_g_monster_soldier_ammo; sound (self, CH_SHOTS, "weapons/reload.wav", VOL_BASE, ATTN_LARGE); } -float SoldierCheckAttack () -{ - local vector spot1 = '0 0 0', spot2 = '0 0 0'; - local entity targ = self.enemy; - local float chance = 0; - - if (self.health <= 0 || targ.health < 1 || targ == world) - return FALSE; - - // see if any entities are in the way of the shot - spot1 = self.origin + self.view_ofs; - spot2 = targ.origin + targ.view_ofs; - - traceline (spot1, spot2, FALSE, self); - - if (trace_ent != targ) - return FALSE; // don't have a clear shot - - if (trace_inwater) - if (trace_inopen) - return FALSE; // sight line crossed contents - - if(self.monster_delayedattack && self.delay != -1) - { - if(time < self.delay) - return FALSE; - - self.monster_delayedattack(); - } - - // missile attack - if (time < self.attack_finished_single) - return FALSE; - - if (vlen(self.enemy.origin - self.origin) >= 2000) - return FALSE; - - if (vlen(self.enemy.origin - self.origin) <= 120) - chance = 0.9; - else if (vlen(self.enemy.origin - self.origin) <= 500) - chance = 0.6; // was 0.4 - else if (vlen(self.enemy.origin - self.origin) <= 1000) - chance = 0.3; // was 0.05 - else - chance = 0; - - if (chance > 0) - if (chance > random()) - return FALSE; - - if(self.currentammo <= 0 && vlen(self.enemy.origin - self.origin) <= 120) - { - monster_sound(self.msound_attack_melee, 0, FALSE); // no delay for attack sounds - self.attack_melee(); - return TRUE; - } - - if(self.currentammo <= 0) - { - soldier_reload(); - return FALSE; - } - - if (self.attack_ranged()) - return TRUE; - - return FALSE; -} - -void soldier_laser () -{ - self.frame = soldier_anim_shoot; - self.attack_finished_single = time + 0.8; - W_Laser_Attack(0); -} - -float soldier_missile_laser () -{ - // FIXME: check if it would hit - soldier_laser(); - return TRUE; -} - .float grunt_cycles; void soldier_uzi_fire () { self.currentammo -= 1; if(self.currentammo <= 0) + { + soldier_reload(); return; + } self.grunt_cycles += 1; @@ -176,85 +94,67 @@ void soldier_uzi_fire () self.monster_delayedattack = soldier_uzi_fire; } -void soldier_uzi () +float soldier_attack() { + monsters_setframe(soldier_anim_shoot); + makevectors(self.angles); + if(self.currentammo <= 0) - return; - - self.frame = soldier_anim_shoot; - self.attack_finished_single = time + 0.8; - self.delay = time + 0.1; - self.monster_delayedattack = soldier_uzi_fire; -} - -float soldier_missile_uzi () -{ - self.grunt_cycles = 0; - // FIXME: check if it would hit - soldier_uzi(); - return TRUE; -} - -void soldier_shotgun () -{ - self.currentammo -= 1; - if(self.currentammo <= 0) - return; - - self.frame = soldier_anim_shoot; - self.attack_finished_single = time + 0.8; - W_Shotgun_Attack(); -} - -float soldier_missile_shotgun () -{ - // FIXME: check if it would hit + { + soldier_reload(); + return FALSE; + } + self.grunt_cycles = 0; - soldier_shotgun(); - return TRUE; -} - -void soldier_rl () -{ - self.currentammo -= 1; - if(self.currentammo <= 0) - return; - - self.frame = soldier_anim_shoot; - self.attack_finished_single = time + 0.8; - W_Rocket_Attack(); -} - -float soldier_missile_rl () -{ - // FIXME: check if it would hit - soldier_rl(); - return TRUE; + + switch(self.weapon) + { + case WEP_ROCKET_LAUNCHER: + { + self.currentammo -= 1; + self.attack_finished_single = time + 0.8; + W_Rocket_Attack(); + return TRUE; + } + case WEP_SHOTGUN: + { + self.currentammo -= 1; + self.attack_finished_single = time + 0.8; + W_Shotgun_Attack(); + return TRUE; + } + case WEP_UZI: + { + self.attack_finished_single = time + 0.8; + self.delay = time + 0.1; + self.monster_delayedattack = soldier_uzi_fire; + return TRUE; + } + case WEP_LASER: + { + self.attack_finished_single = time + 0.8; + W_Laser_Attack(0); + return TRUE; + } + default: + return FALSE; // no weapon? + } } -void soldier_bash () +void soldier_melee () { - self.frame = soldier_anim_shoot; + monsters_setframe(soldier_anim_shoot); self.attack_finished_single = time + 0.8; - monster_melee(self.enemy, autocvar_g_monster_soldier_melee_damage, 70, DEATH_MONSTER_MARINE_SLAP); + monster_melee(self.enemy, autocvar_g_monster_soldier_melee_damage, 0.3, DEATH_MONSTER_MARINE_SLAP, TRUE); } void soldier_die() { Monster_CheckDropCvars ("soldier"); - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.movetype = MOVETYPE_TOSS; - self.think = Monster_Fade; - self.nextthink = time + 2.1; - - if (random() < 0.5) - self.frame = soldier_anim_die1; - else - self.frame = soldier_anim_die2; + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe((random() > 0.5) ? soldier_anim_die1 : soldier_anim_die2); monster_hook_death(); // for post-death mods } @@ -266,14 +166,17 @@ void soldier_spawn () self.damageforcescale = 0.003; self.classname = "monster_soldier"; - self.checkattack = SoldierCheckAttack; - self.attack_melee = soldier_bash; - self.frame = soldier_anim_draw; + self.checkattack = GenericCheckAttack; + self.attack_melee = soldier_melee; + self.attack_ranged = soldier_attack; self.nextthink = time + random() * 0.5 + 0.1; self.think = soldier_think; + self.currentammo = 3; self.sprite_height = 45; self.items = (IT_SHELLS | IT_ROCKETS | IT_NAILS); + monsters_setframe(soldier_anim_draw); + RandomSelection_Init(); RandomSelection_Add(world, WEP_LASER, string_null, autocvar_g_monster_soldier_weapon_laser_chance, 1); RandomSelection_Add(world, WEP_SHOTGUN, string_null, autocvar_g_monster_soldier_weapon_shotgun_chance, 1); @@ -288,34 +191,8 @@ void soldier_spawn () setmodel(self.weaponentity, "models/weapons/v_seeker.md3"); setattachment(self.weaponentity, self, "bip01 r hand"); - if (RandomSelection_chosen_float == WEP_ROCKET_LAUNCHER) - { - self.weapon = WEP_ROCKET_LAUNCHER; - self.currentammo = self.ammo_rockets; - self.armorvalue = 0.9; - self.attack_ranged = soldier_missile_rl; - } - else if (RandomSelection_chosen_float == WEP_UZI) - { - self.weapon = WEP_UZI; - self.currentammo = self.ammo_nails; - self.armorvalue = 0.5; - self.attack_ranged = soldier_missile_uzi; - } - else if (RandomSelection_chosen_float == WEP_SHOTGUN) - { - self.weapon = WEP_SHOTGUN; - self.currentammo = self.ammo_shells; - self.armorvalue = 0.7; - self.attack_ranged = soldier_missile_shotgun; - } - else - { - self.weapon = WEP_LASER; - self.armorvalue = 0.6; - self.currentammo = self.ammo_none; - self.attack_ranged = soldier_missile_laser; - } + self.armorvalue = bound(0.5, random(), 1); + self.weapon = RandomSelection_chosen_float; monster_hook_spawn(); // for post-spawn mods } @@ -326,17 +203,12 @@ void spawnfunc_monster_soldier () self.monster_spawnfunc = spawnfunc_monster_soldier; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } if not (monster_initialize( - "Grunt", - "models/monsters/soldier.zym", + "Grunt", MONSTER_MARINE, + SOLDIER_MODEL, SOLDIER_MIN, SOLDIER_MAX, FALSE, soldier_die, soldier_spawn)) @@ -349,7 +221,10 @@ void spawnfunc_monster_soldier () precache_sound ("weapons/uzi_fire.wav"); precache_sound ("weapons/laser_fire.wav"); precache_sound ("weapons/reload.wav"); + precache_model ("models/weapons/v_seeker.md3"); } // compatibility with old spawns void spawnfunc_monster_army () { spawnfunc_monster_soldier(); } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/spawner.qc b/qcsrc/server/monsters/monster/spawner.qc deleted file mode 100644 index 1e984f53b..000000000 --- a/qcsrc/server/monsters/monster/spawner.qc +++ /dev/null @@ -1,140 +0,0 @@ -// size -const vector SPAWNER_MIN = '-35 -35 -10'; -const vector SPAWNER_MAX = '35 35 70'; - -// cvars -float autocvar_g_monster_spawner; -float autocvar_g_monster_spawner_health; -float autocvar_g_monster_spawner_maxmobs; -string autocvar_g_monster_spawner_forcespawn; - -void spawnmonsters () -{ - if(self.spawner_monstercount >= autocvar_g_monster_spawner_maxmobs || self.frozen) - return; - - vector p1, p2, p3, p4, chosenposi; - float r = random(); - string type = self.spawnmob; - entity e; - - self.spawner_monstercount += 1; - - if(autocvar_g_monster_spawner_forcespawn) - type = autocvar_g_monster_spawner_forcespawn; - - if(type == "" || type == "spawner") // spawner spawning spawners?! - type = "knight"; - - p1 = self.origin - '0 70 -50' * self.scale; - p2 = self.origin + '0 70 50' * self.scale; - p3 = self.origin - '70 0 -50' * self.scale; - p4 = self.origin + '70 0 -50' * self.scale; - - if (r < 0.20) - chosenposi = p1; - else if (r < 0.50) - chosenposi = p2; - else if (r < 0.80) - chosenposi = p3; - else - chosenposi = p4; - - e = spawnmonster(type, self, self, chosenposi, FALSE, MONSTER_MOVE_WANDER); - - e.team = self.team; - e.candrop = FALSE; - - if(self.spawnflags & MONSTERFLAG_GIANT) - e.spawnflags = MONSTERFLAG_GIANT; - - if(self.flags & MONSTERFLAG_MINIBOSS) - e.spawnflags = MONSTERFLAG_MINIBOSS; -} - -void spawner_die () -{ - setmodel(self, ""); - pointparticles(particleeffectnum(((self.scale > 3) ? "explosion_big" : "explosion_medium")), self.origin, '0 0 0', 1); - sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); - - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.think = Monster_Fade; - self.nextthink = time + 1; - - monster_hook_death(); // for post-death mods -} - -void spawner_think() -{ - self.think = spawner_think; - - if(self.spawner_monstercount >= autocvar_g_monster_spawner_maxmobs) - self.nextthink = time + 15; - - if (self.spawner_monstercount <= autocvar_g_monster_spawner_maxmobs) - spawnmonsters(); - - self.nextthink = time + 1; - - if(self.spawner_monstercount <= autocvar_g_monster_spawner_maxmobs) - self.nextthink = time + 0.1; -} - -void spawner_spawn() -{ - if not(self.health) - self.health = autocvar_g_monster_spawner_health * self.scale; - - self.classname = "monster_spawner"; - self.nextthink = time + 0.2; - self.velocity = '0 0 0'; - self.think = spawner_think; - self.touch = func_null; - self.sprite_height = 80; - - self.spawner_monstercount = 0; - - self.movetype = MOVETYPE_NONE; - - monster_hook_spawn(); // for post-spawn mods -} - -/*QUAKED monster_spawner (1 0 0) (-18 -18 -25) (18 18 47) ----------NOTES---------- -Spawns monsters when a player is nearby --------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY -------- -modeldisabled="models/containers/crate01.md3" -*/ -void spawnfunc_monster_spawner() -{ - if not(autocvar_g_monster_spawner) { remove(self); return; } - - self.monster_spawnfunc = spawnfunc_monster_spawner; - - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; - return; - } - - self.scale = 0.8; - - if not (monster_initialize( - "Monster spawner", - "models/containers/crate01.md3", - SPAWNER_MIN, SPAWNER_MAX, - FALSE, - spawner_die, spawner_spawn)) - { - remove(self); - return; - } - - precache_sound("weapons/rocket_impact.wav"); -} diff --git a/qcsrc/server/monsters/monster/spider.qc b/qcsrc/server/monsters/monster/spider.qc index 4bb0eb599..63da385fc 100644 --- a/qcsrc/server/monsters/monster/spider.qc +++ b/qcsrc/server/monsters/monster/spider.qc @@ -1,3 +1,14 @@ +#ifndef MENUQC +// size +const vector SPIDER_MIN = '-18 -18 -25'; +const vector SPIDER_MAX = '18 18 30'; + +// model +string SPIDER_MODEL = "models/monsters/spider.dpm"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_spider; float autocvar_g_monster_spider_stopspeed; @@ -9,43 +20,16 @@ float autocvar_g_monster_spider_speed_walk; float autocvar_g_monster_spider_speed_run; float autocvar_g_monster_spider_attack_type; -// spider animations -#define spider_anim_idle 0 -#define spider_anim_walk 1 -#define spider_anim_attack 2 -#define spider_anim_attack2 3 - -const vector SPIDER_MIN = '-18 -18 -25'; -const vector SPIDER_MAX = '18 18 30'; +// animations +const float spider_anim_idle = 0; +const float spider_anim_walk = 1; +const float spider_anim_attack = 2; +const float spider_anim_attack2 = 3; .float spider_type; // used to switch between fire & ice attacks const float SPIDER_TYPE_ICE = 0; const float SPIDER_TYPE_FIRE = 1; -void spider_spawn(); -void spawnfunc_monster_spider(); -void spider_think(); - -void spider_die () -{ - Monster_CheckDropCvars ("spider"); - - self.angles += '180 0 0'; - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.movetype = MOVETYPE_TOSS; - self.think = Monster_Fade; - self.nextthink = time + 2.1; - self.frame = spider_anim_attack; - - monster_hook_death(); // for post-death mods -} - -/** - * Performe a standing attack on self.enemy. - */ void spider_attack_standing() { float dot = 0, bigdmg = autocvar_g_monster_spider_attack_stand_damage * self.scale; @@ -57,11 +41,8 @@ void spider_attack_standing() dot = normalize (self.enemy.origin - self.origin) * v_forward; if(dot > 0.3) Damage(self.enemy, self, self, bigdmg * monster_skill, DEATH_MONSTER_SPIDER, self.origin, '0 0 0'); - - if(random() < 0.50) - self.frame = spider_anim_attack; - else - self.frame = spider_anim_attack2; + + monsters_setframe((random() > 0.5) ? spider_anim_attack : spider_anim_attack2); self.attack_finished_single = time + autocvar_g_monster_spider_attack_stand_delay; } @@ -132,8 +113,8 @@ void spider_attack_leap() { vector angles_face = vectoangles(self.enemy.origin - self.origin); - // face the enemy - self.frame = spider_anim_attack2; + // face the enemy + monsters_setframe(spider_anim_attack2); self.angles_y = angles_face_y ; self.attack_finished_single = time + autocvar_g_monster_spider_attack_leap_delay; @@ -161,14 +142,23 @@ float spider_attack_ranged() void spider_think() { self.think = spider_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_spider_speed_run, autocvar_g_monster_spider_speed_walk, autocvar_g_monster_spider_stopspeed, spider_anim_walk, spider_anim_walk, spider_anim_idle); } -/** - * Spawn the spider. - */ +void spider_die () +{ + Monster_CheckDropCvars ("spider"); + + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe(spider_anim_attack); + self.angles += '180 0 0'; + + monster_hook_death(); // for post-death mods +} + void spider_spawn() { if not(self.health) @@ -176,45 +166,32 @@ void spider_spawn() self.classname = "monster_spider"; self.nextthink = time + random() * 0.5 + 0.1; - self.frame = spider_anim_idle; self.checkattack = GenericCheckAttack; self.attack_melee = spider_attack_standing; self.attack_ranged = spider_attack_ranged; self.think = spider_think; self.sprite_height = 40; + monsters_setframe(spider_anim_idle); + + if not(self.spider_type) + self.spider_type = autocvar_g_monster_spider_attack_type; + monster_hook_spawn(); // for post-spawn mods } -/*QUAKED monster_spider (1 0 0) (-18 -18 -25) (18 18 47) -Spider, 60 health points. --------- KEYS -------- --------- SPAWNFLAGS -------- -MONSTERFLAG_APPEAR: monster will spawn when triggered. ----------NOTES---------- --------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY -------- -modeldisabled="models/monsters/spider.dpm" -*/ void spawnfunc_monster_spider() { if not(autocvar_g_monster_spider) { remove(self); return; } self.monster_spawnfunc = spawnfunc_monster_spider; - self.classname = "monster_spider"; - if(!self.spider_type) - self.spider_type = autocvar_g_monster_spider_attack_type; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } if not (monster_initialize( - "Spider", - "models/monsters/spider.dpm", + "Spider", MONSTER_SPIDER, + SPIDER_MODEL, SPIDER_MIN, SPIDER_MAX, FALSE, spider_die, spider_spawn)) @@ -223,3 +200,5 @@ void spawnfunc_monster_spider() return; } } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/tarbaby.qc b/qcsrc/server/monsters/monster/tarbaby.qc index 811605789..4acbf3775 100644 --- a/qcsrc/server/monsters/monster/tarbaby.qc +++ b/qcsrc/server/monsters/monster/tarbaby.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size const vector TARBABY_MIN = '-16 -16 -24'; const vector TARBABY_MAX = '16 16 16'; +// model +string TARBABY_MODEL = "models/monsters/tarbaby.mdl"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_tarbaby; float autocvar_g_monster_tarbaby_health; @@ -9,31 +16,29 @@ float autocvar_g_monster_tarbaby_speed_walk; float autocvar_g_monster_tarbaby_speed_run; // animations -#define tarbaby_anim_walk 0 -#define tarbaby_anim_run 1 -#define tarbaby_anim_jump 2 -#define tarbaby_anim_fly 3 -#define tarbaby_anim_explode 4 +const float tarbaby_anim_walk = 0; +const float tarbaby_anim_run = 1; +const float tarbaby_anim_jump = 2; +const float tarbaby_anim_fly = 3; +const float tarbaby_anim_explode = 4; void tarbaby_think () { self.think = tarbaby_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_tarbaby_speed_run, autocvar_g_monster_tarbaby_speed_walk, 20, tarbaby_anim_run, tarbaby_anim_walk, tarbaby_anim_walk); } void Tar_JumpTouch () { - // dunno why this would be called when dead, but to be safe - if (self.health <= 0) - return; - - if (other.takedamage) - if (vlen(self.velocity) > 200) + if(self.health > 0) + if(other.health > 0) + if(other.takedamage) + if(vlen(self.velocity) > 200) { // make the monster die - self.event_damage(self, self, self.health + self.max_health, DEATH_MONSTER_TARBABY, self.origin, '0 0 0'); + self.event_damage(self, self, self.health + self.max_health + 200, DEATH_MONSTER_TARBABY, self.origin, '0 0 0'); return; } @@ -53,10 +58,7 @@ void tarbaby_jump () { if not(self.flags & FL_ONGROUND) return; - self.frame = tarbaby_anim_jump; - // dunno why this would be called when dead, but to be safe - if (self.health <= 0) - return; + monsters_setframe(tarbaby_anim_jump); self.movetype = MOVETYPE_BOUNCE; self.touch = Tar_JumpTouch; makevectors (self.angles); @@ -96,13 +98,11 @@ void tarbaby_explode() void tarbaby_die () { - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; + self.think = tarbaby_explode; + self.nextthink = time + 0.1; self.event_damage = func_null; self.movetype = MOVETYPE_NONE; self.enemy = world; - self.think = tarbaby_explode; - self.nextthink = time + 0.1; } void tarbaby_spawn () @@ -118,7 +118,8 @@ void tarbaby_spawn () self.nextthink = time + random() * 0.5 + 0.1; self.think = tarbaby_think; self.sprite_height = 20; - self.frame = tarbaby_anim_walk; + + monsters_setframe(tarbaby_anim_walk); monster_hook_spawn(); // for post-spawn mods } @@ -129,19 +130,14 @@ void spawnfunc_monster_tarbaby () self.monster_spawnfunc = spawnfunc_monster_tarbaby; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } self.scale = 1.3; if not (monster_initialize( - "Spawn", - "models/monsters/tarbaby.mdl", + "Spawn", MONSTER_TARBABY, + TARBABY_MODEL, TARBABY_MIN, TARBABY_MAX, FALSE, tarbaby_die, tarbaby_spawn)) @@ -155,3 +151,5 @@ void spawnfunc_monster_tarbaby () // compatibility with old spawns void spawnfunc_monster_spawn () { spawnfunc_monster_tarbaby(); } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/wizard.qc b/qcsrc/server/monsters/monster/wizard.qc index 97842688a..58c0fe0ed 100644 --- a/qcsrc/server/monsters/monster/wizard.qc +++ b/qcsrc/server/monsters/monster/wizard.qc @@ -1,7 +1,14 @@ +#ifndef MENUQC // size -const vector WIZARD_MIN = '-16 -16 -24'; -const vector WIZARD_MAX = '16 16 24'; +const vector WIZARD_MIN = '-16 -16 -45'; +const vector WIZARD_MAX = '16 16 16'; +// model +string WIZARD_MODEL = "models/monsters/wizard.mdl"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_wizard; float autocvar_g_monster_wizard_health; @@ -13,11 +20,11 @@ float autocvar_g_monster_wizard_spike_radius; float autocvar_g_monster_wizard_spike_speed; // animations -#define wizard_anim_hover 0 -#define wizard_anim_fly 1 -#define wizard_anim_magic 2 -#define wizard_anim_pain 3 -#define wizard_anim_death 4 +const float wizard_anim_hover = 0; +const float wizard_anim_fly = 1; +const float wizard_anim_magic = 2; +const float wizard_anim_pain = 3; +const float wizard_anim_death = 4; void Wiz_FastExplode() { @@ -95,7 +102,7 @@ void Wiz_StartFast () void wizard_think () { self.think = wizard_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_wizard_speed_run, autocvar_g_monster_wizard_speed_walk, 300, wizard_anim_fly, wizard_anim_hover, wizard_anim_hover); } @@ -110,17 +117,13 @@ void wizard_die () Monster_CheckDropCvars ("wizard"); self.think = Monster_Fade; - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.movetype = MOVETYPE_TOSS; + self.nextthink = time + 5; self.flags = FL_ONGROUND; - self.nextthink = time + 2.1; - self.velocity_x = -200 + 400*random(); - self.velocity_y = -200 + 400*random(); - self.velocity_z = 100 + 100*random(); - self.frame = wizard_anim_death; + self.velocity_x = -200 + 400 * random(); + self.velocity_y = -200 + 400 * random(); + self.velocity_z = 100 + 100 * random(); + + monsters_setframe(wizard_anim_death); monster_hook_death(); // for post-death mods } @@ -154,19 +157,14 @@ void spawnfunc_monster_wizard () self.monster_spawnfunc = spawnfunc_monster_wizard; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } self.scale = 1.3; if not (monster_initialize( - "Scrag", - "models/monsters/wizard.mdl", + "Scrag", MONSTER_SCRAG, + WIZARD_MODEL, WIZARD_MIN, WIZARD_MAX, TRUE, wizard_die, wizard_spawn)) @@ -181,3 +179,5 @@ void spawnfunc_monster_wizard () // compatibility with old spawns void spawnfunc_monster_scrag () { spawnfunc_monster_wizard(); } + +#endif // SVQC diff --git a/qcsrc/server/monsters/monster/zombie.qc b/qcsrc/server/monsters/monster/zombie.qc index 8a5131ad7..225ede8a4 100644 --- a/qcsrc/server/monsters/monster/zombie.qc +++ b/qcsrc/server/monsters/monster/zombie.qc @@ -1,9 +1,14 @@ -/** - * Special purpose fields: - * .delay - time at which to check if zombie's enemy is still in range - * .enemy - enemy of this zombie - */ - +#ifndef MENUQC +// size +const vector ZOMBIE_MIN = '-18 -18 -25'; +const vector ZOMBIE_MAX = '18 18 47'; + +// model +string ZOMBIE_MODEL = "models/monsters/zombie.dpm"; + +#endif + +#ifdef SVQC // cvars float autocvar_g_monster_zombie; float autocvar_g_monster_zombie_stopspeed; @@ -17,125 +22,75 @@ float autocvar_g_monster_zombie_health; float autocvar_g_monster_zombie_speed_walk; float autocvar_g_monster_zombie_speed_run; -// zombie animations -#define zombie_anim_attackleap 0 -#define zombie_anim_attackrun1 1 -#define zombie_anim_attackrun2 2 -#define zombie_anim_attackrun3 3 -#define zombie_anim_attackstanding1 4 -#define zombie_anim_attackstanding2 5 -#define zombie_anim_attackstanding3 6 -#define zombie_anim_blockend 7 -#define zombie_anim_blockstart 8 -#define zombie_anim_deathback1 9 -#define zombie_anim_deathback2 10 -#define zombie_anim_deathback3 11 -#define zombie_anim_deathfront1 12 -#define zombie_anim_deathfront2 13 -#define zombie_anim_deathfront3 14 -#define zombie_anim_deathleft1 15 -#define zombie_anim_deathleft2 16 -#define zombie_anim_deathright1 17 -#define zombie_anim_deathright2 18 -#define zombie_anim_idle 19 -#define zombie_anim_painback1 20 -#define zombie_anim_painback2 21 -#define zombie_anim_painfront1 22 -#define zombie_anim_painfront2 23 -#define zombie_anim_runbackwards 24 -#define zombie_anim_runbackwardsleft 25 -#define zombie_anim_runbackwardsright 26 -#define zombie_anim_runforward 27 -#define zombie_anim_runforwardleft 28 -#define zombie_anim_runforwardright 29 -#define zombie_anim_spawn 30 - -const vector ZOMBIE_MIN = '-18 -18 -25'; -const vector ZOMBIE_MAX = '18 18 47'; - -void zombie_spawn(); -void spawnfunc_monster_zombie(); -void zombie_think(); - -void zombie_die () -{ - Monster_CheckDropCvars ("zombie"); - - self.solid = SOLID_NOT; - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - self.enemy = world; - self.movetype = MOVETYPE_TOSS; - self.think = Monster_Fade; - self.nextthink = time + 2.1; - - if (random() > 0.5) - self.frame = zombie_anim_deathback1; - else - self.frame = zombie_anim_deathfront1; - - monster_hook_death(); // for post-death mods -} +// animations +const float zombie_anim_attackleap = 0; +const float zombie_anim_attackrun1 = 1; +const float zombie_anim_attackrun2 = 2; +const float zombie_anim_attackrun3 = 3; +const float zombie_anim_attackstanding1 = 4; +const float zombie_anim_attackstanding2 = 5; +const float zombie_anim_attackstanding3 = 6; +const float zombie_anim_blockend = 7; +const float zombie_anim_blockstart = 8; +const float zombie_anim_deathback1 = 9; +const float zombie_anim_deathback2 = 10; +const float zombie_anim_deathback3 = 11; +const float zombie_anim_deathfront1 = 12; +const float zombie_anim_deathfront2 = 13; +const float zombie_anim_deathfront3 = 14; +const float zombie_anim_deathleft1 = 15; +const float zombie_anim_deathleft2 = 16; +const float zombie_anim_deathright1 = 17; +const float zombie_anim_deathright2 = 18; +const float zombie_anim_idle = 19; +const float zombie_anim_painback1 = 20; +const float zombie_anim_painback2 = 21; +const float zombie_anim_painfront1 = 22; +const float zombie_anim_painfront2 = 23; +const float zombie_anim_runbackwards = 24; +const float zombie_anim_runbackwardsleft = 25; +const float zombie_anim_runbackwardsright = 26; +const float zombie_anim_runforward = 27; +const float zombie_anim_runforwardleft = 28; +const float zombie_anim_runforwardright = 29; +const float zombie_anim_spawn = 30; void zombie_attack_standing() { - float rand = random(), dot = 0, bigdmg = 0; - - self.velocity_x = 0; - self.velocity_y = 0; - - if(self.monster_owner == self.enemy) - { - self.enemy = world; - return; - } - - bigdmg = autocvar_g_monster_zombie_attack_stand_damage * self.scale; - - //print("zombie attacks!\n"); - makevectors (self.angles); - dot = normalize (self.enemy.origin - self.origin) * v_forward; - if(dot > 0.3) - { - Damage(self.enemy, self, self, bigdmg * monster_skill, DEATH_MONSTER_ZOMBIE_MELEE, self.origin, '0 0 0'); - } - - if(self.enemy.health < 1) - self.enemy = world; + float rand = random(), chosen_anim; if (rand < 0.33) - self.frame = zombie_anim_attackstanding1; + chosen_anim = zombie_anim_attackstanding1; else if (rand < 0.66) - self.frame = zombie_anim_attackstanding2; + chosen_anim = zombie_anim_attackstanding2; else - self.frame = zombie_anim_attackstanding3; + chosen_anim = zombie_anim_attackstanding3; + + monsters_setframe(chosen_anim); - self.nextthink = time + autocvar_g_monster_zombie_attack_stand_delay; - self.attack_finished_single = self.nextthink; + self.attack_finished_single = time + autocvar_g_monster_zombie_attack_stand_delay; + + monster_melee(self.enemy, autocvar_g_monster_zombie_attack_stand_damage, 0.3, DEATH_MONSTER_ZOMBIE_MELEE, TRUE); } -void zombie_attack_leap_touch() +void zombie_attack_leap_touch () { - vector angles_face; - float bigdmg = autocvar_g_monster_zombie_attack_leap_damage * self.scale; - - if (other.deadflag != DEAD_NO) + if (self.health <= 0) return; - if (self.monster_owner == other) - return; - - if (other.takedamage == DAMAGE_NO) - return; - - //void Damage (entity targ, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) - traceline(self.origin, other.origin, FALSE, self); + vector angles_face; + float bigdmg = autocvar_g_monster_zombie_attack_leap_damage * self.scale; - angles_face = vectoangles(self.moveto - self.origin); - angles_face = normalize(angles_face) * autocvar_g_monster_zombie_attack_leap_force; - Damage(other, self, self, bigdmg * monster_skill, DEATH_MONSTER_ZOMBIE_JUMP, trace_endpos, angles_face); - - self.touch = MonsterTouch; + if (monster_isvalidtarget(other, self)) + { + angles_face = vectoangles(self.moveto - self.origin); + angles_face = normalize(angles_face) * autocvar_g_monster_zombie_attack_leap_force; + Damage(other, self, self, bigdmg * monster_skill, DEATH_MONSTER_ZOMBIE_JUMP, other.origin, angles_face); + self.touch = MonsterTouch; // instantly turn it off to stop damage spam + } + + if(self.flags & FL_ONGROUND) + self.touch = MonsterTouch; } float zombie_attack_ranged() @@ -150,11 +105,22 @@ float zombie_attack_ranged() void zombie_think() { self.think = zombie_think; - self.nextthink = time + 0.1; + self.nextthink = time + self.ticrate; monster_move(autocvar_g_monster_zombie_speed_run, autocvar_g_monster_zombie_speed_walk, autocvar_g_monster_zombie_stopspeed, zombie_anim_runforward, zombie_anim_runforward, zombie_anim_idle); } +void zombie_die () +{ + Monster_CheckDropCvars ("zombie"); + + self.think = Monster_Fade; + self.nextthink = time + 5; + monsters_setframe((random() > 0.5) ? zombie_anim_deathback1 : zombie_anim_deathfront1); + + monster_hook_death(); // for post-death mods +} + void zombie_spawn() { if not(self.health) @@ -162,13 +128,18 @@ void zombie_spawn() self.classname = "monster_zombie"; self.nextthink = time + 2.1; - self.frame = zombie_anim_spawn; self.think = zombie_think; self.sprite_height = 50; self.checkattack = GenericCheckAttack; self.attack_melee = zombie_attack_standing; self.attack_ranged = zombie_attack_ranged; - self.skin = rint(random() * 4); + self.respawntime = 0.1; + self.respawnflags = MONSTER_RESPAWN_DEATHPOINT; + + monsters_setframe(zombie_anim_spawn); + + if not(self.monster_respawned) + self.skin = rint(random() * 4); // some sounds if(self.msound_idle == "") self.msound_idle = "monsters/zombie_idle.wav"; @@ -181,33 +152,18 @@ void zombie_spawn() monster_hook_spawn(); // for post-spawn mods } -/*QUAKED monster_zombie (1 0 0) (-18 -18 -25) (18 18 47) -Zombie, 60 health points. --------- KEYS -------- --------- SPAWNFLAGS -------- -MONSTERFLAG_APPEAR: monster will spawn when triggered. ----------NOTES---------- -Original Quake 1 zombie entity used a smaller box ('-16 -16 -24', '16 16 32'). --------- MODEL FOR RADIANT ONLY - DO NOT SET THIS AS A KEY -------- -modeldisabled="models/monsters/zombie.dpm" -*/ void spawnfunc_monster_zombie() { if not(autocvar_g_monster_zombie) { remove(self); return; } self.monster_spawnfunc = spawnfunc_monster_zombie; - if(self.spawnflags & MONSTERFLAG_APPEAR) - { - self.think = func_null; - self.nextthink = -1; - self.use = Monster_Appear; + if(Monster_CheckAppearFlags(self)) return; - } if not (monster_initialize( - "Zombie", - "models/monsters/zombie.dpm", + "Zombie", MONSTER_ZOMBIE, + ZOMBIE_MODEL, ZOMBIE_MIN, ZOMBIE_MAX, FALSE, zombie_die, zombie_spawn)) @@ -216,3 +172,5 @@ void spawnfunc_monster_zombie() return; } } + +#endif //SVQC diff --git a/qcsrc/server/monsters/monsters.qh b/qcsrc/server/monsters/monsters.qh index a9695490b..c43a999d0 100644 --- a/qcsrc/server/monsters/monsters.qh +++ b/qcsrc/server/monsters/monsters.qh @@ -1,9 +1,11 @@ // Lib +#ifdef SVQC #include "lib/defs.qh" #include "lib/monsters.qc" +#include "lib/spawn.qc" +#endif // Monsters -#include "lib/spawn.qc" #include "monster/ogre.qc" #include "monster/demon.qc" #include "monster/shambler.qc" @@ -18,4 +20,3 @@ #include "monster/enforcer.qc" #include "monster/zombie.qc" #include "monster/spider.qc" -#include "monster/spawner.qc" diff --git a/qcsrc/server/mutators/gamemode_ctf.qc b/qcsrc/server/mutators/gamemode_ctf.qc index 8f0bc931e..f578c5f73 100644 --- a/qcsrc/server/mutators/gamemode_ctf.qc +++ b/qcsrc/server/mutators/gamemode_ctf.qc @@ -451,14 +451,19 @@ void ctf_Handle_Capture(entity flag, entity toucher, float capturetype) void ctf_Handle_Return(entity flag, entity player) { // messages and sounds - Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_RETURN_)); + if(player.classname == "player") + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_RETURN_)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_RETURN_), player.netname); sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTN_NONE); ctf_EventLog("return", flag.team, player); // scoring - PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return - PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns + if(player.classname == "player") + { + PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return + PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns + } TeamScore_AddToTeam(flag.team, ST_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the team who was last carrying it @@ -825,6 +830,11 @@ void ctf_FlagTouch() else return; // do nothing } + else if(toucher.flags & FL_MONSTER) + { + if not(autocvar_g_ctf_allow_monster_touch) + return; // do nothing + } else if(toucher.classname != "player") // The flag just touched an object, most likely the world { if(time > self.wait) // if we haven't in a while, play a sound/effect @@ -841,9 +851,9 @@ void ctf_FlagTouch() { case FLAG_BASE: { - if(!IsDifferentTeam(toucher, self) && (toucher.flagcarried) && IsDifferentTeam(toucher.flagcarried, self)) + if(!IsDifferentTeam(toucher, self) && (toucher.flagcarried) && IsDifferentTeam(toucher.flagcarried, self) && !(toucher.flags & FL_MONSTER)) ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base - else if(IsDifferentTeam(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time)) + else if(IsDifferentTeam(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && !(toucher.flags & FL_MONSTER)) ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the enemies flag break; } @@ -852,7 +862,7 @@ void ctf_FlagTouch() { if(!IsDifferentTeam(toucher, self)) ctf_Handle_Return(self, toucher); // toucher just returned his own flag - else if((!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay))) + else if(!(toucher.flags & FL_MONSTER) && (!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay))) ctf_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag break; } diff --git a/qcsrc/server/mutators/gamemode_towerdefense.qc b/qcsrc/server/mutators/gamemode_towerdefense.qc index d971d9e59..1f8a9c33a 100644 --- a/qcsrc/server/mutators/gamemode_towerdefense.qc +++ b/qcsrc/server/mutators/gamemode_towerdefense.qc @@ -25,17 +25,18 @@ void spawnfunc_td_controller() self.classname = "td_controller"; gensurvived = FALSE; - td_dont_end = ((self.dontend) ? self.dontend : autocvar_g_td_generator_dontend); - max_waves = ((self.maxwaves) ? self.maxwaves : autocvar_g_td_max_waves); - totalmonsters = ((self.monstercount) ? self.monstercount : autocvar_g_td_monster_count); - wave_count = ((self.startwave) ? self.startwave : autocvar_g_td_start_wave); - max_turrets = ((self.maxturrets) ? self.maxturrets : autocvar_g_td_turret_max); - build_time = ((self.buildtime) ? self.buildtime : autocvar_g_td_buildphase_time); - m_speed_walk = ((self.mspeed_walk) ? self.mspeed_walk : autocvar_g_td_monsters_speed_walk); - m_speed_run = ((self.mspeed_run) ? self.mspeed_run : autocvar_g_td_monsters_speed_run); - spawn_delay = ((self.spawndelay) ? self.spawndelay : autocvar_g_td_monsters_spawn_delay); - max_current = ((self.maxcurrent) ? self.maxcurrent : autocvar_g_td_current_monsters); - ignore_turrets = ((self.ignoreturrets) ? self.ignoreturrets : autocvar_g_td_monsters_ignore_turrets); + + td_dont_end = ((self.dontend == 0) ? autocvar_g_td_generator_dontend : self.dontend); + max_waves = ((self.maxwaves == 0) ? autocvar_g_td_max_waves : self.maxwaves); + totalmonsters = ((self.monstercount == 0) ? autocvar_g_td_monster_count : self.monstercount); + wave_count = ((self.startwave == 0) ? autocvar_g_td_start_wave : self.startwave); + max_turrets = ((self.maxturrets == 0) ? autocvar_g_td_turret_max : self.maxturrets); + build_time = ((self.buildtime == 0) ? autocvar_g_td_buildphase_time : self.buildtime); + m_speed_walk = ((self.mspeed_walk == 0) ? autocvar_g_td_monsters_speed_walk : self.mspeed_walk); + m_speed_run = ((self.mspeed_run == 0) ? autocvar_g_td_monsters_speed_run : self.mspeed_run); + spawn_delay = ((self.spawndelay == 0) ? autocvar_g_td_monsters_spawn_delay : self.spawndelay); + max_current = ((self.maxcurrent == 0) ? autocvar_g_td_current_monsters : self.maxcurrent); + ignore_turrets = ((self.ignoreturrets == 0) ? autocvar_g_td_monsters_ignore_turrets : self.ignoreturrets); if(autocvar_g_td_monsters_skill_start) monster_skill = autocvar_g_td_monsters_skill_start; @@ -273,6 +274,7 @@ void spawnturret(entity spawnedby, entity own, string turet, vector orig) self.angles_y = spawnedby.v_angle_y; spawnedby.turret_cnt += 1; self.colormap = spawnedby.colormap; + self.colormod = '1 1 1'; switch(turet) { @@ -565,7 +567,7 @@ void build_phase() if(totalmonsters < 1) totalmonsters = ((autocvar_g_td_monster_count > 0) ? autocvar_g_td_monster_count : 10); if(wave_count < 1) wave_count = 1; - Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_TD_PHASE_BUILD, wave_count, totalmonsters, autocvar_g_td_buildphase_time); + Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_TD_PHASE_BUILD, wave_count, totalmonsters, build_time); FOR_EACH_MONSTER(head) { @@ -752,7 +754,7 @@ MUTATOR_HOOKFUNCTION(td_MonsterMove) } if not(self.enemy) // don't change targets while attacking - if((vlen(self.goalentity.origin - self.origin) <= 100 && self.goalentity.classname == "td_waypoint") || (vlen(self.goalentity.origin - self.origin) <= 200 && self.flags & FL_FLY && self.goalentity.classname == "td_waypoint")) + if((vlen(self.goalentity.origin - self.origin) <= 100 && self.goalentity.classname == "td_waypoint") || (vlen(self.goalentity.origin - self.origin) <= 200 && (self.flags & FL_FLY) && self.goalentity.classname == "td_waypoint")) { if(self.goalentity.target2) { @@ -791,27 +793,30 @@ MUTATOR_HOOKFUNCTION(td_MonsterSpawn) self.drop_size = self.health * 0.05; + self.target_range = 600; + if(self.drop_size < 1) self.drop_size = 1; self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_BODY; self.origin += '0 0 25'; // hopefully this fixes monsters falling through the floor - switch(self.classname) + switch(self.monsterid) { - case "monster_knight": n_knights -= 1; break; - case "monster_dog": n_dogs -= 1; break; - case "monster_ogre": n_ogres -= 1; break; - case "monster_shambler": n_shamblers -= 1; AnnounceSpawn("Shambler"); break; - case "monster_wizard": n_wizards -= 1; break; - case "monster_shalrath": n_shalraths -= 1; break; - case "monster_soldier": n_soldiers -= 1; break; - case "monster_hellknight": n_hknights -= 1; break; - case "monster_enforcer": n_enforcers -= 1; break; - case "monster_demon": n_demons -= 1; break; - case "monster_zombie": n_zombies -= 1; break; - case "monster_spider": n_spiders -= 1; break; - case "monster_tarbaby": n_tarbabies -= 1; break; + case MONSTER_ZOMBIE: n_zombies -= 1; break; + case MONSTER_OGRE: n_ogres -= 1; break; + case MONSTER_DEMON: n_demons -= 1; break; + case MONSTER_SHAMBLER: n_shamblers -= 1; break; + case MONSTER_KNIGHT: n_knights -= 1; break; + case MONSTER_MARINE: n_soldiers -= 1; break; + case MONSTER_SCRAG: n_wizards -= 1; break; + case MONSTER_DOG: n_dogs -= 1; break; + case MONSTER_TARBABY: n_tarbabies -= 1; break; + case MONSTER_HELLKNIGHT: n_hknights -= 1; break; + case MONSTER_FISH: n_fish -= 1; break; + case MONSTER_MAGE: n_shalraths -= 1; break; + case MONSTER_ENFORCER: n_enforcers -= 1; break; + case MONSTER_SPIDER: n_spiders -= 1; break; } return TRUE; @@ -831,7 +836,7 @@ MUTATOR_HOOKFUNCTION(td_MonsterDies) PlayerScore_Add(frag_attacker, SP_TD_SCORE, autocvar_g_td_kill_points); PlayerScore_Add(frag_attacker, SP_TD_KILLS, 1); } - else if(IS_PLAYER(frag_attacker.realowner)) + else if(IS_PLAYER(frag_attacker.realowner) && frag_attacker.turrcaps_flags & TFL_TURRCAPS_ISTURRET) { PlayerScore_Add(frag_attacker.realowner, SP_TD_SCORE, autocvar_g_td_turretkill_points); PlayerScore_Add(frag_attacker.realowner, SP_TD_TURKILLS, 1); @@ -930,6 +935,11 @@ MUTATOR_HOOKFUNCTION(td_PlayerCommand) Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_TD_CANTSPAWN); return TRUE; } + if(max_turrets <= 0) + { + Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_TD_TURRETS_DISABLED); + return TRUE; + } if(self.turret_cnt >= max_turrets) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_TD_MAXTURRETS, max_turrets); diff --git a/qcsrc/server/mutators/mutator_zombie_apocalypse.qc b/qcsrc/server/mutators/mutator_zombie_apocalypse.qc index 3f84465a0..cf511ea92 100644 --- a/qcsrc/server/mutators/mutator_zombie_apocalypse.qc +++ b/qcsrc/server/mutators/mutator_zombie_apocalypse.qc @@ -34,9 +34,8 @@ void zombie_spawn_somewhere () if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256)) { - mon = spawnmonster("zombie", self, self, self.origin, TRUE, 2); + mon = spawnmonster("zombie", self, self, self.origin, TRUE, 2); // double tap needed tracebox(mon.origin, mon.mins, mon.maxs, mon.origin, MOVE_NOMONSTERS, mon); - mon.spawnflags |= MONSTERFLAG_NORESPAWN; if(trace_startsolid) { @@ -60,7 +59,7 @@ void za_roundwon() numzoms = autocvar_g_za_monster_count; - monsters_total = numzoms; + monsters_total += numzoms; totalzombies = numzoms; self.think = spawn_zombies; @@ -71,7 +70,7 @@ void spawn_zombies () { self.nextthink = time + 1; - if(totalzombies < 1) + if(totalzombies <= 0) { self.think = za_roundwon; self.nextthink = time; @@ -101,9 +100,9 @@ void za_init () roundcnt = 1; - numzoms = autocvar_g_za_monster_count * roundcnt; + numzoms = autocvar_g_za_monster_count; - monsters_total = numzoms; + monsters_total += numzoms; totalzombies = numzoms; e = spawn(); @@ -113,6 +112,9 @@ void za_init () MUTATOR_HOOKFUNCTION(za_ZombieDies) { + if(self.monster_respawned) + return FALSE; // don't count zombies that respawned + if(frag_attacker.classname == "player") PlayerScore_Add(frag_attacker, SP_SCORE, 1); diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 166f3a823..ba0555a29 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -48,6 +48,8 @@ mutators/gamemode_rts.qh tturrets/include/turrets_early.qh vehicles/vehicles_def.qh +monsters/lib/monsters_early.qh + campaign.qh ../common/campaign_common.qh ../common/mapinfo.qh diff --git a/qcsrc/server/vehicles/vehicles.qc b/qcsrc/server/vehicles/vehicles.qc index bad37d30f..ee230c94d 100644 --- a/qcsrc/server/vehicles/vehicles.qc +++ b/qcsrc/server/vehicles/vehicles.qc @@ -633,6 +633,7 @@ void vehicles_enter() self.team = self.owner.team; self.flags -= FL_NOTARGET; + self.monster_attack = TRUE; if (clienttype(other) == CLIENTTYPE_REAL) { @@ -811,6 +812,7 @@ void vehicles_exit(float eject) sound (_vehicle, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTN_NORM); _vehicle.vehicle_hudmodel.viewmodelforclient = _vehicle; _vehicle.phase = time + 1; + _vehicle.monster_attack = FALSE; _vehicle.vehicle_exit(eject); @@ -1258,7 +1260,6 @@ float vehicle_initialize(string net_name, self.tur_head.owner = self; self.takedamage = DAMAGE_AIM; self.bot_attack = TRUE; - self.monster_attack = TRUE; self.iscreature = TRUE; self.teleportable = FALSE; // no teleporting for vehicles, too buggy self.damagedbycontents = TRUE; diff --git a/scripts/cerberus.shader b/scripts/cerberus.shader deleted file mode 100644 index 82531915e..000000000 --- a/scripts/cerberus.shader +++ /dev/null @@ -1,8 +0,0 @@ -textures/cerberus/cerberus_text -{ - cull none - - { - map textures/cerberus - } -} diff --git a/scripts/mage.shader b/scripts/mage.shader deleted file mode 100644 index 7a235c141..000000000 --- a/scripts/mage.shader +++ /dev/null @@ -1,8 +0,0 @@ -mage -{ - cull none - - { - map textures/mage - } -} diff --git a/scripts/monsters.shader b/scripts/monsters.shader new file mode 100644 index 000000000..dbab45df1 --- /dev/null +++ b/scripts/monsters.shader @@ -0,0 +1,32 @@ +textures/spider/spidertex +{ + cull none + + { + map textures/spidertex + } +} +textures/cerberus/cerberus_text +{ + cull none + + { + map textures/cerberus + } +} +textures/berzerker_texture +{ + cull none + + { + map textures/ogre + } +} +mage +{ + cull none + + { + map textures/mage + } +} diff --git a/scripts/ogre.shader b/scripts/ogre.shader deleted file mode 100644 index 195e5f17a..000000000 --- a/scripts/ogre.shader +++ /dev/null @@ -1,8 +0,0 @@ -textures/berzerker_texture -{ - cull none - - { - map textures/ogre - } -} diff --git a/scripts/shaderlist.txt b/scripts/shaderlist.txt index b779627c5..8082c886f 100644 --- a/scripts/shaderlist.txt +++ b/scripts/shaderlist.txt @@ -14,8 +14,5 @@ tree tuba turrets weapons -mage barricade -spider -cerberus -ogre +monsters diff --git a/scripts/spider.shader b/scripts/spider.shader deleted file mode 100644 index b84c37480..000000000 --- a/scripts/spider.shader +++ /dev/null @@ -1,8 +0,0 @@ -textures/spider/spidertex -{ - cull none - - { - map textures/spidertex - } -}