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;
.entity mage_spike;
.float mage_shield_delay;
-.float mage_shield_time;
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)
{
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;
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),
{
}
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;
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;
}
}
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)
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);
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;
}
{
case MONSTER_ATTACK_MELEE:
{
- if(random() <= 0.7)
+ if(random() <= autocvar_g_monster_mage_attack_push_chance)
{
Weapon wep = WEP_MAGE_SPIKE;
}
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;
}
}
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;
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;
}
{
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