Compare commits

...

4 commits

19 changed files with 444 additions and 201 deletions

View file

@ -50,7 +50,7 @@ Colors_Init(void)
col_refresh_text = [255,255,255] / 255;
col_refresh_bg = [56,56,56] / 255;
col_button_bg = [1,1,1];
col_button_bg = [247,181,57] / 255;
col_button_select = [1,0,0];
col_button_click = [1,1,0];
col_button_disabled = [0.25, 0.25, 0.25];

View file

@ -103,44 +103,6 @@ var int gameinfo_current = -1;
int gameinfo_count;
gameinfo_t *games;
#ifndef WEBMENU
string(float id, float b) getgamedirinfo = #0;
string(int packageidx, int desiredfield) getpackagemanagerinfo = #0;
enum
{
GPMI_NAME, // name of the package, for use with the pkg command.
GPMI_CATEGORY, // category text
GPMI_TITLE, // name of the package, for showing the user.
GPMI_VERSION, // version info (may have multiple with the same name but different versions)
GPMI_DESCRIPTION, // some blurb
GPMI_LICENSE, // what license its distributed under
GPMI_AUTHOR, // name of the person(s) who created it
GPMI_WEBSITE, // where to contribute/find out more info/etc
GPMI_INSTALLED, // current state
GPMI_ACTION, // desired state
GPMI_AVAILABLE, // whether it may be downloaded or not.
GPMI_FILESIZE, // whether it may be downloaded or not.
};
typedef struct
{
string name;
string category;
string title;
string version;
string description;
string license;
string author;
string website;
string installed;
int size;
int uid;
} update_t;
int update_count;
update_t *updates;
#endif
int g_menupage;
enum

View file

@ -63,6 +63,22 @@ Menu_GammaHack(void)
}
}
bool
Menu_HasStartupVideos(void)
{
if (Platform_FileInCurrentGamedir("media/valve.avi"))
return true;
return false;
}
void
Menu_PlayStartupVideos(void)
{
print("playing startup videos\n");
//localcmd("playvideo av:media/sierra.avi av:media/valve.avi\n");
}
/* called upon menu init/restart */
void
m_init(void)
@ -121,6 +137,7 @@ m_init(void)
Colors_Init();
Strings_Init();
Updates_Init();
if (games[gameinfo_current].gamedir != "valve" || games[gameinfo_current].steambg == 1) {
m_intro_skip();
@ -153,6 +170,9 @@ m_init(void)
/* prepare spray logo keys */
spray_setinfokeys();
if (Menu_HasStartupVideos() == true)
Menu_PlayStartupVideos();
g_initialized = true;
}

View file

@ -721,10 +721,12 @@ customgame_lbmods_changed(void)
void
games_setdefaultpkgs(int id)
{
#if 0
if (games[id].gamedir == "valve") {
games[id].pkgname = "valve_patch;addon_furtherdata;addon_holidaymodels";
games[id].pkgfile = "maps/crossfire.bsp"; /* only found in patches */
}
#endif
}
void

View file

