2022-02-08 18:42:28 +00:00
|
|
|
/*
|
|
|
|
server/entities/powerups.qc
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
Power-Up Spawn and Use Logic
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2024-01-07 23:24:48 +00:00
|
|
|
Copyright (C) 2021-2024 NZ:P Team
|
2022-02-08 18:42:28 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2023-03-06 23:44:42 +00:00
|
|
|
#define MAX_POWERUPS 8
|
|
|
|
#define POWERUPS_PER_ROUND 4
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2023-03-06 23:44:42 +00:00
|
|
|
#define PU_NUKE 0
|
|
|
|
#define PU_INSTAKILL 1
|
|
|
|
#define PU_DOUBLEPTS 2
|
|
|
|
#define PU_CARPENTER 3
|
|
|
|
#define PU_MAXAMMO 4
|
2022-05-08 02:18:55 +00:00
|
|
|
|
2024-05-16 03:11:52 +00:00
|
|
|
#define PU_OFFSET_QK '0 0 12'
|
|
|
|
#define PU_OFFSET_HL '0 0 14'
|
|
|
|
|
|
|
|
#define PU_BBOX_MINS '-2 -2 -16'
|
|
|
|
#define PU_BBOX_MAXS '2 2 16'
|
2024-04-08 03:27:10 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
var struct powerup_struct
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
float id;
|
|
|
|
float occupied;
|
|
|
|
string model_path;
|
|
|
|
string voiceover_path;
|
|
|
|
void() function;
|
|
|
|
float() requirement_function;
|
|
|
|
} powerup_array[MAX_POWERUPS] = {};
|
|
|
|
|
|
|
|
float powerup_count;
|
|
|
|
float powerup_index;
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
.float zombie_drop_id;
|
|
|
|
|
2023-03-06 23:44:42 +00:00
|
|
|
//
|
|
|
|
// PU_FreeEnt(ent)
|
|
|
|
// Marks a Power-Up entity as able to be used.
|
|
|
|
//
|
|
|
|
inline void(entity ent) PU_FreeEnt =
|
|
|
|
{
|
|
|
|
setmodel(ent, "");
|
|
|
|
ent.classname = "freePowerUpEntity";
|
2023-07-17 18:51:25 +00:00
|
|
|
ent.hitcount = 0;
|
2023-03-06 23:44:42 +00:00
|
|
|
ent.touch = SUB_Null;
|
|
|
|
ent.think = SUB_Null;
|
2023-03-15 02:14:18 +00:00
|
|
|
ent.frame = 0;
|
2024-01-01 20:38:02 +00:00
|
|
|
ent.scale = 1;
|
2023-08-29 15:44:41 +00:00
|
|
|
ent.effects = 0;
|
2024-05-16 02:17:45 +00:00
|
|
|
|
|
|
|
#ifdef FTE
|
|
|
|
|
|
|
|
ent.SendEntity = __NULL__;
|
|
|
|
ent.SendFlags = 0;
|
|
|
|
|
|
|
|
#endif // FTE
|
|
|
|
|
2023-03-06 23:44:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_GetFreeEnt()
|
|
|
|
// Returns a Power-Up entity to use.
|
|
|
|
//
|
|
|
|
entity() PU_GetFreeEnt =
|
|
|
|
{
|
|
|
|
entity ent;
|
|
|
|
ent = find(world, classname, "freePowerUpEntity");
|
|
|
|
|
|
|
|
if (ent == world)
|
|
|
|
error("PU_GetFreeEnt: No free Power-Up Entity. (Hacks?)\n");
|
|
|
|
|
|
|
|
return ent;
|
|
|
|
};
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_AddToStruct(id, model_path, voiceover_path)
|
|
|
|
// Adds the Power-Up and info to the powerup struct
|
|
|
|
//
|
2024-01-15 01:48:42 +00:00
|
|
|
void(float id, string model_path, string voiceover_path, void() function, float() requirement_function)
|
2022-05-08 02:18:55 +00:00
|
|
|
PU_AddToStruct =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
if (id > MAX_POWERUPS - 1)
|
|
|
|
return;
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// Precache Model and VO
|
|
|
|
precache_model(model_path);
|
|
|
|
precache_sound(voiceover_path);
|
|
|
|
|
|
|
|
// Populate the Struct at Index
|
|
|
|
powerup_array[powerup_count].id = id;
|
|
|
|
powerup_array[powerup_count].occupied = true;
|
|
|
|
powerup_array[powerup_count].model_path = model_path;
|
|
|
|
powerup_array[powerup_count].voiceover_path = voiceover_path;
|
|
|
|
powerup_array[powerup_count].function = function;
|
|
|
|
powerup_array[powerup_count].requirement_function = requirement_function;
|
|
|
|
|
|
|
|
// Increment Index
|
|
|
|
powerup_count++;
|
|
|
|
};
|
|
|
|
|
2022-12-20 20:43:15 +00:00
|
|
|
//
|
|
|
|
// PU_CopyStruct(to, from)
|
|
|
|
// Copies a powerup_struct from to.
|
|
|
|
//
|
2023-02-27 15:27:45 +00:00
|
|
|
#define PU_CopyStruct(to, from) \
|
|
|
|
to.id = from.id; \
|
|
|
|
to.occupied = from.occupied; \
|
|
|
|
to.model_path = from.model_path; \
|
|
|
|
to.voiceover_path = from.voiceover_path; \
|
|
|
|
to.function = from.function; \
|
|
|
|
to.requirement_function = from.requirement_function; \
|
2022-12-20 20:43:15 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_PopulateArray()
|
|
|
|
// Generates a Power-Up array with the Fisher-Yates shuffle
|
|
|
|
//
|
|
|
|
void() PU_PopulateArray =
|
|
|
|
{
|
2023-02-27 15:27:45 +00:00
|
|
|
float amount = powerup_count;
|
|
|
|
float i;
|
|
|
|
powerup_struct t;
|
|
|
|
|
|
|
|
while(amount) {
|
|
|
|
i = floor(random() * amount--);
|
|
|
|
|
|
|
|
// macro'd these to avoid hell with __inout :)
|
|
|
|
PU_CopyStruct(t, powerup_array[amount])
|
|
|
|
PU_CopyStruct(powerup_array[amount], powerup_array[i])
|
|
|
|
PU_CopyStruct(powerup_array[i], t)
|
2022-05-08 02:18:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_GetNextPowerUp()
|
|
|
|
// Returns the next valid Power-Up, and refreshes array if needbe.
|
|
|
|
//
|
|
|
|
float() PU_GetNextPowerUp =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
float id;
|
|
|
|
float found;
|
|
|
|
id = -1;
|
|
|
|
found = false;
|
|
|
|
|
|
|
|
while(found == false) {
|
|
|
|
// Refresh the Array if we're at the end
|
|
|
|
if (powerup_index >= MAX_POWERUPS - 1) {
|
|
|
|
PU_PopulateArray();
|
|
|
|
powerup_index = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Grab a Power-Up
|
|
|
|
powerup_struct pu = powerup_array[powerup_index];
|
|
|
|
powerup_index++;
|
|
|
|
|
|
|
|
// Found a valid Power-Up
|
|
|
|
if (pu.occupied == true) {
|
|
|
|
// Check if we meet the requirements
|
|
|
|
if (pu.requirement_function() == true) {
|
|
|
|
id = pu.id;
|
|
|
|
found = true;
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
return id;
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_ModelPath(id)
|
|
|
|
// Returns model_path from Power-Up struct.
|
|
|
|
//
|
|
|
|
string(float id) PU_ModelPath =
|
|
|
|
{
|
|
|
|
if (id == -1)
|
|
|
|
return "";
|
|
|
|
for(float i = 0; i < MAX_POWERUPS - 1; i++) {
|
|
|
|
if (powerup_array[i].id == id)
|
|
|
|
return powerup_array[i].model_path;
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_VoiceoverPath(id)
|
|
|
|
// Returns model_path from Power-Up struct.
|
|
|
|
//
|
|
|
|
string(float id) PU_VoiceoverPath =
|
|
|
|
{
|
|
|
|
if (id == -1)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
for(float i = 0; i < MAX_POWERUPS - 1; i++) {
|
|
|
|
if (powerup_array[i].id == id)
|
|
|
|
return powerup_array[i].voiceover_path;
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_LogicFunction(id)
|
|
|
|
// Returns function() from Power-Up struct.
|
|
|
|
//
|
|
|
|
void(float id) PU_LogicFunction =
|
|
|
|
{
|
|
|
|
if (id == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for(float i = 0; i < MAX_POWERUPS - 1; i++) {
|
|
|
|
if (powerup_array[i].id == id)
|
|
|
|
powerup_array[i].function();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_NukeFinalize
|
|
|
|
// Wrap Nuke stuff up.
|
|
|
|
//
|
|
|
|
void() PU_NukeFinalize =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
|
|
|
entity players;
|
|
|
|
|
|
|
|
// give 'The F Bomb'
|
|
|
|
if (self.kills == 1) {
|
|
|
|
GiveAchievement(4);
|
|
|
|
}
|
|
|
|
|
|
|
|
// award points
|
|
|
|
players = find(world,classname,"player");
|
|
|
|
while(players)
|
|
|
|
{
|
2024-06-10 00:16:38 +00:00
|
|
|
Player_AddScore(players, 400 * nuke_powerups_activated, true);
|
2022-02-08 18:42:28 +00:00
|
|
|
players = find(players,classname,"player");
|
|
|
|
}
|
|
|
|
|
|
|
|
nuke_powerup_active = false;
|
2022-05-08 02:18:55 +00:00
|
|
|
};
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 21:42:40 +00:00
|
|
|
//
|
|
|
|
// PU_NukeExplosionThink()
|
|
|
|
// Think function for Nuke explosions.
|
|
|
|
//
|
|
|
|
void() PU_NukeExplosionThink =
|
|
|
|
{
|
|
|
|
self.frame++;
|
|
|
|
|
|
|
|
if (self.frame >= 5)
|
2023-03-06 23:44:42 +00:00
|
|
|
PU_FreeEnt(self);
|
2022-05-08 21:42:40 +00:00
|
|
|
|
|
|
|
self.nextthink = time + 0.10;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_NukeExplode()
|
|
|
|
// Spawns an explosion sprite.
|
|
|
|
//
|
|
|
|
void(vector org) PU_NukeExplode =
|
|
|
|
{
|
2023-03-06 23:44:42 +00:00
|
|
|
entity explosion = PU_GetFreeEnt();
|
|
|
|
explosion.classname = "pu_nuke_explosion";
|
2022-05-08 21:42:40 +00:00
|
|
|
setmodel(explosion, "models/sprites/explosion.spr");
|
|
|
|
setorigin(explosion, org);
|
|
|
|
explosion.think = PU_NukeExplosionThink;
|
|
|
|
explosion.nextthink = time + 0.10;
|
|
|
|
}
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_NukeKill()
|
|
|
|
// Kills Targets when Nuke is active.
|
|
|
|
//
|
|
|
|
void() PU_NukeKill =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
|
|
|
// back up ourselves
|
|
|
|
entity oldself;
|
|
|
|
oldself = self;
|
|
|
|
|
|
|
|
// switch to goaldummy, is goaldummy world?
|
|
|
|
if (self.goaldummy == world) {
|
2022-05-08 02:18:55 +00:00
|
|
|
PU_NukeFinalize();
|
2023-03-06 23:44:42 +00:00
|
|
|
PU_FreeEnt(self);
|
2022-02-08 18:42:28 +00:00
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
self = self.goaldummy;
|
|
|
|
}
|
|
|
|
|
2022-05-08 21:42:40 +00:00
|
|
|
// play explosion effects
|
|
|
|
PU_NukeExplode(self.origin + '0 0 13');
|
|
|
|
|
2023-12-20 19:51:52 +00:00
|
|
|
// set them on fire
|
|
|
|
self.onfire = true;
|
|
|
|
|
2022-02-08 18:42:28 +00:00
|
|
|
// kill a target
|
|
|
|
self.th_die();
|
|
|
|
|
2023-03-19 21:40:43 +00:00
|
|
|
// override their death sound
|
2024-09-02 01:12:38 +00:00
|
|
|
Sound_PlaySound(self, "sounds/pu/nuke.wav", SOUND_TYPE_ENV_OBJECT, SOUND_PRIORITY_PLAYALWAYS);
|
2023-03-19 21:40:43 +00:00
|
|
|
|
2022-02-08 18:42:28 +00:00
|
|
|
// 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
|
2022-05-08 02:18:55 +00:00
|
|
|
};
|
|
|
|
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_Nuke()
|
|
|
|
// Nuke Power-Up Function
|
|
|
|
//
|
|
|
|
void() PU_Nuke =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2024-01-15 01:48:42 +00:00
|
|
|
// Flash the screen white
|
|
|
|
nzp_screenflash(world, SCREENFLASH_COLOR_WHITE, 1, SCREENFLASH_FADE_INANDOUT);
|
|
|
|
|
2022-02-08 18:42:28 +00:00
|
|
|
// 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;
|
2023-03-06 23:44:42 +00:00
|
|
|
nuke_watcher = PU_GetFreeEnt();
|
|
|
|
nuke_watcher.classname = "pu_nukewatcher";
|
2022-02-08 18:42:28 +00:00
|
|
|
nuke_watcher.goaldummy = findfloat(world, iszomb, 1);
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
nuke_watcher.think = PU_NukeKill;
|
2022-02-08 18:42:28 +00:00
|
|
|
nuke_watcher.nextthink = (rint((random() * 6) + 1)/10) + time; // random number from 0.1 to 0.7
|
2022-05-08 02:18:55 +00:00
|
|
|
};
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_InstaKill()
|
|
|
|
// Insta-Kill Power-Up Fuction
|
|
|
|
//
|
|
|
|
void() PU_InstaKill =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
instakill_finished = time + 30;
|
|
|
|
other.insta_icon = true;
|
|
|
|
};
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_DoublePoints()
|
|
|
|
// Double Points Power-Up Function
|
|
|
|
//
|
|
|
|
void() PU_DoublePoints =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
x2_finished = time + 30;
|
|
|
|
other.x2_icon = true;
|
|
|
|
};
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
2023-11-03 17:34:59 +00:00
|
|
|
// PU_CarpenterFinalize
|
|
|
|
// Remove the Carpenter Watcher and
|
|
|
|
// rewards Players with Score.
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
2023-11-03 17:34:59 +00:00
|
|
|
void() PU_CarpenterFinalize =
|
2022-05-08 02:18:55 +00:00
|
|
|
{
|
2023-11-03 17:34:59 +00:00
|
|
|
entity players = find(world, classname, "player");
|
|
|
|
|
|
|
|
// Reward Players with Points
|
|
|
|
while(players) {
|
2024-06-10 00:16:38 +00:00
|
|
|
Player_AddScore(players, 200, true);
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2023-11-03 17:34:59 +00:00
|
|
|
players = find(players, classname, "player");
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
|
|
|
|
2023-11-03 17:34:59 +00:00
|
|
|
entity windows = find(world, classname, "window");
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2023-11-03 17:34:59 +00:00
|
|
|
// Reset all windows
|
|
|
|
while(windows) {
|
|
|
|
windows.isspec = 0;
|
|
|
|
windows.box1owner = world;
|
|
|
|
windows.usedent = world;
|
|
|
|
windows.owner = world;
|
|
|
|
windows.ads_release = 0;
|
|
|
|
|
|
|
|
windows = find(windows, classname, "window");
|
|
|
|
}
|
|
|
|
|
|
|
|
carp_powerup_active = false;
|
|
|
|
|
|
|
|
// Free ourselves
|
|
|
|
PU_FreeEnt(self);
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_CarpenterFindWindow
|
|
|
|
// Finds a Barricade elligible for Repair
|
|
|
|
//
|
|
|
|
entity() PU_CarpenterFindWindow =
|
|
|
|
{
|
|
|
|
entity windows = find(world, classname, "window");
|
|
|
|
|
|
|
|
while(windows != world) {
|
|
|
|
// Window needs repaired and is repairable
|
|
|
|
if (windows.health < 6 && windows.health != -10 && !windows.isspec
|
|
|
|
&& !windows.ads_release)
|
|
|
|
return windows;
|
|
|
|
|
|
|
|
windows = find(windows, classname, "window");
|
|
|
|
}
|
|
|
|
|
|
|
|
// No Windows are elligible, return world.
|
|
|
|
return world;
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_CarpenterRepair()
|
|
|
|
// Attempts to Repair a Barricade
|
|
|
|
//
|
|
|
|
void() PU_CarpenterRepair =
|
|
|
|
{
|
|
|
|
// Find a new Barricade to Repair
|
|
|
|
if (self.goaldummy == world || self.goaldummy.isspec == 0) {
|
|
|
|
|
|
|
|
if (self.goaldummy != world)
|
|
|
|
self.goaldummy.owner = world;
|
|
|
|
|
|
|
|
self.goaldummy = PU_CarpenterFindWindow();
|
|
|
|
self.kills = false;
|
|
|
|
|
|
|
|
// Didn't find one, so end the Carpenter sequence.
|
|
|
|
if (self.goaldummy == world) {
|
|
|
|
self.think = PU_CarpenterFinalize;
|
|
|
|
self.nextthink = time + 0.1;
|
|
|
|
return;
|
|
|
|
}
|
2022-05-08 02:18:55 +00:00
|
|
|
|
2023-11-03 17:34:59 +00:00
|
|
|
// Mark the window as being Repaired
|
|
|
|
self.goaldummy.isspec = 1;
|
|
|
|
self.goaldummy.ads_release = 1;
|
|
|
|
self.goaldummy.owner = self;
|
|
|
|
}
|
|
|
|
// Repair our current Barricade
|
|
|
|
else if (!self.kills) {
|
|
|
|
// Trigger the animation
|
|
|
|
entity tempe = self;
|
|
|
|
self = self.goaldummy;
|
|
|
|
switch(self.health) {
|
|
|
|
case 5: window_carpenter_11(); break;
|
|
|
|
case 4: window_carpenter_9(); break;
|
|
|
|
case 3: window_carpenter_7(); break;
|
|
|
|
case 2: window_carpenter_5(); break;
|
|
|
|
case 1: window_carpenter_3(); break;
|
|
|
|
default: window_carpenter_1(); break;
|
|
|
|
}
|
|
|
|
self.health = 6;
|
|
|
|
self = tempe;
|
|
|
|
|
|
|
|
// We're actively building
|
|
|
|
self.kills = true;
|
|
|
|
self.ltime = time + 2.1;
|
|
|
|
} else {
|
|
|
|
if (self.ltime < time) {
|
|
|
|
self.goaldummy.frame = 88;
|
|
|
|
self.goaldummy.isspec = 0;
|
|
|
|
self.goaldummy.health = 6;
|
|
|
|
}
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
2023-11-03 17:34:59 +00:00
|
|
|
|
|
|
|
self.nextthink = time + 0.05;
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_Carpenter()
|
|
|
|
// Carpenter Power-Up Function
|
|
|
|
//
|
|
|
|
void() PU_Carpenter =
|
|
|
|
{
|
|
|
|
// create our watcher entity
|
|
|
|
entity carp_watcher;
|
|
|
|
carp_watcher = PU_GetFreeEnt();
|
|
|
|
carp_watcher.classname = "pu_carpwatcher";
|
|
|
|
|
|
|
|
carp_watcher.think = PU_CarpenterRepair;
|
|
|
|
carp_watcher.nextthink = time + 0.05;
|
|
|
|
|
|
|
|
carp_powerup_active = true;
|
2022-05-08 02:18:55 +00:00
|
|
|
};
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_CarpenterRequirement()
|
|
|
|
// Requirements for Carpenter Power-Up.
|
|
|
|
//
|
|
|
|
float() PU_CarpenterRequirement =
|
|
|
|
{
|
|
|
|
if (total_windows_down >= 5)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_MaxAmmo()
|
|
|
|
// Max Ammo Power-Up Function
|
|
|
|
//
|
|
|
|
void() PU_MaxAmmo =
|
|
|
|
{
|
2023-11-05 22:08:55 +00:00
|
|
|
entity players;
|
2022-05-08 02:18:55 +00:00
|
|
|
entity tempe;
|
|
|
|
|
2023-11-05 22:08:55 +00:00
|
|
|
players = find(world, classname, "player");
|
2022-05-08 02:18:55 +00:00
|
|
|
|
2023-11-05 22:08:55 +00:00
|
|
|
while(players) {
|
2023-03-18 20:23:54 +00:00
|
|
|
|
2023-11-05 22:08:55 +00:00
|
|
|
if (!players.downed) {
|
2023-10-15 15:30:01 +00:00
|
|
|
// Fill all weapons
|
|
|
|
for (float i = 0; i < MAX_PLAYER_WEAPONS; i++) {
|
2023-11-05 22:08:55 +00:00
|
|
|
players.weapons[i].weapon_reserve = getWeaponAmmo(players.weapons[i].weapon_id);
|
2023-10-15 15:30:01 +00:00
|
|
|
}
|
2023-03-18 20:23:54 +00:00
|
|
|
// Give Grenades
|
2023-11-05 22:08:55 +00:00
|
|
|
players.primary_grenades = 4;
|
2023-03-18 20:23:54 +00:00
|
|
|
// Give Betties
|
2023-11-05 22:08:55 +00:00
|
|
|
if (players.grenades & 2) players.secondary_grenades = 2;
|
2023-03-18 20:23:54 +00:00
|
|
|
} else {
|
|
|
|
// Reset shots fired, fill 2 mags into reserve.
|
2023-11-05 22:08:55 +00:00
|
|
|
players.teslacount = 0;
|
|
|
|
players.weapons[0].weapon_reserve = getWeaponMag(players.weapon) * 2;
|
2023-03-18 20:23:54 +00:00
|
|
|
}
|
2022-05-08 02:18:55 +00:00
|
|
|
|
2023-11-05 22:08:55 +00:00
|
|
|
// Force the player to reload if their mag is empty
|
2023-11-06 16:41:56 +00:00
|
|
|
if (players.weapons[0].weapon_magazine == 0 || (IsDualWeapon(players.weapon) && players.weapons[0].weapon_magazine_left == 0)) {
|
2023-11-05 22:08:55 +00:00
|
|
|
tempe = self;
|
|
|
|
self = players;
|
|
|
|
W_Reload(S_BOTH);
|
|
|
|
self = tempe;
|
|
|
|
}
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// MAX AMMO! text
|
2023-01-24 21:12:47 +00:00
|
|
|
nzp_maxammo();
|
2023-02-05 21:03:57 +00:00
|
|
|
|
2023-11-05 22:08:55 +00:00
|
|
|
players = find(players, classname, "player");
|
2022-05-08 02:18:55 +00:00
|
|
|
}
|
|
|
|
};
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_NullRequirement()
|
|
|
|
// Power-Up has no requirements. Always return true.
|
|
|
|
//
|
|
|
|
float() PU_NullRequirement =
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
};
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_Init()
|
|
|
|
// Fill the Power-Up array for the first time and
|
|
|
|
// define used Power-Ups
|
|
|
|
//
|
|
|
|
void() PU_Init =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
// Start with 0 Power-Ups accessible
|
|
|
|
powerup_count = 0;
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// Set the Power-Up array IDs to empty
|
2023-03-06 23:44:42 +00:00
|
|
|
for (float i = 0; i < MAX_POWERUPS; i++) {
|
2022-05-08 02:18:55 +00:00
|
|
|
powerup_array[i].id = -1;
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
2022-05-08 02:18:55 +00:00
|
|
|
|
|
|
|
// Just add all of them for now
|
2024-01-15 01:48:42 +00:00
|
|
|
PU_AddToStruct(PU_NUKE, "models/pu/nuke!.mdl", "sounds/pu/kaboom.wav", PU_Nuke,
|
2022-05-08 02:18:55 +00:00
|
|
|
PU_NullRequirement );
|
2024-01-15 01:48:42 +00:00
|
|
|
PU_AddToStruct(PU_INSTAKILL, "models/pu/instakill!.mdl", "sounds/pu/insta_kill.wav", PU_InstaKill,
|
2022-05-08 02:18:55 +00:00
|
|
|
PU_NullRequirement );
|
2024-01-15 01:48:42 +00:00
|
|
|
PU_AddToStruct(PU_DOUBLEPTS, "models/pu/x2!.mdl", "sounds/pu/double_points.wav", PU_DoublePoints,
|
2022-05-08 02:18:55 +00:00
|
|
|
PU_NullRequirement );
|
2024-01-15 01:48:42 +00:00
|
|
|
PU_AddToStruct(PU_CARPENTER, "models/pu/carpenter!.mdl", "sounds/pu/carpenter.wav", PU_Carpenter,
|
2022-05-08 02:18:55 +00:00
|
|
|
PU_CarpenterRequirement );
|
2024-01-15 01:48:42 +00:00
|
|
|
PU_AddToStruct(PU_MAXAMMO, "models/pu/maxammo!.mdl", "sounds/pu/maxammo.wav", PU_MaxAmmo,
|
2022-05-08 02:18:55 +00:00
|
|
|
PU_NullRequirement );
|
|
|
|
|
2023-03-19 21:40:43 +00:00
|
|
|
// Nuke requires an extra Precache
|
|
|
|
precache_sound("sounds/pu/nuke.wav");
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// Fill the array
|
|
|
|
PU_PopulateArray();
|
2023-03-06 23:44:42 +00:00
|
|
|
|
|
|
|
// Spawn all of our Power-Up spawn entities
|
|
|
|
// We multiply by 4 to account for Power-Up operations like the Nuke.
|
|
|
|
for (float i = 0; i < (POWERUPS_PER_ROUND * 4); i++) {
|
|
|
|
entity tempe = spawn();
|
|
|
|
tempe.classname = "freePowerUpEntity";
|
|
|
|
}
|
2022-05-08 02:18:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_Flash()
|
|
|
|
// Flash Power-Up model in and out
|
|
|
|
//
|
|
|
|
void() PU_Flash =
|
|
|
|
{
|
|
|
|
// Toggle the Power-Up model on and off
|
|
|
|
if (self.hitcount % 2) {
|
|
|
|
// Disappear
|
|
|
|
setmodel(self, "");
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
2022-05-08 02:18:55 +00:00
|
|
|
else {
|
|
|
|
// Reappear
|
|
|
|
setmodel(self, self.oldmodel);
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
2022-05-08 02:18:55 +00:00
|
|
|
|
|
|
|
if (self.hitcount < 15)
|
|
|
|
self.nextthink = time + 0.5;
|
|
|
|
else if (self.hitcount < 25)
|
|
|
|
self.nextthink = time + 0.25;
|
|
|
|
else
|
|
|
|
self.nextthink = time + 0.1;
|
|
|
|
|
|
|
|
self.hitcount++;
|
|
|
|
|
2024-05-16 02:17:45 +00:00
|
|
|
#ifdef FTE
|
|
|
|
|
|
|
|
// Update the drawing of the Power-Up in CSQC to
|
|
|
|
// register the flash.
|
|
|
|
self.SendFlags = 1;
|
|
|
|
|
|
|
|
#endif // FTE
|
|
|
|
|
2024-05-16 02:25:36 +00:00
|
|
|
// Re-set the size after relink.
|
2024-05-16 03:11:52 +00:00
|
|
|
setsize(self, PU_BBOX_MINS, PU_BBOX_MAXS);
|
2024-05-16 02:25:36 +00:00
|
|
|
|
2023-03-06 23:44:42 +00:00
|
|
|
// Too late, free the Power-Up
|
2022-05-08 02:18:55 +00:00
|
|
|
if (self.hitcount >= 40) {
|
2023-03-24 17:35:01 +00:00
|
|
|
Light_None(self);
|
2023-03-06 23:44:42 +00:00
|
|
|
PU_FreeEnt(self);
|
2023-03-09 16:52:10 +00:00
|
|
|
PU_FreeEnt(self.owner);
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_PlayVO()
|
2023-03-06 23:44:42 +00:00
|
|
|
// Play the assigned Voiceover clip before freeing ourself.
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
void() PU_PlayVO =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2024-09-02 01:12:38 +00:00
|
|
|
Sound_PlaySound(world, self.powerup_vo, SOUND_TYPE_ENV_VOICE, SOUND_PRIORITY_ALLOWSKIP);
|
2023-03-06 23:44:42 +00:00
|
|
|
PU_FreeEnt(self);
|
2022-05-08 02:18:55 +00:00
|
|
|
};
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2024-05-16 03:11:52 +00:00
|
|
|
//
|
|
|
|
// PU_SparkleShrink()
|
|
|
|
// Called on Power-Up contact, makes the Sparkle shrink
|
|
|
|
// until eventually disappearing.
|
|
|
|
//
|
|
|
|
void() PU_SparkleShrink =
|
|
|
|
{
|
2024-05-18 21:39:39 +00:00
|
|
|
|
|
|
|
#ifdef FTE
|
|
|
|
|
2024-05-16 03:11:52 +00:00
|
|
|
self.scale -= frametime;
|
|
|
|
|
2024-05-18 21:39:39 +00:00
|
|
|
#else
|
|
|
|
|
|
|
|
self.scale -= frametime*2.25;
|
|
|
|
|
|
|
|
#endif // FTE
|
|
|
|
|
|
|
|
if (self.scale <= 0.02)
|
2024-05-16 03:11:52 +00:00
|
|
|
PU_FreeEnt(self);
|
|
|
|
else
|
|
|
|
self.nextthink = time + 0.01;
|
|
|
|
};
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_Touch()
|
|
|
|
// Run assigned functions and prep for Deletion
|
|
|
|
//
|
|
|
|
void() PU_Touch =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
if (other.classname == "player")
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
// Acquire sound
|
2024-09-02 01:12:38 +00:00
|
|
|
Sound_PlaySound(self.owner, "sounds/pu/pickup.wav", SOUND_TYPE_ENV_OBJECT, SOUND_PRIORITY_PLAYALWAYS);
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// Prepare for VO and destruction
|
|
|
|
self.think = PU_PlayVO;
|
2022-02-08 18:42:28 +00:00
|
|
|
self.nextthink = time + 1;
|
|
|
|
|
2024-05-16 02:17:45 +00:00
|
|
|
#ifdef FTE
|
|
|
|
|
|
|
|
// Update the drawing of the Power-Up in CSQC to
|
|
|
|
// register the flash.
|
|
|
|
self.SendFlags = 1;
|
|
|
|
|
|
|
|
#endif // FTE
|
|
|
|
|
2024-05-16 03:11:52 +00:00
|
|
|
// Prepare to shrink the Power-Up sparkle
|
|
|
|
self.owner.think = PU_SparkleShrink;
|
|
|
|
self.owner.nextthink = time + 0.01;
|
|
|
|
self.owner.scale = 1;
|
|
|
|
|
|
|
|
// slight cleaup
|
2022-02-08 18:42:28 +00:00
|
|
|
setmodel(self, "");
|
2022-05-08 02:18:55 +00:00
|
|
|
Light_None(self);
|
2022-02-08 18:42:28 +00:00
|
|
|
self.touch = SUB_Null;
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// Run Power-Up function
|
|
|
|
PU_LogicFunction(self.walktype);
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
//
|
|
|
|
// PU_SparkleThink()
|
|
|
|
// Increment Frames for the Power-Up Sparkle.
|
|
|
|
//
|
|
|
|
void() PU_SparkleThink =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
float f;
|
|
|
|
|
2022-02-08 18:42:28 +00:00
|
|
|
f = self.frame;
|
2023-08-28 08:08:16 +00:00
|
|
|
while(f == self.frame) {
|
|
|
|
// Pick a random frame from [0,4]
|
|
|
|
f = floor(5 * random());
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
|
|
|
self.frame = f;
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
self.think = PU_SparkleThink;
|
2022-02-08 18:42:28 +00:00
|
|
|
self.nextthink = time + 0.1;
|
|
|
|
|
|
|
|
if(self.calc_time <= time)
|
|
|
|
{
|
2024-09-02 01:12:38 +00:00
|
|
|
Sound_PlaySound(self, "sounds/pu/powerup.wav", SOUND_TYPE_ENV_OBJECT, SOUND_PRIORITY_PLAYALWAYS);
|
2022-02-08 18:42:28 +00:00
|
|
|
self.calc_time = time + 2.998;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-05-16 02:17:45 +00:00
|
|
|
#ifdef FTE
|
|
|
|
|
|
|
|
//
|
|
|
|
// PU_SendEntity(ePVent, flChanged)
|
|
|
|
// Sends ourself to CSQC for rendering.
|
|
|
|
//
|
|
|
|
float PU_SendEntity( entity ePVEnt, float flChanged ) {
|
|
|
|
WriteByte( MSG_ENTITY, 2 );
|
|
|
|
WriteCoord( MSG_ENTITY, self.origin_x ); // Position X
|
|
|
|
WriteCoord( MSG_ENTITY, self.origin_y ); // Position Y
|
|
|
|
WriteCoord( MSG_ENTITY, self.origin_z ); // Position Z
|
|
|
|
WriteShort( MSG_ENTITY, self.modelindex ); // Power-Up Model
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif // FTE
|
|
|
|
|
2022-02-08 18:42:28 +00:00
|
|
|
//
|
2022-05-08 02:18:55 +00:00
|
|
|
// Spawn_Powerup(where, type)
|
|
|
|
// Power-Up spawning function. Use type to force what spawns.
|
2022-02-08 18:42:28 +00:00
|
|
|
//
|
2022-05-08 02:18:55 +00:00
|
|
|
void(vector where, float type) Spawn_Powerup =
|
2022-02-08 18:42:28 +00:00
|
|
|
{
|
2022-05-08 02:18:55 +00:00
|
|
|
entity powerup;
|
|
|
|
entity sparkle;
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2024-04-08 03:27:10 +00:00
|
|
|
// Move the Power-Up up a little to be near the player's
|
|
|
|
// mid torso.
|
|
|
|
if (map_compatibility_mode == MAP_COMPAT_BETA)
|
|
|
|
where += PU_OFFSET_QK;
|
|
|
|
else
|
|
|
|
where += PU_OFFSET_HL;
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// Set Up Power-Up
|
2023-03-06 23:44:42 +00:00
|
|
|
powerup = PU_GetFreeEnt();
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
powerup.origin = where;
|
|
|
|
setorigin(powerup, powerup.origin);
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
powerup.solid = SOLID_TRIGGER;
|
|
|
|
powerup.classname = "item_powerup";
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2024-05-16 03:11:52 +00:00
|
|
|
setsize(powerup, PU_BBOX_MINS, PU_BBOX_MAXS);
|
2022-05-08 02:18:55 +00:00
|
|
|
powerup.movetype = MOVETYPE_NONE;
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
Light_Green(powerup);
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// Set Up Sparkle Effect
|
2023-03-06 23:44:42 +00:00
|
|
|
sparkle = PU_GetFreeEnt();
|
|
|
|
sparkle.classname = "item_powerup_sparkle";
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
powerup.owner = sparkle;
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
sparkle.origin = where;
|
|
|
|
setorigin(sparkle, sparkle.origin);
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
setmodel(sparkle,"models/sprites/sprkle.spr");
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2024-01-01 20:27:09 +00:00
|
|
|
// Scale down for NZ:P Beta
|
|
|
|
if (map_compatibility_mode == MAP_COMPAT_BETA) {
|
2024-05-16 01:23:07 +00:00
|
|
|
powerup.scale = 0.66;
|
|
|
|
sparkle.scale = 0.45;
|
|
|
|
}
|
|
|
|
// The "normal" sized sparkle also needs scaled down
|
|
|
|
// to be less aggressive and more supplemental.
|
|
|
|
else {
|
|
|
|
sparkle.scale = 0.66;
|
2024-01-01 20:27:09 +00:00
|
|
|
}
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
sparkle.think = PU_SparkleThink;
|
|
|
|
sparkle.nextthink = time + 0.1;
|
|
|
|
|
|
|
|
// Drop Sounds
|
2024-09-02 01:12:38 +00:00
|
|
|
Sound_PlaySound(sparkle, "sounds/pu/powerup.wav", SOUND_TYPE_ENV_OBJECT, SOUND_PRIORITY_PLAYALWAYS);
|
|
|
|
Sound_PlaySound(powerup, "sounds/pu/drop.wav", SOUND_TYPE_ENV_CHING, SOUND_PRIORITY_PLAYALWAYS);
|
2022-02-08 18:42:28 +00:00
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// Check if we were forcefully assigned an ID
|
|
|
|
if (type != -1) {
|
|
|
|
powerup.walktype = type;
|
2022-02-08 18:42:28 +00:00
|
|
|
}
|
2022-05-08 02:18:55 +00:00
|
|
|
// No, so let's grab one from the array.
|
|
|
|
else {
|
|
|
|
powerup.walktype = PU_GetNextPowerUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assign the Power-Up model and sound
|
2023-08-29 15:44:41 +00:00
|
|
|
powerup.effects = EF_FULLBRIGHT;
|
2022-05-09 19:47:40 +00:00
|
|
|
powerup.model = powerup.oldmodel = PU_ModelPath(powerup.walktype);
|
|
|
|
setmodel(powerup, powerup.model);
|
2022-05-08 02:18:55 +00:00
|
|
|
powerup.powerup_vo = PU_VoiceoverPath(powerup.walktype);
|
|
|
|
|
2022-05-09 19:47:40 +00:00
|
|
|
// Time out
|
|
|
|
powerup.think = PU_Flash;
|
|
|
|
powerup.nextthink = time + 15;
|
|
|
|
|
2022-05-08 02:18:55 +00:00
|
|
|
// Finally assign collision function
|
|
|
|
powerup.touch = PU_Touch;
|
|
|
|
|
2024-05-16 02:17:45 +00:00
|
|
|
// We draw the Power-Up in CSQC on FTE to employ our custom EF_ROTATE
|
|
|
|
// implementation.
|
|
|
|
#ifdef FTE
|
|
|
|
|
|
|
|
powerup.SendEntity = PU_SendEntity;
|
|
|
|
powerup.SendFlags = 1;
|
|
|
|
|
|
|
|
#endif // FTE
|
|
|
|
|
2022-02-08 18:42:28 +00:00
|
|
|
totalpowerups++;
|
2022-05-08 02:18:55 +00:00
|
|
|
};
|
|
|
|
|