/* * $Header: /HexenWorld/Siege/TRIGGERS.hc 25 6/01/98 5:45p 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.impulse) if(other.impulse!=self.impulse) return; if(!self.siege_team&&self.team)//temp fix self.siege_team=self.team; if(self.siege_team) if(other.siege_team!=self.siege_team) return; if(self.fail_chance) if(random()*100= 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.siege_team==self.owner.siege_team) ) self.classname = "teledeath3"; } if (other.health&&(!self.frags||other.flags2&FL_ALIVE)) { other.deathtype=self.classname; T_Damage (other, self, self.owner, 50000); } }; void(vector org, entity death_owner, float alive_only_tf) 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; if(alive_only_tf) death.frags = TRUE; 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; } } float DEFENDER = 32; float ATTACKER = 64; float AUTOTEAM = 128; void() teleport_touch = { entity t,arrivedeffect; vector org; float poof_speed; float no_throw; // dprint("Teleporter touched "); // dprint(other.classname); // dprint("\n"); /* if(dmMode==DM_SIEGE) if(self.siege_team) { countPlayers(); if(num_playerstime) return; if(self.spawnflags&DEFENDER) become_defender(other); else if(self.spawnflags&ATTACKER) become_attacker(other); else if(self.spawnflags&AUTOTEAM) become_either(other); 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,FALSE); // 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"; thinktime self : 0; self.think=SUB_Remove; } } 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_shut_off () { self.touch=SUB_Null; self.think = SUB_Null; thinktime self : 0; } void() teleport_use = { // if(self.inactive) // return; // dprint("Teleporter used\n"); self.touch = teleport_touch; thinktime self : 0.2; force_retouch = 2; // make sure even still objects get hit self.think = teleport_shut_off; }; /*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY SILENT inactive inactive CHAOS Defender Attacker AutoTeam 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 (); if(!self.targetname) self.touch = teleport_touch; else self.use = teleport_use; // self.touch = teleport_touch; // self.use = teleport_use; // find the destination if (self.target==""&&!self.spawnflags&16) dprint ("Error - no target on trigger_teleport"); 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(self.t_width>time&&other==self.enemy) return;//last owner can't pick up again for 5 seconds 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; thinktime self : 0; self.think = SUB_Remove; 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); } other.pain_target=self.netname; sprint (other,PRINT_MEDIUM, "\n"); other.flags2(+)FL2_HASKEY; WriteTeam (SVC_HASKEY,other); 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 SIEGEKEY Puzzle Piece IMPORTANT! All puzzle_piece models need to be in the subdirectory "models/puzzle/"!!! SIEGEKEY - This puzzle_piece is intended as the key to the throneroom on a siege map -------------------------FIELDS------------------------- puzzle_id: the name that identifies the piece (this should 5 characters or less- no extensions- for example, "cskey.mdl" is just "cskey") mdl: do not set unless you are using it for a Siege Key- set this as the path from your Hexen directory to the model you will be using- for example "models/puzzle/cskey.mdl" netname: the name the player sees when picked up -------------------------------------------------------- */ void puzzle_piece(void) { //RICK: Added floating spawnflag as per Brian R.'s request if(!self.puzzle_id) { dprint("Warning! No 'puzzle_id' field on puzzle_piece!!!\n"); return; } if(!self.flags2&FL2_REPLACEMENT) { precache_sound("items/artpkup.wav"); precache_puzzle_model(self.puzzle_id); } setpuzzlemodel(self,self.puzzle_id); if(self.spawnflags&8||self.puzzle_id=="cskey")//temp fix { g_keyname=self.puzzle_id; g_keymdl=self.model; } self.noise = "items/artpkup.wav"; if (self.spawnflags & 1) { setsize (self, '-4 -4 -16', '4 4 0'); // 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; if(!self.flags2&FL2_REPLACEMENT) spawn_key_tracker(); } void DropPuzzlePiece(void) { entity newpuzz; newpuzz=spawn(); setpuzzlemodel(newpuzz,self.puzzle_id); newpuzz.noise = "items/artpkup.wav"; setsize(newpuzz,'0 0 0','0 0 0'); newpuzz.hull=HULL_POINT; newpuzz.solid = SOLID_BBOX; newpuzz.touch = puzzle_touch; newpuzz.t_width=time+5; newpuzz.enemy=self; newpuzz.think=StartItem; thinktime newpuzz : 0; if ((self.puzzle_id == "glass") || (self.puzzle_id == "lens")) newpuzz.drawflags (+) DRF_TRANSLUCENT; setorigin(newpuzz,self.origin+'0 0 18'); newpuzz.wallspot=newpuzz.origin; newpuzz.classname="puzzle_piece"; newpuzz.netname = self.pain_target; newpuzz.puzzle_id=self.puzzle_id; self.puzzle_id=""; self.flags2(-)FL2_HASKEY; WriteTeam(SVC_NONEHASKEY,self); } 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(other.beast_time>time) 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_yself.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.5; }; /*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; } float PUSH_SCALAR = 100;//push triggers' multiplier on dist from. void push_trigger_touch () { vector push_vec,save_vel; entity oself; float o_spd; if(!self.owner) { self.touch=SUB_Null; self.think=SUB_Remove; thinktime self: 0; return; } else if(self.owner.health<=0) { self.touch=SUB_Null; self.think=SUB_Remove; thinktime self: 0; return; } if(other.classname!="player") return; if(infront_of_ent(self.owner,other)) { makevectors(other.angles); push_vec=v_forward; } else push_vec = normalize(self.owner.origin-other.origin); save_vel = other.velocity; o_spd=vhlen(save_vel); if(o_spd