Skip to content

Commit

Permalink
Configurable squad and open doors capabilities for monsters
Browse files Browse the repository at this point in the history
  • Loading branch information
FreeSlave committed Feb 2, 2025
1 parent 1c46cd6 commit 1d6fac1
Show file tree
Hide file tree
Showing 27 changed files with 385 additions and 26 deletions.
4 changes: 2 additions & 2 deletions dlls/agrunt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,8 @@ void CAGrunt::Spawn()
SetMyHealth( gSkillData.agruntHealth );
SetMyFieldOfView(0.2f);// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
m_afCapability = 0;
m_afCapability |= bits_CAP_SQUAD;
SetMySquadCapabilities(bits_CAP_SQUAD);
SetMyCanOpenDoors(false);

m_HackedGunPos = Vector( 24.0f, 64.0f, 48.0f );

Expand Down
4 changes: 3 additions & 1 deletion dlls/barney.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,9 @@ void CBarney::SpawnImpl(const char* modelName, float health)
m_MonsterState = MONSTERSTATE_NONE;
m_HackedGunPos = Vector ( 0, 0, 55 );

m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD;
SetMySquadCapabilities();
SetMyCanOpenDoors(true);

TalkMonsterInit();
}
Expand Down
2 changes: 2 additions & 0 deletions dlls/basemonster.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ class CBaseMonster : public CBaseToggle
void KeyValue( KeyValueData *pkvd );
void Activate();
void SetMySize(const Vector& vecMin, const Vector& vecMax);
void SetMySquadCapabilities(int defaultCaps = 0);
void SetMyCanOpenDoors(bool enable);

// monster use function
void EXPORT MonsterUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
Expand Down
5 changes: 5 additions & 0 deletions dlls/cbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,11 @@ class CBaseToggle : public CBaseAnimating

#define bits_CAP_DOORS_GROUP (bits_CAP_USE | bits_CAP_AUTO_DOORS | bits_CAP_OPEN_DOORS)

#define bits_CAP_SQUAD_DENY ( 1 << 17 )
#define bits_CAP_SQUAD_ALLOW_OTHER_CLASSIFY ( 1 << 18 )
#define bits_CAP_SQUAD_SAME_CLASSNAME ( 1 << 19 )
#define bits_CAP_SQUAD_SAME_TEMPLATE ( 1 << 20 )

#define bits_CAP_MONSTERCLIPPED ( 1 << 31 )

// used by suit voice to indicate damage sustained and repaired type to player
Expand Down
4 changes: 3 additions & 1 deletion dlls/drillsergeant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ void CDrillSergeant::Spawn()
SetMyFieldOfView(VIEW_FIELD_WIDE); // NOTE: we need a wide field of view so npc will notice player and say hello
m_MonsterState = MONSTERSTATE_NONE;

m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD;
SetMySquadCapabilities();
SetMyCanOpenDoors(true);

TalkMonsterInit();
}
Expand Down
32 changes: 32 additions & 0 deletions dlls/ent_templates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,38 @@ void EntTemplateSystem::AddTemplateFromJsonValue(const char* name, rapidjson::Va
}
}

{
auto it = value.FindMember("squad_capability");
if (it != value.MemberEnd())
{
if (it->value.IsBool())
{
SquadCapabilities caps;
caps.canRecruit = it->value.GetBool();
entTemplate.SetSquadCapabilities(caps);
}
else if (it->value.IsObject())
{
Value& obj = it->value;
SquadCapabilities caps;
UpdatePropertyFromJson(caps.canRecruit, obj, "can_recruit");
UpdatePropertyFromJson(caps.denyRecruiting, obj, "deny_recruiting");
UpdatePropertyFromJson(caps.allowDifferentClassification, obj, "allow_different_classification");
UpdatePropertyFromJson(caps.requireSameClassname, obj, "require_same_classname");
UpdatePropertyFromJson(caps.requireSameEntTemplate, obj, "require_same_ent_template");
entTemplate.SetSquadCapabilities(caps);
}
}
}

{
auto it = value.FindMember("open_door_capability");
if (it != value.MemberEnd())
{
entTemplate.SetCanOpenDoors(it->value.GetBool());
}
}

_entTemplates[templateName] = entTemplate;
}

Expand Down
31 changes: 30 additions & 1 deletion dlls/ent_templates.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@
#include "visuals.h"
#include "soundscripts.h"
#include "json_config.h"
#include "tribool.h"

#include <map>
#include <string>
#include <utility>
#include <vector>

