From ba25f7710c84bae0ae635a4414e5a159b63b849b Mon Sep 17 00:00:00 2001 From: Julian Date: Wed, 10 Jun 2020 01:12:58 -0300 Subject: [PATCH] Add monster_otis. --- cfg/monster_precache.cfg | 1 + cfg/monster_skill.cfg | 4 + src/dlls/Makefile | 1 + src/dlls/cmbasemonster.h | 27 ++++ src/dlls/combat.cpp | 24 ++- src/dlls/dllapi.cpp | 135 ++++++++-------- src/dlls/otis.cpp | 329 +++++++++++++++++++++++++++++++++++++++ src/dlls/skill.cpp | 6 + src/dlls/skill.h | 3 + src/dlls/weapons.h | 1 + 10 files changed, 461 insertions(+), 70 deletions(-) create mode 100644 src/dlls/otis.cpp diff --git a/cfg/monster_precache.cfg b/cfg/monster_precache.cfg index 10d23b0..afed89c 100755 --- a/cfg/monster_precache.cfg +++ b/cfg/monster_precache.cfg @@ -29,3 +29,4 @@ //monster_sentry //monster_gonome //monster_male_assassin +//monster_otis diff --git a/cfg/monster_skill.cfg b/cfg/monster_skill.cfg index 0f03396..37c3d53 100755 --- a/cfg/monster_skill.cfg +++ b/cfg/monster_skill.cfg @@ -100,6 +100,9 @@ sk_gonome_dmg_one_bite 14 sk_massassin_health 50 sk_massassin_kick 25 +// Otis +sk_otis_health 35 + // MONSTER WEAPON DAMAGE sk_9mm_bullet 5 @@ -107,3 +110,4 @@ sk_9mmAR_bullet 4 sk_9mmAR_grenade 100 sk_12mm_bullet 10 sk_762_bullet 100 +sk_357_bullet 40 diff --git a/src/dlls/Makefile b/src/dlls/Makefile index c4ea172..523b9bb 100644 --- a/src/dlls/Makefile +++ b/src/dlls/Makefile @@ -36,6 +36,7 @@ OBJ = \ monsters.o \ monsterstate.o \ nodes.o \ + otis.o \ scientist.o \ skill.o \ sound.o \ diff --git a/src/dlls/cmbasemonster.h b/src/dlls/cmbasemonster.h index 4c0c1d4..e9ae5c4 100644 --- a/src/dlls/cmbasemonster.h +++ b/src/dlls/cmbasemonster.h @@ -1331,4 +1331,31 @@ public: void IdleSound(void); }; +//========================================================= +// Otis +//========================================================= +class CMOtis : public CMBarney +{ +public: + void KeyValue(KeyValueData *pkvd); + + void Spawn(void); + void Precache(void); + void BarneyFirePistol(void); + void AlertSound(void); + void HandleAnimEvent(MonsterEvent_t *pEvent); + + int TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + + // Override these to set behavior + Schedule_t *GetSchedule(void); + + void TalkInit(void); + void TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + void Killed(entvars_t *pevAttacker, int iGib); + + int head; + int bodystate; +}; + #endif // BASEMONSTER_H diff --git a/src/dlls/combat.cpp b/src/dlls/combat.cpp index fb6add8..c40e8b8 100644 --- a/src/dlls/combat.cpp +++ b/src/dlls/combat.cpp @@ -1361,6 +1361,7 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin case BULLET_MONSTER_9MM: case BULLET_MONSTER_12MM: case BULLET_MONSTER_762: + case BULLET_MONSTER_357: default: MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc ); WRITE_BYTE( TE_TRACER ); @@ -1420,6 +1421,13 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin break; + case BULLET_MONSTER_357: + UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + case BULLET_NONE: // FIX UTIL_TraceAttack(pPlayer, pevAttacker, 50, vecDir, &tr, DMG_CLUB); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); @@ -1467,7 +1475,21 @@ void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShootin DecalGunshot( &tr, iBulletType ); } break; - + + case BULLET_MONSTER_762: + pMonster->TraceAttack(pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_MONSTER_357: + pMonster->TraceAttack(pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + case BULLET_NONE: // FIX pMonster->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); diff --git a/src/dlls/dllapi.cpp b/src/dlls/dllapi.cpp index 84fef09..441ab4d 100644 --- a/src/dlls/dllapi.cpp +++ b/src/dlls/dllapi.cpp @@ -154,6 +154,7 @@ monster_type_t monster_types[]= "monster_sentry", FALSE, "monster_gonome", FALSE, // Opposing Force Monsters "monster_male_assassin", FALSE, + "monster_otis", FALSE, "info_node", FALSE, // Nodes "info_node_air", FALSE, "", FALSE @@ -404,76 +405,69 @@ void check_player_dead( edict_t *pPlayer ) } else { - // Any messages from here should only be shown if allowed. - // Level 0 = Disabled - // Level 1 = All messages - // Level 2 = Only monster deaths - if (monster_show_deaths->value == 1) + // Suicide? + if ( pAttacker == pPlayer ) + sprintf( szMessage, "* %s commited suicide.\n", szPlayerName ); + // An entity killed this player. + else if ( ENTINDEX( pAttacker ) > 0 ) { - // Suicide? - if ( pAttacker == pPlayer ) - sprintf( szMessage, "* %s commited suicide.\n", szPlayerName ); - // An entity killed this player. - else if ( ENTINDEX( pAttacker ) > 0 ) - { - // Gather damage type and format death message - if ( g_DamageBits[ iPlayerIndex ] == DMG_GENERIC ) - sprintf( szMessage, "* %s died mysteriously.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_CRUSH ) - sprintf( szMessage, "* %s was smashed.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_BULLET ) - sprintf( szMessage, "* %s was shot.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLASH ) - sprintf( szMessage, "* %s lost it's jelly.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_BURN ) - sprintf( szMessage, "* %s burned to death.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_FREEZE ) - sprintf( szMessage, "* %s froze to death.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_FALL ) - sprintf( szMessage, "* %s broke it's bones.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_BLAST ) - sprintf( szMessage, "* %s blew up.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_CLUB ) - sprintf( szMessage, "* %s was crowbared.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SHOCK ) - sprintf( szMessage, "* %s was electrocuted.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SONIC ) - sprintf( szMessage, "* %s ears popped.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_ENERGYBEAM ) - sprintf( szMessage, "* %s saw the pretty lights.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] == DMG_NEVERGIB ) - sprintf( szMessage, "* %s had a painful death.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] == DMG_ALWAYSGIB ) - sprintf( szMessage, "* %s was gibbed.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWN ) - sprintf( szMessage, "* %s became too drunk.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_PARALYZE ) - sprintf( szMessage, "* %s was paralyzed.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_NERVEGAS ) - sprintf( szMessage, "* %s lost it's brain.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_POISON ) - sprintf( szMessage, "* %s had a slow death.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_RADIATION ) - sprintf( szMessage, "* %s went nuclear.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWNRECOVER ) - sprintf( szMessage, "* %s used too much flex tape.\n", szPlayerName ); // is this type of death even possible? - else if ( g_DamageBits[ iPlayerIndex ] & DMG_ACID ) - sprintf( szMessage, "* %s was melted.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWBURN ) - sprintf( szMessage, "* %s became a cake.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWFREEZE ) - sprintf( szMessage, "* %s died of hypothermia.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_MORTAR ) - sprintf( szMessage, "* %s blew his missile pet.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] == (1 << 30) ) // (1 << 30) = 1073741824. For custom death messages - sprintf( szMessage, "* %s %s.\n", szPlayerName, STRING( pAttacker->v.noise ) ); - else // other mods could have more DMG_ variants that aren't registered here. - sprintf( szMessage, "* %s deadly died.\n", szPlayerName ); - } - // the "world" killed this player - else - sprintf( szMessage, "* %s fell or drowned or something.\n", szPlayerName ); + // Gather damage type and format death message + if ( g_DamageBits[ iPlayerIndex ] == DMG_GENERIC ) + sprintf( szMessage, "* %s died mysteriously.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_CRUSH ) + sprintf( szMessage, "* %s was smashed.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_BULLET ) + sprintf( szMessage, "* %s was shot.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLASH ) + sprintf( szMessage, "* %s lost its jelly.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_BURN ) + sprintf( szMessage, "* %s burned to death.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_FREEZE ) + sprintf( szMessage, "* %s froze to death.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_FALL ) + sprintf( szMessage, "* %s broke its bones.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_BLAST ) + sprintf( szMessage, "* %s blew up.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_CLUB ) + sprintf( szMessage, "* %s was crowbared.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SHOCK ) + sprintf( szMessage, "* %s was electrocuted.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SONIC ) + sprintf( szMessage, "* %s ears popped.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_ENERGYBEAM ) + sprintf( szMessage, "* %s saw the pretty lights.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] == DMG_NEVERGIB ) + sprintf( szMessage, "* %s had a painful death.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] == DMG_ALWAYSGIB ) + sprintf( szMessage, "* %s was gibbed.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWN ) + sprintf( szMessage, "* %s became too drunk.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_PARALYZE ) + sprintf( szMessage, "* %s was paralyzed.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_NERVEGAS ) + sprintf( szMessage, "* %s lost its brain.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_POISON ) + sprintf( szMessage, "* %s had a slow death.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_RADIATION ) + sprintf( szMessage, "* %s went nuclear.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWNRECOVER ) + sprintf( szMessage, "* %s used too much flex tape.\n", szPlayerName ); // is this type of death even possible? + else if ( g_DamageBits[ iPlayerIndex ] & DMG_ACID ) + sprintf( szMessage, "* %s was melted.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWBURN ) + sprintf( szMessage, "* %s became a cake.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWFREEZE ) + sprintf( szMessage, "* %s died of hypothermia.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_MORTAR ) + sprintf( szMessage, "* %s blew its missile pet.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] == (1 << 30) ) // (1 << 30) = 1073741824. For custom death messages + sprintf( szMessage, "* %s %s.\n", szPlayerName, STRING( pAttacker->v.noise ) ); + else // other mods could have more DMG_ variants that aren't registered here. + sprintf( szMessage, "* %s deadly died.\n", szPlayerName ); } + // the "world" killed this player + else + sprintf( szMessage, "* %s fell or drowned or something.\n", szPlayerName ); } // Print the message @@ -623,6 +617,7 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i case 17: monsters[monster_index].pMonster = CreateClassPtr((CMSentry *)NULL); break; case 18: monsters[monster_index].pMonster = CreateClassPtr((CMGonome *)NULL); break; case 19: monsters[monster_index].pMonster = CreateClassPtr((CMMassn *)NULL); break; + case 20: monsters[monster_index].pMonster = CreateClassPtr((CMOtis *)NULL); break; } if (monsters[monster_index].pMonster == NULL) @@ -1300,6 +1295,7 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) CMSentry sentry; CMGonome gonome; CMMassn massn; + CMOtis otis; g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" ); @@ -1331,12 +1327,13 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) case 11: scientist.Precache(); break; case 12: snark.Precache(); break; case 13: zombie.Precache(); break; - case 14: gargantua.Precache(); break; + case 14: gargantua.Precache(); break; case 15: turret.Precache(); break; case 16: miniturret.Precache(); break; case 17: sentry.Precache(); break; case 18: gonome.Precache(); break; case 19: massn.Precache(); break; + case 20: otis.Precache(); break; } } } diff --git a/src/dlls/otis.cpp b/src/dlls/otis.cpp new file mode 100644 index 0000000..df81f42 --- /dev/null +++ b/src/dlls/otis.cpp @@ -0,0 +1,329 @@ +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// monster template +//========================================================= +// UNDONE: Holster weapon? + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "defaultai.h" +#include "weapons.h" + +#define NUM_OTIS_HEADS 2 // heads available for otis model + +#define GUN_GROUP 1 +#define HEAD_GROUP 2 + +#define HEAD_HAIR 0 +#define HEAD_BALD 1 + +#define GUN_NONE 0 +#define GUN_EAGLE 1 +#define GUN_DONUT 2 + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +// first flag is Otis dying for scripted sequences? +#define OTIS_AE_DRAW ( 2 ) +#define OTIS_AE_SHOOT ( 3 ) +#define OTIS_AE_HOLSTER ( 4 ) + +#define OTIS_BODY_GUNHOLSTERED 0 +#define OTIS_BODY_GUNDRAWN 1 +#define OTIS_BODY_DONUT 2 + +//========================================================= +// ALertSound - otis says "Freeze!" +//========================================================= +void CMOtis::AlertSound(void) +{ + if (m_hEnemy != 0) + { + if (FOkToSpeak()) + { + PlaySentence("OT_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE); + } + } +} + +//========================================================= +// BarneyFirePistol - shoots one round from the pistol at +// the enemy otis is facing. +//========================================================= +void CMOtis::BarneyFirePistol(void) +{ + Vector vecShootOrigin; + + UTIL_MakeVectors(pev->angles); + vecShootOrigin = pev->origin + Vector(0, 0, 55); + Vector vecShootDir = ShootAtEnemy(vecShootOrigin); + + Vector angDir = UTIL_VecToAngles(vecShootDir); + SetBlending(0, angDir.x); + pev->effects = EF_MUZZLEFLASH; + + FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_357); + + int pitchShift = RANDOM_LONG(0, 20); + + // Only shift about half the time + if (pitchShift > 10) + pitchShift = 0; + else + pitchShift -= 5; + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/desert_eagle_fire.wav", 1, ATTN_NORM, 0, 100 + pitchShift); + + // UNDONE: Reload? + m_cAmmoLoaded--;// take away a bullet! +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CMOtis::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch (pEvent->event) + { + case OTIS_AE_SHOOT: + BarneyFirePistol(); + break; + + case OTIS_AE_DRAW: + // otis' bodygroup switches here so he can pull gun from holster + // pev->body = OTIS_BODY_GUNDRAWN; + SetBodygroup( GUN_GROUP, GUN_EAGLE ); + m_fGunDrawn = TRUE; + break; + + case OTIS_AE_HOLSTER: + // change bodygroup to replace gun in holster + // pev->body = OTIS_BODY_GUNHOLSTERED; + SetBodygroup( GUN_GROUP, GUN_NONE ); + m_fGunDrawn = FALSE; + break; + + default: + CMBarney::HandleAnimEvent(pEvent); + } +} + +//========================================================= +// Spawn +//========================================================= +void CMOtis::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/otis.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->health = gSkillData.otisHealth; + pev->view_ofs = Vector(0, 0, 50);// position of the eyes relative to monster's origin. + m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello + m_MonsterState = MONSTERSTATE_NONE; + + pev->body = 0; // gun in holster + m_fGunDrawn = FALSE; + + m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + + // Make sure hands are white. + pev->skin = 0; + + // Select a random head. + if (head == -1) + { + SetBodygroup(HEAD_GROUP, RANDOM_LONG(0, NUM_OTIS_HEADS - 1)); + } + else + { + SetBodygroup(HEAD_GROUP, head); + } + + if (bodystate == -1) + { + SetBodygroup(GUN_GROUP, RANDOM_LONG(OTIS_BODY_GUNHOLSTERED, OTIS_BODY_GUNDRAWN)); // don't random donut + } + else + { + SetBodygroup(GUN_GROUP, bodystate); + } + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_otis" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Otis" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMOtis::Precache() +{ + PRECACHE_MODEL("models/otis.mdl"); + + PRECACHE_SOUND("weapons/desert_eagle_fire.wav"); + + PRECACHE_SOUND("barney/ba_pain1.wav"); + PRECACHE_SOUND("barney/ba_pain2.wav"); + PRECACHE_SOUND("barney/ba_pain3.wav"); + + PRECACHE_SOUND("barney/ba_die1.wav"); + PRECACHE_SOUND("barney/ba_die2.wav"); + PRECACHE_SOUND("barney/ba_die3.wav"); + + // every new otis must call this, otherwise + // when a level is loaded, nobody will talk (time is reset to 0) + TalkInit(); + CMTalkMonster::Precache(); +} + +// Init talk data +void CMOtis::TalkInit() +{ + CMTalkMonster::TalkInit(); + + // scientists speach group names (group names are in sentences.txt) + + m_szGrp[TLK_ANSWER] = "OT_ANSWER"; + m_szGrp[TLK_QUESTION] = "OT_QUESTION"; + m_szGrp[TLK_IDLE] = "OT_IDLE"; + m_szGrp[TLK_STARE] = "OT_STARE"; + m_szGrp[TLK_USE] = "OT_OK"; + m_szGrp[TLK_UNUSE] = "OT_WAIT"; + m_szGrp[TLK_STOP] = "OT_STOP"; + + m_szGrp[TLK_NOSHOOT] = "OT_SCARED"; + m_szGrp[TLK_HELLO] = "OT_HELLO"; + + m_szGrp[TLK_PLHURT1] = "!OT_CUREA"; + m_szGrp[TLK_PLHURT2] = "!OT_CUREB"; + m_szGrp[TLK_PLHURT3] = "!OT_CUREC"; + + m_szGrp[TLK_PHELLO] = NULL; + m_szGrp[TLK_PIDLE] = NULL; + m_szGrp[TLK_PQUESTION] = NULL; + + m_szGrp[TLK_SMELL] = "OT_SMELL"; + + m_szGrp[TLK_WOUND] = "OT_WOUND"; + m_szGrp[TLK_MORTAL] = "OT_MORTAL"; + + // get voice for head - just one otis voice for now + m_voicePitch = 100; +} + + +int CMOtis::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) +{ + // make sure friends talk about it if player hurts talkmonsters... + int ret = CMTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); + if (!IsAlive() || pev->deadflag == DEAD_DYING) + return ret; + + if (m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT)) + { + // This is a heurstic to determine if the player intended to harm me + // If I have an enemy, we can't establish intent (may just be crossfire) + if ( ( m_hEnemy != NULL ) && UTIL_IsPlayer(m_hEnemy) ) + { + Remember( bits_MEMORY_PROVOKED ); + } + } + + return ret; +} + +void CMOtis::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + switch (ptr->iHitgroup) + { + case HITGROUP_CHEST: + case HITGROUP_STOMACH: + if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST)) + { + flDamage = flDamage / 2; + } + break; + case 10: // Otis wears no helmet, so do not prevent taking headshot damage. + // always a head shot + ptr->iHitgroup = HITGROUP_HEAD; + break; + default: + break; + } + CMTalkMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType); +} + + +void CMOtis::Killed(entvars_t *pevAttacker, int iGib) +{ + if (GetBodygroup(GUN_GROUP) != OTIS_BODY_GUNHOLSTERED) + { + // drop the gun! + SetBodygroup(GUN_GROUP, OTIS_BODY_GUNHOLSTERED); + } + + CMTalkMonster::Killed(pevAttacker, iGib); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CMOtis::GetSchedule(void) +{ + if (HasConditions(bits_COND_ENEMY_DEAD) && FOkToSpeak()) + { + PlaySentence("OT_KILL", 4, VOL_NORM, ATTN_NORM); + } + + return CMBarney::GetSchedule(); +} + +void CMOtis::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "head")) + { + head = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CMBarney::KeyValue(pkvd); +} diff --git a/src/dlls/skill.cpp b/src/dlls/skill.cpp index 8abe246..d9a5308 100644 --- a/src/dlls/skill.cpp +++ b/src/dlls/skill.cpp @@ -78,11 +78,13 @@ skill_cfg_t skill_cfg[] = { {"sk_gonome_dmg_one_bite", &gSkillData.gonomeDmgOneBite}, {"sk_massassin_health", &gSkillData.massnHealth}, {"sk_massassin_kick", &gSkillData.massnDmgKick}, + {"sk_otis_health", &gSkillData.otisHealth}, {"sk_12mm_bullet", &gSkillData.monDmg9MM}, {"sk_9mmAR_bullet", &gSkillData.monDmgMP5}, {"sk_9mm_bullet", &gSkillData.monDmg12MM}, {"sk_9mmAR_grenade", &gSkillData.monDmgM203Grenade}, {"sk_762_bullet", &gSkillData.monDmg762}, + {"sk_357_bullet", &gSkillData.monDmg357}, {"sk_hornet_dmg", &gSkillData.monDmgHornet}, {"", NULL} }; @@ -229,12 +231,16 @@ void monster_skill_init(void) gSkillData.massnHealth = 50.0f; gSkillData.massnDmgKick = 25.0f; + // Otis + gSkillData.otisHealth = 35.0f; + // MONSTER WEAPONS gSkillData.monDmg9MM = 5.0f; gSkillData.monDmgMP5 = 4.0f; gSkillData.monDmg12MM = 10.0f; gSkillData.monDmgM203Grenade = 100.0f; gSkillData.monDmg762 = 100.0f; + gSkillData.monDmg357 = 40.0f; // HORNET gSkillData.monDmgHornet = 5.0f; diff --git a/src/dlls/skill.h b/src/dlls/skill.h index ba52559..b08b7bc 100644 --- a/src/dlls/skill.h +++ b/src/dlls/skill.h @@ -95,12 +95,15 @@ struct skilldata_t float massnHealth; float massnDmgKick; + float otisHealth; + // weapons shared by monsters float monDmg9MM; float monDmgMP5; float monDmg12MM; float monDmgM203Grenade; float monDmg762; + float monDmg357; float monDmgHornet; }; diff --git a/src/dlls/weapons.h b/src/dlls/weapons.h index b6f3dcb..1ef67cd 100644 --- a/src/dlls/weapons.h +++ b/src/dlls/weapons.h @@ -178,6 +178,7 @@ typedef enum BULLET_MONSTER_MP5, BULLET_MONSTER_12MM, BULLET_MONSTER_762, + BULLET_MONSTER_357, } Bullet;