X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fcommon%2Fmonsters%2Fsv_monsters.qc;h=80cd21b481b41474dcf860d2d42178c95428f228;hb=e2763aba040ce6b50c64165ac93e6927309ddb1d;hp=a89e55b1ce0b6644ddde27b59bacb7b30dbefa03;hpb=c757b2b2307fcc48ebdbf10b590142bd4d42f759;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index a89e55b1c..80cd21b48 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -25,8 +25,8 @@ void monsters_setstatus(entity this) { - this.stat_monsters_total = monsters_total; - this.stat_monsters_killed = monsters_killed; + STAT(MONSTERS_TOTAL, this) = monsters_total; + STAT(MONSTERS_KILLED, this) = monsters_killed; } void monster_dropitem(entity this, entity attacker) @@ -34,7 +34,7 @@ void monster_dropitem(entity this, entity attacker) if(!this.candrop || !this.monster_loot) return; - vector org = this.origin + ((this.mins + this.maxs) * 0.5); + vector org = CENTER_OR_VIEWOFS(this); entity e = new(droppedweapon); // use weapon handling to remove it on touch e.spawnfunc_checked = true; @@ -46,7 +46,7 @@ void monster_dropitem(entity this, entity attacker) if(e && e.monster_loot) { e.noalign = true; - e.monster_loot(e); + StartItem(e, e.monster_loot); e.gravity = 1; set_movetype(e, MOVETYPE_TOSS); e.reset = SUB_Remove; @@ -80,7 +80,7 @@ bool Monster_ValidTarget(entity this, entity targ) if((targ == this) || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen - || (IS_VEHICLE(targ) && !((get_monsterinfo(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless + || (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) || (targ.items & IT_INVISIBILITY) @@ -99,18 +99,17 @@ bool Monster_ValidTarget(entity this, entity targ) return false; } - traceline(this.origin + this.view_ofs, targ.origin, MOVE_NOMONSTERS, this); + vector targ_origin = ((targ.absmin + targ.absmax) * 0.5); + traceline(this.origin + this.view_ofs, targ_origin, MOVE_NOMONSTERS, this); - if(trace_fraction < 1) + if(trace_fraction < 1 && trace_ent != targ) return false; // solid if(autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT)) if(this.enemy != targ) { - float dot; - makevectors (this.angles); - dot = normalize (targ.origin - this.origin) * v_forward; + float dot = normalize (targ.origin - this.origin) * v_forward; if(dot <= autocvar_g_monsters_target_infront_range) { return false; } } @@ -118,25 +117,25 @@ bool Monster_ValidTarget(entity this, entity targ) return true; // this target is valid! } -entity Monster_FindTarget(entity mon) +entity Monster_FindTarget(entity this) { - if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return mon.enemy; } // Handled by a mutator + if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return this.enemy; } // Handled by a mutator entity closest_target = NULL; + vector my_center = CENTER_OR_VIEWOFS(this); // find the closest acceptable target to pass to - FOREACH_ENTITY_RADIUS(mon.origin, mon.target_range, it.monster_attack, + IL_EACH(g_monster_targets, it.monster_attack && vdist(it.origin - this.origin, <, this.target_range), { - if(Monster_ValidTarget(mon, it)) + 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 head_center = CENTER_OR_VIEWOFS(it); - vector ent_center = CENTER_OR_VIEWOFS(mon); + vector targ_center = CENTER_OR_VIEWOFS(it); if(closest_target) { vector closest_target_center = CENTER_OR_VIEWOFS(closest_target); - if(vlen2(ent_center - head_center) < vlen2(ent_center - closest_target_center)) + if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center)) { closest_target = it; } } else { closest_target = it; } @@ -146,43 +145,45 @@ entity Monster_FindTarget(entity mon) return closest_target; } -void monster_setupcolors(entity mon) +void monster_setupcolors(entity this) { - if(IS_PLAYER(mon.realowner)) - mon.colormap = mon.realowner.colormap; - else if(teamplay && mon.team) - mon.colormap = 1024 + (mon.team - 1) * 17; + if(IS_PLAYER(this.realowner)) + this.colormap = this.realowner.colormap; + else if(teamplay && this.team) + this.colormap = 1024 + (this.team - 1) * 17; else { - if(mon.monster_skill <= MONSTER_SKILL_EASY) - mon.colormap = 1029; - else if(mon.monster_skill <= MONSTER_SKILL_MEDIUM) - mon.colormap = 1027; - else if(mon.monster_skill <= MONSTER_SKILL_HARD) - mon.colormap = 1038; - else if(mon.monster_skill <= MONSTER_SKILL_INSANE) - mon.colormap = 1028; - else if(mon.monster_skill <= MONSTER_SKILL_NIGHTMARE) - mon.colormap = 1032; + if(this.monster_skill <= MONSTER_SKILL_EASY) + this.colormap = 1029; + else if(this.monster_skill <= MONSTER_SKILL_MEDIUM) + this.colormap = 1027; + else if(this.monster_skill <= MONSTER_SKILL_HARD) + this.colormap = 1038; + else if(this.monster_skill <= MONSTER_SKILL_INSANE) + this.colormap = 1028; + else if(this.monster_skill <= MONSTER_SKILL_NIGHTMARE) + this.colormap = 1032; else - mon.colormap = 1024; + this.colormap = 1024; } } -void monster_changeteam(entity ent, float newteam) +void monster_changeteam(entity this, int newteam) { if(!teamplay) { return; } - ent.team = newteam; - ent.monster_attack = true; // new team, activate attacking - monster_setupcolors(ent); + this.team = newteam; + if(!this.monster_attack) + IL_PUSH(g_monster_targets, this); + this.monster_attack = true; // new team, activate attacking + monster_setupcolors(this); - if(ent.sprite) + if(this.sprite) { - WaypointSprite_UpdateTeamRadar(ent.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0')); + WaypointSprite_UpdateTeamRadar(this.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0')); - ent.sprite.team = newteam; - ent.sprite.SendFlags |= 1; + this.sprite.team = newteam; + this.sprite.SendFlags |= 1; } } @@ -246,7 +247,7 @@ void Monster_Sound_Precache(string f) { if(tokenize_console(s) != 3) { - LOG_TRACE("Invalid sound info line: ", s); + //LOG_DEBUG("Invalid sound info line: ", s); // probably a comment, no need to spam warnings continue; } PrecacheGlobalSound(strcat(argv(1), " ", argv(2))); @@ -295,13 +296,12 @@ void Monster_Sounds_Clear(entity this) bool Monster_Sounds_Load(entity this, string f, int first) { - float fh; string s; var .string field; - fh = fopen(f, FILE_READ); + float fh = fopen(f, FILE_READ); if(fh < 0) { - LOG_TRACE("Monster sound file not found: ", f); + //LOG_DEBUG("Monster sound file not found: ", f); // no biggie, monster has no sounds, let's not spam it return false; } while((s = fgets(fh))) @@ -330,7 +330,7 @@ void Monster_Sounds_Update(entity this) Monster_Sounds_Load(this, get_monster_model_datafilename(this.model, 0, "sounds"), 0); } -void Monster_Sound(entity this, .string samplefield, float sound_delay, float delaytoo, float chan) +void Monster_Sound(entity this, .string samplefield, float sound_delay, bool delaytoo, float chan) { if(!autocvar_g_monsters_sounds) { return; } @@ -384,7 +384,7 @@ bool Monster_Attack_Leap_Check(entity this, vector vel) this.velocity = vel; tracetoss(this, this); this.velocity = old; - if (trace_ent != this.enemy) + if(trace_ent != this.enemy) return false; return true; @@ -397,12 +397,12 @@ bool Monster_Attack_Leap(entity this, vector anm, void(entity this, entity touch setanim(this, anm, false, true, false); - if(this.animstate_endtime > time && (this.flags & FL_MONSTER)) + if(this.animstate_endtime > time && IS_MONSTER(this)) this.attack_finished_single[0] = this.anim_finished = this.animstate_endtime; else this.attack_finished_single[0] = this.anim_finished = time + animtime; - if(this.flags & FL_MONSTER) + if(IS_MONSTER(this)) this.state = MONSTER_ATTACK_RANGED; settouch(this, touchfunc); this.origin_z += 1; @@ -416,14 +416,14 @@ void Monster_Attack_Check(entity this, entity targ, .entity weaponentity) { int slot = weaponslot(weaponentity); - if((this == NULL || targ == NULL) + if((!this || !targ) || (!this.monster_attackfunc) || (time < this.attack_finished_single[slot]) ) { return; } if(vdist(targ.origin - this.origin, <=, this.attack_range)) { - bool attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ, weaponentity); + int attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ, weaponentity); if(attack_success == 1) Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE); else if(attack_success > 0) @@ -432,7 +432,7 @@ void Monster_Attack_Check(entity this, entity targ, .entity weaponentity) if(vdist(targ.origin - this.origin, >, this.attack_range)) { - float attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ, weaponentity); + int attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ, weaponentity); if(attack_success == 1) Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE); else if(attack_success > 0) @@ -460,7 +460,7 @@ void Monster_UpdateModel(entity this) this.anim_die2 = animfixfps(this, '9 1 0.01', '0 0 0');*/ // then get the real values - Monster mon = get_monsterinfo(this.monsterid); + Monster mon = Monsters_from(this.monsterid); mon.mr_anim(mon, this); } @@ -556,7 +556,8 @@ vector Monster_Move_Target(entity this, entity targ) 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); - if((this.enemy == NULL) + // 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) || (STAT(FROZEN, this.enemy)) || (this.enemy.flags & FL_NOTARGET) @@ -566,7 +567,7 @@ vector Monster_Move_Target(entity this, entity targ) || ((trace_fraction < 1) && (trace_ent != this.enemy))) { this.enemy = NULL; - this.pass_distance = 0; + //this.pass_distance = 0; } if(this.enemy) @@ -657,12 +658,13 @@ 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)); + //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; + vector targpos = to; +#if 0 if(current_height) // make sure we can actually do this arcing path { targpos = (to + ('0 0 1' * current_height)); @@ -678,6 +680,7 @@ void Monster_CalculateVelocity(entity this, vector to, vector from, float turnra } } else { targpos = to; } +#endif //this.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y)); @@ -695,56 +698,14 @@ void Monster_CalculateVelocity(entity this, vector to, vector from, float turnra void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) { // update goal entity if lost - if(this.target2 && this.goalentity.targetname != this.target2) { this.goalentity = find(NULL, targetname, this.target2); } - - entity targ = this.goalentity; + if(this.target2 && this.target2 != "" && this.goalentity.targetname != this.target2) + this.goalentity = find(NULL, targetname, this.target2); - if(STAT(FROZEN, this) == 2) + if(STAT(FROZEN, this)) { - 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); - - if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite) - WaypointSprite_UpdateHealth(this.sprite, this.health); - movelib_brake_simple(this, stpspeed); setanim(this, this.anim_idle, true, false, false); - - this.enemy = NULL; - this.nextthink = time + this.ticrate; - - if(this.revive_progress >= 1) - Unfreeze(this); - - return; - } - 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 ); - - if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite) - WaypointSprite_UpdateHealth(this.sprite, this.health); - - movelib_brake_simple(this, stpspeed); - setanim(this, this.anim_idle, true, false, false); - - this.enemy = NULL; - this.nextthink = time + this.ticrate; - - if(this.health < 1) - { - Unfreeze(this); - this.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'); - } - - else if ( this.revive_progress <= 0 ) - Unfreeze(this); - - return; + return; // no physics while frozen! } if(this.flags & FL_SWIM) @@ -783,8 +744,10 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) } } + entity targ = this.goalentity; + if (MUTATOR_CALLHOOK(MonsterMove, this, runspeed, walkspeed, targ) - || gameover + || game_stopped || this.draggedby != NULL || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || time < game_starttime @@ -802,32 +765,10 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) runspeed = bound(0, M_ARGV(1, float) * MONSTER_SKILLMOD(this), runspeed * 2.5); // limit maxspeed to prevent craziness walkspeed = bound(0, M_ARGV(2, float) * MONSTER_SKILLMOD(this), walkspeed * 2.5); // limit maxspeed to prevent craziness - if(teamplay) - if(autocvar_g_monsters_teams) + if(teamplay && autocvar_g_monsters_teams) if(DIFF_TEAM(this.monster_follow, this)) this.monster_follow = NULL; - if(time >= this.last_enemycheck) - { - if(!this.enemy) - { - 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 - 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); - } - } - - this.last_enemycheck = time + 1; // check for enemies every second - } - if(this.state == MONSTER_ATTACK_RANGED && IS_ONGROUND(this)) { this.state = 0; @@ -855,13 +796,12 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) if(vdist(this.origin - this.moveto, >, 100)) { - float do_run = (this.enemy || this.monster_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)); - if(time > this.pain_finished) // TODO: use anim_finished instead! + if(time > this.pain_finished && time > this.anim_finished) // TODO: use anim_finished instead!? if(!this.state) - if(time > this.anim_finished) if(vdist(this.velocity, >, 10)) setanim(this, ((do_run) ? this.anim_run : this.anim_walk), true, false, false); else @@ -870,14 +810,13 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) else { entity e = this.goalentity; //find(NULL, targetname, this.target2); - if(e.target2) + if(e.target2 && e.target2 != "") this.target2 = e.target2; - else if(e.target) // compatibility + else if(e.target && e.target != "") // compatibility this.target2 = e.target; movelib_brake_simple(this, stpspeed); - if(time > this.anim_finished) - if(time > this.pain_finished) + if(time > this.anim_finished && time > this.pain_finished) if(!this.state) if(vdist(this.velocity, <=, 30)) setanim(this, this.anim_idle, true, false, false); @@ -894,9 +833,6 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) turny = bound(turny * -1, shortangle_f(real_angle.y, this.angles.y), turny); this.angles_y += turny; } - - .entity weaponentity = weaponentities[0]; // TODO? - Monster_Attack_Check(this, this.enemy, weaponentity); } void Monster_Remove(entity this) @@ -1007,7 +943,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) if(IS_PLAYER(attacker)) if(autocvar_g_monsters_score_spawned || !((this.spawnflags & MONSTERFLAG_SPAWNED) || (this.spawnflags & MONSTERFLAG_RESPAWNED))) - PlayerScore_Add(attacker, SP_SCORE, +autocvar_g_monsters_score_kill); + GameRules_scoring_add(attacker, SCORE, +autocvar_g_monsters_score_kill); if(gibbed) { @@ -1015,6 +951,9 @@ void Monster_Dead(entity this, entity attacker, float gibbed) totalspawned -= 1; } + if(!gibbed && this.mdl_dead && this.mdl_dead != "") + _setmodel(this, this.mdl_dead); + this.event_damage = ((gibbed) ? func_null : Monster_Dead_Damage); this.solid = SOLID_CORPSE; this.takedamage = DAMAGE_AIM; @@ -1033,7 +972,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) CSQCModel_UnlinkEntity(this); - Monster mon = get_monsterinfo(this.monsterid); + Monster mon = Monsters_from(this.monsterid); mon.mr_death(mon, this); if(this.candrop && this.weapon) @@ -1060,14 +999,11 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL) return; - vector v; - float take, save; - - v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage); - take = v_x; - save = v_y; + vector v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage); + float take = v.x; + //float save = v.y; - Monster mon = get_monsterinfo(this.monsterid); + Monster mon = Monsters_from(this.monsterid); take = mon.mr_pain(mon, this, take, attacker, deathtype); if(take) @@ -1081,8 +1017,8 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage this.dmg_time = time; - if(sound_allowed(MSG_BROADCAST, attacker) && deathtype != DEATH_DROWN.m_id) - spamsound (this, CH_PAIN, SND(BODYIMPACT1), VOL_BASE, ATTEN_NORM); // FIXME: PLACEHOLDER + if(deathtype != DEATH_DROWN.m_id && deathtype != DEATH_FIRE.m_id && sound_allowed(MSG_BROADCAST, attacker)) + spamsound (this, CH_PAIN, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); // FIXME: PLACEHOLDER this.velocity += force * this.damageforcescale; @@ -1123,7 +1059,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage // don't check for enemies, just keep walking in a straight line void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff) { - if(gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || this.draggedby != NULL || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < this.spawn_time) + if(game_stopped || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || this.draggedby != NULL || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < this.spawn_time) { mspeed = 0; if(time >= this.spawn_time) @@ -1164,8 +1100,7 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff) movelib_move_simple_gravity(this, v_forward, mspeed, 1); - if(time > this.pain_finished) - if(time > this.attack_finished_single[0]) + if(time > this.pain_finished && time > this.attack_finished_single[0]) if(vdist(this.velocity, >, 10)) setanim(this, this.anim_walk, true, false, false); else @@ -1209,22 +1144,92 @@ void Monster_Anim(entity this) */ } +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); + + if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite) + WaypointSprite_UpdateHealth(this.sprite, this.health); + + if(this.revive_progress >= 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 ); + + if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite) + WaypointSprite_UpdateHealth(this.sprite, this.health); + + if(this.health < 1) + { + Unfreeze(this); + this.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'); + } + + else if ( this.revive_progress <= 0 ) + Unfreeze(this); + } + // otherwise, no revival! + + this.enemy = NULL; // TODO: save enemy, and attack when revived? +} + +void Monster_Enemy_Check(entity this) +{ + if(!this.enemy) + { + 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'; + + //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); + } + } +} + void Monster_Think(entity this) { setthink(this, Monster_Think); this.nextthink = time + this.ticrate; - if(this.monster_lifetime) - if(time >= this.monster_lifetime) + 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); return; } - Monster mon = get_monsterinfo(this.monsterid); + if(STAT(FROZEN, this)) + Monster_Frozen_Think(this); + else if(time >= this.last_enemycheck) + { + Monster_Enemy_Check(this); + this.last_enemycheck = time + 1; // check for enemies every second + } + + Monster mon = Monsters_from(this.monsterid); if(mon.mr_think(mon, this)) + { Monster_Move(this, this.speed2, this.speed, this.stopspeed); + .entity weaponentity = weaponentities[0]; // TODO? + Monster_Attack_Check(this, this.enemy, weaponentity); + } + Monster_Anim(this); CSQCMODEL_AUTOUPDATE(this); @@ -1266,7 +1271,11 @@ bool Monster_Spawn_Setup(entity this) Monster_Sounds_Update(this); if(teamplay) + { + if(!this.monster_attack) + IL_PUSH(g_monster_targets, this); this.monster_attack = true; // we can have monster enemies in team games + } Monster_Sound(this, monstersound_spawn, 0, false, CH_VOICE); @@ -1300,7 +1309,13 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) if(!autocvar_g_monsters) { Monster_Remove(this); return false; } if(!(this.spawnflags & MONSTERFLAG_RESPAWNED)) + { IL_PUSH(g_monsters, this); + if(this.mdl && this.mdl != "") + precache_model(this.mdl); + if(this.mdl_dead && this.mdl_dead != "") + precache_model(this.mdl_dead); + } if(check_appear && Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed @@ -1319,7 +1334,11 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) if(!(this.spawnflags & MONSTERFLAG_RESPAWNED)) // don't count re-spawning monsters either monsters_total += 1; - setmodel(this, mon.m_model); + if(this.mdl && this.mdl != "") + _setmodel(this, this.mdl); + else + setmodel(this, mon.m_model); + this.flags = FL_MONSTER; this.classname = "monster"; this.takedamage = DAMAGE_AIM; @@ -1350,7 +1369,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) this.candrop = true; this.view_ofs = '0 0 0.7' * (this.maxs_z * 0.5); this.oldtarget2 = this.target2; - this.pass_distance = 0; + //this.pass_distance = 0; this.deadflag = DEAD_NO; this.spawn_time = time; this.gravity = 1; @@ -1384,7 +1403,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) this.scale *= 1.3; } - setsize(this, mon.mins * this.scale, mon.maxs * this.scale); + setsize(this, mon.m_mins * this.scale, mon.m_maxs * this.scale); this.ticrate = bound(sys_frametime, ((!this.ticrate) ? autocvar_g_monsters_think_delay : this.ticrate), 60);