@ -14,27 +14,8 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define FN_UPDATE_IMGURL "http://www.frag-net.com/dl/img/%s.jpg"
#ifndef WEBMENU
enum
{
GPMI_NAME, // name of the package, for use with the pkg command.
GPMI_CATEGORY, // category text
GPMI_TITLE, // name of the package, for showing the user.
GPMI_VERSION, // version info (may have multiple with the same name but different versions)
GPMI_DESCRIPTION, // some blurb
GPMI_LICENSE, // what license its distributed under
GPMI_AUTHOR, // name of the person(s) who created it
GPMI_WEBSITE, // where to contribute/find out more info/etc
GPMI_INSTALLED, // current state
GPMI_ACTION, // desired state
GPMI_AVAILABLE, // whether it may be downloaded or not.
GPMI_FILESIZE, // size to download.
};
string(int, int) getpackagemanagerinfo = #0;
var int g_updates_initialized = FALSE;
var int autocvar_menu_updating = FALSE;
@ -63,49 +44,25 @@ up_btndone_start(void)
void
up_btninstall_start(void)
{
int pkgid;
pkgid = up_lbUpdates.GetSelected();
localcmd(sprintf("pkg add %s\n", updates[pkgid].name));
updates[pkgid].installed = "pending";
print(sprintf("Marking package %s for install.\n", updates[pkgid].title));
Updates_Remove(up_lbUpdates.GetSelected());
}
void
up_btnremove_start(void)
{
int pkgid;
pkgid = up_lbUpdates.GetSelected();
localcmd(sprintf("pkg rem %s\n", updates[pkgid].name));
updates[pkgid].installed = "rem";
print(sprintf("Marking package %s for 'removal'.\n", updates[pkgid].title));
Updates_Install(up_lbUpdates.GetSelected());
}
void
up_toggleinstall(void)
{
int pkgid;
pkgid = up_lbUpdates.GetSelected();
switch (updates[pkgid].installed) {
case "":
case "rem":
localcmd(sprintf("pkg add %s\n", updates[pkgid].name));
updates[pkgid].installed = "pending";
break;
default:
localcmd(sprintf("pkg rem %s\n", updates[pkgid].name));
updates[pkgid].installed = "rem";
}
Updates_Toggle(up_lbUpdates.GetSelected());
}
void
up_btnapply_start(void)
{
cvar_set("menu_updating", "1");
localcmd("pkg apply\n");
print("Applying package changes.\n");
Updates_ApplyPendingChanges();
}
void
@ -134,94 +91,14 @@ up_sbupdates_changed(int val)
up_lbUpdates.SetScroll(val);
}
void
menu_updates_refresh(void)
{
int c;
update_count = 0;
Updates_Refresh();
int updateCount = Updates_GetPackageCount();
if (updates) {
memfree(updates);
}
#if 0
for (int i = 0; (getpackagemanagerinfo(i, GPMI_NAME)); i++) {
string cat = getpackagemanagerinfo(i, GPMI_CATEGORY);
if (cat == "Plugins/") {
continue;
}
if (cat == "Mod/") {
continue;
}
update_count++;
}
#else
/* look for the valid packages in the gameinfo pkgname */
int pkgcount = tokenize(games[gameinfo_current].pkgname);
for (int i = 0i; i < pkgcount; i++) {
int id = game_getpackageid(argv(i));
if (id == -1)
continue;
update_count++;
}
#endif
c = 0;
updates = memalloc(sizeof(update_t) * update_count);
#if 0
for (int i = 0; (getpackagemanagerinfo(i, GPMI_NAME)); i++) {
string cat = getpackagemanagerinfo(i, GPMI_CATEGORY);
if (cat == "Plugins/") {
continue;
}
if (cat == "Mod/") {
continue;
}
updates[c].name = getpackagemanagerinfo(i, GPMI_NAME);
updates[c].category = getpackagemanagerinfo(i, GPMI_CATEGORY);
updates[c].title = getpackagemanagerinfo(i, GPMI_TITLE);
updates[c].version = getpackagemanagerinfo(i, GPMI_VERSION);
updates[c].description = getpackagemanagerinfo(i, GPMI_DESCRIPTION);
updates[c].license = getpackagemanagerinfo(i, GPMI_LICENSE);
updates[c].author = getpackagemanagerinfo(i, GPMI_AUTHOR);
updates[c].website = getpackagemanagerinfo(i, GPMI_WEBSITE);
updates[c].installed = getpackagemanagerinfo(i, GPMI_INSTALLED);
updates[c].size = (int)stof(getpackagemanagerinfo(i, GPMI_FILESIZE));
updates[c].uid = i;
precache_pic(sprintf(FN_UPDATE_IMGURL, updates[c].name));
c++;
}
#else
for (int i = 0i; i < pkgcount; i++) {
int id = game_getpackageid(argv(i));
if (id == -1)
continue;
updates[c].name = getpackagemanagerinfo(id, GPMI_NAME);
updates[c].category = getpackagemanagerinfo(id, GPMI_CATEGORY);
updates[c].title = getpackagemanagerinfo(id, GPMI_TITLE);
updates[c].version = getpackagemanagerinfo(id, GPMI_VERSION);
updates[c].description = getpackagemanagerinfo(id, GPMI_DESCRIPTION);
updates[c].license = getpackagemanagerinfo(id, GPMI_LICENSE);
updates[c].author = getpackagemanagerinfo(id, GPMI_AUTHOR);
updates[c].website = getpackagemanagerinfo(id, GPMI_WEBSITE);
updates[c].installed = getpackagemanagerinfo(id, GPMI_INSTALLED);
updates[c].size = (int)stof(getpackagemanagerinfo(id, GPMI_FILESIZE));
updates[c].uid = id;
precache_pic(sprintf(FN_UPDATE_IMGURL, updates[c].name));
c++;
}
#endif
up_sbUpdates.SetMax(update_count);
up_lbUpdates.SetMax(update_count);
up_sbUpdates.SetMax(updateCount);
up_lbUpdates.SetMax(updateCount);
up_lbUpdates.SetSelected(0);
}
#endif
@ -281,6 +158,8 @@ menu_updates_init(void)
up_frPreview.SetPos(350,160);
up_frPreview.SetSize(256+6,128+6);
Widget_Add(fn_updates, up_frPreview);
Updates_Refresh();
#endif
}
@ -312,8 +191,8 @@ menu_updates_draw(void)
}
/* query until 1 package is ready */
for (int i = 0; (getpackagemanagerinfo(i, GPMI_NAME)); i++) {
string installed = getpackagemanagerinfo(i, GPMI_INSTALLED);
for (int i = 0; (Updates_GetInfo(i, GPMI_NAME)); i++) {
string installed = Updates_GetInfo(i, GPMI_INSTALLED);
/* increment to keep track */
if (installed == "enabled")
old_enabled++;

View file

@ -14,6 +14,7 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "util.h"
#include "achievements.h"
#include "master.h"
#include "modserver.h"
@ -21,4 +22,4 @@
#include "richpresence.h"
#include "servers.h"
#include "tcp.h"
#include "util.h"
#include "updates.h"

View file

@ -7,4 +7,5 @@ richpresence.qc
servers.qc
tcp.qc
util.qc
updates.qc
#endlist

View file

@ -33,6 +33,12 @@ float srv_fldPlayers;
float srv_fldMaxplayers;
float srv_fldMap;
float srv_fldGame;
float srv_fldServerInfo;
float srv_fldPlayer0;
/* We cache these, because the engine may purge our cache anyway */
int g_masterInternetServers;
int g_masterLANServers;
/** Returns IP of master server. */
string Master_Resolve(void);

View file

@ -45,45 +45,39 @@ Master_GetTotalServers(void)
int
Master_GetLANServers(void)
{
int count = 0;
int tcount = 0;
count = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
for (int i = 0; i < count; i++) {
string address;
address = gethostcachestring(srv_fldAdress, i);
/* skip LAN */
if (!address || !Server_IsLan(address)) {
continue;
}
tcount++;
}
return tcount;
return g_masterLANServers;
}
int
Master_GetInternetServers(void)
{
int count = 0;
int tcount = 0;
return g_masterInternetServers;
}
count = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
void
Master_RecountServers(void)
{
int count = 0i;
g_masterInternetServers = 0i;
g_masterInternetServers = 0i;
count = (int)gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
for (int i = 0; i < count; i++) {
string address;
address = gethostcachestring(srv_fldAdress, i);
/* skip LAN */
if (!address || Server_IsLan(address)) {
/* skip empty entries */
if not (address)
continue;
}
tcount++;
}
return tcount;
/* skip LAN */
if (Server_IsLan(address)) {
g_masterLANServers++;
} else {
g_masterInternetServers++;
}
}
}
void
@ -95,7 +89,8 @@ Master_RefreshCache(void)
sethostcachesort(gethostcacheindexforkey("ping"), FALSE);
refreshhostcache(FALSE);
resorthostcache();
int a = gethostcachevalue(SLIST_HOSTCACHETOTALCOUNT);
Master_RecountServers();
int a = Master_GetLANServers() + Master_GetInternetServers();
if (a) {
NSLog("Master reports a total of %i servers.", a);
@ -111,7 +106,8 @@ Master_UpdateCache(void)
sethostcachesort(gethostcacheindexforkey("ping"), FALSE);
refreshhostcache(TRUE);
resorthostcache();
int a = gethostcachevalue(SLIST_HOSTCACHETOTALCOUNT);
Master_RecountServers();
int a = Master_GetLANServers() + Master_GetInternetServers();
if (a) {
NSLog("Master reports a total of %i servers.", a);
@ -129,6 +125,8 @@ Master_ResortCache(void)
srv_fldMaxplayers = gethostcacheindexforkey("maxplayers");
srv_fldMap = gethostcacheindexforkey("map");
srv_fldGame = gethostcacheindexforkey("game");
srv_fldServerInfo = gethostcacheindexforkey("serverinfo");
srv_fldPlayer0 = gethostcacheindexforkey("player");
}
void

67
src/platform/updates.h Normal file
View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2016-2022 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.
*/
string(float id, float b) getgamedirinfo = #0;
string(int packageidx, int desiredfield) getpackagemanagerinfo = #0;
typedef enum
{
GPMI_NAME, /**< name of the package, for use with the pkg command. */
GPMI_CATEGORY, /**< category text */
GPMI_TITLE, /**< name of the package, for showing the user. */
GPMI_VERSION, /**< version info (may have multiple with the same name but different versions) */
GPMI_DESCRIPTION, /**< some blurb */
GPMI_LICENSE, /**< what license its distributed under */
GPMI_AUTHOR, /**< name of the person(s) who created it */
GPMI_WEBSITE, /**< where to contribute/find out more info/etc */
GPMI_INSTALLED, /**< current state */
GPMI_ACTION, /**< desired state */
GPMI_AVAILABLE, /**< whether it may be downloaded or not. */
GPMI_FILESIZE, /**< size to download. */
} updateType_t;
typedef struct
{
string name;
string category;
string title;
string version;
string description;
string license;
string author;
string website;
string installed;
int size;
int uid;
} update_t;
int g_platform_update_count;
update_t *updates;
#define FN_UPDATE_IMGURL "http://www.frag-net.com/dl/img/%s.jpg"
void Updates_Init(void);
void Updates_Refresh(void);
int Updates_GetPackageCount(void);
int Updates_IDForName(string);
string Updates_NameForID(int);
string Updates_GetInfo(int, updateType_t);
bool Updates_Available(void);
bool Updates_Toggle(int);
bool Updates_Install(int);
bool Updates_Remove(int);
bool Updates_ApplyPendingChanges(void);

153
src/platform/updates.qc Normal file
View file

@ -0,0 +1,153 @@
/** needs to be called upon menu-init, and call Updates_Refresh() if auto-updates
are enabled. if a chooser does not want updates, then we won't. */
void
Updates_Init(void)
{
/*localcmd("pkg addsource https://www.frag-net.com/pkgs/list\n");*/
print("Update system initialized.\n");
}
/** will return a cached value */
int
Updates_GetPackageCount(void)
{
return g_platform_update_count;
}
/** called whenever we need to re-initialize the updates struct */
void
Updates_Refresh(void)
{
int c = 0i;
g_platform_update_count = 0i;
/* clear */
if (updates) {
memfree(updates);
}
/* count all updates that we've got in our package sources */
for (int i = 0i; (Updates_GetInfo(i, GPMI_NAME)); i++) {
g_platform_update_count++;
}
updates = memalloc(sizeof(update_t) * g_platform_update_count);
/* limit it to packages that the game wants */
tokenizebyseparator(games[gameinfo_current].pkgname, ";");
/* fill in all the package values */
for (int i = 0i; i < g_platform_update_count; i++) {
int id = game_getpackageid(argv(i));
if (id == -1)
continue;
updates[c].name = Updates_GetInfo(id, GPMI_NAME);
updates[c].category = Updates_GetInfo(id, GPMI_CATEGORY);
updates[c].title = Updates_GetInfo(id, GPMI_TITLE);
updates[c].version = Updates_GetInfo(id, GPMI_VERSION);
updates[c].description = Updates_GetInfo(id, GPMI_DESCRIPTION);
updates[c].license = Updates_GetInfo(id, GPMI_LICENSE);
updates[c].author = Updates_GetInfo(id, GPMI_AUTHOR);
updates[c].website = Updates_GetInfo(id, GPMI_WEBSITE);
updates[c].installed = Updates_GetInfo(id, GPMI_INSTALLED);
updates[c].size = (int)stof(Updates_GetInfo(id, GPMI_FILESIZE));
updates[c].uid = id;
precache_pic(sprintf(FN_UPDATE_IMGURL, updates[c].name));
c++;
}
print(sprintf("Updates packages for this game: %i (%i Total)\n", c, g_platform_update_count));
g_platform_update_count = c;
}
/** Returns the package ID for a given name. Will return -1 when not available. */
int
Updates_IDForName(string packageName)
{
string tempString = "";
for (int i = 0i; (tempString = getpackagemanagerinfo(i, GPMI_NAME)); i++) {
if (tempString == packageName)
return i;
}
return -1i;
}
/** Returns the package name for a given ID. Returns __NULL__ when not available. */
string
Updates_NameForID(int packageID)
{
string packageName = getpackagemanagerinfo(packageID, GPMI_NAME);
if not (packageName)
return __NULL__;
return packageName;
}
/** Query a package (by ID) for its various info fields. See updateType_t for available fields. */
string
Updates_GetInfo(int packageID, updateType_t fieldType)
{
return getpackagemanagerinfo(packageID, (int)fieldType);
}
/** Returns if our current game has updates available for any installed packages. */
bool
Updates_Available(void)
{
return true;
}
/** Toggle the installation of a package. Will return true if it was done. */
bool
Updates_Toggle(int packageID)
{
switch (updates[packageID].installed) {
case "":
case "rem":
localcmd(sprintf("pkg add %s\n", updates[packageID].name));
updates[packageID].installed = "pending";
break;
default:
localcmd(sprintf("pkg rem %s\n", updates[packageID].name));
updates[packageID].installed = "rem";
}
return true;
}
/** Mark a package as pending installion. May return true/false if it succeeded in marking the package. */
bool
Updates_Install(int packageID)
{
localcmd(sprintf("pkg add %s\n", updates[packageID].name));
updates[packageID].installed = "pending";
print(sprintf("Marking package %s for install.\n", updates[packageID].title));
return true;
}
/** Mark a package as pending deletion. May return true/false if it succeeded in marking the package. */
bool
Updates_Remove(int packageID)
{
localcmd(sprintf("pkg rem %s\n", updates[packageID].name));
updates[packageID].installed = "rem";
print(sprintf("Marking package %s for 'removal'.\n", updates[packageID].title));
return true;
}
/** Apply all pending changes to packages. May return true/false if it succeeded in doing so. */
bool
Updates_ApplyPendingChanges(void)
{
cvar_set("menu_updating", "1");
localcmd("pkg apply\n");
print("Applying package changes.\n");
return true;
}

View file

@ -26,6 +26,7 @@
#include "route.h"
#include "way.h"
#include "lament.h"
/* helper macros */
#define EVALUATE_FIELD(fieldname, changedflag) {\

View file

@ -255,6 +255,8 @@ SV_RunClientCommand(void)
if (self.classname != "player" && self.classname != "spectator")
return;
CheatersLament((NSClientPlayer)cl, input_angles, input_buttons, input_timelength);
if (!Plugin_RunClientCommand()) {
/* TODO */
}

View file

@ -9,6 +9,7 @@ NSGameRules.qc
client.qc
NSTraceAttack.qc
vote.qc
lament.qc
weapons.qc
modelevent.qc
mapcycle.qc

17
src/server/lament.h Normal file
View file

@ -0,0 +1,17 @@
/*
* 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 CheatersLament(NSClientPlayer, vector, float, float);

117
src/server/lament.qc Normal file
View file

@ -0,0 +1,117 @@
/*
* 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.
*/
/* cheaters lament
a system designed to analyze movement deltas and warn about suspicious
behaviour as it is happening.
explanation:
intense movement comes with decreased accuracy. if a player is
showcasing signs of above average reaction time and consistent success
in trailing (and shooting) valid targets we are going to inform the
server admin of suspicious behaviour.
the system does not act on its own, it merely gives feedback and
expects the host to act upon it.
we can tally the feedback and create a score based on suspicious
behaviour as a result. these results should ideally be made public
to the other players so they themselves can make a choice
technical deep dive:
we accumulate the camera angle deltas over time to track
the average mouse movement intensity over a short period of time.
waiting a full second will actually remove a full 360 degrees
of delta.
then we also accumulate accuracy information, we increases a counter
whenever the player is actively firing, while also aiming at an opposing
player within an radius of less than 10 degrees (to compensate for rocket shots
and othe projectile trails).
whenever the player is firing and not aiming with that level of precision,
the counter goes down.
if the deltas together reach a high point together, that must mean that
the player is having frantic mouse movements, combined with
incredible hit accuracy.
the high point calibration is taken from analyzing various demos
of known pro players as well as some cheating incidents.
limitations:
when going through teleporters, the angle deltas may be high
enough to trigger false positives.
this is obviously not a perfect system, but it is to be used
as a tool to assist in the decision making that goes into
moderating a game server.
*/
void
CheatersLament(NSClientPlayer playerEntity, vector absAngles, float buttons, float timeLength)
{
vector angleDelta = playerEntity.pb_last_angles - absAngles; /* diff between old and new */
/* only decrement above 0 */
if (playerEntity.pb_angle_delta > 0.0)
playerEntity.pb_angle_delta -= timeLength * 360;
else
playerEntity.pb_angle_delta = 0.0f;
/* ditto */
if (playerEntity.pb_player_delta > 0.0)
playerEntity.pb_player_delta -= timeLength;
else
playerEntity.pb_player_delta = 0.0f;
/* if the player is firing, calculate the player delta */
if (buttons & INPUT_BUTTON0 || buttons & INPUT_BUTTON3) { /* primary & secondary fire counts */
vector deltaPosition;
float deltaDegrees;
makevectors( absAngles );
for (entity e = world; (e = find(e, ::classname, "player"));) {
deltaPosition = normalize( e.origin - playerEntity.origin );
deltaDegrees = deltaPosition * v_forward;
other = world;
traceline(e.origin, playerEntity.origin, MOVE_OTHERONLY, playerEntity);
if (trace_fraction == 1.0f && deltaDegrees >= 0.95) {
playerEntity.pb_player_delta += timeLength * 4.0f;
}
}
}
playerEntity.pb_last_angles = absAngles; /* set so we don't accumulate */
/* if our angle delta is less than 256, don't bother reporting */
if (vlen(angleDelta) < 256.0) {
return;
}
/* we will add the difference only if it is big enough from last frame */
playerEntity.pb_angle_delta += vlen(angleDelta);
/* if our delta is consistently above 1024 and we've been trailing a player for 2
whole second... suspect foul play */
if (playerEntity.pb_angle_delta > 1024.0 && playerEntity.pb_player_delta > 2.0) {
localcmd(sprintf("echo Lamenting %S (%s) with (a: %f p: %f)\n", playerEntity.netname, infokey(playerEntity, INFOKEY_P_IP), playerEntity.pb_angle_delta, playerEntity.pb_player_delta));
}
}

View file

@ -168,6 +168,10 @@ private:
float m_flPainTime;
entity last_used;
float pb_angle_delta;
float pb_player_delta;
vector pb_last_angles;
#endif
};

View file

@ -61,6 +61,7 @@ typedef struct
string samples; /**< Separated list of samples. */
string name; /**< Name of the soundDef. */
string distshader; /**< soundDef to play where this soundDef is not audible. */
float pointparticle;
} snd_t;
/** A sound sample of a sentences.txt word sequence. */

View file

@ -172,6 +172,9 @@ Sound_ParseField(int i, int a)
case "distshader":
g_sounds[i].distshader = argv(1);
break;
case "pointparticle":
g_sounds[i].pointparticle = particleeffectnum(argv(1));
break;
case "alerts":
dprint("\tSound set to alert enemy AI\n");
g_sounds[i].flags |= SNDFL_ALERTS;
@ -550,6 +553,14 @@ Sound_Play(entity target, int chan, string shader)
}
#endif
#ifdef CLIENT
if (g_sounds[sample].pointparticle)
pointparticles(g_sounds[sample].pointparticle, target.origin, [0,0,0], 1);
#else
if (g_sounds[sample].pointparticle)
print(sprintf("SoundDef %S is attempting to spawn a particle on the server side.\n", shader));
#endif
sound(
target,
chan,