struct SquadCapabilities
{
tribool canRecruit;
tribool denyRecruiting;
tribool allowDifferentClassification;
tribool requireSameClassname;
tribool requireSameEntTemplate;
};

struct EntTemplate
{
public:
Expand Down Expand Up @@ -143,7 +153,7 @@ struct EntTemplate
return (_defined & SIZEFORGRAPPLE_DEFINED) != 0;
}
int SizeForGrapple() const {
return _sizeForGrapple;const char* SoundReplacementFor(const char* sound);
return _sizeForGrapple;
}
void SetSizeForGrapple(int sizeForGrapple)
{
Expand All @@ -155,6 +165,23 @@ struct EntTemplate
void SetSpeechPrefix(const std::string& speechPrefix) {
_speechPrefix = speechPrefix;
}

SquadCapabilities GetSquadCapabilities() const {
return _squadCapabilities;
}
void SetSquadCapabilities(const SquadCapabilities& caps) {
_squadCapabilities = caps;
}

bool IsOpenDoorCapabilityDefined() const {
return !indeterminate(_openDoorCapability);
}
bool CanOpenDoors() const {
return (bool)_openDoorCapability;
}
void SetCanOpenDoors(bool enable) {
_openDoorCapability = enable;
}
private:
std::map<std::string, std::string> _soundScripts;
std::map<std::string, std::string> _visuals;
Expand Down Expand Up @@ -190,6 +217,8 @@ struct EntTemplate
short _sizeForGrapple = 0;

std::string _speechPrefix;
SquadCapabilities _squadCapabilities;
tribool _openDoorCapability;
};

class EntTemplateSystem : public JSONConfig
Expand Down
4 changes: 3 additions & 1 deletion dlls/fgrunt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1963,7 +1963,9 @@ void CHFGrunt::SpawnHelper(const char *defaultModel, float defaultHealth)
m_MonsterState = MONSTERSTATE_NONE;
m_flNextGrenadeCheck = gpGlobals->time + 1;

m_afCapability = bits_CAP_HEAR | bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD;
SetMySquadCapabilities(bits_CAP_SQUAD);
SetMyCanOpenDoors(true);

m_fEnemyEluded = false;
m_fFirstEncounter = true;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
Expand Down
1 change: 1 addition & 0 deletions dlls/genericmonster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ void CGenericMonster::Spawn()
{
m_afCapability = bits_CAP_TURN_HEAD;
}
SetMyCanOpenDoors(false);

m_flIdealYaw = m_flCurrentYaw = 0;

Expand Down
2 changes: 1 addition & 1 deletion dlls/gonome.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ void CGonome::Spawn()
SetMyHealth( gSkillData.gonomeHealth );
SetMyFieldOfView(0.2f);// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
m_afCapability = bits_CAP_DOORS_GROUP;
SetMyCanOpenDoors(true);

m_flNextThrowTime = gpGlobals->time;

Expand Down
4 changes: 3 additions & 1 deletion dlls/hassassin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,9 @@ void CHAssassin::Spawn()
SetMyHealth( gSkillData.hassassinHealth );
SetMyFieldOfView(VIEW_FIELD_WIDE); // indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
m_afCapability = bits_CAP_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_MELEE_ATTACK1;
SetMySquadCapabilities();
SetMyCanOpenDoors(true);
pev->friction = 1;

m_HackedGunPos = Vector( 0, 24, 48 );
Expand Down
4 changes: 3 additions & 1 deletion dlls/hgrunt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,9 @@ void CHGrunt::SpawnHelper(const char* modelName, int health, int bloodColor)
m_flNextPainTime = gpGlobals->time;
m_iSentence = HGRUNT_SENT_NONE;

m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_TURN_HEAD;
SetMySquadCapabilities(bits_CAP_SQUAD);
SetMyCanOpenDoors(true);

m_fEnemyEluded = false;
m_fFirstEncounter = true;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.
Expand Down
3 changes: 2 additions & 1 deletion dlls/houndeye.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,8 @@ void CHoundeye::Spawn()
m_MonsterState = MONSTERSTATE_NONE;
m_iAsleep = HOUNDEYE_AWAKE; // everyone spawns awake
m_iBlink = HOUNDEYE_BLINK;
m_afCapability |= bits_CAP_SQUAD;
SetMySquadCapabilities(bits_CAP_SQUAD|bits_CAP_SQUAD_SAME_CLASSNAME);
SetMyCanOpenDoors(false);

