added vscripts core and the moving npc from that asian dude. added includes as well because we already have something called vscripts.inc i guess
This commit is contained in:
parent
ff34a79347
commit
4d1867a448
506
VScripts/scripting/MovingNPC.sp
Normal file
506
VScripts/scripting/MovingNPC.sp
Normal file
@ -0,0 +1,506 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#define PLUGIN_AUTHOR "Cloud Strife"
|
||||
#define PLUGIN_VERSION "1.0b"
|
||||
|
||||
#include <sourcemod>
|
||||
#include <vscripts/MovingNPC>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
bool enabled = false;
|
||||
|
||||
ArrayList g_aMovingNpc = null;
|
||||
ArrayList g_aNpcConfigNT = null;
|
||||
StringMap g_mNpcConfig = null;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "MovingNPC vscripts",
|
||||
author = PLUGIN_AUTHOR,
|
||||
description = "MovingNPC vscripts",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "https://steamcommunity.com/id/cloudstrifeua/"
|
||||
};
|
||||
|
||||
//TODO: Add start and stop triggers
|
||||
|
||||
methodmap MovingNpcConfig < Basic
|
||||
{
|
||||
public MovingNpcConfig()
|
||||
{
|
||||
Basic myclass = new Basic();
|
||||
myclass.SetFloat("fRate", 0.1);
|
||||
myclass.SetFloat("fDistance", 5000.0);
|
||||
myclass.SetFloat("fRetarget", 7.5);
|
||||
myclass.SetFloat("fForward", 1.0);
|
||||
myclass.SetFloat("fTurning", 0.5);
|
||||
myclass.SetFloat("fLifetime", 0.0);
|
||||
myclass.SetString("sThrusterFwd", "");
|
||||
myclass.SetString("sThrusterSide", "");
|
||||
myclass.SetString("sAttachment", "");
|
||||
myclass.SetString("sTemplate", "");
|
||||
return view_as<MovingNpcConfig>(myclass);
|
||||
}
|
||||
property float lifetime
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fLifetime");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fLifetime", val);
|
||||
}
|
||||
}
|
||||
property float rate
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fRate");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fRate", val);
|
||||
}
|
||||
}
|
||||
property float distance
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fDistance");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fDistance", val);
|
||||
}
|
||||
}
|
||||
property float retarget
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fRetarget");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fRetarget", val);
|
||||
}
|
||||
}
|
||||
property float forward_factor
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fForward");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fForward", val);
|
||||
}
|
||||
}
|
||||
property float turning_factor
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fTurning");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fTurning", val);
|
||||
}
|
||||
}
|
||||
public int GetThrusterFwd(char[] buffer, int size)
|
||||
{
|
||||
return this.GetString("sThrusterFwd", buffer, size);
|
||||
}
|
||||
public void SetThrusterFwd(const char[] sThruster)
|
||||
{
|
||||
this.SetString("sThrusterFwd", sThruster);
|
||||
}
|
||||
public int GetThrusterSide(char[] buffer, int size)
|
||||
{
|
||||
return this.GetString("sThrusterSide", buffer, size);
|
||||
}
|
||||
public void SetThrusterSide(const char[] sThruster)
|
||||
{
|
||||
this.SetString("sThrusterSide", sThruster);
|
||||
}
|
||||
public int GetAttachment(char[] buffer, int size)
|
||||
{
|
||||
return this.GetString("sAttachment", buffer, size);
|
||||
}
|
||||
public void SetAttachment(const char[] sAttachment)
|
||||
{
|
||||
this.SetString("sAttachment", sAttachment);
|
||||
}
|
||||
public int GetTemplate(char[] buffer, int size)
|
||||
{
|
||||
return this.GetString("sTemplate", buffer, size);
|
||||
}
|
||||
public void SetTemplate(const char[] sTemplate)
|
||||
{
|
||||
this.SetString("sTemplate", sTemplate);
|
||||
}
|
||||
public void Delete()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
stock int GetEntityIndex(int entity, const char[] name, const char[] classname = "*")
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if(name[0] == '#')
|
||||
{
|
||||
return Vscripts_GetEntityIndexByHammerID(StringToInt(name[1]), classname, entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Vscripts_GetEntityIndexByName(name, classname, entity);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsMovingNpcExists(int entity)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for(int i = 0; i < g_aMovingNpc.Length; ++i)
|
||||
{
|
||||
MovingNpc npc = g_aMovingNpc.Get(i);
|
||||
if(npc.entity == entity || npc.tf == entity || npc.ts == entity)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void KillNpc(MovingNpc npc)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for(int i = 0; i < g_aMovingNpc.Length; ++i)
|
||||
{
|
||||
MovingNpc cur = g_aMovingNpc.Get(i);
|
||||
if(cur.entity == npc.entity)
|
||||
{
|
||||
npc.Stop();
|
||||
g_aMovingNpc.Erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
npc.kill = true;
|
||||
}
|
||||
|
||||
public Action OnMovingNpcTimeout(Handle timer, MovingNpc npc)
|
||||
{
|
||||
KillTimer(timer);
|
||||
npc.lifetimer = null;
|
||||
KillNpc(npc);
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
public void StartNpc(MovingNpc npc)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
npc.Start();
|
||||
if(npc.lifetime > 0 && !npc.lifetimer)
|
||||
npc.lifetimer = CreateTimer(npc.lifetime, OnMovingNpcTimeout, npc, TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
|
||||
public int GetEntName(int entity, char[] buffer, int size)
|
||||
{
|
||||
return GetEntPropString(entity, Prop_Data, "m_iName", buffer, size);
|
||||
}
|
||||
|
||||
public int GetEntHammerID(int entity)
|
||||
{
|
||||
return GetEntProp(entity, Prop_Data, "m_iHammerID");
|
||||
}
|
||||
|
||||
|
||||
stock bool MatchTrigger(int entity, const char[] sTrigger, bool namefixup = false)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(sTrigger[0] == '#')
|
||||
{
|
||||
return StringToInt(sTrigger[1]) == GetEntHammerID(entity);
|
||||
} else
|
||||
{
|
||||
char name[MAX_ENT_NAME];
|
||||
GetEntName(entity, name, sizeof(name));
|
||||
if(!name[0])
|
||||
return false;
|
||||
if(namefixup)
|
||||
{
|
||||
int c = FindCharInString(name, '&', true);
|
||||
if(c != -1)
|
||||
{
|
||||
name[c] = '\0';
|
||||
}
|
||||
}
|
||||
return StrEqual(name, sTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
enabled = false;
|
||||
char sConfigPath[PLATFORM_MAX_PATH];
|
||||
char sCurMap[PLATFORM_MAX_PATH];
|
||||
GetCurrentMap(sCurMap, sizeof(sCurMap));
|
||||
BuildPath(Path_SM, sConfigPath, sizeof(sConfigPath), "configs/movingnpc/%s.cfg", sCurMap);
|
||||
if(!FileExists(sConfigPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
KeyValues Config = new KeyValues("npc");
|
||||
if(!Config.ImportFromFile(sConfigPath))
|
||||
{
|
||||
LogMessage("ImportFromFile() failed for map %s!", sCurMap);
|
||||
return;
|
||||
}
|
||||
Config.Rewind();
|
||||
if(!Config.GotoFirstSubKey(true))
|
||||
{
|
||||
LogMessage("The current map does not have any moving npcs configured.");
|
||||
return;
|
||||
}
|
||||
enabled = true;
|
||||
g_mNpcConfig = new StringMap();
|
||||
g_aNpcConfigNT = new ArrayList();
|
||||
do
|
||||
{
|
||||
MovingNpcConfig NpcConf = new MovingNpcConfig();
|
||||
char buffer[MAX_ENT_NAME + MAX_INPUT_NAME];
|
||||
Config.GetString("thruster_forward", buffer, sizeof(buffer), "");
|
||||
if(!buffer[0])
|
||||
{
|
||||
delete NpcConf;
|
||||
LogMessage("Could not find \"thruster_forward\" in config for map %s", sCurMap);
|
||||
continue;
|
||||
}
|
||||
NpcConf.SetThrusterFwd(buffer);
|
||||
|
||||
Config.GetString("thruster_side", buffer, sizeof(buffer), "");
|
||||
if(!buffer[0])
|
||||
{
|
||||
delete NpcConf;
|
||||
LogMessage("Could not find \"thruster_side\" in config for map %s", sCurMap);
|
||||
continue;
|
||||
}
|
||||
NpcConf.SetThrusterSide(buffer);
|
||||
|
||||
Config.GetString("attachment", buffer, sizeof(buffer), "");
|
||||
if(!buffer[0])
|
||||
{
|
||||
delete NpcConf;
|
||||
LogMessage("Could not find \"attachment\" in config for map %s", sCurMap);
|
||||
continue;
|
||||
}
|
||||
NpcConf.SetAttachment(buffer);
|
||||
|
||||
char sTemplate[MAX_ENT_NAME];
|
||||
Config.GetString("template", sTemplate, sizeof(sTemplate), "");
|
||||
NpcConf.SetTemplate(sTemplate);
|
||||
NpcConf.rate = Config.GetFloat("tickrate", 0.1);
|
||||
NpcConf.distance = Config.GetFloat("distance", 5000.0);
|
||||
NpcConf.retarget = Config.GetFloat("retarget", 7.5);
|
||||
NpcConf.forward_factor = Config.GetFloat("forward_factor", 1.0);
|
||||
NpcConf.turning_factor = Config.GetFloat("turning_factor", 0.5);
|
||||
NpcConf.lifetime = Config.GetFloat("lifetime", 0.0);
|
||||
if(sTemplate[0])
|
||||
{
|
||||
ArrayList tmp;
|
||||
if(!g_mNpcConfig.GetValue(sTemplate, tmp))
|
||||
{
|
||||
tmp = new ArrayList();
|
||||
}
|
||||
tmp.Push(NpcConf);
|
||||
g_mNpcConfig.SetValue(sTemplate, tmp, true);
|
||||
} else
|
||||
{
|
||||
g_aNpcConfigNT.Push(NpcConf);
|
||||
}
|
||||
} while(Config.GotoNextKey(true));
|
||||
delete Config;
|
||||
g_aMovingNpc = new ArrayList();
|
||||
HookEvent("round_start", OnRoundStart, EventHookMode_PostNoCopy);
|
||||
}
|
||||
|
||||
public void OnRoundStart(Event event, const char[] name, bool dontBroadcast)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for(int i = 0; i < g_aNpcConfigNT.Length; ++i)
|
||||
{
|
||||
MovingNpcConfig NpcConf = g_aNpcConfigNT.Get(i);
|
||||
char sThrusterFwd[MAX_ENT_NAME], sThrusterSide[MAX_ENT_NAME], sAttachment[MAX_ENT_NAME];
|
||||
NpcConf.GetThrusterFwd(sThrusterFwd, sizeof(sThrusterFwd));
|
||||
NpcConf.GetThrusterSide(sThrusterSide, sizeof(sThrusterSide));
|
||||
NpcConf.GetAttachment(sAttachment, sizeof(sAttachment));
|
||||
int thruster_fwd = -1, thruster_side = -1, attachment = -1;
|
||||
while((thruster_fwd = GetEntityIndex(thruster_fwd, sThrusterFwd, "phys_thruster")) != -1)
|
||||
{
|
||||
if(IsMovingNpcExists(thruster_fwd))
|
||||
continue;
|
||||
do {
|
||||
thruster_side = GetEntityIndex(thruster_side, sThrusterSide, "phys_thruster");
|
||||
} while (thruster_side != -1 && IsMovingNpcExists(thruster_side));
|
||||
|
||||
do {
|
||||
attachment = GetEntityIndex(attachment, sAttachment);
|
||||
} while (attachment != -1 && IsMovingNpcExists(attachment));
|
||||
|
||||
if(thruster_side != -1 && attachment != -1)
|
||||
{
|
||||
NewMovingNpc(NpcConf, attachment, thruster_fwd, thruster_side);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stock void NewMovingNpc(MovingNpcConfig NpcConf, int attachment, int thruster_fwd, int thruster_side)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
MovingNpc npc = new MovingNpc(attachment, NpcConf.rate, NpcConf.distance, NpcConf.retarget, NpcConf.forward_factor, NpcConf.turning_factor, NpcConf.lifetime);
|
||||
npc.SetThruster(true, thruster_fwd);
|
||||
npc.SetThruster(false, thruster_side);
|
||||
StartNpc(npc);
|
||||
g_aMovingNpc.Push(npc);
|
||||
}
|
||||
|
||||
public void Vscritps_OnTemplateInstanceCreated(int template, const int[] createdEntities, int size)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(!g_mNpcConfig)
|
||||
return;
|
||||
|
||||
char sTemplate[MAX_ENT_NAME];
|
||||
GetEntName(template, sTemplate, sizeof(sTemplate));
|
||||
ArrayList configs;
|
||||
if(!g_mNpcConfig.GetValue(sTemplate, configs))
|
||||
{
|
||||
Format(sTemplate, sizeof(sTemplate), "#%d", GetEntHammerID(template));
|
||||
if(!g_mNpcConfig.GetValue(sTemplate, configs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < configs.Length; ++i)
|
||||
{
|
||||
int attachment = -1, thruster_fwd = -1, thruster_side = -1;
|
||||
MovingNpcConfig npcConf = configs.Get(i);
|
||||
char sAttachment[MAX_ENT_NAME], sThrusterFwd[MAX_ENT_NAME], sThrusterSide[MAX_ENT_NAME];
|
||||
npcConf.GetAttachment(sAttachment, sizeof(sAttachment));
|
||||
npcConf.GetThrusterFwd(sThrusterFwd, sizeof(sThrusterFwd));
|
||||
npcConf.GetThrusterSide(sThrusterSide, sizeof(sThrusterSide));
|
||||
for(int e = 0; e < size; ++e)
|
||||
{
|
||||
int entity = createdEntities[e];
|
||||
if(MatchTrigger(entity, sAttachment, true))
|
||||
{
|
||||
attachment = entity;
|
||||
} else if(MatchTrigger(entity, sThrusterFwd, true))
|
||||
{
|
||||
thruster_fwd = entity;
|
||||
} else if(MatchTrigger(entity, sThrusterSide, true))
|
||||
{
|
||||
thruster_side = entity;
|
||||
}
|
||||
}
|
||||
if(attachment != -1 && thruster_fwd != -1 && thruster_side != -1)
|
||||
{
|
||||
NewMovingNpc(npcConf, attachment, thruster_fwd, thruster_side);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEntityDestroyed(int entity)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(!g_aMovingNpc)
|
||||
return;
|
||||
for (int i = 0; i < g_aMovingNpc.Length; ++i)
|
||||
{
|
||||
MovingNpc npc = g_aMovingNpc.Get(i);
|
||||
if(npc.entity == entity || npc.tf == entity || npc.ts == entity)
|
||||
{
|
||||
npc.Stop();
|
||||
npc.kill = true;
|
||||
g_aMovingNpc.Erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void OnMapEnd()
|
||||
{
|
||||
UnhookEvent("round_start", OnRoundStart, EventHookMode_PostNoCopy);
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(g_aMovingNpc)
|
||||
{
|
||||
for (int i = 0; i < g_aMovingNpc.Length; ++i)
|
||||
{
|
||||
MovingNpc npc = g_aMovingNpc.Get(i);
|
||||
npc.Stop();
|
||||
npc.kill = true;
|
||||
}
|
||||
delete g_aMovingNpc;
|
||||
}
|
||||
if(g_aNpcConfigNT)
|
||||
{
|
||||
for (int i = 0; i < g_aNpcConfigNT.Length; ++i)
|
||||
{
|
||||
MovingNpcConfig NpcConf = g_aNpcConfigNT.Get(i);
|
||||
NpcConf.Delete();
|
||||
}
|
||||
delete g_aNpcConfigNT;
|
||||
}
|
||||
if(g_mNpcConfig)
|
||||
{
|
||||
StringMapSnapshot ms = g_mNpcConfig.Snapshot();
|
||||
char sTemplate[MAX_ENT_NAME];
|
||||
for(int i = 0; i < ms.Length; ++i)
|
||||
{
|
||||
ms.GetKey(i, sTemplate, sizeof(sTemplate));
|
||||
ArrayList configs;
|
||||
g_mNpcConfig.GetValue(sTemplate, configs);
|
||||
for(int c = 0; c < configs.Length; ++c)
|
||||
{
|
||||
MovingNpcConfig NpcConf = configs.Get(c);
|
||||
NpcConf.Delete();
|
||||
}
|
||||
delete configs;
|
||||
}
|
||||
delete ms;
|
||||
g_mNpcConfig.Clear();
|
||||
delete g_mNpcConfig;
|
||||
}
|
||||
}
|
1
VScripts/scripting/README.MD
Normal file
1
VScripts/scripting/README.MD
Normal file
@ -0,0 +1 @@
|
||||
moving npc requires following extension to run: https://github.com/srcdslab/sm-ext-eventqueue
|
553
VScripts/scripting/Vscripts_Core.sp
Normal file
553
VScripts/scripting/Vscripts_Core.sp
Normal file
@ -0,0 +1,553 @@
|
||||
#pragma semicolon 1
|
||||
|
||||
#define PLUGIN_AUTHOR "Cloud Strife"
|
||||
#define PLUGIN_VERSION "1.00"
|
||||
|
||||
#include <sourcemod>
|
||||
#include <vscripts_moving_npc>
|
||||
#include <dhooks>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Vscripts Core",
|
||||
author = PLUGIN_AUTHOR,
|
||||
description = "",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "https://steamcommunity.com/id/cloudstrifeua/"
|
||||
};
|
||||
|
||||
#define nullptr Address_Null
|
||||
|
||||
ArrayList g_aVscriptTimers = null;
|
||||
|
||||
bool g_bEventQueue = false;
|
||||
|
||||
Handle g_hGetModel = null, g_hGetModelType = null;
|
||||
Handle g_hTemplateCreateInstace = null;
|
||||
|
||||
Address g_pModelInfo = nullptr;
|
||||
|
||||
int g_iSolidType, g_iOwnerEntity, g_iCollisionGroup;
|
||||
int g_iUtlVectorSize = -1;
|
||||
|
||||
GlobalForward g_fwdTemplateCreateInstace;
|
||||
|
||||
enum modtype_t
|
||||
{
|
||||
mod_bad = 0,
|
||||
mod_brush,
|
||||
mod_sprite,
|
||||
mod_studio
|
||||
}
|
||||
|
||||
enum SolidType_t
|
||||
{
|
||||
SOLID_NONE = 0, // no solid model
|
||||
SOLID_BSP = 1, // a BSP tree
|
||||
SOLID_BBOX = 2, // an AABB
|
||||
SOLID_OBB = 3, // an OBB (not implemented yet)
|
||||
SOLID_OBB_YAW = 4, // an OBB, constrained so that it can only yaw
|
||||
SOLID_CUSTOM = 5, // Always call into the entity for tests
|
||||
SOLID_VPHYSICS = 6, // solid vphysics object, get vcollide from the model and collide with that
|
||||
SOLID_LAST,
|
||||
}
|
||||
|
||||
enum Collision_Group_t
|
||||
{
|
||||
COLLISION_GROUP_NONE = 0,
|
||||
COLLISION_GROUP_DEBRIS, // Collides with nothing but world and static stuff
|
||||
COLLISION_GROUP_DEBRIS_TRIGGER, // Same as debris, but hits triggers
|
||||
COLLISION_GROUP_INTERACTIVE_DEBRIS, // Collides with everything except other interactive debris or debris
|
||||
COLLISION_GROUP_INTERACTIVE, // Collides with everything except interactive debris or debris
|
||||
COLLISION_GROUP_PLAYER,
|
||||
COLLISION_GROUP_BREAKABLE_GLASS,
|
||||
COLLISION_GROUP_VEHICLE,
|
||||
COLLISION_GROUP_PLAYER_MOVEMENT, // For HL2, same as Collision_Group_Player, for
|
||||
// TF2, this filters out other players and CBaseObjects
|
||||
COLLISION_GROUP_NPC, // Generic NPC group
|
||||
COLLISION_GROUP_IN_VEHICLE, // for any entity inside a vehicle
|
||||
COLLISION_GROUP_WEAPON, // for any weapons that need collision detection
|
||||
COLLISION_GROUP_VEHICLE_CLIP, // vehicle clip brush to restrict vehicle movement
|
||||
COLLISION_GROUP_PROJECTILE, // Projectiles!
|
||||
COLLISION_GROUP_DOOR_BLOCKER, // Blocks entities not permitted to get near moving doors
|
||||
COLLISION_GROUP_PASSABLE_DOOR, // Doors that the player shouldn't collide with
|
||||
COLLISION_GROUP_DISSOLVING, // Things that are dissolving are in this group
|
||||
COLLISION_GROUP_PUSHAWAY, // Nonsolid on client and server, pushaway in player code
|
||||
COLLISION_GROUP_NPC_ACTOR, // Used so NPCs in scripts ignore the player.
|
||||
COLLISION_GROUP_NPC_SCRIPTED, // USed for NPCs in scripts that should not collide with each other
|
||||
LAST_SHARED_COLLISION_GROUP
|
||||
}
|
||||
|
||||
methodmap VscriptTimer < StringMap
|
||||
{
|
||||
public VscriptTimer(Handle plugin, float time, Function cb, any data)
|
||||
{
|
||||
StringMap myclass = new StringMap();
|
||||
float trigger_time = GetGameTime() + time;
|
||||
myclass.SetValue("m_fTriggerTime", trigger_time);
|
||||
PrivateForward cb_fwd = new PrivateForward(ET_Ignore, Param_Any);
|
||||
cb_fwd.AddFunction(plugin, cb);
|
||||
myclass.SetValue("m_hCallback", cb_fwd);
|
||||
myclass.SetValue("m_Data", data);
|
||||
|
||||
bool inserted = false;
|
||||
for(int i = 0; i < g_aVscriptTimers.Length; ++i)
|
||||
{
|
||||
VscriptTimer cur = g_aVscriptTimers.Get(i);
|
||||
float cur_t;
|
||||
cur.GetValue("m_fTriggerTime", cur_t);
|
||||
if(cur_t > trigger_time)
|
||||
{
|
||||
inserted = true;
|
||||
g_aVscriptTimers.ShiftUp(i);
|
||||
g_aVscriptTimers.Set(i, myclass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!inserted)
|
||||
g_aVscriptTimers.Push(myclass);
|
||||
|
||||
return view_as<VscriptTimer>(myclass);
|
||||
}
|
||||
|
||||
property float time
|
||||
{
|
||||
public get()
|
||||
{
|
||||
float t;
|
||||
this.GetValue("m_fTriggerTime", t);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
property any data
|
||||
{
|
||||
public get()
|
||||
{
|
||||
any data;
|
||||
this.GetValue("m_Data", data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public void Kill()
|
||||
{
|
||||
PrivateForward cb_fwd;
|
||||
this.GetValue("m_hCallback", cb_fwd);
|
||||
delete cb_fwd;
|
||||
delete this;
|
||||
}
|
||||
|
||||
public void Trigger()
|
||||
{
|
||||
PrivateForward cb_fwd;
|
||||
this.GetValue("m_hCallback", cb_fwd);
|
||||
any data = this.data;
|
||||
delete this;
|
||||
|
||||
Call_StartForward(cb_fwd);
|
||||
Call_PushCell(data);
|
||||
Call_Finish();
|
||||
|
||||
delete cb_fwd;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGameFrame()
|
||||
{
|
||||
for(int i = 0; i < g_aVscriptTimers.Length; ++i)
|
||||
{
|
||||
VscriptTimer cur = g_aVscriptTimers.Get(i);
|
||||
if(cur.time <= GetGameTime())
|
||||
{
|
||||
g_aVscriptTimers.Erase(i--);
|
||||
cur.Trigger();
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
||||
{
|
||||
CreateNative("Vscripts_CreateTimer", Native_CreateTimer);
|
||||
CreateNative("Vscripts_IsEventQueueLoaded", Native_IsEventQueueLoaded);
|
||||
CreateNative("Vscripts_TraceFilterSimple", Native_TraceFilterSimple);
|
||||
RegPluginLibrary("vscripts");
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
public any Native_CreateTimer(Handle plugin, int numParams)
|
||||
{
|
||||
float f = GetNativeCell(1);
|
||||
new VscriptTimer(plugin, f, GetNativeFunction(2), GetNativeCell(3));
|
||||
//new VscriptTimer(plugin, f, view_as<VscriptTimerCallback>(GetNativeFunction(2)), GetNativeCell(3));
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public int Native_IsEventQueueLoaded(Handle plugin, int numParams)
|
||||
{
|
||||
return g_bEventQueue;
|
||||
}
|
||||
|
||||
public void OnAllPluginsLoaded()
|
||||
{
|
||||
g_bEventQueue = LibraryExists("Entity Events Queue");
|
||||
}
|
||||
|
||||
//public void OnLibraryAdded(const char[] name)
|
||||
//{
|
||||
//if(StrEqual(name, "Entity Events Queue"))
|
||||
//{
|
||||
//g_bEventQueue = true;
|
||||
//}
|
||||
//}
|
||||
|
||||
public void OnLibraryRemoved(const char[] name)
|
||||
{
|
||||
if(StrEqual(name, "Entity Events Queue"))
|
||||
{
|
||||
g_bEventQueue = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
g_aVscriptTimers = new ArrayList();
|
||||
|
||||
Handle hGameData = LoadGameConfigFile("Vscripts_Core.games");
|
||||
if(!hGameData)
|
||||
SetFailState("Failed to load Vscripts_Core gamedata.");
|
||||
|
||||
StartPrepSDKCall(SDKCall_Entity);
|
||||
if(!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CBaseEntity::GetModel"))
|
||||
{
|
||||
delete hGameData;
|
||||
SetFailState("PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, \"CBaseEntity::GetModel\") failed!");
|
||||
}
|
||||
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
|
||||
g_hGetModel = EndPrepSDKCall();
|
||||
|
||||
StartPrepSDKCall(SDKCall_Raw);
|
||||
if(!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CModelInfo::GetModelType"))
|
||||
{
|
||||
delete hGameData;
|
||||
SetFailState("PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, \"CModelInfo::GetModelType\") failed!");
|
||||
}
|
||||
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
|
||||
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
|
||||
g_hGetModelType = EndPrepSDKCall();
|
||||
|
||||
g_pModelInfo = GameConfGetAddress(hGameData, "modelinfo");
|
||||
if(!g_pModelInfo)
|
||||
{
|
||||
delete hGameData;
|
||||
SetFailState("Couldn't load modelinfo address!");
|
||||
}
|
||||
|
||||
g_iUtlVectorSize = GameConfGetOffset(hGameData, "CUtlVector::m_iSize");
|
||||
if(g_iUtlVectorSize == -1)
|
||||
{
|
||||
delete hGameData;
|
||||
SetFailState("Couldn't load CUtlVector::m_iSize offset!");
|
||||
}
|
||||
|
||||
Address pCreateInstance = GameConfGetAddress(hGameData, "CPointTemplate::CreateInstance");
|
||||
if(!pCreateInstance)
|
||||
{
|
||||
delete hGameData;
|
||||
SetFailState("Couldn't load CPointTemplate::CreateInstance address!");
|
||||
}
|
||||
|
||||
g_hTemplateCreateInstace = DHookCreateDetour(pCreateInstance, CallConv_THISCALL, ReturnType_Bool, ThisPointer_CBaseEntity);
|
||||
DHookAddParam(g_hTemplateCreateInstace, HookParamType_VectorPtr);
|
||||
DHookAddParam(g_hTemplateCreateInstace, HookParamType_VectorPtr);
|
||||
DHookAddParam(g_hTemplateCreateInstace, HookParamType_ObjectPtr);
|
||||
if(!DHookEnableDetour(g_hTemplateCreateInstace, true, CPointTemplate_CreateInstance))
|
||||
{
|
||||
delete hGameData;
|
||||
SetFailState("Couldn't create detour on CPointTemplate::CreateInstance!");
|
||||
}
|
||||
|
||||
delete hGameData;
|
||||
|
||||
g_fwdTemplateCreateInstace = new GlobalForward("Vscritps_OnTemplateInstanceCreated", ET_Ignore, Param_Cell, Param_Array, Param_Cell);
|
||||
}
|
||||
|
||||
public MRESReturn CPointTemplate_CreateInstance(int entity, DHookReturn hReturn, DHookParam hParams)
|
||||
{
|
||||
if( view_as<bool>(DHookGetReturn(hReturn)) == false )
|
||||
{
|
||||
return MRES_Ignored;
|
||||
}
|
||||
int iSize = DHookGetParamObjectPtrVar(hParams, 3, g_iUtlVectorSize, ObjectValueType_Int);
|
||||
Address pBegin = view_as<Address>(DHookGetParamObjectPtrVar(hParams, 3, 0, ObjectValueType_Int));
|
||||
int[] createdEntities = new int[iSize];
|
||||
for(int i = 0; i < iSize; ++i)
|
||||
{
|
||||
createdEntities[i] = GetEntityFromAddress(view_as<Address>(LoadFromAddress(pBegin + view_as<Address>(i*4), NumberType_Int32)));
|
||||
}
|
||||
Call_StartForward(g_fwdTemplateCreateInstace);
|
||||
Call_PushCell(entity);
|
||||
Call_PushArray(createdEntities, iSize);
|
||||
Call_PushCell(iSize);
|
||||
Call_Finish();
|
||||
return MRES_Ignored;
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
g_iSolidType = FindDataMapInfo(0, "m_nSolidType");
|
||||
g_iOwnerEntity = FindDataMapInfo(0, "m_hOwnerEntity");
|
||||
g_iCollisionGroup = FindDataMapInfo(0, "m_CollisionGroup");
|
||||
}
|
||||
|
||||
public modtype_t GetModelType(int entity)
|
||||
{
|
||||
Address pModel = view_as<Address>(SDKCall(g_hGetModel, entity));
|
||||
return view_as<modtype_t>(SDKCall(g_hGetModelType, LoadFromAddress(g_pModelInfo, NumberType_Int32), pModel));
|
||||
}
|
||||
|
||||
public bool StandardFilterRules(int entity, int contentsMask)
|
||||
{
|
||||
if(!IsValidEntity(entity))
|
||||
return true;
|
||||
|
||||
SolidType_t solid = view_as<SolidType_t>(GetEntData(entity, g_iSolidType));
|
||||
modtype_t model = GetModelType(entity);
|
||||
|
||||
if((model != mod_brush) || (solid != SOLID_BSP && solid != SOLID_VPHYSICS))
|
||||
{
|
||||
if((contentsMask & CONTENTS_MONSTER) == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!(contentsMask & CONTENTS_WINDOW) && (GetEntityRenderMode(entity) != RENDER_NORMAL))
|
||||
return false;
|
||||
|
||||
if(!(contentsMask & CONTENTS_MOVEABLE) && (GetEntityMoveType(entity) == MOVETYPE_PUSH))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool PassServerEntityFilter(int entity, int pass)
|
||||
{
|
||||
if(!IsValidEntity(pass) || !IsValidEntity(entity))
|
||||
return true;
|
||||
|
||||
if(entity == pass)
|
||||
return false;
|
||||
|
||||
int owner = GetEntDataEnt2(entity, g_iOwnerEntity);
|
||||
if(owner == pass)
|
||||
return false;
|
||||
|
||||
owner = GetEntDataEnt2(pass, g_iOwnerEntity);
|
||||
if(owner == entity)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GameRules_ShouldCollide(Collision_Group_t collisionGroup0, Collision_Group_t collisionGroup1)
|
||||
{
|
||||
if ( collisionGroup0 > collisionGroup1 )
|
||||
{
|
||||
// swap so that lowest is always first
|
||||
Collision_Group_t tmp = collisionGroup0;
|
||||
collisionGroup0 = collisionGroup1;
|
||||
collisionGroup1 = tmp;
|
||||
}
|
||||
|
||||
if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
|
||||
collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( collisionGroup0 == COLLISION_GROUP_DEBRIS && collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
|
||||
{
|
||||
// let debris and multiplayer objects collide
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// NOTE: All of this code assumes the collision groups have been sorted!!!!
|
||||
// NOTE: Don't change their order without rewriting this code !!!
|
||||
// --------------------------------------------------------------------------
|
||||
// Don't bother if either is in a vehicle...
|
||||
if (( collisionGroup0 == COLLISION_GROUP_IN_VEHICLE ) || ( collisionGroup1 == COLLISION_GROUP_IN_VEHICLE ))
|
||||
return false;
|
||||
|
||||
if ( ( collisionGroup1 == COLLISION_GROUP_DOOR_BLOCKER ) && ( collisionGroup0 != COLLISION_GROUP_NPC ) )
|
||||
return false;
|
||||
|
||||
if ( ( collisionGroup0 == COLLISION_GROUP_PLAYER ) && ( collisionGroup1 == COLLISION_GROUP_PASSABLE_DOOR ) )
|
||||
return false;
|
||||
|
||||
if ( collisionGroup0 == COLLISION_GROUP_DEBRIS || collisionGroup0 == COLLISION_GROUP_DEBRIS_TRIGGER )
|
||||
{
|
||||
// put exceptions here, right now this will only collide with COLLISION_GROUP_NONE
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dissolving guys only collide with COLLISION_GROUP_NONE
|
||||
if ( (collisionGroup0 == COLLISION_GROUP_DISSOLVING) || (collisionGroup1 == COLLISION_GROUP_DISSOLVING) )
|
||||
{
|
||||
if ( collisionGroup0 != COLLISION_GROUP_NONE )
|
||||
return false;
|
||||
}
|
||||
|
||||
// doesn't collide with other members of this group
|
||||
// or debris, but that's handled above
|
||||
if ( collisionGroup0 == COLLISION_GROUP_INTERACTIVE_DEBRIS && collisionGroup1 == COLLISION_GROUP_INTERACTIVE_DEBRIS )
|
||||
return false;
|
||||
|
||||
// This change was breaking HL2DM
|
||||
// Adrian: TEST! Interactive Debris doesn't collide with the player.
|
||||
if ( collisionGroup0 == COLLISION_GROUP_INTERACTIVE_DEBRIS && ( collisionGroup1 == COLLISION_GROUP_PLAYER_MOVEMENT || collisionGroup1 == COLLISION_GROUP_PLAYER ) )
|
||||
return false;
|
||||
|
||||
if ( collisionGroup0 == COLLISION_GROUP_BREAKABLE_GLASS && collisionGroup1 == COLLISION_GROUP_BREAKABLE_GLASS )
|
||||
return false;
|
||||
|
||||
// interactive objects collide with everything except debris & interactive debris
|
||||
if ( collisionGroup1 == COLLISION_GROUP_INTERACTIVE && collisionGroup0 != COLLISION_GROUP_NONE )
|
||||
return false;
|
||||
|
||||
// Projectiles hit everything but debris, weapons, + other projectiles
|
||||
if ( collisionGroup1 == COLLISION_GROUP_PROJECTILE )
|
||||
{
|
||||
if ( collisionGroup0 == COLLISION_GROUP_DEBRIS ||
|
||||
collisionGroup0 == COLLISION_GROUP_WEAPON ||
|
||||
collisionGroup0 == COLLISION_GROUP_PROJECTILE )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't let vehicles collide with weapons
|
||||
// Don't let players collide with weapons...
|
||||
// Don't let NPCs collide with weapons
|
||||
// Weapons are triggers, too, so they should still touch because of that
|
||||
if ( collisionGroup1 == COLLISION_GROUP_WEAPON )
|
||||
{
|
||||
if ( collisionGroup0 == COLLISION_GROUP_VEHICLE ||
|
||||
collisionGroup0 == COLLISION_GROUP_PLAYER ||
|
||||
collisionGroup0 == COLLISION_GROUP_NPC )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// collision with vehicle clip entity??
|
||||
if ( collisionGroup0 == COLLISION_GROUP_VEHICLE_CLIP || collisionGroup1 == COLLISION_GROUP_VEHICLE_CLIP )
|
||||
{
|
||||
// yes then if it's a vehicle, collide, otherwise no collision
|
||||
// vehicle sorts lower than vehicle clip, so must be in 0
|
||||
if ( collisionGroup0 == COLLISION_GROUP_VEHICLE )
|
||||
return true;
|
||||
// vehicle clip against non-vehicle, no collision
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ShouldCollide(int entity, Collision_Group_t collisionGroup, int contentsMask)
|
||||
{
|
||||
Collision_Group_t m_collisionGroup = view_as<Collision_Group_t>(GetEntData(entity, g_iCollisionGroup));
|
||||
if(m_collisionGroup == COLLISION_GROUP_DEBRIS)
|
||||
{
|
||||
if(!(contentsMask & CONTENTS_DEBRIS))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Native_TraceFilterSimple(Handle plugin, int numParams)
|
||||
{
|
||||
int entity = GetNativeCell(1);
|
||||
int contentsMask = GetNativeCell(2);
|
||||
int ignore = GetNativeCell(3);
|
||||
|
||||
if(!IsValidEntity(entity))
|
||||
return false;
|
||||
|
||||
if(!StandardFilterRules(entity, contentsMask))
|
||||
return false;
|
||||
|
||||
if(ignore != -1)
|
||||
{
|
||||
if(!PassServerEntityFilter(entity, ignore))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ShouldCollide(entity, COLLISION_GROUP_NONE, contentsMask))
|
||||
return false;
|
||||
if(!GameRules_ShouldCollide(COLLISION_GROUP_NONE, view_as<Collision_Group_t>(GetEntData(entity, g_iCollisionGroup))))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnMapEnd()
|
||||
{
|
||||
for(int i = 0; i < g_aVscriptTimers.Length; ++i)
|
||||
{
|
||||
VscriptTimer cur = g_aVscriptTimers.Get(i);
|
||||
cur.Kill();
|
||||
}
|
||||
g_aVscriptTimers.Clear();
|
||||
}
|
||||
|
||||
//Copied from https://github.com/nosoop/stocksoup/blob/master/memory.inc
|
||||
/**
|
||||
* Retrieves an entity index from a raw entity handle address.
|
||||
*
|
||||
* Note that SourceMod's entity conversion routine is an implementation detail that may change.
|
||||
*
|
||||
* @param addr Address to a memory location.
|
||||
* @return Entity index, or -1 if not valid.
|
||||
*/
|
||||
stock int LoadEntityHandleFromAddress(Address addr) {
|
||||
return EntRefToEntIndex(LoadFromAddress(addr, NumberType_Int32) | (1 << 31));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an entity index from its address by attempting to read the
|
||||
* CBaseEntity::m_RefEHandle member. This assumes the address of a CBaseEntity is
|
||||
* passed in.
|
||||
*
|
||||
* @param pEntity Address of an entity.
|
||||
* @return Entity index, or -1 if not valid.
|
||||
*/
|
||||
stock int GetEntityFromAddress(Address pEntity) {
|
||||
static int offs_RefEHandle;
|
||||
if (offs_RefEHandle) {
|
||||
return LoadEntityHandleFromAddress(pEntity + view_as<Address>(offs_RefEHandle));
|
||||
}
|
||||
|
||||
// if we don't have it already, attempt to lookup offset based on SDK information
|
||||
// CWorld is derived from CBaseEntity so it should have both offsets
|
||||
int offs_angRotation = FindDataMapInfo(0, "m_angRotation"),
|
||||
offs_vecViewOffset = FindDataMapInfo(0, "m_vecViewOffset");
|
||||
if (offs_angRotation == -1) {
|
||||
ThrowError("Could not find offset for ((CBaseEntity) CWorld)::m_angRotation");
|
||||
} else if (offs_vecViewOffset == -1) {
|
||||
ThrowError("Could not find offset for ((CBaseEntity) CWorld)::m_vecViewOffset");
|
||||
} else if ((offs_angRotation + 0x0C) != (offs_vecViewOffset - 0x04)) {
|
||||
char game[32];
|
||||
GetGameFolderName(game, sizeof(game));
|
||||
ThrowError("Could not confirm offset of CBaseEntity::m_RefEHandle "
|
||||
... "(incorrect assumption for game '%s'?)", game);
|
||||
}
|
||||
|
||||
// offset seems right, cache it for the next call
|
||||
offs_RefEHandle = offs_angRotation + 0x0C;
|
||||
return GetEntityFromAddress(pEntity);
|
||||
}
|
||||
|
||||
|
331
VScripts/scripting/include/MovingNPC.inc
Normal file
331
VScripts/scripting/include/MovingNPC.inc
Normal file
@ -0,0 +1,331 @@
|
||||
#if defined _MovingNPC_included
|
||||
#endinput
|
||||
#endif
|
||||
#define _MovingNPC_included
|
||||
#include <basic>
|
||||
//#include <vscripts>
|
||||
#include <vscripts_moving_npc>
|
||||
#include <float>
|
||||
|
||||
stock float operator%(float oper1, float oper2)
|
||||
{
|
||||
return FloatMod(oper1, oper2);
|
||||
}
|
||||
|
||||
public bool IsValidPlayer(int player)
|
||||
{
|
||||
return player >= 1 && player <= MAXPLAYERS && IsValidEntity(player) && IsPlayerAlive(player);
|
||||
}
|
||||
|
||||
public float GetDistance(const float v1[3], const float v2[3])
|
||||
{
|
||||
return SquareRoot((v1[0] - v2[0]) * (v1[0] - v2[0]) + (v1[1] - v2[1]) * (v1[1] - v2[1]) + (v1[2] - v2[2]) * (v1[2] - v2[2]));
|
||||
}
|
||||
|
||||
methodmap MovingNpc < Basic
|
||||
{
|
||||
public MovingNpc(int entity, float tickrate = 0.1, float distance = 5000.0, float retarget = 7.5, float forward_factor = 1.0, float turning_factor = 0.5, float lifetime = 0.0)
|
||||
{
|
||||
Basic myclass = new Basic();
|
||||
myclass.SetFloat("fRate", tickrate);
|
||||
myclass.SetFloat("fDistance", distance);
|
||||
myclass.SetFloat("fRetarget", retarget);
|
||||
myclass.SetFloat("fForward", forward_factor);
|
||||
myclass.SetFloat("fTurning", turning_factor);
|
||||
myclass.SetFloat("fLifetime", lifetime);
|
||||
myclass.SetInt("iEntity", entity);
|
||||
myclass.SetInt("iTarget", -1);
|
||||
myclass.SetInt("iTf", -1);
|
||||
myclass.SetInt("iTs", -1);
|
||||
myclass.SetFloat("fTtime", 0.0);
|
||||
myclass.SetBool("bTicking", false);
|
||||
myclass.SetHandle("hLifeTimer", null);
|
||||
myclass.SetBool("bKill", false);
|
||||
return view_as<MovingNpc>(myclass);
|
||||
}
|
||||
property bool kill
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetBool("bKill");
|
||||
}
|
||||
public set(bool val)
|
||||
{
|
||||
this.SetBool("bKill", val);
|
||||
}
|
||||
}
|
||||
property Handle lifetimer
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetHandle("hLifeTimer");
|
||||
}
|
||||
public set(Handle val)
|
||||
{
|
||||
this.SetHandle("hLifeTimer", val);
|
||||
}
|
||||
}
|
||||
property float lifetime
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fLifetime");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fLifetime", val);
|
||||
}
|
||||
}
|
||||
property float rate
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fRate");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fRate", val);
|
||||
}
|
||||
}
|
||||
property float distance
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fDistance");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fDistance", val);
|
||||
}
|
||||
}
|
||||
property float retarget
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fRetarget");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fRetarget", val);
|
||||
}
|
||||
}
|
||||
property float forward_factor
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fForward");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fForward", val);
|
||||
}
|
||||
}
|
||||
property float turning_factor
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fTurning");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fTurning", val);
|
||||
}
|
||||
}
|
||||
property int entity
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetInt("iEntity");
|
||||
}
|
||||
public set(int val)
|
||||
{
|
||||
this.SetInt("iEntity", val);
|
||||
}
|
||||
}
|
||||
property int target
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetInt("iTarget");
|
||||
}
|
||||
public set(int val)
|
||||
{
|
||||
this.SetInt("iTarget", val);
|
||||
}
|
||||
}
|
||||
property int tf
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetInt("iTf");
|
||||
}
|
||||
public set(int val)
|
||||
{
|
||||
this.SetInt("iTf", val);
|
||||
}
|
||||
}
|
||||
property int ts
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetInt("iTs");
|
||||
}
|
||||
public set(int val)
|
||||
{
|
||||
this.SetInt("iTs", val);
|
||||
}
|
||||
}
|
||||
property float ttime
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetFloat("fTtime");
|
||||
}
|
||||
public set(float val)
|
||||
{
|
||||
this.SetFloat("fTtime", val);
|
||||
}
|
||||
}
|
||||
property bool ticking
|
||||
{
|
||||
public get()
|
||||
{
|
||||
return this.GetBool("bTicking");
|
||||
}
|
||||
public set(bool val)
|
||||
{
|
||||
this.SetBool("bTicking", val);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if(!this.ticking)
|
||||
{
|
||||
this.ticking = true;
|
||||
Vscripts_CreateTimer(this.rate, Tick_Cb, this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if(this.ticking)
|
||||
{
|
||||
this.ticking = false;
|
||||
}
|
||||
}
|
||||
|
||||
public float GetTargetYaw(const float start[3], const float target[3])
|
||||
{
|
||||
float yaw = 0.00;
|
||||
float v[3];
|
||||
SubtractVectors(start, target, v);
|
||||
float vl = SquareRoot(v[0] * v[0] + v[1] * v[1]);
|
||||
yaw = 180.0 * ArcCosine(v[0] / vl) / 3.14159;
|
||||
if (v[1] < 0.0)
|
||||
yaw = -yaw;
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public void SetThruster(bool fwd, int caller)
|
||||
{
|
||||
if(fwd)
|
||||
this.tf = caller;
|
||||
else
|
||||
this.ts = caller;
|
||||
}
|
||||
|
||||
public void SearchTarget()
|
||||
{
|
||||
this.ttime = 0.00;
|
||||
this.target = -1;
|
||||
int h = -1;
|
||||
ArrayList candidates = new ArrayList();
|
||||
float orig[3];
|
||||
Vscripts_GetOrigin(this.entity, orig);
|
||||
while (-1 != (h = Vscripts_FindEntityByClassnameWithin(h, "player", orig, this.distance)))
|
||||
{
|
||||
//check if target is a valid player + CT team(3) + health above 0 (not dead)
|
||||
if (GetClientTeam(h) == 3 && IsPlayerAlive(h))
|
||||
{
|
||||
//check if the target is in sight of the npc (this physbox origin+48 height)
|
||||
float t_orig[3];
|
||||
Vscripts_GetOrigin(this.entity, orig);
|
||||
orig[2] += 40.0;
|
||||
Vscripts_GetOrigin(h, t_orig);
|
||||
t_orig[2] += 48.0;
|
||||
if (Vscripts_TraceLine(orig, t_orig, this.entity) == 1.00)
|
||||
candidates.Push(h); //if everything required is OK, add the target to the list of candidates
|
||||
}
|
||||
}
|
||||
if(candidates.Length == 0)
|
||||
{
|
||||
delete candidates;
|
||||
return;
|
||||
}
|
||||
this.target = candidates.Get(GetRandomInt(0, candidates.Length - 1));
|
||||
|
||||
delete candidates;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
Vscripts_EntFireByIndex(this.tf, "Deactivate", "", 0.0, -1);
|
||||
Vscripts_EntFireByIndex(this.ts, "Deactivate", "", 0.0, -1);
|
||||
if (!IsValidPlayer(this.target) || GetClientTeam(this.target) != 3 || this.ttime >= this.retarget)
|
||||
{
|
||||
this.SearchTarget();
|
||||
}
|
||||
this.ttime+=this.rate;
|
||||
Vscripts_EntFireByIndex(this.tf, "Activate", "", 0.02, -1);
|
||||
Vscripts_EntFireByIndex(this.ts, "Activate", "", 0.02, -1);
|
||||
if(!IsValidPlayer(this.target))
|
||||
{
|
||||
Vscripts_CreateTimer(this.rate, Tick_Cb, this);
|
||||
return;
|
||||
}
|
||||
float angl[3], s_orig[3], t_orig[3];
|
||||
Vscripts_GetAngles(this.entity, angl);
|
||||
Vscripts_GetOrigin(this.entity, s_orig);
|
||||
Vscripts_GetOrigin(this.target, t_orig);
|
||||
float sa = angl[1];
|
||||
float ta = this.GetTargetYaw(s_orig, t_orig);
|
||||
float ang = FloatAbs((sa - ta + 360.0) % 360.0);
|
||||
if (ang >= 180.0)
|
||||
Vscripts_EntFireByIndex(this.ts, "AddOutput", "angles 0 270 0", 0.0, -1);
|
||||
else
|
||||
Vscripts_EntFireByIndex(this.ts, "AddOutput", "angles 0 90 0", 0.0, -1);
|
||||
float angdif = (sa - ta - 180.0);
|
||||
while (angdif > 360.0) { angdif -= 180.0; }
|
||||
while (angdif < -180.0) { angdif += 360.0; }
|
||||
angdif = FloatAbs(angdif);
|
||||
char input[MAX_INPUT_NAME];
|
||||
Format(input, sizeof(input), "force %.4f", 3000.0 * this.forward_factor);
|
||||
Vscripts_EntFireByIndex(this.tf, "AddOutput", input, 0.0, -1);
|
||||
Format(input, sizeof(input), "force %.4f", (3.0 * this.turning_factor) * angdif);
|
||||
Vscripts_EntFireByIndex(this.ts, "AddOutput", input, 0.0, -1);
|
||||
Vscripts_CreateTimer(this.rate, Tick_Cb, this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick_Cb(MovingNpc npc)
|
||||
{
|
||||
if(npc.kill)
|
||||
{
|
||||
if(npc.lifetimer)
|
||||
KillTimer(npc.lifetimer);
|
||||
delete npc;
|
||||
return;
|
||||
}
|
||||
|
||||
if(npc.ticking)
|
||||
{
|
||||
npc.Tick();
|
||||
}
|
||||
else
|
||||
{
|
||||
Vscripts_EntFireByIndex(npc.tf, "Deactivate", "", 0.0, -1);
|
||||
Vscripts_EntFireByIndex(npc.ts, "Deactivate", "", 0.0, -1);
|
||||
}
|
||||
}
|
||||
|
193
VScripts/scripting/include/vscripts_moving_npc.inc
Normal file
193
VScripts/scripting/include/vscripts_moving_npc.inc
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
**
|
||||
*/
|
||||
#if defined _VSCRIPTS_included
|
||||
#endinput
|
||||
#endif
|
||||
#define _VSCRIPTS_included
|
||||
#include <sdktools>
|
||||
#undef REQUIRE_EXTENSIONS
|
||||
#include <eventqueue>
|
||||
#define REQUIRE_EXTENSIONS
|
||||
|
||||
#define MAX_ENT_NAME 32
|
||||
#define MAX_INPUT_NAME 32
|
||||
|
||||
typedef VscriptTimerCallback = function void(any data);
|
||||
|
||||
native void Vscripts_CreateTimer(float interval, VscriptTimerCallback func, any data = INVALID_HANDLE);
|
||||
native bool Vscripts_IsEventQueueLoaded();
|
||||
native bool Vscripts_TraceFilterSimple(int entity, int contentsMask, int ignore = -1);
|
||||
|
||||
forward void Vscritps_OnTemplateInstanceCreated(int template, const int[] createdEntities, int size);
|
||||
|
||||
stock int Vscripts_GetEntityIndexByHammerID(int HammerID, const char[] classname = "*", int startEnt = -1)
|
||||
{
|
||||
while((startEnt = FindEntityByClassname(startEnt,classname))!= -1) {
|
||||
if (GetEntProp(startEnt, Prop_Data, "m_iHammerID") == HammerID)
|
||||
return startEnt;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
stock int Vscripts_GetEntityIndexByName(const char[] name, const char[] classname = "*", int startEnt = -1)
|
||||
{
|
||||
char buffer[MAX_ENT_NAME];
|
||||
int ch = FindCharInString(name, '*', true);
|
||||
while((startEnt = FindEntityByClassname(startEnt, classname))!= -1){
|
||||
GetEntityClassname(startEnt, buffer, sizeof(buffer));
|
||||
if (strcmp(name, buffer) == 0)
|
||||
return startEnt;
|
||||
|
||||
GetEntPropString(startEnt, Prop_Data, "m_iName", buffer, sizeof(buffer));
|
||||
if(strncmp(name, buffer, ch) == 0)
|
||||
return startEnt;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int Vscripts_FindEntityByClassnameWithin(int startEnt, const char[] classname, const float origin[3], const float radius)
|
||||
{
|
||||
float torigin[3];
|
||||
while((startEnt = FindEntityByClassname(startEnt,classname))!= -1){
|
||||
Vscripts_GetOrigin(startEnt, torigin);
|
||||
if(GetVectorDistance(torigin, origin) <= radius) return startEnt;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public float Vscripts_TraceLine(const float origin[3], const float v2[3], int entity)
|
||||
{
|
||||
TR_TraceRayFilter(origin, v2, MASK_NPCWORLDSTATIC, RayType_EndPoint, TraceEntityFilterSelf, entity);
|
||||
float fraction_left = TR_GetFractionLeftSolid(INVALID_HANDLE), fraction = 0.0;
|
||||
if (fraction_left && TR_StartSolid(INVALID_HANDLE)) {
|
||||
fraction = 1.0 - fraction_left;
|
||||
}
|
||||
else {
|
||||
fraction = TR_GetFraction(INVALID_HANDLE);
|
||||
}
|
||||
return fraction;
|
||||
}
|
||||
|
||||
bool TraceEntityFilterSelf(int entity, int contentsMask, int data)
|
||||
{
|
||||
return Vscripts_TraceFilterSimple(entity, contentsMask, data);
|
||||
}
|
||||
|
||||
public void Vscripts_GetOrigin(int entity, float buffer[3])
|
||||
{
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", buffer);
|
||||
}
|
||||
|
||||
public void Vscripts_GetAngles(int entity, float buffer[3])
|
||||
{
|
||||
GetEntPropVector(entity, Prop_Send, "m_angRotation", buffer);
|
||||
}
|
||||
|
||||
public void Vscripts_SetAngles(int entity, const float buffer[3])
|
||||
{
|
||||
TeleportEntity(entity, NULL_VECTOR, buffer, NULL_VECTOR);
|
||||
}
|
||||
|
||||
public void Vscripts_SetOrigin(int entity, const float buffer[3])
|
||||
{
|
||||
TeleportEntity(entity, buffer, NULL_VECTOR, NULL_VECTOR);
|
||||
}
|
||||
|
||||
public void Vscripts_GetForwardVector(int entity, float buffer[3])
|
||||
{
|
||||
float tmp[3];
|
||||
Vscripts_GetAngles(entity, tmp);
|
||||
GetAngleVectors(tmp, buffer, NULL_VECTOR, NULL_VECTOR);
|
||||
}
|
||||
|
||||
public void Vscripts_SetForwardVector(int entity, const float buffer[3])
|
||||
{
|
||||
float tmp[3];
|
||||
GetVectorAngles(buffer, tmp);
|
||||
Vscripts_SetAngles(entity, tmp);
|
||||
}
|
||||
|
||||
stock void Vscripts_EntFire(const char[] target, const char[] input, const char[] parametr, float delay, int activator = -1)
|
||||
{
|
||||
if(Vscripts_IsEventQueueLoaded())
|
||||
EQ_AddEventByName(target, input, parametr, delay, activator);
|
||||
else
|
||||
{
|
||||
DataPack data = CreateDataPack();
|
||||
data.WriteString(input);
|
||||
data.WriteString(parametr);
|
||||
data.WriteCell(activator >= 0 ? EntIndexToEntRef(activator) : -1);
|
||||
data.WriteCell(true);
|
||||
data.WriteString(target);
|
||||
Vscripts_CreateTimer(delay, InputDelay, data);
|
||||
}
|
||||
}
|
||||
|
||||
stock void Vscripts_EntFireByIndex(int target, const char[] input, const char[] parametr, float delay, int activator = -1)
|
||||
{
|
||||
if(Vscripts_IsEventQueueLoaded())
|
||||
EQ_AddEvent(target, input, parametr, delay, activator);
|
||||
else
|
||||
{
|
||||
DataPack data = CreateDataPack();
|
||||
data.WriteString(input);
|
||||
data.WriteString(parametr);
|
||||
data.WriteCell(activator >= 0 ? EntIndexToEntRef(activator) : -1);
|
||||
data.WriteCell(false);
|
||||
data.WriteCell(EntIndexToEntRef(target));
|
||||
Vscripts_CreateTimer(delay, InputDelay, data);
|
||||
}
|
||||
}
|
||||
|
||||
void InputDelay(DataPack data)
|
||||
{
|
||||
char input[MAX_INPUT_NAME], parametr[2*MAX_ENT_NAME];
|
||||
data.Reset();
|
||||
data.ReadString(input, sizeof(input));
|
||||
data.ReadString(parametr, sizeof(parametr));
|
||||
int activator = data.ReadCell();
|
||||
int target = -1;
|
||||
if(view_as<bool>(data.ReadCell()) == true)
|
||||
{
|
||||
char name[MAX_ENT_NAME];
|
||||
data.ReadString(name, sizeof(name));
|
||||
while((target = Vscripts_GetEntityIndexByName(name, _, target)) != -1)
|
||||
{
|
||||
SetVariantString(parametr);
|
||||
AcceptEntityInput(target, input, activator != -1 ? EntRefToEntIndex(activator) : -1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = EntRefToEntIndex(data.ReadCell());
|
||||
if(IsValidEntity(target))
|
||||
{
|
||||
SetVariantString(parametr);
|
||||
AcceptEntityInput(target, input, activator != -1 ? EntRefToEntIndex(activator) : -1);
|
||||
}
|
||||
}
|
||||
data.Reset(true);
|
||||
delete data;
|
||||
}
|
||||
|
||||
public SharedPlugin __pl_vscripts=
|
||||
{
|
||||
name = "vscripts",
|
||||
file = "Vscripts_Core.smx",
|
||||
#if defined REQUIRE_PLUGIN
|
||||
required = 1
|
||||
#else
|
||||
required = 0
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !defined REQUIRE_PLUGIN
|
||||
public void __pl_vscripts_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("Vscripts_CreateTimer");
|
||||
MarkNativeAsOptional("Vscripts_IsEventQueueLoaded");
|
||||
MarkNativeAsOptional("Vscripts_TraceFilterSimple");
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user