X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fcommon%2Fmonsters%2Fsv_monsters.qc;h=e7056f6c6cf6d5986cca83c2073bd07402f26167;hb=066b04f9c76d2416846e67b05c7f337328cb97eb;hp=15fba0134200deccbfacf77b8f70af222558ef4f;hpb=9366bc8fa80268a5cc80044db3f38f98ecfea3e6;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index 15fba0134..e7056f6c6 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -1,6 +1,5 @@ #include "sv_monsters.qh" -#include #include #include "../constants.qh" #include "../teams.qh" @@ -18,7 +17,7 @@ #include "../vehicles/all.qh" #include #include -#include "../triggers/triggers.qh" +#include "../mapobjects/triggers.qh" #include #include #include @@ -56,6 +55,21 @@ void monster_dropitem(entity this, entity attacker) } } +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)) @@ -72,25 +86,26 @@ void monster_makevectors(entity this, entity targ) // 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)) ) { @@ -99,18 +114,16 @@ bool Monster_ValidTarget(entity this, entity 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! @@ -124,21 +137,27 @@ entity Monster_FindTarget(entity this) 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; @@ -165,6 +184,11 @@ void monster_setupcolors(entity this) 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) @@ -189,7 +213,8 @@ 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) { @@ -275,7 +300,7 @@ void Monster_Sounds_Precache(entity this) 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 } @@ -310,9 +335,7 @@ bool Monster_Sounds_Load(entity this, string f, int first) 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; @@ -339,9 +362,8 @@ void Monster_Sound(entity this, .string samplefield, float sound_delay, bool del 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; } @@ -367,7 +389,7 @@ bool Monster_Attack_Melee(entity this, entity targ, float damg, vector anim, flo 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; } @@ -378,7 +400,7 @@ bool Monster_Attack_Leap_Check(entity this, vector vel) 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 @@ -423,6 +445,7 @@ void Monster_Attack_Check(entity this, entity targ, .entity weaponentity) 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)) @@ -472,10 +495,8 @@ void Monster_Touch(entity this, entity toucher) { 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; } @@ -489,7 +510,7 @@ void Monster_Miniboss_Check(entity this) // 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; @@ -530,10 +551,11 @@ void Monster_Dead_Fade(entity this) 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 @@ -547,7 +569,7 @@ void Monster_Dead_Fade(entity this) 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; @@ -562,7 +584,7 @@ vector Monster_Move_Target(entity this, entity targ) // 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) @@ -660,42 +682,6 @@ vector Monster_Move_Target(entity this, entity targ) } } -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; @@ -720,7 +706,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) { 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) { @@ -798,13 +784,15 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) 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); @@ -826,6 +814,9 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) 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; @@ -864,6 +855,9 @@ void Monster_Dead_Think(entity this) { 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) { @@ -899,7 +893,7 @@ void Monster_Reset(entity this) 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; @@ -907,13 +901,13 @@ void Monster_Reset(entity this) 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); @@ -935,7 +929,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) 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); @@ -959,6 +953,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) _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; @@ -970,6 +965,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) 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'; @@ -986,7 +982,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) } } -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; @@ -1003,7 +999,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage 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; @@ -1012,12 +1008,12 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage 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; @@ -1035,7 +1031,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage 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 @@ -1044,13 +1040,13 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage 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); @@ -1060,6 +1056,18 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage } } +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) { @@ -1152,33 +1160,33 @@ void Monster_Frozen_Think(entity this) { 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! @@ -1213,7 +1221,7 @@ void Monster_Think(entity this) 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; } @@ -1245,8 +1253,8 @@ bool Monster_Spawn_Setup(entity this) 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; } @@ -1256,14 +1264,15 @@ bool Monster_Spawn_Setup(entity this) 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; @@ -1291,7 +1300,7 @@ bool Monster_Spawn_Setup(entity this) 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)); } } @@ -1356,6 +1365,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) 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; @@ -1371,7 +1381,6 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) 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; @@ -1388,7 +1397,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) 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) @@ -1397,17 +1406,11 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) 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);