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:
parent
099dba11f6
commit
daf8512aa2
12 changed files with 164 additions and 67 deletions
|
@ -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";
|
||||
}
|
|
@ -7,4 +7,5 @@ fx_breakmodel.qc
|
|||
fx_corpse.qc
|
||||
fx_gibhuman.qc
|
||||
fx_impact.qc
|
||||
TestWeapon.qc
|
||||
#endlist
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
};
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue