hexen2/H2W/HCode/TRIGGERS.hc
2000-11-10 00:00:00 +00:00

2558 lines
61 KiB
C++

/*
* $Header: /HexenWorld/HCode/TRIGGERS.hc 10 5/31/98 2:58p Mgummelt $
*/
void() button_return;
void() multi_touch;
float SPAWNFLAG_DODAMAGE = 1;
float SPAWNFLAG_QMULT = 2;
float COUNTER_ORDERED = 2;
entity stemp, otemp, s, old;
void() trigger_reactivate =
{
self.solid = SOLID_TRIGGER;
};
//=============================================================================
float SPAWNFLAG_NOMESSAGE = 1;
float SPAWNFLAG_NOTOUCH = 1;
float SPAWNFLAG_MTOUCH = 2;
float SPAWNFLAG_PUSHTOUCH = 4;
float ALWAYS_RETURN = 4;//for trigger_counter
//float SPAWNFLAG_ACTIVATED = 8;
float SPAWNFLAG_REMOVE_PP = 16;
float SPAWNFLAG_NO_PP = 32;
// the wait time has passed, so set back up for another activation
void() multi_wait =
{
self.check_ok=FALSE;
if (self.max_health)
{
self.health = self.max_health;
self.takedamage = DAMAGE_YES;
self.solid = SOLID_BBOX;
}
};
// the trigger was just touched/killed/used
// self.enemy should be set to the activator so it can be held through a delay
// so wait for the delay time before firing
void() multi_trigger =
{
//dprint("trigger fired\n");
if (self.nextthink > time)
{
return; // already been triggered
}
if (self.classname == "trigger_secret")
{
if (self.enemy.classname != "player")
return;
found_secrets = found_secrets + 1;
WriteByte (MSG_ALL, SVC_FOUNDSECRET);
}
if (self.noise) sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
// don't trigger again until reset
self.takedamage = DAMAGE_NO;
activator = self.enemy;
if (self.experience_value)
{
AwardExperience(activator,self,0);
}
self.check_ok=TRUE;
SUB_UseTargets();
if (self.wait > 0)
{
self.think = multi_wait;
thinktime self : self.wait;
}
else
{ // we can't just remove (self) here, because this is a touch function
// called while C code is looping through area links...
self.touch = self.think = self.use = SUB_Null;
self.nextthink=-1;
/*Don't want to remove- may be checked later
thinktime self : 0.1;
self.think = SUB_Remove;
*/
}
};
void() multi_killed =
{
self.enemy = damage_attacker;
multi_trigger();
};
float client_has_piece(entity client, string piece)
{
if (client.puzzle_inv1 == piece ||
client.puzzle_inv2 == piece ||
client.puzzle_inv3 == piece ||
client.puzzle_inv4 == piece ||
client.puzzle_inv5 == piece ||
client.puzzle_inv6 == piece ||
client.puzzle_inv7 == piece ||
client.puzzle_inv8 == piece)
return 1;
if (client.puzzles_cheat) // Did they cheat to get through
return 1;
return 0;
}
void client_remove_piece(entity client, string piece)
{
if (!piece) return;
if (client.puzzle_inv1 == piece)
client.puzzle_inv1 = string_null;
else if (client.puzzle_inv2 == piece)
client.puzzle_inv2 = string_null;
else if (client.puzzle_inv3 == piece)
client.puzzle_inv3 = string_null;
else if (client.puzzle_inv4 == piece)
client.puzzle_inv4 = string_null;
else if (client.puzzle_inv5 == piece)
client.puzzle_inv5 = string_null;
else if (client.puzzle_inv6 == piece)
client.puzzle_inv6 = string_null;
else if (client.puzzle_inv7 == piece)
client.puzzle_inv7 = string_null;
else if (client.puzzle_inv8 == piece)
client.puzzle_inv8 = string_null;
}
float check_puzzle_pieces(entity client, float remove_pieces, float inverse)
{
float required, has;
entity found;
required = has = 0;
if (self.puzzle_piece_1)
{
required (+) 1;
if (client_has_piece(client,self.puzzle_piece_1))
has (+) 1;
}
if (self.puzzle_piece_2)
{
required (+) 2;
if (client_has_piece(client,self.puzzle_piece_2))
has (+) 2;
}
if (self.puzzle_piece_3)
{
required (+) 4;
if (client_has_piece(client,self.puzzle_piece_3))
has (+) 4;
}
if (self.puzzle_piece_4)
{
required (+) 8;
if (client_has_piece(client,self.puzzle_piece_4))
has (+) 8;
}
if (!inverse && required != has)
return 0;
else if (inverse && required == has)
return 0;
if (remove_pieces)
{
found = find(world, classname, "player");
while (found)
{
client_remove_piece(found,self.puzzle_piece_1);
client_remove_piece(found,self.puzzle_piece_2);
client_remove_piece(found,self.puzzle_piece_3);
client_remove_piece(found,self.puzzle_piece_4);
found = find(found, classname, "player");
}
}
return 1;
}
void() multi_use =
{
string temp;
float removepp, inversepp;
if (time < self.attack_finished)
return;
if (self.spawnflags & SPAWNFLAG_ACTIVATED)
{
self.touch = multi_touch;
return;
}
removepp = (self.spawnflags & SPAWNFLAG_REMOVE_PP);
inversepp = (self.spawnflags & SPAWNFLAG_NO_PP);
if (!check_puzzle_pieces(other,removepp,inversepp))
{
if (self.no_puzzle_msg && !deathmatch)
{
temp = getstring(self.no_puzzle_msg);
if (!deathmatch)
centerprint (other, temp);
self.attack_finished = time + 2;
}
return;
}
self.enemy = activator;
multi_trigger();
};
void() multi_touch =
{
float removepp, inversepp;
string temp;
if (time < self.attack_finished)
return;
if(self.inactive)
return;
if (self.spawnflags & SPAWNFLAG_MTOUCH)
{
if (!other.flags & FL_MONSTER)
return;
}
else if (self.spawnflags & SPAWNFLAG_PUSHTOUCH)
{
if (!other.flags & FL_PUSH)
return;
}
else if (other.classname != "player")
return;
// if the trigger has an angles field, check player's facing direction
if (self.movedir != '0 0 0')
{
makevectors (other.angles);
if (v_forward * self.movedir < 0)
return; // not facing the right way
}
removepp = (self.spawnflags & SPAWNFLAG_REMOVE_PP);
inversepp = (self.spawnflags & SPAWNFLAG_NO_PP);
if (!check_puzzle_pieces(other,removepp,inversepp))
{
if (self.no_puzzle_msg && !deathmatch)
{
temp = getstring(self.no_puzzle_msg);
if (!deathmatch)
centerprint (other, temp);
self.attack_finished = time + 2;
}
return;
}
self.enemy = other;
multi_trigger ();
};
/*QUAKED trigger_multiple (.5 .5 .5) ? notouch monstertouch pushtouch deactivated remove_pp no_pp lighttoggle lightstartlow
Variable sized repeatable trigger. Must be targeted at one or more entities.
If "health" is set, the trigger must be killed to activate each time.
If "delay" is set, the trigger waits some time after activating before firing.
"wait" : Seconds between triggerings. (.2 default)
If notouch is set, the trigger is only fired by other entities, not by touching.
If monstertouch is set, only monsters may set of the trigger
If deactivated is set, trigger will not fire until it is triggered itself
NOTOUCH has been obsoleted by trigger_relay!
soundtype
1) secret
2) beep beep
3) large switch
4)
set "message" to text string
Puzzle Pieces (use the puzzle_id value from the pieces)
puzzle_piece_1
puzzle_piece_2
puzzle_piece_3
puzzle_piece_4
no_puzzle_msg: message when player doesn't have the right pieces
*/
void() trigger_multiple =
{
if (self.soundtype == 1)
{
precache_sound ("misc/secret.wav");
self.noise = "misc/secret.wav";
}
else if (self.soundtype == 2)
{
precache_sound ("misc/comm.wav");
self.noise = "misc/comm.wav";
}
else if (self.soundtype == 3)
{
precache_sound ("misc/trigger1.wav");
self.noise = "misc/trigger1.wav";
}
if (!self.wait)
self.wait = 0.2;
self.use = multi_use;
InitTrigger ();
if (self.health)
{
if (self.spawnflags & SPAWNFLAG_NOTOUCH)
objerror ("health and notouch don't make sense\n");
self.max_health = self.health;
self.th_die = multi_killed;
self.takedamage = DAMAGE_YES;
self.solid = SOLID_BBOX;
setorigin (self, self.origin); // make sure it links into the world
}
else
{ //NOTE: was turning off touch for activate- is this necc?
if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH))// && !(self.spawnflags & SPAWNFLAG_ACTIVATED))
{
self.touch = multi_touch;
}
}
};
/*QUAKED trigger_once (.5 .5 .5) ? notouch monstertouch pushtouch deactivated remove_pp no_pp lighttoggle lightstartlow
Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching
"targetname". If "health" is set, the trigger must be killed to activate.
If notouch is set, the trigger is only fired by other entities, not by touching.
If monstertouch is set, only monsters can set of the triggers
If deactivated is set, trigger will not work until it is triggered
if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0.
soundtype
1) secret
2) beep beep
3) large switch
4)
set "message" to text string
---------------------------------------
lighttoggle = It will toggle on/off all lights in a level with a matching .style field.
.style = Valid light styles are 33-63.
.lightvalue1 (default 0)
.lightvalue2 (default 11)
Two values the light will fade-toggle between, 0 is black, 25 is brightest, 11 is equivalent to a value of 300.
The lightvalue of .style will always start with the lightvalue1 of the FIRST trigger or button spawned with that .style.
.fadespeed (default 0.5) = How many seconds it will take to complete the desired lighting change
If you turn on lighttoggle, you MUST give this trigger a style value or it will turn on and off all the "normal" lights in the level (hey, maybe that's what you want!)
If you give a .style value between 0 and 32 it will change one of the preset lightstyles.
---------------------------------------
Puzzle Pieces (use the puzzle_id value from the pieces)
puzzle_piece_1
puzzle_piece_2
puzzle_piece_3
puzzle_piece_4
no_puzzle_msg: message when player doesn't have the right pieces
*/
void() trigger_once =
{
self.wait = -1;
trigger_multiple();
};
/*QUAKED trigger_activate (.5 .5 .5) ? ONCE RELAY x deactivated
*/
void() trigger_activate =
{
float temp_flags;
temp_flags=self.spawnflags;
self.spawnflags(-)1|2; //Clear first two spawnflags before calling the main trigger funcs
if (temp_flags & 1)
trigger_once();
else if (temp_flags & 2)
self.use = SUB_UseTargets;
else trigger_multiple();
};
/*QUAKED trigger_deactivate (.5 .5 .5) ? ONCE RELAY x deactivated
*/
void() trigger_deactivate =
{
//Only diff is classname
trigger_activate();
};
//=============================================================================
void () interval_use =
{
SUB_UseTargets();
// dprint("interval used\n");
self.think = interval_use;
thinktime self : self.wait;
};
/*QUAKED trigger_interval (.5 .5 .5) (-8 -8 -8) (8 8 8)
*/
void() trigger_interval =
{
if (!self.wait)
self.wait = 5;
//Note- next line was commented out
InitTrigger ();
self.use = interval_use;
self.think = interval_use;
if (!self.targetname)
thinktime self : 0.1;
};
/*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
This fixed size trigger cannot be touched, it can only be fired by
other events. It can contain killtargets, targets, delays, and
messages.
*/
void() trigger_relay_use =
{
SUB_UseTargets();
};
void() trigger_relay =
{
self.use = trigger_relay_use;
};
//=============================================================================
/*QUAK-ED trigger_secret (.5 .5 .5) ?
secret counter trigger
soundtype
1) secret
2) beep beep
3)
4)
set "message" to text string
*/
/*
void() trigger_secret =
{
total_secrets = total_secrets + 1;
self.wait = -1;
if (!self.message)
self.message = 400; // You found a secret area!
if (!self.soundtype)
self.soundtype = 1;
if (self.soundtype == 1)
{
precache_sound ("misc/secret.wav");
self.noise = "misc/secret.wav";
}
else if (self.soundtype == 2)
{
precache_sound ("misc/comm.wav");
self.noise = "misc/comm.wav";
}
trigger_multiple ();
};
*/
//=============================================================================
void() counter_find_linked =
{
entity starte, t;
starte = self;
t=nextent(world);
if (self.netname == "") objerror("Ordered counter without a netname\n");
self.think = SUB_Null;
while (t != world)
{
self.owner = starte;
t = find(t, netname, starte.netname);
if(t!=world && t!=starte)
{
self.lockentity = t;
self = t;
}
}
self=starte;
};
void counter_return_buttons ()
{
entity t;
t = self.lockentity;
while(t)
{
if (t.classname == "button")//Check for netname match too?
{
t.think = button_return;
t.nextthink = t.ltime + 1;
}
t = t.lockentity;
}
}
void() counter_use_ordered =
{
string oldtarg;
float oldmsg;
string temp;
//replace flags with aflag
if(self.mangle)
{
if(
(self.cnt==1&&other.aflag!=self.mangle_x)||
(self.cnt==2&&other.aflag!=self.mangle_y)||
(self.cnt==3&&other.aflag!=self.mangle_z)
)
self.items = 1;//Wrong order
}
else if (other.aflag != self.cnt)
self.items = 1;//Wrong order?
self.cnt += 1;
self.count -= 1;
if (!self.items)
{
if (self.count < 0)
{
self.check_ok = TRUE;
if(self.spawnflags&ALWAYS_RETURN)
counter_return_buttons();
if (activator.classname == "player" && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0 &&
!deathmatch)
{
if(self.message)
temp=getstring(self.message);
else
temp="Sequence completed!";
centerprint(activator, temp);
}
self.enemy = activator;
multi_trigger ();
self.cnt = 1;
self.count = self.frags;
self.items = 0;
}
}
else
{
if (self.count < 0)
{
self.check_ok = FALSE;
if (activator.classname == "player" && !deathmatch)
{
if (self.msg2)
temp = getstring(self.msg2);
else
temp = "Nothing seemed to happen";
centerprint(activator, temp);
}
oldtarg = self.target;
self.target = self.puzzle_id;
oldmsg = self.message;
self.message = FALSE;
SUB_UseTargets();
self.message = oldmsg;
self.target = oldtarg;
self.cnt = 1;
self.count = self.frags;
self.items = 0;
counter_return_buttons();
}
}
};
void() counter_use =
{
// local string junk;
self.count -= 1;
if (self.count < 0)
return;
if (self.count != 0)
{
if (activator.classname == "player"
&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0 && !deathmatch)
{
if (self.count >= 4)
centerprint (activator, "There are more to go...");
else if (self.count == 3)
centerprint (activator, "Only 3 more to go...");
else if (self.count == 2)
centerprint (activator, "Only 2 more to go...");
else
centerprint (activator, "Only 1 more to go...");
}
self.check_ok=FALSE;
return;
}
if (activator.classname == "player"
&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0 && !deathmatch)
{
centerprint(activator, "Sequence completed!");
sound(activator,CHAN_ITEM,"misc/comm.wav",1,ATTN_NORM);
}
self.check_ok=TRUE;
self.enemy = activator;
multi_trigger ();
self.cnt = 1;
self.count = self.frags;
self.items = 0;
};
/*QUAKED trigger_counter (.5 .5 .5) ? nomessage ordered always_return deactivated
Acts as an intermediary for an action that takes multiple inputs.
nomessage = it will print "1 more.. " etc when triggered and "sequence complete" when finished.
ordered = things must be triggered in order to make the counter go off
always_return = Buttons will pop back to ready position even if successful (default is that they stay down once correct combination is found)
- The triggers that trigger the counter need to be ordered using the "aflag" field
- The first trigger is 1, second is 2, etc.
- If a trigger is hit out of order, the counter resets
- Triggers need a name in their netname function, the same name must be in the counter triggers netname fields (the target of the counter should NOT have a netname field, only the things triggering the counter)
- Count must still be the number of triggers until the counter fires, minus 1 (don't ask why)
"wait" = how long to wait after successful before giving it another try. Default is -1, meaning it works once and shut off. If you specify a wait time, the trigger will become a multiple trigger.
"mangle" = This entity has the ability to have a non-sequential sequence of numbers as a combination using mangle.
The format is like a vector, for example, if you want the counter (ordered) to work only if the cnt order of 3, 5, 7 is used, enter the value "3 5 7" (no quotes).
A trigger_combination_assign trigger can pass it's "mangle" value to trigger_counter when it uses it.
This way you can have a number of different possible combinations that could be used and only one wouldbe right (depending, say, on which path the player took).
The values can be as high as you like (okay, from 1 to 65336), so you can have any number of buttons in this puzzle.
After the counter has been triggered "count" times (default 2), it will fire all of it's targets and shut off, unless you specify a wait time.
*/
void() trigger_counter =
{
if(!self.wait)
self.wait = -1;
if (!self.count)
self.count = 2;
if(self.spawnflags&8)
self.inactive=TRUE;
//used for the ordered trigger
self.items = 0;
self.cnt = 1;
self.frags = self.count;
if (self.spawnflags & COUNTER_ORDERED)
{
self.use = counter_use_ordered;
thinktime self : 0.1;
self.think = counter_find_linked;
}
else
self.use = counter_use;
};
/*QUAKED trigger_combination_assign (.5 .5 .5) ? notouch monstertouch pushtouch deactivated remove_pp no_pp lighttoggle lightstartlow
This will pass it's "mangle" field to it's target- meant for use with an ordered trigger_counter.
It will pass the "mangle" but not USE the counter (it WILL use other targets normally, however).
Otherwise, it behaves just like any other trigger.
Giving it a wait of -1 will make it only work once.
*/
void trigger_combination_assign ()
{
trigger_multiple();
}
/*QUAKED trigger_counter_reset (.5 .5 .5) ? notouch monstertouch pushtouch deactivated remove_pp no_pp lighttoggle lightstartlow
This will reset a trigger_counter to start counting again as if it hasn't been used yet. Useful for when you want a counter to count more than once but the counting can be interrupted.
It will reset the counter but not USE the counter (it WILL use other targets normally, however).
Otherwise, it behaves just like any other trigger.
Giving it a wait of -1 will make it only work once.
*/
void trigger_counter_reset ()
{
trigger_multiple();
}
void() check_find_linked =
{
entity starte, t;
starte = self;
t=nextent(world);
if (self.netname == "") objerror("Check trigger without a netname\n");
self.think = SUB_Null;
while (t != world)
{
t = find(t, netname, starte.netname);
if(t!=world&&t!=starte)
{
// dprint(t.classname);
// dprint(" added to trigger_check chain\n");
self.check_chain = t;
self = t;
self.owner = starte;
}
}
self=starte;
};
void check_use ()
{
entity t;
float failed;
t=self.check_chain;
while(t)
{
if (!t.check_ok)
{
// dprint(t.classname);
// dprint(" failed!\n");
failed = TRUE;
}
t = t.check_chain;
}
if (!failed && !self.check_ok)
{
// dprint("Trigger_check: all passed\n");
self.check_ok = TRUE;
SUB_UseTargets();
}
else if (failed && self.check_ok)
{
// dprint("Failed but check okay, now i'm not check_ok\n");
self.check_ok = FALSE;
SUB_UseTargets();
}
}
/*QUAKED trigger_check (.5 .5 .5) ?
Checks to see if its child entities are active, and if they are, it triggers
netname = the name to check for its child entities. Like the trigger_counter, each
entity that this checks must share its netname.
You do not need to specify how many children the trigger has
*/
void() trigger_check =
{
self.use = check_use;
thinktime self : 0.1;
self.think = check_find_linked;
};
/*
==================================================================================
trigger_quake
==================================================================================
*/
void() quake_shake_next =
{
entity player;
if (self.spawnflags & SPAWNFLAG_DODAMAGE) T_Damage (self.enemy, self, self, self.dmg);
player = world;
player = find(world, classname, "player");
if (!player)
return;
player.punchangle=RandomVector('5 4 4');
if(player.flags&FL_ONGROUND)
{
player.velocity+=RandomVector('25 25 0');
player.velocity_z+=random(100,200);
player.flags(-)FL_ONGROUND;
}
self.think = quake_shake_next;
thinktime self : 0.1;
if (self.lifespan < time)
{
self.nextthink = -1;
self.wait = -1;
}
else
thinktime self : 0.1;
};
//Isn't this a great function name?
void() quake_shake =
{
sound(self,CHAN_AUTO,"weapons/explode.wav",1,ATTN_NONE);
sound(self,CHAN_AUTO,"fx/quake.wav",1,ATTN_NONE);
self.think = quake_shake_next;
thinktime self : 0.1;
SUB_UseTargets();
if (!self.spawnflags & SPAWNFLAG_QMULT) self.wait = -1;
};
void() quake_use =
{
if (self.nextthink >= time||self.nextthink<0)
return;
self.think = quake_shake;
self.lifespan+=time;
if(!self.spawnflags&2)
self.use=SUB_Null;
thinktime self : self.wait;
};
/*QUAKED trigger_quake (3 26 0) (-10 -10 -10) (10 10 10) dodamage multiple
Earthquake effect
Sorry some of the entity names are screwy, but it saves space
damage default = 5;
lifespan default = 2;
wait default = 1;
dodamage = inflict damage on player
"items" radius of quake
"dmg" damage done to victim
"lifespan" duration of the quake
"target" name of trigger to target (for other effects)
"targetname" set this if you want something else to trigger the trigger
"wait" delay before the quake goes off
*/
void() trigger_quake =
{
self.use = quake_use;
if (!self.wait)
self.wait = 1;
if (!self.dmg)
self.dmg = 5;
if (!self.lifespan)
self.lifespan = 2;
InitTrigger ();
self.touch = SUB_Null;
};
/*
==============================================================================
TELEPORT TRIGGERS
==============================================================================
*/
float PLAYER_ONLY = 1;
float SILENT = 2;
void() play_teleport =
{
local float v;
local string tmpstr;
v = random(5);
if (v < 1)
tmpstr = "misc/teleprt1.wav";
else if (v < 2)
tmpstr = "misc/teleprt2.wav";
else if (v < 3)
tmpstr = "misc/teleprt3.wav";
else if (v < 4)
tmpstr = "misc/teleprt4.wav";
else
tmpstr = "misc/teleprt5.wav";
sound (self, CHAN_VOICE, tmpstr, 1, ATTN_NORM);
remove (self);
};
void(vector org) spawn_tfog =
{
s = spawn ();
s.origin = org;
thinktime s : 0.05;
s.think = play_teleport;
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_TELEPORT);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
};
void() tdeath_touch =
{
if (other == self.owner)
return;
// frag anyone who teleports in on top of an invincible player
if (other.classname == "player")
{
if (self.owner.classname != "player")
{ // other monsters explode themselves
T_Damage (self.owner, self, self.owner, 50000);
return;
}
if (other.artifact_active&ART_INVINCIBILITY)
{
if(self.owner.artifact_active&ART_INVINCIBILITY)
{
self.classname = "teledeath4";
other.deathtype=self.owner.deathtype=self.classname;
remove_invincibility(other);
remove_invincibility(self.owner);
T_Damage (other, self, self.owner, 50000);
}
else
self.classname = "teledeath2";
other=self.owner;
}
if ((coop&&teamplay&&self.owner.classname=="player")||
(deathmatch&&teamplay&&other.team==self.owner.team)
)
self.classname = "teledeath3";
}
if (other.health)
{
other.deathtype=self.classname;
T_Damage (other, self, self.owner, 50000);
}
};
void(vector org, entity death_owner) spawn_tdeath =
{
entity death;
death = spawn();
death.classname = "teledeath";
death.movetype = MOVETYPE_NONE;
death.solid = SOLID_TRIGGER;
death.angles = '0 0 0';
setsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1');
setorigin (death, org);
death.touch = tdeath_touch;
thinktime death : 0.2;
death.think = SUB_Remove;
death.owner = death_owner;
force_retouch = 2; // make sure even still objects get hit
};
void teleport_effect_delay ()
{
GenerateTeleportEffect(self.enemy.origin,0);
self.attack_finished=time+0.5;
//commented this for chaos device hangin-around
if (self.classname == "teleportcoin2")
{
self.think = SUB_Remove;
self.nextthink = time + HX_FRAME_TIME;
}
}
void() teleport_touch =
{//problem- can't jail people while catapulted due to teleport_time
entity t,arrivedeffect;
vector org;
float poof_speed;
float no_throw;
if (other.classname == "tripwire")//no teleport tripmines!
{
T_Damage (other, self, self, 200 );
return;
}
if (other.classname == "chain_head")//no teleport tripmines!
{
T_Damage (other.owner, self, self, 200 );
return;
}
if(self.inactive)
return;
if (self.spawnflags & PLAYER_ONLY)
{
if (other.classname != "player")
return;
}
// Don't teleport world geometry
if (other.solid == SOLID_BSP||other.solid==SOLID_TRIGGER||other.teleport_time>time)
return;
SUB_UseTargets ();
// put a tfog where the player was UNLESS silent is checked (jweier)
if (!self.spawnflags & SILENT)
GenerateTeleportEffect(other.origin,0);
if (self.classname != "teleportcoin")
{
if(self.spawnflags&16)
t = SelectSpawnPoint ();
else
{
t = find (world, targetname, self.target);
while(t!=world&&t.classname!="info_teleport_destination")
t = find (t, targetname, self.target);
}
if (!t)
objerror ("couldn't find target");
}
else
t = self.goalentity;
// spawn a tfog flash in front of the destination
if(t.avelocity!='0 0 0')
t.mangle=t.angles;
if(self.spawnflags&16||t.spawnflags&1||self.classname=="teleportcoin")
no_throw=TRUE;
else
no_throw=FALSE;
if(!no_throw)
{
makevectors (t.mangle);
org = t.origin + 32 * v_forward;
}
else
org=t.origin;
spawn_tdeath(t.origin, other);
// move the player and lock him down for a little while
if (!other.health&&other.size!='0 0 0')
{//Exclude projectiles!
other.origin = t.origin;
if(!no_throw) //In case you don't want to push them in a certain dir
other.velocity = (v_forward * other.velocity_x) + (v_forward * other.velocity_y);
return;
}
if((t.spawnflags&2||self.spawnflags&16)&&other.classname=="player")
other.velocity='0 0 0';//Kill all player's velocity
setorigin (other, t.origin);
if (!self.spawnflags & SILENT)
{
//adding condition--else part is new--for chaos device hangin-around
if (self.classname != "teleportcoin")
{
self.enemy=other;
self.think=teleport_effect_delay;
thinktime self : 0.05;
}
else
{
arrivedeffect=spawn();
arrivedeffect.enemy=other;
arrivedeffect.think=teleport_effect_delay;
thinktime arrivedeffect : 0.05;
arrivedeffect.classname="teleportcoin2";
}
}
other.teleport_time = time + 0.7;
if(!no_throw)
{
other.angles = t.mangle;
other.fixangle = 1; // turn this way immediately
if(other.classname!="player"&&other.velocity!='0 0 0')
poof_speed = vlen(other.velocity);
else
poof_speed = 300;
other.velocity = v_forward * poof_speed;
}
other.flags(-)FL_ONGROUND;
UpdateMissileVelocity(other);
};
/*QUAKED info_teleport_destination (.5 .5 .5) (-8 -8 -8) (8 8 32) NO_THROW kill_velocity
This is the destination marker for a teleporter. It should have a "targetname" field with the same value as a teleporter's "target" field.
NO_THROW = won't throw the entity it teleports in the direction (angles) it's facing
kill_velocity = players will come out the other side with no velocity
=====FIELDS=====
"angles" - Will turn player this way and push him in this direction unless the NO_THROW spawnflag is on.
================
*/
void() info_teleport_destination =
{
// this does nothing, just serves as a target spot
if(self.avelocity!='0 0 0')
self.movetype = MOVETYPE_NOCLIP;
self.mangle = self.angles;
self.angles = '0 0 0';
self.model = "";
self.origin = self.origin + '0 0 27';
if (!self.targetname)
dprint("error- no targetname on teleport dest");
};
void() teleport_use =
{
// if(self.inactive)
// return;
thinktime self : 0.2;
force_retouch = 2; // make sure even still objects get hit
self.think = SUB_Null;
};
/*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY SILENT inactive inactive CHAOS
Any object touching this will be transported to the corresponding info_teleport_destination entity. You must set the "target" field, and create an object with a "targetname" field that matches.
SILENT = No effect or sound
CHAOS = Will act like a Chaos device- teleports you to a start spot somewhere on the map
COOL DESIGN IDEA: If you like, you can use a trigger_message_transfer to change the target of the teleporter so it can go different places at different times.
If the trigger_teleport has a targetname, it will only teleport entities when it has been fired.
*/
void() trigger_teleport =
{
vector o;
InitTrigger ();
self.touch = teleport_touch;
// find the destination
if (self.target==""&&!self.spawnflags&16)
dprint ("Error - no target on trigger_teleport");
self.use = teleport_use;
if (!(self.spawnflags & SILENT))
{
precache_sound ("ambience/newhum1.wav");
o = (self.mins + self.maxs)*0.5;
ambientsound (o, "ambience/newhum1.wav",0.5 , ATTN_STATIC);
}
if (self.spawnflags & 4)
self.inactive = TRUE;
};
/*-----------------------------------
Inter-Level Teleport - aleggett
-----------------------------------*/
/*
void teleport_newmap_touch()
{
if(other.classname != "player" || other.health <= 0 ||
other.solid != SOLID_SLIDEBOX)
return;
stuffcmd(self, self.target);
}
*/
/*QUAK-ED trigger_teleport_newmap (.5 .5 .5)
Any player touching this will be transported to the map named in .target.
.target uses the syntax:
map e1m1
or corresponding to any other levelname.
*/
/*
void trigger_teleport_newmap()
{
InitTrigger();
if(!self.target)
objerror("no target map");
self.touch = teleport_newmap_touch;
}
*/
/*
==============================================================================
trigger_setskill
==============================================================================
*/
/*
void() trigger_skill_touch =
{
string temp;
if (other.classname != "player")
return;
temp = getstring(self.message);
cvar_set ("skill", temp);
};
*/
/*QUAKED trigger_setskill (.5 .5 .5) ?
sets skill level to the value of "message".
Only used on start map.
*/
/*
void() trigger_setskill =
{
InitTrigger ();
self.touch = trigger_skill_touch;
};
*/
/*
==============================================================================
ONLY REGISTERED TRIGGERS
==============================================================================
*/
/*
void() trigger_onlyregistered_touch =
{
if (other.classname != "player")
return;
if (self.attack_finished > time)
return;
self.attack_finished = time + 2;
if (cvar("registered"))
{
self.message = "";
SUB_UseTargets ();
remove (self);
}
else
{
if (self.message != "" && !deathmatch)
{
centerprint (other, self.message);
sound (other, CHAN_BODY, "misc/comm.wav", 1, ATTN_NORM);
}
}
};
*/
/*QUAK-ED trigger_onlyregistered (.5 .5 .5) ?
Only fires if playing the registered version, otherwise prints the message
*/
/*
void() trigger_onlyregistered =
{
precache_sound ("misc/comm.wav");
InitTrigger ();
self.touch = trigger_onlyregistered_touch;
};
*/
//============================================================================
void() hurt_on =
{
self.solid = SOLID_TRIGGER;
self.nextthink = -1;
};
void() hurt_touch =
{
if (other.takedamage)
{
self.solid = SOLID_NOT;
T_Damage (other, self, self, self.dmg);
self.think = hurt_on;
thinktime self : 1;
}
return;
};
/*QUAKED trigger_hurt (.5 .5 .5) ?
Any object touching this will be hurt
set dmg to damage amount
defalt dmg = 5
*/
void() trigger_hurt =
{
InitTrigger ();
self.touch = hurt_touch;
if (!self.dmg)
self.dmg = 1000;
};
//============================================================================
//============================================================================
float PUSH_ONCE = 1;
void trigger_push_gone (void)
{
remove(self);
}
void() trigger_push_touch =
{
if(self.inactive)
return;
if(self.spawnflags&2)
if(other.flags&FL_ONGROUND)
return;
if (other.movetype&&other.solid!=SOLID_BSP)//health>0?
{
other.velocity = self.movedir * self.speed;
UpdateMissileVelocity(other);
if(other.movedir!='0 0 0')
other.movedir=self.movedir;
if ((other.classname == "player") && (other.flags & FL_ONGROUND))
{
sound (other, CHAN_AUTO, "ambience/windpush.wav", 1, ATTN_NORM);//MAKE OPTIONAL
other.flags (-) FL_ONGROUND;
}
other.safe_time=time+0.5;//so they don't take impact damage from push brushes
}
if (self.spawnflags & PUSH_ONCE)
remove(self);
};
void trigger_push_turn_on ()
{
self.use = trigger_push_gone;
self.touch = trigger_push_touch;
}
/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE no_pickup x INACTIVE
Pushes the player in the direction set by angles
When used while "on", removes it.
PUSH_ONCE - will go away after one use.
no_pickup - will not lift player off the ground- they have to jump first to be lifted
INACTIVE - Must be turned on by a trigger_activate before it can be used
-------------------------FIELDS-------------------------
Angles - the direction to push
Speed - how hard to push (default 500)
If you target it, it waits to be triggered to turn on- next use will remove it.
--------------------------------------------------------
*/
void() trigger_push =
{
if(self.angles=='0 0 0')
self.movedir='1 0 0';
InitTrigger ();
precache_sound ("ambience/windpush.wav");
if(self.targetname)
{
self.use = trigger_push_turn_on;
}
else
{
self.use = trigger_push_gone;
self.touch = trigger_push_touch;
}
if (!self.speed)
self.speed = 500;
};
//============================================================================
void() trigger_monsterjump_touch =
{
if ( other.flags & (FL_MONSTER | FL_FLY | FL_SWIM) != FL_MONSTER )
return;
// set XY even if not on ground, so the jump will clear lips
other.velocity_x = self.movedir_x * self.speed;
other.velocity_y = self.movedir_y * self.speed;
if ( !(other.flags & FL_ONGROUND) )
return;
other.flags(-)FL_ONGROUND;
other.velocity_z = self.height;
if(self.wait=-1)
self.touch=SUB_Null;
if(other.th_jump)
{
other.think=other.th_jump;
thinktime other : 0;
}
};
/*QUAKED trigger_monsterjump (.5 .5 .5) ?
Walking monsters that touch this will jump in the direction of the trigger's angle
"speed" default to 200, the speed thrown forward
"height" default to 200, the speed thrown upwards
*/
void() trigger_monsterjump =
{
if (!self.speed)
self.speed = 200;
if (!self.height)
self.height = 200;
if (self.angles == '0 0 0')
self.angles = '0 360 0';
InitTrigger ();
self.touch = trigger_monsterjump_touch;
};
/*
void() trigger_magicfield_touch =
{
if (other.classname == "grenade")
other.velocity = self.speed * self.movedir * 10;
else if (other.health > 0)
{
if (other.artifact_active & ART_TOMEOFPOWER)
return;
else other.velocity = self.speed * self.movedir * 10;
if (other.classname == "player" && !deathmatch)
{
makevectors(other.angles);
SpawnPuff(other.origin + (v_forward * random(160)), '0 0 -10', 101,other);
SpawnPuff(other.origin + (v_forward * random(160)), '5 5 0', 101,other);
SpawnPuff(other.origin + (v_forward * random(160)), '0 0 10', 101,other);
centerprint(other, "You must have the Tome of Power\n");
}
}
if (self.spawnflags & PUSH_ONCE)
remove(self);
};
*/
/*QUAK-ED trigger_magicfield (.5 .5 .5) ?
Denies player access without a certain item
*/
/*
void() trigger_magicfield =
{
InitTrigger ();
self.touch = trigger_magicfield_touch;
if (!self.speed)
self.speed = 100;
};
*/
/*
==============================================================================
trigger_crosslevel
==============================================================================
*/
void() trigger_crosslevel_use =
{
if(other.classname=="trigger_check")
if(!other.check_ok)
{
self.check_ok=FALSE;
if (self.spawnflags & 1)
serverflags(-)SFL_CROSS_TRIGGER_1;
if (self.spawnflags & 2)
serverflags(-)SFL_CROSS_TRIGGER_2;
if (self.spawnflags & 4)
serverflags(-)SFL_CROSS_TRIGGER_3;
if (self.spawnflags & 8)
serverflags(-)SFL_CROSS_TRIGGER_4;
if (self.spawnflags & 16)
serverflags(-)SFL_CROSS_TRIGGER_5;
if (self.spawnflags & 32)
serverflags(-)SFL_CROSS_TRIGGER_6;
if (self.spawnflags & 64)
serverflags(-)SFL_CROSS_TRIGGER_7;
if (self.spawnflags & 128)
serverflags(-)SFL_CROSS_TRIGGER_8;
return;
}
if (self.spawnflags & 1)
serverflags(+)SFL_CROSS_TRIGGER_1;
if (self.spawnflags & 2)
serverflags(+)SFL_CROSS_TRIGGER_2;
if (self.spawnflags & 4)
serverflags(+)SFL_CROSS_TRIGGER_3;
if (self.spawnflags & 8)
serverflags(+)SFL_CROSS_TRIGGER_4;
if (self.spawnflags & 16)
serverflags(+)SFL_CROSS_TRIGGER_5;
if (self.spawnflags & 32)
serverflags(+)SFL_CROSS_TRIGGER_6;
if (self.spawnflags & 64)
serverflags(+)SFL_CROSS_TRIGGER_7;
if (self.spawnflags & 128)
serverflags(+)SFL_CROSS_TRIGGER_8;
self.check_ok=TRUE;
SUB_UseTargets();
self.solid = SOLID_NOT;
};
void() trigger_crosslevel_touch =
{
if (other.classname != "player")
return;
activator = other;
trigger_crosslevel_use();
};
/*QUAKED trigger_crosslevel (.5 .5 .5) ? trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
Once this trigger is touched/used, any trigger_crosslevel_target with the same trigger number is automatically used when a level is started within the same unit. It is OK to check multiple triggers. Message, delay, target, and killtarget also work.
*/
void() trigger_crosslevel =
{
if (((self.spawnflags & 1) && (serverflags & SFL_CROSS_TRIGGER_1)) ||
((self.spawnflags & 2) && (serverflags & SFL_CROSS_TRIGGER_2)) ||
((self.spawnflags & 4) && (serverflags & SFL_CROSS_TRIGGER_3)) ||
((self.spawnflags & 8) && (serverflags & SFL_CROSS_TRIGGER_4)) ||
((self.spawnflags & 16) && (serverflags & SFL_CROSS_TRIGGER_5)) ||
((self.spawnflags & 32) && (serverflags & SFL_CROSS_TRIGGER_6)) ||
((self.spawnflags & 64) && (serverflags & SFL_CROSS_TRIGGER_7)) ||
((self.spawnflags & 128) && (serverflags & SFL_CROSS_TRIGGER_8)))
{
self.solid = SOLID_NOT;
self.flags(+)FL_ARCHIVE_OVERRIDE;
return;
}
InitTrigger ();
self.touch = trigger_crosslevel_touch;
self.use = trigger_crosslevel_use;
};
/*QUAKED trigger_crosslevel_target (.5 .5 .5) ? trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
Triggered by a trigger_crosslevel elsewhere within a unit. It is OK to check multiple triggers. Delay, target and killtarget also work.
*/
void() trigger_crosslevel_target_think =
{
entity found;
found=find(world,classname,"player");
if(!found)
{
// bprint("Postponing check\n");
thinktime self : 3;
}
else if (((self.spawnflags & 1) && (serverflags & SFL_CROSS_TRIGGER_1)) ||
((self.spawnflags & 2) && (serverflags & SFL_CROSS_TRIGGER_2)) ||
((self.spawnflags & 4) && (serverflags & SFL_CROSS_TRIGGER_3)) ||
((self.spawnflags & 8) && (serverflags & SFL_CROSS_TRIGGER_4)) ||
((self.spawnflags & 16) && (serverflags & SFL_CROSS_TRIGGER_5)) ||
((self.spawnflags & 32) && (serverflags & SFL_CROSS_TRIGGER_6)) ||
((self.spawnflags & 64) && (serverflags & SFL_CROSS_TRIGGER_7)) ||
((self.spawnflags & 128) && (serverflags & SFL_CROSS_TRIGGER_8)))
{
activator = world;
self.check_ok=TRUE;
SUB_UseTargets();
}
else
self.check_ok=FALSE;
};
void() trigger_crosslevel_target =
{
self.think = trigger_crosslevel_target_think;
//FIXME: temporarily lenghtened this so I could use the addserverflags impulse
// thinktime self : 0.5;
thinktime self : 3;
self.solid = SOLID_NOT;
self.flags(+)FL_ARCHIVE_OVERRIDE;
};
/*QUAK-ED trigger_deathtouch (.5 .5 .5)
Kills anything that has a matching targetname and touches it.
th_die = Set this if you want the object to have a specific death, defaults to SUB_Remove.
If it is SUB_Remove, it will execute the th_die of the object, if it has one.
If the object doesn't have a th_die, but it has health, it will execute chunk_death.
If it doesn't have health, it will just be removed.
FIXME: Solid_bsp's don't seem to touch this
*/
/*
void trigger_deathtouch_touch (void)
{
if(other.targetname!=self.target)
return;
other.targetname="";//so i don't keep on killing it
if(self.th_die)
other.think=self.th_die;
else if(other.th_die)
other.think=other.th_die;
else if(other.health)
other.think=chunk_death;
else
other.think=SUB_Remove;
thinktime other : 0.05;
}
void trigger_deathtouch (void)
{
InitTrigger ();
self.touch = trigger_deathtouch_touch;
}
*/
void GetPuzzle(entity item, entity person)
{
if (!person.puzzle_inv1)
person.puzzle_inv1 = item.puzzle_id;
else if (!person.puzzle_inv2)
person.puzzle_inv2 = item.puzzle_id;
else if (!person.puzzle_inv3)
person.puzzle_inv3 = item.puzzle_id;
else if (!person.puzzle_inv4)
person.puzzle_inv4 = item.puzzle_id;
else if (!person.puzzle_inv5)
person.puzzle_inv5 = item.puzzle_id;
else if (!person.puzzle_inv6)
person.puzzle_inv6 = item.puzzle_id;
else if (!person.puzzle_inv7)
person.puzzle_inv7 = item.puzzle_id;
else if (person.puzzle_inv8)
person.puzzle_inv8 = item.puzzle_id;
else
dprint("No room for puzzle piece!\n");
}
void GetPuzzle2(entity item, entity person, string which)
{
item.puzzle_id = which;
}
void puzzle_touch(void)
{
local entity stemp;
local float amount;
if (other.classname != "player")
return;
if (other.health <= 0) // Dead players can't pick stuff up
return;
if (other.puzzle_inv1 == self.puzzle_id ||
other.puzzle_inv2 == self.puzzle_id ||
other.puzzle_inv3 == self.puzzle_id ||
other.puzzle_inv4 == self.puzzle_id ||
other.puzzle_inv5 == self.puzzle_id ||
other.puzzle_inv6 == self.puzzle_id ||
other.puzzle_inv7 == self.puzzle_id ||
other.puzzle_inv8 == self.puzzle_id)
return;
amount = random();
if (amount < 0.5)
{
sprinti (other, PRINT_MEDIUM, STR_YOUPOSSESS);
sprint (other, PRINT_MEDIUM, self.netname);
}
else
{
sprinti (other, PRINT_MEDIUM, STR_YOUHAVEACQUIRED);
sprint (other, PRINT_MEDIUM, self.netname);
}
sprint (other,PRINT_MEDIUM, "\n");
GetPuzzle(self, other);
sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);
stuffcmd (other, "bf\n");
if (coop)
return;
self.solid = SOLID_NOT;
self.model = string_null;
/* if (coop)
{
self.mdl = self.model;
thinktime self : 60;
self.think = SUB_regen;
}*/
activator = other;
SUB_UseTargets(); // fire all targets / killtargets
}
void puzzle_use(void)
{
entity found;
float num_found;
self.effects(-)EF_NODRAW;
self.solid = SOLID_TRIGGER;
self.use = SUB_Null;
self.touch = puzzle_touch;
setorigin(self,self.origin);
num_found = 0;
if (self.spawnflags & 4)
{
found = find(world, classname, "player");
while (found)
{
if (vlen(found.origin-self.origin) < 200)
{
num_found += 1;
other = found;
self.touch();
}
found = find(found, classname, "player");
}
}
if (num_found == 1 && !coop)
{
remove(self);
}
else
{
StartItem();
}
}
/*QUAKED puzzle_piece (1 .6 0) (-8 -8 -28) (8 8 8) SPAWN FLOATING AUTO_GET
Puzzle Piece
-------------------------FIELDS-------------------------
puzzle_id: the number that identifies the piece
(this should 5 characters or less)
netname: the name the player sees when picked up
--------------------------------------------------------
*/
void puzzle_piece(void)
{
//RICK: Added floating spawnflag as per Brian R.'s request
precache_sound("items/artpkup.wav");
precache_puzzle_model(self.puzzle_id);
setpuzzlemodel(self,self.puzzle_id);
self.noise = "items/artpkup.wav";
if (self.spawnflags & 1)
{
setsize (self, '-8 -8 -8', '8 8 16');
self.spawnflags(-)1;
self.solid = SOLID_NOT;
self.effects(+)EF_NODRAW;
self.use = puzzle_use;
}
else
{
setsize(self,'0 0 0','0 0 0');
self.hull=HULL_POINT;
self.solid = SOLID_BBOX;
self.touch = puzzle_touch;
self.think=StartItem;
thinktime self : 0;
}
if(self.spawnflags&2)
self.spawnflags=1;
if ((self.puzzle_id == "glass") || (self.puzzle_id == "lens"))
self.drawflags (+) DRF_TRANSLUCENT;
}
void puzzle_static_use(void)
{
setpuzzlemodel(self,self.puzzle_id);
/*if (!droptofloor())
{
dprint ("Static Puzzle Piece fell out of level at ");
dprint (vtos(self.origin));
dprint ("\n");
remove(self);
return;
}*/
SUB_UseTargets();
if (self.lifespan)
{
thinktime self : self.lifespan;
self.think = SUB_Remove;
}
}
/*QUAKED puzzle_static_piece (1 .6 0) (-8 -8 -8) (8 8 8)
Puzzle Static Piece
-------------------------FIELDS-------------------------
puzzle_id: the name of the model to be created
lifespan: how long the puzzle piece should be around
--------------------------------------------------------
*/
void puzzle_static_piece(void)
{
precache_puzzle_model(self.puzzle_id);
setmodel(self, self.model);
self.solid = SOLID_NOT;
self.movetype = MOVETYPE_NONE;
setsize (self, '0 0 0', '0 0 0');
self.use = puzzle_static_use;
}
void reset_mangle (void)
{
SUB_CalcAngleMove(self.mangle,10,SUB_Null);
}
void() control_return =
{
if(self.goalentity.classname!="catapult")
{
self.goalentity.oldthink=SUB_Null;
self.goalentity.think=reset_mangle;
thinktime self.goalentity : 0;
}
if(self.check_ok)
{
// other.weaponmodel.drawflags(-)DRF_TRANSLUCENT;
// other.weaponmodel.abslight=0;
self.enemy.oldweapon=0;
self.enemy.th_weapon=W_SetCurrentAmmo;
self.check_ok = FALSE;
self.enemy=world;
}
};
void() catapult_ready;
void() control_touch =
{
vector org, dir;
float fire_range;
if (other.classname != "player")
return;
if (self.enemy != world && other != self.enemy) return;
if(self.goalentity.health<=0&&self.health)
{
self.think=SUB_Remove;
thinktime self : 0;
return;
}
other.attack_finished=time+0.1;
if(other.weaponmodel!="models/xhair.mdl");
{
other.weaponmodel="models/xhair.mdl";
other.weaponframe = 0;
other.th_weapon=SUB_Null;
self.check_ok = TRUE;
}
if(self.enemy!=other)
centerprint(other,"You're in control!\n");
self.enemy = other;
self.goalentity.enemy = self;
makevectors(self.enemy.v_angle);
if(self.goalentity.classname=="catapult")
{
if(self.enemy.angles_y<self.goalentity.angles_y+5&&self.enemy.angles_y>self.goalentity.angles_y - 5)
self.goalentity.angles_y=self.enemy.angles_y;
if(self.goalentity.think==catapult_ready)
if(self.enemy.button0)
{
self.goalentity.think=self.goalentity.th_weapon;
thinktime self.goalentity : 0;
}
}
else
{
org=self.enemy.origin+self.enemy.proj_ofs;
dir=normalize(v_forward);
traceline(org,org+dir*10000,FALSE,self.enemy);
org=self.goalentity.origin+self.goalentity.proj_ofs;
fire_range=vlen(org-trace_endpos);
if(fire_range>128)
{
dir=normalize(trace_endpos-org);
if(trace_ent.health&&trace_ent.origin!='0 0 0')//Many breakable brishes have no origin
self.goalentity.goalentity=trace_ent;
else
self.goalentity.goalentity=world;
self.goalentity.view_ofs=trace_endpos;
dir=vectoangles(dir);
self.goalentity.angles=dir;
self.goalentity.angles_z=dir_z/10;
if(self.goalentity.think!=self.goalentity.th_weapon)
if(self.enemy.button0&&self.goalentity.th_weapon!=SUB_Null)
{
// self.goalentity.oldthink = control_return;
self.goalentity.think=self.goalentity.th_weapon;
thinktime self.goalentity : 0;
}
// else
// {
// self.goalentity.think = control_return;
// thinktime self.goalentity : 0.1;
// }
}
}
self.think = control_return;
thinktime self : 0.1;
};
/*QUAKED trigger_control (.5 .5 .5) ?
Takes over a ballista when the player is inside of it
*/
void trigger_control_find_target (void)
{
if (!self.target)
objerror("Nothing to control!\n");
self.goalentity = find(world, targetname, self.target);
if(self.goalentity.takedamage)
self.health=TRUE;
if (!self.goalentity)
objerror("Could not find target\n");
else if(self.goalentity.classname=="catapult"||self.goalentity.classname=="obj_catapult2")
{
self.goalentity.movechain=self;
self.flags(+)FL_MOVECHAIN_ANGLE;
self.movetype=MOVETYPE_NOCLIP;
}
else
self.goalentity.mangle = self.goalentity.angles;
}
void() trigger_control =
{
self.enemy = world;
self.touch = control_touch;
self.ltime = time;
InitTrigger();
self.think=trigger_control_find_target;
thinktime self : 1;
};
/*QUAK-ED trigger_no_friction (.5 .5 .5)
Takes FL_ONGROUND flag off anything
*/
/*
void trigger_no_fric_touch (void)
{
other.flags(-)FL_ONGROUND;
}
void trigger_no_friction (void)
{
InitTrigger();
self.touch = trigger_no_fric_touch;
}
*/
/*QUAKED trigger_attack (.5 .5 .5) ?
Checks to see if a player touching it has tried to fire.
*/
void trigger_attack_touch (void)
{
if(other.classname!="player")
return;
if(other.last_attack+0.3>=time)
{
SUB_UseTargets();
remove(self);
}
}
void trigger_attack (void)
{
InitTrigger();
self.touch=trigger_attack_touch;
}
/*QUAKED trigger_message_transfer (.5 .5 .5) ?
Special case- will player it's message and transfer it's activating trigger's next target to it's target.
Does NOT activate it's target, only transfers the name to the activating trigger
These triggers also cannot be deactivated by touch
===================
FEILDS
.message = A message to display when used.
*/
void trigger_message_transfer_use ()
{
string temp;
temp = getstring(self.message);
if (!deathmatch)
centerprint(activator, temp);
other.nexttarget=self.target;
}
void trigger_message_transfer ()
{
InitTrigger();
self.use=trigger_message_transfer_use;
}
/*
* $Log: /HexenWorld/HCode/TRIGGERS.hc $
*
* 10 5/31/98 2:58p Mgummelt
*
* 9 4/13/98 1:13p Mgummelt
* fixing push fields so they don't caus eplayer impact damage
*
* 8 3/29/98 10:39p Rmidthun
* Added support for trigger_teleports to fire off targets other than
* destinations and teleport only to destination spots
*
* 7 3/29/98 6:47p Rmidthun
*
* 6 3/25/98 4:31p Ssengele
* teleporting tripmines doesn't work no more.
*
* 5 3/21/98 8:05p Ssengele
*
* 4 3/13/98 2:11p Ssengele
* finished teleporters for chaos device?; made reflection polys and wind
* tunnels update missiles correctly; wind tunnels work as in mission pack
*
* 3 3/12/98 7:40p Ssengele
* change to chaos device--visual effect will change, but the general idea
* is there. oh, and how long it lasts will change, too.
*
* 2 3/10/98 9:23p Ssengele
* missiles update correctly (if client already knows about them in the
* case of effects) thru teleporters
*
* 1 2/04/98 1:59p Rjohnson
*
* 187 9/25/97 5:24p Mgummelt
*
* 186 9/24/97 11:52a Rjohnson
* Made puzzle pieces auto-getabble
*
* 185 9/23/97 4:06p Mgummelt
*
* 184 9/23/97 11:44a Mgummelt
*
* 183 9/22/97 5:36p Rjohnson
* Auto picks up puzzple pieces
*
* 182 9/09/97 2:31p Rjohnson
* Fix
*
* 181 9/02/97 9:54p Mgummelt
*
* 180 9/02/97 9:53p Mgummelt
*
* 179 9/02/97 2:01a Rlove
*
* 178 9/01/97 10:14p Mgummelt
*
* 177 9/01/97 9:54p Mgummelt
*
* 176 9/01/97 4:45p Mgummelt
*
* 175 9/01/97 12:11a Jweier
*
* 174 8/31/97 12:54p Jweier
*
* 173 8/28/97 5:41p Mgummelt
*
* 172 8/28/97 1:54p Rjohnson
* Puzzle piece update
*
* 171 8/28/97 2:25a Mgummelt
*
* 170 8/28/97 12:44a Mgummelt
*
* 169 8/28/97 12:06a Mgummelt
*
* 168 8/27/97 11:44p Mgummelt
*
* 167 8/26/97 2:10p Mgummelt
*
* 166 8/26/97 10:08a Mgummelt
*
* 165 8/25/97 1:39p Mgummelt
*
* 164 8/25/97 10:55a Rjohnson
* removed deathmatch messages
*
* 163 8/25/97 8:11a Rlove
*
* 162 8/24/97 8:32p Mgummelt
*
* 161 8/24/97 4:12p Mgummelt
*
* 160 8/23/97 7:15p Rlove
*
* 159 8/22/97 10:36p Rjohnson
* Bugs
*
* 158 8/21/97 3:52p Mgummelt
*
* 157 8/21/97 1:53p Mgummelt
*
* 156 8/21/97 1:17p Mgummelt
*
* 155 8/21/97 1:08p Rjohnson
* Puzzle Change
*
* 154 8/20/97 11:09p Rjohnson
* Puzzle pieces don't go away for coop
*
* 153 8/20/97 7:54p Rjohnson
* Removed message for deathmatch
*
* 152 8/20/97 2:34p Rjohnson
* Respawning of puzzle pieces for co-op
*
* 151 8/18/97 5:21p Bgokey
*
* 150 8/17/97 3:06p Mgummelt
*
* 149 8/15/97 4:59p Mgummelt
*
* 148 8/13/97 1:28a Mgummelt
*
* 147 8/06/97 10:14p Mgummelt
*
* 146 8/05/97 8:33p Mgummelt
*
* 145 8/05/97 6:48p Mgummelt
*
* 144 8/05/97 5:36a Rlove
* removed translucency from Eric's models
*
* 143 8/04/97 12:16p Rlove
* made some puzzle items translucent
*
* 142 7/29/97 7:13a Rlove
*
* 141 7/25/97 6:34p Rlove
*
* 140 7/24/97 8:47p Mgummelt
*
* 139 7/23/97 7:04p Mgummelt
*
* 138 7/23/97 6:46p Mgummelt
*
* 137 7/23/97 6:42p Mgummelt
*
* 136 7/22/97 11:34a Mgummelt
*
* 135 7/21/97 6:05p Rlove
*
* 134 7/21/97 6:00p Rlove
*
* 133 7/21/97 4:04p Mgummelt
*
* 132 7/21/97 4:02p Mgummelt
*
* 131 7/20/97 2:25p Bgokey
*
* 130 7/17/97 4:54p Rlove
*
* 129 7/17/97 2:16p Mgummelt
*
* 128 7/17/97 12:25p Mgummelt
*
* 127 7/17/97 11:15a Rlove
*
* 126 7/15/97 9:20a Rlove
* Changed puzzle piece solid value so they could move with plats.
*
* 125 7/14/97 4:46p Mgummelt
*
* 124 7/11/97 12:34p Rjohnson
* Added a puzzle precache, changed puzzle precaching, added a parameter
* to the call back for client reentering a level
*
* 121 7/09/97 12:03p Rjohnson
* Added msg2 to the global text file
*
* 120 7/09/97 11:55a Mgummelt
*
* 119 7/08/97 3:23p Rjohnson
* Switched messages to using a string index
*
* 118 7/07/97 2:45p Mgummelt
*
* 117 7/07/97 2:43p Mgummelt
*
* 116 7/03/97 4:31p Rlove
*
* 115 7/03/97 3:30p Mgummelt
*
* 114 7/03/97 3:24p Mgummelt
*
* 113 7/03/97 1:59p Rlove
*
* 112 7/03/97 12:48p Mgummelt
*
* 111 7/03/97 9:47a Rlove
* Deactivate trigger now works on multiple_trigger
*
* 110 7/02/97 11:34a Mgummelt
*
* 109 6/30/97 7:10p Rlove
* More puzzle models
*
* 108 6/30/97 3:22p Mgummelt
*
* 107 6/28/97 6:33p Mgummelt
*
* 106 6/26/97 1:45p Mgummelt
*
* 105 6/26/97 11:16a Rjohnson
* Global text for puzzle messages
*
* 104 6/25/97 3:00p Mgummelt
*
* 103 6/24/97 5:44p Rlove
* Rings of Flight and Regeneration are working
*
* 102 6/18/97 7:42p Mgummelt
*
* 101 6/18/97 4:19p Mgummelt
*
* 100 6/18/97 4:00p Mgummelt
*
* 99 6/18/97 10:46a Rjohnson
* Code cleanu
*
* 98 6/17/97 3:35p Mgummelt
*
* 97 6/17/97 10:20a Rlove
*
* 96 6/16/97 6:49p Rlove
* Changed timing of teleport sound
*
* 95 6/16/97 4:03p Jweier
*
* 94 6/16/97 8:35a Jweier
*
* 93 6/15/97 5:10p Mgummelt
*
* 92 6/14/97 3:00p Mgummelt
*
* 91 6/14/97 2:22p Mgummelt
*
* 90 6/14/97 9:36a Mgummelt
*
* 89 6/13/97 10:11a Rlove
* Moved all message.hc to strings.hc
*
* 88 6/11/97 2:44p Jweier
*
* 87 6/10/97 2:53p Mgummelt
*
* 86 6/10/97 2:42p Mgummelt
*
* 85 6/06/97 4:08p Mgummelt
*
* 84 6/06/97 3:54p Mgummelt
*
* 83 6/06/97 10:58a Rjohnson
* Fix for lights
*
* 82 6/05/97 8:16p Mgummelt
*
* 81 6/04/97 1:04p Jweier
*
* 80 6/03/97 7:59a Rlove
* Change take_art.wav to artpkup.wav
*
* 79 6/02/97 11:44a Rlove
* Teleport Artifact works
*
* 78 5/31/97 9:29p Mgummelt
*
* 77 5/30/97 5:01p Rjohnson
* Made it so you can pick up spawned puzzle pieces - nice feature
*
* 76 5/30/97 3:04p Rjohnson
* Spawnable puzzle pieces
*
* 75 5/29/97 9:23p Mgummelt
*
* 74 5/28/97 2:19p Rlove
* Doors that require puzzles should now open if you use F5.
*
* 73 5/27/97 6:41p Jweier
*
* 72 5/24/97 2:48p Rlove
* Taking out old Id sounds
*
* 71 5/23/97 5:03p Jweier
*
* 70 5/19/97 11:36p Mgummelt
*
* 69 5/19/97 12:43p Mgummelt
*
* 68 5/19/97 12:07p Mgummelt
*
* 67 5/16/97 11:27p Mgummelt
*
* 66 5/13/97 3:52p Mgummelt
*
* 65 5/12/97 11:12p Mgummelt
*
* 64 5/12/97 12:48p Rjohnson
* Fixed a problem with spawning puzzle items and setsize
*
* 63 5/11/97 7:30a Mgummelt
*
* 62 5/10/97 1:54p Mgummelt
*
* 61 5/10/97 12:30p Jweier
*
* 60 5/09/97 7:36p Jweier
*
* 59 5/09/97 7:17p Mgummelt
*
* 58 5/09/97 6:33p Jweier
*
* 57 5/09/97 6:30p Jweier
*
* 56 5/09/97 5:54p Jweier
*
* 55 5/09/97 4:57p Mgummelt
*
* 54 5/08/97 7:06p Jweier
* added trigger_check
*
* 53 5/07/97 7:19p Jweier
*
* 52 5/07/97 6:19p Mgummelt
*
* 51 5/07/97 5:36p Jweier
*
* 50 5/07/97 5:35p Jweier
*
* 49 5/02/97 6:17p Jweier
*
* 48 5/01/97 7:28p Jweier
*
* 46 4/30/97 12:08p Rjohnson
* Added a special pre-cache for puzzle pieces
*
* 45 4/30/97 10:48a Rjohnson
* Added static puzzle pieces
*
* 44 4/29/97 7:13p Jweier
*
* 43 4/29/97 7:09p Mgummelt
*
* 42 4/29/97 6:58p Jweier
*
* 41 4/29/97 1:11p Mgummelt
*
* 40 4/29/97 1:09p Mgummelt
*
* 39 4/26/97 3:52p Mgummelt
*
* 38 4/18/97 4:46p Rjohnson
* Added messages for when you don't have the puzzle pieces
*
* 37 4/18/97 3:38p Rjohnson
* Designers can now name the puzzle pieces
*
* 36 4/18/97 3:22p Rjohnson
* Made the puzzle pieces use strings instead of numbers
*
* 35 4/16/97 4:45p Rjohnson
* More modifications to puzzle pieces
*
* 34 4/16/97 3:17p Rjohnson
* Initial version of the puzzle piece system
*
* 33 4/15/97 7:07p Jweier
*
* 32 4/15/96 6:15p Mgummelt
*
* 31 4/12/97 3:21p Jweier
* Activating triggers
*
* 30 4/09/97 3:43p Rjohnson
* Added code for multi-level triggers
*
* 29 4/09/97 2:36p Mgummelt
*
* 28 4/05/97 8:41p Mgummelt
*
* 27 4/05/97 11:41a Jweier
*
* 26 4/03/97 4:44p Jweier
* ordered triggers now wait until all triggers are pushed to tell the
* player it is wrong
*
* 25 4/02/97 6:11p Jweier
* added ordered trigger_count
*
* 24 3/31/97 6:34a Rlove
* Added PRECACHE.HC and DAMAGE.HC. Changed SpawnBlood to SpawnPuff.
*
* 23 3/24/97 12:58p Jweier
*
* 22 3/24/97 8:08a Jweier
*
* 21 3/19/97 7:16p Jweier
* added trigger_magicfield (moved out of misc.hc)
*
* 20 3/18/97 6:20p Jweier
* Added PUSHTOUCH field (pushable item trigger)
*
* 19 3/18/97 5:06p Rlove
* Another problem with monster touch triggers
*
* 18 3/18/97 11:56a Rlove
* Small fix on triggers
*
* 17 3/14/97 6:22p Aleggett
*
* 16 3/14/97 6:01p Aleggett
* Finished wind brush, with a gust effect if you like!
*
* 15 3/13/97 9:57a Rlove
* Changed constant DAMAGE_AIM to DAMAGE_YES and the old DAMAGE_YES to
* DAMAGE_YES
*
* 14 3/12/97 4:28p Jweier
*
* 13 3/11/97 5:24p Aleggett
* Started Wind Brush
*
* 12 3/07/97 5:50p Aleggett
* Added rudimentary inter-level teleport
*
* 11 3/07/97 4:56p Jweier
* Fixed up all the trigger_quake flukes
*
* 10 3/06/97 5:26p Jweier
* Changed how trigger_quake works (has radius)
*
* 9 3/06/97 4:12p Jweier
* Fixed trigger touching problems
*
* 8 2/28/97 4:49p Jweier
* finished trigger_quake
*
* 7 2/28/97 2:34p Jweier
* Added trigger_quake
*
* 6 2/27/97 5:21p Jweier
* Added monstertouch spawnflag to triggers. Only monsters are able to
* trigger these!
*
* 5 2/21/97 7:06p Rlove
* Added SILENT feature to teleports (jweier)
*
* 4 1/28/97 3:23p Rjohnson
* Added experience point awarding for triggers
*
* 3 11/18/96 3:29p Rlove
* Changed sounds variable to soundtype
*
* 2 11/11/96 1:23p Rlove
* Added Source Safe stuff
*/