From cad9e7c0e6bb22a945273ab6a39601db86d7db20 Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Sun, 29 Mar 2020 19:59:19 +0200 Subject: Implement coronavirus --- sound/player/cough0.wav | Bin 0 -> 66692 bytes sound/player/cough1.wav | Bin 0 -> 71250 bytes sound/player/cough2.wav | Bin 0 -> 35824 bytes sound/player/cough3.wav | Bin 0 -> 55062 bytes sound/player/cough4.wav | Bin 0 -> 41476 bytes src/cgame/cg_event.c | 8 +++ src/cgame/cg_local.h | 3 + src/cgame/cg_main.c | 3 + src/game/bg_public.h | 8 ++- src/game/g_active.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++- src/game/g_client.c | 10 ++++ src/game/g_combat.c | 5 +- src/game/g_local.h | 14 +++++ 13 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 sound/player/cough0.wav create mode 100644 sound/player/cough1.wav create mode 100644 sound/player/cough2.wav create mode 100644 sound/player/cough3.wav create mode 100644 sound/player/cough4.wav diff --git a/sound/player/cough0.wav b/sound/player/cough0.wav new file mode 100644 index 0000000..0150d90 Binary files /dev/null and b/sound/player/cough0.wav differ diff --git a/sound/player/cough1.wav b/sound/player/cough1.wav new file mode 100644 index 0000000..dc2e2ba Binary files /dev/null and b/sound/player/cough1.wav differ diff --git a/sound/player/cough2.wav b/sound/player/cough2.wav new file mode 100644 index 0000000..4578b47 Binary files /dev/null and b/sound/player/cough2.wav differ diff --git a/sound/player/cough3.wav b/sound/player/cough3.wav new file mode 100644 index 0000000..d767fb7 Binary files /dev/null and b/sound/player/cough3.wav differ diff --git a/sound/player/cough4.wav b/sound/player/cough4.wav new file mode 100644 index 0000000..803aabc Binary files /dev/null and b/sound/player/cough4.wav differ diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c index 07bcea9..c06a05c 100644 --- a/src/cgame/cg_event.c +++ b/src/cgame/cg_event.c @@ -132,6 +132,9 @@ static void CG_Obituary( entityState_t *ent ) case MOD_SWARM: message = "was hunted down by the swarm"; break; + case MOD_CORONAVIRUS: + message = "died of viral lung infection"; + break; default: message = NULL; break; @@ -658,6 +661,11 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) ); break; + case EV_COUGH: + DEBUGNAME( "EV_COUGH" ); + trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.coughingSounds[ rand ( ) % NUM_COUGHING_SOUNDS ] ); + break; + // // weapon events // diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h index e68e94b..7618118 100644 --- a/src/cgame/cg_local.h +++ b/src/cgame/cg_local.h @@ -1146,6 +1146,7 @@ typedef struct int topSpeedTime; } cg_t; +#define NUM_COUGHING_SOUNDS 5 // all of the model, shader, and sound references that are // loaded at gamestate time are stored in cgMedia_t @@ -1277,6 +1278,8 @@ typedef struct qhandle_t buildWeaponTimerPie[ 8 ]; qhandle_t upgradeClassIconShader; + + sfxHandle_t coughingSounds[ NUM_COUGHING_SOUNDS ]; } cgMedia_t; typedef struct diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c index 89a4c5c..24a82c2 100644 --- a/src/cgame/cg_main.c +++ b/src/cgame/cg_main.c @@ -955,6 +955,9 @@ static void CG_RegisterSounds( void ) cgs.media.buildableRepairedSound = trap_S_RegisterSound( "sound/buildables/human/repaired.wav", qfalse ); cgs.media.lCannonWarningSound = trap_S_RegisterSound( "models/weapons/lcannon/warning.wav", qfalse ); + + for( i = 0; i < NUM_COUGHING_SOUNDS; i++ ) + cgs.media.coughingSounds[ i ] = trap_S_RegisterSound( va( "sound/player/cough%d.wav", i ), qfalse ); } diff --git a/src/game/bg_public.h b/src/game/bg_public.h index 69967a1..b04b681 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -579,7 +579,9 @@ typedef enum EV_DCC_ATTACK, //TA: dcc under attack - EV_RPTUSE_SOUND //TA: trigger a sound + EV_RPTUSE_SOUND, //TA: trigger a sound + + EV_COUGH } entity_event_t; typedef enum @@ -894,7 +896,9 @@ typedef enum MOD_ASPAWN, MOD_ATUBE, MOD_OVERMIND, - MOD_SLAP + MOD_SLAP, + + MOD_CORONAVIRUS } meansOfDeath_t; diff --git a/src/game/g_active.c b/src/game/g_active.c index 1dd27f4..528b927 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -73,7 +73,8 @@ void P_DamageFeedback( gentity_t *player ) if( ( level.time > player->pain_debounce_time ) && !( player->flags & FL_GODMODE ) ) { player->pain_debounce_time = level.time + 700; - G_AddEvent( player, EV_PAIN, player->health > 255 ? 255 : player->health ); + if( player->lastDamageMOD != MOD_CORONAVIRUS ) + G_AddEvent( player, EV_PAIN, player->health > 255 ? 255 : player->health ); client->ps.damageEvent++; } @@ -561,6 +562,157 @@ qboolean ClientInactivityTimer( gclient_t *client ) return qtrue; } +/* +================== +G_ContractCoronavirus + +Called once per player when contracting the virus. +There's a chance this is called at spawn. +================== +*/ + +void G_ContractCoronavirus( gentity_t *ent ) +{ + float rng; + + rng = random( ); + + if ( rng < 0.3f ) + ent->client->covidKind = COVID_ASYMPTOMATIC; + else if ( rng < 0.5f ) // 20% chance + ent->client->covidKind = COVID_SEVERE; + else + ent->client->covidKind = COVID_MODERATE; + + trap_SendServerCommand( (int)( ent - g_entities ), + va("print \"^1COVID: ^7You contracted COVID of kind ^1%d^7.\n\"", ent->client->covidKind ) ); +} + + +/* +================== +G_Coronavirus + +Runs every second, spreads the disease and causes symptoms. +================== +*/ + +#define COVID_RANGE 300.0f +#define COVID_INCUBATION_PERIOD 60.0f +#define COVID_AVERAGE_LENGTH 240.0f + +void G_Coronavirus( gentity_t *ent ) +{ + int entityList[ MAX_GENTITIES ]; + vec3_t range = { COVID_RANGE, COVID_RANGE, COVID_RANGE }; + vec3_t mins, maxs; + int i, num; + gclient_t *client = ent->client; + + if( client->covidKind == COVID_NONE + || client->covidKind == COVID_RECOVERED ) + return; + + VectorAdd( client->ps.origin, range, maxs ); + VectorSubtract( client->ps.origin, range, mins ); + + // Infect nearby players + + num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); + for( i = 0; i < num; i++ ) + { + gentity_t *target; + trace_t tr; + float chance = 0.001f; // to spread the disease + float distance; + + target = g_entities + entityList[ i ]; + + if( !target->client || target->health < 0 ) + continue; + + if( target->client->covidKind != COVID_NONE ) + continue; + + trap_Trace( &tr, ent->s.origin, NULL, NULL, target->s.origin, target->s.number, MASK_SHOT ); + if( tr.entityNum == ENTITYNUM_WORLD ) + continue; + + if( client->pers.teamSelection == target->client->pers.teamSelection ) + chance *= 10.0f; + + if( BG_InventoryContainsUpgrade( UP_HELMET, target->client->ps.stats ) ) + chance /= 2.0f; + + if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, target->client->ps.stats ) ) + chance /= 8.0f; + + distance = Distance(ent->s.origin, target->s.origin); + + if( distance < COVID_RANGE / 10.0f ) + chance *= 100.0f; + else if( distance < COVID_RANGE / 5.0f ) + chance *= 20.0f; + else if( distance < COVID_RANGE / 3.0f ) + chance *= 5.0f; + else if( distance < COVID_RANGE / 2.0f ) + chance *= 2.0f; + + trap_SendServerCommand( (int)( ent - g_entities ), va( "print \"^1COVID:^7 Chance to infect %s^7 is ^1%f^7\n\"", + target->client->pers.netname, chance ) ); + + if( random( ) < chance ) + { + trap_SendServerCommand( (int)( ent - g_entities ), va( "print \"^1COVID:^7 You spread the virus.\n\"" ) ); + G_ContractCoronavirus( target ); + } + } + + // Progression of the disease + + client->covidProgress += 2.0f * random( ) / COVID_AVERAGE_LENGTH; + if( client->covidProgress > COVID_INCUBATION_PERIOD / COVID_AVERAGE_LENGTH ) + { + float factor; + + factor = 1 - 2 * COVID_INCUBATION_PERIOD / COVID_AVERAGE_LENGTH / 3 - client->covidProgress; + client->covidSeverity += 0.4f * factor * client->covidSeverity + 0.01f * factor; + } + + if( client->covidKind == COVID_ASYMPTOMATIC ) + client->covidSeverity *= 0.8f; + else if( client->covidKind == COVID_MODERATE ) + client->covidSeverity *= 0.91f; + else + client->covidSeverity *= 0.935f; + + if( client->covidProgress > 0.75f && client->covidSeverity < 0.01f ) + { + client->covidKind = COVID_RECOVERED; + return; + } + + // Symptoms + + if( random( ) < client->covidSeverity / 4.0f ) + { + G_AddEvent( ent, EV_COUGH, 0 ); + } + + client->covidDamage += client->covidSeverity; + if( client->covidDamage > 1.0f ) + { + int damage; + + damage = floor( client->covidDamage ); + client->covidDamage -= damage; + G_Damage( ent, NULL, NULL, NULL, NULL, damage, DAMAGE_NO_PROTECTION, MOD_CORONAVIRUS ); + } + + trap_SendServerCommand( (int)( ent - g_entities ), va( "print \"^1COVID^7: Kind=^1%d^7, progress=^1%f^7, severity=^1%f^7.\n\"", + client->covidKind, client->covidProgress, client->covidSeverity) ); +} + /* ================== ClientTimerActions @@ -967,6 +1119,8 @@ void ClientTimerActions( gentity_t *ent, int msec ) } } } + + G_Coronavirus( ent ); } while( client->time10000 >= 10000 ) diff --git a/src/game/g_client.c b/src/game/g_client.c index d204831..9f6710c 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -1907,6 +1907,16 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles ent->client->ps.stats[ STAT_STATE ] = 0; VectorSet( ent->client->ps.grapplePoint, 0.0f, 0.0f, 1.0f ); + client->covidKind = COVID_NONE; + client->covidProgress = 0.0f; + client->covidSeverity = 0.0f; + client->covidDamage = 0.0f; + + // 1 in 10 chance they spawn sick + //if( rand( ) % 10 == 0 ) + if( client->pers.classSelection != PCL_NONE ) + G_ContractCoronavirus( ent ); + // health will count down towards max_health ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; //* 1.25; diff --git a/src/game/g_combat.c b/src/game/g_combat.c index aef76d1..5b4dac6 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -118,7 +118,9 @@ char *modNames[ ] = "MOD_ASPAWN", "MOD_ATUBE", "MOD_OVERMIND", - "MOD_SLAP" + "MOD_SLAP", + + "MOD_CORONAVIRUS" }; /* @@ -1486,6 +1488,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, targ->client->ps.stats[ STAT_HEALTH ] = targ->health; targ->lastDamageTime = level.time; + targ->lastDamageMOD = mod; //TA: add to the attackers "account" on the target if( targ->client && attacker->client ) diff --git a/src/game/g_local.h b/src/game/g_local.h index baf69ee..68c1ac8 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -256,6 +256,7 @@ struct gentity_s int suicideTime; // when the client will suicide int lastDamageTime; + int lastDamageMOD; int bdnumb; // buildlog entry ID @@ -469,6 +470,14 @@ typedef struct { float rangeBoost; } adminRangeBoosts_t; +enum { + COVID_NONE, + COVID_ASYMPTOMATIC, + COVID_MODERATE, + COVID_SEVERE, + COVID_RECOVERED +}; + // this structure is cleared on each ClientSpawn(), // except for 'client->pers' and 'client->sess' struct gclient_s @@ -570,6 +579,10 @@ struct gclient_s adminRangeBoosts_t newRange; + int covidKind; + float covidProgress; + float covidSeverity; + float covidDamage; }; @@ -1171,6 +1184,7 @@ void G_UnlaggedClear( gentity_t *ent ); void G_UnlaggedCalc( int time, gentity_t *skipEnt ); void G_UnlaggedOn( gentity_t *attacker, vec3_t muzzle, float range ); void G_UnlaggedOff( void ); +void G_ContractCoronavirus( gentity_t *ent ); void ClientThink( int clientNum ); void ClientEndFrame( gentity_t *ent ); void G_RunClient( gentity_t *ent ); -- cgit