mirror of
https://github.com/id-Software/quake-rerelease-qc.git
synced 2024-11-21 20:10:55 +00:00
899 lines
24 KiB
C++
899 lines
24 KiB
C++
/* Copyright (C) 1996-2022 id Software LLC
|
|
|
|
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 the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
See file, 'COPYING', for details.
|
|
*/
|
|
|
|
/* Items QuickC program
|
|
By Jim Dose' 9/13/96
|
|
*/
|
|
|
|
float UNDERWATER = 2;
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
HIPNOTIC ITEMS
|
|
|
|
===============================================================================
|
|
*/
|
|
//
|
|
// hip_powerup_touch function
|
|
//
|
|
void() hip_powerup_touch =
|
|
{
|
|
local entity stemp;
|
|
local float best;
|
|
|
|
if (other.classname != "player")
|
|
return;
|
|
if (other.health <= 0)
|
|
return;
|
|
|
|
sprint(other, "$qc_got_item", self.netname);
|
|
|
|
if (deathmatch)
|
|
{
|
|
self.mdl = self.model;
|
|
|
|
// if ((self.classname == "item_artifact_invulnerability") ||
|
|
// (self.classname == "item_artifact_invisibility"))
|
|
// self.nextthink = time + 60*5;
|
|
// else
|
|
self.nextthink = time + 60;
|
|
|
|
self.think = SUB_regen;
|
|
}
|
|
|
|
sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);
|
|
stuffcmd (other, "bf\n");
|
|
self.solid = SOLID_NOT;
|
|
other.items2 = other.items2 | self.items2;
|
|
self.model = string_null;
|
|
|
|
// do the apropriate action
|
|
if ( self.classname == "item_artifact_wetsuit" )
|
|
{
|
|
other.wetsuit_time = 1;
|
|
other.wetsuit_finished = time + 30;
|
|
}
|
|
if ( self.classname == "item_artifact_empathy_shields" )
|
|
{
|
|
other.empathy_time = 1;
|
|
other.empathy_finished = time + 30;
|
|
}
|
|
|
|
activator = other;
|
|
SUB_UseTargets(); // fire all targets / killtargets
|
|
};
|
|
|
|
|
|
/*QUAKED item_artifact_wetsuit (0 .5 .8) (-16 -16 -24) (16 16 32)
|
|
Player takes no damage from electrical attacks and swims faster for 30 seconds
|
|
*/
|
|
void() item_artifact_wetsuit =
|
|
{
|
|
self.touch = hip_powerup_touch;
|
|
|
|
precache_model ("progs/wetsuit.mdl");
|
|
precache_sound ("misc/wetsuit.wav");
|
|
precache_sound ("misc/weton.wav");
|
|
precache_sound ("items/suit2.wav");
|
|
self.noise = "misc/weton.wav";
|
|
setmodel (self, "progs/wetsuit.mdl");
|
|
self.netname = "$qc_wetsuit";
|
|
self.items2 = HIP_IT_WETSUIT;
|
|
setsize (self, '-16 -16 -24', '16 16 32');
|
|
|
|
StartItem ();
|
|
};
|
|
|
|
/*
|
|
===============================================================================
|
|
//
|
|
// Horn of Conjuring
|
|
//
|
|
===============================================================================
|
|
*/
|
|
|
|
void() horn_touch =
|
|
{
|
|
local float amount;
|
|
local float value;
|
|
|
|
if (other.classname != "player")
|
|
return;
|
|
|
|
if (deathmatch)
|
|
{
|
|
self.mdl = self.model;
|
|
|
|
self.nextthink = time + 60;
|
|
|
|
self.think = SUB_regen;
|
|
}
|
|
|
|
self.solid = SOLID_NOT;
|
|
self.model = string_null;
|
|
sprint (other, "$qc_got_horn");
|
|
sound (other, CHAN_VOICE, self.noise, 1, ATTN_NONE);
|
|
stuffcmd (other, "bf\n");
|
|
activator = other;
|
|
horn_active = 1;
|
|
horn_charmer = other;
|
|
SUB_UseTargets(); // fire all targets / killtargets
|
|
horn_active = 0;
|
|
};
|
|
|
|
/*QUAKED item_hornofconjuring (0 .5 .8) (-16 -16 0) (16 16 32)
|
|
Horn of Conjuring.
|
|
You must make func_spawn entities connected to this entity
|
|
to spawn the charmed creature.
|
|
*/
|
|
void() item_hornofconjuring =
|
|
{
|
|
self.touch = horn_touch;
|
|
|
|
precache_model("progs/horn.mdl");
|
|
precache_sound("hipitems/horn.wav");
|
|
setmodel(self, "progs/horn.mdl");
|
|
self.noise = "hipitems/horn.wav";
|
|
setsize (self, '-16 -16 0', '16 16 32');
|
|
StartItem ();
|
|
};
|
|
|
|
/*QUAKED item_artifact_empathy_shields (0 .5 .8) (-16 -16 0) (16 16 32)
|
|
Empathy Shield.
|
|
*/
|
|
void() item_artifact_empathy_shields =
|
|
{
|
|
self.touch = hip_powerup_touch;
|
|
|
|
precache_model("progs/empathy.mdl");
|
|
precache_sound("hipitems/empathy.wav");
|
|
precache_sound("hipitems/empathy2.wav");
|
|
precache_sound ("items/suit2.wav");
|
|
setmodel(self, "progs/empathy.mdl");
|
|
self.noise = "hipitems/empathy.wav";
|
|
self.netname = "$qc_empathy_shields";
|
|
self.items2 = HIP_IT_EMPATHY_SHIELDS;
|
|
setsize (self, '-16 -16 0', '16 16 32');
|
|
StartItem ();
|
|
};
|
|
/*
|
|
===============================================================================
|
|
|
|
HIPNOTIC WEAPONS
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*QUAKED weapon_mjolnir (0 .5 .8) (-16 -16 0) (16 16 32)
|
|
*/
|
|
|
|
void() weapon_mjolnir =
|
|
{
|
|
precache_model ("progs/g_hammer.mdl");
|
|
setmodel (self, "progs/g_hammer.mdl");
|
|
self.weapon = 3;
|
|
self.netname = "$qc_mjolnir";
|
|
self.items = IT_MJOLNIR;
|
|
self.touch = weapon_touch;
|
|
setsize (self, '-16 -16 0', '16 16 56');
|
|
StartItem ();
|
|
};
|
|
|
|
/*QUAKED weapon_laser_gun (0 .5 .8) (-16 -16 0) (16 16 32)
|
|
*/
|
|
|
|
void() weapon_laser_gun =
|
|
{
|
|
precache_model ("progs/g_laserg.mdl");
|
|
setmodel (self, "progs/g_laserg.mdl");
|
|
self.weapon = 3;
|
|
self.netname = "$qc_laser_cannon";
|
|
self.items = IT_LASER_CANNON;
|
|
self.touch = weapon_touch;
|
|
setsize (self, '-16 -16 0', '16 16 56');
|
|
StartItem ();
|
|
};
|
|
|
|
/*QUAKED weapon_proximity_gun (0 .5 .8) (-16 -16 0) (16 16 32)
|
|
*/
|
|
|
|
void() weapon_proximity_gun =
|
|
{
|
|
precache_model ("progs/g_prox.mdl");
|
|
setmodel (self, "progs/g_prox.mdl");
|
|
self.weapon = 3;
|
|
self.netname = "$qc_prox_gun";
|
|
self.items = IT_PROXIMITY_GUN;
|
|
self.touch = weapon_touch;
|
|
setsize (self, '-16 -16 0', '16 16 56');
|
|
StartItem ();
|
|
};
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
HIPNOTIC HAZARDS
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
void() spikemine_Home =
|
|
{
|
|
local entity head;
|
|
local entity selected;
|
|
local float cur_dist;
|
|
local float head_dist;
|
|
local vector dir, vtemp;
|
|
|
|
self.frame = self.frame + 1;
|
|
if (self.frame==9) self.frame = 0;
|
|
self.nextthink = time + 0.2;
|
|
self.think = spikemine_Home;
|
|
|
|
// look in our immediate vicinity
|
|
|
|
if (self.search_time < time)
|
|
{
|
|
selected = world;
|
|
cur_dist = 2000;
|
|
head = findradius(self.origin, 2000);
|
|
while(head)
|
|
{
|
|
if(!(head.flags & FL_NOTARGET) && (head.flags & FL_CLIENT))
|
|
{
|
|
if (visible(head) && (head.health > 0))
|
|
{
|
|
head_dist = vlen(head.origin-self.origin);
|
|
if (head_dist < cur_dist)
|
|
{
|
|
selected = head;
|
|
cur_dist = head_dist;
|
|
}
|
|
}
|
|
}
|
|
head = head.chain;
|
|
}
|
|
// if (selected != world && selected != self.enemy)
|
|
if (selected != world)
|
|
sound (self, CHAN_VOICE, "hipitems/spikmine.wav", 1, ATTN_NORM);
|
|
self.enemy = selected;
|
|
self.search_time = time + 1.3;
|
|
}
|
|
if (self.enemy == world)
|
|
{
|
|
sound (self, CHAN_VOICE, "misc/null.wav", 1, ATTN_NORM);
|
|
self.velocity = '0 0 0';
|
|
return;
|
|
}
|
|
vtemp = self.enemy.origin + '0 0 10';
|
|
dir = normalize(vtemp - self.origin);
|
|
if (infront(self.enemy))
|
|
{
|
|
self.velocity = dir * ((skill*50) + 50);
|
|
}
|
|
else
|
|
{
|
|
self.velocity = dir * ((skill*50) + 150);
|
|
}
|
|
};
|
|
|
|
void() spikemine_Touch =
|
|
{
|
|
if (self.health>0)
|
|
{
|
|
if (other.classname == "trap_spike_mine")
|
|
return;
|
|
if (other.classname == "missile")
|
|
return;
|
|
if (other.classname == "grenade")
|
|
return;
|
|
if (other.classname == "hiplaser")
|
|
return;
|
|
if (other.classname == "proximity_grenade")
|
|
return;
|
|
|
|
T_Damage(self,self,self,self.health+10);
|
|
// killed_monsters = killed_monsters + 1;
|
|
// WriteByte (MSG_ALL, SVC_KILLEDMONSTER);
|
|
}
|
|
// self.effects = self.effects | EF_MUZZLEFLASH;
|
|
|
|
T_RadiusDamage (self, self, 110, world);
|
|
sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM);
|
|
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
|
|
sound (self, CHAN_VOICE, "misc/null.wav", 1, ATTN_NORM);
|
|
self.velocity = '0 0 0';
|
|
self.touch = SUB_Null;
|
|
setmodel (self, "progs/s_explod.spr");
|
|
self.solid = SOLID_NOT;
|
|
s_explode1 ();
|
|
};
|
|
|
|
/*
|
|
spike_mine_first_think
|
|
*/
|
|
|
|
void() spike_mine_first_think =
|
|
{
|
|
self.think = spikemine_Home;
|
|
self.nextthink = time + 0.1;
|
|
self.search_time = 0;
|
|
self.takedamage = DAMAGE_AIM;
|
|
self.use = monster_use;
|
|
};
|
|
|
|
/*QUAKED trap_spike_mine (0 .5 .8) (-16 -16 0) (16 16 32)
|
|
*/
|
|
|
|
void() trap_spike_mine =
|
|
{
|
|
if (deathmatch)
|
|
{
|
|
remove(self);
|
|
return;
|
|
}
|
|
precache_model ("progs/spikmine.mdl");
|
|
precache_sound ("weapons/r_exp3.wav");
|
|
precache_sound ("hipitems/spikmine.wav");
|
|
precache_sound ("misc/null.wav");
|
|
setmodel (self, "progs/spikmine.mdl");
|
|
// setmodel (self, "progs/spike.mdl");
|
|
setsize (self, self.mins, self.maxs);
|
|
self.classname = "trap_spike_mine";
|
|
self.solid = SOLID_BBOX;
|
|
self.movetype = MOVETYPE_FLYMISSILE;
|
|
// setsize (self, '0 0 0', '0 0 0');
|
|
// self.avelocity = '-100 100 -100';
|
|
self.avelocity = '-50 100 150';
|
|
if (cvar("skill") <= 1)
|
|
self.health = 200;
|
|
else
|
|
self.health = 400;
|
|
self.frame = 0;
|
|
self.think = spike_mine_first_think;
|
|
self.touch = spikemine_Touch;
|
|
self.th_die = spikemine_Touch;
|
|
self.th_stand = spikemine_Home;
|
|
self.th_walk = spikemine_Home;
|
|
self.th_run = spikemine_Home;
|
|
self.th_melee = spikemine_Home;
|
|
self.th_missile = spikemine_Home;
|
|
self.nextthink = time + 0.2;
|
|
total_monsters = total_monsters + 1;
|
|
self.flags = self.flags | FL_MONSTER;
|
|
self.deathtype = "$qc_suicide_mine";
|
|
};
|
|
|
|
//============================================================================
|
|
float LIGHTNING_RANDOM = 1;
|
|
float LIGHTNING_BOOM = 2;
|
|
|
|
void() SpawnLightningThink =
|
|
{
|
|
if (time > self.delay)
|
|
{
|
|
remove(self);
|
|
return;
|
|
}
|
|
self.think = SpawnLightningThink;
|
|
if (checkclient())
|
|
{
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
|
|
WriteEntity (MSG_BROADCAST, self);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
WriteCoord (MSG_BROADCAST, self.oldorigin_x);
|
|
WriteCoord (MSG_BROADCAST, self.oldorigin_y);
|
|
WriteCoord (MSG_BROADCAST, self.oldorigin_z);
|
|
}
|
|
LightningDamage(self.origin, self.oldorigin, self.lastvictim, self.dmg);
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
void() trap_lightning_use =
|
|
{
|
|
local vector p1, p2;
|
|
local vector dir;
|
|
local float dst;
|
|
local float remainder;
|
|
|
|
if (time >= self.pausetime)
|
|
{
|
|
if (self.spawnflags & LIGHTNING_BOOM)
|
|
sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);
|
|
else
|
|
sound (self, CHAN_AUTO, "weapons/lhit.wav", 1, ATTN_NORM);
|
|
if (self.classname == "trap_lightning_triggered")
|
|
self.pausetime = time + 0.1;
|
|
}
|
|
if (self.target)
|
|
{
|
|
p1 = self.origin;
|
|
p2 = self.enemy.origin;
|
|
}
|
|
else
|
|
{
|
|
makevectors (self.angles);
|
|
self.movedir = v_forward;
|
|
traceline (self.origin, self.origin + self.movedir*600, TRUE, self);
|
|
p1 = self.origin;
|
|
p2 = trace_endpos;
|
|
}
|
|
// fix up both ends of the lightning
|
|
// lightning bolts are 30 units long each
|
|
dir = normalize( p2-p1 );
|
|
dst = vlen(p2-p1);
|
|
dst = dst / 30.0;
|
|
remainder = dst - floor(dst);
|
|
if (remainder > 0)
|
|
{
|
|
remainder = remainder - 1;
|
|
// split half the remainder with the front and back
|
|
remainder = remainder * 15;
|
|
p1 = p1 + (remainder*dir);
|
|
p2 = p2 - (remainder*dir);
|
|
}
|
|
if (self.duration > 0.1)
|
|
{
|
|
local entity temp;
|
|
|
|
temp = self;
|
|
self = spawn();
|
|
self.origin = p1;
|
|
self.oldorigin = p2;
|
|
self.lastvictim = temp;
|
|
self.dmg = temp.dmg;
|
|
self.delay = time + temp.duration;
|
|
SpawnLightningThink();
|
|
self = temp;
|
|
}
|
|
else if (checkclient())
|
|
{
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
|
|
WriteEntity (MSG_BROADCAST, self);
|
|
WriteCoord (MSG_BROADCAST, p1_x);
|
|
WriteCoord (MSG_BROADCAST, p1_y);
|
|
WriteCoord (MSG_BROADCAST, p1_z);
|
|
WriteCoord (MSG_BROADCAST, p2_x);
|
|
WriteCoord (MSG_BROADCAST, p2_y);
|
|
WriteCoord (MSG_BROADCAST, p2_z);
|
|
LightningDamage(p1, p2, self, self.dmg);
|
|
}
|
|
else
|
|
LightningDamage(p1, p2, self, self.dmg);
|
|
};
|
|
|
|
void() lightning_think =
|
|
{
|
|
local float timedelay;
|
|
|
|
if (self.state)
|
|
{
|
|
trap_lightning_use();
|
|
}
|
|
if (self.cnt == 0)
|
|
{
|
|
if (self.spawnflags & LIGHTNING_RANDOM)
|
|
{
|
|
timedelay = self.wait*random();
|
|
}
|
|
else
|
|
{
|
|
timedelay = self.wait;
|
|
}
|
|
self.cnt = 1;
|
|
self.t_length = time + self.duration - 0.1;
|
|
self.pausetime = time + self.duration - 0.1;
|
|
if (self.pausetime < time + 0.3)
|
|
self.pausetime = time + 0.3;
|
|
if (timedelay < self.duration)
|
|
timedelay = self.duration;
|
|
self.t_width = time + timedelay;
|
|
}
|
|
if (time >= self.t_length)
|
|
{
|
|
self.cnt = 0;
|
|
self.nextthink = self.t_width;
|
|
}
|
|
else
|
|
{
|
|
self.nextthink = time + 0.2;
|
|
}
|
|
};
|
|
|
|
void() lightning_firstthink =
|
|
{
|
|
local entity targ;
|
|
if (self.target)
|
|
{
|
|
targ = find(world,targetname,self.target);
|
|
self.dest = targ.origin;
|
|
self.enemy = targ;
|
|
}
|
|
self.think = SUB_Null;
|
|
self.nextthink = 0;
|
|
if (self.classname != "trap_lightning_triggered")
|
|
{
|
|
self.nextthink = self.huntingcharmer + self.wait + self.ltime;
|
|
self.think = lightning_think;
|
|
}
|
|
};
|
|
|
|
/*QUAKED trap_lightning_triggered (0 .5 .8) (-8 -8 -8) (8 8 8) random boom
|
|
When triggered, fires lightning in the direction set in QuakeEd.
|
|
"wait" how long to wait between blasts (1.0 default)
|
|
if in random mode wait is multiplied by random
|
|
"dmg" how much damage lightning should inflict (30 default)
|
|
"duration" how long each lightning attack should last (0.1 default)
|
|
*/
|
|
|
|
void() trap_lightning_triggered =
|
|
{
|
|
if (self.wait == 0)
|
|
self.wait = 1.0;
|
|
if (self.dmg == 0)
|
|
self.dmg = 30;
|
|
if (self.duration == 0)
|
|
self.duration = 0.1;
|
|
self.cnt = 0;
|
|
self.use = trap_lightning_use;
|
|
precache_sound ("weapons/lhit.wav");
|
|
precache_sound ("weapons/lstart.wav");
|
|
self.huntingcharmer = self.nextthink;
|
|
self.think = lightning_firstthink;
|
|
self.nextthink = time + 0.25;
|
|
self.deathtype = "$qc_suicide_electrocuted";
|
|
};
|
|
|
|
|
|
/*QUAKED trap_lightning (0 .5 .8) (-8 -8 -8) (8 8 8) random boom
|
|
Continuously fire lightning.
|
|
"wait" how long to wait between blasts (1.0 default)
|
|
if in random mode wait is multiplied by random
|
|
"nextthink" delay before firing first lightning, so multiple traps can be stagered.
|
|
"dmg" how much damage lightning should inflict (30 default)
|
|
"duration" how long each lightning attack should last (0.1 default)
|
|
*/
|
|
void() trap_lightning =
|
|
{
|
|
trap_lightning_triggered ();
|
|
self.state = 1;
|
|
};
|
|
|
|
void() trap_lightning_switched_use =
|
|
{
|
|
self.state = 1 - self.state;
|
|
if (self.state == 1)
|
|
self.nextthink = self.huntingcharmer;
|
|
};
|
|
/*QUAKED trap_lightning_switched (0 .5 .8) (-8 -8 -8) (8 8 8) random boom
|
|
Continuously fires lightning.
|
|
"wait" how long to wait between blasts (1.0 default)
|
|
if in random mode wait is multiplied by random
|
|
"nextthink" delay before firing first lightning, so multiple traps can be stagered.
|
|
"dmg" how much damage lightning should inflict (30 default)
|
|
"duration" how long each lightning attack should last (0.1 default)
|
|
"state" 0 (default) initially off, 1 initially on.
|
|
*/
|
|
void() trap_lightning_switched =
|
|
{
|
|
trap_lightning_triggered ();
|
|
self.use = trap_lightning_switched_use;
|
|
};
|
|
|
|
|
|
entity tesla_target;
|
|
float tesla_numtargets;
|
|
void() trap_tesla_scan =
|
|
{
|
|
local entity head;
|
|
local entity prev;
|
|
|
|
// look in our immediate vicinity
|
|
|
|
tesla_numtargets = 0;
|
|
head = findradius(self.origin, self.distance);
|
|
while(head)
|
|
{
|
|
if(!(head.flags & FL_NOTARGET) && (head.flags & self.cnt))
|
|
{
|
|
if (visible(head) && (head.health > 0) && (head.struck_by_mjolnir==0))
|
|
{
|
|
if (tesla_numtargets == 0)
|
|
{
|
|
tesla_target = head;
|
|
}
|
|
else
|
|
{
|
|
prev.next_ent = head;
|
|
}
|
|
tesla_numtargets = tesla_numtargets + 1;
|
|
prev = head;
|
|
if (tesla_numtargets==self.count)
|
|
return;
|
|
}
|
|
}
|
|
head = head.chain;
|
|
}
|
|
};
|
|
|
|
void() TeslaLightningThink =
|
|
{
|
|
self.owner.attack_state = 2;
|
|
if (time > self.delay)
|
|
{
|
|
self.enemy.struck_by_mjolnir = 0;
|
|
remove(self);
|
|
return;
|
|
}
|
|
traceline (self.origin, self.enemy.origin, TRUE, self);
|
|
|
|
if (trace_fraction != 1.0 || self.enemy.health<=0 || vlen(self.origin-self.enemy.origin) > (self.distance+10))
|
|
{
|
|
self.enemy.struck_by_mjolnir = 0;
|
|
remove(self);
|
|
return;
|
|
}
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
|
|
WriteEntity (MSG_BROADCAST, self);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_x);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_y);
|
|
WriteCoord (MSG_BROADCAST, trace_endpos_z);
|
|
LightningDamage(self.origin, trace_endpos, self.lastvictim, self.dmg);
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
void(entity targ) SpawnTeslaLightning =
|
|
{
|
|
local entity lgt;
|
|
// spawn actual lightning
|
|
lgt = spawn();
|
|
if (self.duration>0)
|
|
{
|
|
lgt.delay = time + self.duration;
|
|
}
|
|
else
|
|
{
|
|
lgt.delay = time + 9999;
|
|
}
|
|
lgt.enemy = targ;
|
|
targ.struck_by_mjolnir = 1;
|
|
lgt.distance = self.distance;
|
|
lgt.owner = self;
|
|
lgt.lastvictim = self.lastvictim;
|
|
lgt.dmg = self.dmg;
|
|
lgt.origin = self.origin;
|
|
lgt.think = TeslaLightningThink;
|
|
lgt.nextthink = time;
|
|
lgt.deathtype = self.deathtype;
|
|
};
|
|
|
|
void() trap_tesla_think =
|
|
{
|
|
if (self.state == 0)
|
|
{
|
|
self.nextthink = time + 0.25;
|
|
return;
|
|
}
|
|
if (self.attack_state == 0)
|
|
{
|
|
self.think = trap_tesla_think;
|
|
trap_tesla_scan();
|
|
if (tesla_numtargets > 0)
|
|
{
|
|
if (self.wait > 0)
|
|
sound (self, CHAN_AUTO, "misc/tesla.wav", 1, ATTN_NORM);
|
|
self.attack_state = 1;
|
|
self.nextthink = time + self.wait;
|
|
return;
|
|
}
|
|
self.nextthink = time + 0.25;
|
|
if (self.delay > 0)
|
|
{
|
|
if (time > self.search_time)
|
|
{
|
|
self.attack_state = 3;
|
|
}
|
|
}
|
|
}
|
|
else if (self.attack_state == 1)
|
|
{
|
|
trap_tesla_scan();
|
|
while (tesla_numtargets > 0)
|
|
{
|
|
sound (self, CHAN_AUTO, "hipweap/mjolhit.wav", 1, ATTN_NORM);
|
|
SpawnTeslaLightning (tesla_target);
|
|
tesla_target = tesla_target.next_ent;
|
|
tesla_numtargets = tesla_numtargets - 1;
|
|
}
|
|
self.attack_state = 2;
|
|
self.nextthink = time + 1;
|
|
}
|
|
else if (self.attack_state == 2)
|
|
{
|
|
self.attack_state = 3;
|
|
self.nextthink = time + 0.2;
|
|
}
|
|
else if (self.attack_state == 3)
|
|
{
|
|
self.attack_state = 0;
|
|
self.nextthink = time + 0.1;
|
|
if (self.classname == "trap_gods_wrath")
|
|
{
|
|
self.nextthink = -1;
|
|
}
|
|
}
|
|
};
|
|
|
|
/*QUAKED trap_tesla_coil (0 .5 .8) (-8 -8 -8) (8 8 8) targetenemies
|
|
targets enemies in vicinity and fires at them
|
|
"wait" how long build up should be (2 second default)
|
|
"dmg" how much damage lightning should inflict (2 + 5*skill default)
|
|
"duration" how long each lightning attack should last (continuous default)
|
|
"distance" how far the tesla coil should reach (600 default)
|
|
"state" on/off for the coil (0 default is off)
|
|
"count" number of people to target (2 default)
|
|
*/
|
|
void() trap_tesla_coil =
|
|
{
|
|
precache_sound ("misc/tesla.wav");
|
|
precache_sound ("hipweap/mjolhit.wav"); // lightning sound
|
|
if (self.wait == 0)
|
|
self.wait = 2;
|
|
if (self.dmg == 0)
|
|
self.dmg = 2 + (5*cvar("skill"));
|
|
if (self.duration == 0)
|
|
self.duration = -1;
|
|
if (self.distance == 0)
|
|
self.distance = 600;
|
|
if (self.spawnflags & 1)
|
|
self.cnt = FL_CLIENT | FL_MONSTER;
|
|
else
|
|
self.cnt = FL_CLIENT;
|
|
self.use = trap_lightning_switched_use;
|
|
if (self.delay == 0)
|
|
self.delay = -1;
|
|
self.nextthink = time + random();
|
|
self.think = trap_tesla_think;
|
|
self.lastvictim = world;
|
|
tesla_numtargets = 0;
|
|
self.attack_state = 0;
|
|
self.deathtype = "$qc_suicide_electrocuted";
|
|
};
|
|
|
|
void() trap_gods_wrath_use =
|
|
{
|
|
if (self.attack_state==0)
|
|
{
|
|
self.search_time = time + self.delay;
|
|
self.lastvictim = activator;
|
|
trap_tesla_think();
|
|
}
|
|
};
|
|
|
|
/*QUAKED trap_gods_wrath (0 .5 .8) (-8 -8 -8) (8 8 8) targetenemies
|
|
targets enemies in vicinity and fires at them
|
|
"dmg" how much damage lightning should inflict (5 default)
|
|
"duration" how long each lightning attack should last (continuous default)
|
|
"distance" how far god's wrath should reach (600 default)
|
|
"delay" how long to wait until god calms down
|
|
this is only needed if no one is targetted (5 seconds default)
|
|
"count" number of people to target (2 default)
|
|
*/
|
|
void() trap_gods_wrath =
|
|
{
|
|
if (self.delay == 0)
|
|
self.delay = 5;
|
|
trap_tesla_coil();
|
|
self.wait = 0;
|
|
self.state = 1;
|
|
self.nextthink = -1;
|
|
self.deathtype = "$qc_suicide_wrath";
|
|
// self.attack_state = 1;
|
|
self.use = trap_gods_wrath_use;
|
|
};
|
|
|
|
void() trap_gravity_touch =
|
|
{
|
|
if ( self.attack_finished > time )
|
|
return;
|
|
|
|
if (other.takedamage)
|
|
{
|
|
T_Damage (other, self, self, self.dmg );
|
|
self.attack_finished = time + 0.2;
|
|
}
|
|
};
|
|
|
|
void() trap_gravity_think =
|
|
{
|
|
local vector vel;
|
|
local vector dir;
|
|
local vector delta;
|
|
|
|
self.ltime = time;
|
|
trap_tesla_scan();
|
|
while (tesla_numtargets > 0)
|
|
{
|
|
delta = self.origin - tesla_target.origin;
|
|
dir = normalize( delta );
|
|
vel = dir * self.speed;
|
|
if ( ( tesla_target.wetsuit_finished > time ) &&
|
|
( self.spawnflags & UNDERWATER ) )
|
|
{
|
|
vel = vel * 0.6;
|
|
}
|
|
|
|
tesla_target.velocity = tesla_target.velocity + vel;
|
|
tesla_target = tesla_target.next_ent;
|
|
tesla_numtargets = tesla_numtargets - 1;
|
|
}
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
/*QUAKED trap_gravity_well (.8 .5 0) (-8 -8 -8) (8 8 8) targetenemies UNDERWATER
|
|
targets enemies in vicinity and draws them near, gibbing them on contact.
|
|
|
|
UNDERWATER cuts the pull in half for players wearing the wetsuit
|
|
|
|
"distance" how far the gravity well should reach (600 default)
|
|
"count" number of people to target (2 default)
|
|
"speed" is how strong the pull is. (210 default)
|
|
"dmg" is how much damage to do each touch. (10000 default)
|
|
*/
|
|
void() trap_gravity_well =
|
|
{
|
|
self.solid = SOLID_TRIGGER;
|
|
self.movetype = MOVETYPE_NONE;
|
|
setsize( self, '-16 -16 -16','16 16 16');
|
|
if ( self.dmg == 0 )
|
|
{
|
|
self.dmg = 10000;
|
|
}
|
|
if ( self.speed == 0 )
|
|
self.speed = 210;
|
|
if (self.distance == 0)
|
|
self.distance = 600;
|
|
if (self.spawnflags & 1)
|
|
self.cnt = FL_CLIENT | FL_MONSTER;
|
|
else
|
|
self.cnt = FL_CLIENT;
|
|
|
|
self.attack_finished = 0;
|
|
self.think = trap_gravity_think;
|
|
self.touch = trap_gravity_touch;
|
|
self.lastvictim = world;
|
|
tesla_numtargets = 0;
|
|
self.nextthink = time + 0.1;
|
|
self.ltime = time;
|
|
};
|