From 50d8779208632d2cc23be36e144b0297b0c02d2b Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Fri, 23 Feb 2018 20:43:53 +0100 Subject: Add !curse with some basic features. --- src/game/g_active.c | 11 +++-- src/game/g_admin.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/game/g_admin.h | 2 + src/game/g_client.c | 4 ++ src/game/g_combat.c | 30 ++++++++++++++ src/game/g_local.h | 14 +++++++ src/game/g_main.c | 2 + 7 files changed, 174 insertions(+), 5 deletions(-) (limited to 'src') 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 }, -- cgit