mirror of
https://github.com/nzp-team/quakec.git
synced 2025-02-01 22:21:59 +00:00
576 lines
11 KiB
C++
576 lines
11 KiB
C++
|
/*
|
||
|
server/entities/powerups.qc
|
||
|
|
||
|
powerup logic
|
||
|
|
||
|
Copyright (C) 2021 NZ:P Team
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License
|
||
|
as published by the Free Software Foundation; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
|
||
|
See the GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to:
|
||
|
|
||
|
Free Software Foundation, Inc.
|
||
|
59 Temple Place - Suite 330
|
||
|
Boston, MA 02111-1307, USA
|
||
|
|
||
|
*/
|
||
|
|
||
|
.float zombie_drop_id;
|
||
|
.float flashtime;
|
||
|
.float flash_step;
|
||
|
|
||
|
// the powerup's model
|
||
|
string(float type) getPowerupModel =
|
||
|
{
|
||
|
switch(type) {
|
||
|
case 1: return "models/pu/maxammo!.mdl"; break;
|
||
|
case 2: return "models/pu/x2!.mdl"; break;
|
||
|
case 3: return "models/pu/instakill!.mdl"; break;
|
||
|
case 4: return "models/pu/nuke!.mdl"; break;
|
||
|
case 5: return "models/pu/carpenter!.mdl"; break;
|
||
|
case 6: return "models/pu/perkbottle!.mdl"; break;
|
||
|
default: return ""; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// the powerup's pick up voice clip
|
||
|
string(float type) getPowerupVO =
|
||
|
{
|
||
|
switch(type) {
|
||
|
case 1: return "sounds/pu/maxammo.wav"; break;
|
||
|
case 2: return "sounds/pu/double_points.wav"; break;
|
||
|
case 3: return "sounds/pu/insta_kill.wav"; break;
|
||
|
case 4: return "sounds/pu/nuke.wav"; break;
|
||
|
case 5: return "sounds/pu/carpenter.wav"; break;
|
||
|
default: return ""; break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void() maxammo =
|
||
|
{
|
||
|
entity tempe, temp;
|
||
|
|
||
|
tempe = find (world, classname, "player");
|
||
|
while (tempe)
|
||
|
{
|
||
|
if (!tempe.downed)
|
||
|
{
|
||
|
if (tempe.weapon)
|
||
|
{
|
||
|
tempe.currentammo = getWeaponAmmo(tempe.weapon);
|
||
|
if (!tempe.currentmag)
|
||
|
{
|
||
|
temp = self;
|
||
|
self = tempe;
|
||
|
W_Reload(S_BOTH);
|
||
|
self = temp;
|
||
|
}
|
||
|
}
|
||
|
if (tempe.secondaryweapon)
|
||
|
tempe.secondaryammo = getWeaponAmmo(tempe.secondaryweapon);
|
||
|
|
||
|
tempe.primary_grenades = 4;
|
||
|
|
||
|
if (tempe.grenades & 2)
|
||
|
tempe.secondary_grenades = 2;
|
||
|
}
|
||
|
#ifdef PC
|
||
|
ScrollText("MAX AMMO!", tempe);
|
||
|
#endif
|
||
|
#ifdef PSP
|
||
|
nzp_maxammo();
|
||
|
#endif
|
||
|
|
||
|
tempe = find (tempe, classname, "player");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void() nuke_finalize =
|
||
|
{
|
||
|
entity players;
|
||
|
|
||
|
// give 'The F Bomb'
|
||
|
#ifndef NX
|
||
|
if (self.kills == 1) {
|
||
|
GiveAchievement(4);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// award points
|
||
|
players = find(world,classname,"player");
|
||
|
while(players)
|
||
|
{
|
||
|
addmoney(players, 400*nuke_powerups_activated, 1);
|
||
|
players = find(players,classname,"player");
|
||
|
}
|
||
|
|
||
|
nuke_powerup_active = false;
|
||
|
}
|
||
|
|
||
|
void() do_nuke_kill =
|
||
|
{
|
||
|
// back up ourselves
|
||
|
entity oldself;
|
||
|
oldself = self;
|
||
|
|
||
|
// switch to goaldummy, is goaldummy world?
|
||
|
if (self.goaldummy == world) {
|
||
|
nuke_finalize();
|
||
|
remove(self);
|
||
|
return;
|
||
|
} else {
|
||
|
self = self.goaldummy;
|
||
|
}
|
||
|
|
||
|
// kill a target
|
||
|
self.th_die();
|
||
|
|
||
|
// restore self
|
||
|
self = oldself;
|
||
|
|
||
|
// increment kills
|
||
|
self.kills++;
|
||
|
|
||
|
// find new target
|
||
|
self.goaldummy = findfloat(self.goaldummy, iszomb, 1);
|
||
|
|
||
|
self.nextthink = (rint((random() * 6) + 1)/10) + time; // random number from 0.1 to 0.7
|
||
|
}
|
||
|
|
||
|
void() nuke =
|
||
|
{
|
||
|
// if there's already one active, just increment the point multiplier
|
||
|
if (nuke_powerup_active == true) {
|
||
|
nuke_powerups_activated++;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// mark nuke as being active, to prevent zombie damage and spawning.
|
||
|
nuke_powerup_active = true;
|
||
|
nuke_powerup_spawndelay = time + 3;
|
||
|
nuke_powerups_activated = 1;
|
||
|
|
||
|
// create our watcher entity
|
||
|
entity nuke_watcher;
|
||
|
nuke_watcher = spawn();
|
||
|
nuke_watcher.goaldummy = findfloat(world, iszomb, 1);
|
||
|
|
||
|
nuke_watcher.think = do_nuke_kill;
|
||
|
nuke_watcher.nextthink = (rint((random() * 6) + 1)/10) + time; // random number from 0.1 to 0.7
|
||
|
}
|
||
|
|
||
|
void() carpenter =
|
||
|
{
|
||
|
local entity oldself;
|
||
|
local entity who;
|
||
|
oldself = self;
|
||
|
|
||
|
who = find(world,classname,"window");
|
||
|
while(who != world)
|
||
|
{
|
||
|
if(who.health < 6 && who.health != -10)//-10 is for boardless windows
|
||
|
{
|
||
|
self = who;
|
||
|
window_carpenter_1 ();
|
||
|
who.health = 6;
|
||
|
self = oldself;
|
||
|
}
|
||
|
|
||
|
who = find(who,classname,"window");
|
||
|
}
|
||
|
|
||
|
who = find(world,classname,"player");
|
||
|
while(who)
|
||
|
{
|
||
|
addmoney(who, 200, 1);
|
||
|
|
||
|
who = find(who,classname,"player");
|
||
|
}
|
||
|
total_windows_down = 0;
|
||
|
}
|
||
|
|
||
|
void(entity who) give_perkdrop_logic =
|
||
|
{
|
||
|
// Return here if we already have all of the Perks
|
||
|
if ((who.perks & P_REVIVE) && (who.perks & P_JUG) && (who.perks & P_SPEED) && (who.perks & P_DOUBLE) &&
|
||
|
(who.perks & P_FLOP) && (who.perks & P_STAMIN) && (who.perks & P_DEAD) && (who.perks & P_MULE)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
local float perk;
|
||
|
perk = 0;
|
||
|
|
||
|
while(perk == 0) {
|
||
|
local float num;
|
||
|
num = rint((random() * 7)) + 1;
|
||
|
|
||
|
switch(num) {
|
||
|
case 1:
|
||
|
if (!(who.perks & P_JUG)) {
|
||
|
perk = 1;
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
if (!(who.perks & P_DOUBLE)) {
|
||
|
perk = 2;
|
||
|
}
|
||
|
break;
|
||
|
case 3:
|
||
|
if (!(who.perks & P_SPEED)) {
|
||
|
perk = 4;
|
||
|
}
|
||
|
break;
|
||
|
case 4:
|
||
|
if (!(who.perks & P_REVIVE)) {
|
||
|
perk = 8;
|
||
|
}
|
||
|
break;
|
||
|
case 5:
|
||
|
if (!(who.perks & P_FLOP)) {
|
||
|
perk = 16;
|
||
|
}
|
||
|
break;
|
||
|
case 6:
|
||
|
if (!(who.perks & P_STAMIN)) {
|
||
|
perk = 32;
|
||
|
}
|
||
|
break;
|
||
|
case 7:
|
||
|
if (!(who.perks & P_DEAD)) {
|
||
|
perk = 64;
|
||
|
}
|
||
|
break;
|
||
|
case 8:
|
||
|
if (!(who.perks & P_MULE)) {
|
||
|
perk = 128;
|
||
|
}
|
||
|
break;
|
||
|
default: break;
|
||
|
}
|
||
|
|
||
|
who.perks = who.perks | perk;
|
||
|
}
|
||
|
SetPerk(who, who.perks);
|
||
|
}
|
||
|
|
||
|
void() give_perk =
|
||
|
{
|
||
|
// Individual Power-Up
|
||
|
if (self.style == 1) {
|
||
|
give_perkdrop_logic(other);
|
||
|
}
|
||
|
// OUR Power-Up
|
||
|
else {
|
||
|
local entity who;
|
||
|
who = find(world, classname, "player");
|
||
|
|
||
|
while(who) {
|
||
|
give_perkdrop_logic(who);
|
||
|
who = find(who, classname, "player");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void() powerup_flash =
|
||
|
{
|
||
|
if(self.flash_step == 0)
|
||
|
{
|
||
|
self.flash_step = 1;
|
||
|
self.flashtime = time + 3;
|
||
|
self.nextthink = time + 0.6;
|
||
|
}
|
||
|
else if(self.flash_step == 1)
|
||
|
{
|
||
|
self.nextthink = time + 0.6;
|
||
|
|
||
|
if(self.flashtime < time)
|
||
|
{
|
||
|
self.flash_step = 2;
|
||
|
self.nextthink = time + 0.3;
|
||
|
self.flashtime = time + 3;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else if(self.flash_step == 2)
|
||
|
{
|
||
|
self.nextthink = time + 0.3;
|
||
|
|
||
|
if(self.flashtime < time)
|
||
|
{
|
||
|
self.flash_step = 3;
|
||
|
self.nextthink = time + 0.15;
|
||
|
self.flashtime = time + 3;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else if(self.flash_step == 3)
|
||
|
{
|
||
|
self.nextthink = time + 0.15;
|
||
|
if(self.flashtime < time)
|
||
|
{
|
||
|
// moto - we used to setmodel blank here too, but it caused errors with the sprite.
|
||
|
remove(self.owner);
|
||
|
remove(self);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(self.model == "") {
|
||
|
setmodel(self, getPowerupModel(self.zombie_drop_id));
|
||
|
} else {
|
||
|
setmodel(self,"");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void() powerup_play_sound =
|
||
|
{
|
||
|
// play the VO clip if its not the perk bottle
|
||
|
if (self.zombie_drop_id != 6)
|
||
|
sound(self, CHAN_VOICE, self.powerup_vo, 1, ATTN_NONE);
|
||
|
|
||
|
// finally, remove the (invisible/inactive) powerup.
|
||
|
remove(self);
|
||
|
}
|
||
|
|
||
|
void() powerup_touch =
|
||
|
{
|
||
|
local float t;
|
||
|
|
||
|
t = random();
|
||
|
|
||
|
if(other.classname == "player")
|
||
|
{
|
||
|
// pickup sound
|
||
|
sound(self.owner,CHAN_VOICE,"sounds/pu/pickup.wav",1,ATTN_NONE);
|
||
|
|
||
|
// add a slight delay before VO play
|
||
|
self.think = powerup_play_sound;
|
||
|
self.nextthink = time + 1;
|
||
|
|
||
|
// hide powerup until we remove (after sound)
|
||
|
setmodel(self, "");
|
||
|
self.effects = 0;
|
||
|
self.touch = SUB_Null;
|
||
|
remove(self.owner);
|
||
|
|
||
|
// slight screen flash
|
||
|
stuffcmd(other, "bf\n");
|
||
|
|
||
|
// powerup effects
|
||
|
switch(self.zombie_drop_id) {
|
||
|
// max ammo
|
||
|
case 1:
|
||
|
maxammo();
|
||
|
break;
|
||
|
// double points
|
||
|
case 2:
|
||
|
x2_finished = time + 30;
|
||
|
other.x2_icon = true;
|
||
|
break;
|
||
|
// insta kill
|
||
|
case 3:
|
||
|
instakill_finished = time + 30;
|
||
|
other.insta_icon = true;
|
||
|
break;
|
||
|
// nuke
|
||
|
case 4:
|
||
|
nuke();
|
||
|
break;
|
||
|
// carpenter
|
||
|
case 5:
|
||
|
carpenter();
|
||
|
break;
|
||
|
// free perk
|
||
|
case 6:
|
||
|
give_perk();
|
||
|
break;
|
||
|
// broken!
|
||
|
default:
|
||
|
centerprint(other, strcat("INVALID POWER-UP ID: ", ftos(self.zombie_drop_id)));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void() sparkle_think =
|
||
|
{
|
||
|
local float f;
|
||
|
f = self.frame;
|
||
|
local float r;
|
||
|
while(f == self.frame)
|
||
|
{
|
||
|
r = random();
|
||
|
r = ((r > 0.2) ? 1 : 0) + ((r > 0.4) ? 1 : 0) + ((r > 0.6) ? 1 : 0) + ((r > 0.8) ? 1 : 0);
|
||
|
f = r;
|
||
|
}
|
||
|
self.frame = f;
|
||
|
|
||
|
self.think = sparkle_think;
|
||
|
self.nextthink = time + 0.1;
|
||
|
|
||
|
if(self.calc_time <= time)
|
||
|
{
|
||
|
sound(self,CHAN_VOICE,"sounds/pu/powerup.wav",0.6,ATTN_NORM);
|
||
|
self.calc_time = time + 2.998;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// GetPowerupID()
|
||
|
// Returns a powerup id, checks if the game allows for
|
||
|
// one and if requirements for said powerup are met.
|
||
|
//
|
||
|
float() GetPowerupID =
|
||
|
{
|
||
|
float found;
|
||
|
float carpenter_able;
|
||
|
float perk_able;
|
||
|
float id;
|
||
|
|
||
|
id = carpenter_able = perk_able = 0;
|
||
|
|
||
|
// Check if we can get a carpenter or a free perk drop
|
||
|
if (total_windows_down >= 5)
|
||
|
carpenter_able = true;
|
||
|
if (rounds >= 15)
|
||
|
perk_able = true;
|
||
|
|
||
|
float total_powerups = 5; // nuke, insta, 2x, maxammo, carpenter, free perk
|
||
|
|
||
|
// Start ID loop
|
||
|
found = false;
|
||
|
while(found == false) {
|
||
|
float t = random();
|
||
|
|
||
|
// loop through all IDs
|
||
|
for (float i = 0; i < total_powerups + 1; i++) {
|
||
|
// check if the ID we got was viable
|
||
|
if (t > (i/total_powerups)) {
|
||
|
switch(i) {
|
||
|
case 1:
|
||
|
found = true;
|
||
|
id = i;
|
||
|
break;
|
||
|
case 2:
|
||
|
found = true;
|
||
|
id = i;
|
||
|
break;
|
||
|
case 3:
|
||
|
found = true;
|
||
|
id = i;
|
||
|
break;
|
||
|
case 4:
|
||
|
found = true;
|
||
|
id = i;
|
||
|
break;
|
||
|
case 5:
|
||
|
if (carpenter_able) {
|
||
|
found = true;
|
||
|
id = i;
|
||
|
}
|
||
|
break;
|
||
|
case 6:
|
||
|
/*if (perk_able) {
|
||
|
found = true;
|
||
|
id = i;
|
||
|
}*/
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
float last_pu;
|
||
|
void(vector where, float type) Spawn_Powerup =
|
||
|
{
|
||
|
entity new_powerup;
|
||
|
float id;
|
||
|
|
||
|
new_powerup = spawn();
|
||
|
new_powerup.origin = where;
|
||
|
setorigin(new_powerup, new_powerup.origin);
|
||
|
new_powerup.solid = SOLID_TRIGGER;
|
||
|
new_powerup.classname = "item_powerup";
|
||
|
|
||
|
|
||
|
setsize (new_powerup, VEC_HULL_MIN, VEC_HULL_MAX);
|
||
|
new_powerup.movetype = MOVETYPE_NONE;
|
||
|
Light_Green(new_powerup);
|
||
|
|
||
|
//=================== Sparkle Effects =====================
|
||
|
|
||
|
entity twlt_Sparkle;
|
||
|
twlt_Sparkle = spawn();
|
||
|
new_powerup.owner = twlt_Sparkle; //just a reference so power up can delete the sparkle later on
|
||
|
|
||
|
setorigin(twlt_Sparkle,where);
|
||
|
#ifndef PC
|
||
|
setmodel(twlt_Sparkle,"models/sprites/sprkle.spr");
|
||
|
#endif
|
||
|
twlt_Sparkle.think = sparkle_think;
|
||
|
twlt_Sparkle.nextthink = time + 0.1;
|
||
|
sound(twlt_Sparkle,CHAN_VOICE,"sounds/pu/powerup.wav",0.6,ATTN_NORM);
|
||
|
|
||
|
sound(new_powerup,CHAN_AUTO,"sounds/pu/drop.wav",1,ATTN_NONE);
|
||
|
|
||
|
//========================================================
|
||
|
|
||
|
// Specific Power-Ups (for dogs)
|
||
|
if (type) {
|
||
|
setmodel(new_powerup, getPowerupModel(type));
|
||
|
new_powerup.zombie_drop_id = type;
|
||
|
new_powerup.powerup_vo = getPowerupVO(type);
|
||
|
} else {
|
||
|
// Grab a powerup ID
|
||
|
id = GetPowerupID();
|
||
|
|
||
|
// Should perk drops be individual?
|
||
|
if (id == 6) {
|
||
|
// Yes!
|
||
|
if (random() > (1/2)) {
|
||
|
// Set Style and make light Blue to symbolize it is an individual drop
|
||
|
// TODO: Make a sprite too??
|
||
|
new_powerup.style = 1;
|
||
|
Light_None(new_powerup);
|
||
|
Light_Blue(new_powerup);
|
||
|
}
|
||
|
// No!
|
||
|
else {
|
||
|
// failsafe
|
||
|
new_powerup.style = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// finally, assign the id and model to our id.
|
||
|
setmodel(new_powerup, getPowerupModel(id));
|
||
|
new_powerup.zombie_drop_id = id;
|
||
|
new_powerup.powerup_vo = getPowerupVO(id);
|
||
|
}
|
||
|
|
||
|
last_pu = new_powerup.zombie_drop_id;
|
||
|
new_powerup.touch = powerup_touch;
|
||
|
|
||
|
new_powerup.think = powerup_flash;
|
||
|
new_powerup.nextthink = time + 21;
|
||
|
totalpowerups++;
|
||
|
}
|