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
// 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 ${* ?}"
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"
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
// ====================
-1 8 10 1 // hellknight stand\r10 19 10 1 // hellknight walk\r30 7 10 1 // hellknight run\r38 4 10 0 // hellknight pain\r43 11 10 0 // hellknight death1\r55 8 10 0 // hellknight death2\r64 15 10 0 // hellknight charge1\r80 13 10 0 // hellknight magic1\r94 12 10 0 // hellknight magic2\r107 5 10 0 // hellknight charge2\r113 9 10 1 // hellknight slice\r123 9 10 1 // hellknight smash\r133 21 10 1 // hellknight weapon attack\r155 10 10 0 //hellknight magic3
\ No newline at end of file
+1 8 10 1 // hellknight stand\r10 19 10 1 // hellknight walk\r30 7 10 1 // hellknight run\r38 4 10 0 // hellknight pain\r43 11 10 0 // hellknight death1\r55 8 10 0 // hellknight death2\r64 15 10 1 // hellknight charge1\r80 13 10 1 // hellknight magic1\r94 12 10 1 // hellknight magic2\r107 5 10 1 // hellknight charge2\r113 9 10 1 // hellknight slice\r123 9 10 1 // hellknight smash\r133 21 10 1 // hellknight weapon attack\r155 10 10 1 //hellknight magic3
\ No newline at end of file
-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
--- /dev/null
+bloodyskull,cleanskull
+meat,meat
\ No newline at end of file
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"
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;
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;
--- /dev/null
+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;
+ }
+}
--- /dev/null
+void ent_monster();
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
../warpzonelib/client.qc
tturrets.qc
+../server/monsters/monsters.qh
+monsters.qc
+
player_skeleton.qc
../common/animdecide.qc
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;
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"), "") \
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;
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
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"); }
FOR_EACH_PLAYER(head)
head.monstercount = 0;
+ monsters_total = 0; // reset stats?
+ monsters_killed = 0;
+
totalspawned = 0;
if(removed_count <= 0)
#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
mirrorforce *= g_weaponforcefactor;
}
- if(targ.frozen && attacker.classname != "monster_spider")
+ if(targ.frozen && attacker.monsterid != MONSTER_SPIDER)
{
damage = 0;
force *= 0.2;
.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
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;
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)
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)
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 ()
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)
{
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();
}
}
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
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);
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;
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;
}
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);
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)
if(gameover || time < game_starttime)
{
runspeed = walkspeed = 0;
- self.frame = manim_idle;
+ monsters_setframe(manim_idle);
movelib_beak_simple(stopspeed);
return;
}
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);
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))
{
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
{
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
if(self.enemy && self.checkattack)
self.checkattack();
+
+ self.SendFlags |= MSF_ANG;
+ if(vlen(self.velocity) > 0)
+ self.SendFlags |= MSF_MOVE;
}
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)
if(monster_isvalidtarget(attacker, self))
self.enemy = attacker;
- damage *= self.armorvalue;
+ if(deathtype != DEATH_KILL)
+ damage *= self.armorvalue;
self.health -= damage;
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)
{
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;
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,
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)
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;
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;
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;
}
--- /dev/null
+// 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
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();
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);
+#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;
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 ()
{
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
}
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))
// Compatibility with old spawns
void spawnfunc_monster_demon1 () { spawnfunc_monster_demon(); }
+
+#endif // SVQC
+#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;
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 ()
{
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 ()
{
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
}
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
}
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))
return;
}
}
+
+#endif // SVQC
+#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;
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;
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 ()
{
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
}
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;
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
}
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))
return;
}
}
+
+#endif // SVQC
+#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;
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
}
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))
return;
}
}
+
+#endif // SVQC
+#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;
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()
{
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)
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);
}
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;
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;
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;
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;
self.hknight_cycles = 0;
- if (self.enemy.classname == "monster_zombie")
+ if (self.enemy.monsterid == MONSTER_ZOMBIE)
{
// always use fireball to kill zombies
hellknight_magic2();
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)
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
}
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
}
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))
// compatibility with old spawns
void spawnfunc_monster_hellknight () { spawnfunc_monster_hell_knight(); }
+
+#endif // SVQC
+#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
}
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
}
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))
return;
}
}
+
+#endif // SVQC
+#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;
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)
{
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;
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;
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;
void ogre_gl ()
{
W_Grenade_Attack2();
- self.frame = ogre_anim_pain;
+ monsters_setframe(ogre_anim_pain);
self.attack_finished_single = time + 0.8;
}
{
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
}
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
}
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))
weapon_action(WEP_GRENADE_LAUNCHER, WR_PRECACHE);
}
+
+#endif // SVQC
+#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;
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;
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;
}
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)
// 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
}
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
}
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))
// compatibility with old spawns
void spawnfunc_monster_vore () { spawnfunc_monster_shalrath(); }
+
+#endif // SVQC
+#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;
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);
}
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;
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;
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';
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 ()
{
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
}
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
}
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))
precache_sound ("weapons/lgbeam_fire.wav");
}
+
+#endif // SVQC
+#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;
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;
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;
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
}
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);
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
}
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))
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
+++ /dev/null
-// 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");
-}
+#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;
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;
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;
}
{
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;
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)
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))
return;
}
}
+
+#endif // SVQC
+#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;
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;
}
{
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);
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 ()
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
}
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))
// compatibility with old spawns
void spawnfunc_monster_spawn () { spawnfunc_monster_tarbaby(); }
+
+#endif // SVQC
+#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;
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()
{
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);
}
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
}
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))
// compatibility with old spawns
void spawnfunc_monster_scrag () { spawnfunc_monster_wizard(); }
+
+#endif // SVQC
-/**
- * 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;
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()
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)
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";
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))
return;
}
}
+
+#endif //SVQC
// 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"
#include "monster/enforcer.qc"
#include "monster/zombie.qc"
#include "monster/spider.qc"
-#include "monster/spawner.qc"
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
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
{
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;
}
{
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;
}
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;
self.angles_y = spawnedby.v_angle_y;
spawnedby.turret_cnt += 1;
self.colormap = spawnedby.colormap;
+ self.colormod = '1 1 1';
switch(turet)
{
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)
{
}
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)
{
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;
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);
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);
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)
{
numzoms = autocvar_g_za_monster_count;
- monsters_total = numzoms;
+ monsters_total += numzoms;
totalzombies = numzoms;
self.think = spawn_zombies;
{
self.nextthink = time + 1;
- if(totalzombies < 1)
+ if(totalzombies <= 0)
{
self.think = za_roundwon;
self.nextthink = time;
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();
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);
tturrets/include/turrets_early.qh
vehicles/vehicles_def.qh
+monsters/lib/monsters_early.qh
+
campaign.qh
../common/campaign_common.qh
../common/mapinfo.qh
self.team = self.owner.team;
self.flags -= FL_NOTARGET;
+ self.monster_attack = TRUE;
if (clienttype(other) == CLIENTTYPE_REAL)
{
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);
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;
+++ /dev/null
-textures/cerberus/cerberus_text
-{
- cull none
-
- {
- map textures/cerberus
- }
-}
+++ /dev/null
-mage
-{
- cull none
-
- {
- map textures/mage
- }
-}
--- /dev/null
+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
+ }
+}
+++ /dev/null
-textures/berzerker_texture
-{
- cull none
-
- {
- map textures/ogre
- }
-}
tuba
turrets
weapons
-mage
barricade
-spider
-cerberus
-ogre
+monsters
+++ /dev/null
-textures/spider/spidertex
-{
- cull none
-
- {
- map textures/spidertex
- }
-}