From 31774ce3f123027598a667aa9fa1496ac73aed37 Mon Sep 17 00:00:00 2001 From: Marco Cawthorne Date: Tue, 20 Jun 2023 21:19:00 -0700 Subject: [PATCH] EntityDef work on NSMonster, NSTalkMonster, NSProjectile etc. --- src/server/entityDef.qc | 33 +- src/shared/NSEntity.h | 3 + src/shared/NSEntity.qc | 20 ++ src/shared/NSMonster.h | 73 ++++ src/shared/NSMonster.qc | 546 ++++++++++++++++++++++++++++-- src/shared/NSProjectile.h | 5 + src/shared/NSProjectile.qc | 29 ++ src/shared/NSRenderableEntity.qc | 16 +- src/shared/NSSquadMonster.h | 6 + src/shared/NSSquadMonster.qc | 35 ++ src/shared/NSSurfacePropEntity.qc | 4 + src/shared/NSTalkMonster.h | 5 +- src/shared/NSTalkMonster.qc | 125 ++++++- 13 files changed, 855 insertions(+), 45 deletions(-) diff --git a/src/server/entityDef.qc b/src/server/entityDef.qc index 17c0a31c..3e68fa0f 100644 --- a/src/server/entityDef.qc +++ b/src/server/entityDef.qc @@ -83,7 +83,8 @@ enum EDEFTWEAK_EQ = 0, EDEFTWEAK_LT, EDEFTWEAK_GT, - EDEFTWEAK_NOT + EDEFTWEAK_NOT, + EDEFTWEAK_CONTAINS }; typedef struct @@ -203,16 +204,19 @@ EntityDef_ReadFile(string filePath) switch (argv(i+2)) { case "equals": currentDef.tweakDefs = strcat(currentDef.tweakDefs, argv(i+1), " 0 ", argv(i+3), ";"); - break; + break; case "less-than": currentDef.tweakDefs = strcat(currentDef.tweakDefs, argv(i+1), " 1 ", argv(i+3), ";"); - break; + break; case "greater-than": currentDef.tweakDefs = strcat(currentDef.tweakDefs, argv(i+1), " 2 ", argv(i+3), ";"); - break; + break; case "is-not": currentDef.tweakDefs = strcat(currentDef.tweakDefs, argv(i+1), " 3 ", argv(i+3), ";"); break; + case "contains": + currentDef.tweakDefs = strcat(currentDef.tweakDefs, argv(i+1), " 4 ", argv(i+3), ";"); + break; } inEvent = false; @@ -264,7 +268,7 @@ EntityDef_Init(void) } } -#if 1 +#if 0 for (int i = 0i; i < g_entDefCount; i++) { int numKeys = tokenize_console(g_entDefTable[i].spawnData); print(sprintf("edef %i: %S\n", i, g_entDefTable[i].entClass)); @@ -323,6 +327,12 @@ EntityDef_CheckCondition(int id, string keyWord, float tweakCondition, string ke case EDEFTWEAK_NOT: if (key == keyWord && value != keyValue) return true; + case EDEFTWEAK_CONTAINS: + tmp1 = stof(keyValue); + tmp2 = stof(value); + + if (key == keyWord && tmp2 & tmp1) + return true; break; } } @@ -456,6 +466,19 @@ EntityDef_Precaches(int index) spawnWords = tokenize_console(g_entDefTable[index].spawnData); } } + + /* handle soundDef events */ + spawnWords = tokenize(g_entDefTable[index].eventList); + for (int i = 0; i < spawnWords; i+=3) { + int testCode = stoi(argv(i+0)); + string testInput = argv(i+1); + string testData = argv(i+2); + + if (testInput == "StartSoundDef") { + Sound_Precache(testData); + tokenize(g_entDefTable[index].eventList); + } + } } NSEntity diff --git a/src/shared/NSEntity.h b/src/shared/NSEntity.h index a1505047..665b9042 100644 --- a/src/shared/NSEntity.h +++ b/src/shared/NSEntity.h @@ -358,6 +358,9 @@ public: /** Returns either true or false depending on if this entity is facing the entity in question. */ nonvirtual bool IsFacing(entity); + /** Returns either true or false depending on if this entity is facing a position in question. */ + nonvirtual bool IsFacingPosition(vector); + /** Returns the time that's passed since the entity has been spawned. */ nonvirtual float GetSpawnAge(void); diff --git a/src/shared/NSEntity.qc b/src/shared/NSEntity.qc index f715a25d..f35830d2 100644 --- a/src/shared/NSEntity.qc +++ b/src/shared/NSEntity.qc @@ -748,6 +748,19 @@ void NSEntity::Input( entity eAct, string strInput, string strData ) { if ( PlayerUse ) PlayerUse(); break; + case "SpawnDef": + break; + case "SpawnProjectileDef": + if (EntityDef_HasSpawnClass(strData)) { + NSProjectile_SpawnDefAttachment(strData, this, 0); + } else { + float rangedDmg = Skill_GetDefValue(EntityDef_GetKeyValue(strData, "damage")); + TraceAttack_FireBullets(1, origin + view_ofs, rangedDmg, [0.01,0.01], 0); + } + break; + case "StartSoundDef": + StartSoundDef(strData, CHAN_VOICE, true); + break; default: NSTrigger::Input( eAct, strInput, strData ); } @@ -902,6 +915,13 @@ bool NSEntity::IsFacing(entity target) return ((vecDiff * v_forward) > 0 ) ? true : false; } +bool NSEntity::IsFacingPosition(vector targetPos) +{ + vector vecDiff = normalize(targetPos - origin); + makevectors(angles); + return ((vecDiff * v_forward) > 0 ) ? true : false; +} + float NSEntity::GetSpawnAge(void) { diff --git a/src/shared/NSMonster.h b/src/shared/NSMonster.h index c13c5996..39352b3f 100644 --- a/src/shared/NSMonster.h +++ b/src/shared/NSMonster.h @@ -50,6 +50,7 @@ typedef enumflags MONFL_CHANGED_RENDERCOLOR, MONFL_CHANGED_RENDERAMT, MONFL_CHANGED_RENDERMODE, + MONFL_CHANGED_HEADYAW } nsmonster_changed_t; /** List of supported ACT types. @@ -373,11 +374,19 @@ public: private: + vector v_angle_net; + #ifdef CLIENT nonvirtual void _RenderDebugViewCone(); #endif + PREDICTED_FLOAT(m_flHeadYaw) + PREDICTED_FLOAT_N(frame1time) + PREDICTED_FLOAT_N(subblendfrac) + PREDICTED_FLOAT_N(bonecontrol1) + #ifdef SERVER + entity m_eLookAt; entity m_ssLast; vector oldnet_velocity; float m_flPitch; @@ -422,6 +431,70 @@ private: /* caching variables, don't save these */ float m_actIdle; bool m_bTurning; + float m_flIdleNext; + float _m_flMeleeAttempts; + float _m_flMeleeDelay; + float _m_flBurstCount; + bool _m_bShouldThrow; + + /* save these please */ + float _m_flReloadTracker; + bool m_bWeaponDrawn; + + /* entityDef related */ + float m_flEyeHeight; + string m_sndSight; + string m_sndIdle; + float m_flIdleMin; + float m_flIdleMax; + string m_sndFootstep; + string m_sndChatter; + string m_sndChatterCombat; + string m_sndPain; + + string m_sndMeleeAttack; + string m_sndMeleeAttackHit; + string m_sndMeleeAttackMiss; + + string m_sndDeath; + string m_sndThud; + + + /* attack definitions, if defined will fire projectiles */ + string m_defSpecial1; + float m_flSpecial1Range; + string m_defSpecial2; + float m_flSpecial2Range; + string m_defRanged1; + float m_flRanged1Range; + string m_defRanged2; + float m_flRanged2Range; + + /* ranged1 only */ + int m_iNumProjectiles; + float m_flProjectileDelay; + float m_flProjectileSpread; + + /* general */ + float m_flAttackCone; + float m_flAttackAccuracy; + + /* melee attack */ + string m_defMelee; + float m_flMeleeRange; + + string m_sndRangedAttack; + float m_flReloadCount; + float m_flReloadDelay; + string m_sndReload; + + string m_sndRangedAttack2; + + bool m_bWeaponStartsDrawn; + float m_flBodyOnDraw; + + float m_flWalkSpeed; + float m_flRunSpeed; nonvirtual void _LerpTurnToEnemy(void); nonvirtual void _LerpTurnToPos(vector); diff --git a/src/shared/NSMonster.qc b/src/shared/NSMonster.qc index 741a8942..0491a146 100644 --- a/src/shared/NSMonster.qc +++ b/src/shared/NSMonster.qc @@ -6,7 +6,7 @@ * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER @@ -14,6 +14,9 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +var float autocvar_ai_walkSpeed = 64; +var float autocvar_ai_runSpeed = 364; + void NSMonster::NSMonster(void) { @@ -51,6 +54,44 @@ NSMonster::NSMonster(void) m_flAnimTime = 0.0f; m_flTrackingTime = 0.0f; m_actIdle = -1; + m_flIdleNext = 0.0f; + m_flEyeHeight = 64.0f; + + m_sndSight = __NULL__; + m_sndIdle = __NULL__; + m_flIdleMin = 5.0f; + m_flIdleMax = 10.0f; + m_sndFootstep = __NULL__; + m_sndChatter = __NULL__; + m_sndChatterCombat = __NULL__; + m_sndPain = __NULL__; + m_sndMeleeAttack = __NULL__; + m_sndMeleeAttackHit = __NULL__; + m_sndMeleeAttackMiss = __NULL__; + m_sndDeath = __NULL__; + m_sndThud = __NULL__; + m_defMelee = __NULL__; + m_defSpecial1 = __NULL__; + m_iNumProjectiles = 1i; + m_flProjectileDelay = 0.0f; + m_flProjectileSpread = 0.0f; + m_flAttackCone = 0.0f; + m_flAttackAccuracy = 1.0f; + m_flMeleeRange = -1; + + m_defRanged1 = __NULL__; + m_defRanged2 = __NULL__; + + /* invalidate all ranges. */ + m_flRanged1Range = + m_flRanged2Range = + m_flSpecial1Range = + m_flSpecial2Range = -1.0f; + + m_bWeaponStartsDrawn = true; + + m_flWalkSpeed = autocvar_ai_walkSpeed; + m_flRunSpeed = autocvar_ai_runSpeed; #endif } @@ -245,7 +286,9 @@ NSMonster::Gib(void) vector vecDir = vectoangles(GetOrigin() - g_dmg_vecLocation); SetState(MONSTER_DEAD); SetTakedamage(DAMAGE_NO); - FX_GibHuman(origin, vecDir, g_dmg_iDamage * 2.5f); + + string breakModel = GetPropData(PROPINFO_BREAKMODEL); + BreakModel_Spawn(absmin, absmax, vecDir, g_dmg_iDamage * 2.5f, vlen(size) / 10, breakModel); Disappear(); } @@ -257,11 +300,17 @@ NSMonster::FallNoise(void) void NSMonster::IdleNoise(void) { + if (m_flIdleNext > time) + return; + + StartSoundDef(m_sndIdle, CHAN_VOICE, true); + m_flIdleNext = time + random(m_flIdleMin, m_flIdleMax); } void NSMonster::AlertNoise(void) { + StartSoundDef(m_sndSight, CHAN_VOICE, true); } bool @@ -279,7 +328,7 @@ NSMonster::IsFriend(int al) float NSMonster::MeleeMaxDistance(void) { - return 96; + return m_flMeleeRange; } /* Whether or not we should attempt a melee attack */ @@ -302,6 +351,9 @@ NSMonster::AlertNearby(void) return; for (entity w = world; (w = findfloat(w, ::takedamage, DAMAGE_YES));) { + if (w.classname != classname) + continue; + if (!IsFriend(w.m_iAlliance)) continue; @@ -362,6 +414,22 @@ NSMonster_TraceAgainsTarget(NSMonster monster, NSEntity target) void NSMonster::SeeThink(void) { + if (m_eLookAt) { + vector vecDelta; + makevectors( angles ); + vecDelta = normalize( (m_eLookAt.origin + m_eLookAt.view_ofs) - GetEyePos() ); + m_flHeadYaw = (vecDelta * v_right) * -60; + //print(sprintf("head yaw: %f %v\n", m_flHeadYaw, vecDelta)); + + /* this will make the actor 'aim" at the target */ + { + makevectors(v_angle); + vector tmp = vectoangles(v_forward); + subblendfrac = tmp[0] / 90; + bonecontrol1 = m_flHeadYaw; /* head turning */ + } + } + if (m_flAttackThink < time) if (m_eEnemy) { /* check if we should invalidate current enemy */ @@ -371,6 +439,7 @@ NSMonster::SeeThink(void) return; m_flSeeTime = time + 0.25f; + m_eLookAt = m_eEnemy; /* see if we can trace our target, if yes, update our timestamp */ if (NSMonster_TraceAgainsTarget(this, (NSEntity) m_eEnemy) == true) { @@ -391,6 +460,7 @@ NSMonster::SeeThink(void) SetState(MONSTER_ALERT); m_eEnemy = __NULL__; + m_eLookAt = __NULL__; m_flSeeTime = 0; } @@ -434,27 +504,22 @@ NSMonster::SeeThink(void) } } -var float autocvar_ai_stepSize = 128; - float NSMonster::GetWalkSpeed(void) { - float speed = autocvar_ai_stepSize / frameduration(modelindex, FramegroupForAct(ACT_WALK)); - return speed; + return m_flWalkSpeed; } float NSMonster::GetChaseSpeed(void) { - float speed = autocvar_ai_stepSize / frameduration(modelindex, FramegroupForAct(ACT_RUN)); - return speed; + return m_flRunSpeed; } float NSMonster::GetRunSpeed(void) { - float speed = autocvar_ai_stepSize / frameduration(modelindex, FramegroupForAct(ACT_RUN)); - return speed; + return m_flRunSpeed; } float @@ -519,6 +584,9 @@ NSMonster::_LerpTurnToPos(vector turnPos) void NSMonster::_LerpTurnToEnemy(void) { + vector enemyEyePos; + vector dirAim; + if (!m_eEnemy) return; @@ -529,6 +597,11 @@ NSMonster::_LerpTurnToEnemy(void) return; _LerpTurnToPos(m_eEnemy.origin); + + enemyEyePos = (m_eEnemy.origin + m_eEnemy.view_ofs); + + dirAim = vectoangles(enemyEyePos - GetEyePos()); + v_angle[0] = dirAim[0]; } void @@ -545,7 +618,7 @@ NSMonster::AttackThink(void) /* do we have a clear shot? */ other = world; - traceline(origin, m_eEnemy.origin, MOVE_OTHERONLY, this); + traceline(GetEyePos(), m_eEnemy.origin, MOVE_OTHERONLY, this); /* something is blocking us */ if (trace_fraction < 1.0f) { @@ -589,13 +662,165 @@ NSMonster::AttackThink(void) int NSMonster::AttackMelee(void) { - m_flAttackThink = time + 0.5f; - return (0); + float actMelee1 = FramegroupForAct(ACT_MELEE_ATTACK1); + float actMelee2 = FramegroupForAct(ACT_MELEE_ATTACK2); + + if (!m_defMelee) + return (0); + + _m_flMeleeDelay = Skill_GetDefValue(EntityDef_GetKeyValue(m_defMelee, "delay")); + _m_flMeleeAttempts = Skill_GetDefValue(EntityDef_GetKeyValue(m_defMelee, "attempts")); + + //print(sprintf("Melee attack %S with delay %f and %d attempts\n", m_defMelee, _m_flMeleeDelay, _m_flMeleeAttempts)); + + static void + AttackMelee_AttackFlail(void) + { + float meleeDmg = Skill_GetDefValue(EntityDef_GetKeyValue(m_defMelee, "damage")); + float meleeWait = Skill_GetDefValue(EntityDef_GetKeyValue(m_defMelee, "wait")); + traceline(origin, m_eEnemy.origin, FALSE, this); + + if (trace_fraction >= 1.0 || trace_ent.takedamage != DAMAGE_YES) { + StartSoundDef(m_sndMeleeAttackMiss, CHAN_WEAPON, true); + return; + } + + Damage_Apply(trace_ent, this, meleeDmg, 0, 0); + StartSoundDef(m_sndMeleeAttackHit, CHAN_WEAPON, true); + + _m_flMeleeAttempts--; + + if (_m_flMeleeAttempts > 0) + ScheduleThink(AttackMelee_AttackFlail, _m_flMeleeDelay + meleeWait); + } + + if (random() < 0.5 || actMelee2 == -1) + AnimPlay(actMelee1); + else + AnimPlay(actMelee2); + + m_flAttackThink = m_flAnimTime; + StartSoundDef(m_sndMeleeAttack, CHAN_WEAPON, true); + + /* functional */ + ScheduleThink(AttackMelee_AttackFlail, _m_flMeleeDelay); + return (1); } int NSMonster::AttackRanged(void) { + static void AttackRanged_Throw(void) + { + for (int i = 0; i < m_iNumProjectiles; i++) + NSProjectile_SpawnDef(m_defSpecial1, this); + } + static void AttackRanged_RangedSpecial(void) + { + NSProjectile_SpawnDef(m_defRanged2, this); + } + + float distToEnemy = vlen(m_eEnemy.origin - GetOrigin()); + + bool inSpecial1Range = (distToEnemy < m_flSpecial1Range && m_flSpecial1Range != -1.0) ? true : false; + bool inSpecial2Range = (distToEnemy < m_flSpecial2Range && m_flSpecial2Range != -1.0) ? true : false; + bool inRanged1Range = (distToEnemy < m_flRanged1Range && m_flRanged1Range != -1.0) ? true : false; + bool inRanged2Range = (distToEnemy < m_flRanged2Range && m_flRanged2Range != -1.0) ? true : false; + bool throwAnyway = false; + + traceline(GetEyePos(), m_eEnemy.origin, MOVE_NORMAL, this); + + if (_m_bShouldThrow == false && inSpecial1Range && m_flReloadCount) + if (_m_flReloadTracker > m_flReloadCount) { + throwAnyway = true; + _m_bShouldThrow = true; + } + + /* special always first if possible */ + if (throwAnyway == false && inRanged1Range && trace_ent == m_eEnemy) { + float rangedDmg = Skill_GetDefValue(EntityDef_GetKeyValue(m_defRanged1, "damage")); + float rangedDly = Skill_GetDefValue(EntityDef_GetKeyValue(m_defRanged1, "delay")); + float rangedMin = Skill_GetDefValue(EntityDef_GetKeyValue(m_defRanged1, "delay_min")); + float rangedMax = Skill_GetDefValue(EntityDef_GetKeyValue(m_defRanged1, "delay_max")); + float burstCount = Skill_GetDefValue(EntityDef_GetKeyValue(m_defRanged1, "burst")); + float burstDelay = Skill_GetDefValue(EntityDef_GetKeyValue(m_defRanged1, "burst_delay")); + float actRanged = FramegroupForAct(ACT_RANGE_ATTACK1); + float burstTime = 0.0f; + + if (rangedDly <= 0.0) { + rangedDly = random(rangedMin, rangedMax); + } + + /* can't shoot anything ranged anymore, need to reload. */ + if (m_flReloadCount) + if (_m_flReloadTracker > m_flReloadCount) { + float actReload = FramegroupForAct(ACT_RELOAD); + _m_flReloadTracker = 0; + AnimPlay(actReload); + StartSoundDef(m_sndReload, CHAN_WEAPON, true); + _m_bShouldThrow = false; + + if (m_flReloadDelay) + m_flAttackThink = time + m_flReloadDelay; + else + m_flAttackThink = time + frameduration(modelindex, actReload); + + return 1; + } + + AnimPlay(actRanged); + + /* if we have no spawnclass, it must be a hitscan weapon */ + if (EntityDef_HasSpawnClass(m_defRanged1)) { + NSProjectile_SpawnDef(m_defRanged1, this); + } else { + TraceAttack_FireBullets(1, GetEyePos(), rangedDmg, [0.01,0.01] * m_flAttackAccuracy, 0); + } + + StartSoundDef(m_sndRangedAttack, CHAN_WEAPON, true); + _m_flBurstCount++; + _m_flReloadTracker++; + + if (burstCount) + if (_m_flBurstCount >= burstCount) { + _m_flBurstCount = 0; + burstTime = burstDelay; + } + + if (rangedDly) + m_flAttackThink = time + rangedDly + burstTime; + else + m_flAttackThink = time + frameduration(modelindex, actRanged) + burstTime; + + return 1; + } else if (throwAnyway == false && inRanged2Range && trace_ent == m_eEnemy) { + float actRangedSpecial = FramegroupForAct(ACT_RANGE_ATTACK2); + AnimPlay(actRangedSpecial); + ScheduleThink(AttackRanged_RangedSpecial, 0.0f); + m_flAttackThink = time + frameduration(modelindex, actRangedSpecial); + return 1; + } else if (inSpecial1Range) { + AnimPlay(FramegroupForAct(ACT_SPECIAL_ATTACK1)); + ScheduleThink(AttackRanged_Throw, m_flProjectileDelay); + + if (_m_bShouldThrow) + m_flAttackThink = time + 1.0f; + else + m_flAttackThink = time + 2.5f; + + return 1; + } else if (inSpecial2Range) { + AnimPlay(FramegroupForAct(ACT_SPECIAL_ATTACK2)); + ScheduleThink(AttackRanged_Throw, m_flProjectileDelay); + + if (_m_bShouldThrow) + m_flAttackThink = time + 1.0f; + else + m_flAttackThink = time + 2.5f; + + return 1; + } + m_flAttackThink = time + 0.5f; return (0); } @@ -603,15 +828,20 @@ NSMonster::AttackRanged(void) void NSMonster::AttackDraw(void) { - NSMonster_Log("^1%s::AttackDraw: Not defined!", classname); - m_flAttackThink = time + 0.5f; + float actDraw = FramegroupForAct(ACT_ARM); + AnimPlay(actDraw); + m_flAttackThink = time + frameduration(modelindex, actDraw); + + if (m_flBodyOnDraw) + SetBody(m_flBodyOnDraw); } void NSMonster::AttackHolster(void) { - NSMonster_Log("^1%s::AttackHolster: Not defined!", classname); - m_flAttackThink = time + 0.5f; + float actHolster = FramegroupForAct(ACT_DISARM); + AnimPlay(actHolster); + m_flAttackThink = time + frameduration(modelindex, actHolster); } void @@ -787,8 +1017,27 @@ NSMonster::IsAlive(void) void NSMonster::StateChanged(monsterState_t oldState, monsterState_t newState) { - NSMonster_Log("^2%s::^3StateChanged^7: state changed from %d to %d", \ - classname, oldState, newState); + switch (newState) { + case MONSTER_AIMING: + /* we're coming from an alerted/raised state to pointing a gun. */ + if (oldState == MONSTER_ALERT) { + /* only draw if it wasn't already drawn */ + if (m_bWeaponDrawn == false) { + AttackDraw(); + m_bWeaponDrawn = true; + } + } + break; + case MONSTER_ALERT: + if (oldState == MONSTER_AIMING) { + /* only holster if it wasn't in the initial state either */ + if (m_bWeaponStartsDrawn == false) { + AttackHolster(); + m_bWeaponDrawn = false; + } + } + break; + } } void @@ -899,8 +1148,7 @@ NSMonster::Physics(void) } } - m_flBaseTime = frame1time; - frame1time += frametime; + //print(sprintf("%f was %f\n", frame1time, m_flBaseTime)); processmodelevents(modelindex, frame, m_flBaseTime, frame1time, HandleAnimEvent); @@ -912,6 +1160,7 @@ NSMonster::Touch(entity eToucher) if (movetype != MOVETYPE_WALK) return; + if (autocvar(pm_pushMonsters, 0)) if (eToucher.movetype == MOVETYPE_WALK) { if (eToucher.absmin[2] < origin[2]) velocity = normalize(eToucher.origin - origin) * -128; @@ -927,6 +1176,11 @@ NSMonster::HasBeenHit(void) void NSMonster::Pain(void) { + float actSmallFlinch = FramegroupForAct(ACT_SMALL_FLINCH); + float actBigFlinch = FramegroupForAct(ACT_BIG_FLINCH); + float actTwitch = FramegroupForAct(ACT_TWITCH); + float actPain = -1; + /* dead things tell nuthin */ if (IsAlive() == false) return; @@ -951,6 +1205,42 @@ NSMonster::Pain(void) /* alert all nearby friendlies */ AlertNearby(); + + switch (g_dmg_iHitBody) { + case BODY_HEAD: + actPain = FramegroupForAct(ACT_FLINCH_HEAD); + break; + case BODY_CHEST: + actPain = FramegroupForAct(ACT_FLINCH_CHEST); + break; + case BODY_STOMACH: + actPain = FramegroupForAct(ACT_FLINCH_STOMACH); + break; + case BODY_ARMLEFT: + actPain = FramegroupForAct(ACT_FLINCH_LEFTARM); + break; + case BODY_ARMRIGHT: + actPain = FramegroupForAct(ACT_FLINCH_RIGHTARM); + break; + case BODY_LEGLEFT: + actPain = FramegroupForAct(ACT_FLINCH_LEFTLEG); + break; + case BODY_LEGRIGHT: + actPain = FramegroupForAct(ACT_FLINCH_RIGHTLEG); + break; + } + + /* fallback in case we do not have specialized flinches */ + if (actPain == -1) { + /* for big damage pain anim, we need to take at least 1/3rd of health */ + if (actBigFlinch >= 0 && g_dmg_iDamage > (base_health / 3)) + actPain = actBigFlinch; + else if (actSmallFlinch >= 0) + actPain = actSmallFlinch; + } + + AnimPlay(actPain); + StartSoundDef(m_sndPain, CHAN_VOICE, true); HasBeenHit(); } @@ -981,6 +1271,19 @@ NSMonster::_Alerted(void) void NSMonster::Death(void) { + static void Death_Thud(void) + { + StartSoundDef(m_sndThud, CHAN_BODY, true); + } + + float actViolent = FramegroupForAct(ACT_DIEVIOLENT); + float actForward = FramegroupForAct(ACT_DIEFORWARD); + float actBackward = FramegroupForAct(ACT_DIEBACKWARD); + float actSimple = FramegroupForAct(ACT_DIESIMPLE); + float actBackshot = FramegroupForAct(ACT_DIE_BACKSHOT); + + float actDeath = -1; + /* we were already dead before, so gib */ if (GetState() == MONSTER_DEAD) { HasBeenGibbed(); @@ -997,8 +1300,37 @@ NSMonster::Death(void) return; } - /* make sure we're not causing any more obituaries */ + switch (g_dmg_iHitBody) { + case BODY_HEAD: + actDeath = FramegroupForAct(ACT_DIE_HEADSHOT); + break; + case BODY_CHEST: + actDeath = FramegroupForAct(ACT_DIE_CHESTSHOT); + break; + case BODY_STOMACH: + actDeath = FramegroupForAct(ACT_DIE_GUTSHOT); + break; + } + + if (actDeath == -1) { + if (actViolent >= 0 && GetHealth() < -15) /* lots of damage */ + AnimPlay(actViolent); + else if (actBackshot >= 0 && IsFacingPosition(g_dmg_vecLocation) == false) + AnimPlay(actBackshot); + else if (actForward >= 0 && IsFacingPosition(g_dmg_vecLocation) == false) + AnimPlay(actForward); + else if (actBackward >= 0 && IsFacingPosition(g_dmg_vecLocation) == true) + AnimPlay(actBackward); + else + AnimPlay(actSimple); + } else { + AnimPlay(actDeath); + } + + StartSoundDef(m_sndDeath, CHAN_VOICE, true); HasBeenKilled(); + + /* make sure we're not causing any more obituaries */ RemoveFlags(FL_MONSTER); /* set the monster up for getting gibbed */ @@ -1010,6 +1342,9 @@ NSMonster::Death(void) /* monsters trigger their targets when dead */ if (GetTriggerCondition() == MTRIG_DEATH) TriggerTargets(); + + /* play thud sound */ + ScheduleThink(Death_Thud, frameduration(modelindex, frame) * 0.5f); } #if 0 @@ -1047,8 +1382,15 @@ NSMonster::Respawn(void) SetModel(GetSpawnModel()); SetSize(base_mins, base_maxs); SetOrigin(GetSpawnOrigin()); + SetEyePos([0, 0, m_flEyeHeight]); DropToFloor(); + + if (m_bWeaponStartsDrawn) { + m_bWeaponDrawn = true; + } else { + m_bWeaponDrawn = false; + } } void @@ -1057,10 +1399,148 @@ NSMonster::SpawnKey(string strKey, string strValue) switch (strKey) { /* The legacy GoldSrc trigger condition system */ case "TriggerCondition": - m_iTriggerCondition = stoi(strValue); + m_iTriggerCondition = ReadInt(strValue); break; case "TriggerTarget": - m_strTriggerTarget = strValue; + m_strTriggerTarget = ReadString(strValue); + break; + /* entityDef related */ + case "netname": /* used for obituries and debug info */ + netname = ReadString(strValue); + break; + case "eye_height": + m_flEyeHeight = ReadFloat(strValue); + break; + case "snd_sight": + m_sndSight = ReadString(strValue); + break; + case "snd_idle": + m_sndIdle = ReadString(strValue); + break; + case "idle_min": /* used for idle sound timer */ + m_flIdleMin = ReadFloat(strValue); + break; + case "idle_max": /* ditto */ + m_flIdleMax = ReadFloat(strValue); + break; + case "snd_footstep": + m_sndFootstep = ReadString(strValue); + break; + case "snd_chatter": + m_sndChatter = ReadString(strValue); + break; + case "snd_chatter_combat": + m_sndChatterCombat = ReadString(strValue); + break; + case "snd_pain": + m_sndPain = ReadString(strValue); + break; + case "snd_death": + m_sndDeath = ReadString(strValue); + break; + case "snd_thud": + m_sndThud = ReadString(strValue); + break; + case "def_melee": /* melee attack information */ + case "def_attack_melee": + m_defMelee = ReadString(strValue); + break; + case "attack_melee_range": + case "melee_range": /* Doom 3 compat */ + m_flMeleeRange = ReadFloat(strValue); + break; + case "snd_melee_attack": + m_sndMeleeAttack = ReadString(strValue); + break; + case "snd_melee_attack_hit": + m_sndMeleeAttackHit = ReadString(strValue); + break; + case "snd_melee_attack_miss": + m_sndMeleeAttackMiss = ReadString(strValue); + break; + case "def_attack_ranged": /* primary ranged attack */ + case "def_attack_ranged_1": + m_defRanged1 = ReadString(strValue); + break; + case "attack_ranged1_range": + case "attack_ranged_range": + case "ranged_range": + m_flRanged1Range = ReadFloat(strValue); + break; + case "def_attack_ranged_2": /* special ranged attack */ + m_defRanged2 = ReadString(strValue); + break; + case "attack_ranged2_range": + case "ranged2_range": + m_flRanged2Range = ReadFloat(strValue); + break; + case "snd_ranged_attack": + m_sndRangedAttack = ReadString(strValue); + break; + case "reload_count": /* how many ranged attacks until reload */ + m_flReloadCount = ReadFloat(strValue); + break; + case "reload_delay": /* time between reloads */ + m_flReloadDelay = ReadFloat(strValue); + break; + case "snd_reload": + m_sndReload = ReadString(strValue); + break; + case "def_attack_special": + case "def_attack_special_1": + m_defSpecial1 = ReadString(strValue); + break; + case "attack_special1_range": + case "attack_special_range": + case "special1_range": + m_flSpecial1Range = ReadFloat(strValue); + break; + case "def_attack_special_2": /* projectile */ + m_defSpecial2 = ReadString(strValue); + break; + case "attack_special2_range": + case "special2_range": + m_flSpecial2Range = ReadFloat(strValue); + break; + case "num_projectiles": + m_iNumProjectiles = ReadInt(strValue); + break; + case "projectile_spread": + m_flProjectileSpread = ReadFloat(strValue); + break; + case "projectile_delay": + m_flProjectileDelay = ReadFloat(strValue); + break; + case "attack_cone": + m_flAttackCone = ReadFloat(strValue); + break; + case "attack_accuracy": /* affects ranged accuracy */ + m_flAttackAccuracy = ReadFloat(strValue); + break; + case "weapon_drawn": + m_bWeaponStartsDrawn = ReadBool(strValue); + break; + case "body_on_draw": + m_flBodyOnDraw = ReadFloat(strValue); + break; + case "speed_walk": + m_flWalkSpeed = ReadFloat(strValue); + break; + case "speed_run": + m_flRunSpeed = ReadFloat(strValue); + break; + /* compat */ + case "maxs": + base_maxs = ReadVector(strValue); + break; + case "mins": + base_mins = ReadVector(strValue); + break; + case "team": + m_iAlliance = ReadInt(strValue); + break; + case "health": + base_health = Skill_GetDefValue(strValue); break; default: NSSurfacePropEntity::SpawnKey(strKey, strValue); @@ -1077,6 +1557,7 @@ NSMonster::EvaluateEntity(void) EVALUATE_VECTOR(angles, 0, MONFL_CHANGED_ANGLES_X) EVALUATE_VECTOR(angles, 1, MONFL_CHANGED_ANGLES_Y) EVALUATE_VECTOR(angles, 2, MONFL_CHANGED_ANGLES_Z) + EVALUATE_VECTOR(v_angle, 0, MONFL_CHANGED_ANGLES_X) EVALUATE_FIELD(modelindex, MONFL_CHANGED_MODELINDEX) EVALUATE_VECTOR(view_ofs, 2, MONFL_CHANGED_MODELINDEX) EVALUATE_FIELD(solid, MONFL_CHANGED_SOLID) @@ -1103,6 +1584,9 @@ NSMonster::EvaluateEntity(void) EVALUATE_VECTOR(m_vecRenderColor, 1, MONFL_CHANGED_RENDERCOLOR) EVALUATE_VECTOR(m_vecRenderColor, 2, MONFL_CHANGED_RENDERCOLOR) EVALUATE_FIELD(m_flRenderAmt, MONFL_CHANGED_RENDERAMT) + EVALUATE_FIELD(bonecontrol1, MONFL_CHANGED_HEADYAW) + EVALUATE_FIELD(subblendfrac, MONFL_CHANGED_HEADYAW) + EVALUATE_FIELD(frame1time, MONFL_CHANGED_HEADYAW) } /* Make sure StartFrame calls this */ @@ -1126,6 +1610,7 @@ NSMonster::SendEntity(entity ePEnt, float flChanged) SENDENTITY_ANGLE(angles[0], MONFL_CHANGED_ANGLES_X) SENDENTITY_ANGLE(angles[1], MONFL_CHANGED_ANGLES_Y) SENDENTITY_ANGLE(angles[2], MONFL_CHANGED_ANGLES_Z) + SENDENTITY_ANGLE(v_angle[0], MONFL_CHANGED_ANGLES_X) SENDENTITY_SHORT(modelindex, MONFL_CHANGED_MODELINDEX) SENDENTITY_BYTE(view_ofs[2], MONFL_CHANGED_MODELINDEX) SENDENTITY_BYTE(solid, MONFL_CHANGED_SOLID) @@ -1152,6 +1637,9 @@ NSMonster::SendEntity(entity ePEnt, float flChanged) SENDENTITY_ANGLE(m_vecRenderColor[1], MONFL_CHANGED_RENDERCOLOR) SENDENTITY_ANGLE(m_vecRenderColor[2], MONFL_CHANGED_RENDERCOLOR) SENDENTITY_ANGLE(m_flRenderAmt, MONFL_CHANGED_RENDERAMT) + SENDENTITY_FLOAT(bonecontrol1, MONFL_CHANGED_HEADYAW) + SENDENTITY_FLOAT(subblendfrac, MONFL_CHANGED_HEADYAW) + SENDENTITY_FLOAT(frame1time, MONFL_CHANGED_HEADYAW) return (1); } @@ -1202,6 +1690,7 @@ NSMonster::ReceiveEntity(float flNew, float flChanged) READENTITY_ANGLE(angles[0], MONFL_CHANGED_ANGLES_X) READENTITY_ANGLE(angles[1], MONFL_CHANGED_ANGLES_Y) READENTITY_ANGLE(angles[2], MONFL_CHANGED_ANGLES_Z) + READENTITY_ANGLE(v_angle[0], MONFL_CHANGED_ANGLES_X) READENTITY_SHORT(modelindex, MONFL_CHANGED_MODELINDEX) READENTITY_BYTE(view_ofs[2], MONFL_CHANGED_MODELINDEX) READENTITY_BYTE(solid, MONFL_CHANGED_SOLID) @@ -1228,16 +1717,17 @@ NSMonster::ReceiveEntity(float flNew, float flChanged) READENTITY_ANGLE(m_vecRenderColor[1], MONFL_CHANGED_RENDERCOLOR) READENTITY_ANGLE(m_vecRenderColor[2], MONFL_CHANGED_RENDERCOLOR) READENTITY_ANGLE(m_flRenderAmt, MONFL_CHANGED_RENDERAMT) + READENTITY_FLOAT(bonecontrol1, MONFL_CHANGED_HEADYAW) + READENTITY_FLOAT(subblendfrac, MONFL_CHANGED_HEADYAW) + READENTITY_FLOAT(frame1time, MONFL_CHANGED_HEADYAW) if (scale == 0.0) scale = 1.0f; - if (flChanged & MONFL_CHANGED_FRAME) - frame1time = 0.0f; if (flChanged & MONFL_CHANGED_SIZE) setsize(this, mins * scale, maxs * scale); if (flChanged & MONFL_CHANGED_BODY) - setcustomskin(this, "", sprintf("geomset 0 %i\ngeomset 1 %i\n", m_iBody, m_iBody)); + _UpdateGeomset(); setorigin(this, origin); } diff --git a/src/shared/NSProjectile.h b/src/shared/NSProjectile.h index 5197c086..35791108 100644 --- a/src/shared/NSProjectile.h +++ b/src/shared/NSProjectile.h @@ -136,4 +136,9 @@ public: #ifdef CLIENT void NSProjectile_ReadEntity(bool); +#endif + +#ifdef SERVER +void NSProjectile_SpawnDef(string entityDef, NSEntity theOwner) +void NSProjectile_SpawnDefAttachment(string entityDef, NSEntity theOwner, int attachmentID) #endif \ No newline at end of file diff --git a/src/shared/NSProjectile.qc b/src/shared/NSProjectile.qc index c9a3c74b..593f1c60 100644 --- a/src/shared/NSProjectile.qc +++ b/src/shared/NSProjectile.qc @@ -503,4 +503,33 @@ NSProjectile_ReadEntity(bool new) fl = readfloat(); rend.ReceiveEntity(new, fl); } +#endif + +#ifdef SERVER +void +NSProjectile_SpawnDef(string entityDef, NSEntity theOwner) +{ + entity oldself = self; + NSProjectile rocket = spawn(NSProjectile); + rocket.owner = theOwner; + self = rocket; + EntityDef_SpawnClassname(entityDef); + self = oldself; + rocket.Launch(theOwner.GetOrigin() + theOwner.view_ofs, theOwner.GetAngles(), 0.0f, 0.0f, 0.0f); +} +void +NSProjectile_SpawnDefAttachment(string entityDef, NSEntity theOwner, int attachmentID) +{ + entity oldself = self; + float skeletonIndex = skel_create(theOwner.modelindex); + vector attachmentPos = gettaginfo(theOwner, skel_get_numbones(skeletonIndex) + attachmentID); + skel_delete(skeletonIndex); + + NSProjectile rocket = spawn(NSProjectile); + rocket.owner = theOwner; + self = rocket; + EntityDef_SpawnClassname(entityDef); + self = oldself; + rocket.Launch(attachmentPos, theOwner.GetAngles(), 0.0f, 0.0f, 0.0f); +} #endif \ No newline at end of file diff --git a/src/shared/NSRenderableEntity.qc b/src/shared/NSRenderableEntity.qc index 90913a13..e9fedae5 100644 --- a/src/shared/NSRenderableEntity.qc +++ b/src/shared/NSRenderableEntity.qc @@ -45,7 +45,14 @@ void NSRenderableEntity::RendererRestarted( void ) { void NSRenderableEntity::_UpdateGeomset(void) { - setcustomskin(this, "", sprintf("geomset 0 %i\ngeomset 1 %i\n", m_iBody, m_iBody)); + int firstBody = (m_iBody & 0x0F); + int secondBody = ((m_iBody >> 4) & 0x0F); + + //print(sprintf("%i body 1: %i body 2: %i\n", m_iBody , firstBody, secondBody)); + + setcustomskin(this, "", + sprintf("geomset 0 %i\ngeomset 1 %i\ngeomset 2 %i\n", secondBody, firstBody, secondBody) + ); } #endif @@ -276,7 +283,7 @@ NSRenderableEntity::ReceiveEntity(float flNew, float flChanged) if (flChanged & RDENT_CHANGED_SIZE) setsize(this, mins * scale, maxs * scale); if (flChanged & RDENT_CHANGED_BODY) - setcustomskin(this, "", sprintf("geomset 0 %i\ngeomset 1 %i\n", m_iBody, m_iBody)); + _UpdateGeomset(); setorigin(this, origin); } @@ -541,9 +548,6 @@ NSRenderableEntity::predraw(void) angles[1] -= frametime * 120.0; } - if (serverkeyfloat(SERVERKEY_PAUSESTATE) != 1) - frame1time += frametime; - processmodelevents(modelindex, frame, m_flBaseTime, frame1time, HandleAnimEvent); @@ -775,6 +779,8 @@ NSRenderableEntity::HandleAnimEvent(float flTimeStamp, int iCode, string strData tokenize(m_strModelEventCB); /* ensure argv() is 'rewound'... */ } } + + //print(sprintf("Received: %f %i %S\n", flTimeStamp, iCode, strData)); #else NSLog("Unknown model event: %f %i %S", flTimeStamp, iCode, strData); #endif diff --git a/src/shared/NSSquadMonster.h b/src/shared/NSSquadMonster.h index f8fd1604..6181a0e4 100644 --- a/src/shared/NSSquadMonster.h +++ b/src/shared/NSSquadMonster.h @@ -24,6 +24,10 @@ public: void NSSquadMonster(void); #ifdef SERVER + /* overrides */ + virtual void Spawned(void); + virtual void SpawnKey(string, string); + /** Overridable: Called when this NPC became squad leader. */ virtual void HasBecomeSquadLeader(void); /** Overridable: Called when this NPC joined a squad. */ @@ -51,6 +55,8 @@ public: #ifdef SERVER private: + int m_iSquadLeaderBody; + bool m_bStartAsLeader; bool m_inSquad; NSSquadMonster m_eSquadLeader; diff --git a/src/shared/NSSquadMonster.qc b/src/shared/NSSquadMonster.qc index 06ba610f..5b75de9c 100644 --- a/src/shared/NSSquadMonster.qc +++ b/src/shared/NSSquadMonster.qc @@ -20,6 +20,7 @@ NSSquadMonster::NSSquadMonster(void) #ifdef SERVER m_inSquad = false; m_eSquadLeader = __NULL__; + m_bStartAsLeader = false; for (int i = 0; i < NSSQUADMONSTER_MAXMEMBERS; i++) { m_eSquadMembers[i] = __NULL__; @@ -28,6 +29,39 @@ NSSquadMonster::NSSquadMonster(void) } #ifdef SERVER +void +NSSquadMonster::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "squad_leader": + m_bStartAsLeader = ReadBool(strValue); + break; + case "squad_leader_body": + m_iSquadLeaderBody = ReadInt(strValue); + break; + default: + super::SpawnKey(strKey, strValue); + } +} + +void +NSSquadMonster::Spawned(void) +{ + static void AttachToNearBySquad(void) { + FindSquadNearMe(1024); + } + + super::Spawned(); + + /* unless specified manually, make up who becomes squad member */ + if (m_bStartAsLeader) { + m_eSquadLeader = this; + m_inSquad = true; + } else { + ScheduleThink(AttachToNearBySquad, 0.5f); + } +} + void NSSquadMonster::HasBecomeSquadLeader(void) { @@ -97,6 +131,7 @@ NSSquadMonster::AddToSquad(NSSquadMonster addMember) startMember = this; m_inSquad = true; print(sprintf("%s (%d) became squad leader\n", classname, num_for_edict(this))); + SetBody(GetBody() | m_iSquadLeaderBody); HasBecomeSquadLeader(); } diff --git a/src/shared/NSSurfacePropEntity.qc b/src/shared/NSSurfacePropEntity.qc index ff69f93b..e22f716d 100644 --- a/src/shared/NSSurfacePropEntity.qc +++ b/src/shared/NSSurfacePropEntity.qc @@ -602,6 +602,10 @@ NSSurfacePropEntity::SpawnKey(string strKey, string strValue) case "materialdata": SetSurfaceData(strValue); break; + /* entityDef */ + case "blood_color": + m_vecBloodColor = ReadVector(strValue); + break; /* Input/Output system */ #ifdef SERVER case "OnBreak": diff --git a/src/shared/NSTalkMonster.h b/src/shared/NSTalkMonster.h index 821648ec..80a69f55 100644 --- a/src/shared/NSTalkMonster.h +++ b/src/shared/NSTalkMonster.h @@ -98,6 +98,8 @@ public: virtual void TalkFollow(void); /** Called when they tell the player that they'll stop following. */ virtual void TalkStopFollow(void); + /** Called when they tell the player they won't follow you. */ + virtual void TalkDenyFollow(void); #endif #ifdef CLIENT @@ -127,7 +129,7 @@ private: float m_flTraceTime; float m_flFollowSpeedChanged; float m_flFollowSpeed; - + bool m_bFollowOnUse; /* sentences identifiers */ string m_talkAnswer; /* random answer to whenever a question is asked */ @@ -153,6 +155,7 @@ private: string m_talkUnfollow; /* when the player asks us to stop following */ string m_talkFollow; /* whenever player asks the NPC to follow */ string m_talkStopFollow; /* we have to stop following */ + string m_talkDenyFollow; /* deny the follow request. */ virtual void _Alerted(void); #endif diff --git a/src/shared/NSTalkMonster.qc b/src/shared/NSTalkMonster.qc index f785c1d2..7b72616a 100644 --- a/src/shared/NSTalkMonster.qc +++ b/src/shared/NSTalkMonster.qc @@ -49,6 +49,8 @@ NSTalkMonster::NSTalkMonster(void) m_talkUnfollow = __NULL__; m_talkFollow = __NULL__; m_talkStopFollow = __NULL__; + m_talkDenyFollow = __NULL__; + m_bFollowOnUse = false; #else m_flSentenceTime = 0.0f; m_pSentenceQue = __NULL__; @@ -300,13 +302,13 @@ NSTalkMonster::TalkPlayerGreet(void) /* Find players in a specific radius */ if (vlen(p.origin - origin) < PLAYER_DETECT_RADIUS) { /* If we can't physically see him, don't do anything */ - traceline(origin, p.origin, FALSE, this); - if (trace_ent != p) { + if (VisibleVec(p.origin + p.view_ofs) == false) continue; - } + Sentence(m_talkPlayerGreet); m_flNextSentence = time + 10.0; m_iFlags |= MONSTER_METPLAYER; + m_eLookAt = p; break; } } @@ -485,6 +487,17 @@ NSTalkMonster::TalkStopFollow(void) m_flNextSentence = time + 10.0; } +void +NSTalkMonster::TalkDenyFollow(void) +{ + if (m_iSequenceState != SEQUENCESTATE_NONE) + return; + + Sentence(m_talkDenyFollow); + m_flNextSentence = time + 10.0; +} + + void NSTalkMonster::FollowPlayer(void) { @@ -506,7 +519,7 @@ NSTalkMonster::FollowPlayer(void) /* Give up after 1024 units */ if (flPlayerDist > 1024) { m_eFollowing = world; - } else if (flPlayerDist > 64) { + } else if (flPlayerDist > 128) { /* we only allow speed changes every second, avoid jitter */ if (m_flFollowSpeedChanged < time) { float flNextSpeed = GetChaseSpeed(); @@ -605,6 +618,7 @@ NSTalkMonster::RunAI(void) FollowChain(); if (m_eFollowing != world) { + m_eLookAt = m_eFollowing; FollowPlayer(); } else if (m_iFlags & MONSTER_FEAR) { PanicFrame(); @@ -623,6 +637,11 @@ NSTalkMonster::Respawn(void) super::Respawn(); m_eFollowing = world; m_eFollowingChain = world; + PlayerUse = OnPlayerUse; + + if (m_bFollowOnUse) { + m_iFlags |= MONSTER_CANFOLLOW; + } } void @@ -632,8 +651,10 @@ NSTalkMonster::OnPlayerUse(void) return; /* can't press use any non-allies */ - if (!(m_iFlags & MONSTER_CANFOLLOW)) + if (!(m_iFlags & MONSTER_CANFOLLOW)) { + TalkDenyFollow(); return; + } if ((m_eFollowing == world)) { if (!(m_iFlags & MONSTER_USED)) { @@ -660,6 +681,77 @@ NSTalkMonster::SpawnKey(string strKey, string strValue) case "UseSentence": m_talkFollow = strcat("!", strValue); break; + + /* entityDef */ + case "talk_answer": + m_talkAnswer = ReadString(strValue); + break; + case "talk_ask": + m_talkAsk = ReadString(strValue); + break; + case "talk_ally_shot": + m_talkAllyShot = ReadString(strValue); + break; + case "talk_greet": + m_talkGreet = ReadString(strValue); + break; + case "talk_idle": + m_talkIdle = ReadString(strValue); + break; + case "talk_panic": + m_talkPanic = ReadString(strValue); + break; + case "talk_hearing": + m_talkHearing = ReadString(strValue); + break; + case "talk_smelling": + m_talkSmelling = ReadString(strValue); + break; + case "talk_stare": + m_talkStare = ReadString(strValue); + break; + case "talk_survived": + m_talkSurvived = ReadString(strValue); + break; + case "talk_wounded": + m_talkWounded = ReadString(strValue); + break; + case "talk_alert": + m_talkAlert = ReadString(strValue); + break; + case "talk_player_ask": + m_talkPlayerAsk = ReadString(strValue); + break; + case "talk_player_greet": + m_talkPlayerGreet = ReadString(strValue); + break; + case "talk_player_idle": + m_talkPlayerIdle = ReadString(strValue); + break; + case "talk_player_wounded1": + m_talkPlayerWounded1 = ReadString(strValue); + break; + case "talk_player_wounded2": + m_talkPlayerWounded2 = ReadString(strValue); + break; + case "talk_player_wounded3": + m_talkPlayerWounded3 = ReadString(strValue); + break; + case "talk_unfollow": + m_talkUnfollow = ReadString(strValue); + break; + case "talk_follow": + m_talkFollow = ReadString(strValue); + break; + case "talk_stop_follow": + m_talkStopFollow = ReadString(strValue); + break; + case "talk_deny_follow": + m_talkDenyFollow = ReadString(strValue); + break; + case "follow_on_use": + m_bFollowOnUse = ReadBool(strValue); + break; default: super::SpawnKey(strKey, strValue); break; @@ -686,6 +778,7 @@ NSTalkMonster::SendEntity(entity ePEnt, float flChanged) SENDENTITY_ANGLE(angles[0], MONFL_CHANGED_ANGLES_X) SENDENTITY_ANGLE(angles[1], MONFL_CHANGED_ANGLES_Y) SENDENTITY_ANGLE(angles[2], MONFL_CHANGED_ANGLES_Z) + SENDENTITY_FLOAT(v_angle[0], MONFL_CHANGED_ANGLES_X) SENDENTITY_SHORT(modelindex, MONFL_CHANGED_MODELINDEX) SENDENTITY_BYTE(view_ofs[2], MONFL_CHANGED_MODELINDEX) SENDENTITY_BYTE(solid, MONFL_CHANGED_SOLID) @@ -712,6 +805,7 @@ NSTalkMonster::SendEntity(entity ePEnt, float flChanged) SENDENTITY_ANGLE(m_vecRenderColor[1], MONFL_CHANGED_RENDERCOLOR) SENDENTITY_ANGLE(m_vecRenderColor[2], MONFL_CHANGED_RENDERCOLOR) SENDENTITY_ANGLE(m_flRenderAmt, MONFL_CHANGED_RENDERAMT) + SENDENTITY_FLOAT(m_flHeadYaw, MONFL_CHANGED_HEADYAW) return (1); } @@ -844,6 +938,23 @@ NSTalkMonster::predraw(void) m_bWasPaused = true; } + bonecontrol2 = autocvar(bonecontrol2, 0); + bonecontrol3 = autocvar(bonecontrol3, 0); + bonecontrol4 = autocvar(bonecontrol4, 0); + + /* this will make the actor 'aim" at the target */ + { + makevectors(v_angle); + vector tmp = vectoangles(v_forward); + subblendfrac = tmp[0] / 90; + bonecontrol1 = m_flHeadYaw; /* head turning */ + } + + //print(sprintf("yaw: %f %f\n", subblendfrac, v_angle[0])); + subblend2frac = autocvar(subblend2frac, 0); + basesubblendfrac = autocvar(basesubblendfrac, 0); + basesubblend2frac = autocvar(basesubblend2frac, 0); + addentity(this); _RenderDebugViewCone(); @@ -864,6 +975,7 @@ NSTalkMonster::ReceiveEntity(float flNew, float flChanged) READENTITY_ANGLE(angles[0], MONFL_CHANGED_ANGLES_X) READENTITY_ANGLE(angles[1], MONFL_CHANGED_ANGLES_Y) READENTITY_ANGLE(angles[2], MONFL_CHANGED_ANGLES_Z) + READENTITY_FLOAT(v_angle[0], MONFL_CHANGED_ANGLES_X) READENTITY_SHORT(modelindex, MONFL_CHANGED_MODELINDEX) READENTITY_BYTE(view_ofs[2], MONFL_CHANGED_MODELINDEX) READENTITY_BYTE(solid, MONFL_CHANGED_SOLID) @@ -890,6 +1002,7 @@ NSTalkMonster::ReceiveEntity(float flNew, float flChanged) READENTITY_ANGLE(m_vecRenderColor[1], MONFL_CHANGED_RENDERCOLOR) READENTITY_ANGLE(m_vecRenderColor[2], MONFL_CHANGED_RENDERCOLOR) READENTITY_ANGLE(m_flRenderAmt, MONFL_CHANGED_RENDERAMT) + READENTITY_FLOAT(m_flHeadYaw, MONFL_CHANGED_HEADYAW) if (scale == 0.0) scale = 1.0f; @@ -899,7 +1012,7 @@ NSTalkMonster::ReceiveEntity(float flNew, float flChanged) if (flChanged & MONFL_CHANGED_SIZE) setsize(this, mins * scale, maxs * scale); if (flChanged & MONFL_CHANGED_BODY) - setcustomskin(this, "", sprintf("geomset 0 %i\ngeomset 1 %i\n", m_iBody, m_iBody)); + _UpdateGeomset(); setorigin(this, origin); }