#include "sv_monsters.qh"
-#include <server/g_subs.qh>
#include <lib/warpzone/common.qh>
#include "../constants.qh"
#include "../teams.qh"
#include "../vehicles/all.qh"
#include <server/campaign.qh>
#include <server/command/_mod.qh>
-#include "../triggers/triggers.qh"
+#include "../mapobjects/triggers.qh"
#include <lib/csqcmodel/sv_model.qh>
#include <server/round_handler.qh>
#include <server/weapons/_mod.qh>
}
}
+bool monster_facing(entity this, entity targ)
+{
+ // relies on target having an origin
+ makevectors(this.angles);
+ vector targ_org = targ.origin, my_org = this.origin;
+ if(autocvar_g_monsters_target_infront_2d)
+ {
+ targ_org = vec2(targ_org);
+ my_org = vec2(my_org);
+ }
+ float dot = normalize(targ_org - my_org) * v_forward;
+
+ return !(dot <= autocvar_g_monsters_target_infront_range);
+}
+
void monster_makevectors(entity this, entity targ)
{
if(IS_MONSTER(this))
// Target handling
// ===============
-bool Monster_ValidTarget(entity this, entity targ)
+bool Monster_ValidTarget(entity this, entity targ, bool skipfacing)
{
// ensure we're not checking nonexistent monster/target
if(!this || !targ) { return false; }
if((targ == this)
- || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen
- || (IS_VEHICLE(targ) && !((Monsters_from(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
|| (time < game_starttime) // monsters do nothing before match has started
|| (targ.takedamage == DAMAGE_NO)
+ || (game_stopped)
|| (targ.items & IT_INVISIBILITY)
|| (IS_SPEC(targ) || IS_OBSERVER(targ)) // don't attack spectators
- || (!IS_VEHICLE(targ) && (IS_DEAD(targ) || IS_DEAD(this) || targ.health <= 0 || this.health <= 0))
+ || (!IS_VEHICLE(targ) && (IS_DEAD(targ) || IS_DEAD(this) || GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(this, RESOURCE_HEALTH) <= 0))
|| (this.monster_follow == targ || targ.monster_follow == this)
|| (!IS_VEHICLE(targ) && (targ.flags & FL_NOTARGET))
|| (!autocvar_g_monsters_typefrag && PHYS_INPUT_BUTTON_CHAT(targ))
+ || (IS_VEHICLE(targ) && !((Monsters_from(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
|| (SAME_TEAM(targ, this))
|| (STAT(FROZEN, targ))
|| (targ.alpha != 0 && targ.alpha < 0.5)
+ || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen
|| (MUTATOR_CALLHOOK(MonsterValidTarget, this, targ))
)
{
}
vector targ_origin = ((targ.absmin + targ.absmax) * 0.5);
- traceline(this.origin + this.view_ofs, targ_origin, MOVE_NOMONSTERS, this);
+ traceline(this.origin + this.view_ofs, targ_origin, MOVE_NOMONSTERS, this); // TODO: maybe we can rely a bit on PVS data instead?
if(trace_fraction < 1 && trace_ent != targ)
return false; // solid
- if(autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT))
+ if(!skipfacing && (autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT)))
if(this.enemy != targ)
{
- makevectors (this.angles);
- float dot = normalize (targ.origin - this.origin) * v_forward;
-
- if(dot <= autocvar_g_monsters_target_infront_range) { return false; }
+ if(!monster_facing(this, targ))
+ return false;
}
return true; // this target is valid!
vector my_center = CENTER_OR_VIEWOFS(this);
// find the closest acceptable target to pass to
- IL_EACH(g_monster_targets, it.monster_attack && vdist(it.origin - this.origin, <, this.target_range),
+ IL_EACH(g_monster_targets, it.monster_attack,
{
- if(Monster_ValidTarget(this, it))
- {
- // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
- vector targ_center = CENTER_OR_VIEWOFS(it);
+ float trange = this.target_range;
+ if(PHYS_INPUT_BUTTON_CROUCH(it))
+ trange *= 0.75; // TODO cvar this
+ vector theirmid = (it.absmin + it.absmax) * 0.5;
+ if(vdist(theirmid - this.origin, >, trange))
+ continue;
+ if(!Monster_ValidTarget(this, it, false))
+ continue;
- if(closest_target)
- {
- vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
- if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center))
- { closest_target = it; }
- }
- else { closest_target = it; }
+ // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
+ vector targ_center = CENTER_OR_VIEWOFS(it);
+
+ if(closest_target)
+ {
+ vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
+ if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center))
+ { closest_target = it; }
}
+ else { closest_target = it; }
});
return closest_target;
else
this.colormap = 1024;
}
+
+ if(this.colormap > 0)
+ this.glowmod = colormapPaletteColor(this.colormap & 0x0F, false);
+ else
+ this.glowmod = '1 1 1';
}
void monster_changeteam(entity this, int newteam)
.void(entity) monster_delayedfunc;
void Monster_Delay_Action(entity this)
{
- if(Monster_ValidTarget(this.owner, this.owner.enemy)) { this.monster_delayedfunc(this.owner); }
+ // TODO: maybe do check for facing here
+ if(Monster_ValidTarget(this.owner, this.owner.enemy, false)) { this.monster_delayedfunc(this.owner); }
if(this.cnt > 1)
{
void Monster_Sounds_Clear(entity this)
{
-#define _MSOUND(m) if(this.monstersound_##m) { strunzone(this.monstersound_##m); this.monstersound_##m = string_null; }
+#define _MSOUND(m) strfree(this.monstersound_##m);
ALLMONSTERSOUNDS
#undef _MSOUND
}
field = Monster_Sound_SampleField(argv(0));
if(GetMonsterSoundSampleField_notFound)
continue;
- if (this.(field))
- strunzone(this.(field));
- this.(field) = strzone(strcat(argv(1), " ", argv(2)));
+ strcpy(this.(field), strcat(argv(1), " ", argv(2)));
}
fclose(fh);
return true;
string sample = this.(samplefield);
if (sample != "") sample = GlobalSound_sample(sample, random());
float myscale = ((this.scale) ? this.scale : 1); // safety net
- float scale_inverse = 1 / myscale;
// TODO: change volume depending on size too?
- sound7(this, chan, sample, VOL_BASE, ATTEN_NORM, scale_inverse * 100, 0);
+ sound7(this, chan, sample, VOL_BASE, ATTEN_NORM, 100 / myscale, 0);
this.msound_delay = time + sound_delay;
}
traceline(this.origin + this.view_ofs, this.origin + v_forward * er, 0, this);
if(trace_ent.takedamage)
- Damage(trace_ent, this, this, damg * MONSTER_SKILLMOD(this), deathtype, trace_ent.origin, normalize(trace_ent.origin - this.origin));
+ Damage(trace_ent, this, this, damg * MONSTER_SKILLMOD(this), deathtype, DMG_NOWEP, trace_ent.origin, normalize(trace_ent.origin - this.origin));
return true;
}
return false; // already attacking
if(!IS_ONGROUND(this))
return false; // not on the ground
- if(this.health <= 0 || IS_DEAD(this))
+ if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0 || IS_DEAD(this))
return false; // called when dead?
if(time < this.attack_finished_single[0])
return false; // still attacking
if((!this || !targ)
|| (!this.monster_attackfunc)
|| (time < this.attack_finished_single[slot])
+ || ((autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT)) && !monster_facing(this, targ))
) { return; }
if(vdist(targ.origin - this.origin, <=, this.attack_range))
{
if(toucher == NULL) { return; }
- if(toucher.monster_attack)
- if(this.enemy != toucher)
- if(!IS_MONSTER(toucher))
- if(Monster_ValidTarget(this, toucher))
+ if(toucher.monster_attack && this.enemy != toucher && !IS_MONSTER(toucher) && time >= this.spawn_time)
+ if(Monster_ValidTarget(this, toucher, true))
this.enemy = toucher;
}
// g_monsters_miniboss_chance cvar or spawnflags 64 causes a monster to be a miniboss
if ((this.spawnflags & MONSTERFLAG_MINIBOSS) || (chance < autocvar_g_monsters_miniboss_chance))
{
- this.health += autocvar_g_monsters_miniboss_healthboost;
+ GiveResource(this, RESOURCE_HEALTH, autocvar_g_monsters_miniboss_healthboost);
this.effects |= EF_RED;
if(!this.weapon)
this.weapon = WEP_VORTEX.m_id;
this.pos2 = this.angles;
}
this.event_damage = func_null;
+ this.event_heal = func_null;
this.takedamage = DAMAGE_NO;
setorigin(this, this.pos1);
this.angles = this.pos2;
- this.health = this.max_health;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health);
setmodel(this, MDL_Null);
}
else
void Monster_Use(entity this, entity actor, entity trigger)
{
- if(Monster_ValidTarget(this, actor)) { this.enemy = actor; }
+ if(Monster_ValidTarget(this, actor, true)) { this.enemy = actor; }
}
.float pass_distance;
// cases where the enemy may have changed their state (don't need to check everything here)
if((!this.enemy)
- || (IS_DEAD(this.enemy) || this.enemy.health < 1)
+ || (IS_DEAD(this.enemy) || GetResourceAmount(this.enemy, RESOURCE_HEALTH) < 1)
|| (STAT(FROZEN, this.enemy))
|| (this.enemy.flags & FL_NOTARGET)
|| (this.enemy.alpha < 0.5 && this.enemy.alpha != 0)
}
}
-void Monster_CalculateVelocity(entity this, vector to, vector from, float turnrate, float movespeed)
-{
- //float current_distance = vlen((('1 0 0' * to.x) + ('0 1 0' * to.y)) - (('1 0 0' * from.x) + ('0 1 0' * from.y))); // for the sake of this check, exclude Z axis
- //float initial_height = 0; //min(50, (targ_distance * tanh(20)));
- //float current_height = (initial_height * min(1, (this.pass_distance) ? (current_distance / this.pass_distance) : current_distance));
- //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n");
-
- vector targpos = to;
-#if 0
- if(current_height) // make sure we can actually do this arcing path
- {
- targpos = (to + ('0 0 1' * current_height));
- WarpZone_TraceLine(this.origin, targpos, MOVE_NOMONSTERS, this);
- if(trace_fraction < 1)
- {
- //print("normal arc line failed, trying to find new pos...");
- WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, this);
- targpos = (trace_endpos + '0 0 -10');
- WarpZone_TraceLine(this.origin, targpos, MOVE_NOMONSTERS, this);
- if(trace_fraction < 1) { targpos = to; /* print(" ^1FAILURE^7, reverting to original direction.\n"); */ }
- /*else { print(" ^3SUCCESS^7, using new arc line.\n"); } */
- }
- }
- else { targpos = to; }
-#endif
-
- //this.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y));
-
- vector desired_direction = normalize(targpos - from);
- if(turnrate) { this.velocity = (normalize(normalize(this.velocity) + (desired_direction * 50)) * movespeed); }
- else { this.velocity = (desired_direction * movespeed); }
-
- //this.steerto = steerlib_attract2(targpos, 0.5, 500, 0.95);
- //this.angles = vectoangles(this.velocity);
-}
-
.entity draggedby;
.entity target2;
{
this.last_trace = time + 0.4;
- Damage (this, NULL, NULL, 2, DEATH_DROWN.m_id, this.origin, '0 0 0');
+ Damage (this, NULL, NULL, 2, DEATH_DROWN.m_id, DMG_NOWEP, this.origin, '0 0 0');
this.angles = '90 90 0';
if(random() < 0.5)
{
if(!(this.spawnflags & MONSTERFLAG_FLY_VERTICAL) && !(this.flags & FL_SWIM))
this.moveto_z = this.origin_z;
- if(vdist(this.origin - this.moveto, >, 100))
+ fixedmakevectors(this.angles);
+ float vz = this.velocity_z;
+
+ if(!turret_closetotarget(this, this.moveto))
{
bool do_run = (this.enemy || this.monster_moveto);
- if(IS_ONGROUND(this) || ((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
- Monster_CalculateVelocity(this, this.moveto, this.origin, true, ((do_run) ? runspeed : walkspeed));
+ movelib_move_simple(this, v_forward, ((do_run) ? runspeed : walkspeed), 0.4);
- if(time > this.pain_finished && time > this.anim_finished) // TODO: use anim_finished instead!?
+ if(time > this.pain_finished && time > this.anim_finished)
if(!this.state)
if(vdist(this.velocity, >, 10))
setanim(this, ((do_run) ? this.anim_run : this.anim_walk), true, false, false);
setanim(this, this.anim_idle, true, false, false);
}
+ if(!((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
+ this.velocity_z = vz;
+
this.steerto = steerlib_attract2(this, ((this.monster_face) ? this.monster_face : this.moveto), 0.5, 500, 0.95);
vector real_angle = vectoangles(this.steerto) - this.angles;
{
this.nextthink = time + this.ticrate;
+ Monster mon = Monsters_from(this.monsterid);
+ mon.mr_deadthink(mon, this);
+
if(this.monster_lifetime != 0)
if(time >= this.monster_lifetime)
{
Unfreeze(this); // remove any icy remains
- this.health = this.max_health;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health);
this.velocity = '0 0 0';
this.enemy = NULL;
this.goalentity = NULL;
this.moveto = this.origin;
}
-void Monster_Dead_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+void Monster_Dead_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
{
- this.health -= damage;
+ TakeResource(this, RESOURCE_HEALTH, damage);
Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
- if(this.health <= -50) // 100 health until gone?
+ if(GetResourceAmount(this, RESOURCE_HEALTH) <= -50) // 100 health until gone?
{
Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker);
if(STAT(FROZEN, this))
{
Unfreeze(this); // remove any icy remains
- this.health = 0; // reset by Unfreeze
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); // reset by Unfreeze (TODO)
}
monster_dropitem(this, attacker);
_setmodel(this, this.mdl_dead);
this.event_damage = ((gibbed) ? func_null : Monster_Dead_Damage);
+ this.event_heal = func_null;
this.solid = SOLID_CORPSE;
this.takedamage = DAMAGE_AIM;
this.deadflag = DEAD_DEAD;
this.state = 0;
this.attack_finished_single[0] = 0;
this.effects = 0;
+ this.dphitcontentsmask &= ~DPCONTENTS_BODY;
if(!((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
this.velocity = '0 0 0';
}
}
-void Monster_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+void Monster_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
{
if((this.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL.m_id && !ITEM_DAMAGE_NEEDKILL(deathtype))
return;
if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL)
return;
- vector v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage);
+ vector v = healtharmor_applydamage(100, GetResourceAmount(this, RESOURCE_ARMOR) / 100, deathtype, damage);
float take = v.x;
//float save = v.y;
if(take)
{
- this.health -= take;
+ TakeResource(this, RESOURCE_HEALTH, take);
Monster_Sound(this, monstersound_pain, 1.2, true, CH_PAIN);
}
if(this.sprite)
- WaypointSprite_UpdateHealth(this.sprite, this.health);
+ WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH));
this.dmg_time = time;
Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker);
}
- if(this.health <= 0)
+ if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0)
{
if(deathtype == DEATH_KILL.m_id)
this.candrop = false; // killed by mobkill command
SUB_UseTargets(this, attacker, this.enemy);
this.target2 = this.oldtarget2; // reset to original target on death, incase we respawn
- Monster_Dead(this, attacker, (this.health <= -100 || deathtype == DEATH_KILL.m_id));
+ Monster_Dead(this, attacker, (GetResourceAmount(this, RESOURCE_HEALTH) <= -100 || deathtype == DEATH_KILL.m_id));
WaypointSprite_Kill(this.sprite);
MUTATOR_CALLHOOK(MonsterDies, this, attacker, deathtype);
- if(this.health <= -100 || deathtype == DEATH_KILL.m_id) // check if we're already gibbed
+ if(GetResourceAmount(this, RESOURCE_HEALTH) <= -100 || deathtype == DEATH_KILL.m_id) // check if we're already gibbed
{
Violence_GibSplash(this, 1, 0.5, attacker);
}
}
+bool Monster_Heal(entity targ, entity inflictor, float amount, float limit)
+{
+ float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health);
+ if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit)
+ return false;
+
+ GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit);
+ if(targ.sprite)
+ WaypointSprite_UpdateHealth(targ.sprite, GetResourceAmount(targ, RESOURCE_HEALTH));
+ return true;
+}
+
// don't check for enemies, just keep walking in a straight line
void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
{
{
if(STAT(FROZEN, this) == 2)
{
- this.revive_progress = bound(0, this.revive_progress + this.ticrate * this.revive_speed, 1);
- this.health = max(1, this.revive_progress * this.max_health);
- this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1);
+ STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + this.ticrate * this.revive_speed, 1);
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * this.max_health));
+ this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1);
if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite)
- WaypointSprite_UpdateHealth(this.sprite, this.health);
+ WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH));
- if(this.revive_progress >= 1)
+ if(STAT(REVIVE_PROGRESS, this) >= 1)
Unfreeze(this);
}
else if(STAT(FROZEN, this) == 3)
{
- this.revive_progress = bound(0, this.revive_progress - this.ticrate * this.revive_speed, 1);
- this.health = max(0, autocvar_g_nades_ice_health + (this.max_health-autocvar_g_nades_ice_health) * this.revive_progress );
+ STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - this.ticrate * this.revive_speed, 1);
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(0, autocvar_g_nades_ice_health + (this.max_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this)));
if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite)
- WaypointSprite_UpdateHealth(this.sprite, this.health);
+ WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH));
- if(this.health < 1)
+ if(GetResourceAmount(this, RESOURCE_HEALTH) < 1)
{
Unfreeze(this);
- this.health = 0;
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0);
if(this.event_damage)
- this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0');
+ this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0');
}
- else if ( this.revive_progress <= 0 )
+ else if ( STAT(REVIVE_PROGRESS, this) <= 0 )
Unfreeze(this);
}
// otherwise, no revival!
if(this.monster_lifetime && time >= this.monster_lifetime)
{
- Damage(this, this, this, this.health + this.max_health, DEATH_KILL.m_id, this.origin, this.origin);
+ Damage(this, this, this, GetResourceAmount(this, RESOURCE_HEALTH) + this.max_health, DEATH_KILL.m_id, DMG_NOWEP, this.origin, this.origin);
return;
}
mon.mr_setup(mon, this);
// ensure some basic needs are met
- if(!this.health) { this.health = 100; }
- if(!this.armorvalue) { this.armorvalue = bound(0.2, 0.5 * MONSTER_SKILLMOD(this), 0.9); }
+ if(!GetResourceAmount(this, RESOURCE_HEALTH)) { SetResourceAmountExplicit(this, RESOURCE_HEALTH, 100); }
+ if(!GetResourceAmount(this, RESOURCE_ARMOR)) { SetResourceAmountExplicit(this, RESOURCE_ARMOR, bound(0.2, 0.5 * MONSTER_SKILLMOD(this), 0.9)); }
if(!this.target_range) { this.target_range = autocvar_g_monsters_target_range; }
if(!this.respawntime) { this.respawntime = autocvar_g_monsters_respawn_delay; }
if(!this.monster_moveflags) { this.monster_moveflags = MONSTER_MOVE_WANDER; }
if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
{
Monster_Miniboss_Check(this);
- this.health *= MONSTER_SKILLMOD(this);
+ SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH) * MONSTER_SKILLMOD(this));
if(!this.skin)
this.skin = rint(random() * 4);
}
- this.max_health = this.health;
+ this.max_health = GetResourceAmount(this, RESOURCE_HEALTH);
this.pain_finished = this.nextthink;
+ this.last_enemycheck = this.spawn_time + random(); // slight delay
if(IS_PLAYER(this.monster_follow))
this.effects |= EF_DIMLIGHT;
if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE))
{
WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health);
- WaypointSprite_UpdateHealth(this.sprite, this.health);
+ WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH));
}
}
this.damagedbycontents = true;
this.monsterid = mon_id;
this.event_damage = Monster_Damage;
+ this.event_heal = Monster_Heal;
settouch(this, Monster_Touch);
this.use = Monster_Use;
this.solid = SOLID_BBOX;
this.monster_attackfunc = mon.monster_attackfunc;
this.monster_name = mon.monster_name;
this.candrop = true;
- this.view_ofs = '0 0 0.7' * (this.maxs_z * 0.5);
this.oldtarget2 = this.target2;
//this.pass_distance = 0;
this.deadflag = DEAD_NO;
if(autocvar_g_nodepthtestplayers) { this.effects |= EF_NODEPTHTEST; }
if(mon.spawnflags & MONSTER_TYPE_SWIM) { this.flags |= FL_SWIM; }
- if(autocvar_g_playerclip_collisions)
+ if(autocvar_g_monsters_playerclip_collisions)
this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
if(mon.spawnflags & MONSTER_TYPE_FLY)
set_movetype(this, MOVETYPE_FLY);
}
- if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
- {
- if(mon.spawnflags & MONSTER_SIZE_BROKEN)
- this.scale *= 1.3;
-
- if(mon.spawnflags & MONSTER_SIZE_QUAKE)
- if(autocvar_g_monsters_quake_resize)
- this.scale *= 1.3;
- }
+ if((mon.spawnflags & MONSTER_SIZE_QUAKE) && autocvar_g_monsters_quake_resize && !(this.spawnflags & MONSTERFLAG_RESPAWNED))
+ this.scale *= 1.3;
setsize(this, mon.m_mins * this.scale, mon.m_maxs * this.scale);
+ this.view_ofs = '0 0 0.7' * (this.maxs_z * 0.5);
this.ticrate = bound(sys_frametime, ((!this.ticrate) ? autocvar_g_monsters_think_delay : this.ticrate), 60);