NSTalkMonster: Don't greet non-friendly players.

NSMonster: Improvements to alerting, turning and what to do when they lose their target.
NSInteractiveSurface: Unbreak it by allowing it to spawn in CSQC
Server: Spawn AI nodes for info_player_{start,deathmatch} when no real nodes are present
This commit is contained in:
Marco Cawthorne 2023-02-06 16:41:19 -08:00
parent 099dba11f6
commit daf8512aa2
Signed by: eukara
GPG Key ID: CE2032F0A2882A22
12 changed files with 164 additions and 67 deletions

View File

@ -5,8 +5,6 @@ public:
void TestWeapon(void);
virtual string GetWorldModel(void);
private:
};
void
@ -18,5 +16,5 @@ TestWeapon::TestWeapon(void)
string
TestWeapon::GetWorldModel(void)
{
return "models/error.vvm";
}

View File

@ -7,4 +7,5 @@ fx_breakmodel.qc
fx_corpse.qc
fx_gibhuman.qc
fx_impact.qc
TestWeapon.qc
#endlist

View File

@ -68,9 +68,16 @@ public:
virtual bool FocusCheck(vector,vector);
virtual void RendererRestarted(void);
virtual bool CanSpawn(bool);
};
bool
NSInteractiveSurface::CanSpawn(bool clientSide)
{
return true;
}
bool
NSInteractiveSurface::FocusCheck(vector vecViewPos, vector vecViewAng)
{

View File

@ -174,10 +174,12 @@ NSTraceAttack::Fire(void)
m_iMultiValue = 0;
while (m_iShots > 0) {
if (m_eOwner.flags & FL_CLIENT)
if (m_eOwner.flags & FL_CLIENT) {
vecDir = aim(m_eOwner, 100000);
else
vecDir = m_eOwner.v_angle;
} else {
makevectors(m_eOwner.v_angle);
vecDir = v_forward;
}
#ifndef BULLETPATTERNS
vecDir += random(-1,1) * m_vecSpread[0] * v_right;

View File

@ -146,36 +146,39 @@ Node_AutoLink(node_t *new)
}
}
static void
Nodes_InsertNodeForClassname(string classn)
{
for (entity a = world; (a = find(a, ::classname, classn));) {
int iID = g_iNodes++;
g_pNodes = (node_t *)memrealloc(g_pNodes, sizeof(node_t), iID, g_iNodes);
node_t *n = g_pNodes + iID;
n->origin = a.origin;
n->nb = __NULL__;
n->nb_count = 0;
n->radius = 32;
Node_AutoLink(n);
}
}
void
Nodes_BuildFromEnts(void)
{
print("rebuilding node tree...");
/* run through the ents and rebuild the tree */
for (entity a = world; (a = find(a, ::classname, "info_node"));) {
int iID = g_iNodes++;
g_pNodes = (node_t *)memrealloc(g_pNodes, sizeof(node_t), iID, g_iNodes);
node_t *n = g_pNodes + iID;
n->origin = a.origin;
n->nb = __NULL__;
n->nb_count = 0;
n->radius = 32;
Node_AutoLink(n);
}
Nodes_InsertNodeForClassname("info_node");
#if 0
for (entity a = world; (a = find(a, ::classname, "scripted_sequence"));) {
int iID = g_iNodes++;
g_pNodes = (node_t *)memrealloc(g_pNodes, sizeof(node_t), iID, g_iNodes);
node_t *n = g_pNodes + iID;
n->origin = a.origin;
n->nb = __NULL__;
n->nb_count = 0;
n->radius = 32;
Node_AutoLink(n);
}
Nodes_InsertNodeForClassname("scripted_sequence");
#endif
/* last resort */
if (g_iNodes == 0)
Nodes_InsertNodeForClassname("info_player_start");
if (g_iNodes == 0)
Nodes_InsertNodeForClassname("info_player_deathmatch");
print(sprintf("%i possible nodes found in %s\n", g_iNodes, mapname));
if (g_iNodes) {

View File

@ -288,6 +288,8 @@ public:
virtual void IdleNoise(void);
/** Overridable: Called when they start falling. */
virtual void FallNoise(void);
/** Overridable: Called when this monster gets 'alerted' to something new. */
virtual void AlertNoise(void);
/** Returns if they're considered alive. */
virtual bool IsAlive(void);

View File

@ -216,6 +216,11 @@ NSMonster::IdleNoise(void)
{
}
void
NSMonster::AlertNoise(void)
{
}
bool
NSMonster::IsFriend(int al)
{
@ -300,15 +305,21 @@ NSMonster::IsValidEnemy(entity enny)
void
NSMonster::SeeThink(void)
{
if (m_flAttackThink < time)
if (m_eEnemy) {
/* check if we should invalidate current enemy */
if (IsValidEnemy(m_eEnemy))
return;
/* enemy is not valid anymore, reset it, clear route and search for new enemy */
RouteClear();
makevectors(angles);
RouteToPosition(m_eEnemy.origin + (v_forward * -64));
m_flSequenceSpeed = GetWalkSpeed();
SetState(MONSTER_ALERT);
m_eEnemy = __NULL__;
RouteClear();
m_flSeeTime = 0;
}
@ -332,7 +343,7 @@ NSMonster::SeeThink(void)
continue;
/* first, is the potential enemy in our field of view? */
makevectors(v_angle);
makevectors(angles);
vector v = normalize(w.origin - origin);
float flDot = v * v_forward;
@ -372,6 +383,33 @@ NSMonster::GetRunSpeed(void)
return 140;
}
void
NSMonster::_LerpTurnToEnemy(float flSpeed)
{
if (GetState() != MONSTER_AIMING)
return;
if (!m_eEnemy)
return;
vector vecWishAngle = vectoangles(m_eEnemy.origin - origin);
vector vecFinalAngle;
float flLerp = frametime * flSpeed;
if (flLerp > 1.0)
flLerp = 0.95;
makevectors(vecWishAngle);
vecFinalAngle = v_forward;
makevectors( v_angle );
vecFinalAngle[0] = Math_Lerp( v_forward[0], vecFinalAngle[0], flLerp );
vecFinalAngle[1] = Math_Lerp( v_forward[1], vecFinalAngle[1], flLerp );
vecFinalAngle[2] = Math_Lerp( v_forward[2], vecFinalAngle[2], flLerp );
vecWishAngle = vectoangles( vecFinalAngle );
v_angle[1] = Math_FixDelta(vecWishAngle[1]);
angles[1] = input_angles[1] = v_angle[1];
}
void
NSMonster::AttackThink(void)
{
@ -412,6 +450,9 @@ NSMonster::AttackThink(void)
if (GetState() == MONSTER_AIMING) {
int m;
_LerpTurnToEnemy(1000);
if (MeleeCondition() == TRUE)
m = AttackMelee();
else {
@ -419,8 +460,7 @@ NSMonster::AttackThink(void)
/* if we don't have the desired attack mode, walk */
if (m == FALSE)
SetState(MONSTER_CHASING);
SetState(MONSTER_CHASING);
}
}
}
@ -571,6 +611,9 @@ NSMonster::AnimationUpdate(void)
if (GetState() == MONSTER_DEAD)
return;
if (GetState() == MONSTER_AIMING)
return;
float spvel = vlen(velocity);
float midspeed = GetWalkSpeed() + ((GetRunSpeed() - GetWalkSpeed()) * 0.5f);
@ -700,6 +743,7 @@ NSMonster::Physics(void)
RunAI();
}
_LerpTurnToEnemy(30);
AnimationUpdate();
}
@ -1051,6 +1095,9 @@ NSMonster_ReadEntity(bool new)
}
#else
var int autocvar_ai_alertdebug = 0;
var float g_monsteralert_timer;
void
NSMonster_AlertEnemyAlliance(vector pos, float radius, int alliance)
@ -1059,33 +1106,53 @@ NSMonster_AlertEnemyAlliance(vector pos, float radius, int alliance)
if (g_monsteralert_timer > time)
return;
if (autocvar_ai_alertdebug)
print(sprintf("AI alert from %v with radius %f and alliance %i\n", pos, radius, alliance));
for (entity w = world; (w = findfloat(w, ::takedamage, DAMAGE_YES));) {
/* out of radius */
if (vlen(pos - w.origin) > radius)
if (vlen(pos - w.origin) > radius) {
continue;
}
/* only target monsters */
if (!(w.flags & FL_MONSTER))
if (!(w.flags & FL_MONSTER)) {
if (autocvar_ai_alertdebug)
print(sprintf("\t%S is not a monster\n", w.classname));
continue;
}
NSMonster f = (NSMonster)w;
/* they already got a target of some kind */
if (f.m_eEnemy)
if (f.m_eEnemy) {
if (autocvar_ai_alertdebug)
print(sprintf("\t%S already has a target\n", w.classname));
continue;
}
/* if they're our friend... ignore*/
if (f.IsFriend(alliance))
if (f.IsFriend(alliance)) {
if (autocvar_ai_alertdebug)
print(sprintf("\t%S is friend of alliance %i\n", w.classname, alliance));
continue;
}
/* if the monster is dead... ignore */
if (f.health <= 0)
if (f.health <= 0) {
if (autocvar_ai_alertdebug)
print(sprintf("\t%S is dead, cannot be alerted\n", w.classname));
continue;
}
if (autocvar_ai_alertdebug)
print(sprintf("\twe're alerting %S to go to %v\n", w.classname, pos));
/* we've heard a noise. investigate the location */
f.RouteClear();
f.RouteToPosition(pos);
f.m_flSequenceSpeed = 140;
f.m_flSequenceSpeed = f.GetWalkSpeed();
f.AlertNoise();
}
g_monsteralert_timer = time + 0.5f;