MonsterInit();
}
Expand Down
4 changes: 3 additions & 1 deletion dlls/hwgrunt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ void CHWGrunt::Spawn()
m_MonsterState = MONSTERSTATE_NONE;
m_flNextPainTime = gpGlobals->time;

m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_TURN_HEAD;
SetMySquadCapabilities(bits_CAP_SQUAD);
SetMyCanOpenDoors(true);

m_fEnemyEluded = false;

Expand Down
7 changes: 3 additions & 4 deletions dlls/islave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1405,10 +1405,9 @@ void CISlave::Spawn()
pev->view_ofs = Vector( 0, 0, 64 );// position of the eyes relative to monster's origin.
SetMyFieldOfView(VIEW_FIELD_WIDE); // NOTE: we need a wide field of view so npc will notice player and say hello
m_MonsterState = MONSTERSTATE_NONE;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_RANGE_ATTACK2 | bits_CAP_DOORS_GROUP;

if (g_modFeatures.vortigaunt_squad)
m_afCapability |= bits_CAP_SQUAD;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_RANGE_ATTACK2;
SetMySquadCapabilities(g_modFeatures.vortigaunt_squad ? bits_CAP_SQUAD : 0);
SetMyCanOpenDoors(true);

m_voicePitch = RANDOM_LONG( 85, 110 );

Expand Down
47 changes: 47 additions & 0 deletions dlls/monsters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3605,6 +3605,12 @@ void CBaseMonster::ReportAIState( ALERT_TYPE level )
if (HasConditions(bits_COND_SEE_ENEMY))
ALERT(level, "Sees enemy; ");

if (FBitSet(m_afCapability, bits_CAP_DOORS_GROUP))
ALERT(level, "Can open doors; ");

if (FBitSet(m_afCapability, bits_CAP_SQUAD))
ALERT(level, "Can form squads; ");

if (shouldReportRoute)
{
int iMyNode = WorldGraph.FindNearestNode( pev->origin, this );
Expand Down Expand Up @@ -3755,6 +3761,47 @@ void CBaseMonster::SetMySize(const Vector &vecMin, const Vector &vecMax)
UTIL_SetSize(pev, m_minHullSize == g_vecZero ? vecMins : m_minHullSize, m_maxHullSize == g_vecZero ? vecMaxs : m_maxHullSize);
}

static void SetCapFromTriBool(int& ret, tribool b, int cap)
{
if (!indeterminate(b))
{
if (b)
{
SetBits(ret, cap);
}
else
{
ClearBits(ret, cap);
}
}
}

void CBaseMonster::SetMySquadCapabilities(int defaultCaps)
{
m_afCapability |= defaultCaps;
const EntTemplate* entTemplate = GetMyEntTemplate();
if (entTemplate)
{
SquadCapabilities squadCaps = entTemplate->GetSquadCapabilities();
SetCapFromTriBool(m_afCapability, squadCaps.canRecruit, bits_CAP_SQUAD);
SetCapFromTriBool(m_afCapability, squadCaps.denyRecruiting, bits_CAP_SQUAD_DENY);
SetCapFromTriBool(m_afCapability, squadCaps.allowDifferentClassification, bits_CAP_SQUAD_ALLOW_OTHER_CLASSIFY);
SetCapFromTriBool(m_afCapability, squadCaps.requireSameClassname, bits_CAP_SQUAD_SAME_CLASSNAME);
SetCapFromTriBool(m_afCapability, squadCaps.requireSameEntTemplate, bits_CAP_SQUAD_SAME_TEMPLATE);
}
}

void CBaseMonster::SetMyCanOpenDoors(bool enable)
{
const EntTemplate* entTemplate = GetMyEntTemplate();
if (entTemplate && entTemplate->IsOpenDoorCapabilityDefined())
{
enable = entTemplate->CanOpenDoors();
}
if (enable)
m_afCapability |= bits_CAP_DOORS_GROUP;
}

//=========================================================
// FCheckAITrigger - checks the monster's AI Trigger Conditions,
// if there is a condition, then checks to see if condition is
Expand Down
3 changes: 2 additions & 1 deletion dlls/pitdrone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,8 @@ void CPitdrone::Spawn()
SetMyHealth( gSkillData.pitdroneHealth );
SetMyFieldOfView(0.2f);// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
m_afCapability = bits_CAP_SQUAD;
SetMySquadCapabilities(bits_CAP_SQUAD);
SetMyCanOpenDoors(false);

m_flNextSpitTime = gpGlobals->time;

