X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fcommon%2Fmonsters%2Fmonster%2Fmage.qc;h=f307b8aa1da4d0972bc6c0ca38fe60db39b641c4;hb=323105d51f8eb6dea47c063965be5e34ad57f987;hp=efdd836d21e8af7bedd7de2ebffe8555031e173a;hpb=139a594d29f65508e5d6001e90d4ee4fad82ca66;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index efdd836d2..f307b8aa1 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -12,6 +12,7 @@ METHOD(MageSpike, wr_think, void(MageSpike thiswep, entity actor, .entity weapon if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) { if (!actor.target_range) actor.target_range = autocvar_g_monsters_target_range; actor.enemy = Monster_FindTarget(actor); + monster_makevectors(actor, actor.enemy); W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_MageSpike_FIRE, CH_WEAPON_B, 0, DEATH_MONSTER_MAGE.m_id); if (!IS_PLAYER(actor)) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin); M_Mage_Attack_Spike(actor, w_shotdir); @@ -46,16 +47,22 @@ float autocvar_g_monster_mage_attack_spike_radius; float autocvar_g_monster_mage_attack_spike_delay; float autocvar_g_monster_mage_attack_spike_accel; float autocvar_g_monster_mage_attack_spike_decel; +float autocvar_g_monster_mage_attack_spike_chance = 0.45; float autocvar_g_monster_mage_attack_spike_turnrate; float autocvar_g_monster_mage_attack_spike_speed_max; float autocvar_g_monster_mage_attack_spike_smart; float autocvar_g_monster_mage_attack_spike_smart_trace_min; float autocvar_g_monster_mage_attack_spike_smart_trace_max; float autocvar_g_monster_mage_attack_spike_smart_mindist; +float autocvar_g_monster_mage_attack_push_chance = 0.7; float autocvar_g_monster_mage_attack_push_damage; float autocvar_g_monster_mage_attack_push_radius; float autocvar_g_monster_mage_attack_push_delay; float autocvar_g_monster_mage_attack_push_force; +float autocvar_g_monster_mage_attack_teleport_chance = 0.2; +float autocvar_g_monster_mage_attack_teleport_delay = 2; +float autocvar_g_monster_mage_attack_teleport_random = 0.4; +float autocvar_g_monster_mage_attack_teleport_random_range = 1200; float autocvar_g_monster_mage_heal_self; float autocvar_g_monster_mage_heal_allies; float autocvar_g_monster_mage_heal_minhealth; @@ -82,7 +89,6 @@ void M_Mage_Defend_Shield(entity this); .entity mage_spike; .float mage_shield_delay; -.float mage_shield_time; bool M_Mage_Defend_Heal_Check(entity this, entity targ) { @@ -96,7 +102,7 @@ bool M_Mage_Defend_Heal_Check(entity this, entity targ) return false; if(!IS_PLAYER(targ)) return (IS_MONSTER(targ) && GetResource(targ, RES_HEALTH) < targ.max_health); - if(targ.items & ITEM_Shield.m_itemid) + if(StatusEffects_active(STATUSEFFECT_Shield, targ)) return false; switch(this.skin) @@ -200,7 +206,7 @@ void M_Mage_Attack_Spike(entity this, vector dir) { makevectors(this.angles); - entity missile = spawn(); + entity missile = new(M_Mage_Attack_Spike); missile.owner = missile.realowner = this; setthink(missile, M_Mage_Attack_Spike_Think); missile.ltime = time + 7; @@ -224,7 +230,7 @@ void M_Mage_Attack_Spike(entity this, vector dir) void M_Mage_Defend_Heal(entity this) { - float washealed = false; + bool washealed = false; FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_mage_heal_range, M_Mage_Defend_Heal_Check(this, it), { @@ -242,11 +248,11 @@ void M_Mage_Defend_Heal(entity this) } case 1: { - if(GetResource(this, RES_CELLS)) GiveResourceWithLimit(it, RES_CELLS, 1, g_pickup_cells_max); - if(GetResource(this, RES_PLASMA)) GiveResourceWithLimit(it, RES_PLASMA, 1, g_pickup_plasma_max); - if(GetResource(this, RES_ROCKETS)) GiveResourceWithLimit(it, RES_ROCKETS, 1, g_pickup_rockets_max); - if(GetResource(this, RES_SHELLS)) GiveResourceWithLimit(it, RES_SHELLS, 2, g_pickup_shells_max); - if(GetResource(this, RES_BULLETS)) GiveResourceWithLimit(it, RES_BULLETS, 5, g_pickup_nails_max); + if(GetResource(it, RES_CELLS)) GiveResourceWithLimit(it, RES_CELLS, 1, g_pickup_cells_max); + if(GetResource(it, RES_PLASMA)) GiveResourceWithLimit(it, RES_PLASMA, 1, g_pickup_plasma_max); + if(GetResource(it, RES_ROCKETS)) GiveResourceWithLimit(it, RES_ROCKETS, 1, g_pickup_rockets_max); + if(GetResource(it, RES_SHELLS)) GiveResourceWithLimit(it, RES_SHELLS, 2, g_pickup_shells_max); + if(GetResource(it, RES_BULLETS)) GiveResourceWithLimit(it, RES_BULLETS, 5, g_pickup_nails_max); // TODO: fuel? fx = EFFECT_AMMO_REGEN; break; @@ -278,8 +284,9 @@ void M_Mage_Defend_Heal(entity this) if(washealed) { - setanim(this, this.anim_shoot, true, true, true); + setanim(this, this.anim_melee, true, true, true); this.attack_finished_single[0] = time + (autocvar_g_monster_mage_heal_delay); + this.state = MONSTER_ATTACK_MELEE; this.anim_finished = time + 1.5; } } @@ -291,8 +298,10 @@ void M_Mage_Attack_Push(entity this) NULL, NULL, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE.m_id, DMG_NOWEP, this.enemy); Send_Effect(EFFECT_TE_EXPLOSION, this.origin, '0 0 0', 1); - setanim(this, this.anim_shoot, true, true, true); + setanim(this, this.anim_duckjump, true, true, true); this.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_push_delay); + this.anim_finished = time + 1; + this.state = MONSTER_ATTACK_MELEE; // prevent moving while firing spike } void M_Mage_Attack_Teleport(entity this, entity targ) @@ -300,13 +309,33 @@ void M_Mage_Attack_Teleport(entity this, entity targ) if(!targ) return; if(vdist(targ.origin - this.origin, >, 1500)) return; + if(autocvar_g_monster_mage_attack_teleport_random && random() <= autocvar_g_monster_mage_attack_teleport_random) + { + vector oldpos = this.origin; + vector extrasize = '1 1 1' * autocvar_g_monster_mage_attack_teleport_random_range; + if(MoveToRandomLocationWithinBounds(this, this.absmin - extrasize, this.absmax + extrasize, + DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, + Q3SURFACEFLAG_SKY, 10, 64, 256, true)) + { + vector a = vectoangles(targ.origin - this.origin); + this.angles = '0 1 0' * a.y; + this.fixangle = true; + Send_Effect(EFFECT_SPAWN_NEUTRAL, oldpos, '0 0 0', 1); + Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); + this.attack_finished_single[0] = time + autocvar_g_monster_mage_attack_teleport_delay; + return; + } + } + + if(!IS_ONGROUND(targ)) return; + makevectors(targ.angles); - tracebox(targ.origin + ((v_forward * -1) * 200), this.mins, this.maxs, this.origin, MOVE_NOMONSTERS, this); + tracebox(CENTER_OR_VIEWOFS(targ), this.mins, this.maxs, CENTER_OR_VIEWOFS(targ) + ((v_forward * -1) * 200), MOVE_NOMONSTERS, this); if(trace_fraction < 1) return; - vector newpos = targ.origin + ((v_forward * -1) * 200); + vector newpos = trace_endpos; Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); Send_Effect(EFFECT_SPAWN_NEUTRAL, newpos, '0 0 0', 1); @@ -320,23 +349,16 @@ void M_Mage_Attack_Teleport(entity this, entity targ) this.fixangle = true; this.velocity *= 0.5; - this.attack_finished_single[0] = time + 0.2; -} - -void M_Mage_Defend_Shield_Remove(entity this) -{ - this.effects &= ~(EF_ADDITIVE | EF_BLUE); - SetResourceExplicit(this, RES_ARMOR, autocvar_g_monsters_armor_blockpercent); + this.attack_finished_single[0] = time + autocvar_g_monster_mage_attack_teleport_delay; } void M_Mage_Defend_Shield(entity this) { - this.effects |= (EF_ADDITIVE | EF_BLUE); + StatusEffects_apply(STATUSEFFECT_Shield, this, time + autocvar_g_monster_mage_shield_time, 0); this.mage_shield_delay = time + (autocvar_g_monster_mage_shield_delay); SetResourceExplicit(this, RES_ARMOR, autocvar_g_monster_mage_shield_blockpercent); - this.mage_shield_time = time + (autocvar_g_monster_mage_shield_time); setanim(this, this.anim_shoot, true, true, true); - this.attack_finished_single[0] = time + 1; + this.attack_finished_single[0] = time + 1; // give just a short cooldown on attacking this.anim_finished = time + 1; } @@ -346,7 +368,7 @@ bool M_Mage_Attack(int attack_type, entity actor, entity targ, .entity weaponent { case MONSTER_ATTACK_MELEE: { - if(random() <= 0.7) + if(random() <= autocvar_g_monster_mage_attack_push_chance) { Weapon wep = WEP_MAGE_SPIKE; @@ -358,36 +380,32 @@ bool M_Mage_Attack(int attack_type, entity actor, entity targ, .entity weaponent } case MONSTER_ATTACK_RANGED: { - if(!actor.mage_spike) + if(random() <= autocvar_g_monster_mage_attack_teleport_chance) { - if(random() <= 0.4) - { - OffhandWeapon off = OFFHAND_MAGE_TELEPORT; - off.offhand_think(off, actor, true); - return true; - } - else - { - setanim(actor, actor.anim_shoot, true, true, true); - actor.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_spike_delay); - actor.anim_finished = time + 1; - Weapon wep = WEP_MAGE_SPIKE; - wep.wr_think(wep, actor, weaponentity, 1); - return true; - } + OffhandWeapon off = OFFHAND_MAGE_TELEPORT; + actor.OffhandMageTeleport_key_pressed = 0; + off.offhand_think(off, actor, 1); + return true; } - - if(actor.mage_spike) + else if(!actor.mage_spike && random() <= autocvar_g_monster_mage_attack_spike_chance) + { + setanim(actor, actor.anim_shoot, true, true, true); + actor.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_spike_delay); + actor.anim_finished = time + 1; + actor.state = MONSTER_ATTACK_MELEE; // prevent moving while firing spike + Weapon wep = WEP_MAGE_SPIKE; + wep.wr_think(wep, actor, weaponentity, 1); return true; - else - return false; + } + + return false; } } return false; } -spawnfunc(monster_mage) { Monster_Spawn(this, true, MON_MAGE.monsterid); } +spawnfunc(monster_mage) { Monster_Spawn(this, true, MON_MAGE); } #endif // SVQC @@ -425,13 +443,8 @@ METHOD(Mage, mr_think, bool(Mage thismon, entity actor)) if(random() < 0.5) M_Mage_Defend_Heal(actor); - if(time >= actor.mage_shield_time && GetResource(actor, RES_ARMOR)) - M_Mage_Defend_Shield_Remove(actor); - - if(actor.enemy) - if(GetResource(actor, RES_HEALTH) < actor.max_health) - if(time >= actor.mage_shield_delay) - if(random() < 0.5) + if(actor.enemy && time >= actor.mage_shield_delay && random() < 0.5) + if(GetResource(actor, RES_HEALTH) < actor.max_health && !StatusEffects_active(STATUSEFFECT_Shield, actor)) M_Mage_Defend_Shield(actor); return true; @@ -446,7 +459,7 @@ METHOD(Mage, mr_pain, float(Mage this, entity actor, float damage_take, entity a METHOD(Mage, mr_death, bool(Mage this, entity actor)) { TC(Mage, this); - setanim(actor, actor.anim_die1, false, true, true); + setanim(actor, ((random() > 0.5) ? actor.anim_die2 : actor.anim_die1), false, true, true); return true; } @@ -456,12 +469,22 @@ METHOD(Mage, mr_anim, bool(Mage this, entity actor)) { TC(Mage, this); vector none = '0 0 0'; - actor.anim_die1 = animfixfps(actor, '4 1 0.5', none); // 2 seconds - actor.anim_walk = animfixfps(actor, '1 1 1', none); actor.anim_idle = animfixfps(actor, '0 1 1', none); - actor.anim_pain1 = animfixfps(actor, '3 1 2', none); // 0.5 seconds + actor.anim_walk = animfixfps(actor, '1 1 1', none); + actor.anim_run = animfixfps(actor, '1 1 1', none); actor.anim_shoot = animfixfps(actor, '2 1 5', none); // analyze models and set framerate - actor.anim_run = animfixfps(actor, '5 1 1', none); + actor.anim_duckjump = animfixfps(actor, '4 1 5', none); // analyze models and set framerate + actor.anim_melee = animfixfps(actor, '5 1 5', none); // analyze models and set framerate + //actor.anim_fire1 = animfixfps(actor, '3 1 5', none); // analyze models and set framerate + //actor.anim_fire2 = animfixfps(actor, '4 1 5', none); // analyze models and set framerate + //actor.anim_fire3 = animfixfps(actor, '5 1 5', none); // analyze models and set framerate + actor.anim_pain1 = animfixfps(actor, '6 1 2', none); // 0.5 seconds + actor.anim_pain2 = animfixfps(actor, '7 1 2', none); // 0.5 seconds + //actor.anim_pain3 = animfixfps(actor, '8 1 2', none); // 0.5 seconds + actor.anim_die1 = animfixfps(actor, '9 1 0.5', none); // 2 seconds + actor.anim_die2 = animfixfps(actor, '10 1 0.5', none); // 2 seconds + //actor.anim_dead1 = animfixfps(actor, '11 1 0.5', none); // 2 seconds + //actor.anim_dead2 = animfixfps(actor, '12 1 0.5', none); // 2 seconds return true; } #endif