View File

@ -309,6 +309,11 @@ NSTalkMonster::TalkPlayerIdle(void)
return;
for (entity p = world; (p = find(p, ::classname, "player"));) {
if (!IsFriend(p.m_iAlliance)) {
continue;
}
/* 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 */

View File

@ -4,6 +4,14 @@
/** 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
@ -104,5 +112,8 @@ public:
#ifdef SERVER
virtual float SendEntity(entity, float);
virtual void Touch(entity);
virtual void Respawn(void);
#endif
};

View File

@ -71,6 +71,36 @@ NSWeapon::SendEntity(entity ePEnt, float flChanged)
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
}
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

View File

@ -19,37 +19,7 @@
string
Colors_RGB8_to_HEX(vector color)
{
string out = "^x";
for (int i = 0; i < 3; i++) {
string a = "";
float b = rint(color[i] * 15);
switch (b) {
case 10:
a = "A";
break;
case 11:
a = "B";
break;
case 12:
a = "C";
break;
case 13:
a = "D";
break;
case 14:
a = "E";
break;
case 15:
a = "F";
break;
default:
a = ftos(b);
}
out = sprintf("%s%s", out, a);
}
return out;
return sprintf("^x%x%x%x", color[0] * 15, color[1] * 15, color[2] * 15);
}
/** Takes a 0-255 based color vector and returns the hexadecimal equivalent

View File

@ -25,6 +25,7 @@ typedef enum
ENT_MONSTER, /**< of type NSMonster */
ENT_TALKMONSTER, /**< of type NSTalkMonster */
ENT_PLAYER, /**< of type NSClientPlayer */
ENT_WEAPON, /**< of type NSWeapon */
ENT_SPECTATOR, /**< of type NSClientSpectator */
ENT_AMBIENTSOUND, /**< of type ambient_generic */
ENT_BEAM, /**< of type env_beam */