summaryrefslogtreecommitdiff
path: root/src/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/game')
-rw-r--r--src/game/g_active.c11
-rw-r--r--src/game/g_admin.c116
-rw-r--r--src/game/g_admin.h2
-rw-r--r--src/game/g_client.c4
-rw-r--r--src/game/g_combat.c30
-rw-r--r--src/game/g_local.h14
-rw-r--r--src/game/g_main.c2
7 files changed, 174 insertions, 5 deletions
diff --git a/src/game/g_active.c b/src/game/g_active.c
index 72e070e..c419e30 100644
--- a/src/game/g_active.c
+++ b/src/game/g_active.c
@@ -655,6 +655,7 @@ void ClientTimerActions( gentity_t *ent, int msec )
int staminaLarmour = STAMINA_LARMOUR_TAKE;
int staminaSprint = STAMINA_SPRINT_TAKE;
int timestep10000;
+ adminCurses_t *curses;
ucmd = &ent->client->pers.cmd;
@@ -666,6 +667,8 @@ void ClientTimerActions( gentity_t *ent, int msec )
client->time1000 += msec;
client->time10000 += msec;
+ curses = &client->curses;
+
if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) &&
g_modHumanStamina.integer > 0 )
{
@@ -689,9 +692,9 @@ void ClientTimerActions( gentity_t *ent, int msec )
{
//subtract stamina
if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) )
- client->ps.stats[ STAT_STAMINA ] -= staminaLarmour;
+ client->ps.stats[ STAT_STAMINA ] -= staminaLarmour * (curses->staminaUse + 1.0f);
else
- client->ps.stats[ STAT_STAMINA ] -= staminaSprint;
+ client->ps.stats[ STAT_STAMINA ] -= staminaSprint * (curses->staminaUse + 1.0f);
if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA )
client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA;
@@ -700,7 +703,7 @@ void ClientTimerActions( gentity_t *ent, int msec )
if( ( aForward <= 64 && aForward > 5 ) || ( aRight <= 64 && aRight > 5 ) )
{
//restore stamina
- client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE;
+ client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE * (curses->staminaRegen + 1.0f);
if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )
client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
@@ -708,7 +711,7 @@ void ClientTimerActions( gentity_t *ent, int msec )
else if( aForward <= 5 && aRight <= 5 )
{
//restore stamina faster
- client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE;
+ client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE * (curses->staminaRegen + 1.0f);
if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )
client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
diff --git a/src/game/g_admin.c b/src/game/g_admin.c
index ca641e0..b696cc9 100644
--- a/src/game/g_admin.c
+++ b/src/game/g_admin.c
@@ -117,6 +117,11 @@ g_admin_cmd_t g_admin_cmds[ ] =
"[^3name|slot#^7] ..."
},
+ {"curse", G_admin_curse, "curse",
+ "punish players without making it obvious",
+ "[^3name|slot#^7] ([^3option^7] [^5value^7])"
+ },
+
{"demo", G_admin_demo, "demo",
"turn admin chat off for the caller so it does not appear in demos. "
"this is a toggle use !demo again to turn warnings back on",
@@ -375,7 +380,7 @@ g_admin_cmd_t g_admin_cmds[ ] =
"sets the admin level of a player",
"[^3name|slot#|admin#^7] [^3level^7]"
},
-
+
{"showbans", G_admin_showbans, "showbans",
"display a (partial) list of active bans",
"(^5start at ban#^7) (^5name|IP|'-subnet'^7)"
@@ -10530,3 +10535,112 @@ qboolean G_admin_versions(gentity_t *ent, int skiparg)
ADMBP_end();
return qtrue;
}
+
+qboolean G_admin_curse(gentity_t *ent, int skiparg)
+{
+ char arg_name_raw[MAX_NAME_LENGTH];
+ char arg_name[MAX_NAME_LENGTH];
+ char arg_option[30];
+ int i, target_id;
+ float value;
+ gentity_t *target;
+ adminCurses_t *curses;
+
+ if (G_SayArgc() < 2 + skiparg) {
+ ADMP("^3!curse: ^7usage: !curse [target] ([option] [value])\n");
+ return qfalse;
+ }
+
+ G_SayArgv(1 + skiparg, arg_name_raw, sizeof(arg_name_raw));
+
+ if (!Q_stricmp(arg_name_raw, "-reset")) {
+ for (i = 0; i < level.maxclients; i++)
+ memset(&level.clients[i].curses, 0, sizeof(adminCurses_t));
+
+ G_AdminsPrintf("^3!curses: ^7reset by %s\n",
+ ent ? G_admin_adminPrintName(ent) : "console");
+ return qtrue;
+ }
+
+ G_SanitiseString(arg_name_raw, arg_name, sizeof(arg_name));
+
+ if (is_numeric(arg_name)) {
+ target_id = atoi(arg_name);
+
+ if (target_id < 0 || target_id >= MAX_CLIENTS) {
+ ADMP(va("^3!curse: ^7invalid client number\n"));
+ return qfalse;
+ }
+ } else {
+ int pids[ MAX_CLIENTS ];
+
+ if (G_ClientNumbersFromString(arg_name, pids) != 1) {
+ char error[MAX_STRING_CHARS];
+
+ G_MatchOnePlayer(pids, error, sizeof(error));
+ ADMP(va("^3!curse: ^7%s\n", error));
+ return qfalse;
+ }
+
+ target_id = pids[0];
+ }
+
+ target = g_entities + target_id;
+
+ if (!target->client ||
+ target->client->pers.connected != CON_CONNECTED) {
+ ADMP("^3!curse: ^7invalid target\n");
+ return qfalse;
+ }
+
+ curses = &target->client->curses;
+
+ if (G_SayArgc() < 4 + skiparg) {
+ ADMP(va("^3!curse: ^7info for %s^7:\n",
+ target->client->pers.netname));
+ ADMP(va("outDamage = %f\n", curses->outDamage));
+ ADMP(va("incDamage = %f\n", curses->incDamage));
+ ADMP(va("damageFeedback = %f\n", curses->damageFeedback));
+ ADMP(va("staminaUse = %f\n", curses->staminaUse));
+ ADMP(va("staminaRegen = %f\n", curses->staminaRegen));
+ return qtrue;
+ }
+
+ if (!g_adminCurses.integer) {
+ ADMP("^3!curse: ^7disabled by the server operator\n");
+ return qfalse;
+ }
+
+ if (!admin_higher(ent, target)) {
+ ADMP("^3!curse: ^7sorry, but your intended victim has a higher admin level than you\n");
+ return qfalse;
+ }
+
+ G_SayArgv(3 + skiparg, arg_option, sizeof(arg_option)); // var reuse
+ value = atof(arg_option);
+ G_SayArgv(2 + skiparg, arg_option, sizeof(arg_option));
+
+ if (!Q_stricmp(arg_option, "outdamage"))
+ curses->outDamage = value - 1.0f;
+ else if (!Q_stricmp(arg_option, "incdamage"))
+ curses->incDamage = value - 1.0f;
+ else if (!Q_stricmp(arg_option, "damagefeedback"))
+ curses->damageFeedback = value;
+ else if (!Q_stricmp(arg_option, "ffoutdamage"))
+ curses->ffOutDamage = value;
+ else if (!Q_stricmp(arg_option, "fffeedback"))
+ curses->ffFeedback = value;
+ else if (!Q_stricmp(arg_option, "staminause"))
+ curses->staminaUse = value - 1.0f;
+ else if (!Q_stricmp(arg_option, "staminaregen"))
+ curses->staminaRegen = value - 1.0f;
+ else {
+ ADMP("^3!curse: ^7unrecognized option\n");
+ return qfalse;
+ }
+
+ G_AdminsPrintf("^3!curse: ^7%s^7 cursed %s^7 with %s = %f\n",
+ ent ? G_admin_adminPrintName(ent) : "console",
+ target->client->pers.netname, arg_option, value);
+ return qtrue;
+}
diff --git a/src/game/g_admin.h b/src/game/g_admin.h
index 3f768f3..e13156e 100644
--- a/src/game/g_admin.h
+++ b/src/game/g_admin.h
@@ -371,6 +371,8 @@ qboolean G_admin_give( gentity_t *ent, int skiparg );
qboolean G_admin_versions( gentity_t *ent, int skiparg );
+qboolean G_admin_curse( gentity_t *ent, int skiparg );
+
void G_admin_print( gentity_t *ent, char *m );
void G_admin_buffer_print( gentity_t *ent, char *m );
void G_admin_buffer_begin( void );
diff --git a/src/game/g_client.c b/src/game/g_client.c
index 78e1c4f..6e941f5 100644
--- a/src/game/g_client.c
+++ b/src/game/g_client.c
@@ -1955,6 +1955,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
int i;
clientPersistant_t saved;
clientSession_t savedSess;
+ adminCurses_t savedCurses;
int persistant[ MAX_PERSISTANT ];
gentity_t *spawnPoint = NULL;
int flags;
@@ -2046,6 +2047,8 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
for( i = 0; i < MAX_PERSISTANT; i++ )
persistant[ i ] = client->ps.persistant[ i ];
+ memcpy( &savedCurses, &client->curses, sizeof( adminCurses_t ) );
+
eventSequence = client->ps.eventSequence;
memset( client, 0, sizeof( *client ) );
@@ -2053,6 +2056,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
client->sess = savedSess;
client->ps.ping = savedPing;
client->lastkilled_client = -1;
+ memcpy( &client->curses, &savedCurses, sizeof( adminCurses_t ) );
for( i = 0; i < MAX_PERSISTANT; i++ )
client->ps.persistant[ i ] = persistant[ i ];
diff --git a/src/game/g_combat.c b/src/game/g_combat.c
index d8eef62..f0ea442 100644
--- a/src/game/g_combat.c
+++ b/src/game/g_combat.c
@@ -1498,6 +1498,36 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
if( take < 1 )
take = 1;
+ // admin curses
+ if( attacker->client )
+ {
+ adminCurses_t *curses = &attacker->client->curses;
+ qboolean friendlyFire = qfalse;
+
+ if( targ->biteam == attacker->client->pers.teamSelection ||
+ OnSameTeam( targ, attacker ) )
+ friendlyFire = qtrue;
+
+ // feed some damage back to the attacker
+ if( curses->damageFeedback && !( dflags & DAMAGE_FEEDBACK ) )
+ G_Damage( attacker, inflictor, attacker, dir, point,
+ damage * curses->damageFeedback, dflags | DAMAGE_FEEDBACK, mod );
+
+ // feed some friendly fire back to the attacker
+ if( friendlyFire && curses->ffFeedback && !( dflags & DAMAGE_FEEDBACK ) )
+ G_Damage( attacker, inflictor, attacker, dir, point,
+ damage * curses->ffFeedback, dflags | DAMAGE_FEEDBACK, mod );
+
+ // multiply outgoing damage
+ take *= curses->outDamage + 1.0f;
+ if( friendlyFire )
+ take *= curses->ffOutDamage + 1.0f;
+ }
+
+ // admin curses: multiply incoming damage
+ if( targ->client )
+ take *= targ->client->curses.incDamage + 1.0f;
+
if( g_debugDamage.integer )
{
G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number,
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 46bc31d..e59a6ad 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -453,6 +453,16 @@ typedef struct unlagged_s {
qboolean used;
} unlagged_t;
+typedef struct {
+ float incDamage; // less one
+ float outDamage; // less one
+ float damageFeedback;
+ float ffOutDamage; // less one
+ float ffFeedback;
+ float staminaUse; // less one
+ float staminaRegen; // less one
+} adminCurses_t;
+
// this structure is cleared on each ClientSpawn(),
// except for 'client->pers' and 'client->sess'
struct gclient_s
@@ -553,6 +563,8 @@ struct gclient_s
int tkcredits[ MAX_CLIENTS ];
int revertCookie;
+
+ adminCurses_t curses;
};
@@ -1030,6 +1042,7 @@ void G_InitDamageLocations( void );
#define DAMAGE_KNOCKBACK 0x00000004 // affect velocity, not just view angles
#define DAMAGE_NO_PROTECTION 0x00000008 // armor, shields, invulnerability, and godmode have no effect
#define DAMAGE_NO_LOCDAMAGE 0x00000010 // do not apply locational damage
+#define DAMAGE_FEEDBACK 0x00000020 // damage comes from a feedback curse (do *not* feed it back again)
//
// g_missile.c
@@ -1477,6 +1490,7 @@ extern vmCvar_t g_banNotice;
extern vmCvar_t g_karma;
extern vmCvar_t g_chat;
extern vmCvar_t g_adminExpireTime;
+extern vmCvar_t g_adminCurses;
extern vmCvar_t g_nullifyTyrantKills;
diff --git a/src/game/g_main.c b/src/game/g_main.c
index 741f61a..82c963d 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -190,6 +190,7 @@ vmCvar_t g_adminRegisterAdminPass;
vmCvar_t g_adminRegisterAdminLevel;
vmCvar_t g_minLevelToJoinTeam;
vmCvar_t g_forceAutoSelect;
+vmCvar_t g_adminCurses;
vmCvar_t g_privateMessages;
vmCvar_t g_logPrivateMessages;
@@ -493,6 +494,7 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_karma, "g_karma", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_chat, "g_chat", "chat.dat", CVAR_ARCHIVE, 0, qfalse },
{ &g_adminExpireTime, "g_adminExpireTime", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_adminCurses, "g_adminCurses", "1", CVAR_ARCHIVE, 0, qfalse },
{ &g_devmapKillerHP, "g_devmapKillerHP", "0", CVAR_ARCHIVE, 0, qtrue },
{ &g_killerHP, "g_killerHP", "0", CVAR_ARCHIVE, 0, qfalse },