const int MON_ATTACK_TOUCH = BIT(1);
const int MON_ATTACK_PROJECTILE = BIT(2);
+const int MON_DAMAGE_NONE = BIT(0);
+const int MON_DAMAGE_NOMONSTERS = BIT(1);
+
const int MON_ATTACKTYPE_GRENADE = 0;
const int MON_ATTACKTYPE_FIREBALL = 1;
// here come the fields
.bool mon_jumpoff;
+.int mon_alwaysturn;
.float mon_touchdelay, mon_touchangle;
.float touch_timer; // reused field
.float mon_jumpdelay, mon_jumpheight;
.float jump_delay;
.float shot_dmg, shot_radius;
.float mon_proj_speed, mon_proj_speed_up;
+.float mon_attack_delay;
-.int mon_movetype, mon_attacks, mon_attacktype;
+.int mon_movetype, mon_attacks, mon_attacktype, mon_damageflags;
void M_CustomMonster_Touch(entity this, entity toucher)
{
- if((this.mon_movetype & MON_MOVE_TOUCH) && time < this.touch_timer && vdist(this.velocity, <, this.speed))
+ if((this.mon_movetype & MON_MOVE_TOUCH) && vdist(this.velocity, <, this.speed))
{
- fixedmakevectors(toucher.angles);
- this.velocity = v_forward * this.speed2;
+ if(time < this.touch_timer)
+ return; // just return
+ if(IS_PLAYER(toucher))
+ {
+ fixedmakevectors(toucher.angles);
+ this.angles = toucher.angles; // to make sidescrolling movement work
+ this.velocity = v_forward * this.speed2;
+ this.attack_finished_single[0] = time + this.mon_attack_delay; // don't immediately damage the player that pushed it
+ }
this.touch_timer = time + this.mon_touchdelay;
return;
}
if(vdir.z <= this.mon_touchangle)
{
Damage(toucher, this, this, this.dmg, DEATH_MONSTER_ZOMBIE_MELEE.m_id, toucher.origin, '0 0 0');
- this.attack_finished_single[0] = time + this.delay;
+ this.attack_finished_single[0] = time + this.mon_attack_delay;
}
}
case MONSTER_ATTACK_MELEE:
{
if(actor.mon_attacks & MON_ATTACK_MELEE)
- return Monster_Attack_Melee(actor, actor.enemy, actor.dmg, actor.anim_melee, actor.attack_range, actor.delay, DEATH_MONSTER_ZOMBIE_MELEE.m_id, actor.wait); // just fall back to zombie melee deathtype
+ return Monster_Attack_Melee(actor, actor.enemy, actor.dmg, actor.anim_melee, actor.attack_range, actor.mon_attack_delay, DEATH_MONSTER_ZOMBIE_MELEE.m_id, actor.wait); // just fall back to zombie melee deathtype
return false;
}
case MONSTER_ATTACK_RANGED:
makevectors(actor.angles);
W_SetupProjVelocity_Explicit(proj, v_forward, v_up, actor.mon_proj_speed, actor.mon_proj_speed_up, 0, 0, false);
UpdateCSQCProjectile(proj);
- actor.attack_finished_single[0] = time + (actor.delay * random());
+ actor.attack_finished_single[0] = time + (actor.mon_attack_delay * random());
return true;
}
return false;
}
if(actor.mon_movetype & MON_MOVE_2D)
{
- Monster_Move_2D(actor, actor.speed, actor.mon_jumpoff);
+ if((actor.mon_movetype & MON_MOVE_TOUCH) && !vec2(actor.velocity))
+ return false; // wait until pushed
+ Monster_Move_2D(actor, actor.speed, actor.mon_jumpoff, actor.mon_alwaysturn);
M_CustomMonster_TargetEnemey(actor); // not called by regular code in this case
return false;
}
METHOD(CustomMonster, mr_pain, float(CustomMonster this, entity actor, float damage_take, entity attacker, float deathtype))
{
TC(CustomMonster, this);
+ if((actor.mon_damageflags & MON_DAMAGE_NONE))
+ return 0;
+ if((actor.mon_damageflags & MON_DAMAGE_NOMONSTERS) && IS_MONSTER(attacker))
+ return 0;
+
setanim(actor, actor.anim_pain1, true, true, false);
actor.pain_finished = actor.animstate_endtime;
+ actor.velocity = '0 0 0'; // reset velocity
return damage_take;
}
bool autocvar_g_monster_spawner_copyfields = false; // just incase this gets too nasty
+.bool use_trigger_origin;
+
void spawner_use(entity this, entity actor, entity trigger)
{
int moncount = 0;
e.monster_skill = this.monster_skill;
}
- e = spawnmonster(e, this.spawnmob, 0, this, this, this.origin, false, true, this.monster_moveflags);
+ vector org = this.origin;
+ if(this.use_trigger_origin)
+ {
+ org = trigger.origin; // TODO: doesn't support brushes
+ e.angles = trigger.angles;
+ }
+ e = spawnmonster(e, this.spawnmob, 0, this, this, org, false, true, this.monster_moveflags);
}
spawnfunc(monster_spawner)
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;
+ if(this.damageforcescale > 0)
+ this.velocity += force * this.damageforcescale;
if(deathtype != DEATH_DROWN.m_id && take)
{
}
// don't check for enemies, just keep walking in a straight line
-void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
+void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff, int always_turn)
{
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)
{
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(!(always_turn & 1) && trace_ent && IS_PLAYER(trace_ent) && !(trace_ent.items & ITEM_Strength.m_itemid))
reverse = false;
- if(trace_ent && IS_MONSTER(trace_ent))
+ if(!(always_turn & 2) && trace_ent && IS_MONSTER(trace_ent))
reverse = true;
// TODO: fix this... tracing is broken if the floor is thin