quakec/source/server/entities/traps.qc
Steam Deck User 89742dc652 GLOBAL: Retire CTR QuakeC, Merge with PSP.
This rebrands the two under the "HANDHELD" name. The two platforms now
function identically in regards to server functions, making them
unified in behavior and general server-reliant functions.
2022-12-28 15:21:19 -05:00

314 lines
No EOL
7.5 KiB
C++

/*
server/entities/map_entities.qc
logic and entities pertaining to in-game traps
Copyright (C) 2021-2022 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
*/
//
// --------------------
// Electric Trap
// --------------------
//
#define ELECTRICTRAP_MODE_TIMER 0 // Der Riese style; trap cools down after X seconds.
#define ELECTRICTRAP_MODE_ROUND 1 // SNN/Verruckt style; trap cools down at end of Round.
//
// Electric Switch On Animation
//
void() A_ElecSwitchOn1 = [ 1, A_ElecSwitchOn2 ] {self.frame = 0;};
void() A_ElecSwitchOn2 = [ 2, A_ElecSwitchOn3 ] {self.frame = 1;};
void() A_ElecSwitchOn3 = [ 3, A_ElecSwitchOn4 ] {self.frame = 2;};
void() A_ElecSwitchOn4 = [ 4, SUB_Null ] {self.frame = 2;};
//
// Electric Swich Off Animation
//
void() A_ElecSwitchOff1 = [ 1, A_ElecSwitchOff2 ] {self.frame = 2;};
void() A_ElecSwitchOff2 = [ 2, A_ElecSwitchOff3 ] {self.frame = 1;};
void() A_ElecSwitchOff3 = [ 3, SUB_Null ] {self.frame = 0;};
//
// zapper_do_damage
// Called when entities touch the Electric barrier.
//
void() zapper_do_damage
{
entity tempe;
// Player Logic:
// - Trap instantly kills player on contact without Jugg,
// having Jugg/Toughness reduces damage to only 50.
// - 1.25s delay before damage can be dealt again.
// - Player is slowed down by 50% for 2.5 seconds. (Stop sprinting)
if (other.classname == "player")
{
// Inflict Damage
if (other.damage_timer < time) {
if (other.perks & P_JUG)
DamageHandler(other, self, 50, S_ZAPPER);
else
DamageHandler(other, self, other.health, S_ZAPPER);
other.damage_timer = time + 1.25;
}
// Inflict Speed Penalty
if (other.speed_penalty_time < time) {
other.speed_penalty = 0.5;
other.speed_penalty_time = time + 2.5;
// Make sure we can't sprint
if (other.sprinting) {
tempe = self;
self = other;
W_SprintStop();
self = tempe;
}
}
}
// Zombie Logic:
// - Zombie can take up to 1.25 seconds to die after contact,
// cannot damage in this state.
// - Has a chance to gib.
// - No normal death sound, play elec sound on contact, then
// again on death.
if (other.classname == "ai_zombie" && !other.electro_targeted) {
tempe = self;
self = other;
Z_ElectroShock();
self = tempe;
}
// Dog Logic:
// - Dogs should always explode on contact, instantly.
// - Trap plays Electro-Shock sound, dog plays explosion.
else if (other.classname == "ai_dog") {
sound(self, CHAN_WEAPON, "sounds/machines/elec_shock.wav", 1, ATTN_NORM);
tempe = self;
self = other;
self.electro_targeted = true;
self.th_die();
self = tempe;
}
}
void zapper_play () {
entity zents = find(world, targetname, self.target);
local vector org = self.origin;
local vector targetorg = zents.origin;
if (zents) {
#ifdef PC
te_lightning2(self, self.origin, zents.origin);
#endif
#ifdef HANDHELD
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
WriteEntity (MSG_BROADCAST, self);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
WriteCoord (MSG_BROADCAST, targetorg_x);
WriteCoord (MSG_BROADCAST, targetorg_y);
WriteCoord (MSG_BROADCAST, targetorg_z);
#endif
}
zents.touch = zapper_do_damage;
self.think = zapper_play;
self.nextthink = time + (0.5*random());
}
void() zapper_touch;
void() zapper_cooldown =
{
A_ElecSwitchOff1();
self.touch = zapper_touch;
}
void zapper_stop() {
entity zents;
entity tempe;
zents = find(world, zappername, self.zappername);
while (zents)
{
if (zents.classname == "zapper_light") {
zents.skin = 0;
} else if (zents.classname == "zapper_switch") {
tempe = self;
self = zents;
self.state = 0;
if (self.mode == ELECTRICTRAP_MODE_TIMER) {
self.think = zapper_cooldown;
self.nextthink = time + self.cooldown;
}
self = tempe;
} else if (zents.classname == "zapper_node" && zents.target) {
zents.think = SUB_Null;
zents.nextthink = 0;
}
zents.touch = SUB_Null;
zents = find(zents, zappername, self.zappername);
}
remove(self);
}
void zapper_start(string zapper) {
entity zents;
entity tempe;
zents = find(world, zappername, zapper);
while (zents)
{
if (zents.classname == "zapper_light") {
zents.skin = 1;
} else if (zents.classname == "zapper_switch") {
tempe = self;
self = zents;
self.state = 1;
A_ElecSwitchOn1();
self = tempe;
} else if (zents.classname == "zapper_node" && zents.target) {
zents.think = zapper_play;
zents.nextthink = time + 0.1;
}
zents = find(zents, zappername, zapper);
}
tempe = spawn();
tempe.think = zapper_stop;
tempe.nextthink = time + self.calc_time;
tempe.zappername = zapper;
}
void zapper_touch () {
if (other.classname != "player") {
return;
}
if (self.requirespower == true && !isPowerOn) {
useprint (other, 8, 0, 0);
return;
}
if (self.state == 0) {
useprint (other, 11, self.cost, self.weapon);
if (!other.button7) {
return;
}
if (other.points < self.cost) {
centerprint(other, STR_NOTENOUGHPOINTS);
return;
}
zapper_start(self.zappername);
addmoney(other, -1*self.cost, false);
}
}
void() zapper_switch =
{
//
// Set Default Stats for Compatibility
//
// Model
if (!self.model)
self.model = "models/machines/quake_scale/zapper/z_switch.mdl";
// Cost
if (!self.cost)
self.cost = 1000;
// Cooldown Time
if (!self.cooldown)
self.cooldown = 30;
// Lasting Time
if (!self.calc_time)
self.calc_time = 60;
self.solid = SOLID_TRIGGER;
precache_model(self.model);
precache_sound("sounds/machines/elec_shock.wav");
setorigin(self, self.origin);
setmodel(self, self.model);
setsize(self, '-4 -4 -4', '4 4 4');
self.state = 0;
self.touch = zapper_touch;
self.movetype = MOVETYPE_NONE;
self.classname = "zapper_switch";
};
void() zapper_node =
{
//
// Set Default Stats for Compatibility
//
// Model
if (!self.model)
self.model = "models/machines/quake_scale/zapper/z_zap.mdl";
self.solid = SOLID_TRIGGER;
precache_model(self.model);
setorigin(self, self.origin);
setmodel(self, self.model);
setsize (self, '-4 -4 -4', '4 4 4');
self.movetype = MOVETYPE_NONE;
self.classname = "zapper_node";
};
void() zapper_light =
{
//
// Set Default Stats for Compatibility
//
// Model
if (!self.model)
self.model = "models/machines/quake_scale/zapper/z_light.mdl";
self.solid = SOLID_TRIGGER;
precache_model(self.model);
setorigin(self, self.origin);
setmodel(self, self.model);
setsize(self, '-4 -4 -4', '4 4 4');
self.movetype = MOVETYPE_NONE;
self.classname = "zapper_light";
};
void() trigger_electro =
{
self.classname = "zapper_field";
InitTrigger ();
self.solid = SOLID_TRIGGER;
};