X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fcommon%2Fmonsters%2Fsv_monsters.qc;h=1635b5cb0d235c6eeb84dac673c18d98e9421e96;hb=da71736a95bbb74a671107ecdeda24f2056ac3e6;hp=4c2b5ff269a601b1ff502f3977f97af5552aa532;hpb=be996f9b9d67ebbb153442b7e077253de6da798c;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index 4c2b5ff26..1635b5cb0 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -1,25 +1,33 @@ #include "sv_monsters.qh" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include "../constants.qh" -#include "../teams.qh" -#include "../util.qh" -#include "all.qh" -#include "../physics/movelib.qh" -#include "../weapons/_mod.qh" -#include -#include -#include "../deathtypes/all.qh" -#include -#include -#include "../turrets/sv_turrets.qh" -#include "../turrets/util.qh" -#include "../vehicles/all.qh" #include +#include +#include #include -#include "../mapobjects/triggers.qh" -#include +#include +#include +#include #include +#include #include void monsters_setstatus(entity this) @@ -47,6 +55,8 @@ void monster_dropitem(entity this, entity attacker) { e.noalign = true; StartItem(e, e.monster_loot); + if(startitem_failed || wasfreed(e)) + return; e.gravity = 1; setorigin(e, org); e.velocity = randomvec() * 175 + '0 0 325'; @@ -55,6 +65,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)) @@ -71,18 +96,16 @@ 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) && !((REGISTRY_GET(Monsters, this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless + || (IS_VEHICLE(targ) && !(this.monsterdef.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) || GetResource(targ, RES_HEALTH) <= 0 || GetResource(this, RES_HEALTH) <= 0)) || (this.monster_follow == targ || targ.monster_follow == this) @@ -91,6 +114,7 @@ bool Monster_ValidTarget(entity this, entity targ) || (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 +123,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 +146,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; @@ -153,18 +181,23 @@ void monster_setupcolors(entity this) else { if(this.monster_skill <= MONSTER_SKILL_EASY) - this.colormap = 1029; + this.colormap = 1126; else if(this.monster_skill <= MONSTER_SKILL_MEDIUM) - this.colormap = 1027; + this.colormap = 1075; else if(this.monster_skill <= MONSTER_SKILL_HARD) - this.colormap = 1038; + this.colormap = 1228; else if(this.monster_skill <= MONSTER_SKILL_INSANE) - this.colormap = 1028; + this.colormap = 1092; else if(this.monster_skill <= MONSTER_SKILL_NIGHTMARE) - this.colormap = 1032; + this.colormap = 1160; 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 +222,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)) + // TODO: maybe do check for facing here + if(Monster_ValidTarget(this.owner, this.owner.enemy, false)) { monster_makevectors(this.owner, this.owner.enemy); this.monster_delayedfunc(this.owner); @@ -211,7 +245,7 @@ void Monster_Delay_Action(entity this) void Monster_Delay(entity this, int repeat_count, float defer_amnt, void(entity) func) { // deferred attacking, checks if monster is still alive and target is still valid before attacking - entity e = spawn(); + entity e = new_pure(Monster_Delay); setthink(e, Monster_Delay_Action); e.nextthink = time + defer_amnt; @@ -260,7 +294,7 @@ void Monster_Sound_Precache(string f) void Monster_Sounds_Precache(entity this) { - string m = (REGISTRY_GET(Monsters, this.monsterid)).m_model.model_str(); + string m = this.monsterdef.m_model.model_str(); float globhandle, n, i; string f; @@ -341,7 +375,6 @@ 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 - // TODO: change volume depending on size too? sound7(this, chan, sample, VOL_BASE, ATTEN_NORM, 100 / myscale, 0); this.msound_delay = time + sound_delay; @@ -421,7 +454,9 @@ void Monster_Attack_Check(entity this, entity targ, .entity weaponentity) if((!this || !targ) || (!this.monster_attackfunc) + || (game_stopped) || (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)) @@ -465,18 +500,16 @@ void Monster_UpdateModel(entity this) this.anim_die2 = animfixfps(this, '9 1 0.01', '0 0 0');*/ // then get the real values - Monster mon = REGISTRY_GET(Monsters, this.monsterid); + Monster mon = this.monsterdef; mon.mr_anim(mon, this); } void Monster_Touch(entity this, entity toucher) { - if(toucher == NULL) { return; } + if(!toucher) { 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; } @@ -512,7 +545,7 @@ bool Monster_Respawn_Check(entity this) return true; } -void Monster_Respawn(entity this) { Monster_Spawn(this, true, this.monsterid); } +void Monster_Respawn(entity this) { Monster_Spawn(this, true, this.monsterdef); } .vector pos1, pos2; @@ -549,10 +582,9 @@ 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; vector Monster_Move_Target(entity this, entity targ) { // enemy is always preferred target @@ -560,21 +592,6 @@ vector Monster_Move_Target(entity this, entity targ) { vector targ_origin = ((this.enemy.absmin + this.enemy.absmax) * 0.5); targ_origin = WarpZone_RefSys_TransformOrigin(this.enemy, this, targ_origin); // origin of target as seen by the monster (us) - WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this); - - // cases where the enemy may have changed their state (don't need to check everything here) - if((!this.enemy) - || (IS_DEAD(this.enemy) || GetResource(this.enemy, RES_HEALTH) < 1) - || (STAT(FROZEN, this.enemy)) - || (this.enemy.flags & FL_NOTARGET) - || (this.enemy.alpha < 0.5 && this.enemy.alpha != 0) - || (this.enemy.takedamage == DAMAGE_NO) - || (vdist(this.origin - targ_origin, >, this.target_range)) - || ((trace_fraction < 1) && (trace_ent != this.enemy))) - { - this.enemy = NULL; - //this.pass_distance = 0; - } if(this.enemy) { @@ -662,42 +679,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; void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) @@ -799,13 +780,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, 16)) { 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)) @@ -829,6 +812,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; @@ -867,6 +853,9 @@ void Monster_Dead_Think(entity this) { this.nextthink = time + this.ticrate; + Monster mon = REGISTRY_GET(Monsters, this.monsterid); + mon.mr_deadthink(mon, this); + if(this.monster_lifetime != 0) if(time >= this.monster_lifetime) { @@ -878,16 +867,16 @@ void Monster_Dead_Think(entity this) void Monster_Appear(entity this, entity actor, entity trigger) { this.enemy = actor; - Monster_Spawn(this, false, this.monsterid); + Monster_Spawn(this, false, this.monsterdef); } -bool Monster_Appear_Check(entity this, int monster_id) +bool Monster_Appear_Check(entity this, Monster monster_id) { if(!(this.spawnflags & MONSTERFLAG_APPEAR)) return false; setthink(this, func_null); - this.monsterid = monster_id; // set so this monster is properly registered (otherwise, normal initialization is used) + this.monsterdef = monster_id; // set so this monster is properly registered (otherwise, normal initialization is used) this.nextthink = 0; this.use = Monster_Appear; this.flags = FL_MONSTER; // set so this monster can get butchered @@ -897,6 +886,12 @@ bool Monster_Appear_Check(entity this, int monster_id) void Monster_Reset(entity this) { + if(this.spawnflags & MONSTERFLAG_SPAWNED) + { + Monster_Remove(this); + return; + } + setorigin(this, this.pos1); this.angles = this.pos2; @@ -971,13 +966,14 @@ 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'; CSQCModel_UnlinkEntity(this); - Monster mon = REGISTRY_GET(Monsters, this.monsterid); + Monster mon = this.monsterdef; mon.mr_death(mon, this); if(this.candrop && this.weapon) @@ -998,7 +994,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage //if(time < this.pain_finished && deathtype != DEATH_KILL.m_id) //return; - if(time < this.spawnshieldtime && deathtype != DEATH_KILL.m_id) + if(StatusEffects_active(STATUSEFFECT_SpawnShield, this) && deathtype != DEATH_KILL.m_id) return; if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL) @@ -1008,7 +1004,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage float take = v.x; //float save = v.y; - Monster mon = REGISTRY_GET(Monsters, this.monsterid); + Monster mon = this.monsterdef; take = mon.mr_pain(mon, this, take, attacker, deathtype); if(take) @@ -1094,7 +1090,7 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff) bool reverse = false; if(trace_fraction != 1.0) reverse = true; - if(trace_ent && IS_PLAYER(trace_ent) && !(trace_ent.items & ITEM_Strength.m_itemid)) + if(trace_ent && IS_PLAYER(trace_ent)) reverse = false; if(trace_ent && IS_MONSTER(trace_ent)) reverse = true; @@ -1145,7 +1141,7 @@ void Monster_Anim(entity this) int animbits = deadbits; if(STAT(FROZEN, this)) animbits |= ANIMSTATE_FROZEN; - if(this.crouch) + if(IS_DUCKED(this)) animbits |= ANIMSTATE_DUCK; // not that monsters can crouch currently... animdecide_setstate(this, animbits, false); animdecide_setimplicitstate(this, (IS_ONGROUND(this))); @@ -1199,22 +1195,42 @@ void Monster_Frozen_Think(entity this) void Monster_Enemy_Check(entity this) { - if(!this.enemy) + if(this.enemy) { - this.enemy = Monster_FindTarget(this); - if(this.enemy) + vector targ_origin = ((this.enemy.absmin + this.enemy.absmax) * 0.5); + targ_origin = WarpZone_RefSys_TransformOrigin(this.enemy, this, targ_origin); // origin of target as seen by the monster (us) + WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this); + + // cases where the enemy may have changed their state (don't need to check everything here) + if( (IS_DEAD(this.enemy) || GetResource(this.enemy, RES_HEALTH) < 1) + || (STAT(FROZEN, this.enemy)) + || (this.enemy.flags & FL_NOTARGET) + || (this.enemy.alpha < 0.5 && this.enemy.alpha != 0) + || (this.enemy.takedamage == DAMAGE_NO) + || (vdist(this.origin - targ_origin, >, this.target_range)) + || ((trace_fraction < 1) && (trace_ent != this.enemy)) + ) + { + this.enemy = NULL; + } + else { - WarpZone_RefSys_Copy(this.enemy, this); - WarpZone_RefSys_AddInverse(this.enemy, this); // wz1^-1 ... wzn^-1 receiver - // update move target immediately? - this.moveto = WarpZone_RefSys_TransformOrigin(this.enemy, this, (0.5 * (this.enemy.absmin + this.enemy.absmax))); - this.monster_moveto = '0 0 0'; - this.monster_face = '0 0 0'; - - //this.pass_distance = vlen((('1 0 0' * this.enemy.origin_x) + ('0 1 0' * this.enemy.origin_y)) - (('1 0 0' * this.origin_x) + ('0 1 0' * this.origin_y))); - Monster_Sound(this, monstersound_sight, 0, false, CH_VOICE); + return; } } + + this.enemy = Monster_FindTarget(this); + if(this.enemy) + { + WarpZone_RefSys_Copy(this.enemy, this); + WarpZone_RefSys_AddInverse(this.enemy, this); // wz1^-1 ... wzn^-1 receiver + // update move target immediately? + this.moveto = WarpZone_RefSys_TransformOrigin(this.enemy, this, (0.5 * (this.enemy.absmin + this.enemy.absmax))); + this.monster_moveto = '0 0 0'; + this.monster_face = '0 0 0'; + + Monster_Sound(this, monstersound_sight, 0, false, CH_VOICE); + } } void Monster_Think(entity this) @@ -1236,7 +1252,7 @@ void Monster_Think(entity this) this.last_enemycheck = time + 1; // check for enemies every second } - Monster mon = REGISTRY_GET(Monsters, this.monsterid); + Monster mon = this.monsterdef; if(mon.mr_think(mon, this)) { Monster_Move(this, this.speed2, this.speed, this.stopspeed); @@ -1252,7 +1268,7 @@ void Monster_Think(entity this) bool Monster_Spawn_Setup(entity this) { - Monster mon = REGISTRY_GET(Monsters, this.monsterid); + Monster mon = this.monsterdef; mon.mr_setup(mon, this); // ensure some basic needs are met @@ -1275,6 +1291,7 @@ bool Monster_Spawn_Setup(entity this) this.max_health = GetResource(this, RES_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; @@ -1297,7 +1314,7 @@ bool Monster_Spawn_Setup(entity this) if(autocvar_g_monsters_healthbars) { entity wp = WaypointSprite_Spawn(WP_Monster, 0, 1024, this, '0 0 1' * (this.maxs.z + 15), NULL, this.team, this, sprite, true, RADARICON_DANGER); - wp.wp_extra = this.monsterid; + wp.wp_extra = this.monsterdef.monsterid; wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 0 0'); if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE)) { @@ -1315,12 +1332,11 @@ bool Monster_Spawn_Setup(entity this) return true; } -bool Monster_Spawn(entity this, bool check_appear, int mon_id) +bool Monster_Spawn(entity this, bool check_appear, Monster mon) { // setup the basic required properties for a monster - entity mon = REGISTRY_GET(Monsters, mon_id); - if(!mon.monsterid) { return false; } // invalid monster + if(!mon || mon == MON_Null) { return false; } // invalid monster if(!autocvar_g_monsters) { Monster_Remove(this); return false; } if(!(this.spawnflags & MONSTERFLAG_RESPAWNED) && !(this.flags & FL_MONSTER)) @@ -1332,7 +1348,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) precache_model(this.mdl_dead); } - if(check_appear && Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed + if(check_appear && Monster_Appear_Check(this, mon)) { return true; } // return true so the monster isn't removed if(!this.monster_skill) this.monster_skill = cvar("g_monsters_skill"); @@ -1354,6 +1370,17 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) else setmodel(this, mon.m_model); + if(!this.monster_name || this.monster_name == "") + this.monster_name = mon.monster_name; + + if(this.statuseffects && this.statuseffects.owner == this) + { + StatusEffects_clearall(this.statuseffects); + StatusEffects_update(this); + } + else + this.statuseffects = NULL; + this.flags = FL_MONSTER; this.classname = "monster"; this.takedamage = DAMAGE_AIM; @@ -1365,14 +1392,14 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) if(!this.damagedbycontents) IL_PUSH(g_damagedbycontents, this); this.damagedbycontents = true; - this.monsterid = mon_id; + this.monsterdef = mon; this.event_damage = Monster_Damage; this.event_heal = Monster_Heal; settouch(this, Monster_Touch); this.use = Monster_Use; this.solid = SOLID_BBOX; set_movetype(this, MOVETYPE_WALK); - this.spawnshieldtime = time + autocvar_g_monsters_spawnshieldtime; + StatusEffects_apply(STATUSEFFECT_SpawnShield, this, time + autocvar_g_monsters_spawnshieldtime, 0); this.enemy = NULL; this.velocity = '0 0 0'; this.moveto = this.origin; @@ -1381,11 +1408,8 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) this.reset = Monster_Reset; this.netname = mon.netname; 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; this.spawn_time = time; this.gravity = 1; @@ -1400,7 +1424,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) @@ -1409,17 +1433,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); + setsize(this, RoundPerfectVector(mon.m_mins * this.scale), RoundPerfectVector(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);