Expand Down
4 changes: 3 additions & 1 deletion dlls/recruit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ void CRecruit::Spawn()
SetMyFieldOfView(VIEW_FIELD_WIDE); // NOTE: we need a wide field of view so npc will notice player and say hello
m_MonsterState = MONSTERSTATE_NONE;

m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD;
SetMySquadCapabilities();
SetMyCanOpenDoors(true);

TalkMonsterInit();
}
Expand Down
4 changes: 3 additions & 1 deletion dlls/scientist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,9 @@ void CScientist::SciSpawnHelper(const char* modelName, float health)

//m_flDistTooFar = 256.0;

m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE;
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD;
SetMySquadCapabilities();
SetMyCanOpenDoors(true);
}

int CScientist::GetDefaultVoicePitch()
Expand Down
13 changes: 8 additions & 5 deletions dlls/squadmonster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,8 @@ int CSquadMonster::SquadRecruit( int searchRadius, int maxMembers )

if( pRecruit )
{
if( !pRecruit->InSquad() && pRecruit->Classify() == iMyClass && pRecruit != this )
const int rel = pRecruit->IRelationship(this);
if( !pRecruit->InSquad() && (rel == R_AL || rel == R_NO) && pRecruit != this && (FBitSet(m_afCapability, bits_CAP_SQUAD_ALLOW_OTHER_CLASSIFY) || pRecruit->Classify() == iMyClass) )
{
// minimum protection here against user error.in worldcraft.
if( !SquadAdd( pRecruit ) )
Expand All @@ -411,10 +412,12 @@ int CSquadMonster::SquadRecruit( int searchRadius, int maxMembers )

if( pRecruit && pRecruit != this && pRecruit->IsFullyAlive() && !pRecruit->m_pCine && !FBitSet(pRecruit->pev->spawnflags, SF_MONSTER_PRISONER) )
{
const int rel = pRecruit->IRelationship(this);
// Can we recruit this guy?
if( !pRecruit->InSquad() && pRecruit->Classify() == iMyClass &&
( ( DefaultClassify() != CLASS_ALIEN_MONSTER ) || FStrEq( STRING( pev->classname ), STRING( pRecruit->pev->classname ) ) ) &&
FStringNull( pRecruit->pev->netname ) )
if( !pRecruit->InSquad() && (rel == R_AL || rel == R_NO) && FStringNull(pRecruit->pev->netname) && !FBitSet(pRecruit->m_afCapability, bits_CAP_SQUAD_DENY) &&
(FBitSet(m_afCapability, bits_CAP_SQUAD_ALLOW_OTHER_CLASSIFY) || pRecruit->Classify() == iMyClass) &&
((!FBitSet(m_afCapability, bits_CAP_SQUAD_SAME_CLASSNAME) && !FBitSet(pRecruit->m_afCapability, bits_CAP_SQUAD_SAME_CLASSNAME)) || FStrEq( STRING( pev->classname ), STRING( pRecruit->pev->classname ) )) &&
((!FBitSet(m_afCapability, bits_CAP_SQUAD_SAME_TEMPLATE) && !FBitSet(pRecruit->m_afCapability, bits_CAP_SQUAD_SAME_TEMPLATE)) || FStrEq(m_entTemplate, pRecruit->m_entTemplate)) )
{
TraceResult tr;
UTIL_TraceLine( pev->origin + pev->view_ofs, pRecruit->pev->origin + pev->view_ofs, ignore_monsters, pRecruit->edict(), &tr );// try to hit recruit with a traceline.
Expand Down Expand Up @@ -471,7 +474,7 @@ void CSquadMonster::StartMonster( void )
{
CBaseMonster::StartMonster();

if( ( m_afCapability & bits_CAP_SQUAD ) && !InSquad() )
if( ( FBitSet(m_afCapability, bits_CAP_SQUAD) ) && !InSquad() )
{
if( !FStringNull( pev->netname ) )
{
Expand Down
13 changes: 13 additions & 0 deletions dlls/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,19 @@ inline bool FStrEq(const char*sz1, const char*sz2)
return (strcmp(sz1, sz2) == 0);
}

inline bool FStrEq(string_t s1, string_t s2)
{
if (s1 == s2)
{
return true;
}
if (!FStringNull(s1) && !FStringNull(s2))
{
return FStrEq(STRING(s1), STRING(s2));
}
return false;
}

inline bool FClassnameIs(edict_t* pent, const char* szClassname)
{
return FStrEq(STRING(VARS(pent)->classname), szClassname);
Expand Down
Loading

0 comments on commit 1d6fac1

Please sign in to comment.