quakec/source/server/entities/traps.qc
2023-01-13 10:58:57 -05:00

320 lines
No EOL
7.7 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_head" || other.classname == "ai_zombie_rarm"
|| other.classname == "ai_zombie_larm") {
// If we're a limb, grab our body.
other = other.owner;
}
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);
makevectors(self.angles);
setsize (self, '-20 -20 -4', '20 20 0' + (v_up * 100));
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;
};