NSMonster: Explicitly reset animation time before attack acts happen, add class documentation
This commit is contained in:
parent
9d29ad6635
commit
4f35c7b6bf
2 changed files with 120 additions and 31 deletions
|
@ -179,10 +179,10 @@ typedef enum
|
|||
/** Alliance states. */
|
||||
typedef enum
|
||||
{
|
||||
MAL_FRIEND, /* friendly towards the player */
|
||||
MAL_ENEMY, /* unfriendly towards the player */
|
||||
MAL_ALIEN, /* unfriendly towards anyone but themselves */
|
||||
MAL_ROGUE /* no allies, not even amongst themselves */
|
||||
MAL_FRIEND, /**< 1, friendly towards the player */
|
||||
MAL_ENEMY, /**< 2, unfriendly towards the player */
|
||||
MAL_ALIEN, /**< 3, unfriendly towards anyone but themselves */
|
||||
MAL_ROGUE /**< 4, no allies, not even amongst themselves */
|
||||
} allianceState_t;
|
||||
|
||||
/** Movement states */
|
||||
|
@ -199,37 +199,100 @@ it's set to. If any of those checks are successful, we trigger our target
|
|||
under the m_strTriggerTarget attribute. */
|
||||
typedef enum
|
||||
{
|
||||
MTRIG_NONE, /**< nothing */
|
||||
MTRIG_SEEPLAYER_ANGRY, /**< we see an enemy player, that we want to harm */
|
||||
MTRIG_PAIN, /**< taken damage */
|
||||
MTRIG_HALFHEALTH, /**< lost half of our base_health */
|
||||
MTRIG_DEATH, /**< we have died. */
|
||||
MTRIG_SQUADMEMBERDEAD, /**< a squad member died */
|
||||
MTRIG_SQUADLEADERDEAD, /**< the squad leader died */
|
||||
MTRIG_HEARNOISE, /**< we hear some noise around the world. */
|
||||
MTRIG_HEARENEMYPLAYER, /**< we hear a player we are enemies with */
|
||||
MTRIG_HEARWEAPONS, /**< we hear weapons being fired */
|
||||
MTRIG_SEEPLAYER, /**< we see a player, don't have to be angry at him. */
|
||||
MTRIG_SEEPLAYER_RELAXED, /**< we see a player and we're currently attacking anything */
|
||||
MTRIG_NONE, /**< 1, nothing */
|
||||
MTRIG_SEEPLAYER_ANGRY, /**< 2, we see an enemy player, that we want to harm */
|
||||
MTRIG_PAIN, /**< 3, taken damage */
|
||||
MTRIG_HALFHEALTH, /**< 4, lost half of our base_health */
|
||||
MTRIG_DEATH, /**< 5, we have died. */
|
||||
MTRIG_SQUADMEMBERDEAD, /**< 6, a squad member died */
|
||||
MTRIG_SQUADLEADERDEAD, /**< 7, the squad leader died */
|
||||
MTRIG_HEARNOISE, /**< 8, we hear some noise around the world. */
|
||||
MTRIG_HEARENEMYPLAYER, /**< 9, we hear a player we are enemies with */
|
||||
MTRIG_HEARWEAPONS, /**< 10, we hear weapons being fired */
|
||||
MTRIG_SEEPLAYER, /**< 11, we see a player, don't have to be angry at him. */
|
||||
MTRIG_SEEPLAYER_RELAXED, /**< 12, we see a player and we're currently attacking anything */
|
||||
} triggerCondition_t;
|
||||
|
||||
/* FIXME: I'd like to move this into NSMonster, but our current IsFriend()
|
||||
* check is currently only checking on a .takedamage basis. */
|
||||
.int m_iAlliance;
|
||||
|
||||
/** This entity class represents non-player characters.
|
||||
/*! \brief This entity class represents non-player characters. */
|
||||
/*!QUAKED NSMonster (0 0.8 0.8) (-16 -16 0) (16 16 72) WAITTILLSEEN GAG MONSTERCLIP x PRISONER x IGNOREPLAYER WAITFORSCRIPT PREDISASTER FADECORPSE MULTIPLAYER FALLING HORDE
|
||||
# OVERVIEW
|
||||
This entity class represents non-player characters.
|
||||
They have the ability to move around (or stand still) but are all
|
||||
capable of fighting if prompted to.
|
||||
|
||||
There are a few methods that you need to reimplement in order for them
|
||||
to do some basic combat:
|
||||
# KEYS
|
||||
- "targetname" : Name
|
||||
- "netname" : Name used for obituaries and debug info.
|
||||
- "maxs" : Bounding box mins.
|
||||
- "mins" : Bounding box maxs.
|
||||
|
||||
virtual void(void) AttackDraw;
|
||||
virtual void(void) AttackHolster;
|
||||
virtual int(void) AttackMelee;
|
||||
virtual int(void) AttackRanged;
|
||||
## KEYS - TRIGGERS
|
||||
- "TriggerCondition" : See triggerCondition_t for which numerical values to pick.
|
||||
- "TriggerTarget" : Will trigger this entity when TriggerCondition (triggerCondition_t) is met.
|
||||
|
||||
Check their individual descriptions as to how you're supposed to approach them.
|
||||
## KEYS - BEHAVIOUR
|
||||
- "health" : Starting health.
|
||||
- "team" : Alliance. See allianceState_t for which numerical values to pick.
|
||||
- "speed_walk" : Walk speed in units per second.
|
||||
- "speed_run" : Run speed in units per second.
|
||||
- "eye_height" : Height in units at which to place the eyes from the origin. Use the cvar r_showViewCone to debug it.
|
||||
- "snd_sight" : SoundDef to play upon 'alert'.
|
||||
- "snd_idle" : SoundDef to play when the monster is idle.
|
||||
- "idle_min" : Min idle delay in seconds.
|
||||
- "idle_max" : Max idle delay in seconds.
|
||||
- "snd_footstep" : Which soundDef to play when a footstep should occur.
|
||||
- "snd_chatter" : An idle type soundDef to play.
|
||||
- "snd_chatter_combat" : An idle type soundDef, only during combat.
|
||||
- "snd_pain" : SoundDef to play when in pain.
|
||||
- "snd_death" : SoundDef to play when death settles in.
|
||||
- "snd_thud" : SoundDef to play when the monster falls to the ground.
|
||||
|
||||
## KEYS - ATTACK (MELEE)
|
||||
- "def_attack_melee" : Which entityDef to look into for a melee attack. [CONTINUED]
|
||||
- "attack_melee_range" : Range under which melee attacks occur.
|
||||
- "snd_melee_attack" : SoundDef to play when melee attacking.
|
||||
- "snd_melee_attack_hit" : SoundDef to play when a successful melee attack occurs.
|
||||
- "snd_melee_attack_miss" : SoundDef to play when a melee attack misses.
|
||||
|
||||
## KEYS - ATTACK (RANGED)
|
||||
- "def_attack_ranged_1" : EntityDef that contains primary ranged attack info.
|
||||
- "attack_ranged1_range" : Range for the primary ranged attack.
|
||||
- "def_attack_ranged_2" : EntityDef that contains secondary ranged attack info.
|
||||
- "attack_ranged2_range" : Range for the secondary ranged attack.
|
||||
- "snd_ranged_attack" : SoundDef to play upon ranged attack.
|
||||
- "reload_count" : how many ranged attacks until reload. Only affects primary ranged attacks.
|
||||
- "reload_delay" : Time between reloads in seconds. Requires `reload_count` to be set > 0.
|
||||
- "snd_reload" : SoundDef to play when reloading.
|
||||
- "attack_cone" : Cone in which to attack.
|
||||
- "attack_accuracy" : Accuracy (or rather, lack of) multiplier.
|
||||
|
||||
## KEYS - ATTACK (SPECIAL)
|
||||
- "def_attack_special_1" : EntityDef that contains primary special attack info. Intended for projectiles.
|
||||
- "attack_special1_range" : Range for the primary special attack.
|
||||
- "num_projectiles" : The number of primary special projectiles to shoot.
|
||||
- "projectile_spread" : Spread of the projectiles. 0 is none. 1 is the max.
|
||||
- "projectile_delay" : Delay in seconds until a special attack projectile is thrown.
|
||||
|
||||
- "weapon_drawn" : Whether or not the weapon is drawn by default. Either 0 or 1.
|
||||
- "body_on_draw" : Which bodygroup to switch to when the monster has drawn its weapon.
|
||||
- "leap_damage" : Amount of damage appled when the enemy leaps towards you and hits.
|
||||
|
||||
# SPAWNFLAGS
|
||||
- WAITTILLSEEN (1) - Play scripted sequence only once the monster gets seen by a player.
|
||||
- GAG (2) - Won't speak.
|
||||
- MONSTERCLIP (4) - Interacts with monsterclips?
|
||||
- PRISONER (16) - Never used.
|
||||
- IGNOREPLAYER (64) - Ignores the player. Like 'notarget'.
|
||||
- WAITFORSCRIPT (128) - Does nothing, until a scripted sequence runs on the monster. Then becomes alive.
|
||||
- PREDISASTER (256) - Special flag used in Half-Life.
|
||||
- FADECORPSE (512) - Corpse will disappear on its own.
|
||||
- MULTIPLAYER (1024) - Available in multiplayer.
|
||||
- FALLING (2048) - Will not drop to the floor upon level spawn - but fall when in-game.
|
||||
- HORDE (4096) - Never used.
|
||||
*/
|
||||
class NSMonster:NSNavAI
|
||||
{
|
||||
|
@ -348,6 +411,7 @@ public:
|
|||
virtual void AnimationUpdate(void);
|
||||
/** Returns if we're currently in a forced animation sequence. */
|
||||
nonvirtual bool InAnimation(void);
|
||||
nonvirtual void AnimReset(void);
|
||||
|
||||
/* states */
|
||||
/** Called whenever the state of this NSMonster changes. */
|
||||
|
|
|
@ -492,7 +492,13 @@ NSMonster::AnimPlay(float seq)
|
|||
SetSendFlags(MONFL_CHANGED_FRAME);
|
||||
|
||||
SetFrame(seq);
|
||||
m_flAnimTime = time + frameduration(modelindex, frame);
|
||||
m_flAnimTime = time + frameduration(modelindex, seq);
|
||||
}
|
||||
|
||||
void
|
||||
NSMonster::AnimReset(void)
|
||||
{
|
||||
frame1time = 0.0f;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -921,6 +927,8 @@ NSMonster::AttackMelee(void)
|
|||
ScheduleThink(AttackMelee_AttackFlail, _m_flMeleeDelay + meleeWait);
|
||||
}
|
||||
|
||||
AnimReset();
|
||||
|
||||
if (random() < 0.5 || actMelee2 == -1)
|
||||
AnimPlay(actMelee1);
|
||||
else
|
||||
|
@ -944,6 +952,7 @@ NSMonster::AttackRanged(void)
|
|||
}
|
||||
static void AttackRanged_RangedSpecial(void)
|
||||
{
|
||||
NSMonster_Log("AttackRanged_RangedSpecial: %S", m_defRanged2);
|
||||
NSProjectile_SpawnDef(m_defRanged2, this);
|
||||
}
|
||||
|
||||
|
@ -961,6 +970,7 @@ NSMonster::AttackRanged(void)
|
|||
if (_m_flReloadTracker > m_flReloadCount) {
|
||||
throwAnyway = true;
|
||||
_m_bShouldThrow = true;
|
||||
NSMonster_Log("throwAnyway: true!");
|
||||
}
|
||||
|
||||
/* special always first if possible */
|
||||
|
@ -987,21 +997,29 @@ NSMonster::AttackRanged(void)
|
|||
StartSoundDef(m_sndReload, CHAN_WEAPON, true);
|
||||
_m_bShouldThrow = false;
|
||||
|
||||
if (m_flReloadDelay)
|
||||
if (m_flReloadDelay) {
|
||||
m_flAttackThink = time + m_flReloadDelay;
|
||||
else
|
||||
m_flAttackThink = time + frameduration(modelindex, actReload);
|
||||
NSMonster_Log("Reloading, delay %f seconds", m_flReloadDelay);
|
||||
} else {
|
||||
float actDuration = frameduration(modelindex, actReload);
|
||||
m_flAttackThink = time + actDuration;
|
||||
NSMonster_Log("Reloading, act delays it by %f seconds", actDuration);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
AnimReset();
|
||||
AnimPlay(actRanged);
|
||||
|
||||
/* if we have no spawnclass, it must be a hitscan weapon */
|
||||
if (m_defRanged1)
|
||||
if (EntityDef_HasSpawnClass(m_defRanged1)) {
|
||||
NSProjectile_SpawnDef(m_defRanged1, this);
|
||||
NSMonster_Log("Firing ranged def %S", m_defRanged1);
|
||||
} else {
|
||||
TraceAttack_FireBullets(1, GetEyePos(), rangedDmg, [0.01,0.01] * m_flAttackAccuracy, 0);
|
||||
NSMonster_Log("Firing traceline def with %d", rangedDmg);
|
||||
}
|
||||
|
||||
StartSoundDef(m_sndRangedAttack, CHAN_WEAPON, true);
|
||||
|
@ -1014,19 +1032,25 @@ NSMonster::AttackRanged(void)
|
|||
burstTime = burstDelay;
|
||||
}
|
||||
|
||||
if (rangedDly)
|
||||
if (rangedDly) {
|
||||
m_flAttackThink = time + rangedDly + burstTime;
|
||||
else
|
||||
m_flAttackThink = time + frameduration(modelindex, actRanged) + burstTime;
|
||||
NSMonster_Log("Primary ranged attack, delay %f seconds (burst %d)", rangedDly, burstTime);
|
||||
} else {
|
||||
float actAttackTime = frameduration(modelindex, actRanged);
|
||||
m_flAttackThink = time + actAttackTime + burstTime;
|
||||
NSMonster_Log("Primary ranged attack, act delays it by %f seconds (burst %f)", actAttackTime, burstTime);
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else if (throwAnyway == false && inRanged2Range && trace_ent == m_eEnemy) {
|
||||
float actRangedSpecial = FramegroupForAct(ACT_RANGE_ATTACK2);
|
||||
AnimReset();
|
||||
AnimPlay(actRangedSpecial);
|
||||
ScheduleThink(AttackRanged_RangedSpecial, 0.0f);
|
||||
m_flAttackThink = time + frameduration(modelindex, actRangedSpecial);
|
||||
return 1;
|
||||
} else if (inSpecial1Range) {
|
||||
AnimReset();
|
||||
AnimPlay(FramegroupForAct(ACT_SPECIAL_ATTACK1));
|
||||
ScheduleThink(AttackRanged_Throw, m_flProjectileDelay);
|
||||
|
||||
|
@ -1037,6 +1061,7 @@ NSMonster::AttackRanged(void)
|
|||
|
||||
return 1;
|
||||
} else if (inSpecial2Range) {
|
||||
AnimReset();
|
||||
AnimPlay(FramegroupForAct(ACT_SPECIAL_ATTACK2));
|
||||
ScheduleThink(AttackRanged_Throw, m_flProjectileDelay);
|
||||
|
||||
|
|
Loading…
Reference in a new issue