358 lines
12 KiB
Plaintext
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);
|
|
};
|