Initial implementation of NSSquadMonster, squad management for NSTalkMonster based classes

This commit is contained in:
Marco Cawthorne 2023-06-02 19:27:16 -07:00
parent 40dc1267fa
commit 66745985d4
Signed by: eukara
GPG key ID: CE2032F0A2882A22
8 changed files with 288 additions and 374 deletions

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2023 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* 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
* 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
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define NSSQUADMONSTER_MAXMEMBERS 5
/** NSSquadMonster based NPCs are able to communicate strategies together. */
class
NSSquadMonster:NSMonster
{
public:
void NSSquadMonster(void);
#ifdef SERVER
/** Overridable: Called when this NPC became squad leader. */
virtual void HasBecomeSquadLeader(void);
/** Overridable: Called when this NPC joined a squad. */
virtual void HasJoinedSquad(void);
/** Returns true/false if they're in a squad. */
nonvirtual bool InSquad(void);
/** Returns whether or not they're the squad leader. */
nonvirtual bool IsSquadLeader(void);
/** Returns the leader of their squad. Invalid if none. */
nonvirtual NSSquadMonster GetSquadLeader(void);
/** Will find and attach to a Squad in the specified radius. */
nonvirtual void FindSquadNearMe(float);
/** Will add the specified NPC to this entity's current squad. */
nonvirtual void AddToSquad(NSSquadMonster);
/** Will remove the specified NPC from this entity's current squad. Can be called on self. */
nonvirtual void RemoveFromSquad(NSSquadMonster);
/** Returns the nearest available member of its squad. */
nonvirtual NSSquadMonster GetNearestSquadMember(void);
/** Returns the farthest available member of its squad. */
nonvirtual NSSquadMonster GetFarthestSquadMember(void);
#endif
#ifdef SERVER
private:
bool m_inSquad;
NSSquadMonster m_eSquadLeader;
/* stored only in the squad leader's memory */
NSSquadMonster m_eSquadMembers[NSSQUADMONSTER_MAXMEMBERS];
#endif
};

View file

@ -0,0 +1,216 @@
/*
* Copyright (c) 2023 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* 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
* 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
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
void
NSSquadMonster::NSSquadMonster(void)
{
#ifdef SERVER
m_inSquad = false;
m_eSquadLeader = __NULL__;
for (int i = 0; i < NSSQUADMONSTER_MAXMEMBERS; i++) {
m_eSquadMembers[i] = __NULL__;
}
#endif
}
#ifdef SERVER
void
NSSquadMonster::HasBecomeSquadLeader(void)
{
/* implemented by sub-class */
}
void
NSSquadMonster::HasJoinedSquad(void)
{
/* implemented by sub-class */
}
bool
NSSquadMonster::InSquad(void)
{
return m_inSquad;
}
bool
NSSquadMonster::IsSquadLeader(void)
{
if (m_eSquadLeader == this)
return true;
return false;
}
NSSquadMonster
NSSquadMonster::GetSquadLeader(void)
{
return m_eSquadLeader;
}
void
NSSquadMonster::FindSquadNearMe(float searchRadius)
{
entity searchResult = findradius(GetOrigin(), searchRadius);
while (searchResult) {
/* found someone just like us */
if (searchResult != this)
if (searchResult.classname == classname) {
NSSquadMonster squadMember = (NSSquadMonster)searchResult;
/* we found someone, they may not be in a squad (that's ok!)
as they will then create one. */
squadMember.AddToSquad(this);
return;
}
/* advance the chain. */
searchResult = searchResult.chain;
}
}
void
NSSquadMonster::AddToSquad(NSSquadMonster addMember)
{
NSSquadMonster startMember = __NULL__;
if (!addMember)
return;
/* no start? this monster just became a squad leader */
if (InSquad() == false) {
m_eSquadLeader = this;
startMember = this;
m_inSquad = true;
print(sprintf("%s (%d) became squad leader\n", classname, num_for_edict(this)));
HasBecomeSquadLeader();
}
startMember = GetSquadLeader();
/* fit the member into the nearest slot */
for (int i = 0; i < NSSQUADMONSTER_MAXMEMBERS; i++) {
if (startMember.m_eSquadMembers[i] == __NULL__) {
startMember.m_eSquadMembers[i] = addMember;
addMember.m_eSquadLeader = startMember;
addMember.m_inSquad = true;
addMember.HasJoinedSquad();
print(sprintf("%s (%d) added to squad, member %i\n", classname, num_for_edict(this), i));
return;
}
}
}
void
NSSquadMonster::RemoveFromSquad(NSSquadMonster toRemove)
{
NSSquadMonster startMember = __NULL__;
/* don't bother if not in squad. */
if (InSquad() == false)
return;
if (!toRemove)
return;
startMember = GetSquadLeader();
/* fit the member into the nearest slot */
for (int i = 0; i < NSSQUADMONSTER_MAXMEMBERS; i++) {
if (startMember.m_eSquadMembers[i] == toRemove) {
startMember.m_eSquadMembers[i] = __NULL__;
toRemove.m_eSquadLeader = __NULL__;
toRemove.m_inSquad = false;
return;
}
}
}
NSSquadMonster
NSSquadMonster::GetNearestSquadMember(void)
{
NSSquadMonster member = __NULL__;
NSSquadMonster nearestMember = __NULL__;
float dist = 0.0f;
float nearestDist = 99999.0f;
NSSquadMonster startMember;
if (InSquad() == false)
return __NULL__;
/* only leaders have the member list */
startMember = GetSquadLeader();
/* iterate through members... */
for (int i = 0; i < NSSQUADMONSTER_MAXMEMBERS; i++) {
member = startMember.m_eSquadMembers[i];
/* don't recognize self, ever */
if (member == __NULL__ || member == this)
continue;
/* check the distance from us to a valid member */
member = startMember.m_eSquadMembers[i];
dist = vlen(member.GetOrigin() - GetOrigin());
/* found one */
if (dist < nearestDist) {
nearestDist = dist;
nearestMember = member;
}
}
return nearestMember;
}
NSSquadMonster
NSSquadMonster::GetFarthestSquadMember(void)
{
NSSquadMonster member = __NULL__;
NSSquadMonster farthestMember = __NULL__;
float dist = 0.0f;
float farthestDist = 0.0f;
NSSquadMonster startMember;
if (InSquad() == false)
return __NULL__;
/* only leaders have the member list */
startMember = GetSquadLeader();
/* iterate through members... */
for (int i = 0; i < NSSQUADMONSTER_MAXMEMBERS; i++) {
member = startMember.m_eSquadMembers[i];
/* don't recognize self, ever */
if (member == __NULL__ || member == this)
continue;
/* check the distance from us to a valid member */
member = startMember.m_eSquadMembers[i];
dist = vlen(member.GetOrigin() - GetOrigin());
/* found one */
if (dist > farthestDist) {
farthestDist = dist;
farthestMember = member;
}
}
return farthestMember;
}
#endif

