Files
quakemapping/mod_ad_dev/my_progs/client_debuff.qc
2019-12-30 22:24:44 +01:00

358 lines
12 KiB
Plaintext

/*======================================================================
CheckDeBuff - Check for negative buffs affecting the player
======================================================================*/
.float cshift_upd; // Screen updates (v_cshift) are active
.float cshift_time; // Timer (0.1s tick) for screen updates
.vector cshift_col; // RGB / XYZ values for screen colour
.float cshift_den; // Screen colour density
.float cshift_step; // Quantity to reduce density by per tick
.float debuff_sound; // Heartbeat sound when tick happens
.float debuff_qty; // Quantity to do something with (temp)
.float bleeding; // Bleeding debuff status
.float bleeding_time; // Timer (1s tick) for screen/sound update
.float bleeding_qty; // Total amount of health stored (HoT)
.float burning; // Burning debuff status
.float burning_time; // Timer (1s tick) for screen/sound update
.float burning_qty; // Total amount of damage stored (DoT)
.float poisoned; // Poison debuff status
.float poisoned_time; // Timer (1s tick) for screen/sound update
.float poisoned_qty; // Total amount of damage stored (DoT)
// IT_INVISIBILITY + IT_INVULNERABILITY + IT_SUIT + IT_QUAD
// 524288 + 1048576 + 2097152 + 4194304
float ALL_ITARTIFACTS = 7864320;
// Wraith Health drain
vector DEBUFF_BLEED_COL = '200 0 0'; // Strong red
float DEBUFF_BLEED_DEN = 120;
float DEBUFF_BLEED_STEP = 10;
float DEBUFF_BLEED_HEAL = 5;
float DEBUFF_BLEED_MIN = 5;
// Pyro flame burn
vector DEBUFF_BURN_COL = '200 100 0'; // Orange
float DEBUFF_BURN_DEN = 120;
float DEBUFF_BURN_STEP = 10;
float DEBUFF_BURN_DMG = 1;
float DEBUFF_BURN_MAX = 10;
// Mummy poisoned gibs
vector DEBUFF_POISON_COL = '0 200 0'; // Strong Green
float DEBUFF_POISON_DEN = 84;
float DEBUFF_POISON_STEP = 7;
float DEBUFF_POISON_DMG = 1;
//----------------------------------------------------------------------
// Various debuff reset functions, used all over the place
//----------------------------------------------------------------------
void(entity targ) ResetDebuffScreen =
{
targ.cshift_upd = FALSE;
targ.cshift_time = LARGE_TIMER;
stuffcmd(targ, "v_cshift 0 0 0 0\n");
targ.cshift_col = '0 0 0';
targ.cshift_den = 0;
};
void(entity targ) ResetDebuffBurning =
{ targ.burning = targ.burning_qty = 0; };
void(entity targ) ResetDebuffPoisoned =
{ targ.poisoned = targ.poisoned_qty = 0; };
void(entity targ) ResetDebuffBleeding =
{ targ.bleeding = targ.bleeding_qty = 0; };
void(entity targ) ResetDebuffSystem =
{
ResetDebuffScreen(targ);
ResetDebuffBurning(targ);
ResetDebuffPoisoned(targ);
ResetDebuffBleeding(targ);
};
//----------------------------------------------------------------------
// Core debuff function, run every frame from client.qc
//----------------------------------------------------------------------
void() ClientDeBuff =
{
// If player is dead or intermission running, turn everything off
if (intermission_running > 0 || self.health < 1) {
ResetDebuffSystem(self);
return;
}
// Check for burning debuff (DoT)
if (self.burning) {
// Is it time for a debuff update
if (self.burning_time < time) {
if (self.burning_qty > 0) {
// Reset burning tick timer
self.burning_time = time + 1;
// Give priority to bleeding(heal) and poisoned
if (!self.bleeding && !self.poisoned) {
if (self.debuff_sound < time) self.debuff_sound = time + 1;
self.cshift_upd = TRUE; // Update screen colour
self.cshift_time = -1; // Update striaght away
self.cshift_col = DEBUFF_BURN_COL;
self.cshift_den = DEBUFF_BURN_DEN;
self.cshift_step = DEBUFF_BURN_STEP;
}
// Setup default burning damage
self.debuff_qty = DEBUFF_BURN_DMG;
// Check burning does not exceed stored damage
if (self.burning_qty < self.debuff_qty) self.debuff_qty = self.burning_qty;
// Subtract burning damage from counters
self.burning_qty = self.burning_qty - self.debuff_qty;
// Use global damage function (has death checks)
T_Damage (self, world, world, self.debuff_qty, NOARMOR);
}
}
// Check if player is in water up to waist?
// 0 = not in, 1 = feet, 2 = waist, 3 = eyes
if (self.waterlevel >= 2 || self.burning_qty < 1) {
ResetDebuffBurning(self);
}
}
// Check for poisoned debuff (DoT)
if (self.poisoned) {
// Is it time for a debuff update
if (self.poisoned_time < time) {
if (self.poisoned_qty > 0) {
// Reset poisoned tick timer
self.poisoned_time = time + 1;
// Give priority to bleeding(heal)
if (!self.bleeding) {
if (self.debuff_sound < time) self.debuff_sound = time + 1;
self.cshift_upd = TRUE; // Update screen colour
self.cshift_time = -1; // Update striaght away
self.cshift_col = DEBUFF_POISON_COL;
self.cshift_den = DEBUFF_POISON_DEN;
self.cshift_step = DEBUFF_POISON_STEP;
}
// Setup default poison damage
self.debuff_qty = DEBUFF_POISON_DMG;
// Check poison does not exceed damage left
if (self.poisoned_qty < self.debuff_qty) self.debuff_qty = self.poisoned_qty;
// Subtract poison damage from counters
self.poisoned_qty = self.poisoned_qty - self.debuff_qty;
// Use global damage function (has death checks)
T_Damage (self, world, world, self.debuff_qty, NOARMOR);
}
}
// Check if poison has finished?
if (self.poisoned_qty < 1) ResetDebuffPoisoned(self);
}
// Check for bleeding debuff (HoT)
if (self.bleeding) {
// Is it time for a debuff update
if (self.bleeding_time < time) {
if (self.bleeding_qty > 0) {
// Reset bleeding(healing) tick timer
self.bleeding_time = time + 1;
if (self.debuff_sound < time) self.debuff_sound = time + 1;
self.cshift_upd = TRUE; // Update screen colour
self.cshift_time = -1; // Update striaght away
self.cshift_col = DEBUFF_BLEED_COL;
self.cshift_den = DEBUFF_BLEED_DEN;
self.cshift_step = DEBUFF_BLEED_STEP;
// Setup default healing
self.debuff_qty = DEBUFF_BLEED_HEAL;
// Check healing does not exceed current reserve
if (self.bleeding_qty < self.debuff_qty) self.debuff_qty = self.bleeding_qty;
// Subtract bleeding heals from counters
self.bleeding_qty = self.bleeding_qty - self.debuff_qty;
// Check healing does not go over max player limit
if (self.health + self.debuff_qty > self.max_health)
self.debuff_qty = self.max_health - self.health;
// Directly add health back to player
self.health = self.health + self.debuff_qty;
}
}
// Check if player is over max health or run out of healing
if (self.health >= self.max_health || self.bleeding_qty < 1) {
ResetDebuffBleeding(self);
}
}
// Check for any debuff screen updates?
if (self.cshift_upd) {
// Play different debuff sounds (pressure is on!)
if (self.debuff_sound > time) {
self.debuff_sound = -1; // Reset
// Sound exception for god/pentagram mode
if (self.flags & FL_GODMODE || self.invincible_finished >= time) {
if (self.invincible_sound < time) {
sound (self, CHAN_VOICE, SOUND_ARTPENT3, 1, ATTN_NORM);
self.invincible_sound = time + 2;
}
}
else {
// Play heartbeat only for bleeding/healing
if (self.bleeding)
sound (self, CHAN_VOICE, SOUND_HEARTBEAT, 1, ATTN_IDLE);
// Re-use player pain sounds
else if (self.poisoned) {
if (random() < 0.5) sound (self, CHAN_VOICE, "player/pain2.wav", 1, ATTN_IDLE);
else sound (self, CHAN_VOICE, "player/pain3.wav", 1, ATTN_IDLE);
}
// Play on the same channel as player pain
else if (self.burning) {
if (random() < 0.5) sound (self, CHAN_VOICE, "player/pain1.wav", 1, ATTN_IDLE);
else sound (self, CHAN_VOICE, "player/pain4.wav", 1, ATTN_IDLE);
}
}
}
// Check for screen update? 0.1s tick interval
if (self.cshift_time < time) {
// Generate some particles based on debuff
// Priority is bleeding > poisoned > burning
if (self.bleeding) particle_debuff(self.origin, 16, rint(1+random()*4), PARTICLE_BURST_RED);
else if (self.poisoned) particle_debuff(self.origin, 16, rint(1+random()*4), PARTICLE_BURST_GREEN);
else if (self.burning) particle_debuff(self.origin, 16, rint(1+random()*4), PARTICLE_BURST_YELLOW);
// Count down density and check for reset condition
self.cshift_den = self.cshift_den - self.cshift_step;
if (self.cshift_den < 1) ResetDebuffScreen(self);
else {
// Update screen using stuffcmds!
self.cshift_time = time + 0.1;
// Don't update the screen if an artifact is active
// The engine will be updating at the same time
if (self.items & ALL_ITARTIFACTS == 0) {
stuffcmd(self, "v_cshift ");
stuffcmd(self, ftos(rint(self.cshift_col_x)));
stuffcmd(self, " ");
stuffcmd(self, ftos(rint(self.cshift_col_y)));
stuffcmd(self, " ");
stuffcmd(self, ftos(rint(self.cshift_col_z)));
stuffcmd(self, " ");
stuffcmd(self, ftos(rint(self.cshift_den)));
stuffcmd(self, "\n");
}
}
}
}
// Reset cshift if nothing is active (engine does not reset)
// This is to catch new games or quickload resets
else if (self.cshift_time < time) ResetDebuffScreen(self);
};
//----------------------------------------------------------------------
void(entity targ) BleedDeBuff =
{
local float debuffqty;
// Check for minimum amount of health for debuff
// Immune to debuff if using Pent of Protection or BioSuit
if (targ.health < DEBUFF_BLEED_MIN) return;
if (targ.flags & FL_CLIENT == FALSE) return;
if (targ.items & (IT_INVULNERABILITY | IT_SUIT)) {
T_Damage (targ, self, self, DAMGE_WRAITHBOLT, DAMARMOR);
return;
}
// if debuff not active, starting bleeding!
if (!targ.bleeding) {
targ.bleeding = TRUE;
targ.bleeding_time = -1;
}
// Take health from player and store it
debuffqty = rint(targ.health * 0.5);
T_Damage (targ, self, self, debuffqty, NOARMOR);
targ.bleeding_qty = targ.bleeding_qty + debuffqty;
};
//----------------------------------------------------------------------
void(entity targ) BurnDeBuff =
{
local float debuffqty;
// Do not stack burning time beyond a certain limit
// Cannot be burning if knee deep in water
// Immune to debuff if using Pent of Protection or BioSuit
if (targ.burning_qty > DEBUFF_BURN_MAX) return;
if (targ.flags & FL_CLIENT == FALSE) return;
if (targ.waterlevel >= 3) return;
if (targ.items & (IT_INVULNERABILITY | IT_SUIT)) return;
// if debuff not active, starting burning!
if (!targ.burning) {
targ.burning = TRUE;
targ.burning_time = -1;
}
// work out time remaining on debuff
debuffqty = rint(5*skill + 3 + 2*random());
targ.burning_qty = targ.burning_qty + debuffqty;
if (targ.burning_qty > DEBUFF_BURN_MAX) targ.burning_qty = DEBUFF_BURN_MAX;
targ.cshift_upd = TRUE; // Update screen colour
targ.cshift_time = -1; // Update striaght away
targ.cshift_col = DEBUFF_BURN_COL;
targ.cshift_den = DEBUFF_BURN_DEN;
targ.cshift_step = DEBUFF_BURN_STEP;
};
//----------------------------------------------------------------------
void(entity targ) PoisonDeBuff =
{
local float debuffqty;
// Immune to debuff if using Pent of Protection or BioSuit
if (targ.flags & FL_CLIENT == FALSE) return;
if (targ.items & (IT_INVULNERABILITY | IT_SUIT)) return;
if (targ.poisoned) return; // Only poison once
// if debuff not active, start poison!
targ.poisoned = TRUE;
targ.poisoned_time = -1;
// work out time remaining on debuff
debuffqty = rint(skill + 3 + 2*random());
targ.poisoned_qty = targ.poisoned_qty + debuffqty;
targ.cshift_upd = TRUE; // Update screen colour
targ.cshift_time = -1; // Update striaght away
targ.cshift_col = DEBUFF_POISON_COL;
targ.cshift_den = DEBUFF_POISON_DEN;
targ.cshift_step = DEBUFF_POISON_STEP;
};
//----------------------------------------------------------------------
// Used by Pyro and Wraith; consistent damage from both monsters
//----------------------------------------------------------------------
void(entity targ, float playdmg, float mondmg) ApplyFireDmg =
{
local float reduction, firedamage;
// Bio suit reduction - easy=80%, normal=70%, hard=60%, nm=50%
reduction = 0.2 + skill * 0.1;
// Damage is different for monsters/players
if (targ.flags & FL_MONSTER) firedamage = mondmg;
else if (targ.items & IT_SUIT) firedamage = rint(playdmg * reduction);
else firedamage = playdmg;
// Final damage (no armour save)
T_Damage (targ, self, self.owner, firedamage, NOARMOR);
BurnDeBuff(targ);
};