View file

@ -34,7 +34,7 @@ information and can speak more complicated dialogue.
They also can communicate with other NSTalkMonster based entities.
*/
class NSTalkMonster:NSMonster
class NSTalkMonster:NSSquadMonster
{
public:
void NSTalkMonster(void);

View file

@ -490,9 +490,10 @@ NSTalkMonster::FollowPlayer(void)
{
float flPlayerDist;
input_angles = vectoangles(m_eFollowingChain.origin - origin);
input_angles[0] = 0;
input_angles[1] = Math_FixDelta(input_angles[1]);
input_angles[2] = 0;
input_angles[0] = 0;
input_angles[1] = Math_FixDelta(input_angles[1]);
input_angles[2] = 0;
_LerpTurnToYaw(input_angles[1]);
/* for best results, we want to ignore the Z plane
this avoids the problem of a follower spinning
@ -521,7 +522,8 @@ NSTalkMonster::FollowPlayer(void)
}
}
input_movevalues[0] = m_flFollowSpeed;
if (DistanceFromYaw(vecParent) > 0.9f)
input_movevalues[0] = m_flFollowSpeed;
other = world;
traceline(origin, m_eFollowingChain.origin, MOVE_OTHERONLY, this);
@ -532,6 +534,7 @@ NSTalkMonster::FollowPlayer(void)
input_angles[0] = 0;
input_angles[1] = Math_FixDelta(input_angles[1]);
input_angles[2] = 0;
_LerpTurnToYaw(input_angles[1]);
} else {
m_vecLastUserPos = m_eFollowingChain.origin;
}
@ -617,7 +620,7 @@ NSTalkMonster::RunAI(void)
void
NSTalkMonster::Respawn(void)
{
NSMonster::Respawn();
super::Respawn();
m_eFollowing = world;
m_eFollowingChain = world;
}
@ -658,7 +661,7 @@ NSTalkMonster::SpawnKey(string strKey, string strValue)
m_talkFollow = strcat("!", strValue);
break;
default:
NSMonster::SpawnKey(strKey, strValue);
super::SpawnKey(strKey, strValue);
break;
}
}

View file

@ -1,119 +0,0 @@
#ifndef MAX_WEAPONS
#define MAX_WEAPONS 32
#endif
/** This class represents inventory items and weapons that you can directly interact with.
Trouble that's standing in the way of this taking off:
Level changes currently only support client entities from setting up
changelevel parameters. There is parm_string that we *could* use to
store weapon entity information in, but this will grow massively.
For the time being, we need to use the legacy system if we want to support
singleplayer.
*/
class
NSWeapon:NSRenderableEntity
{
private:
entity m_owner;
string m_strName; /* Full character name */
int m_iSlot;
int m_iSlotPos;
bool m_bAllowDropping;
int m_iWeight;
/* generic info */
int m_iClip1;
int m_iClip2;
float m_flPrimaryNext;
float m_flSecondaryNext;
float m_flLastFired;
public:
void NSWeapon(void);
/* inspired by GMOD API https://wiki.facepunch.com/gmod/Weapon */
/** Returns the model used to attach to players that wield this weapon */
virtual string GetPlayerModel(void);
/** Returns the model used to display in-world representations of this weapon. */
virtual string GetWorldModel(void);
/** Returns the name used in printed text for this weapon */
virtual string GetPrintName(void);
/** Returns if this weapon is allowed to be dropped. */
virtual bool AllowDropping(void);
/** Returns the framegroup used for the top-half of a player when aiming this weapon. */
virtual int GetPlayerAnim(void);
/** Returns a formatted obituary message.
Should contain two %s parameters, the first is the attacker and the second is the target. */
virtual string GetObituaryMessage(void);
/** Returns the weapon type. Check weapontype_t for details. */
virtual int GetType(void);
/** Returns primary attack clip */
virtual int GetClip1(void);
/** Returns secondary attack clip */
virtual int GetClip2(void);
/** Returns primary max clip size */
virtual int GetMaxClip1(void);
/** Returns secondary max clip size */
virtual int GetMaxClip2(void);
/** Returns the next time the primary mode can fire */
virtual float GetNextPrimaryFire(void);
/** Returns the next time the secondary mode can fire */
virtual float GetNextSecondaryFire(void);
/** Returns the slot/HUD category the weapon belongs in. */
virtual int GetSlot(void);
/** Returns the position the weapon belongs in of the slot specified in GetSlot() */
virtual int GetSlotPos(void);
/** Returns the 'weight', used for deciding what the next best weapon to switch to is. */
virtual int GetWeight(void);
/** Returns absolute time at which the weapon was last fired */
virtual float LastFireTime(void);
/** Sets the primary ammo clip count */
virtual void SetClip1(int);
/** Sets the secondary ammo clip count */
virtual void SetClip2(int);
/** Returns whether the weapon allows to being switched from when a better weighted weapon is picked up */
virtual bool AllowsAutoSwitchFrom(void);
/** Returns whether the weapon allows to being switched to when a better weighted weapon is picked up */
virtual bool AllowsAutoSwitchTo(void);
/** Returns if the weapon is empty, with no reserve ammonition */
virtual bool IsEmpty(void);
/** Returns if the weapon has ammo left in it. */
virtual bool HasAmmo(void);
/* calls */
/** Called to reload resources utilized by this weapon. */
virtual void Precache(void);
/** Called when the weapon was switched to from another. */
virtual void Draw(void);
/** Called right before switching to a new weapon. */
virtual void Holster(void);
/** Called whenever the command +attack is called by a client. */
virtual void Primary(void);
/** Called whenever the command +attack2 is called by a client. */
virtual void Secondary(void);
/** Called whenever the command +reload is called by a client. */
virtual void Reload(void);
/** Called whenever the no weapon command is called by a client. */
virtual void Release(void);
#ifdef CLIENT
/** Called before 3D world rendering is performed. */
virtual void ClientPredraw(void);
/** Called after 3D world rendering is performed. */
virtual void ClientPostdraw(void);
virtual void ReceiveEntity(float, float);
#endif
#ifdef SERVER
virtual float SendEntity(entity, float);
virtual void Touch(entity);
virtual void Respawn(void);
#endif
};

View file

@ -1,246 +0,0 @@
void
NSWeapon::NSWeapon(void)
{
}
/* calls */
void
NSWeapon::Precache(void)
{
}
void
NSWeapon::Draw(void)
{
}
void
NSWeapon::Holster(void)
{
}
void
NSWeapon::Primary(void)
{
}
void
NSWeapon::Secondary(void)
{
}
void
NSWeapon::Reload(void)
{
}
void
NSWeapon::Release(void)
{
}
#ifdef CLIENT
void
NSWeapon::ClientPredraw(void)
{
}
void
NSWeapon::ClientPostdraw(void)
{
}
void
NSWeapon::ReceiveEntity(float new, float flChanged)
{
}
#endif
#ifdef SERVER
float
NSWeapon::SendEntity(entity ePEnt, float flChanged)
{
/* if we have a model, assume we're a pickup */
if (modelindex) {
return super::SendEntity(ePEnt, flChanged);
}
/* don't network to anyone but the owner */
if (ePEnt != owner) {
return (false);
}
#if 0
WriteByte(MSG_ENTITY, ENT_WEAPON);
WriteFloat(MSG_ENTITY, flChanged);
WriteInt(MSG_ENTITY, m_iSlot);
WriteInt(MSG_ENTITY, m_iSlotPos);
WriteByte(MSG_ENTITY, m_bAllowDropping);
WriteInt(MSG_ENTITY, m_iWeight);
WriteInt(MSG_ENTITY, m_iClip1);
WriteInt(MSG_ENTITY, m_iClip2);
WriteFloat(MSG_ENTITY, m_flPrimaryNext);
WriteFloat(MSG_ENTITY, m_flSecondaryNext);
WriteFloat(MSG_ENTITY, m_flLastFired);
#endif
return (true);
}
void
NSWeapon::Touch(entity eToucher)
{
Hide();
SetSolid(SOLID_NOT);
}
void
NSWeapon::Respawn(void)
{
/* the weapons gets placed in-world */
SetModel(GetWorldModel());
SetSolid(SOLID_TRIGGER);
SetOrigin(GetSpawnOrigin());
}
#endif
/* get */
string
NSWeapon::GetPlayerModel(void)
{
return "models/error.vvm";
}
string
NSWeapon::GetWorldModel(void)
{
return "models/error.vvm";
}
string
NSWeapon::GetPrintName(void)
{
return m_strName;
}
int
NSWeapon::GetSlot(void)
{
return m_iSlot;
}
int
NSWeapon::GetSlotPos(void)
{
return m_iSlotPos;
}
bool
NSWeapon::AllowDropping(void)
{
return false;
}
int
NSWeapon::GetWeight(void)
{
return 0;
}
int
NSWeapon::GetPlayerAnim(void)
{
return 0;
}
bool
NSWeapon::IsEmpty(void)
{
return false;
}
string
NSWeapon::GetObituaryMessage(void)
{
return "%s killed %s with Unknown";
}
int
NSWeapon::GetType(void)
{
return 0;
}
int
NSWeapon::GetClip1(void)
{
return 0;
}
int
NSWeapon::GetClip2(void)
{
return 0;
}
int
NSWeapon::GetMaxClip1(void)
{
return 0;
}
int
NSWeapon::GetMaxClip2(void)
{
return 0;
}
float
NSWeapon::GetNextPrimaryFire(void)
{
return m_flPrimaryNext;
}
float
NSWeapon::GetNextSecondaryFire(void)
{
return m_flSecondaryNext;
}
float
NSWeapon::LastFireTime(void)
{
return m_flLastFired;
}
void
NSWeapon::SetClip1(int new_clip)
{
m_iClip1 = new_clip;
}
void
NSWeapon::SetClip2(int new_clip)
{
m_iClip1 = new_clip;
}
bool
NSWeapon::AllowsAutoSwitchFrom(void)
{
return true;
}
bool
NSWeapon::AllowsAutoSwitchTo(void)
{
return true;
}
bool
NSWeapon::HasAmmo(void)
{
return false;
}

View file

@ -62,6 +62,7 @@ string __fullspawndata;
#include "NSPointTrigger.h"
#include "NSNavAI.h"
#include "NSMonster.h"
#include "NSSquadMonster.h"
#include "NSTalkMonster.h"
#include "NSProjectile.h"
#include "NSItem.h"
@ -71,7 +72,6 @@ string __fullspawndata;
#include "../xr/defs.h"
#include "NSClient.h"
#include "NSClientSpectator.h"
#include "NSWeapon.h"
#include "NSClientPlayer.h"
#include "NSVehicle.h"

View file

@ -10,10 +10,10 @@ NSMoverEntity.qc
NSPhysicsEntity.qc
NSBrushTrigger.qc
NSPointTrigger.qc
NSWeapon.qc
NSVehicle.qc
NSNavAI.qc
NSMonster.qc
NSSquadMonster.qc
NSTalkMonster.qc
NSProjectile.qc
NSItem.qc