]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'TimePath/experiments/csqc_prediction' into Mario/qc_physics
authorMario <zacjardine@y7mail.com>
Tue, 17 Feb 2015 19:45:21 +0000 (06:45 +1100)
committerMario <zacjardine@y7mail.com>
Tue, 17 Feb 2015 19:45:21 +0000 (06:45 +1100)
Conflicts:
qcsrc/client/bgmscript.qh
qcsrc/client/command/cl_cmd.qc
qcsrc/client/damage.qc
qcsrc/client/main.qc
qcsrc/client/progs.src
qcsrc/client/waypointsprites.qc
qcsrc/common/constants.qh
qcsrc/common/physics.qh
qcsrc/common/triggers/func/breakable.qc
qcsrc/common/triggers/target/music.qc
qcsrc/common/triggers/target/spawn.qc
qcsrc/common/triggers/trigger/jumppads.qc
qcsrc/common/triggers/trigger/secret.qc
qcsrc/common/triggers/trigger/secret.qh
qcsrc/common/triggers/trigger/swamp.qc
qcsrc/server/constants.qh
qcsrc/server/g_subs.qc
qcsrc/server/g_triggers.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/progs.src
qcsrc/server/t_halflife.qc
qcsrc/server/t_plats.qc

129 files changed:
qcsrc/client/bgmscript.qc
qcsrc/client/bgmscript.qh
qcsrc/client/command/cl_cmd.qc
qcsrc/client/damage.qc
qcsrc/client/effects.qc
qcsrc/client/main.qc
qcsrc/client/progs.src
qcsrc/client/waypointsprites.qc
qcsrc/common/constants.qh
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/net_notice.qc
qcsrc/common/physics.qc
qcsrc/common/physics.qh
qcsrc/common/triggers/func/bobbing.qc [new file with mode: 0644]
qcsrc/common/triggers/func/breakable.qc [new file with mode: 0644]
qcsrc/common/triggers/func/button.qc [new file with mode: 0644]
qcsrc/common/triggers/func/conveyor.qc [new file with mode: 0644]
qcsrc/common/triggers/func/conveyor.qh [new file with mode: 0644]
qcsrc/common/triggers/func/door.qc [new file with mode: 0644]
qcsrc/common/triggers/func/door.qh [new file with mode: 0644]
qcsrc/common/triggers/func/door_rotating.qc [new file with mode: 0644]
qcsrc/common/triggers/func/door_secret.qc [new file with mode: 0644]
qcsrc/common/triggers/func/fourier.qc [new file with mode: 0644]
qcsrc/common/triggers/func/include.qc [new file with mode: 0644]
qcsrc/common/triggers/func/include.qh [new file with mode: 0644]
qcsrc/common/triggers/func/ladder.qc [new file with mode: 0644]
qcsrc/common/triggers/func/ladder.qh [new file with mode: 0644]
qcsrc/common/triggers/func/pendulum.qc [new file with mode: 0644]
qcsrc/common/triggers/func/plat.qc [new file with mode: 0644]
qcsrc/common/triggers/func/plat.qh [new file with mode: 0644]
qcsrc/common/triggers/func/pointparticles.qc [new file with mode: 0644]
qcsrc/common/triggers/func/rainsnow.qc [new file with mode: 0644]
qcsrc/common/triggers/func/rotating.qc [new file with mode: 0644]
qcsrc/common/triggers/func/stardust.qc [new file with mode: 0644]
qcsrc/common/triggers/func/train.qc [new file with mode: 0644]
qcsrc/common/triggers/func/vectormamamam.qc [new file with mode: 0644]
qcsrc/common/triggers/include.qc [new file with mode: 0644]
qcsrc/common/triggers/include.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/corner.qc [new file with mode: 0644]
qcsrc/common/triggers/misc/follow.qc [new file with mode: 0644]
qcsrc/common/triggers/misc/include.qc [new file with mode: 0644]
qcsrc/common/triggers/misc/include.qh [new file with mode: 0644]
qcsrc/common/triggers/misc/laser.qc [new file with mode: 0644]
qcsrc/common/triggers/platforms.qc [new file with mode: 0644]
qcsrc/common/triggers/platforms.qh [new file with mode: 0644]
qcsrc/common/triggers/subs.qc [new file with mode: 0644]
qcsrc/common/triggers/subs.qh [new file with mode: 0644]
qcsrc/common/triggers/target/changelevel.qc [new file with mode: 0644]
qcsrc/common/triggers/target/include.qc [new file with mode: 0644]
qcsrc/common/triggers/target/include.qh [new file with mode: 0644]
qcsrc/common/triggers/target/music.qc [new file with mode: 0644]
qcsrc/common/triggers/target/music.qh [new file with mode: 0644]
qcsrc/common/triggers/target/spawn.qc [new file with mode: 0644]
qcsrc/common/triggers/target/speaker.qc [new file with mode: 0644]
qcsrc/common/triggers/target/voicescript.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/counter.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/delay.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/disablerelay.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/flipflop.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/gamestart.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/gravity.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/heal.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/hurt.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/impulse.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/impulse.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/include.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/include.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/jumppads.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/jumppads.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/magicear.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/monoflop.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/multi.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/multi.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/multivibrator.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay_activators.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay_if.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/relay_teamcheck.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/secret.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/secret.qh [new file with mode: 0644]
qcsrc/common/triggers/trigger/swamp.qc [new file with mode: 0644]
qcsrc/common/triggers/trigger/swamp.qh [new file with mode: 0644]
qcsrc/common/triggers/triggers.qc [new file with mode: 0644]
qcsrc/common/triggers/triggers.qh [new file with mode: 0644]
qcsrc/common/weapons/w_porto.qc
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/cheats.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_player.qc
qcsrc/server/constants.qh
qcsrc/server/defs.qh
qcsrc/server/func_breakable.qc [deleted file]
qcsrc/server/g_hook.qc
qcsrc/server/g_models.qc
qcsrc/server/g_subs.qc
qcsrc/server/g_subs.qh
qcsrc/server/g_triggers.qc [deleted file]
qcsrc/server/g_triggers.qh [deleted file]
qcsrc/server/g_world.qc
qcsrc/server/item_key.qc
qcsrc/server/item_key.qh
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/mutators/gamemode_assault.qc
qcsrc/server/mutators/gamemode_domination.qc
qcsrc/server/mutators/gamemode_onslaught.qc
qcsrc/server/mutators/mutator_buffs.qc
qcsrc/server/mutators/mutator_nades.qc
qcsrc/server/mutators/mutators_include.qc
qcsrc/server/progs.src
qcsrc/server/race.qc
qcsrc/server/secret.qc [deleted file]
qcsrc/server/secret.qh [deleted file]
qcsrc/server/spawnpoints.qc
qcsrc/server/t_halflife.qc
qcsrc/server/t_items.qc
qcsrc/server/t_jumppads.qc [deleted file]
qcsrc/server/t_jumppads.qh
qcsrc/server/t_plats.qc [deleted file]
qcsrc/server/t_swamp.qc [deleted file]
qcsrc/server/t_teleporters.qc
qcsrc/server/target_music.qc [deleted file]
qcsrc/server/target_spawn.qc [deleted file]
qcsrc/server/tturrets/system/system_main.qc
qcsrc/server/tturrets/system/system_misc.qc
qcsrc/server/vehicles/racer.qc
qcsrc/server/vehicles/vehicles.qc
qcsrc/warpzonelib/common.qc
qcsrc/warpzonelib/server.qc

index b567190caa666f165cfe49bea7a09ea6fd8b0d73..615b571694f0caca933b08a51c0e1bded62f3ce4 100644 (file)
@@ -1,6 +1,7 @@
 #if defined(CSQC)
        #include "../dpdefs/csprogsdefs.qh"
        #include "defs.qh"
+       #include "../common/triggers/triggers.qh"
        #include "../common/util.qh"
        #include "autocvars.qh"
        #include "bgmscript.qh"
index 910ef6b5df006c69f3347ad33363498d31f53a74..fba28762a349652a1a27616083a509f9f7d0b6a9 100644 (file)
@@ -1,12 +1,6 @@
 #ifndef BGMSCRIPT_H
 #define BGMSCRIPT_H
 
-.string bgmscript;
-.float bgmscriptattack;
-.float bgmscriptdecay;
-.float bgmscriptsustain;
-.float bgmscriptrelease;
-
 .float just_toggled;
 
 void BGMScript_InitEntity(entity e);
index 7b74d2dd7fa56752889ee75f6abb3cdbb1e08115..0748483c5f1ec62d9901a540483aaca67352671d 100644 (file)
@@ -339,6 +339,31 @@ void LocalCommand_mv_download(int request, int argc)
        }
 }
 
+void LocalCommand_find(int request, int argc)
+{
+       switch(request)
+       {
+               case CMD_REQUEST_COMMAND:
+               {
+                       entity client;
+
+                       for(client = world; (client = find(client, classname, argv(1))); )
+                               print(etos(client), "\n");
+
+                       return;
+               }
+
+               default:
+                       print("Incorrect parameters for ^2find^7\n");
+               case CMD_REQUEST_USAGE:
+               {
+                       print("\nUsage:^3 cl_cmd find classname\n");
+                       print("  Where 'classname' is the classname to search for.\n");
+                       return;
+               }
+       }
+}
+
 void LocalCommand_sendcvar(int request, int argc)
 {
        switch(request)
@@ -410,6 +435,7 @@ void LocalCommand_(int request)
        CLIENT_COMMAND("handlevote", LocalCommand_handlevote(request, arguments), "System to handle selecting a vote or option") \
        CLIENT_COMMAND("hud", LocalCommand_hud(request, arguments), "Commands regarding/controlling the HUD system") \
        CLIENT_COMMAND("localprint", LocalCommand_localprint(request, arguments), "Create your own centerprint sent to yourself") \
+       CLIENT_COMMAND("find", LocalCommand_find(request, arguments), "Search through entities for matching classname") \
        CLIENT_COMMAND("mv_download", LocalCommand_mv_download(request, arguments), "Retrieve mapshot picture from the server") \
        CLIENT_COMMAND("sendcvar", LocalCommand_sendcvar(request, arguments), "Send a cvar to the server (like weaponpriority)") \
        /* nothing */
index 000ef8bcc9f45106d9c33e38c085c1e66653ebb3..c3d260863faaeeec23689c88c475c5c53adb28f0 100644 (file)
@@ -47,7 +47,7 @@ void DamageEffect_Think()
        pointparticles(self.team, org, '0 0 0', 1);
 }
 
-void DamageEffect(vector hitorg, float dmg, int type, int specnum)
+void DamageEffect(vector hitorg, float thedamage, int type, int specnum)
 {
        // particle effects for players and objects damaged by weapons (eg: flames coming out of victims shot with rockets)
 
@@ -92,7 +92,7 @@ void DamageEffect(vector hitorg, float dmg, int type, int specnum)
                        return; // allow a single damage on non-skeletal models
        }
 
-       life = bound(autocvar_cl_damageeffect_lifetime_min, dmg * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max);
+       life = bound(autocvar_cl_damageeffect_lifetime_min, thedamage * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max);
 
        effectname = get_weaponinfo(DEATH_WEAPONOF(type)).netname;
 
@@ -121,7 +121,7 @@ void DamageEffect(vector hitorg, float dmg, int type, int specnum)
 
 void Ent_DamageInfo(float isNew)
 {
-       float dmg, rad, edge, thisdmg;
+       float thedamage, rad, edge, thisdmg;
        bool hitplayer = false;
        int species, forcemul;
        vector force, thisforce;
@@ -137,7 +137,7 @@ void Ent_DamageInfo(float isNew)
        w_org.y = ReadCoord();
        w_org.z = ReadCoord();
 
-       dmg = ReadByte();
+       thedamage = ReadByte();
        rad = ReadByte();
        edge = ReadByte();
        force = decompressShortVector(ReadShort());
@@ -168,10 +168,10 @@ void Ent_DamageInfo(float isNew)
                                continue;
                        if(thisdmg < 0)
                                thisdmg = 0;
-                       if(dmg)
+                       if(thedamage)
                        {
-                               thisdmg = dmg + (edge - dmg) * thisdmg;
-                               thisforce = forcemul * vlen(force) * (thisdmg / dmg) * normalize(self.origin - w_org);
+                               thisdmg = thedamage + (edge - thedamage) * thisdmg;
+                               thisforce = forcemul * vlen(force) * (thisdmg / thedamage) * normalize(self.origin - w_org);
                        }
                        else
                        {
@@ -184,7 +184,7 @@ void Ent_DamageInfo(float isNew)
                        if(vlen(nearest - w_org) > bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS))
                                continue;
 
-                       thisdmg = dmg;
+                       thisdmg = thedamage;
                        thisforce = forcemul * force;
                }
 
index df688465739685158b99a52f0346115d2012818b..e9bce8f979df565549bf00c2e4955852ffc8cf95 100644 (file)
@@ -12,9 +12,6 @@
 .string fx_texture;
 .float  fx_lifetime;
 
-void SUB_Remove()
-{ remove(self); }
-
 void b_draw()
 {
     //Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, 0, time * 3, '1 1 1', 0.7, DRAWFLAG_ADDITIVE, view_origin);
index e5c875b37c25abdf2f620422f62ae5f7b678a5ef..3903cb42716563c9020979a58f04f5a56c1098a1 100644 (file)
@@ -17,6 +17,8 @@
 
 #include "../common/monsters/monsters.qh"
 
+#include "../common/triggers/include.qh"
+
 #include "../warpzonelib/client.qh"
 
 // --------------------------------------------------------------------------
@@ -350,26 +352,6 @@ float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)
 
 // --------------------------------------------------------------------------
 // BEGIN OPTIONAL CSQC FUNCTIONS
-void trigger_touch_generic(void() touchfunc)
-{
-       entity e;
-       for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
-       if(e.isplayermodel)
-       {
-               vector emin = e.absmin, emax = e.absmax;
-               if(self.solid == SOLID_BSP)
-               {
-                       emin -= '1 1 1';
-                       emax += '1 1 1';
-               }
-               if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
-               if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
-               {
-                       other = e;
-                       touchfunc();
-               }
-       }
-}
 
 void Ent_RemoveEntCS()
 {
@@ -779,12 +761,6 @@ void Ent_ReadSpawnEvent(float is_new)
 void Ent_RadarLink();
 void Ent_Init();
 void Ent_ScoresInfo();
-#ifdef CSQC
-void ent_func_ladder();
-void ent_trigger_push();
-void ent_target_push();
-void ent_conveyor();
-#endif
 void CSQC_Ent_Update(float bIsNewEntity)
 {
        float t;
@@ -873,6 +849,11 @@ void CSQC_Ent_Update(float bIsNewEntity)
                case ENT_CLIENT_TRIGGER_PUSH: ent_trigger_push(); break;
                case ENT_CLIENT_TARGET_PUSH: ent_target_push(); break;
                case ENT_CLIENT_CONVEYOR: ent_conveyor(); break;
+               case ENT_CLIENT_DOOR: ent_door(); break;
+               case ENT_CLIENT_DOOR_TRIGGER: ent_door_trigger(); break;
+               case ENT_CLIENT_PLAT: ent_plat(); break;
+               case ENT_CLIENT_PLAT_TRIGGER: ent_plat_trigger(); break;
+               case ENT_CLIENT_SWAMP: ent_swamp(); break;
 
                default:
                        //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype));
index 640a6d6e9791c5c657d792d10a3108643c31a683..e1b469734207b091b1ca1e0e0187d8f3723bc6b2 100644 (file)
@@ -62,20 +62,20 @@ weapons/projectile.qc // TODO
 
 ../common/weapons/weapons.qc // TODO
 
+../common/triggers/include.qc
+
 ../csqcmodellib/cl_model.qc
 ../csqcmodellib/cl_player.qc
 ../csqcmodellib/interpolate.qc
 
 ../server/movelib.qc
-../server/t_halflife.qc
-../server/t_items.qc
-../server/t_jumppads.qc
-../server/t_plats.qc
 
 ../server/mutators/mutator_multijump.qc
 
 ../server/vehicles/bumblebee.qc
 
+../server/t_items.qc
+
 ../warpzonelib/anglestransform.qc
 ../warpzonelib/client.qc
 ../warpzonelib/common.qc
index 788dd871eed84f74e2050375211ef9eca122ee20..4a8b156799cce1beb339e139e4d5f55375cd045c 100644 (file)
@@ -38,7 +38,7 @@ void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, f
        R_EndPolygon();
 }
 
-void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float height, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f)
+void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float theheight, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f)
 {
        vector o, ri, up;
        float owidth; // outer width
@@ -56,13 +56,13 @@ void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, fl
        up = rotate(up, rot);
 
        owidth = width + 2 * border;
-       o = o - up * (margin + border + height) + ri * (sz.x - owidth) * 0.5;
+       o = o - up * (margin + border + theheight) + ri * (sz.x - owidth) * 0.5;
 
        drawquad(o - up * border,                               ri * owidth,    up * border, "", rgb,  a,  f);
-       drawquad(o + up * height,                               ri * owidth,    up * border, "", rgb,  a,  f);
-       drawquad(o,                                             ri * border,    up * height, "", rgb,  a,  f);
-       drawquad(o + ri * (owidth - border),                    ri * border,    up * height, "", rgb,  a,  f);
-       drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
+       drawquad(o + up * theheight,                               ri * owidth,    up * border, "", rgb,  a,  f);
+       drawquad(o,                                             ri * border,    up * theheight, "", rgb,  a,  f);
+       drawquad(o + ri * (owidth - border),                    ri * border,    up * theheight, "", rgb,  a,  f);
+       drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * theheight, "", hrgb, ha, f);
 }
 
 // returns location of sprite text
index 37f80863a6e58722711aeb27dfc74da35bcacc70..7f594be060b7cf505cf8218aed37de88eab7e39c 100644 (file)
@@ -107,6 +107,11 @@ const int ENT_CLIENT_LADDER = 61;
 const int ENT_CLIENT_TRIGGER_PUSH = 62;
 const int ENT_CLIENT_TARGET_PUSH = 63;
 const int ENT_CLIENT_CONVEYOR = 64;
+const int ENT_CLIENT_DOOR = 65;
+const int ENT_CLIENT_DOOR_TRIGGER = 66;
+const int ENT_CLIENT_PLAT = 67;
+const int ENT_CLIENT_PLAT_TRIGGER = 68;
+const int ENT_CLIENT_SWAMP = 69;
 
 const int ENT_CLIENT_HEALING_ORB = 80;
 
index 28430e9617e8a9a6666519a444d39df47303a103..3526a1f18bf66eab391b26c9fc5e6dbbf7648bd1 100644 (file)
@@ -19,6 +19,7 @@
     #include "../../server/campaign.qh"
     #include "../../server/command/common.qh"
     #include "../../server/command/cmd.qh"
+       #include "../triggers/triggers.qh"
     #include "../../csqcmodellib/sv_model.qh"
     #include "../../server/round_handler.qh"
     #include "../../server/tturrets/include/turrets.qh"
index bc520e59d57005b7547d8ddbeba0a3cd6a5b9584..fb9958b08fcbd186fd151b65afb89462d442a357 100644 (file)
@@ -47,9 +47,6 @@ void sv_notice_toall(string _notice, float _howlong, float _modal)
 #endif // SVQC
 
 #ifdef CSQC
-void SUB_Remove()
-{ remove(self); }
-
 void cl_notice_read()
 {
     entity _notice;
index b8fdb1a6c838b8415b41481f38f7733c1a1d1811..e77184750eeb1117d092a05769c22acb3c8e4ecc 100644 (file)
@@ -1,4 +1,6 @@
 #include "physics.qh"
+#include "triggers/trigger/swamp.qh"
+#include "triggers/trigger/jumppads.qh"
 
 #ifdef SVQC
 
@@ -1199,18 +1201,6 @@ void PM_ladder(float maxspd_mod)
                PM_Accelerate(wishdir, wishspeed, wishspeed, PHYS_ACCELERATE*maxspd_mod, 1, 0, 0, 0);
 }
 
-void PM_check_jumppad()
-{
-#ifdef CSQC
-       entity oldself = self;
-
-       for(self = world; (self = find(self, classname, "jumppad")); )
-               trigger_push_draw();
-
-       self = oldself;
-#endif
-}
-
 void PM_jetpack(float maxspd_mod)
 {
        //makevectors(PHYS_INPUT_ANGLES(self).y * '0 1 0');
@@ -1777,11 +1767,8 @@ void PM_Main()
 
        maxspeed_mod = 1;
 
-#ifdef SVQC
-       if (self.in_swamp) {
+       if (self.in_swamp)
                maxspeed_mod *= self.swamp_slowdown; //cvar("g_balance_swamp_moverate");
-       }
-#endif
 
        // conveyors: first fix velocity
        if (self.conveyor.state)
@@ -1873,8 +1860,6 @@ void PM_Main()
 #endif
                CheckPlayerJump();
 
-       PM_check_jumppad();
-
        if (self.flags & /* FL_WATERJUMP */ 2048)
        {
                self.velocity_x = self.movedir_x;
index 9612f0cfb9d33663bb97c984a7ae061a79545c0f..c256a9eb10aed7456c3f02307e74af036302dd10 100644 (file)
@@ -24,15 +24,15 @@ float AdjustAirAccelQW(float accelqw, float factor);
 
 #ifdef CSQC
 
-       #include "../server/t_jumppads.qh"
-
        float PM_multijump_checkjump();
        void PM_multijump();
-    .float speed;
-    .float watertype;
-    .float jumppadcount;
-    .float ladder_time;
-    .entity ladder_entity;
+
+       .float watertype;
+
+// TODO
+       #define IS_CLIENT(s)                                            (s).isplayermodel
+       #define IS_PLAYER(s)                                            (s).isplayermodel
+       #define isPushable(s)                                           (s).isplayermodel
 
        float player_multijump;
        float player_jumpheight;
diff --git a/qcsrc/common/triggers/func/bobbing.qc b/qcsrc/common/triggers/func/bobbing.qc
new file mode 100644 (file)
index 0000000..f671b8a
--- /dev/null
@@ -0,0 +1,85 @@
+#ifdef SVQC
+.float height;
+void func_bobbing_controller_think()
+{
+       vector v;
+       self.nextthink = time + 0.1;
+
+       if(self.owner.active != ACTIVE_ACTIVE)
+       {
+               self.owner.velocity = '0 0 0';
+               return;
+       }
+
+       // calculate sinewave using makevectors
+       makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
+       v = self.owner.destvec + self.owner.movedir * v_forward_y;
+       if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
+               // * 10 so it will arrive in 0.1 sec
+               self.owner.velocity = (v - self.owner.origin) * 10;
+}
+
+/*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
+Brush model that moves back and forth on one axis (default Z).
+speed : how long one cycle takes in seconds (default 4)
+height : how far the cycle moves (default 32)
+phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
+noise : path/name of looping .wav file to play.
+dmg : Do this mutch dmg every .dmgtime intervall when blocked
+dmgtime : See above.
+*/
+void spawnfunc_func_bobbing()
+{
+       entity controller;
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+       if (!self.speed)
+               self.speed = 4;
+       if (!self.height)
+               self.height = 32;
+       // center of bobbing motion
+       self.destvec = self.origin;
+       // time scale to get degrees
+       self.cnt = 360 / self.speed;
+
+       self.active = ACTIVE_ACTIVE;
+
+       // damage when blocked
+       self.blocked = generic_plat_blocked;
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       // how far to bob
+       if (self.spawnflags & 1) // X
+               self.movedir = '1 0 0' * self.height;
+       else if (self.spawnflags & 2) // Y
+               self.movedir = '0 1 0' * self.height;
+       else // Z
+               self.movedir = '0 0 1' * self.height;
+
+       if (!InitMovingBrushTrigger())
+               return;
+
+       // wait for targets to spawn
+       controller = spawn();
+       controller.classname = "func_bobbing_controller";
+       controller.owner = self;
+       controller.nextthink = time + 1;
+       controller.think = func_bobbing_controller_think;
+       self.nextthink = self.ltime + 999999999;
+       self.think = SUB_NullThink; // for PushMove
+
+       // Savage: Reduce bandwith, critical on e.g. nexdm02
+       self.effects |= EF_LOWPRECISION;
+
+       // TODO make a reset function for this one
+}
+#endif
diff --git a/qcsrc/common/triggers/func/breakable.qc b/qcsrc/common/triggers/func/breakable.qc
new file mode 100644 (file)
index 0000000..eac14a4
--- /dev/null
@@ -0,0 +1,288 @@
+#ifdef SVQC
+#include "../../../server/weapons/common.qh"
+
+.entity sprite;
+
+.float dmg;
+.float dmg_edge;
+.float dmg_radius;
+.float dmg_force;
+.float debrismovetype;
+.float debrissolid;
+.vector debrisvelocity;
+.vector debrisvelocityjitter;
+.vector debrisavelocityjitter;
+.float debristime;
+.float debristimejitter;
+.float debrisfadetime;
+.float debrisdamageforcescale;
+.float debrisskin;
+
+.string mdl_dead; // or "" to hide when broken
+.string debris; // space separated list of debris models
+// other fields:
+//   mdl = particle effect name
+//   count = particle effect multiplier
+//   targetname = target to trigger to unbreak the model
+//   target = targets to trigger when broken
+//   health = amount of damage it can take
+//   spawnflags:
+//     1 = start disabled (needs to be triggered to activate)
+//     2 = indicate damage
+// notes:
+//   for mdl_dead to work, origin must be set (using a common/origin brush).
+//   Otherwise mdl_dead will be displayed at the map origin, and nobody would
+//   want that!
+
+void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
+
+//
+// func_breakable
+// - basically func_assault_destructible for general gameplay use
+//
+void LaunchDebris (string debrisname, vector force)
+{
+       entity dbr = spawn();
+       setorigin(dbr, self.absmin
+                  + '1 0 0' * random() * (self.absmax.x - self.absmin.x)
+                  + '0 1 0' * random() * (self.absmax.y - self.absmin.y)
+                  + '0 0 1' * random() * (self.absmax.z - self.absmin.z));
+       setmodel (dbr, debrisname );
+       dbr.skin = self.debrisskin;
+       dbr.colormap = self.colormap; // inherit team colors
+       dbr.owner = self; // do not be affected by our own explosion
+       dbr.movetype = self.debrismovetype;
+       dbr.solid = self.debrissolid;
+       if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out
+               setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it
+       dbr.velocity_x = self.debrisvelocity.x + self.debrisvelocityjitter.x * crandom();
+       dbr.velocity_y = self.debrisvelocity.y + self.debrisvelocityjitter.y * crandom();
+       dbr.velocity_z = self.debrisvelocity.z + self.debrisvelocityjitter.z * crandom();
+       self.velocity = self.velocity + force * self.debrisdamageforcescale;
+       dbr.avelocity_x = random()*self.debrisavelocityjitter.x;
+       dbr.avelocity_y = random()*self.debrisavelocityjitter.y;
+       dbr.avelocity_z = random()*self.debrisavelocityjitter.z;
+       dbr.damageforcescale = self.debrisdamageforcescale;
+       if(dbr.damageforcescale)
+               dbr.takedamage = DAMAGE_YES;
+       SUB_SetFade(dbr, time + self.debristime + crandom() * self.debristimejitter, self.debrisfadetime);
+}
+
+void func_breakable_colormod()
+{
+       float h;
+       if (!(self.spawnflags & 2))
+               return;
+       h = self.health / self.max_health;
+       if(h < 0.25)
+               self.colormod = '1 0 0';
+       else if(h <= 0.75)
+               self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5);
+       else
+               self.colormod = '1 1 1';
+
+       CSQCMODEL_AUTOUPDATE();
+}
+
+void func_breakable_look_destroyed()
+{
+       float floorZ;
+
+       if(self.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first
+               self.dropped_origin = self.origin;
+
+       if(self.mdl_dead == "")
+               self.model = "";
+       else {
+               if (self.origin == '0 0 0')     {       // probably no origin brush, so don't spawn in the middle of the map..
+                       floorZ = self.absmin.z;
+                       setorigin(self,((self.absmax+self.absmin)*.5));
+                       self.origin_z = floorZ;
+               }
+               setmodel(self, self.mdl_dead);
+       }
+
+       self.solid = SOLID_NOT;
+}
+
+void func_breakable_look_restore()
+{
+       setmodel(self, self.mdl);
+       if(self.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow
+               setorigin(self, self.dropped_origin);
+       self.solid = SOLID_BSP;
+}
+
+void func_breakable_behave_destroyed()
+{
+       self.health = self.max_health;
+       self.takedamage = DAMAGE_NO;
+       self.bot_attack = false;
+       self.event_damage = func_null;
+       self.state = 1;
+       func_breakable_colormod();
+}
+
+void func_breakable_behave_restore()
+{
+       self.health = self.max_health;
+       if(self.sprite)
+       {
+               WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+               WaypointSprite_UpdateHealth(self.sprite, self.health);
+       }
+       self.takedamage = DAMAGE_AIM;
+       self.bot_attack = true;
+       self.event_damage = func_breakable_damage;
+       self.state = 0;
+       self.nextthink = 0; // cancel auto respawn
+       func_breakable_colormod();
+}
+
+void func_breakable_destroyed()
+{
+       func_breakable_look_destroyed();
+       func_breakable_behave_destroyed();
+
+       CSQCMODEL_AUTOUPDATE();
+}
+
+void func_breakable_restore()
+{
+       func_breakable_look_restore();
+       func_breakable_behave_restore();
+
+       CSQCMODEL_AUTOUPDATE();
+}
+
+vector debrisforce; // global, set before calling this
+void func_breakable_destroy() {
+       float n, i;
+       string oldmsg;
+
+       activator = self.owner;
+       self.owner = world; // set by W_PrepareExplosionByDamage
+
+       // now throw around the debris
+       n = tokenize_console(self.debris);
+       for(i = 0; i < n; ++i)
+               LaunchDebris(argv(i), debrisforce);
+
+       func_breakable_destroyed();
+
+       if(self.noise)
+               sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+
+       if(self.dmg)
+               RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world);
+
+       if(self.cnt)
+               pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
+
+       if(self.respawntime)
+       {
+               self.think = func_breakable_restore;
+               self.nextthink = time + self.respawntime + crandom() * self.respawntimejitter;
+       }
+
+       oldmsg = self.message;
+       self.message = "";
+       SUB_UseTargets();
+       self.message = oldmsg;
+}
+
+void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+       if(self.state == 1)
+               return;
+       if(self.spawnflags & DOOR_NOSPLASH)
+               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+                       return;
+       if(self.team)
+               if(attacker.team == self.team)
+                       return;
+       self.health = self.health - damage;
+       if(self.sprite)
+       {
+               WaypointSprite_Ping(self.sprite);
+               WaypointSprite_UpdateHealth(self.sprite, self.health);
+       }
+       func_breakable_colormod();
+
+       if(self.health <= 0)
+       {
+               debrisforce = force;
+               W_PrepareExplosionByDamage(attacker, func_breakable_destroy);
+       }
+}
+
+void func_breakable_reset()
+{
+       self.team = self.team_saved;
+       func_breakable_look_restore();
+       if(self.spawnflags & 1)
+               func_breakable_behave_destroyed();
+       else
+               func_breakable_behave_restore();
+
+       CSQCMODEL_AUTOUPDATE();
+}
+
+// destructible walls that can be used to trigger target_objective_decrease
+void spawnfunc_func_breakable() {
+       float n, i;
+       if(!self.health)
+               self.health = 100;
+       self.max_health = self.health;
+
+       // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway
+       if(!self.debrismovetype) self.debrismovetype = MOVETYPE_BOUNCE;
+       if(!self.debrissolid) self.debrissolid = SOLID_NOT;
+       if(self.debrisvelocity == '0 0 0') self.debrisvelocity = '0 0 140';
+       if(self.debrisvelocityjitter == '0 0 0') self.debrisvelocityjitter = '70 70 70';
+       if(self.debrisavelocityjitter == '0 0 0') self.debrisavelocityjitter = '600 600 600';
+       if(!self.debristime) self.debristime = 3.5;
+       if(!self.debristimejitter) self.debristime = 2.5;
+
+       if(self.mdl != "")
+               self.cnt = particleeffectnum(self.mdl);
+       if(self.count == 0)
+               self.count = 1;
+
+       if(self.message == "")
+               self.message = "got too close to an explosion";
+       if(self.message2 == "")
+               self.message2 = "was pushed into an explosion by";
+       if(!self.dmg_radius)
+               self.dmg_radius = 150;
+       if(!self.dmg_force)
+               self.dmg_force = 200;
+
+       self.mdl = self.model;
+       SetBrushEntityModel();
+
+       self.use = func_breakable_restore;
+
+       // precache all the models
+       if (self.mdl_dead)
+               precache_model(self.mdl_dead);
+       n = tokenize_console(self.debris);
+       for(i = 0; i < n; ++i)
+               precache_model(argv(i));
+       if(self.noise)
+               precache_sound(self.noise);
+
+       self.team_saved = self.team;
+       self.dropped_origin = self.origin;
+
+       self.reset = func_breakable_reset;
+       func_breakable_reset();
+
+       CSQCMODEL_AUTOINIT();
+}
+
+// for use in maps with a "model" key set
+void spawnfunc_misc_breakablemodel() {
+       spawnfunc_func_breakable();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/button.qc b/qcsrc/common/triggers/func/button.qc
new file mode 100644 (file)
index 0000000..4bcbab3
--- /dev/null
@@ -0,0 +1,155 @@
+#ifdef SVQC
+// button and multiple button
+
+void() button_wait;
+void() button_return;
+
+void button_wait()
+{
+       self.state = STATE_TOP;
+       self.nextthink = self.ltime + self.wait;
+       self.think = button_return;
+       activator = self.enemy;
+       SUB_UseTargets();
+       self.frame = 1;                 // use alternate textures
+}
+
+void button_done()
+{
+       self.state = STATE_BOTTOM;
+}
+
+void button_return()
+{
+       self.state = STATE_DOWN;
+       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
+       self.frame = 0;                 // use normal textures
+       if (self.health)
+               self.takedamage = DAMAGE_YES;   // can be shot again
+}
+
+
+void button_blocked()
+{
+       // do nothing, just don't come all the way back out
+}
+
+
+void button_fire()
+{
+       self.health = self.max_health;
+       self.takedamage = DAMAGE_NO;    // will be reset upon return
+
+       if (self.state == STATE_UP || self.state == STATE_TOP)
+               return;
+
+       if (self.noise != "")
+               sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+
+       self.state = STATE_UP;
+       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
+}
+
+void button_reset()
+{
+       self.health = self.max_health;
+       setorigin(self, self.pos1);
+       self.frame = 0;                 // use normal textures
+       self.state = STATE_BOTTOM;
+       if (self.health)
+               self.takedamage = DAMAGE_YES;   // can be shot again
+}
+
+void button_use()
+{
+       if(self.active != ACTIVE_ACTIVE)
+               return;
+
+       self.enemy = activator;
+       button_fire ();
+}
+
+void button_touch()
+{
+       if (!other)
+               return;
+       if (!other.iscreature)
+               return;
+       if(other.velocity * self.movedir < 0)
+               return;
+       self.enemy = other;
+       if (other.owner)
+               self.enemy = other.owner;
+       button_fire ();
+}
+
+void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if(self.spawnflags & DOOR_NOSPLASH)
+               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+                       return;
+       self.health = self.health - damage;
+       if (self.health <= 0)
+       {
+               self.enemy = damage_attacker;
+               button_fire ();
+       }
+}
+
+
+/*QUAKED spawnfunc_func_button (0 .5 .8) ?
+When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
+
+"angle"                determines the opening direction
+"target"       all entities with a matching targetname will be used
+"speed"                override the default 40 speed
+"wait"         override the default 1 second wait (-1 = never return)
+"lip"          override the default 4 pixel lip remaining at end of move
+"health"       if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the InstaGib laser
+"sounds"
+0) steam metal
+1) wooden clunk
+2) metallic click
+3) in-out
+*/
+void spawnfunc_func_button()
+{
+       SetMovedir ();
+
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+
+       self.blocked = button_blocked;
+       self.use = button_use;
+
+//     if (self.health == 0) // all buttons are now shootable
+//             self.health = 10;
+       if (self.health)
+       {
+               self.max_health = self.health;
+               self.event_damage = button_damage;
+               self.takedamage = DAMAGE_YES;
+       }
+       else
+               self.touch = button_touch;
+
+       if (!self.speed)
+               self.speed = 40;
+       if (!self.wait)
+               self.wait = 1;
+       if (!self.lip)
+               self.lip = 4;
+
+    if(self.noise != "")
+        precache_sound(self.noise);
+
+       self.active = ACTIVE_ACTIVE;
+
+       self.pos1 = self.origin;
+       self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
+    self.flags |= FL_NOTARGET;
+
+       button_reset();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/conveyor.qc b/qcsrc/common/triggers/func/conveyor.qc
new file mode 100644 (file)
index 0000000..05f0fa0
--- /dev/null
@@ -0,0 +1,197 @@
+void conveyor_think()
+{
+#ifdef CSQC
+       // TODO: check if this is what is causing the glitchiness when switching between them
+       float dt = time - self.move_time;
+       self.move_time = time;
+       if(dt <= 0) { return; }
+#endif
+       entity e;
+
+       // set myself as current conveyor where possible
+       for(e = world; (e = findentity(e, conveyor, self)); )
+               e.conveyor = world;
+
+       if(self.state)
+       {
+               for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
+                       if(!e.conveyor.state)
+                               if(isPushable(e))
+                               {
+                                       vector emin = e.absmin;
+                                       vector emax = e.absmax;
+                                       if(self.solid == SOLID_BSP)
+                                       {
+                                               emin -= '1 1 1';
+                                               emax += '1 1 1';
+                                       }
+                                       if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
+                                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
+                                                       e.conveyor = self;
+                               }
+
+               for(e = world; (e = findentity(e, conveyor, self)); )
+               {
+                       if(IS_CLIENT(e)) // doing it via velocity has quite some advantages
+                               continue; // done in SV_PlayerPhysics   continue;
+
+                       setorigin(e, e.origin + self.movedir * PHYS_INPUT_FRAMETIME);
+                       move_out_of_solid(e);
+#ifdef SVQC
+                       UpdateCSQCProjectile(e);
+#endif
+                       /*
+                       // stupid conveyor code
+                       tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
+                       if(trace_fraction > 0)
+                               setorigin(e, trace_endpos);
+                       */
+               }
+       }
+
+#ifdef SVQC
+       self.nextthink = time;
+#endif
+}
+
+#ifdef SVQC
+
+void conveyor_use()
+{
+       self.state = !self.state;
+
+       self.SendFlags |= 2;
+}
+
+void conveyor_reset()
+{
+       self.state = (self.spawnflags & 1);
+
+       self.SendFlags |= 2;
+}
+
+float conveyor_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_CONVEYOR);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & 1)
+       {
+               WriteByte(MSG_ENTITY, self.warpzone_isboxy);
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               WriteCoord(MSG_ENTITY, self.mins_x);
+               WriteCoord(MSG_ENTITY, self.mins_y);
+               WriteCoord(MSG_ENTITY, self.mins_z);
+               WriteCoord(MSG_ENTITY, self.maxs_x);
+               WriteCoord(MSG_ENTITY, self.maxs_y);
+               WriteCoord(MSG_ENTITY, self.maxs_z);
+
+               WriteCoord(MSG_ENTITY, self.movedir_x);
+               WriteCoord(MSG_ENTITY, self.movedir_y);
+               WriteCoord(MSG_ENTITY, self.movedir_z);
+
+               WriteByte(MSG_ENTITY, self.speed);
+               WriteByte(MSG_ENTITY, self.state);
+
+               WriteString(MSG_ENTITY, self.targetname);
+               WriteString(MSG_ENTITY, self.target);
+       }
+
+       if(sf & 2)
+               WriteByte(MSG_ENTITY, self.state);
+
+       return true;
+}
+
+void conveyor_init()
+{
+       if (!self.speed)
+               self.speed = 200;
+       self.movedir = self.movedir * self.speed;
+       self.think = conveyor_think;
+       self.nextthink = time;
+       IFTARGETED
+       {
+               self.use = conveyor_use;
+               self.reset = conveyor_reset;
+               conveyor_reset();
+       }
+       else
+               self.state = 1;
+
+       FixSize(self);
+
+       Net_LinkEntity(self, 0, false, conveyor_send);
+
+       self.SendFlags |= 1;
+}
+
+void spawnfunc_trigger_conveyor()
+{
+       SetMovedir();
+       EXACTTRIGGER_INIT;
+       conveyor_init();
+}
+
+void spawnfunc_func_conveyor()
+{
+       SetMovedir();
+       InitMovingBrushTrigger();
+       self.movetype = MOVETYPE_NONE;
+       conveyor_init();
+}
+
+#elif defined(CSQC)
+
+void conveyor_init()
+{
+       self.draw = conveyor_think;
+       self.drawmask = MASK_NORMAL;
+
+       self.movetype = MOVETYPE_NONE;
+       self.model = "";
+       self.solid = SOLID_TRIGGER;
+       self.move_origin = self.origin;
+       self.move_time = time;
+}
+
+void ent_conveyor()
+{
+       float sf = ReadByte();
+
+       if(sf & 1)
+       {
+               self.warpzone_isboxy = ReadByte();
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+
+               self.mins_x = ReadCoord();
+               self.mins_y = ReadCoord();
+               self.mins_z = ReadCoord();
+               self.maxs_x = ReadCoord();
+               self.maxs_y = ReadCoord();
+               self.maxs_z = ReadCoord();
+               setsize(self, self.mins, self.maxs);
+
+               self.movedir_x = ReadCoord();
+               self.movedir_y = ReadCoord();
+               self.movedir_z = ReadCoord();
+
+               self.speed = ReadByte();
+               self.state = ReadByte();
+
+               self.targetname = strzone(ReadString());
+               self.target = strzone(ReadString());
+
+               conveyor_init();
+       }
+
+       if(sf & 2)
+               self.state = ReadByte();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/conveyor.qh b/qcsrc/common/triggers/func/conveyor.qh
new file mode 100644 (file)
index 0000000..f272030
--- /dev/null
@@ -0,0 +1,3 @@
+#ifdef CSQC
+void ent_conveyor();
+#endif
diff --git a/qcsrc/common/triggers/func/door.qc b/qcsrc/common/triggers/func/door.qc
new file mode 100644 (file)
index 0000000..79277c7
--- /dev/null
@@ -0,0 +1,960 @@
+/*
+
+Doors are similar to buttons, but can spawn a fat trigger field around them
+to open without a touch, and they link together to form simultanious
+double/quad doors.
+
+Door.owner is the master door.  If there is only one door, it points to itself.
+If multiple doors, all will point to a single one.
+
+Door.enemy chains from the master door through all doors linked in the chain.
+
+*/
+
+
+/*
+=============================================================================
+
+THINK FUNCTIONS
+
+=============================================================================
+*/
+
+void() door_go_down;
+void() door_go_up;
+void() door_rotating_go_down;
+void() door_rotating_go_up;
+
+void door_blocked()
+{
+       if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO))
+       { // KIll Kill Kill!!
+#ifdef SVQC
+               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+       }
+       else
+       {
+#ifdef SVQC
+               if((self.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
+                       Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+
+                //Dont chamge direction for dead or dying stuff
+               if(PHYS_DEAD(other) && (other.takedamage == DAMAGE_NO))
+               {
+                       if (self.wait >= 0)
+                       {
+                               if (self.state == STATE_DOWN)
+                       if (self.classname == "door")
+                       {
+                               door_go_up ();
+                       } else
+                       {
+                               door_rotating_go_up ();
+                       }
+                               else
+                       if (self.classname == "door")
+                       {
+                               door_go_down ();
+                       } else
+                       {
+                               door_rotating_go_down ();
+                       }
+                       }
+               }
+#ifdef SVQC
+               else
+               {
+                       //gib dying stuff just to make sure
+                       if((self.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
+                               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+#endif
+       }
+}
+
+void door_hit_top()
+{
+       if (self.noise1 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_TOP;
+       if (self.spawnflags & DOOR_TOGGLE)
+               return;         // don't come down automatically
+       if (self.classname == "door")
+       {
+               self.think = door_go_down;
+       } else
+       {
+               self.think = door_rotating_go_down;
+       }
+       self.nextthink = self.ltime + self.wait;
+}
+
+void door_hit_bottom()
+{
+       if (self.noise1 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_BOTTOM;
+}
+
+void door_go_down()
+{
+       if (self.noise2 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       if (self.max_health)
+       {
+               self.takedamage = DAMAGE_YES;
+               self.health = self.max_health;
+       }
+       print(
+#ifdef SVQC
+       "Server ",
+#elif defined(CSQC)
+       "Client ",
+#endif
+       "going down at time ", ftos(time), "\n");
+
+       self.state = STATE_DOWN;
+       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
+}
+
+void door_go_up()
+{
+       if (self.state == STATE_UP)
+               return;         // already going up
+
+       if (self.state == STATE_TOP)
+       {       // reset top wait time
+               self.nextthink = self.ltime + self.wait;
+               return;
+       }
+
+       if (self.noise2 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_UP;
+       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
+
+       string oldmessage;
+       oldmessage = self.message;
+       self.message = "";
+       SUB_UseTargets();
+       self.message = oldmessage;
+}
+
+
+/*
+=============================================================================
+
+ACTIVATION FUNCTIONS
+
+=============================================================================
+*/
+
+float door_check_keys(void)
+{
+       local entity door;
+
+
+       if (self.owner)
+               door = self.owner;
+       else
+               door = self;
+
+       // no key needed
+       if (!door.itemkeys)
+               return true;
+
+       // this door require a key
+       // only a player can have a key
+       if (!IS_PLAYER(other))
+               return false;
+
+#ifdef SVQC
+       if (item_keys_usekey(door, other))
+       {
+               // some keys were used
+               if (other.key_door_messagetime <= time)
+               {
+
+                       play2(other, "misc/talk.wav");
+                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys));
+                       other.key_door_messagetime = time + 2;
+               }
+       }
+       else
+       {
+               // no keys were used
+               if (other.key_door_messagetime <= time)
+               {
+                       play2(other, "misc/talk.wav");
+                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys));
+
+                       other.key_door_messagetime = time + 2;
+               }
+       }
+#endif
+
+       if (door.itemkeys)
+       {
+#ifdef SVQC
+               // door is now unlocked
+               play2(other, "misc/talk.wav");
+               Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_UNLOCKED);
+#endif
+               return true;
+       }
+       else
+               return false;
+}
+
+void door_fire()
+{
+       entity  oself;
+       entity  starte;
+
+       if (self.owner != self)
+               objerror ("door_fire: self.owner != self");
+
+       oself = self;
+
+       if (self.spawnflags & DOOR_TOGGLE)
+       {
+               if (self.state == STATE_UP || self.state == STATE_TOP)
+               {
+                       starte = self;
+                       do
+                       {
+                               if (self.classname == "door")
+                               {
+                                       door_go_down ();
+                               }
+                               else
+                               {
+                                       door_rotating_go_down ();
+                               }
+                               self = self.enemy;
+                       } while ( (self != starte) && (self != world) );
+                       self = oself;
+                       return;
+               }
+       }
+
+// trigger all paired doors
+       starte = self;
+       do
+       {
+               if (self.classname == "door")
+               {
+                       door_go_up ();
+               } else
+               {
+                       // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
+                       if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
+                       {
+                               self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
+                               self.pos2 = '0 0 0' - self.pos2;
+                       }
+                       // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
+                       if (!((self.spawnflags & 2) &&  (self.spawnflags & 8) && self.state == STATE_DOWN
+                               && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
+                       {
+                               door_rotating_go_up ();
+                       }
+               }
+               self = self.enemy;
+       } while ( (self != starte) && (self != world) );
+       self = oself;
+}
+
+void door_use()
+{
+       entity oself;
+
+       //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
+
+       if (self.owner)
+       {
+               oself = self;
+               self = self.owner;
+               door_fire ();
+               self = oself;
+       }
+}
+
+void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       entity oself;
+       if(self.spawnflags & DOOR_NOSPLASH)
+               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+                       return;
+       self.health = self.health - damage;
+
+       if (self.itemkeys)
+       {
+               // don't allow opening doors through damage if keys are required
+               return;
+       }
+
+       if (self.health <= 0)
+       {
+               oself = self;
+               self = self.owner;
+               self.health = self.max_health;
+               self.takedamage = DAMAGE_NO;    // wil be reset upon return
+               door_use ();
+               self = oself;
+       }
+}
+
+
+/*
+================
+door_touch
+
+Prints messages
+================
+*/
+
+void door_touch()
+{
+       if (!IS_PLAYER(other))
+               return;
+       if (self.owner.attack_finished_single > time)
+               return;
+
+       self.owner.attack_finished_single = time + 2;
+
+#ifdef SVQC
+       if (!(self.owner.dmg) && (self.owner.message != ""))
+       {
+               if (IS_CLIENT(other))
+                       centerprint(other, self.owner.message);
+               play2(other, "misc/talk.wav");
+       }
+#endif
+}
+
+void door_generic_plat_blocked()
+{
+
+       if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
+#ifdef SVQC
+               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+       }
+       else
+       {
+
+#ifdef SVQC
+               if((self.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
+                       Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+
+                //Dont chamge direction for dead or dying stuff
+               if(PHYS_DEAD(other) && (other.takedamage == DAMAGE_NO))
+               {
+                       if (self.wait >= 0)
+                       {
+                               if (self.state == STATE_DOWN)
+                                       door_rotating_go_up ();
+                               else
+                                       door_rotating_go_down ();
+                       }
+               }
+#ifdef SVQC
+               else
+               {
+                       //gib dying stuff just to make sure
+                       if((self.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
+                               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+#endif
+       }
+}
+
+void door_rotating_hit_top()
+{
+       if (self.noise1 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_TOP;
+       if (self.spawnflags & DOOR_TOGGLE)
+               return;         // don't come down automatically
+       self.think = door_rotating_go_down;
+       self.nextthink = self.ltime + self.wait;
+}
+
+void door_rotating_hit_bottom()
+{
+       if (self.noise1 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
+       {
+               self.pos2 = '0 0 0' - self.pos2;
+               self.lip = 0;
+       }
+       self.state = STATE_BOTTOM;
+}
+
+void door_rotating_go_down()
+{
+       if (self.noise2 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       if (self.max_health)
+       {
+               self.takedamage = DAMAGE_YES;
+               self.health = self.max_health;
+       }
+
+       self.state = STATE_DOWN;
+       SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
+}
+
+void door_rotating_go_up()
+{
+       if (self.state == STATE_UP)
+               return;         // already going up
+
+       if (self.state == STATE_TOP)
+       {       // reset top wait time
+               self.nextthink = self.ltime + self.wait;
+               return;
+       }
+       if (self.noise2 != "")
+               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       self.state = STATE_UP;
+       SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
+
+       string oldmessage;
+       oldmessage = self.message;
+       self.message = "";
+       SUB_UseTargets();
+       self.message = oldmessage;
+}
+
+
+/*
+=========================================
+door trigger
+
+Spawned if a door lacks a real activator
+=========================================
+*/
+
+void door_trigger_touch()
+{
+       if (other.health < 1)
+#ifdef SVQC
+               if (!(other.iscreature && !PHYS_DEAD(other)))
+#elif defined(CSQC)
+               if(!(IS_CLIENT(other) && !PHYS_DEAD(other)))
+                       return;
+#endif
+
+       if (time < self.attack_finished_single)
+               return;
+
+       // check if door is locked
+       if (!door_check_keys())
+               return;
+
+       self.attack_finished_single = time + 1;
+
+       activator = other;
+
+       self = self.owner;
+       door_use ();
+}
+
+#ifdef SVQC
+
+float door_trigger_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_DOOR_TRIGGER);
+
+       WriteShort(MSG_ENTITY, num_for_edict(self.owner));
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       WriteCoord(MSG_ENTITY, self.mins_x);
+       WriteCoord(MSG_ENTITY, self.mins_y);
+       WriteCoord(MSG_ENTITY, self.mins_z);
+       WriteCoord(MSG_ENTITY, self.maxs_x);
+       WriteCoord(MSG_ENTITY, self.maxs_y);
+       WriteCoord(MSG_ENTITY, self.maxs_z);
+
+       return true;
+}
+
+void door_trigger_link(entity trig)
+{
+       Net_LinkEntity(trig, false, 0, door_trigger_send);
+}
+
+void spawn_field(vector fmins, vector fmaxs)
+{
+       entity  trigger;
+       vector  t1 = fmins, t2 = fmaxs;
+
+       trigger = spawn();
+       trigger.classname = "doortriggerfield";
+       trigger.movetype = MOVETYPE_NONE;
+       trigger.solid = SOLID_TRIGGER;
+       trigger.owner = self;
+       trigger.touch = door_trigger_touch;
+
+       setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
+       door_trigger_link(trigger);
+}
+
+#elif defined(CSQC)
+
+void ent_door_trigger()
+{
+       float entnum = ReadShort();
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+       self.mins_x = ReadCoord();
+       self.mins_y = ReadCoord();
+       self.mins_z = ReadCoord();
+       self.maxs_x = ReadCoord();
+       self.maxs_y = ReadCoord();
+       self.maxs_z = ReadCoord();
+       setsize(self, self.mins, self.maxs);
+
+       self.owner = findfloat(world, sv_entnum, entnum); // if owner doesn't exist, it shouldn't matter much
+       self.classname = "doortriggerfield";
+       self.movetype = MOVETYPE_NONE;
+       self.solid = SOLID_TRIGGER;
+       self.trigger_touch = door_trigger_touch;
+       self.draw = trigger_draw_generic;
+       self.drawmask = MASK_NORMAL;
+       self.move_time = time;
+}
+
+#endif
+#ifdef SVQC
+/*
+=============
+LinkDoors
+
+
+=============
+*/
+
+entity LinkDoors_nextent(entity cur, entity near, entity pass)
+{
+       while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
+       {
+       }
+       return cur;
+}
+
+float LinkDoors_isconnected(entity e1, entity e2, entity pass)
+{
+       float DELTA = 4;
+       if((e1.absmin_x > e2.absmax_x + DELTA)
+       || (e1.absmin_y > e2.absmax_y + DELTA)
+       || (e1.absmin_z > e2.absmax_z + DELTA)
+       || (e2.absmin_x > e1.absmax_x + DELTA)
+       || (e2.absmin_y > e1.absmax_y + DELTA)
+       || (e2.absmin_z > e1.absmax_z + DELTA)
+       ) { return false; }
+       return true;
+}
+
+void door_link();
+void LinkDoors()
+{
+       entity  t;
+       vector  cmins, cmaxs;
+
+       door_link();
+
+       if (self.enemy)
+               return;         // already linked by another door
+       if (self.spawnflags & 4)
+       {
+               self.owner = self.enemy = self;
+
+               if (self.health)
+                       return;
+               IFTARGETED
+                       return;
+               if (self.items)
+                       return;
+               spawn_field(self.absmin, self.absmax);
+
+               return;         // don't want to link this door
+       }
+
+       FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
+
+       // set owner, and make a loop of the chain
+       dprint("LinkDoors: linking doors:");
+       for(t = self; ; t = t.enemy)
+       {
+               dprint(" ", etos(t));
+               t.owner = self;
+               if(t.enemy == world)
+               {
+                       t.enemy = self;
+                       break;
+               }
+       }
+       dprint("\n");
+
+       // collect health, targetname, message, size
+       cmins = self.absmin;
+       cmaxs = self.absmax;
+       for(t = self; ; t = t.enemy)
+       {
+               if(t.health && !self.health)
+                       self.health = t.health;
+               if((t.targetname != "") && (self.targetname == ""))
+                       self.targetname = t.targetname;
+               if((t.message != "") && (self.message == ""))
+                       self.message = t.message;
+               if (t.absmin_x < cmins_x)
+                       cmins_x = t.absmin_x;
+               if (t.absmin_y < cmins_y)
+                       cmins_y = t.absmin_y;
+               if (t.absmin_z < cmins_z)
+                       cmins_z = t.absmin_z;
+               if (t.absmax_x > cmaxs_x)
+                       cmaxs_x = t.absmax_x;
+               if (t.absmax_y > cmaxs_y)
+                       cmaxs_y = t.absmax_y;
+               if (t.absmax_z > cmaxs_z)
+                       cmaxs_z = t.absmax_z;
+               if(t.enemy == self)
+                       break;
+       }
+
+       // distribute health, targetname, message
+       for(t = self; t; t = t.enemy)
+       {
+               t.health = self.health;
+               t.targetname = self.targetname;
+               t.message = self.message;
+               if(t.enemy == self)
+                       break;
+       }
+
+       // shootable, or triggered doors just needed the owner/enemy links,
+       // they don't spawn a field
+
+       if (self.health)
+               return;
+       IFTARGETED
+               return;
+       if (self.items)
+               return;
+
+       spawn_field(cmins, cmaxs);
+}
+
+
+/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
+if two doors touch, they are assumed to be connected and operate as a unit.
+
+TOGGLE causes the door to wait in both the start and end states for a trigger event.
+
+START_OPEN causes the door to move to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
+
+GOLD_KEY causes the door to open only if the activator holds a gold key.
+
+SILVER_KEY causes the door to open only if the activator holds a silver key.
+
+"message"      is printed when the door is touched if it is a trigger door and it hasn't been fired yet
+"angle"                determines the opening direction
+"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
+"health"       if set, door must be shot open
+"speed"                movement speed (100 default)
+"wait"         wait before returning (3 default, -1 = never return)
+"lip"          lip remaining at end of move (8 default)
+"dmg"          damage to inflict when blocked (2 default)
+"sounds"
+0)     no sound
+1)     stone
+2)     base
+3)     stone chain
+4)     screechy metal
+FIXME: only one sound set available at the time being
+
+*/
+
+float door_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_DOOR);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               WriteString(MSG_ENTITY, self.classname);
+               WriteByte(MSG_ENTITY, self.spawnflags);
+               WriteShort(MSG_ENTITY, ((self.owner == self || !self.owner) ? -1 : num_for_edict(self.owner)));
+               WriteShort(MSG_ENTITY, ((self.enemy == self || !self.enemy) ? -1 : num_for_edict(self.enemy)));
+               WriteShort(MSG_ENTITY, num_for_edict(self));
+
+               WriteByte(MSG_ENTITY, self.scale);
+
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               WriteString(MSG_ENTITY, self.model);
+
+               WriteCoord(MSG_ENTITY, self.mins_x);
+               WriteCoord(MSG_ENTITY, self.mins_y);
+               WriteCoord(MSG_ENTITY, self.mins_z);
+               WriteCoord(MSG_ENTITY, self.maxs_x);
+               WriteCoord(MSG_ENTITY, self.maxs_y);
+               WriteCoord(MSG_ENTITY, self.maxs_z);
+
+               WriteCoord(MSG_ENTITY, self.movedir_x);
+               WriteCoord(MSG_ENTITY, self.movedir_y);
+               WriteCoord(MSG_ENTITY, self.movedir_z);
+
+               WriteAngle(MSG_ENTITY, self.angles_x);
+               WriteAngle(MSG_ENTITY, self.angles_y);
+               WriteAngle(MSG_ENTITY, self.angles_z);
+
+               WriteCoord(MSG_ENTITY, self.pos1_x);
+               WriteCoord(MSG_ENTITY, self.pos1_y);
+               WriteCoord(MSG_ENTITY, self.pos1_z);
+               WriteCoord(MSG_ENTITY, self.pos2_x);
+               WriteCoord(MSG_ENTITY, self.pos2_y);
+               WriteCoord(MSG_ENTITY, self.pos2_z);
+
+               WriteCoord(MSG_ENTITY, self.size_x);
+               WriteCoord(MSG_ENTITY, self.size_y);
+               WriteCoord(MSG_ENTITY, self.size_z);
+
+               WriteShort(MSG_ENTITY, self.wait);
+               WriteShort(MSG_ENTITY, self.speed);
+               WriteByte(MSG_ENTITY, self.lip);
+               WriteByte(MSG_ENTITY, self.state);
+               WriteShort(MSG_ENTITY, self.ltime);
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               // client makes use of this, we do not
+       }
+
+       if(sf & SF_TRIGGER_UPDATE)
+       {
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               WriteCoord(MSG_ENTITY, self.pos1_x);
+               WriteCoord(MSG_ENTITY, self.pos1_y);
+               WriteCoord(MSG_ENTITY, self.pos1_z);
+               WriteCoord(MSG_ENTITY, self.pos2_x);
+               WriteCoord(MSG_ENTITY, self.pos2_y);
+               WriteCoord(MSG_ENTITY, self.pos2_z);
+       }
+
+       return true;
+}
+
+void door_link()
+{
+       // set size now, as everything is loaded
+       FixSize(self);
+       Net_LinkEntity(self, false, 0, door_send);
+}
+
+void door_init_startopen()
+{
+       setorigin (self, self.pos2);
+       self.pos2 = self.pos1;
+       self.pos1 = self.origin;
+
+       self.SendFlags |= SF_TRIGGER_UPDATE;
+}
+
+#endif
+
+void door_reset()
+{
+       setorigin(self, self.pos1);
+       self.velocity = '0 0 0';
+       self.state = STATE_BOTTOM;
+       self.think = func_null;
+       self.nextthink = 0;
+
+#ifdef SVQC
+       self.SendFlags |= SF_TRIGGER_RESET;
+#endif
+}
+
+#ifdef SVQC
+
+// spawnflags require key (for now only func_door)
+void spawnfunc_func_door()
+{
+       // Quake 1 keys compatibility
+       if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
+               self.itemkeys |= ITEM_KEY_BIT(0);
+       if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
+               self.itemkeys |= ITEM_KEY_BIT(1);
+
+       SetMovedir ();
+
+       self.max_health = self.health;
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+       self.classname = "door";
+
+       self.blocked = door_blocked;
+       self.use = door_use;
+
+       if(self.dmg && (self.message == ""))
+               self.message = "was squished";
+       if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+
+       if (self.sounds > 0)
+       {
+               precache_sound ("plats/medplat1.wav");
+               precache_sound ("plats/medplat2.wav");
+               self.noise2 = "plats/medplat1.wav";
+               self.noise1 = "plats/medplat2.wav";
+       }
+
+       if (!self.speed)
+               self.speed = 100;
+       if (!self.wait)
+               self.wait = 3;
+       if (!self.lip)
+               self.lip = 8;
+
+       self.pos1 = self.origin;
+       self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
+
+// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
+// but spawn in the open position
+       if (self.spawnflags & DOOR_START_OPEN)
+               InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
+
+       self.state = STATE_BOTTOM;
+
+       if (self.health)
+       {
+               self.takedamage = DAMAGE_YES;
+               self.event_damage = door_damage;
+       }
+
+       if (self.items)
+               self.wait = -1;
+
+       self.touch = door_touch;
+
+// LinkDoors can't be done until all of the doors have been spawned, so
+// the sizes can be detected properly.
+       InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
+
+       self.reset = door_reset;
+}
+
+#elif defined(CSQC)
+
+void ent_door()
+{
+       float sf = ReadByte();
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               self.classname = strzone(ReadString());
+               self.spawnflags = ReadByte();
+               float myowner = ReadShort();
+               float myenemy = ReadShort();
+               self.sv_entnum = ReadShort();
+
+               self.scale = ReadByte();
+
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+
+               self.mdl = strzone(ReadString());
+               setmodel(self, self.mdl);
+
+               self.mins_x = ReadCoord();
+               self.mins_y = ReadCoord();
+               self.mins_z = ReadCoord();
+               self.maxs_x = ReadCoord();
+               self.maxs_y = ReadCoord();
+               self.maxs_z = ReadCoord();
+               setsize(self, self.mins, self.maxs);
+
+               self.movedir_x = ReadCoord();
+               self.movedir_y = ReadCoord();
+               self.movedir_z = ReadCoord();
+
+               self.angles_x = ReadAngle();
+               self.angles_y = ReadAngle();
+               self.angles_z = ReadAngle();
+
+               self.pos1_x = ReadCoord();
+               self.pos1_y = ReadCoord();
+               self.pos1_z = ReadCoord();
+               self.pos2_x = ReadCoord();
+               self.pos2_y = ReadCoord();
+               self.pos2_z = ReadCoord();
+
+               self.size_x = ReadCoord();
+               self.size_y = ReadCoord();
+               self.size_z = ReadCoord();
+
+               self.wait = ReadShort();
+               self.speed = ReadShort();
+               self.lip = ReadByte();
+               self.state = ReadByte();
+               self.ltime = ReadShort();
+
+               self.movetype = MOVETYPE_PUSH;
+               self.solid = SOLID_BSP;
+               self.trigger_touch = door_touch;
+               self.draw = trigger_draw_generic;
+               self.drawmask = MASK_NORMAL;
+               self.move_time = time;
+               self.use = door_use;
+               self.blocked = door_blocked;
+
+               print(ftos(self.entnum), " ", ftos(self.sv_entnum), "\n");
+
+               self.owner = ((myowner == -1) ? self : findfloat(world, sv_entnum, myowner));
+               self.enemy = ((myenemy == -1) ? self : findfloat(world, sv_entnum, myenemy));
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               door_reset();
+       }
+
+       if(sf & SF_TRIGGER_UPDATE)
+       {
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+
+               self.pos1_x = ReadCoord();
+               self.pos1_y = ReadCoord();
+               self.pos1_z = ReadCoord();
+               self.pos2_x = ReadCoord();
+               self.pos2_y = ReadCoord();
+               self.pos2_z = ReadCoord();
+       }
+}
+
+#endif
diff --git a/qcsrc/common/triggers/func/door.qh b/qcsrc/common/triggers/func/door.qh
new file mode 100644 (file)
index 0000000..cc508e8
--- /dev/null
@@ -0,0 +1,18 @@
+// door constants
+const float DOOR_START_OPEN = 1;
+const float DOOR_DONT_LINK = 4;
+const float DOOR_TOGGLE = 32;
+
+const float DOOR_NOSPLASH = 256; // generic anti-splashdamage spawnflag
+
+const float SPAWNFLAGS_GOLD_KEY = 8;
+const float SPAWNFLAGS_SILVER_KEY = 16;
+
+#ifdef CSQC
+// stuff for preload
+void ent_door();
+void ent_door_trigger();
+
+// abused
+.float attack_finished_single;
+#endif
diff --git a/qcsrc/common/triggers/func/door_rotating.qc b/qcsrc/common/triggers/func/door_rotating.qc
new file mode 100644 (file)
index 0000000..bdf05a0
--- /dev/null
@@ -0,0 +1,126 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
+if two doors touch, they are assumed to be connected and operate as a unit.
+
+TOGGLE causes the door to wait in both the start and end states for a trigger event.
+
+BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
+The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
+must have set trigger_reverse to 1.
+BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
+
+START_OPEN causes the door to move to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).
+
+"message"      is printed when the door is touched if it is a trigger door and it hasn't been fired yet
+"angle"                determines the destination angle for opening. negative values reverse the direction.
+"targetname"    if set, no touch field will be spawned and a remote button or trigger field activates the door.
+"health"       if set, door must be shot open
+"speed"                movement speed (100 default)
+"wait"         wait before returning (3 default, -1 = never return)
+"dmg"          damage to inflict when blocked (2 default)
+"sounds"
+0)     no sound
+1)     stone
+2)     base
+3)     stone chain
+4)     screechy metal
+FIXME: only one sound set available at the time being
+*/
+
+void door_rotating_reset()
+{
+       self.angles = self.pos1;
+       self.avelocity = '0 0 0';
+       self.state = STATE_BOTTOM;
+       self.think = func_null;
+       self.nextthink = 0;
+}
+
+void door_rotating_init_startopen()
+{
+       self.angles = self.movedir;
+       self.pos2 = '0 0 0';
+       self.pos1 = self.movedir;
+}
+
+
+void spawnfunc_func_door_rotating()
+{
+
+       //if (!self.deathtype) // map makers can override this
+       //      self.deathtype = " got in the way";
+
+       // I abuse "movedir" for denoting the axis for now
+       if (self.spawnflags & 64) // X (untested)
+               self.movedir = '0 0 1';
+       else if (self.spawnflags & 128) // Y (untested)
+               self.movedir = '1 0 0';
+       else // Z
+               self.movedir = '0 1 0';
+
+       if (self.angles_y==0) self.angles_y = 90;
+
+       self.movedir = self.movedir * self.angles_y;
+       self.angles = '0 0 0';
+
+       self.max_health = self.health;
+       self.avelocity = self.movedir;
+       if (!InitMovingBrushTrigger())
+               return;
+       self.velocity = '0 0 0';
+       //self.effects |= EF_LOWPRECISION;
+       self.classname = "door_rotating";
+
+       self.blocked = door_blocked;
+       self.use = door_use;
+
+    if(self.spawnflags & 8)
+        self.dmg = 10000;
+
+    if(self.dmg && (self.message == ""))
+               self.message = "was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+
+    if (self.sounds > 0)
+       {
+               precache_sound ("plats/medplat1.wav");
+               precache_sound ("plats/medplat2.wav");
+               self.noise2 = "plats/medplat1.wav";
+               self.noise1 = "plats/medplat2.wav";
+       }
+
+       if (!self.speed)
+               self.speed = 50;
+       if (!self.wait)
+               self.wait = 1;
+       self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
+
+       self.pos1 = '0 0 0';
+       self.pos2 = self.movedir;
+
+// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
+// but spawn in the open position
+       if (self.spawnflags & DOOR_START_OPEN)
+               InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
+
+       self.state = STATE_BOTTOM;
+
+       if (self.health)
+       {
+               self.takedamage = DAMAGE_YES;
+               self.event_damage = door_damage;
+       }
+
+       if (self.items)
+               self.wait = -1;
+
+       self.touch = door_touch;
+
+// LinkDoors can't be done until all of the doors have been spawned, so
+// the sizes can be detected properly.
+       InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
+
+       self.reset = door_rotating_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/func/door_secret.qc b/qcsrc/common/triggers/func/door_secret.qc
new file mode 100644 (file)
index 0000000..307fb77
--- /dev/null
@@ -0,0 +1,236 @@
+#ifdef SVQC
+void() fd_secret_move1;
+void() fd_secret_move2;
+void() fd_secret_move3;
+void() fd_secret_move4;
+void() fd_secret_move5;
+void() fd_secret_move6;
+void() fd_secret_done;
+
+const float SECRET_OPEN_ONCE = 1;              // stays open
+const float SECRET_1ST_LEFT = 2;               // 1st move is left of arrow
+const float SECRET_1ST_DOWN = 4;               // 1st move is down from arrow
+const float SECRET_NO_SHOOT = 8;               // only opened by trigger
+const float SECRET_YES_SHOOT = 16;     // shootable even if targeted
+
+void fd_secret_use()
+{
+       float temp;
+       string message_save;
+
+       self.health = 10000;
+       self.bot_attack = true;
+
+       // exit if still moving around...
+       if (self.origin != self.oldorigin)
+               return;
+
+       message_save = self.message;
+       self.message = ""; // no more message
+       SUB_UseTargets();                               // fire all targets / killtargets
+       self.message = message_save;
+
+       self.velocity = '0 0 0';
+
+       // Make a sound, wait a little...
+
+       if (self.noise1 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.nextthink = self.ltime + 0.1;
+
+       temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
+       makevectors(self.mangle);
+
+       if (!self.t_width)
+       {
+               if (self.spawnflags & SECRET_1ST_DOWN)
+                       self.t_width = fabs(v_up * self.size);
+               else
+                       self.t_width = fabs(v_right * self.size);
+       }
+
+       if (!self.t_length)
+               self.t_length = fabs(v_forward * self.size);
+
+       if (self.spawnflags & SECRET_1ST_DOWN)
+               self.dest1 = self.origin - v_up * self.t_width;
+       else
+               self.dest1 = self.origin + v_right * (self.t_width * temp);
+
+       self.dest2 = self.dest1 + v_forward * self.t_length;
+       SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
+       if (self.noise2 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+}
+
+void fd_secret_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       fd_secret_use();
+}
+
+// Wait after first movement...
+void fd_secret_move1()
+{
+       self.nextthink = self.ltime + 1.0;
+       self.think = fd_secret_move2;
+       if (self.noise3 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+}
+
+// Start moving sideways w/sound...
+void fd_secret_move2()
+{
+       if (self.noise2 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
+}
+
+// Wait here until time to go back...
+void fd_secret_move3()
+{
+       if (self.noise3 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+       if (!(self.spawnflags & SECRET_OPEN_ONCE))
+       {
+               self.nextthink = self.ltime + self.wait;
+               self.think = fd_secret_move4;
+       }
+}
+
+// Move backward...
+void fd_secret_move4()
+{
+       if (self.noise2 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
+}
+
+// Wait 1 second...
+void fd_secret_move5()
+{
+       self.nextthink = self.ltime + 1.0;
+       self.think = fd_secret_move6;
+       if (self.noise3 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+}
+
+void fd_secret_move6()
+{
+       if (self.noise2 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
+       SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
+}
+
+void fd_secret_done()
+{
+       if (self.spawnflags&SECRET_YES_SHOOT)
+       {
+               self.health = 10000;
+               self.takedamage = DAMAGE_YES;
+               //self.th_pain = fd_secret_use;
+       }
+       if (self.noise3 != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
+}
+
+void secret_blocked()
+{
+       if (time < self.attack_finished_single)
+               return;
+       self.attack_finished_single = time + 0.5;
+       //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
+}
+
+/*
+==============
+secret_touch
+
+Prints messages
+================
+*/
+void secret_touch()
+{
+       if (!other.iscreature)
+               return;
+       if (self.attack_finished_single > time)
+               return;
+
+       self.attack_finished_single = time + 2;
+
+       if (self.message)
+       {
+               if (IS_CLIENT(other))
+                       centerprint(other, self.message);
+               play2(other, "misc/talk.wav");
+       }
+}
+
+void secret_reset()
+{
+       if (self.spawnflags&SECRET_YES_SHOOT)
+       {
+               self.health = 10000;
+               self.takedamage = DAMAGE_YES;
+       }
+       setorigin(self, self.oldorigin);
+       self.think = func_null;
+       self.nextthink = 0;
+}
+
+/*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
+Basic secret door. Slides back, then to the side. Angle determines direction.
+wait  = # of seconds before coming back
+1st_left = 1st move is left of arrow
+1st_down = 1st move is down from arrow
+always_shoot = even if targeted, keep shootable
+t_width = override WIDTH to move back (or height if going down)
+t_length = override LENGTH to move sideways
+"dmg"          damage to inflict when blocked (2 default)
+
+If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
+"sounds"
+1) medieval
+2) metal
+3) base
+*/
+
+void spawnfunc_func_door_secret()
+{
+       /*if (!self.deathtype) // map makers can override this
+               self.deathtype = " got in the way";*/
+
+       if (!self.dmg)
+               self.dmg = 2;
+
+       // Magic formula...
+       self.mangle = self.angles;
+       self.angles = '0 0 0';
+       self.classname = "door";
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+
+       self.touch = secret_touch;
+       self.blocked = secret_blocked;
+       self.speed = 50;
+       self.use = fd_secret_use;
+       IFTARGETED
+       {
+       }
+       else
+               self.spawnflags |= SECRET_YES_SHOOT;
+
+       if(self.spawnflags&SECRET_YES_SHOOT)
+       {
+               self.health = 10000;
+               self.takedamage = DAMAGE_YES;
+               self.event_damage = fd_secret_damage;
+       }
+       self.oldorigin = self.origin;
+       if (!self.wait)
+               self.wait = 5;          // 5 seconds before closing
+
+       self.reset = secret_reset;
+       secret_reset();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/fourier.qc b/qcsrc/common/triggers/func/fourier.qc
new file mode 100644 (file)
index 0000000..b6ba9cd
--- /dev/null
@@ -0,0 +1,89 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
+Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
+netname: list of <frequencymultiplier> <phase> <x> <y> <z> quadruples, separated by spaces; note that phase 0 represents a sine wave, and phase 0.25 a cosine wave (by default, it uses 1 0 0 0 1, to match func_bobbing's defaults
+speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
+height: amplitude modifier (default 32)
+phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
+noise: path/name of looping .wav file to play.
+dmg: Do this mutch dmg every .dmgtime intervall when blocked
+dmgtime: See above.
+*/
+
+void func_fourier_controller_think()
+{
+       vector v;
+       float n, i, t;
+
+       self.nextthink = time + 0.1;
+       if(self.owner.active != ACTIVE_ACTIVE)
+       {
+               self.owner.velocity = '0 0 0';
+               return;
+       }
+
+
+       n = floor((tokenize_console(self.owner.netname)) / 5);
+       t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
+
+       v = self.owner.destvec;
+
+       for(i = 0; i < n; ++i)
+       {
+               makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
+               v = v + ('1 0 0' * stof(argv(i*5+2)) + '0 1 0' * stof(argv(i*5+3)) + '0 0 1' * stof(argv(i*5+4))) * self.owner.height * v_forward_y;
+       }
+
+       if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
+               // * 10 so it will arrive in 0.1 sec
+               self.owner.velocity = (v - self.owner.origin) * 10;
+}
+
+void spawnfunc_func_fourier()
+{
+       entity controller;
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+
+       if (!self.speed)
+               self.speed = 4;
+       if (!self.height)
+               self.height = 32;
+       self.destvec = self.origin;
+       self.cnt = 360 / self.speed;
+
+       self.blocked = generic_plat_blocked;
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       if(self.netname == "")
+               self.netname = "1 0 0 0 1";
+
+       if (!InitMovingBrushTrigger())
+               return;
+
+       self.active = ACTIVE_ACTIVE;
+
+       // wait for targets to spawn
+       controller = spawn();
+       controller.classname = "func_fourier_controller";
+       controller.owner = self;
+       controller.nextthink = time + 1;
+       controller.think = func_fourier_controller_think;
+       self.nextthink = self.ltime + 999999999;
+       self.think = SUB_NullThink; // for PushMove
+
+       // Savage: Reduce bandwith, critical on e.g. nexdm02
+       self.effects |= EF_LOWPRECISION;
+
+       // TODO make a reset function for this one
+}
+#endif
diff --git a/qcsrc/common/triggers/func/include.qc b/qcsrc/common/triggers/func/include.qc
new file mode 100644 (file)
index 0000000..290c2e9
--- /dev/null
@@ -0,0 +1,19 @@
+#include "include.qh"
+
+#include "bobbing.qc"
+#include "breakable.qc"
+#include "button.qc"
+#include "conveyor.qc"
+#include "door.qc"
+#include "door_rotating.qc"
+#include "door_secret.qc"
+#include "fourier.qc"
+#include "ladder.qc"
+#include "pendulum.qc"
+#include "plat.qc"
+#include "pointparticles.qc"
+#include "rainsnow.qc"
+#include "rotating.qc"
+#include "stardust.qc"
+#include "train.qc"
+#include "vectormamamam.qc"
diff --git a/qcsrc/common/triggers/func/include.qh b/qcsrc/common/triggers/func/include.qh
new file mode 100644 (file)
index 0000000..cca6f91
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef TRIGGERS_FUNC_INCLUDE_H
+#define TRIGGERS_FUNC_INCLUDE_H
+
+#include "conveyor.qh"
+#include "door.qh"
+#include "ladder.qh"
+#include "plat.qh"
+
+#endif
diff --git a/qcsrc/common/triggers/func/ladder.qc b/qcsrc/common/triggers/func/ladder.qc
new file mode 100644 (file)
index 0000000..483fef8
--- /dev/null
@@ -0,0 +1,107 @@
+void func_ladder_touch()
+{
+#ifdef SVQC
+       if (!other.iscreature)
+               return;
+       if (other.vehicle_flags & VHF_ISVEHICLE)
+               return;
+#endif
+#ifdef CSQC
+       if(other.classname != "csqcmodel")
+               return;
+#endif
+
+       EXACTTRIGGER_TOUCH;
+
+       other.ladder_time = time + 0.1;
+       other.ladder_entity = self;
+}
+
+#ifdef SVQC
+float func_ladder_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER);
+
+       WriteString(MSG_ENTITY, self.classname);
+       WriteByte(MSG_ENTITY, self.warpzone_isboxy);
+       WriteByte(MSG_ENTITY, self.skin);
+       WriteByte(MSG_ENTITY, self.speed);
+       WriteByte(MSG_ENTITY, self.scale);
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       WriteCoord(MSG_ENTITY, self.mins_x);
+       WriteCoord(MSG_ENTITY, self.mins_y);
+       WriteCoord(MSG_ENTITY, self.mins_z);
+       WriteCoord(MSG_ENTITY, self.maxs_x);
+       WriteCoord(MSG_ENTITY, self.maxs_y);
+       WriteCoord(MSG_ENTITY, self.maxs_z);
+
+       WriteCoord(MSG_ENTITY, self.movedir_x);
+       WriteCoord(MSG_ENTITY, self.movedir_y);
+       WriteCoord(MSG_ENTITY, self.movedir_z);
+
+       WriteCoord(MSG_ENTITY, self.angles_x);
+       WriteCoord(MSG_ENTITY, self.angles_y);
+       WriteCoord(MSG_ENTITY, self.angles_z);
+
+       return true;
+}
+
+void func_ladder_link()
+{
+       Net_LinkEntity(self, false, 0, func_ladder_send);
+}
+
+void spawnfunc_func_ladder()
+{
+       EXACTTRIGGER_INIT;
+       self.touch = func_ladder_touch;
+
+       func_ladder_link();
+}
+
+void spawnfunc_func_water()
+{
+       EXACTTRIGGER_INIT;
+       self.touch = func_ladder_touch;
+
+       func_ladder_link();
+}
+
+#elif defined(CSQC)
+.float speed;
+
+void ent_func_ladder()
+{
+       self.classname = strzone(ReadString());
+       self.warpzone_isboxy = ReadByte();
+       self.skin = ReadByte();
+       self.speed = ReadByte();
+       self.scale = ReadByte();
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+       self.mins_x = ReadCoord();
+       self.mins_y = ReadCoord();
+       self.mins_z = ReadCoord();
+       self.maxs_x = ReadCoord();
+       self.maxs_y = ReadCoord();
+       self.maxs_z = ReadCoord();
+       setsize(self, self.mins, self.maxs);
+       self.movedir_x = ReadCoord();
+       self.movedir_y = ReadCoord();
+       self.movedir_z = ReadCoord();
+       self.angles_x = ReadCoord();
+       self.angles_y = ReadCoord();
+       self.angles_z = ReadCoord();
+
+       self.solid = SOLID_TRIGGER;
+       self.draw = trigger_draw_generic;
+       self.trigger_touch = func_ladder_touch;
+       self.drawmask = MASK_NORMAL;
+       self.move_time = time;
+}
+#endif
diff --git a/qcsrc/common/triggers/func/ladder.qh b/qcsrc/common/triggers/func/ladder.qh
new file mode 100644 (file)
index 0000000..11eeeda
--- /dev/null
@@ -0,0 +1,6 @@
+.float ladder_time;
+.entity ladder_entity;
+
+#ifdef CSQC
+void ent_func_ladder();
+#endif
diff --git a/qcsrc/common/triggers/func/pendulum.qc b/qcsrc/common/triggers/func/pendulum.qc
new file mode 100644 (file)
index 0000000..503b459
--- /dev/null
@@ -0,0 +1,77 @@
+#ifdef SVQC
+.float freq;
+void func_pendulum_controller_think()
+{
+       float v;
+       self.nextthink = time + 0.1;
+
+       if (!(self.owner.active == ACTIVE_ACTIVE))
+       {
+               self.owner.avelocity_x = 0;
+               return;
+       }
+
+       // calculate sinewave using makevectors
+       makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
+       v = self.owner.speed * v_forward_y + self.cnt;
+       if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
+       {
+               // * 10 so it will arrive in 0.1 sec
+               self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
+       }
+}
+
+void spawnfunc_func_pendulum()
+{
+       entity controller;
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+
+       self.active = ACTIVE_ACTIVE;
+
+       // keys: angle, speed, phase, noise, freq
+
+       if(!self.speed)
+               self.speed = 30;
+       // not initializing self.dmg to 2, to allow damageless pendulum
+
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+       if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       self.blocked = generic_plat_blocked;
+
+       self.avelocity_z = 0.0000001;
+       if (!InitMovingBrushTrigger())
+               return;
+
+       if(!self.freq)
+       {
+               // find pendulum length (same formula as Q3A)
+               self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
+       }
+
+       // copy initial angle
+       self.cnt = self.angles_z;
+
+       // wait for targets to spawn
+       controller = spawn();
+       controller.classname = "func_pendulum_controller";
+       controller.owner = self;
+       controller.nextthink = time + 1;
+       controller.think = func_pendulum_controller_think;
+       self.nextthink = self.ltime + 999999999;
+       self.think = SUB_NullThink; // for PushMove
+
+       //self.effects |= EF_LOWPRECISION;
+
+       // TODO make a reset function for this one
+}
+#endif
diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc
new file mode 100644 (file)
index 0000000..5ac8b2a
--- /dev/null
@@ -0,0 +1,226 @@
+#ifdef SVQC
+void plat_delayedinit()
+{
+       plat_spawn_inside_trigger ();   // the "start moving" trigger
+}
+
+float plat_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_PLAT);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               WriteShort(MSG_ENTITY, num_for_edict(self));
+               WriteString(MSG_ENTITY, self.target);
+               WriteString(MSG_ENTITY, self.target2);
+               WriteString(MSG_ENTITY, self.target3);
+               WriteString(MSG_ENTITY, self.target4);
+               WriteString(MSG_ENTITY, self.targetname);
+
+               WriteByte(MSG_ENTITY, self.platmovetype_start);
+               WriteByte(MSG_ENTITY, self.platmovetype_turn);
+               WriteByte(MSG_ENTITY, self.platmovetype_end);
+
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               WriteString(MSG_ENTITY, self.model);
+
+               WriteCoord(MSG_ENTITY, self.mins_x);
+               WriteCoord(MSG_ENTITY, self.mins_y);
+               WriteCoord(MSG_ENTITY, self.mins_z);
+               WriteCoord(MSG_ENTITY, self.maxs_x);
+               WriteCoord(MSG_ENTITY, self.maxs_y);
+               WriteCoord(MSG_ENTITY, self.maxs_z);
+
+               WriteCoord(MSG_ENTITY, self.pos1_x);
+               WriteCoord(MSG_ENTITY, self.pos1_y);
+               WriteCoord(MSG_ENTITY, self.pos1_z);
+               WriteCoord(MSG_ENTITY, self.pos2_x);
+               WriteCoord(MSG_ENTITY, self.pos2_y);
+               WriteCoord(MSG_ENTITY, self.pos2_z);
+
+               WriteCoord(MSG_ENTITY, self.size_x);
+               WriteCoord(MSG_ENTITY, self.size_y);
+               WriteCoord(MSG_ENTITY, self.size_z);
+
+               WriteAngle(MSG_ENTITY, self.angles_x);
+               WriteAngle(MSG_ENTITY, self.angles_y);
+               WriteAngle(MSG_ENTITY, self.angles_z);
+
+               WriteAngle(MSG_ENTITY, self.mangle_x);
+               WriteAngle(MSG_ENTITY, self.mangle_y);
+               WriteAngle(MSG_ENTITY, self.mangle_z);
+
+               WriteShort(MSG_ENTITY, self.speed);
+               WriteShort(MSG_ENTITY, self.height);
+               WriteByte(MSG_ENTITY, self.lip);
+               WriteByte(MSG_ENTITY, self.state);
+
+               WriteShort(MSG_ENTITY, self.dmg);
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               // used on client
+       }
+
+       return true;
+}
+
+void plat_link()
+{
+       Net_LinkEntity(self, 0, false, plat_send);
+}
+
+void spawnfunc_func_plat()
+{
+       if (self.sounds == 0)
+               self.sounds = 2;
+
+    if(self.spawnflags & 4)
+        self.dmg = 10000;
+
+    if(self.dmg && (self.message == ""))
+               self.message = "was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+
+       if (self.sounds == 1)
+       {
+               precache_sound ("plats/plat1.wav");
+               precache_sound ("plats/plat2.wav");
+               self.noise = "plats/plat1.wav";
+               self.noise1 = "plats/plat2.wav";
+       }
+
+       if (self.sounds == 2)
+       {
+               precache_sound ("plats/medplat1.wav");
+               precache_sound ("plats/medplat2.wav");
+               self.noise = "plats/medplat1.wav";
+               self.noise1 = "plats/medplat2.wav";
+       }
+
+       if (self.sound1)
+       {
+               precache_sound (self.sound1);
+               self.noise = self.sound1;
+       }
+       if (self.sound2)
+       {
+               precache_sound (self.sound2);
+               self.noise1 = self.sound2;
+       }
+
+       self.mangle = self.angles;
+       self.angles = '0 0 0';
+
+       self.classname = "plat";
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+       setsize (self, self.mins , self.maxs);
+
+       self.blocked = plat_crush;
+
+       if (!self.speed)
+               self.speed = 150;
+       if (!self.lip)
+               self.lip = 16;
+       if (!self.height)
+               self.height = self.size_z - self.lip;
+
+       self.pos1 = self.origin;
+       self.pos2 = self.origin;
+       self.pos2_z = self.origin_z - self.height;
+
+       self.reset = plat_reset;
+       plat_reset();
+
+       plat_link();
+
+       InitializeEntity(self, plat_delayedinit, INITPRIO_FINDTARGET);
+}
+#elif defined(CSQC)
+void plat_draw()
+{
+
+}
+
+void ent_plat()
+{
+       float sf = ReadByte();
+
+       if(sf & SF_TRIGGER_INIT)
+       {
+               self.sv_entnum = ReadShort();
+               self.target = strzone(ReadString());
+               self.target2 = strzone(ReadString());
+               self.target3 = strzone(ReadString());
+               self.target4 = strzone(ReadString());
+               self.targetname = strzone(ReadString());
+
+               self.platmovetype_start = ReadByte();
+               self.platmovetype_turn = ReadByte();
+               self.platmovetype_end = ReadByte();
+
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+
+               self.model = strzone(ReadString());
+               setmodel(self, self.model);
+
+               self.mins_x = ReadCoord();
+               self.mins_y = ReadCoord();
+               self.mins_z = ReadCoord();
+               self.maxs_x = ReadCoord();
+               self.maxs_y = ReadCoord();
+               self.maxs_z = ReadCoord();
+               setsize(self, self.mins, self.maxs);
+
+               self.pos1_x = ReadCoord();
+               self.pos1_y = ReadCoord();
+               self.pos1_z = ReadCoord();
+               self.pos2_x = ReadCoord();
+               self.pos2_y = ReadCoord();
+               self.pos2_z = ReadCoord();
+
+               self.size_x = ReadCoord();
+               self.size_y = ReadCoord();
+               self.size_z = ReadCoord();
+
+               self.angles_x = ReadAngle();
+               self.angles_y = ReadAngle();
+               self.angles_z = ReadAngle();
+
+               self.mangle_x = ReadAngle();
+               self.mangle_y = ReadAngle();
+               self.mangle_z = ReadAngle();
+
+               self.speed = ReadShort();
+               self.height = ReadShort();
+               self.lip = ReadByte();
+               self.state = ReadByte();
+
+               self.dmg = ReadShort();
+
+               self.solid = SOLID_BSP;
+               self.movetype = MOVETYPE_PUSH;
+               self.drawmask = MASK_NORMAL;
+               self.draw = plat_draw;
+               self.use = plat_use;
+
+               plat_reset(); // also called here
+       }
+
+       if(sf & SF_TRIGGER_RESET)
+       {
+               plat_reset();
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/func/plat.qh b/qcsrc/common/triggers/func/plat.qh
new file mode 100644 (file)
index 0000000..e3562f4
--- /dev/null
@@ -0,0 +1,3 @@
+#ifdef CSQC
+void ent_plat();
+#endif
diff --git a/qcsrc/common/triggers/func/pointparticles.qc b/qcsrc/common/triggers/func/pointparticles.qc
new file mode 100644 (file)
index 0000000..896bd79
--- /dev/null
@@ -0,0 +1,180 @@
+#ifdef SVQC
+// NOTE: also contains func_sparks
+
+float pointparticles_SendEntity(entity to, float fl)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
+
+       // optional features to save space
+       fl = fl & 0x0F;
+       if(self.spawnflags & 2)
+               fl |= 0x10; // absolute count on toggle-on
+       if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
+               fl |= 0x20; // 4 bytes - saves CPU
+       if(self.waterlevel || self.count != 1)
+               fl |= 0x40; // 4 bytes - obscure features almost never used
+       if(self.mins != '0 0 0' || self.maxs != '0 0 0')
+               fl |= 0x80; // 14 bytes - saves lots of space
+
+       WriteByte(MSG_ENTITY, fl);
+       if(fl & 2)
+       {
+               if(self.state)
+                       WriteCoord(MSG_ENTITY, self.impulse);
+               else
+                       WriteCoord(MSG_ENTITY, 0); // off
+       }
+       if(fl & 4)
+       {
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+       }
+       if(fl & 1)
+       {
+               if(self.model != "null")
+               {
+                       WriteShort(MSG_ENTITY, self.modelindex);
+                       if(fl & 0x80)
+                       {
+                               WriteCoord(MSG_ENTITY, self.mins_x);
+                               WriteCoord(MSG_ENTITY, self.mins_y);
+                               WriteCoord(MSG_ENTITY, self.mins_z);
+                               WriteCoord(MSG_ENTITY, self.maxs_x);
+                               WriteCoord(MSG_ENTITY, self.maxs_y);
+                               WriteCoord(MSG_ENTITY, self.maxs_z);
+                       }
+               }
+               else
+               {
+                       WriteShort(MSG_ENTITY, 0);
+                       if(fl & 0x80)
+                       {
+                               WriteCoord(MSG_ENTITY, self.maxs_x);
+                               WriteCoord(MSG_ENTITY, self.maxs_y);
+                               WriteCoord(MSG_ENTITY, self.maxs_z);
+                       }
+               }
+               WriteShort(MSG_ENTITY, self.cnt);
+               if(fl & 0x20)
+               {
+                       WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
+                       WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
+               }
+               if(fl & 0x40)
+               {
+                       WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
+                       WriteByte(MSG_ENTITY, self.count * 16.0);
+               }
+               WriteString(MSG_ENTITY, self.noise);
+               if(self.noise != "")
+               {
+                       WriteByte(MSG_ENTITY, floor(self.atten * 64));
+                       WriteByte(MSG_ENTITY, floor(self.volume * 255));
+               }
+               WriteString(MSG_ENTITY, self.bgmscript);
+               if(self.bgmscript != "")
+               {
+                       WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
+                       WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
+                       WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
+                       WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
+               }
+       }
+       return 1;
+}
+
+void pointparticles_use()
+{
+       self.state = !self.state;
+       self.SendFlags |= 2;
+}
+
+void pointparticles_think()
+{
+       if(self.origin != self.oldorigin)
+       {
+               self.SendFlags |= 4;
+               self.oldorigin = self.origin;
+       }
+       self.nextthink = time;
+}
+
+void pointparticles_reset()
+{
+       if(self.spawnflags & 1)
+               self.state = 1;
+       else
+               self.state = 0;
+}
+
+void spawnfunc_func_pointparticles()
+{
+       if(self.model != "")
+               setmodel(self, self.model);
+       if(self.noise != "")
+               precache_sound (self.noise);
+
+       if(!self.bgmscriptsustain)
+               self.bgmscriptsustain = 1;
+       else if(self.bgmscriptsustain < 0)
+               self.bgmscriptsustain = 0;
+
+       if(!self.atten)
+               self.atten = ATTEN_NORM;
+       else if(self.atten < 0)
+               self.atten = 0;
+       if(!self.volume)
+               self.volume = 1;
+       if(!self.count)
+               self.count = 1;
+       if(!self.impulse)
+               self.impulse = 1;
+
+       if(!self.modelindex)
+       {
+               setorigin(self, self.origin + self.mins);
+               setsize(self, '0 0 0', self.maxs - self.mins);
+       }
+       if(!self.cnt)
+               self.cnt = particleeffectnum(self.mdl);
+
+       Net_LinkEntity(self, (self.spawnflags & 4), 0, pointparticles_SendEntity);
+
+       IFTARGETED
+       {
+               self.use = pointparticles_use;
+               self.reset = pointparticles_reset;
+               self.reset();
+       }
+       else
+               self.state = 1;
+       self.think = pointparticles_think;
+       self.nextthink = time;
+}
+
+void spawnfunc_func_sparks()
+{
+       // self.cnt is the amount of sparks that one burst will spawn
+       if(self.cnt < 1) {
+               self.cnt = 25.0; // nice default value
+       }
+
+       // self.wait is the probability that a sparkthink will spawn a spark shower
+       // range: 0 - 1, but 0 makes little sense, so...
+       if(self.wait < 0.05) {
+               self.wait = 0.25; // nice default value
+       }
+
+       self.count = self.cnt;
+       self.mins = '0 0 0';
+       self.maxs = '0 0 0';
+       self.velocity = '0 0 -1';
+       self.mdl = "TE_SPARK";
+       self.impulse = 10 * self.wait; // by default 2.5/sec
+       self.wait = 0;
+       self.cnt = 0; // use mdl
+
+       spawnfunc_func_pointparticles();
+}
+#endif
diff --git a/qcsrc/common/triggers/func/rainsnow.qc b/qcsrc/common/triggers/func/rainsnow.qc
new file mode 100644 (file)
index 0000000..e86b156
--- /dev/null
@@ -0,0 +1,92 @@
+#ifdef SVQC
+float rainsnow_SendEntity(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
+       WriteByte(MSG_ENTITY, self.state);
+       WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
+       WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
+       WriteCoord(MSG_ENTITY, self.origin_z + self.mins_z);
+       WriteCoord(MSG_ENTITY, self.maxs_x - self.mins_x);
+       WriteCoord(MSG_ENTITY, self.maxs_y - self.mins_y);
+       WriteCoord(MSG_ENTITY, self.maxs_z - self.mins_z);
+       WriteShort(MSG_ENTITY, compressShortVector(self.dest));
+       WriteShort(MSG_ENTITY, self.count);
+       WriteByte(MSG_ENTITY, self.cnt);
+       return 1;
+}
+
+/*QUAKED spawnfunc_func_rain (0 .5 .8) ?
+This is an invisible area like a trigger, which rain falls inside of.
+
+Keys:
+"velocity"
+ falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
+"cnt"
+ sets color of rain (default 12 - white)
+"count"
+ adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
+*/
+void spawnfunc_func_rain()
+{
+       self.dest = self.velocity;
+       self.velocity = '0 0 0';
+       if (!self.dest)
+               self.dest = '0 0 -700';
+       self.angles = '0 0 0';
+       self.movetype = MOVETYPE_NONE;
+       self.solid = SOLID_NOT;
+       SetBrushEntityModel();
+       if (!self.cnt)
+               self.cnt = 12;
+       if (!self.count)
+               self.count = 2000;
+       self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
+       if (self.count < 1)
+               self.count = 1;
+       if(self.count > 65535)
+               self.count = 65535;
+
+       self.state = 1; // 1 is rain, 0 is snow
+       self.Version = 1;
+
+       Net_LinkEntity(self, false, 0, rainsnow_SendEntity);
+}
+
+
+/*QUAKED spawnfunc_func_snow (0 .5 .8) ?
+This is an invisible area like a trigger, which snow falls inside of.
+
+Keys:
+"velocity"
+ falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
+"cnt"
+ sets color of rain (default 12 - white)
+"count"
+ adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
+*/
+void spawnfunc_func_snow()
+{
+       self.dest = self.velocity;
+       self.velocity = '0 0 0';
+       if (!self.dest)
+               self.dest = '0 0 -300';
+       self.angles = '0 0 0';
+       self.movetype = MOVETYPE_NONE;
+       self.solid = SOLID_NOT;
+       SetBrushEntityModel();
+       if (!self.cnt)
+               self.cnt = 12;
+       if (!self.count)
+               self.count = 2000;
+       self.count = 0.01 * self.count * (self.size_x / 1024) * (self.size_y / 1024);
+       if (self.count < 1)
+               self.count = 1;
+       if(self.count > 65535)
+               self.count = 65535;
+
+       self.state = 0; // 1 is rain, 0 is snow
+       self.Version = 1;
+
+       Net_LinkEntity(self, false, 0, rainsnow_SendEntity);
+}
+#endif
diff --git a/qcsrc/common/triggers/func/rotating.qc b/qcsrc/common/triggers/func/rotating.qc
new file mode 100644 (file)
index 0000000..2474f31
--- /dev/null
@@ -0,0 +1,77 @@
+#ifdef SVQC
+void func_rotating_setactive(float astate)
+{
+
+       if (astate == ACTIVE_TOGGLE)
+       {
+               if(self.active == ACTIVE_ACTIVE)
+                       self.active = ACTIVE_NOT;
+               else
+                       self.active = ACTIVE_ACTIVE;
+       }
+       else
+               self.active = astate;
+
+       if(self.active  == ACTIVE_NOT)
+               self.avelocity = '0 0 0';
+       else
+               self.avelocity = self.pos1;
+}
+
+/*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
+Brush model that spins in place on one axis (default Z).
+speed   : speed to rotate (in degrees per second)
+noise   : path/name of looping .wav file to play.
+dmg     : Do this mutch dmg every .dmgtime intervall when blocked
+dmgtime : See above.
+*/
+
+void spawnfunc_func_rotating()
+{
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               ambientsound(self.origin, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+
+       self.active = ACTIVE_ACTIVE;
+       self.setactive = func_rotating_setactive;
+
+       if (!self.speed)
+               self.speed = 100;
+       // FIXME: test if this turns the right way, then remove this comment (negate as needed)
+       if (self.spawnflags & 4) // X (untested)
+               self.avelocity = '0 0 1' * self.speed;
+       // FIXME: test if this turns the right way, then remove this comment (negate as needed)
+       else if (self.spawnflags & 8) // Y (untested)
+               self.avelocity = '1 0 0' * self.speed;
+       // FIXME: test if this turns the right way, then remove this comment (negate as needed)
+       else // Z
+               self.avelocity = '0 1 0' * self.speed;
+
+       self.pos1 = self.avelocity;
+
+    if(self.dmg && (self.message == ""))
+        self.message = " was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+
+
+    if(self.dmg && (!self.dmgtime))
+        self.dmgtime = 0.25;
+
+    self.dmgtime2 = time;
+
+       if (!InitMovingBrushTrigger())
+               return;
+       // no EF_LOWPRECISION here, as rounding angles is bad
+
+    self.blocked = generic_plat_blocked;
+
+       // wait for targets to spawn
+       self.nextthink = self.ltime + 999999999;
+       self.think = SUB_NullThink; // for PushMove
+
+       // TODO make a reset function for this one
+}
+#endif
diff --git a/qcsrc/common/triggers/func/stardust.qc b/qcsrc/common/triggers/func/stardust.qc
new file mode 100644 (file)
index 0000000..e8043fe
--- /dev/null
@@ -0,0 +1,6 @@
+#ifdef SVQC
+void spawnfunc_func_stardust()
+{
+       self.effects = EF_STARDUST;
+}
+#endif
diff --git a/qcsrc/common/triggers/func/train.qc b/qcsrc/common/triggers/func/train.qc
new file mode 100644 (file)
index 0000000..14da459
--- /dev/null
@@ -0,0 +1,165 @@
+#ifdef SVQC
+.float train_wait_turning;
+void() train_next;
+void train_wait()
+{
+       entity oldself;
+       oldself = self;
+       self = self.enemy;
+       SUB_UseTargets();
+       self = oldself;
+       self.enemy = world;
+
+       // if turning is enabled, the train will turn toward the next point while waiting
+       if(self.platmovetype_turn && !self.train_wait_turning)
+       {
+               entity targ, cp;
+               vector ang;
+               targ = find(world, targetname, self.target);
+               if((self.spawnflags & 1) && targ.curvetarget)
+                       cp = find(world, targetname, targ.curvetarget);
+               else
+                       cp = world;
+
+               if(cp) // bezier curves movement
+                       ang = cp.origin - (self.origin - self.view_ofs); // use the origin of the control point of the next path_corner
+               else // linear movement
+                       ang = targ.origin - (self.origin - self.view_ofs); // use the origin of the next path_corner
+               ang = vectoangles(ang);
+               ang_x = -ang_x; // flip up / down orientation
+
+               if(self.wait > 0) // slow turning
+                       SUB_CalcAngleMove(ang, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
+               else // instant turning
+                       SUB_CalcAngleMove(ang, TSPEED_TIME, 0.0000001, train_wait);
+               self.train_wait_turning = true;
+               return;
+       }
+
+       if(self.noise != "")
+               stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
+
+       if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
+       {
+               self.train_wait_turning = false;
+               train_next();
+       }
+       else
+       {
+               self.think = train_next;
+               self.nextthink = self.ltime + self.wait;
+       }
+}
+
+void train_next()
+{
+       entity targ, cp = world;
+       vector cp_org = '0 0 0';
+
+       targ = find(world, targetname, self.target);
+       self.target = targ.target;
+       if (self.spawnflags & 1)
+       {
+               if(targ.curvetarget)
+               {
+                       cp = find(world, targetname, targ.curvetarget); // get its second target (the control point)
+                       cp_org = cp.origin - self.view_ofs; // no control point found, assume a straight line to the destination
+               }
+       }
+       if (self.target == "")
+               objerror("train_next: no next target");
+       self.wait = targ.wait;
+       if (!self.wait)
+               self.wait = 0.1;
+
+       if(targ.platmovetype)
+       {
+               // this path_corner contains a movetype overrider, apply it
+               self.platmovetype_start = targ.platmovetype_start;
+               self.platmovetype_end = targ.platmovetype_end;
+       }
+       else
+       {
+               // this path_corner doesn't contain a movetype overrider, use the train's defaults
+               self.platmovetype_start = self.platmovetype_start_default;
+               self.platmovetype_end = self.platmovetype_end_default;
+       }
+
+       if (targ.speed)
+       {
+               if (cp)
+                       SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
+               else
+                       SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
+       }
+       else
+       {
+               if (cp)
+                       SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
+               else
+                       SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
+       }
+
+       if(self.noise != "")
+               sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+}
+
+void func_train_find()
+{
+       entity targ;
+       targ = find(world, targetname, self.target);
+       self.target = targ.target;
+       if (self.target == "")
+               objerror("func_train_find: no next target");
+       setorigin(self, targ.origin - self.view_ofs);
+       self.nextthink = self.ltime + 1;
+       self.think = train_next;
+}
+
+/*QUAKED spawnfunc_func_train (0 .5 .8) ?
+Ridable platform, targets spawnfunc_path_corner path to follow.
+speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
+target : targetname of first spawnfunc_path_corner (starts here)
+*/
+void spawnfunc_func_train()
+{
+       if (self.noise != "")
+               precache_sound(self.noise);
+
+       if (self.target == "")
+               objerror("func_train without a target");
+       if (!self.speed)
+               self.speed = 100;
+
+       if (!InitMovingBrushTrigger())
+               return;
+       self.effects |= EF_LOWPRECISION;
+       
+       if (self.spawnflags & 2)
+       {
+               self.platmovetype_turn = true;
+               self.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now
+       }
+       else
+               self.view_ofs = self.mins;
+
+       // wait for targets to spawn
+       InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
+
+       self.blocked = generic_plat_blocked;
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+    if(self.dmg && (self.message2 == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       if(!set_platmovetype(self, self.platmovetype))
+               return;
+       self.platmovetype_start_default = self.platmovetype_start;
+       self.platmovetype_end_default = self.platmovetype_end;
+
+       // TODO make a reset function for this one
+}
+#endif
diff --git a/qcsrc/common/triggers/func/vectormamamam.qc b/qcsrc/common/triggers/func/vectormamamam.qc
new file mode 100644 (file)
index 0000000..7d0d20f
--- /dev/null
@@ -0,0 +1,159 @@
+#ifdef SVQC
+// reusing some fields havocbots declared
+.entity wp00, wp01, wp02, wp03;
+
+.float targetfactor, target2factor, target3factor, target4factor;
+.vector targetnormal, target2normal, target3normal, target4normal;
+
+vector func_vectormamamam_origin(entity o, float t)
+{
+       vector v, p;
+       float f;
+       entity e;
+
+       f = o.spawnflags;
+       v = '0 0 0';
+
+       e = o.wp00;
+       if(e)
+       {
+               p = e.origin + t * e.velocity;
+               if(f & 1)
+                       v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
+               else
+                       v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
+       }
+
+       e = o.wp01;
+       if(e)
+       {
+               p = e.origin + t * e.velocity;
+               if(f & 2)
+                       v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
+               else
+                       v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
+       }
+
+       e = o.wp02;
+       if(e)
+       {
+               p = e.origin + t * e.velocity;
+               if(f & 4)
+                       v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
+               else
+                       v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
+       }
+
+       e = o.wp03;
+       if(e)
+       {
+               p = e.origin + t * e.velocity;
+               if(f & 8)
+                       v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
+               else
+                       v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
+       }
+
+       return v;
+}
+
+void func_vectormamamam_controller_think()
+{
+       self.nextthink = time + 0.1;
+
+       if(self.owner.active != ACTIVE_ACTIVE)
+       {
+               self.owner.velocity = '0 0 0';
+               return;
+       }
+
+       if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
+               self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
+}
+
+void func_vectormamamam_findtarget()
+{
+       if(self.target != "")
+               self.wp00 = find(world, targetname, self.target);
+
+       if(self.target2 != "")
+               self.wp01 = find(world, targetname, self.target2);
+
+       if(self.target3 != "")
+               self.wp02 = find(world, targetname, self.target3);
+
+       if(self.target4 != "")
+               self.wp03 = find(world, targetname, self.target4);
+
+       if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
+               objerror("No reference entity found, so there is nothing to move. Aborting.");
+
+       self.destvec = self.origin - func_vectormamamam_origin(self, 0);
+
+       entity controller;
+       controller = spawn();
+       controller.classname = "func_vectormamamam_controller";
+       controller.owner = self;
+       controller.nextthink = time + 1;
+       controller.think = func_vectormamamam_controller_think;
+}
+
+void spawnfunc_func_vectormamamam()
+{
+       if (self.noise != "")
+       {
+               precache_sound(self.noise);
+               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
+       }
+
+       if(!self.targetfactor)
+               self.targetfactor = 1;
+
+       if(!self.target2factor)
+               self.target2factor = 1;
+
+       if(!self.target3factor)
+               self.target3factor = 1;
+
+       if(!self.target4factor)
+               self.target4factor = 1;
+
+       if(vlen(self.targetnormal))
+               self.targetnormal = normalize(self.targetnormal);
+
+       if(vlen(self.target2normal))
+               self.target2normal = normalize(self.target2normal);
+
+       if(vlen(self.target3normal))
+               self.target3normal = normalize(self.target3normal);
+
+       if(vlen(self.target4normal))
+               self.target4normal = normalize(self.target4normal);
+
+       self.blocked = generic_plat_blocked;
+       if(self.dmg && (self.message == ""))
+               self.message = " was squished";
+    if(self.dmg && (self.message == ""))
+               self.message2 = "was squished by";
+       if(self.dmg && (!self.dmgtime))
+               self.dmgtime = 0.25;
+       self.dmgtime2 = time;
+
+       if(self.netname == "")
+               self.netname = "1 0 0 0 1";
+
+       if (!InitMovingBrushTrigger())
+               return;
+
+       // wait for targets to spawn
+       self.nextthink = self.ltime + 999999999;
+       self.think = SUB_NullThink; // for PushMove
+
+       // Savage: Reduce bandwith, critical on e.g. nexdm02
+       self.effects |= EF_LOWPRECISION;
+
+       self.active = ACTIVE_ACTIVE;
+
+       InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
+}
+#endif
diff --git a/qcsrc/common/triggers/include.qc b/qcsrc/common/triggers/include.qc
new file mode 100644 (file)
index 0000000..8189d3b
--- /dev/null
@@ -0,0 +1,19 @@
+#include "include.qh"
+
+// some required common stuff
+#include "subs.qc"
+#include "triggers.qc"
+#include "platforms.qc"
+
+// func
+#include "func/include.qc"
+
+// misc
+#include "misc/include.qc"
+
+// target
+#include "target/include.qc"
+
+// trigger
+#include "trigger/include.qc"
+
diff --git a/qcsrc/common/triggers/include.qh b/qcsrc/common/triggers/include.qh
new file mode 100644 (file)
index 0000000..1cd37db
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef TRIGGERS_INCLUDE_H
+#define TRIGGERS_INCLUDE_H
+
+// some required common stuff
+#ifdef CSQC
+       #include "../../server/item_key.qh"
+#endif
+#include "triggers.qh"
+#include "subs.qh"
+#include "platforms.qh"
+
+// func
+#include "func/include.qh"
+
+// misc
+#include "misc/include.qh"
+
+// target
+#include "target/include.qh"
+
+// trigger
+#include "trigger/include.qh"
+
+#endif
diff --git a/qcsrc/common/triggers/misc/corner.qc b/qcsrc/common/triggers/misc/corner.qc
new file mode 100644 (file)
index 0000000..82fe6bb
--- /dev/null
@@ -0,0 +1,9 @@
+#ifdef SVQC
+void spawnfunc_path_corner()
+{
+       // setup values for overriding train movement
+       // if a second value does not exist, both start and end speeds are the single value specified
+       if(!set_platmovetype(self, self.platmovetype))
+               return;
+}
+#endif
diff --git a/qcsrc/common/triggers/misc/follow.qc b/qcsrc/common/triggers/misc/follow.qc
new file mode 100644 (file)
index 0000000..aace453
--- /dev/null
@@ -0,0 +1,67 @@
+#ifdef SVQC
+void follow_init()
+{
+       entity src, dst;
+       src = world;
+       dst = world;
+       if(self.killtarget != "")
+               src = find(world, targetname, self.killtarget);
+       if(self.target != "")
+               dst = find(world, targetname, self.target);
+
+       if(!src && !dst)
+       {
+               objerror("follow: could not find target/killtarget");
+               return;
+       }
+
+       if(self.jointtype)
+       {
+               // already done :P entity must stay
+               self.aiment = src;
+               self.enemy = dst;
+       }
+       else if(!src || !dst)
+       {
+               objerror("follow: could not find target/killtarget");
+               return;
+       }
+       else if(self.spawnflags & 1)
+       {
+               // attach
+               if(self.spawnflags & 2)
+               {
+                       setattachment(dst, src, self.message);
+               }
+               else
+               {
+                       attach_sameorigin(dst, src, self.message);
+               }
+
+               dst.solid = SOLID_NOT; // solid doesn't work with attachment
+               remove(self);
+       }
+       else
+       {
+               if(self.spawnflags & 2)
+               {
+                       dst.movetype = MOVETYPE_FOLLOW;
+                       dst.aiment = src;
+                       // dst.punchangle = '0 0 0'; // keep unchanged
+                       dst.view_ofs = dst.origin;
+                       dst.v_angle = dst.angles;
+               }
+               else
+               {
+                       follow_sameorigin(dst, src);
+               }
+
+               remove(self);
+       }
+}
+
+void spawnfunc_misc_follow()
+{
+       InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
+}
+#endif
diff --git a/qcsrc/common/triggers/misc/include.qc b/qcsrc/common/triggers/misc/include.qc
new file mode 100644 (file)
index 0000000..8c5678f
--- /dev/null
@@ -0,0 +1,5 @@
+#include "include.qh"
+
+#include "corner.qc"
+#include "follow.qc"
+#include "laser.qc"
diff --git a/qcsrc/common/triggers/misc/include.qh b/qcsrc/common/triggers/misc/include.qh
new file mode 100644 (file)
index 0000000..6f43e2a
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef TRIGGERS_MISC_INCLUDE_H
+#define TRIGGERS_MISC_INCLUDE_H
+
+// nothing yet
+
+#endif
diff --git a/qcsrc/common/triggers/misc/laser.qc b/qcsrc/common/triggers/misc/laser.qc
new file mode 100644 (file)
index 0000000..2aaf399
--- /dev/null
@@ -0,0 +1,257 @@
+#ifdef SVQC
+.float modelscale;
+void misc_laser_aim()
+{
+       vector a;
+       if(self.enemy)
+       {
+               if(self.spawnflags & 2)
+               {
+                       if(self.enemy.origin != self.mangle)
+                       {
+                               self.mangle = self.enemy.origin;
+                               self.SendFlags |= 2;
+                       }
+               }
+               else
+               {
+                       a = vectoangles(self.enemy.origin - self.origin);
+                       a_x = -a_x;
+                       if(a != self.mangle)
+                       {
+                               self.mangle = a;
+                               self.SendFlags |= 2;
+                       }
+               }
+       }
+       else
+       {
+               if(self.angles != self.mangle)
+               {
+                       self.mangle = self.angles;
+                       self.SendFlags |= 2;
+               }
+       }
+       if(self.origin != self.oldorigin)
+       {
+               self.SendFlags |= 1;
+               self.oldorigin = self.origin;
+       }
+}
+
+void misc_laser_init()
+{
+       if(self.target != "")
+               self.enemy = find(world, targetname, self.target);
+}
+
+.entity pusher;
+void misc_laser_think()
+{
+       vector o;
+       entity oldself;
+       entity hitent;
+       vector hitloc;
+
+       self.nextthink = time;
+
+       if(!self.state)
+               return;
+
+       misc_laser_aim();
+
+       if(self.enemy)
+       {
+               o = self.enemy.origin;
+               if (!(self.spawnflags & 2))
+                       o = self.origin + normalize(o - self.origin) * 32768;
+       }
+       else
+       {
+               makevectors(self.mangle);
+               o = self.origin + v_forward * 32768;
+       }
+
+       if(self.dmg || self.enemy.target != "")
+       {
+               traceline(self.origin, o, MOVE_NORMAL, self);
+       }
+       hitent = trace_ent;
+       hitloc = trace_endpos;
+
+       if(self.enemy.target != "") // DETECTOR laser
+       {
+               if(trace_ent.iscreature)
+               {
+                       self.pusher = hitent;
+                       if(!self.count)
+                       {
+                               self.count = 1;
+
+                               oldself = self;
+                               self = self.enemy;
+                               activator = self.pusher;
+                               SUB_UseTargets();
+                               self = oldself;
+                       }
+               }
+               else
+               {
+                       if(self.count)
+                       {
+                               self.count = 0;
+
+                               oldself = self;
+                               self = self.enemy;
+                               activator = self.pusher;
+                               SUB_UseTargets();
+                               self = oldself;
+                       }
+               }
+       }
+
+       if(self.dmg)
+       {
+               if(self.team)
+                       if(((self.spawnflags & 8) == 0) == (self.team != hitent.team))
+                               return;
+               if(hitent.takedamage)
+                       Damage(hitent, self, self, ((self.dmg < 0) ? 100000 : (self.dmg * frametime)), DEATH_HURTTRIGGER, hitloc, '0 0 0');
+       }
+}
+
+float laser_SendEntity(entity to, float fl)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
+       fl = fl - (fl & 0xF0); // use that bit to indicate finite length laser
+       if(self.spawnflags & 2)
+               fl |= 0x80;
+       if(self.alpha)
+               fl |= 0x40;
+       if(self.scale != 1 || self.modelscale != 1)
+               fl |= 0x20;
+       if(self.spawnflags & 4)
+               fl |= 0x10;
+       WriteByte(MSG_ENTITY, fl);
+       if(fl & 1)
+       {
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+       }
+       if(fl & 8)
+       {
+               WriteByte(MSG_ENTITY, self.colormod_x * 255.0);
+               WriteByte(MSG_ENTITY, self.colormod_y * 255.0);
+               WriteByte(MSG_ENTITY, self.colormod_z * 255.0);
+               if(fl & 0x40)
+                       WriteByte(MSG_ENTITY, self.alpha * 255.0);
+               if(fl & 0x20)
+               {
+                       WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
+                       WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
+               }
+               if((fl & 0x80) || !(fl & 0x10)) // effect doesn't need sending if the laser is infinite and has collision testing turned off
+                       WriteShort(MSG_ENTITY, self.cnt + 1);
+       }
+       if(fl & 2)
+       {
+               if(fl & 0x80)
+               {
+                       WriteCoord(MSG_ENTITY, self.enemy.origin_x);
+                       WriteCoord(MSG_ENTITY, self.enemy.origin_y);
+                       WriteCoord(MSG_ENTITY, self.enemy.origin_z);
+               }
+               else
+               {
+                       WriteAngle(MSG_ENTITY, self.mangle_x);
+                       WriteAngle(MSG_ENTITY, self.mangle_y);
+               }
+       }
+       if(fl & 4)
+               WriteByte(MSG_ENTITY, self.state);
+       return 1;
+}
+
+/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
+Any object touching the beam will be hurt
+Keys:
+"target"
+ spawnfunc_target_position where the laser ends
+"mdl"
+ name of beam end effect to use
+"colormod"
+ color of the beam (default: red)
+"dmg"
+ damage per second (-1 for a laser that kills immediately)
+*/
+void laser_use()
+{
+       self.state = !self.state;
+       self.SendFlags |= 4;
+       misc_laser_aim();
+}
+
+void laser_reset()
+{
+       if(self.spawnflags & 1)
+               self.state = 1;
+       else
+               self.state = 0;
+}
+
+void spawnfunc_misc_laser()
+{
+       if(self.mdl)
+       {
+               if(self.mdl == "none")
+                       self.cnt = -1;
+               else
+               {
+                       self.cnt = particleeffectnum(self.mdl);
+                       if(self.cnt < 0)
+                               if(self.dmg)
+                                       self.cnt = particleeffectnum("laser_deadly");
+               }
+       }
+       else if(!self.cnt)
+       {
+               if(self.dmg)
+                       self.cnt = particleeffectnum("laser_deadly");
+               else
+                       self.cnt = -1;
+       }
+       if(self.cnt < 0)
+               self.cnt = -1;
+
+       if(self.colormod == '0 0 0')
+               if(!self.alpha)
+                       self.colormod = '1 0 0';
+       if(self.message == "")
+               self.message = "saw the light";
+       if (self.message2 == "")
+               self.message2 = "was pushed into a laser by";
+       if(!self.scale)
+               self.scale = 1;
+       if(!self.modelscale)
+               self.modelscale = 1;
+       else if(self.modelscale < 0)
+               self.modelscale = 0;
+       self.think = misc_laser_think;
+       self.nextthink = time;
+       InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
+
+       self.mangle = self.angles;
+
+       Net_LinkEntity(self, false, 0, laser_SendEntity);
+
+       IFTARGETED
+       {
+               self.reset = laser_reset;
+               laser_reset();
+               self.use = laser_use;
+       }
+       else
+               self.state = 1;
+}
+#endif
diff --git a/qcsrc/common/triggers/platforms.qc b/qcsrc/common/triggers/platforms.qc
new file mode 100644 (file)
index 0000000..fd4825e
--- /dev/null
@@ -0,0 +1,273 @@
+void generic_plat_blocked()
+{
+#ifdef SVQC
+       if(self.dmg && other.takedamage != DAMAGE_NO)
+       {
+               if(self.dmgtime2 < time)
+               {
+                       Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+                       self.dmgtime2 = time + self.dmgtime;
+               }
+
+               // Gib dead/dying stuff
+               if(other.deadflag != DEAD_NO)
+                       Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+       }
+#endif
+}
+
+#ifdef SVQC
+float plat_trigger_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_PLAT_TRIGGER);
+       WriteShort(MSG_ENTITY, num_for_edict(self.enemy));
+
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       WriteCoord(MSG_ENTITY, self.mins_x);
+       WriteCoord(MSG_ENTITY, self.mins_y);
+       WriteCoord(MSG_ENTITY, self.mins_z);
+       WriteCoord(MSG_ENTITY, self.maxs_x);
+       WriteCoord(MSG_ENTITY, self.maxs_y);
+       WriteCoord(MSG_ENTITY, self.maxs_z);
+       return true;
+}
+
+void plat_spawn_inside_trigger()
+{
+       entity trigger;
+       vector tmin, tmax;
+
+       trigger = spawn();
+       trigger.touch = plat_center_touch;
+       trigger.movetype = MOVETYPE_NONE;
+       trigger.solid = SOLID_TRIGGER;
+       trigger.enemy = self;
+
+       tmin = self.absmin + '25 25 0';
+       tmax = self.absmax - '25 25 -8';
+       tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
+       if (self.spawnflags & PLAT_LOW_TRIGGER)
+               tmax_z = tmin_z + 8;
+
+       if (self.size_x <= 50)
+       {
+               tmin_x = (self.mins_x + self.maxs_x) / 2;
+               tmax_x = tmin_x + 1;
+       }
+       if (self.size_y <= 50)
+       {
+               tmin_y = (self.mins_y + self.maxs_y) / 2;
+               tmax_y = tmin_y + 1;
+       }
+
+       if(tmin_x < tmax_x)
+               if(tmin_y < tmax_y)
+                       if(tmin_z < tmax_z)
+                       {
+                               setsize (trigger, tmin, tmax);
+                               Net_LinkEntity(trigger, false, 0, plat_trigger_send);
+                               return;
+                       }
+
+       // otherwise, something is fishy...
+       remove(trigger);
+       objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
+}
+#elif defined(CSQC)
+void ent_plat_trigger()
+{
+       float myenemy = ReadShort();
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+
+       self.mins_x = ReadCoord();
+       self.mins_y = ReadCoord();
+       self.mins_z = ReadCoord();
+       self.maxs_x = ReadCoord();
+       self.maxs_y = ReadCoord();
+       self.maxs_z = ReadCoord();
+       setsize(self, self.mins, self.maxs);
+
+       self.enemy = findfloat(world, sv_entnum, myenemy);
+       if(!self.enemy) { print("^1BAD BAD BAD!!!\n"); }
+       self.drawmask = MASK_NORMAL;
+       self.draw = trigger_draw_generic;
+       self.trigger_touch = plat_center_touch;
+       self.solid = SOLID_TRIGGER;
+}
+#endif
+
+void plat_hit_top()
+{
+       sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = 1;
+       self.think = plat_go_down;
+       self.nextthink = self.ltime + 3;
+}
+
+void plat_hit_bottom()
+{
+       sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
+       self.state = 2;
+}
+
+void plat_go_down()
+{
+       sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
+       self.state = 3;
+       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, plat_hit_bottom);
+}
+
+void plat_go_up()
+{
+       sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
+       self.state = 4;
+       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, plat_hit_top);
+}
+
+void plat_center_touch()
+{
+#ifdef SVQC
+       if (!other.iscreature)
+               return;
+
+       if (other.health <= 0)
+               return;
+#elif defined(CSQC)
+       if (!IS_PLAYER(other))
+               return;
+#endif
+
+#ifdef CSQC
+       print("Got this far\n");
+#endif
+
+       self = self.enemy;
+       if (self.state == 2)
+               plat_go_up ();
+       else if (self.state == 1)
+               self.nextthink = self.ltime + 1;        // delay going down
+}
+
+void plat_outside_touch()
+{
+#ifdef SVQC
+       if (!other.iscreature)
+               return;
+
+       if (other.health <= 0)
+               return;
+#elif defined(CSQC)
+       if (!IS_PLAYER(other))
+               return;
+#endif
+
+       self = self.enemy;
+       if (self.state == 1)
+               plat_go_down ();
+}
+
+void plat_trigger_use()
+{
+       if (self.think)
+               return;         // already activated
+       plat_go_down();
+}
+
+
+void plat_crush()
+{
+       if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO))
+       { // KIll Kill Kill!!
+#ifdef SVQC
+               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+#endif
+       }
+       else
+       {
+#ifdef SVQC
+               if((self.dmg) && (other.takedamage != DAMAGE_NO))
+               {   // Shall we bite?
+                       Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+                       // Gib dead/dying stuff
+                       if(other.deadflag != DEAD_NO)
+                               Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+#endif
+
+               if (self.state == 4)
+                       plat_go_down ();
+               else if (self.state == 3)
+                       plat_go_up ();
+       // when in other states, then the plat_crush event came delayed after
+       // plat state already had changed
+       // this isn't a bug per se!
+       }
+}
+
+void plat_use()
+{
+       self.use = func_null;
+       if (self.state != 4)
+               objerror ("plat_use: not in up state");
+       plat_go_down();
+}
+
+.string sound1, sound2;
+
+void plat_reset()
+{
+       IFTARGETED
+       {
+               setorigin (self, self.pos1);
+               self.state = 4;
+               self.use = plat_use;
+       }
+       else
+       {
+               setorigin (self, self.pos2);
+               self.state = 2;
+               self.use = plat_trigger_use;
+       }
+
+#ifdef SVQC
+       self.SendFlags |= SF_TRIGGER_RESET;
+#endif
+}
+
+#ifdef SVQC
+.float platmovetype_start_default, platmovetype_end_default;
+float set_platmovetype(entity e, string s)
+{
+       // sets platmovetype_start and platmovetype_end based on a string consisting of two values
+
+       float n;
+       n = tokenize_console(s);
+       if(n > 0)
+               e.platmovetype_start = stof(argv(0));
+       else
+               e.platmovetype_start = 0;
+
+       if(n > 1)
+               e.platmovetype_end = stof(argv(1));
+       else
+               e.platmovetype_end = e.platmovetype_start;
+
+       if(n > 2)
+               if(argv(2) == "force")
+                       return true; // no checking, return immediately
+
+       if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end))
+       {
+               objerror("Invalid platform move type; platform would go in reverse, which is not allowed.");
+               return false;
+       }
+
+       return true;
+}
+#endif
diff --git a/qcsrc/common/triggers/platforms.qh b/qcsrc/common/triggers/platforms.qh
new file mode 100644 (file)
index 0000000..69ad2fa
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef PLATFORMS_H
+#define PLATFORMS_H
+
+.float dmgtime2;
+
+void() plat_center_touch;
+void() plat_outside_touch;
+void() plat_trigger_use;
+void() plat_go_up;
+void() plat_go_down;
+void() plat_crush;
+const float PLAT_LOW_TRIGGER = 1;
+
+.float dmg;
+
+#ifdef CSQC
+void ent_plat_trigger();
+#endif
+
+#endif
diff --git a/qcsrc/common/triggers/subs.qc b/qcsrc/common/triggers/subs.qc
new file mode 100644 (file)
index 0000000..04860cd
--- /dev/null
@@ -0,0 +1,392 @@
+void SUB_NullThink(void) { }
+
+void()  SUB_CalcMoveDone;
+void() SUB_CalcAngleMoveDone;
+//void() SUB_UseTargets;
+
+/*
+==================
+SUB_Remove
+
+Remove self
+==================
+*/
+void SUB_Remove()
+{
+       remove (self);
+}
+
+/*
+==================
+SUB_Friction
+
+Applies some friction to self
+==================
+*/
+.float friction;
+void SUB_Friction (void)
+{
+       self.nextthink = time;
+       if(self.flags & FL_ONGROUND)
+               self.velocity = self.velocity * (1 - frametime * self.friction);
+}
+
+/*
+==================
+SUB_VanishOrRemove
+
+Makes client invisible or removes non-client
+==================
+*/
+void SUB_VanishOrRemove (entity ent)
+{
+       if (IS_CLIENT(ent))
+       {
+               // vanish
+               ent.alpha = -1;
+               ent.effects = 0;
+#ifdef SVQC
+               ent.glow_size = 0;
+               ent.pflags = 0;
+#endif
+       }
+       else
+       {
+               // remove
+               remove (ent);
+       }
+}
+
+void SUB_SetFade_Think (void)
+{
+       if(self.alpha == 0)
+               self.alpha = 1;
+       self.think = SUB_SetFade_Think;
+       self.nextthink = time;
+       self.alpha -= frametime * self.fade_rate;
+       if (self.alpha < 0.01)
+               SUB_VanishOrRemove(self);
+       else
+               self.nextthink = time;
+}
+
+/*
+==================
+SUB_SetFade
+
+Fade 'ent' out when time >= 'when'
+==================
+*/
+void SUB_SetFade (entity ent, float when, float fading_time)
+{
+       ent.fade_rate = 1/fading_time;
+       ent.think = SUB_SetFade_Think;
+       ent.nextthink = when;
+}
+
+/*
+=============
+SUB_CalcMove
+
+calculate self.velocity and self.nextthink to reach dest from
+self.origin traveling at speed
+===============
+*/
+void SUB_CalcMoveDone (void)
+{
+       // After moving, set origin to exact final destination
+
+       setorigin (self, self.finaldest);
+       self.velocity = '0 0 0';
+       self.nextthink = -1;
+       if (self.think1)
+               self.think1 ();
+}
+
+.float platmovetype_turn;
+void SUB_CalcMove_controller_think (void)
+{
+       entity oldself;
+       float traveltime;
+       float phasepos;
+       float nexttick;
+       vector delta;
+       vector delta2;
+       vector veloc;
+       vector angloc;
+       vector nextpos;
+       delta = self.destvec;
+       delta2 = self.destvec2;
+       if(time < self.animstate_endtime)
+       {
+               nexttick = time + PHYS_INPUT_FRAMETIME;
+
+               traveltime = self.animstate_endtime - self.animstate_starttime;
+               phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
+               phasepos = cubic_speedfunc(self.platmovetype_start, self.platmovetype_end, phasepos);
+               nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
+               // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
+
+               if(self.owner.platmovetype_turn)
+               {
+                       vector destangle;
+                       destangle = delta + 2 * delta2 * phasepos;
+                       destangle = vectoangles(destangle);
+                       destangle_x = -destangle_x; // flip up / down orientation
+
+                       // take the shortest distance for the angles
+                       self.owner.angles_x -= 360 * floor((self.owner.angles_x - destangle_x) / 360 + 0.5);
+                       self.owner.angles_y -= 360 * floor((self.owner.angles_y - destangle_y) / 360 + 0.5);
+                       self.owner.angles_z -= 360 * floor((self.owner.angles_z - destangle_z) / 360 + 0.5);
+                       angloc = destangle - self.owner.angles;
+                       angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
+                       self.owner.avelocity = angloc;
+               }
+               if(nexttick < self.animstate_endtime)
+                       veloc = nextpos - self.owner.origin;
+               else
+                       veloc = self.finaldest - self.owner.origin;
+               veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
+
+               self.owner.velocity = veloc;
+               self.nextthink = nexttick;
+       }
+       else
+       {
+               // derivative: delta + 2 * delta2 (e.g. for angle positioning)
+               oldself = self;
+               self.owner.think = self.think1;
+               self = self.owner;
+               remove(oldself);
+               self.think();
+       }
+}
+
+void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector destin)
+{
+       // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
+       // 2 * control * t - 2 * control * t * t + destin * t * t
+       // 2 * control * t + (destin - 2 * control) * t * t
+
+       controller.origin = org; // starting point
+       control -= org;
+       destin -= org;
+
+       controller.destvec = 2 * control; // control point
+       controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point
+       // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control)
+}
+
+void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector destin)
+{
+       // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
+       // 2 * control * t - 2 * control * t * t + destin * t * t
+       // 2 * control * t + (destin - 2 * control) * t * t
+
+       controller.origin = org; // starting point
+       destin -= org;
+
+       controller.destvec = destin; // end point
+       controller.destvec2 = '0 0 0';
+}
+
+float TSPEED_TIME = -1;
+float TSPEED_LINEAR = 0;
+float TSPEED_START = 1;
+float TSPEED_END = 2;
+// TODO average too?
+
+void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func)
+{
+       float   traveltime;
+       entity controller;
+
+       if (!tspeed)
+               objerror ("No speed is defined!");
+
+       self.think1 = func;
+       self.finaldest = tdest;
+       self.think = SUB_CalcMoveDone;
+
+       switch(tspeedtype)
+       {
+               default:
+               case TSPEED_START:
+                       traveltime = 2 * vlen(tcontrol - self.origin) / tspeed;
+                       break;
+               case TSPEED_END:
+                       traveltime = 2 * vlen(tcontrol - tdest)       / tspeed;
+                       break;
+               case TSPEED_LINEAR:
+                       traveltime = vlen(tdest - self.origin)        / tspeed;
+                       break;
+               case TSPEED_TIME:
+                       traveltime = tspeed;
+                       break;
+       }
+
+       if (traveltime < 0.1) // useless anim
+       {
+               self.velocity = '0 0 0';
+               self.nextthink = self.ltime + 0.1;
+               return;
+       }
+
+       controller = spawn();
+       controller.classname = "SUB_CalcMove_controller";
+       controller.owner = self;
+       controller.platmovetype = self.platmovetype;
+       controller.platmovetype_start = self.platmovetype_start;
+       controller.platmovetype_end = self.platmovetype_end;
+       SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest);
+       controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
+       controller.animstate_starttime = time;
+       controller.animstate_endtime = time + traveltime;
+       controller.think = SUB_CalcMove_controller_think;
+       controller.think1 = self.think;
+
+       // the thinking is now done by the controller
+       self.think = SUB_NullThink; // for PushMove
+       self.nextthink = self.ltime + traveltime;
+
+       // invoke controller
+       self = controller;
+       self.think();
+       self = self.owner;
+}
+
+void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func)
+{
+       vector  delta;
+       float   traveltime;
+
+       if (!tspeed)
+               objerror ("No speed is defined!");
+
+       self.think1 = func;
+       self.finaldest = tdest;
+       self.think = SUB_CalcMoveDone;
+
+       if (tdest == self.origin)
+       {
+               self.velocity = '0 0 0';
+               self.nextthink = self.ltime + 0.1;
+               return;
+       }
+
+       delta = tdest - self.origin;
+
+       switch(tspeedtype)
+       {
+               default:
+               case TSPEED_START:
+               case TSPEED_END:
+               case TSPEED_LINEAR:
+                       traveltime = vlen (delta) / tspeed;
+                       break;
+               case TSPEED_TIME:
+                       traveltime = tspeed;
+                       break;
+       }
+
+       // Very short animations don't really show off the effect
+       // of controlled animation, so let's just use linear movement.
+       // Alternatively entities can choose to specify non-controlled movement.
+        // The only currently implemented alternative movement is linear (value 1)
+       if (traveltime < 0.15 || (self.platmovetype_start == 1 && self.platmovetype_end == 1)) // is this correct?
+       {
+               self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
+               self.nextthink = self.ltime + traveltime;
+               return;
+       }
+
+       // now just run like a bezier curve...
+       SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
+}
+
+void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func)
+{
+       entity  oldself;
+
+       oldself = self;
+       self = ent;
+
+       SUB_CalcMove (tdest, tspeedtype, tspeed, func);
+
+       self = oldself;
+}
+
+/*
+=============
+SUB_CalcAngleMove
+
+calculate self.avelocity and self.nextthink to reach destangle from
+self.angles rotating
+
+The calling function should make sure self.think is valid
+===============
+*/
+void SUB_CalcAngleMoveDone (void)
+{
+       // After rotating, set angle to exact final angle
+       self.angles = self.finalangle;
+       self.avelocity = '0 0 0';
+       self.nextthink = -1;
+       if (self.think1)
+               self.think1 ();
+}
+
+// FIXME: I fixed this function only for rotation around the main axes
+void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func)
+{
+       vector  delta;
+       float   traveltime;
+
+       if (!tspeed)
+               objerror ("No speed is defined!");
+
+       // take the shortest distance for the angles
+       self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
+       self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
+       self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
+       delta = destangle - self.angles;
+
+       switch(tspeedtype)
+       {
+               default:
+               case TSPEED_START:
+               case TSPEED_END:
+               case TSPEED_LINEAR:
+                       traveltime = vlen (delta) / tspeed;
+                       break;
+               case TSPEED_TIME:
+                       traveltime = tspeed;
+                       break;
+       }
+
+       self.think1 = func;
+       self.finalangle = destangle;
+       self.think = SUB_CalcAngleMoveDone;
+
+       if (traveltime < 0.1)
+       {
+               self.avelocity = '0 0 0';
+               self.nextthink = self.ltime + 0.1;
+               return;
+       }
+
+       self.avelocity = delta * (1 / traveltime);
+       self.nextthink = self.ltime + traveltime;
+}
+
+void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
+{
+       entity  oldself;
+
+       oldself = self;
+       self = ent;
+
+       SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);
+
+       self = oldself;
+}
diff --git a/qcsrc/common/triggers/subs.qh b/qcsrc/common/triggers/subs.qh
new file mode 100644 (file)
index 0000000..bf09948
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef SUBS_H
+#define SUBS_H
+
+void SUB_Remove();
+void SUB_SetFade (entity ent, float when, float fading_time);
+void SUB_VanishOrRemove (entity ent);
+
+.vector                finaldest, finalangle;          //plat.qc stuff
+.void()                think1;
+.float state;
+.float         t_length, t_width;
+
+.vector destvec;
+.vector destvec2;
+
+// player animation state
+.float animstate_startframe;
+.float animstate_numframes;
+.float animstate_framerate;
+.float animstate_starttime;
+.float animstate_endtime;
+.float animstate_override;
+.float animstate_looping;
+
+.float delay;
+.float wait;
+.float lip;
+.float speed;
+.float sounds;
+.string  platmovetype;
+.float platmovetype_start, platmovetype_end;
+
+entity activator;
+
+.string killtarget;
+
+.vector        pos1, pos2;
+.vector        mangle;
+
+.string target2;
+.string target3;
+.string target4;
+.string curvetarget;
+.float target_random;
+.float trigger_reverse;
+
+// Keys player is holding
+.float itemkeys;
+// message delay for func_door locked by keys and key locks
+// this field is used on player entities
+.float key_door_messagetime;
+
+.vector dest1, dest2;
+
+#ifdef CSQC
+// this stuff is defined in the server side engine VM, so we must define it separately here
+.float takedamage;
+const float DAMAGE_NO  = 0;
+const float DAMAGE_YES = 1;
+const float DAMAGE_AIM = 2;
+
+float  STATE_TOP               = 0;
+float  STATE_BOTTOM    = 1;
+float  STATE_UP                = 2;
+float  STATE_DOWN              = 3;
+
+.string                noise, noise1, noise2, noise3;  // contains names of wavs to play
+
+.float         max_health;             // players maximum health is stored here
+#endif
+
+#endif
diff --git a/qcsrc/common/triggers/target/changelevel.qc b/qcsrc/common/triggers/target/changelevel.qc
new file mode 100644 (file)
index 0000000..1ec8cc9
--- /dev/null
@@ -0,0 +1,18 @@
+#ifdef SVQC
+.string chmap, gametype;
+void spawnfunc_target_changelevel_use()
+{
+       if(self.gametype != "")
+               MapInfo_SwitchGameType(MapInfo_Type_FromString(self.gametype));
+
+       if (self.chmap == "")
+               localcmd("endmatch\n");
+       else
+               localcmd(strcat("changelevel ", self.chmap, "\n"));
+}
+
+void spawnfunc_target_changelevel()
+{
+       self.use = spawnfunc_target_changelevel_use;
+}
+#endif
diff --git a/qcsrc/common/triggers/target/include.qc b/qcsrc/common/triggers/target/include.qc
new file mode 100644 (file)
index 0000000..3b8e024
--- /dev/null
@@ -0,0 +1,7 @@
+#include "include.qh"
+
+#include "changelevel.qc"
+#include "music.qc"
+#include "spawn.qc"
+#include "speaker.qc"
+#include "voicescript.qc"
diff --git a/qcsrc/common/triggers/target/include.qh b/qcsrc/common/triggers/target/include.qh
new file mode 100644 (file)
index 0000000..cadbf37
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef TRIGGERS_TARGET_INCLUDE_H
+#define TRIGGERS_TARGET_INCLUDE_H
+
+// nothing yet
+
+#endif
diff --git a/qcsrc/common/triggers/target/music.qc b/qcsrc/common/triggers/target/music.qc
new file mode 100644 (file)
index 0000000..fb38dad
--- /dev/null
@@ -0,0 +1,146 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../../dpdefs/progsdefs.qh"
+    #include "../../../dpdefs/dpextensions.qh"
+    #include "../../constants.qh"
+    #include "../../../server/constants.qh"
+    #include "../../../server/defs.qh"
+#endif
+
+#ifdef SVQC
+
+// values:
+//   volume
+//   noise
+//   targetname
+//   lifetime
+//   fade_time
+//   fade_rate
+// when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0)
+// when targetname is not set, THIS ONE is default
+void target_music_sendto(float to, float is)
+{
+       WriteByte(to, SVC_TEMPENTITY);
+       WriteByte(to, TE_CSQC_TARGET_MUSIC);
+       WriteShort(to, num_for_edict(self));
+       WriteByte(to, self.volume * 255.0 * is);
+       WriteByte(to, self.fade_time * 16.0);
+       WriteByte(to, self.fade_rate * 16.0);
+       WriteByte(to, self.lifetime);
+       WriteString(to, self.noise);
+}
+void target_music_reset()
+{
+       if(self.targetname == "")
+               target_music_sendto(MSG_ALL, 1);
+}
+void target_music_use()
+{
+       if(!activator)
+               return;
+       if(IS_REAL_CLIENT(activator))
+       {
+               msg_entity = activator;
+               target_music_sendto(MSG_ONE, 1);
+       }
+       entity head;
+       FOR_EACH_SPEC(head) if(head.enemy == activator) { msg_entity = head; target_music_sendto(MSG_ONE, 1); }
+}
+void spawnfunc_target_music()
+{
+       self.use = target_music_use;
+       self.reset = target_music_reset;
+       if(!self.volume)
+               self.volume = 1;
+       if(self.targetname == "")
+               target_music_sendto(MSG_INIT, 1);
+       else
+               target_music_sendto(MSG_INIT, 0);
+}
+void TargetMusic_RestoreGame()
+{
+       for(self = world; (self = find(self, classname, "target_music")); )
+       {
+               if(self.targetname == "")
+                       target_music_sendto(MSG_INIT, 1);
+               else
+                       target_music_sendto(MSG_INIT, 0);
+       }
+}
+// values:
+//   volume
+//   noise
+//   targetname
+//   fade_time
+// spawnflags:
+//   1 = START_OFF
+// when triggered, it is disabled/enabled for everyone
+float trigger_music_SendEntity(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC);
+       sf &= ~0x80;
+       if(self.cnt)
+               sf |= 0x80;
+       WriteByte(MSG_ENTITY, sf);
+       if(sf & 4)
+       {
+               WriteCoord(MSG_ENTITY, self.origin.x);
+               WriteCoord(MSG_ENTITY, self.origin.y);
+               WriteCoord(MSG_ENTITY, self.origin.z);
+       }
+       if(sf & 1)
+       {
+               if(self.model != "null")
+               {
+                       WriteShort(MSG_ENTITY, self.modelindex);
+                       WriteCoord(MSG_ENTITY, self.mins.x);
+                       WriteCoord(MSG_ENTITY, self.mins.y);
+                       WriteCoord(MSG_ENTITY, self.mins.z);
+                       WriteCoord(MSG_ENTITY, self.maxs.x);
+                       WriteCoord(MSG_ENTITY, self.maxs.y);
+                       WriteCoord(MSG_ENTITY, self.maxs.z);
+               }
+               else
+               {
+                       WriteShort(MSG_ENTITY, 0);
+                       WriteCoord(MSG_ENTITY, self.maxs.x);
+                       WriteCoord(MSG_ENTITY, self.maxs.y);
+                       WriteCoord(MSG_ENTITY, self.maxs.z);
+               }
+               WriteByte(MSG_ENTITY, self.volume * 255.0);
+               WriteByte(MSG_ENTITY, self.fade_time * 16.0);
+               WriteByte(MSG_ENTITY, self.fade_rate * 16.0);
+               WriteString(MSG_ENTITY, self.noise);
+       }
+       return 1;
+}
+void trigger_music_reset()
+{
+       self.cnt = !(self.spawnflags & 1);
+       self.SendFlags |= 0x80;
+}
+void trigger_music_use()
+{
+       self.cnt = !self.cnt;
+       self.SendFlags |= 0x80;
+}
+void spawnfunc_trigger_music()
+{
+       if(self.model != "")
+               setmodel(self, self.model);
+       if(!self.volume)
+               self.volume = 1;
+       if(!self.modelindex)
+       {
+               setorigin(self, self.origin + self.mins);
+               setsize(self, '0 0 0', self.maxs - self.mins);
+       }
+       trigger_music_reset();
+
+       self.use = trigger_music_use;
+       self.reset = trigger_music_reset;
+
+       Net_LinkEntity(self, false, 0, trigger_music_SendEntity);
+}
+#endif
diff --git a/qcsrc/common/triggers/target/music.qh b/qcsrc/common/triggers/target/music.qh
new file mode 100644 (file)
index 0000000..400e4b8
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef TARGET_MUSIC_H
+#define TARGET_MUSIC_H
+
+.float lifetime;
+
+#endif
diff --git a/qcsrc/common/triggers/target/spawn.qc b/qcsrc/common/triggers/target/spawn.qc
new file mode 100644 (file)
index 0000000..98a3209
--- /dev/null
@@ -0,0 +1,347 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../../dpdefs/progsdefs.qh"
+    #include "../../../dpdefs/dpextensions.qh"
+    #include "../../util.qh"
+    #include "../../../server/defs.qh"
+#endif
+
+#ifdef SVQC
+
+// spawner entity
+// "classname" "target_spawn"
+// "message" "fieldname value fieldname value ..."
+// "spawnflags"
+//   1 = call the spawn function
+//   2 = trigger on map load
+
+float target_spawn_initialized;
+.void() target_spawn_spawnfunc;
+float target_spawn_spawnfunc_field;
+.entity target_spawn_activator;
+.float target_spawn_id;
+float target_spawn_count;
+
+void target_spawn_helper_setmodel()
+{
+       setmodel(self, self.model);
+}
+
+void target_spawn_helper_setsize()
+{
+       setsize(self, self.mins, self.maxs);
+}
+
+void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act)
+{
+       float i, n, valuefieldpos;
+       string key, value, valuefield, valueoffset, valueoffsetrandom;
+       entity valueent;
+       vector data, data2;
+       entity oldself;
+       entity oldactivator;
+
+       n = tokenize_console(msg);
+
+       for(i = 0; i < n-1; i += 2)
+       {
+               key = argv(i);
+               value = argv(i+1);
+               if(key == "$")
+               {
+                       data.x = -1;
+                       data.y = FIELD_STRING;
+               }
+               else
+               {
+                       data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key)));
+                       if(data.y == 0) // undefined field, i.e., invalid type
+                       {
+                               print("target_spawn: invalid/unknown entity key ", key, " specified, ignored!\n");
+                               continue;
+                       }
+               }
+               if(substring(value, 0, 1) == "$")
+               {
+                       value = substring(value, 1, strlen(value) - 1);
+                       if(substring(value, 0, 1) == "$")
+                       {
+                               // deferred replacement
+                               // do nothing
+                               // useful for creating target_spawns with this!
+                       }
+                       else
+                       {
+                               // replace me!
+                               valuefieldpos = strstrofs(value, "+", 0);
+                               valueoffset = "";
+                               if(valuefieldpos != -1)
+                               {
+                                       valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
+                                       value = substring(value, 0, valuefieldpos);
+                               }
+
+                               valuefieldpos = strstrofs(valueoffset, "+", 0);
+                               valueoffsetrandom = "";
+                               if(valuefieldpos != -1)
+                               {
+                                       valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1);
+                                       valueoffset = substring(valueoffset, 0, valuefieldpos);
+                               }
+
+                               valuefieldpos = strstrofs(value, ".", 0);
+                               valuefield = "";
+                               if(valuefieldpos != -1)
+                               {
+                                       valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
+                                       value = substring(value, 0, valuefieldpos);
+                               }
+
+                               if(value == "self")
+                               {
+                                       valueent = self;
+                                       value = "";
+                               }
+                               else if(value == "activator")
+                               {
+                                       valueent = act;
+                                       value = "";
+                               }
+                               else if(value == "other")
+                               {
+                                       valueent = other;
+                                       value = "";
+                               }
+                               else if(value == "pusher")
+                               {
+                                       if(time < act.pushltime)
+                                               valueent = act.pusher;
+                                       else
+                                               valueent = world;
+                                       value = "";
+                               }
+                               else if(value == "target")
+                               {
+                                       valueent = e;
+                                       value = "";
+                               }
+                               else if(value == "killtarget")
+                               {
+                                       valueent = kt;
+                                       value = "";
+                               }
+                               else if(value == "target2")
+                               {
+                                       valueent = t2;
+                                       value = "";
+                               }
+                               else if(value == "target3")
+                               {
+                                       valueent = t3;
+                                       value = "";
+                               }
+                               else if(value == "target4")
+                               {
+                                       valueent = t4;
+                                       value = "";
+                               }
+                               else if(value == "time")
+                               {
+                                       valueent = world;
+                                       value = ftos(time);
+                               }
+                               else
+                               {
+                                       print("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!\n");
+                                       continue;
+                               }
+
+                               if(valuefield == "")
+                               {
+                                       if(value == "")
+                                               value = ftos(num_for_edict(valueent));
+                               }
+                               else
+                               {
+                                       if(value != "")
+                                       {
+                                               print("target_spawn: try to get a field of a non-entity, ignored!\n");
+                                               continue;
+                                       }
+                                       data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield)));
+                                       if(data2_y == 0) // undefined field, i.e., invalid type
+                                       {
+                                               print("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!\n");
+                                               continue;
+                                       }
+                                       value = getentityfieldstring(data2_x, valueent);
+                               }
+
+                               if(valueoffset != "")
+                               {
+                                       switch(data.y)
+                                       {
+                                               case FIELD_STRING:
+                                                       value = strcat(value, valueoffset);
+                                                       break;
+                                               case FIELD_FLOAT:
+                                                       value = ftos(stof(value) + stof(valueoffset));
+                                                       break;
+                                               case FIELD_VECTOR:
+                                                       value = vtos(stov(value) + stov(valueoffset));
+                                                       break;
+                                               default:
+                                                       print("target_spawn: only string, float and vector fields can do calculations, calculation ignored!\n");
+                                                       break;
+                                       }
+                               }
+
+                               if(valueoffsetrandom != "")
+                               {
+                                       switch(data.y)
+                                       {
+                                               case FIELD_FLOAT:
+                                                       value = ftos(stof(value) + random() * stof(valueoffsetrandom));
+                                                       break;
+                                               case FIELD_VECTOR:
+                                                       data2 = stov(valueoffsetrandom);
+                                                       value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1');
+                                                       break;
+                                               default:
+                                                       print("target_spawn: only float and vector fields can do random calculations, calculation ignored!\n");
+                                                       break;
+                                       }
+                               }
+                       }
+               }
+               if(key == "$")
+               {
+                       if(substring(value, 0, 1) == "_")
+                               value = strcat("target_spawn_helper", value);
+                       putentityfieldstring(target_spawn_spawnfunc_field, e, value);
+
+                       oldself = self;
+                       oldactivator = activator;
+
+                       self = e;
+                       activator = act;
+
+                       self.target_spawn_spawnfunc();
+
+                       self = oldself;
+                       activator = oldactivator;
+               }
+               else
+               {
+                       if(data.y == FIELD_VECTOR)
+                               value = strreplace("'", "", value); // why?!?
+                       putentityfieldstring(data.x, e, value);
+               }
+       }
+}
+
+void target_spawn_useon(entity e)
+{
+       self.target_spawn_activator = activator;
+       target_spawn_edit_entity(
+               e,
+               self.message,
+               find(world, targetname, self.killtarget),
+               find(world, targetname, self.target2),
+               find(world, targetname, self.target3),
+               find(world, targetname, self.target4),
+               activator
+       );
+}
+
+float target_spawn_cancreate()
+{
+       float c;
+       entity e;
+
+       c = self.count;
+       if(c == 0) // no limit?
+               return 1;
+
+       ++c; // increase count to not include MYSELF
+       for(e = world; (e = findfloat(e, target_spawn_id, self.target_spawn_id)); --c)
+               ;
+
+       // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more
+       if(c == 0)
+               return 0;
+       return 1;
+}
+
+void target_spawn_use()
+{
+       entity e;
+
+       if(self.target == "")
+       {
+               // spawn new entity
+               if(!target_spawn_cancreate())
+                       return;
+               e = spawn();
+               target_spawn_useon(e);
+               e.target_spawn_id = self.target_spawn_id;
+       }
+       else if(self.target == "*activator")
+       {
+               // edit entity
+               if(activator)
+                       target_spawn_useon(activator);
+       }
+       else
+       {
+               // edit entity
+               for(e = world; (e = find(e, targetname, self.target)); )
+                       target_spawn_useon(e);
+       }
+}
+
+void target_spawn_spawnfirst()
+{
+       activator = self.target_spawn_activator;
+       if(self.spawnflags & 2)
+               target_spawn_use();
+}
+
+void initialize_field_db()
+{
+       if(!target_spawn_initialized)
+       {
+               float n, i;
+               string fn;
+               vector prev, new;
+               float ft;
+
+               n = numentityfields();
+               for(i = 0; i < n; ++i)
+               {
+                       fn = entityfieldname(i);
+                       ft = entityfieldtype(i);
+                       new = i * '1 0 0' + ft * '0 1 0' + '0 0 1';
+                       prev = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", fn)));
+                       if(prev.y == 0)
+                       {
+                               db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(new));
+                               if(fn == "target_spawn_spawnfunc")
+                                       target_spawn_spawnfunc_field = i;
+                       }
+               }
+
+               target_spawn_initialized = 1;
+       }
+}
+
+void spawnfunc_target_spawn()
+{
+       initialize_field_db();
+       self.use = target_spawn_use;
+       self.message = strzone(strreplace("'", "\"", self.message));
+       self.target_spawn_id = ++target_spawn_count;
+       InitializeEntity(self, target_spawn_spawnfirst, INITPRIO_LAST);
+}
+#endif
diff --git a/qcsrc/common/triggers/target/speaker.qc b/qcsrc/common/triggers/target/speaker.qc
new file mode 100644 (file)
index 0000000..7be8b91
--- /dev/null
@@ -0,0 +1,133 @@
+#ifdef SVQC
+// TODO add a way to do looped sounds with sound(); then complete this entity
+void target_speaker_use_off();
+void target_speaker_use_activator()
+{
+       if (!IS_REAL_CLIENT(activator))
+               return;
+       string snd;
+       if(substring(self.noise, 0, 1) == "*")
+       {
+               var .string sample;
+               sample = GetVoiceMessageSampleField(substring(self.noise, 1, -1));
+               if(GetPlayerSoundSampleField_notFound)
+                       snd = "misc/null.wav";
+               else if(activator.sample == "")
+                       snd = "misc/null.wav";
+               else
+               {
+                       tokenize_console(activator.sample);
+                       float n;
+                       n = stof(argv(1));
+                       if(n > 0)
+                               snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
+                       else
+                               snd = strcat(argv(0), ".wav"); // randomization
+               }
+       }
+       else
+               snd = self.noise;
+       msg_entity = activator;
+       soundto(MSG_ONE, self, CH_TRIGGER, snd, VOL_BASE * self.volume, self.atten);
+}
+void target_speaker_use_on()
+{
+       string snd;
+       if(substring(self.noise, 0, 1) == "*")
+       {
+               var .string sample;
+               sample = GetVoiceMessageSampleField(substring(self.noise, 1, -1));
+               if(GetPlayerSoundSampleField_notFound)
+                       snd = "misc/null.wav";
+               else if(activator.sample == "")
+                       snd = "misc/null.wav";
+               else
+               {
+                       tokenize_console(activator.sample);
+                       float n;
+                       n = stof(argv(1));
+                       if(n > 0)
+                               snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
+                       else
+                               snd = strcat(argv(0), ".wav"); // randomization
+               }
+       }
+       else
+               snd = self.noise;
+       sound(self, CH_TRIGGER_SINGLE, snd, VOL_BASE * self.volume, self.atten);
+       if(self.spawnflags & 3)
+               self.use = target_speaker_use_off;
+}
+void target_speaker_use_off()
+{
+       sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASE * self.volume, self.atten);
+       self.use = target_speaker_use_on;
+}
+void target_speaker_reset()
+{
+       if(self.spawnflags & 1) // LOOPED_ON
+       {
+               if(self.use == target_speaker_use_on)
+                       target_speaker_use_on();
+       }
+       else if(self.spawnflags & 2)
+       {
+               if(self.use == target_speaker_use_off)
+                       target_speaker_use_off();
+       }
+}
+
+void spawnfunc_target_speaker()
+{
+       // TODO: "*" prefix to sound file name
+       // TODO: wait and random (just, HOW? random is not a field)
+       if(self.noise)
+               precache_sound (self.noise);
+
+       if(!self.atten && !(self.spawnflags & 4))
+       {
+               IFTARGETED
+                       self.atten = ATTEN_NORM;
+               else
+                       self.atten = ATTEN_STATIC;
+       }
+       else if(self.atten < 0)
+               self.atten = 0;
+
+       if(!self.volume)
+               self.volume = 1;
+
+       IFTARGETED
+       {
+               if(self.spawnflags & 8) // ACTIVATOR
+                       self.use = target_speaker_use_activator;
+               else if(self.spawnflags & 1) // LOOPED_ON
+               {
+                       target_speaker_use_on();
+                       self.reset = target_speaker_reset;
+               }
+               else if(self.spawnflags & 2) // LOOPED_OFF
+               {
+                       self.use = target_speaker_use_on;
+                       self.reset = target_speaker_reset;
+               }
+               else
+                       self.use = target_speaker_use_on;
+       }
+       else if(self.spawnflags & 1) // LOOPED_ON
+       {
+               ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
+               remove(self);
+       }
+       else if(self.spawnflags & 2) // LOOPED_OFF
+       {
+               objerror("This sound entity can never be activated");
+       }
+       else
+       {
+               // Quake/Nexuiz fallback
+               ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
+               remove(self);
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/target/voicescript.qc b/qcsrc/common/triggers/target/voicescript.qc
new file mode 100644 (file)
index 0000000..c173d80
--- /dev/null
@@ -0,0 +1,101 @@
+#ifdef SVQC
+.entity voicescript; // attached voice script
+.float voicescript_index; // index of next voice, or -1 to use the randomized ones
+.float voicescript_nextthink; // time to play next voice
+.float voicescript_voiceend; // time when this voice ends
+
+void target_voicescript_clear(entity pl)
+{
+       pl.voicescript = world;
+}
+
+void target_voicescript_use()
+{
+       if(activator.voicescript != self)
+       {
+               activator.voicescript = self;
+               activator.voicescript_index = 0;
+               activator.voicescript_nextthink = time + self.delay;
+       }
+}
+
+void target_voicescript_next(entity pl)
+{
+       entity vs;
+       float i, n, dt;
+
+       vs = pl.voicescript;
+       if(!vs)
+               return;
+       if(vs.message == "")
+               return;
+       if (!IS_PLAYER(pl))
+               return;
+       if(gameover)
+               return;
+
+       if(time >= pl.voicescript_voiceend)
+       {
+               if(time >= pl.voicescript_nextthink)
+               {
+                       // get the next voice...
+                       n = tokenize_console(vs.message);
+
+                       if(pl.voicescript_index < vs.cnt)
+                               i = pl.voicescript_index * 2;
+                       else if(n > vs.cnt * 2)
+                               i = ((pl.voicescript_index - vs.cnt) % ((n - vs.cnt * 2 - 1) / 2)) * 2 + vs.cnt * 2 + 1;
+                       else
+                               i = -1;
+
+                       if(i >= 0)
+                       {
+                               play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
+                               dt = stof(argv(i + 1));
+                               if(dt >= 0)
+                               {
+                                       pl.voicescript_voiceend = time + dt;
+                                       pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
+                               }
+                               else
+                               {
+                                       pl.voicescript_voiceend = time - dt;
+                                       pl.voicescript_nextthink = pl.voicescript_voiceend;
+                               }
+
+                               pl.voicescript_index += 1;
+                       }
+                       else
+                       {
+                               pl.voicescript = world; // stop trying then
+                       }
+               }
+       }
+}
+
+void spawnfunc_target_voicescript()
+{
+       // netname: directory of the sound files
+       // message: list of "sound file" duration "sound file" duration, a *, and again a list
+       //          foo1 4.1 foo2 4.0 foo3 -3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
+       //          Here, a - in front of the duration means that no delay is to be
+       //          added after this message
+       // wait: average time between messages
+       // delay: initial delay before the first message
+
+       float i, n;
+       self.use = target_voicescript_use;
+
+       n = tokenize_console(self.message);
+       self.cnt = n / 2;
+       for(i = 0; i+1 < n; i += 2)
+       {
+               if(argv(i) == "*")
+               {
+                       self.cnt = i / 2;
+                       ++i;
+               }
+               precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/counter.qc b/qcsrc/common/triggers/trigger/counter.qc
new file mode 100644 (file)
index 0000000..bf1d9b2
--- /dev/null
@@ -0,0 +1,49 @@
+#ifdef SVQC
+void counter_use()
+{
+       self.count -= 1;
+       if (self.count < 0)
+               return;
+
+       if (self.count == 0)
+       {
+               if(IS_PLAYER(activator) && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
+                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COMPLETED);
+
+               self.enemy = activator;
+               multi_trigger ();
+       }
+       else
+       {
+               if(IS_PLAYER(activator) && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
+               if(self.count >= 4)
+                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COUNTER);
+               else
+                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COUNTER_FEWMORE, self.count);
+       }
+}
+
+void counter_reset()
+{
+       self.count = self.cnt;
+       multi_reset();
+}
+
+/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
+Acts as an intermediary for an action that takes multiple inputs.
+
+If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
+
+After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
+*/
+void spawnfunc_trigger_counter()
+{
+       self.wait = -1;
+       if (!self.count)
+               self.count = 2;
+       self.cnt = self.count;
+
+       self.use = counter_use;
+       self.reset = counter_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/delay.qc b/qcsrc/common/triggers/trigger/delay.qc
new file mode 100644 (file)
index 0000000..981c3fe
--- /dev/null
@@ -0,0 +1,22 @@
+#ifdef SVQC
+void delay_use()
+{
+    self.think = SUB_UseTargets;
+    self.nextthink = self.wait;
+}
+
+void delay_reset()
+{
+       self.think = func_null;
+       self.nextthink = 0;
+}
+
+void spawnfunc_trigger_delay()
+{
+    if(!self.wait)
+        self.wait = 1;
+
+    self.use = delay_use;
+    self.reset = delay_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/disablerelay.qc b/qcsrc/common/triggers/trigger/disablerelay.qc
new file mode 100644 (file)
index 0000000..cd5fdff
--- /dev/null
@@ -0,0 +1,31 @@
+#ifdef SVQC
+void trigger_disablerelay_use()
+{
+       entity e;
+
+       float a, b;
+       a = b = 0;
+
+       for(e = world; (e = find(e, targetname, self.target)); )
+       {
+               if(e.use == SUB_UseTargets)
+               {
+                       e.use = SUB_DontUseTargets;
+                       ++a;
+               }
+               else if(e.use == SUB_DontUseTargets)
+               {
+                       e.use = SUB_UseTargets;
+                       ++b;
+               }
+       }
+
+       if((!a) == (!b))
+               print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
+}
+
+void spawnfunc_trigger_disablerelay()
+{
+       self.use = trigger_disablerelay_use;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/flipflop.qc b/qcsrc/common/triggers/trigger/flipflop.qc
new file mode 100644 (file)
index 0000000..12d8a59
--- /dev/null
@@ -0,0 +1,19 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
+"Flip-flop" trigger gate... lets only every second trigger event through
+*/
+void flipflop_use()
+{
+    self.state = !self.state;
+    if(self.state)
+        SUB_UseTargets();
+}
+
+void spawnfunc_trigger_flipflop()
+{
+    if(self.spawnflags & 1)
+        self.state = 1;
+    self.use = flipflop_use;
+    self.reset = spawnfunc_trigger_flipflop; // perfect resetter
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/gamestart.qc b/qcsrc/common/triggers/trigger/gamestart.qc
new file mode 100644 (file)
index 0000000..3ad419d
--- /dev/null
@@ -0,0 +1,22 @@
+#ifdef SVQC
+void gamestart_use()
+{
+       activator = self;
+       SUB_UseTargets();
+       remove(self);
+}
+
+void spawnfunc_trigger_gamestart()
+{
+       self.use = gamestart_use;
+       self.reset2 = spawnfunc_trigger_gamestart;
+
+       if(self.wait)
+       {
+               self.think = self.use;
+               self.nextthink = game_starttime + self.wait;
+       }
+       else
+               InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/gravity.qc b/qcsrc/common/triggers/trigger/gravity.qc
new file mode 100644 (file)
index 0000000..a709a1f
--- /dev/null
@@ -0,0 +1,106 @@
+#ifdef SVQC
+.entity trigger_gravity_check;
+void trigger_gravity_remove(entity own)
+{
+       if(own.trigger_gravity_check.owner == own)
+       {
+               UpdateCSQCProjectile(own);
+               own.gravity = own.trigger_gravity_check.gravity;
+               remove(own.trigger_gravity_check);
+       }
+       else
+               backtrace("Removing a trigger_gravity_check with no valid owner");
+       own.trigger_gravity_check = world;
+}
+void trigger_gravity_check_think()
+{
+       // This spawns when a player enters the gravity zone and checks if he left.
+       // Each frame, self.count is set to 2 by trigger_gravity_touch() and decreased by 1 here.
+       // It the player has left the gravity trigger, this will be allowed to reach 0 and indicate that.
+       if(self.count <= 0)
+       {
+               if(self.owner.trigger_gravity_check == self)
+                       trigger_gravity_remove(self.owner);
+               else
+                       remove(self);
+               return;
+       }
+       else
+       {
+               self.count -= 1;
+               self.nextthink = time;
+       }
+}
+
+void trigger_gravity_use()
+{
+       self.state = !self.state;
+}
+
+void trigger_gravity_touch()
+{
+       float g;
+
+       if(self.state != true)
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+       g = self.gravity;
+
+       if (!(self.spawnflags & 1))
+       {
+               if(other.trigger_gravity_check)
+               {
+                       if(self == other.trigger_gravity_check.enemy)
+                       {
+                               // same?
+                               other.trigger_gravity_check.count = 2; // gravity one more frame...
+                               return;
+                       }
+
+                       // compare prio
+                       if(self.cnt > other.trigger_gravity_check.enemy.cnt)
+                               trigger_gravity_remove(other);
+                       else
+                               return;
+               }
+               other.trigger_gravity_check = spawn();
+               other.trigger_gravity_check.enemy = self;
+               other.trigger_gravity_check.owner = other;
+               other.trigger_gravity_check.gravity = other.gravity;
+               other.trigger_gravity_check.think = trigger_gravity_check_think;
+               other.trigger_gravity_check.nextthink = time;
+               other.trigger_gravity_check.count = 2;
+               if(other.gravity)
+                       g *= other.gravity;
+       }
+
+       if (other.gravity != g)
+       {
+               other.gravity = g;
+               if(self.noise != "")
+                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+               UpdateCSQCProjectile(self.owner);
+       }
+}
+
+void spawnfunc_trigger_gravity()
+{
+       if(self.gravity == 1)
+               return;
+
+       EXACTTRIGGER_INIT;
+       self.touch = trigger_gravity_touch;
+       if(self.noise != "")
+               precache_sound(self.noise);
+
+       self.state = true;
+       IFTARGETED
+       {
+               self.use = trigger_gravity_use;
+               if(self.spawnflags & 2)
+                       self.state = false;
+       }
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/heal.qc b/qcsrc/common/triggers/trigger/heal.qc
new file mode 100644 (file)
index 0000000..6d68610
--- /dev/null
@@ -0,0 +1,42 @@
+#ifdef SVQC
+.float triggerhealtime;
+void trigger_heal_touch()
+{
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
+       if (other.iscreature)
+       {
+               if (other.takedamage)
+               if (!other.deadflag)
+               if (other.triggerhealtime < time)
+               {
+                       EXACTTRIGGER_TOUCH;
+                       other.triggerhealtime = time + 1;
+
+                       if (other.health < self.max_health)
+                       {
+                               other.health = min(other.health + self.health, self.max_health);
+                               other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
+                               sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+                       }
+               }
+       }
+}
+
+void spawnfunc_trigger_heal()
+{
+       self.active = ACTIVE_ACTIVE;
+
+       EXACTTRIGGER_INIT;
+       self.touch = trigger_heal_touch;
+       if (!self.health)
+               self.health = 10;
+       if (!self.max_health)
+               self.max_health = 200; //Max health topoff for field
+       if(self.noise == "")
+               self.noise = "misc/mediumhealth.wav";
+       precache_sound(self.noise);
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/hurt.qc b/qcsrc/common/triggers/trigger/hurt.qc
new file mode 100644 (file)
index 0000000..3a15a77
--- /dev/null
@@ -0,0 +1,92 @@
+#ifdef SVQC
+void trigger_hurt_use()
+{
+       if(IS_PLAYER(activator))
+               self.enemy = activator;
+       else
+               self.enemy = world; // let's just destroy it, if taking over is too much work
+}
+
+.float triggerhurttime;
+void trigger_hurt_touch()
+{
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       if(self.team)
+               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
+                       return;
+
+       // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
+       if (other.iscreature)
+       {
+               if (other.takedamage)
+               if (other.triggerhurttime < time)
+               {
+                       EXACTTRIGGER_TOUCH;
+                       other.triggerhurttime = time + 1;
+
+                       entity own;
+                       own = self.enemy;
+                       if (!IS_PLAYER(own))
+                       {
+                               own = self;
+                               self.enemy = world; // I still hate you all
+                       }
+
+                       Damage (other, self, own, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+       }
+       else if(other.damagedbytriggers)
+       {
+               if(other.takedamage)
+               {
+                       EXACTTRIGGER_TOUCH;
+                       Damage(other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+       }
+
+       return;
+}
+
+/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
+Any object touching this will be hurt
+set dmg to damage amount
+defalt dmg = 5
+*/
+.entity trigger_hurt_next;
+entity trigger_hurt_last;
+entity trigger_hurt_first;
+void spawnfunc_trigger_hurt()
+{
+       EXACTTRIGGER_INIT;
+       self.active = ACTIVE_ACTIVE;
+       self.touch = trigger_hurt_touch;
+       self.use = trigger_hurt_use;
+       self.enemy = world; // I hate you all
+       if (!self.dmg)
+               self.dmg = 1000;
+       if (self.message == "")
+               self.message = "was in the wrong place";
+       if (self.message2 == "")
+               self.message2 = "was thrown into a world of hurt by";
+       // self.message = "someone like %s always gets wrongplaced";
+
+       if(!trigger_hurt_first)
+               trigger_hurt_first = self;
+       if(trigger_hurt_last)
+               trigger_hurt_last.trigger_hurt_next = self;
+       trigger_hurt_last = self;
+}
+
+float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
+{
+       entity th;
+
+       for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
+               if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
+                       return true;
+
+       return false;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/impulse.qc b/qcsrc/common/triggers/trigger/impulse.qc
new file mode 100644 (file)
index 0000000..d436881
--- /dev/null
@@ -0,0 +1,146 @@
+#ifdef SVQC
+// targeted (directional) mode
+void trigger_impulse_touch1()
+{
+       entity targ;
+    float pushdeltatime;
+    float str;
+
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       if (!isPushable(other))
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+    targ = find(world, targetname, self.target);
+    if(!targ)
+    {
+        objerror("trigger_force without a (valid) .target!\n");
+        remove(self);
+        return;
+    }
+
+    str = min(self.radius, vlen(self.origin - other.origin));
+
+    if(self.falloff == 1)
+        str = (str / self.radius) * self.strength;
+    else if(self.falloff == 2)
+        str = (1 - (str / self.radius)) * self.strength;
+    else
+        str = self.strength;
+
+    pushdeltatime = time - other.lastpushtime;
+    if (pushdeltatime > 0.15) pushdeltatime = 0;
+    other.lastpushtime = time;
+    if(!pushdeltatime) return;
+
+    other.velocity = other.velocity + normalize(targ.origin - self.origin) * str * pushdeltatime;
+    other.flags &= ~FL_ONGROUND;
+    UpdateCSQCProjectile(other);
+}
+
+// Directionless (accelerator/decelerator) mode
+void trigger_impulse_touch2()
+{
+    float pushdeltatime;
+
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       if (!isPushable(other))
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+    pushdeltatime = time - other.lastpushtime;
+    if (pushdeltatime > 0.15) pushdeltatime = 0;
+    other.lastpushtime = time;
+    if(!pushdeltatime) return;
+
+    // div0: ticrate independent, 1 = identity (not 20)
+    other.velocity = other.velocity * pow(self.strength, pushdeltatime);
+    UpdateCSQCProjectile(other);
+}
+
+// Spherical (gravity/repulsor) mode
+void trigger_impulse_touch3()
+{
+    float pushdeltatime;
+    float str;
+
+       if (self.active != ACTIVE_ACTIVE)
+               return;
+
+       if (!isPushable(other))
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+    pushdeltatime = time - other.lastpushtime;
+    if (pushdeltatime > 0.15) pushdeltatime = 0;
+    other.lastpushtime = time;
+    if(!pushdeltatime) return;
+
+    setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
+
+       str = min(self.radius, vlen(self.origin - other.origin));
+
+    if(self.falloff == 1)
+        str = (1 - str / self.radius) * self.strength; // 1 in the inside
+    else if(self.falloff == 2)
+        str = (str / self.radius) * self.strength; // 0 in the inside
+    else
+        str = self.strength;
+
+    other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
+    UpdateCSQCProjectile(other);
+}
+
+/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
+-------- KEYS --------
+target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
+         If not, this trigger acts like a damper/accelerator field.
+
+strength : This is how mutch force to add in the direction of .target each second
+           when .target is set. If not, this is hoe mutch to slow down/accelerate
+           someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
+
+radius   : If set, act as a spherical device rather then a liniar one.
+
+falloff : 0 = none, 1 = liniar, 2 = inverted liniar
+
+-------- NOTES --------
+Use a brush textured with common/origin in the trigger entity to determine the origin of the force
+in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
+*/
+
+void spawnfunc_trigger_impulse()
+{
+       self.active = ACTIVE_ACTIVE;
+
+       EXACTTRIGGER_INIT;
+    if(self.radius)
+    {
+        if(!self.strength) self.strength = 2000 * autocvar_g_triggerimpulse_radial_multiplier;
+        setorigin(self, self.origin);
+        setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
+        self.touch = trigger_impulse_touch3;
+    }
+    else
+    {
+        if(self.target)
+        {
+            if(!self.strength) self.strength = 950 * autocvar_g_triggerimpulse_directional_multiplier;
+            self.touch = trigger_impulse_touch1;
+        }
+        else
+        {
+            if(!self.strength) self.strength = 0.9;
+                       self.strength = pow(self.strength, autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier;
+            self.touch = trigger_impulse_touch2;
+        }
+    }
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/impulse.qh b/qcsrc/common/triggers/trigger/impulse.qh
new file mode 100644 (file)
index 0000000..67d6361
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef TRIGGER_IMPULSE_H
+#define TRIGGER_IMPULSE_H
+
+// tZorks trigger impulse / gravity
+.float radius;
+.float falloff;
+.float strength;
+.float lastpushtime;
+
+#endif
diff --git a/qcsrc/common/triggers/trigger/include.qc b/qcsrc/common/triggers/trigger/include.qc
new file mode 100644 (file)
index 0000000..a71de89
--- /dev/null
@@ -0,0 +1,22 @@
+#include "include.qh"
+
+#include "counter.qc"
+#include "delay.qc"
+#include "disablerelay.qc"
+#include "flipflop.qc"
+#include "gamestart.qc"
+#include "gravity.qc"
+#include "heal.qc"
+#include "hurt.qc"
+#include "impulse.qc"
+#include "jumppads.qc"
+#include "magicear.qc"
+#include "monoflop.qc"
+#include "multi.qc"
+#include "multivibrator.qc"
+#include "relay.qc"
+#include "relay_activators.qc"
+#include "relay_if.qc"
+#include "relay_teamcheck.qc"
+#include "secret.qc"
+#include "swamp.qc"
diff --git a/qcsrc/common/triggers/trigger/include.qh b/qcsrc/common/triggers/trigger/include.qh
new file mode 100644 (file)
index 0000000..6f4825b
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef TRIGGERS_TRIGGER_INCLUDE_H
+#define TRIGGERS_TRIGGER_INCLUDE_H
+
+#include "multi.qh"
+#include "jumppads.qh"
+#include "secret.qh"
+#include "swamp.qh"
+
+#endif
diff --git a/qcsrc/common/triggers/trigger/jumppads.qc b/qcsrc/common/triggers/trigger/jumppads.qc
new file mode 100644 (file)
index 0000000..d60e4ff
--- /dev/null
@@ -0,0 +1,491 @@
+// TODO: split target_push and put it in the target folder
+#ifdef SVQC
+#include "jumppads.qh"
+
+void trigger_push_use()
+{
+       if(teamplay)
+       {
+               self.team = activator.team;
+               self.SendFlags |= 2;
+       }
+}
+#endif
+
+/*
+       trigger_push_calculatevelocity
+
+       Arguments:
+         org - origin of the object which is to be pushed
+         tgt - target entity (can be either a point or a model entity; if it is
+               the latter, its midpoint is used)
+         ht  - jump height, measured from the higher one of org and tgt's midpoint
+
+       Returns: velocity for the jump
+       the global trigger_push_calculatevelocity_flighttime is set to the total
+       jump time
+ */
+
+vector trigger_push_calculatevelocity(vector org, entity tgt, float ht)
+{
+       float grav, sdist, zdist, vs, vz, jumpheight;
+       vector sdir, torg;
+
+       torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5;
+
+       grav = PHYS_GRAVITY;
+       if(PHYS_ENTGRAVITY(other))
+               grav *= PHYS_ENTGRAVITY(other);
+
+       zdist = torg.z - org.z;
+       sdist = vlen(torg - org - zdist * '0 0 1');
+       sdir = normalize(torg - org - zdist * '0 0 1');
+
+       // how high do we need to push the player?
+       jumpheight = fabs(ht);
+       if(zdist > 0)
+               jumpheight = jumpheight + zdist;
+
+       /*
+               STOP.
+
+               You will not understand the following equations anyway...
+               But here is what I did to get them.
+
+               I used the functions
+
+                 s(t) = t * vs
+                 z(t) = t * vz - 1/2 grav t^2
+
+               and solved for:
+
+                 s(ti) = sdist
+                 z(ti) = zdist
+                 max(z, ti) = jumpheight
+
+               From these three equations, you will find the three parameters vs, vz
+               and ti.
+        */
+
+       // push him so high...
+       vz = sqrt(fabs(2 * grav * jumpheight)); // NOTE: sqrt(positive)!
+
+       // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump!
+       if(ht < 0)
+               if(zdist < 0)
+                       vz = -vz;
+
+       vector solution;
+       solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist"
+       // ALWAYS solvable because jumpheight >= zdist
+       if(!solution.z)
+               solution.y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0)
+       if(zdist == 0)
+               solution.x = solution.y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually)
+
+       if(zdist < 0)
+       {
+               // down-jump
+               if(ht < 0)
+               {
+                       // almost straight line type
+                       // jump apex is before the jump
+                       // we must take the larger one
+                       trigger_push_calculatevelocity_flighttime = solution.y;
+               }
+               else
+               {
+                       // regular jump
+                       // jump apex is during the jump
+                       // we must take the larger one too
+                       trigger_push_calculatevelocity_flighttime = solution.y;
+               }
+       }
+       else
+       {
+               // up-jump
+               if(ht < 0)
+               {
+                       // almost straight line type
+                       // jump apex is after the jump
+                       // we must take the smaller one
+                       trigger_push_calculatevelocity_flighttime = solution.x;
+               }
+               else
+               {
+                       // regular jump
+                       // jump apex is during the jump
+                       // we must take the larger one
+                       trigger_push_calculatevelocity_flighttime = solution.y;
+               }
+       }
+       vs = sdist / trigger_push_calculatevelocity_flighttime;
+
+       // finally calculate the velocity
+       return sdir * vs + '0 0 1' * vz;
+}
+
+void trigger_push_touch()
+{
+       if (self.active == ACTIVE_NOT)
+               return;
+
+#ifdef SVQC
+       if (!isPushable(other))
+               return;
+#endif
+
+       if(self.team)
+               if(((self.spawnflags & 4) == 0) == (DIFF_TEAM(self, other)))
+                       return;
+
+       EXACTTRIGGER_TOUCH;
+
+       if(self.enemy)
+       {
+               other.velocity = trigger_push_calculatevelocity(other.origin, self.enemy, self.height);
+       }
+       else if(self.target)
+       {
+               entity e;
+               RandomSelection_Init();
+               for(e = world; (e = find(e, targetname, self.target)); )
+               {
+                       if(e.cnt)
+                               RandomSelection_Add(e, 0, string_null, e.cnt, 1);
+                       else
+                               RandomSelection_Add(e, 0, string_null, 1, 1);
+               }
+               other.velocity = trigger_push_calculatevelocity(other.origin, RandomSelection_chosen_ent, self.height);
+       }
+       else
+       {
+               other.velocity = self.movedir;
+       }
+
+       UNSET_ONGROUND(other);
+
+#ifdef SVQC
+       if (IS_PLAYER(other))
+       {
+               // reset tracking of oldvelocity for impact damage (sudden velocity changes)
+               other.oldvelocity = other.velocity;
+
+               if(self.pushltime < time)  // prevent "snorring" sound when a player hits the jumppad more than once
+               {
+                       // flash when activated
+                       pointparticles(particleeffectnum("jumppad_activate"), other.origin, other.velocity, 1);
+                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+                       self.pushltime = time + 0.2;
+               }
+               if(IS_REAL_CLIENT(other) || IS_BOT_CLIENT(other))
+               {
+                       bool found = false;
+                       for(int i = 0; i < other.jumppadcount && i < NUM_JUMPPADSUSED; ++i)
+                               if(other.(jumppadsused[i]) == self)
+                                       found = true;
+                       if(!found)
+                       {
+                               other.(jumppadsused[other.jumppadcount % NUM_JUMPPADSUSED]) = self;
+                               other.jumppadcount = other.jumppadcount + 1;
+                       }
+
+                       if(IS_REAL_CLIENT(other))
+                       {
+                               if(self.message)
+                                       centerprint(other, self.message);
+                       }
+                       else
+                               other.lastteleporttime = time;
+
+                       if (other.deadflag == DEAD_NO)
+                               animdecide_setaction(other, ANIMACTION_JUMP, true);
+               }
+               else
+                       other.jumppadcount = true;
+
+               // reset tracking of who pushed you into a hazard (for kill credit)
+               other.pushltime = 0;
+               other.istypefrag = 0;
+       }
+
+       if(self.enemy.target)
+       {
+               entity oldself;
+               oldself = self;
+               activator = other;
+               self = self.enemy;
+               SUB_UseTargets();
+               self = oldself;
+       }
+
+       if (other.flags & FL_PROJECTILE)
+       {
+               other.angles = vectoangles (other.velocity);
+               switch(other.movetype)
+               {
+                       case MOVETYPE_FLY:
+                               other.movetype = MOVETYPE_TOSS;
+                               other.gravity = 1;
+                               break;
+                       case MOVETYPE_BOUNCEMISSILE:
+                               other.movetype = MOVETYPE_BOUNCE;
+                               other.gravity = 1;
+                               break;
+               }
+               UpdateCSQCProjectile(other);
+       }
+
+       if (self.spawnflags & PUSH_ONCE)
+       {
+               self.touch = func_null;
+               self.think = SUB_Remove;
+               self.nextthink = time;
+       }
+#endif
+}
+
+#ifdef SVQC
+void trigger_push_link();
+void trigger_push_updatelink();
+#endif
+void trigger_push_findtarget()
+{
+       entity t;
+       vector org;
+
+       // first calculate a typical start point for the jump
+       org = (self.absmin + self.absmax) * 0.5;
+       org.z = self.absmax.z - PL_MIN_z;
+
+       if (self.target)
+       {
+               float n = 0;
+               for(t = world; (t = find(t, targetname, self.target)); )
+               {
+                       ++n;
+#ifdef SVQC
+                       entity e = spawn();
+                       setorigin(e, org);
+                       setsize(e, PL_MIN, PL_MAX);
+                       e.velocity = trigger_push_calculatevelocity(org, t, self.height);
+                       tracetoss(e, e);
+                       if(e.movetype == MOVETYPE_NONE)
+                               waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
+                       remove(e);
+#endif
+               }
+
+               if(!n)
+               {
+                       // no dest!
+#ifdef SVQC
+                       objerror ("Jumppad with nonexistant target");
+#endif
+                       return;
+               }
+               else if(n == 1)
+               {
+                       // exactly one dest - bots love that
+                       self.enemy = find(world, targetname, self.target);
+               }
+               else
+               {
+                       // have to use random selection every single time
+                       self.enemy = world;
+               }
+       }
+#ifdef SVQC
+       else
+       {
+               entity e = spawn();
+               setorigin(e, org);
+               setsize(e, PL_MIN, PL_MAX);
+               e.velocity = self.movedir;
+               tracetoss(e, e);
+               waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
+               remove(e);
+       }
+
+       trigger_push_link();
+       defer(0.1, trigger_push_updatelink);
+#endif
+}
+
+#ifdef SVQC
+float trigger_push_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & 1)
+       {
+               WriteString(MSG_ENTITY, self.target);
+               WriteByte(MSG_ENTITY, self.team);
+               WriteInt24_t(MSG_ENTITY, self.spawnflags);
+               WriteByte(MSG_ENTITY, self.active);
+               WriteByte(MSG_ENTITY, self.warpzone_isboxy);
+               WriteByte(MSG_ENTITY, self.height);
+               WriteByte(MSG_ENTITY, self.scale);
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               WriteCoord(MSG_ENTITY, self.mins_x);
+               WriteCoord(MSG_ENTITY, self.mins_y);
+               WriteCoord(MSG_ENTITY, self.mins_z);
+               WriteCoord(MSG_ENTITY, self.maxs_x);
+               WriteCoord(MSG_ENTITY, self.maxs_y);
+               WriteCoord(MSG_ENTITY, self.maxs_z);
+
+               WriteCoord(MSG_ENTITY, self.movedir_x);
+               WriteCoord(MSG_ENTITY, self.movedir_y);
+               WriteCoord(MSG_ENTITY, self.movedir_z);
+
+               WriteCoord(MSG_ENTITY, self.angles_x);
+               WriteCoord(MSG_ENTITY, self.angles_y);
+               WriteCoord(MSG_ENTITY, self.angles_z);
+       }
+
+       if(sf & 2)
+       {
+               WriteByte(MSG_ENTITY, self.team);
+               WriteByte(MSG_ENTITY, self.active);
+       }
+
+       return true;
+}
+
+void trigger_push_updatelink()
+{
+       self.SendFlags |= 1;
+}
+
+void trigger_push_link()
+{
+       Net_LinkEntity(self, false, 0, trigger_push_send);
+}
+#endif
+#ifdef SVQC
+/*
+ * ENTITY PARAMETERS:
+ *
+ *   target:  target of jump
+ *   height:  the absolute value is the height of the highest point of the jump
+ *            trajectory above the higher one of the player and the target.
+ *            the sign indicates whether the highest point is INSIDE (positive)
+ *            or OUTSIDE (negative) of the jump trajectory. General rule: use
+ *            positive values for targets mounted on the floor, and use negative
+ *            values to target a point on the ceiling.
+ *   movedir: if target is not set, this * speed * 10 is the velocity to be reached.
+ */
+void spawnfunc_trigger_push()
+{
+       SetMovedir ();
+
+       EXACTTRIGGER_INIT;
+
+       self.active = ACTIVE_ACTIVE;
+       self.use = trigger_push_use;
+       self.touch = trigger_push_touch;
+
+       // normal push setup
+       if (!self.speed)
+               self.speed = 1000;
+       self.movedir = self.movedir * self.speed * 10;
+
+       if (!self.noise)
+               self.noise = "misc/jumppad.wav";
+       precache_sound (self.noise);
+
+       // this must be called to spawn the teleport waypoints for bots
+       InitializeEntity(self, trigger_push_findtarget, INITPRIO_FINDTARGET);
+}
+
+
+float target_push_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH);
+
+       WriteByte(MSG_ENTITY, self.cnt);
+       WriteString(MSG_ENTITY, self.targetname);
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       return true;
+}
+
+void target_push_link()
+{
+       Net_LinkEntity(self, false, 0, target_push_send);
+       self.SendFlags |= 1; // update
+}
+
+void spawnfunc_target_push() { target_push_link(); }
+void spawnfunc_info_notnull() { target_push_link(); }
+void spawnfunc_target_position() { target_push_link(); }
+
+#endif
+
+#ifdef CSQC
+void ent_trigger_push()
+{
+       float sf = ReadByte();
+
+       if(sf & 1)
+       {
+               self.classname = "jumppad";
+               self.target = strzone(ReadString());
+               float mytm = ReadByte(); if(mytm) { self.team = mytm - 1; }
+               self.spawnflags = ReadInt24_t();
+               self.active = ReadByte();
+               self.warpzone_isboxy = ReadByte();
+               self.height = ReadByte();
+               self.scale = ReadByte();
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+               self.mins_x = ReadCoord();
+               self.mins_y = ReadCoord();
+               self.mins_z = ReadCoord();
+               self.maxs_x = ReadCoord();
+               self.maxs_y = ReadCoord();
+               self.maxs_z = ReadCoord();
+               setsize(self, self.mins, self.maxs);
+               self.movedir_x = ReadCoord();
+               self.movedir_y = ReadCoord();
+               self.movedir_z = ReadCoord();
+               self.angles_x = ReadCoord();
+               self.angles_y = ReadCoord();
+               self.angles_z = ReadCoord();
+
+               self.solid = SOLID_TRIGGER;
+               self.draw = trigger_draw_generic;
+               self.trigger_touch = trigger_push_touch;
+               self.drawmask = MASK_ENGINE;
+               self.move_time = time;
+               trigger_push_findtarget();
+       }
+
+       if(sf & 2)
+       {
+               self.team = ReadByte();
+               self.active = ReadByte();
+       }
+}
+
+void ent_target_push()
+{
+       self.classname = "push_target";
+       self.cnt = ReadByte();
+       self.targetname = strzone(ReadString());
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+
+       self.drawmask = MASK_ENGINE;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/jumppads.qh b/qcsrc/common/triggers/trigger/jumppads.qh
new file mode 100644 (file)
index 0000000..bb0f475
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef T_JUMPPADS_H
+#define T_JUMPPADS_H
+
+const float PUSH_ONCE          = 1;
+const float PUSH_SILENT                = 2;
+
+.float pushltime;
+.float istypefrag;
+.float height;
+
+const int NUM_JUMPPADSUSED = 3;
+.float jumppadcount;
+.entity jumppadsused[NUM_JUMPPADSUSED];
+
+float trigger_push_calculatevelocity_flighttime;
+
+#ifdef SVQC
+void() SUB_UseTargets;
+void trigger_push_use();
+#endif
+
+#ifdef CSQC
+void ent_trigger_push();
+
+void ent_target_push();
+#endif
+
+/*
+       trigger_push_calculatevelocity
+
+       Arguments:
+         org - origin of the object which is to be pushed
+         tgt - target entity (can be either a point or a model entity; if it is
+               the latter, its midpoint is used)
+         ht  - jump height, measured from the higher one of org and tgt's midpoint
+
+       Returns: velocity for the jump
+       the global trigger_push_calculatevelocity_flighttime is set to the total
+       jump time
+ */
+
+vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
+
+void trigger_push_touch();
+
+.vector dest;
+void trigger_push_findtarget();
+
+/*
+ * ENTITY PARAMETERS:
+ *
+ *   target:  target of jump
+ *   height:  the absolute value is the height of the highest point of the jump
+ *            trajectory above the higher one of the player and the target.
+ *            the sign indicates whether the highest point is INSIDE (positive)
+ *            or OUTSIDE (negative) of the jump trajectory. General rule: use
+ *            positive values for targets mounted on the floor, and use negative
+ *            values to target a point on the ceiling.
+ *   movedir: if target is not set, this * speed * 10 is the velocity to be reached.
+ */
+#ifdef SVQC
+void spawnfunc_trigger_push();
+
+void spawnfunc_target_push();
+void spawnfunc_info_notnull();
+void spawnfunc_target_position();
+#endif
+#endif
diff --git a/qcsrc/common/triggers/trigger/magicear.qc b/qcsrc/common/triggers/trigger/magicear.qc
new file mode 100644 (file)
index 0000000..f14b75c
--- /dev/null
@@ -0,0 +1,204 @@
+#ifdef SVQC
+float magicear_matched;
+float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo);
+string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin)
+{
+       float domatch, dotrigger, matchstart, l;
+       string s, msg;
+       entity oldself;
+       string savemessage;
+
+       magicear_matched = false;
+
+       dotrigger = ((IS_PLAYER(source)) && (source.deadflag == DEAD_NO) && ((ear.radius == 0) || (vlen(source.origin - ear.origin) <= ear.radius)));
+       domatch = ((ear.spawnflags & 32) || dotrigger);
+
+       if (!domatch)
+               return msgin;
+
+       if (!msgin)
+       {
+               // we are in TUBA mode!
+               if (!(ear.spawnflags & 256))
+                       return msgin;
+
+               if(!W_Tuba_HasPlayed(source, ear.message, ear.movedir_x, !(ear.spawnflags & 512), ear.movedir_y, ear.movedir_z))
+                       return msgin;
+
+               magicear_matched = true;
+
+               if(dotrigger)
+               {
+                       oldself = self;
+                       activator = source;
+                       self = ear;
+                       savemessage = self.message;
+                       self.message = string_null;
+                       SUB_UseTargets();
+                       self.message = savemessage;
+                       self = oldself;
+               }
+
+               if(ear.netname != "")
+                       return ear.netname;
+
+               return msgin;
+       }
+
+       if(ear.spawnflags & 256) // ENOTUBA
+               return msgin;
+
+       if(privatesay)
+       {
+               if(ear.spawnflags & 4)
+                       return msgin;
+       }
+       else
+       {
+               if(!teamsay)
+                       if(ear.spawnflags & 1)
+                               return msgin;
+               if(teamsay > 0)
+                       if(ear.spawnflags & 2)
+                               return msgin;
+               if(teamsay < 0)
+                       if(ear.spawnflags & 8)
+                               return msgin;
+       }
+
+       matchstart = -1;
+       l = strlen(ear.message);
+
+       if(ear.spawnflags & 128)
+               msg = msgin;
+       else
+               msg = strdecolorize(msgin);
+
+       if(substring(ear.message, 0, 1) == "*")
+       {
+               if(substring(ear.message, -1, 1) == "*")
+               {
+                       // two wildcards
+                       // as we need multi-replacement here...
+                       s = substring(ear.message, 1, -2);
+                       l -= 2;
+                       if(strstrofs(msg, s, 0) >= 0)
+                               matchstart = -2; // we use strreplace on s
+               }
+               else
+               {
+                       // match at start
+                       s = substring(ear.message, 1, -1);
+                       l -= 1;
+                       if(substring(msg, -l, l) == s)
+                               matchstart = strlen(msg) - l;
+               }
+       }
+       else
+       {
+               if(substring(ear.message, -1, 1) == "*")
+               {
+                       // match at end
+                       s = substring(ear.message, 0, -2);
+                       l -= 1;
+                       if(substring(msg, 0, l) == s)
+                               matchstart = 0;
+               }
+               else
+               {
+                       // full match
+                       s = ear.message;
+                       if(msg == ear.message)
+                               matchstart = 0;
+               }
+       }
+
+       if(matchstart == -1) // no match
+               return msgin;
+
+       magicear_matched = true;
+
+       if(dotrigger)
+       {
+               oldself = self;
+               activator = source;
+               self = ear;
+               savemessage = self.message;
+               self.message = string_null;
+               SUB_UseTargets();
+               self.message = savemessage;
+               self = oldself;
+       }
+
+       if(ear.spawnflags & 16)
+       {
+               return ear.netname;
+       }
+       else if(ear.netname != "")
+       {
+               if(matchstart < 0)
+                       return strreplace(s, ear.netname, msg);
+               else
+                       return strcat(
+                               substring(msg, 0, matchstart),
+                               ear.netname,
+                               substring(msg, matchstart + l, -1)
+                       );
+       }
+       else
+               return msgin;
+}
+
+entity magicears;
+string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin)
+{
+       entity ear;
+       string msgout;
+       for(ear = magicears; ear; ear = ear.enemy)
+       {
+               msgout = trigger_magicear_processmessage(ear, source, teamsay, privatesay, msgin);
+               if(!(ear.spawnflags & 64))
+               if(magicear_matched)
+                       return msgout;
+               msgin = msgout;
+       }
+       return msgin;
+}
+
+void spawnfunc_trigger_magicear()
+{
+       self.enemy = magicears;
+       magicears = self;
+
+       // actually handled in "say" processing
+       // spawnflags:
+       //    1 = ignore say
+       //    2 = ignore teamsay
+       //    4 = ignore tell
+       //    8 = ignore tell to unknown player
+       //   16 = let netname replace the whole message (otherwise, netname is a word replacement if set)
+       //   32 = perform the replacement even if outside the radius or dead
+       //   64 = continue replacing/triggering even if this one matched
+       //  128 = don't decolorize message before matching
+       //  256 = message is a tuba note sequence (pitch.duration pitch.duration ...)
+       //  512 = tuba notes must be exact right pitch, no transposing
+       // message: either
+       //   *pattern*
+       // or
+       //   *pattern
+       // or
+       //   pattern*
+       // or
+       //   pattern
+       // netname:
+       //   if set, replacement for the matched text
+       // radius:
+       //   "hearing distance"
+       // target:
+       //   what to trigger
+       // movedir:
+       //   for spawnflags 256, defines 'instrument+1 mintempo maxtempo' (zero component doesn't matter)
+
+       self.movedir_x -= 1; // map to tuba instrument numbers
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/monoflop.qc b/qcsrc/common/triggers/trigger/monoflop.qc
new file mode 100644 (file)
index 0000000..45ce761
--- /dev/null
@@ -0,0 +1,49 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
+"Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
+*/
+void monoflop_use()
+{
+       self.nextthink = time + self.wait;
+       self.enemy = activator;
+       if(self.state)
+               return;
+       self.state = 1;
+       SUB_UseTargets();
+}
+void monoflop_fixed_use()
+{
+       if(self.state)
+               return;
+       self.nextthink = time + self.wait;
+       self.state = 1;
+       self.enemy = activator;
+       SUB_UseTargets();
+}
+
+void monoflop_think()
+{
+       self.state = 0;
+       activator = self.enemy;
+       SUB_UseTargets();
+}
+
+void monoflop_reset()
+{
+       self.state = 0;
+       self.nextthink = 0;
+}
+
+void spawnfunc_trigger_monoflop()
+{
+       if(!self.wait)
+               self.wait = 1;
+       if(self.spawnflags & 1)
+               self.use = monoflop_fixed_use;
+       else
+               self.use = monoflop_use;
+       self.think = monoflop_think;
+       self.state = 0;
+       self.reset = monoflop_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/multi.qc b/qcsrc/common/triggers/trigger/multi.qc
new file mode 100644 (file)
index 0000000..ae73aaf
--- /dev/null
@@ -0,0 +1,204 @@
+// NOTE: also contains trigger_once at bottom
+
+#ifdef SVQC
+// the wait time has passed, so set back up for another activation
+void multi_wait()
+{
+       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()
+{
+       if (self.nextthink > time)
+       {
+               return;         // allready been triggered
+       }
+
+       if (self.classname == "trigger_secret")
+       {
+               if (!IS_PLAYER(self.enemy))
+                       return;
+               found_secrets = found_secrets + 1;
+               WriteByte (MSG_ALL, SVC_FOUNDSECRET);
+       }
+
+       if (self.noise)
+               sound (self.enemy, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+
+// don't trigger again until reset
+       self.takedamage = DAMAGE_NO;
+
+       activator = self.enemy;
+       other = self.goalentity;
+       SUB_UseTargets();
+
+       if (self.wait > 0)
+       {
+               self.think = multi_wait;
+               self.nextthink = time + self.wait;
+       }
+       else if (self.wait == 0)
+       {
+               multi_wait(); // waiting finished
+       }
+       else
+       {       // we can't just remove (self) here, because this is a touch function
+               // called wheil C code is looping through area links...
+               self.touch = func_null;
+       }
+}
+
+void multi_use()
+{
+       self.goalentity = other;
+       self.enemy = activator;
+       multi_trigger();
+}
+
+void multi_touch()
+{
+       if(!(self.spawnflags & 2))
+       if(!other.iscreature)
+                       return;
+
+       if(self.team)
+               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
+                       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
+       }
+
+       EXACTTRIGGER_TOUCH;
+
+       self.enemy = other;
+       self.goalentity = other;
+       multi_trigger ();
+}
+
+void multi_eventdamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if (!self.takedamage)
+               return;
+       if(self.spawnflags & DOOR_NOSPLASH)
+               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+                       return;
+       self.health = self.health - damage;
+       if (self.health <= 0)
+       {
+               self.enemy = attacker;
+               self.goalentity = inflictor;
+               multi_trigger();
+       }
+}
+
+void multi_reset()
+{
+       if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
+               self.touch = multi_touch;
+       if (self.max_health)
+       {
+               self.health = self.max_health;
+               self.takedamage = DAMAGE_YES;
+               self.solid = SOLID_BBOX;
+       }
+       self.think = func_null;
+       self.nextthink = 0;
+       self.team = self.team_saved;
+}
+
+/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
+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.
+NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
+sounds
+1)     secret
+2)     beep beep
+3)     large switch
+4)
+set "message" to text string
+*/
+void spawnfunc_trigger_multiple()
+{
+       self.reset = multi_reset;
+       if (self.sounds == 1)
+       {
+               precache_sound ("misc/secret.wav");
+               self.noise = "misc/secret.wav";
+       }
+       else if (self.sounds == 2)
+       {
+               precache_sound ("misc/talk.wav");
+               self.noise = "misc/talk.wav";
+       }
+       else if (self.sounds == 3)
+       {
+               precache_sound ("misc/trigger1.wav");
+               self.noise = "misc/trigger1.wav";
+       }
+
+       if (!self.wait)
+               self.wait = 0.2;
+       else if(self.wait < -1)
+               self.wait = 0;
+       self.use = multi_use;
+
+       EXACTTRIGGER_INIT;
+
+       self.team_saved = self.team;
+
+       if (self.health)
+       {
+               if (self.spawnflags & SPAWNFLAG_NOTOUCH)
+                       objerror ("health and notouch don't make sense\n");
+               self.max_health = self.health;
+               self.event_damage = multi_eventdamage;
+               self.takedamage = DAMAGE_YES;
+               self.solid = SOLID_BBOX;
+               setorigin (self, self.origin);  // make sure it links into the world
+       }
+       else
+       {
+               if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
+               {
+                       self.touch = multi_touch;
+                       setorigin (self, self.origin);  // make sure it links into the world
+               }
+       }
+}
+
+
+/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
+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 "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.
+sounds
+1)     secret
+2)     beep beep
+3)     large switch
+4)
+set "message" to text string
+*/
+void spawnfunc_trigger_once()
+{
+       self.wait = -1;
+       spawnfunc_trigger_multiple();
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/multi.qh b/qcsrc/common/triggers/trigger/multi.qh
new file mode 100644 (file)
index 0000000..f60ff2a
--- /dev/null
@@ -0,0 +1,4 @@
+#ifdef SVQC
+void multi_trigger();
+void multi_reset();
+#endif
diff --git a/qcsrc/common/triggers/trigger/multivibrator.qc b/qcsrc/common/triggers/trigger/multivibrator.qc
new file mode 100644 (file)
index 0000000..02a258e
--- /dev/null
@@ -0,0 +1,73 @@
+#ifdef SVQC
+void multivibrator_send()
+{
+       float newstate;
+       float cyclestart;
+
+       cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
+
+       newstate = (time < cyclestart + self.wait);
+
+       activator = self;
+       if(self.state != newstate)
+               SUB_UseTargets();
+       self.state = newstate;
+
+       if(self.state)
+               self.nextthink = cyclestart + self.wait + 0.01;
+       else
+               self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
+}
+
+void multivibrator_toggle()
+{
+       if(self.nextthink == 0)
+       {
+               multivibrator_send();
+       }
+       else
+       {
+               if(self.state)
+               {
+                       SUB_UseTargets();
+                       self.state = 0;
+               }
+               self.nextthink = 0;
+       }
+}
+
+void multivibrator_reset()
+{
+       if(!(self.spawnflags & 1))
+               self.nextthink = 0; // wait for a trigger event
+       else
+               self.nextthink = max(1, time);
+}
+
+/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
+"Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
+-------- KEYS --------
+target: trigger all entities with this targetname when it goes off
+targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
+phase: offset of the timing
+wait: "on" cycle time (default: 1)
+respawntime: "off" cycle time (default: same as wait)
+-------- SPAWNFLAGS --------
+START_ON: assume it is already turned on (when targeted)
+*/
+void spawnfunc_trigger_multivibrator()
+{
+       if(!self.wait)
+               self.wait = 1;
+       if(!self.respawntime)
+               self.respawntime = self.wait;
+
+       self.state = 0;
+       self.use = multivibrator_toggle;
+       self.think = multivibrator_send;
+       self.nextthink = max(1, time);
+
+       IFTARGETED
+               multivibrator_reset();
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/relay.qc b/qcsrc/common/triggers/trigger/relay.qc
new file mode 100644 (file)
index 0000000..e037028
--- /dev/null
@@ -0,0 +1,10 @@
+#ifdef SVQC
+/*QUAKED spawnfunc_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 spawnfunc_trigger_relay()
+{
+       self.use = SUB_UseTargets;
+       self.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/relay_activators.qc b/qcsrc/common/triggers/trigger/relay_activators.qc
new file mode 100644 (file)
index 0000000..83c0103
--- /dev/null
@@ -0,0 +1,45 @@
+#ifdef SVQC
+void relay_activators_use()
+{
+       entity trg, os;
+
+       os = self;
+
+       for(trg = world; (trg = find(trg, targetname, os.target)); )
+       {
+               self = trg;
+               if (trg.setactive)
+                       trg.setactive(os.cnt);
+               else
+               {
+                       //bprint("Not using setactive\n");
+                       if(os.cnt == ACTIVE_TOGGLE)
+                               if(trg.active == ACTIVE_ACTIVE)
+                                       trg.active = ACTIVE_NOT;
+                               else
+                                       trg.active = ACTIVE_ACTIVE;
+                       else
+                               trg.active = os.cnt;
+               }
+       }
+       self = os;
+}
+
+void spawnfunc_relay_activate()
+{
+       self.cnt = ACTIVE_ACTIVE;
+       self.use = relay_activators_use;
+}
+
+void spawnfunc_relay_deactivate()
+{
+       self.cnt = ACTIVE_NOT;
+       self.use = relay_activators_use;
+}
+
+void spawnfunc_relay_activatetoggle()
+{
+       self.cnt = ACTIVE_TOGGLE;
+       self.use = relay_activators_use;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/relay_if.qc b/qcsrc/common/triggers/trigger/relay_if.qc
new file mode 100644 (file)
index 0000000..ade56c1
--- /dev/null
@@ -0,0 +1,20 @@
+#ifdef SVQC
+void trigger_relay_if_use()
+{
+       float n;
+       n = self.count;
+
+       // TODO make this generic AND faster than nextent()ing through all, if somehow possible
+       n = (cvar_string(self.netname) == cvar_string(self.message));
+       if(self.spawnflags & 1)
+               n = !n;
+
+       if(n)
+               SUB_UseTargets();
+}
+
+void spawnfunc_trigger_relay_if()
+{
+       self.use = trigger_relay_if_use;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qc b/qcsrc/common/triggers/trigger/relay_teamcheck.qc
new file mode 100644 (file)
index 0000000..8a77cef
--- /dev/null
@@ -0,0 +1,35 @@
+#ifdef SVQC
+void trigger_relay_teamcheck_use()
+{
+       if(activator.team)
+       {
+               if(self.spawnflags & 2)
+               {
+                       if(DIFF_TEAM(activator, self))
+                               SUB_UseTargets();
+               }
+               else
+               {
+                       if(SAME_TEAM(activator, self))
+                               SUB_UseTargets();
+               }
+       }
+       else
+       {
+               if(self.spawnflags & 1)
+                       SUB_UseTargets();
+       }
+}
+
+void trigger_relay_teamcheck_reset()
+{
+       self.team = self.team_saved;
+}
+
+void spawnfunc_trigger_relay_teamcheck()
+{
+       self.team_saved = self.team;
+       self.use = trigger_relay_teamcheck_use;
+       self.reset = trigger_relay_teamcheck_reset;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/secret.qc b/qcsrc/common/triggers/trigger/secret.qc
new file mode 100644 (file)
index 0000000..267c69a
--- /dev/null
@@ -0,0 +1,85 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../..//dpdefs/progsdefs.qh"
+    #include "../../util.qh"
+    #include "../../../server/defs.qh"
+    #include "secret.qh"
+#endif
+
+#ifdef SVQC
+
+void secrets_setstatus() {
+       self.stat_secrets_total = secrets_total;
+       self.stat_secrets_found = secrets_found;
+}
+
+/**
+ * A secret has been found (maybe :P)
+ */
+void trigger_secret_touch() {
+       // only a player can trigger this
+       if (!IS_PLAYER(other))
+               return;
+
+       // update secrets found counter
+       secrets_found += 1;
+       //print("Secret found: ", ftos(secret_counter.cnt), "/");
+       //print(ftos(secret_counter.count), "\n");
+
+       // centerprint message (multi_touch() doesn't always call centerprint())
+       centerprint(other, self.message);
+       self.message = "";
+
+       // handle normal trigger features
+       multi_touch();
+       remove(self);
+}
+
+/*QUAKED trigger_secret (.5 .5 .5) ?
+Variable sized secret trigger. Can be targeted at one or more entities.
+Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found.
+-------- KEYS --------
+sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1)
+noise: path to sound file, if you want to play something else
+target: trigger all entities with this targetname when triggered
+message: print this message to the player who activated the trigger instead of the standard 'You found a secret!'
+killtarget: remove all entities with this targetname when triggered
+-------- NOTES --------
+You should create a common/trigger textured brush covering the entrance to a secret room/area.
+Trigger secret can only be trigger by a player's touch and can not be a target itself.
+*/
+void spawnfunc_trigger_secret() {
+       // FIXME: should it be disabled in most modes?
+
+       // update secrets count
+       secrets_total += 1;
+
+       // add default message
+       if (self.message == "")
+               self.message = "You found a secret!";
+
+       // set default sound
+       if (self.noise == "")
+       if (!self.sounds)
+               self.sounds = 1; // misc/secret.wav
+
+       // this entity can't be a target itself!!!!
+       self.targetname = "";
+
+       // you can't just shoot a room to find it, can you?
+       self.health = 0;
+
+       // a secret can not be delayed
+       self.delay = 0;
+
+       // convert this trigger to trigger_once
+       self.classname = "trigger_once";
+       spawnfunc_trigger_once();
+
+       // take over the touch() function, so we can mark secret as found
+       self.touch = trigger_secret_touch;
+       // ignore triggering;
+       self.use = func_null;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/secret.qh b/qcsrc/common/triggers/trigger/secret.qh
new file mode 100644 (file)
index 0000000..c09da6b
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SECRET_H
+#define SECRET_H
+#ifdef SVQC
+
+/**
+ * Total number of secrets on the map.
+ */
+float secrets_total;
+
+/**
+ * Total numbe of secrets found on the map.
+ */
+float secrets_found;
+
+
+.float stat_secrets_total;
+.float stat_secrets_found;
+
+/**
+ * update secrets status.
+ */
+void secrets_setstatus();
+#endif
+#endif
diff --git a/qcsrc/common/triggers/trigger/swamp.qc b/qcsrc/common/triggers/trigger/swamp.qc
new file mode 100644 (file)
index 0000000..474eb24
--- /dev/null
@@ -0,0 +1,192 @@
+#if defined(CSQC)
+#elif defined(MENUQC)
+#elif defined(SVQC)
+       #include "../../../dpdefs/progsdefs.qh"
+    #include "../../../warpzonelib/util_server.qh"
+    #include "../../weapons/weapons.qh"
+    #include "../../../server/defs.qh"
+    #include "../../deathtypes.qh"
+#endif
+
+/*
+*              t_swamp.c
+*              Adds spawnfunc_trigger_swamp and suppoart routines for xonotic 1.2.1+
+*              Author tZork (Jakob MG)
+*              jakob@games43.se
+*              2005 11 29
+*/
+
+.float swamp_interval; //Hurt players in swamp with this interval
+.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?)
+.entity swampslug;
+
+#ifdef SVQC
+void spawnfunc_trigger_swamp(void);
+#endif
+void swamp_touch(void);
+void swampslug_think();
+
+
+/*
+* Uses a entity calld swampslug to handle players in the swamp
+* It works like this: When the plyer enters teh swamp the spawnfunc_trigger_swamp
+* attaches a new "swampslug" to the player. As long as the plyer is inside
+* the swamp the swamp gives the slug new health. But the slug slowly kills itself
+* so when the player goes outside the swamp, it dies and releases the player from the
+* swamps curses (dmg/slowdown)
+*
+* I do it this way becuz there is no "untouch" event.
+*/
+void swampslug_think(void)
+{
+       //Slowly kill the slug
+       self.health = self.health - 1;
+
+       //Slug dead? then remove curses.
+       if(self.health <= 0)
+       {
+               self.owner.in_swamp = 0;
+               remove(self);
+               //centerprint(self.owner,"Killing slug...\n");
+               return;
+       }
+
+       // Slug still alive, so we are still in the swamp
+       // Or we have exited it very recently.
+       // Do the damage and renew the timer.
+#ifdef SVQC
+       Damage (self.owner, self, self, self.dmg, DEATH_SWAMP, other.origin, '0 0 0');
+#endif
+
+       self.nextthink = time + self.swamp_interval;
+}
+
+void swamp_touch(void)
+{
+       // If whatever thats touching the swamp is not a player
+       // or if its a dead player, just dont care abt it.
+       if(!IS_PLAYER(other) || PHYS_DEAD(other))
+               return;
+
+       EXACTTRIGGER_TOUCH;
+
+       // Chech if player alredy got a swampslug.
+       if(other.in_swamp != 1)
+       {
+               // If not attach one.
+               //centerprint(other,"Entering swamp!\n");
+               other.swampslug = spawn();
+               other.swampslug.health = 2;
+               other.swampslug.think = swampslug_think;
+               other.swampslug.nextthink = time;
+               other.swampslug.owner = other;
+               other.swampslug.dmg = self.dmg;
+               other.swampslug.swamp_interval = self.swamp_interval;
+               other.swamp_slowdown = self.swamp_slowdown;
+               other.in_swamp = 1;
+               return;
+       }
+
+       //other.in_swamp = 1;
+
+       //Revitalize players swampslug
+       other.swampslug.health = 2;
+}
+
+#ifdef SVQC
+float swamp_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER);
+
+       WriteByte(MSG_ENTITY, self.warpzone_isboxy);
+       WriteByte(MSG_ENTITY, self.scale);
+       WriteByte(MSG_ENTITY, self.dmg); // can probably get away with using a single byte here
+       WriteByte(MSG_ENTITY, self.swamp_slowdown);
+       WriteByte(MSG_ENTITY, self.swamp_interval);
+       WriteCoord(MSG_ENTITY, self.origin_x);
+       WriteCoord(MSG_ENTITY, self.origin_y);
+       WriteCoord(MSG_ENTITY, self.origin_z);
+
+       WriteCoord(MSG_ENTITY, self.mins_x);
+       WriteCoord(MSG_ENTITY, self.mins_y);
+       WriteCoord(MSG_ENTITY, self.mins_z);
+       WriteCoord(MSG_ENTITY, self.maxs_x);
+       WriteCoord(MSG_ENTITY, self.maxs_y);
+       WriteCoord(MSG_ENTITY, self.maxs_z);
+
+       WriteCoord(MSG_ENTITY, self.movedir_x);
+       WriteCoord(MSG_ENTITY, self.movedir_y);
+       WriteCoord(MSG_ENTITY, self.movedir_z);
+
+       WriteAngle(MSG_ENTITY, self.angles_x);
+       WriteAngle(MSG_ENTITY, self.angles_y);
+       WriteAngle(MSG_ENTITY, self.angles_z);
+
+       return true;
+}
+
+void swamp_link()
+{
+       Net_LinkEntity(self, false, 0, func_ladder_send);
+}
+
+/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ?
+Players gettin into the swamp will
+get slowd down and damaged
+*/
+void spawnfunc_trigger_swamp(void)
+{
+       // Init stuff
+       EXACTTRIGGER_INIT;
+       self.touch = swamp_touch;
+
+       // Setup default keys, if missing
+       if(self.dmg <= 0)
+               self.dmg = 5;
+       if(self.swamp_interval <= 0)
+               self.swamp_interval = 1;
+       if(self.swamp_slowdown <= 0)
+               self.swamp_slowdown = 0.5;
+
+       swamp_link();
+}
+
+#elif defined(CSQC)
+
+void ent_swamp()
+{
+       self.warpzone_isboxy = ReadByte();
+       self.scale = ReadByte();
+       self.dmg = ReadByte();
+       self.swamp_slowdown = ReadByte();
+       self.swamp_interval = ReadByte();
+
+       self.origin_x = ReadCoord();
+       self.origin_y = ReadCoord();
+       self.origin_z = ReadCoord();
+       setorigin(self, self.origin);
+
+       self.mins_x = ReadCoord();
+       self.mins_y = ReadCoord();
+       self.mins_z = ReadCoord();
+       self.maxs_x = ReadCoord();
+       self.maxs_y = ReadCoord();
+       self.maxs_z = ReadCoord();
+       setsize(self, self.mins, self.maxs);
+
+       self.movedir_x = ReadCoord();
+       self.movedir_y = ReadCoord();
+       self.movedir_z = ReadCoord();
+
+       self.angles_x = ReadAngle();
+       self.angles_y = ReadAngle();
+       self.angles_z = ReadAngle();
+
+       self.classname = "trigger_swamp";
+       self.solid = SOLID_TRIGGER;
+       self.draw = trigger_draw_generic;
+       self.trigger_touch = swamp_touch;
+       self.drawmask = MASK_NORMAL;
+       self.move_time = time;
+}
+#endif
diff --git a/qcsrc/common/triggers/trigger/swamp.qh b/qcsrc/common/triggers/trigger/swamp.qh
new file mode 100644 (file)
index 0000000..86b1431
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef TRIGGER_SWAMP_H
+#define TRIGGER_SWAMP_H
+
+.float swamp_interval; //Hurt players in swamp with this interval
+.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?)
+.entity swampslug;
+
+.float in_swamp;              // bool
+.entity swampslug;            // Uses this to release from swamp ("untouch" fix)
+
+#ifdef CSQC
+void ent_swamp();
+#endif
+
+#endif
diff --git a/qcsrc/common/triggers/triggers.qc b/qcsrc/common/triggers/triggers.qc
new file mode 100644 (file)
index 0000000..e8131b4
--- /dev/null
@@ -0,0 +1,175 @@
+void SUB_DontUseTargets() { }
+
+void() SUB_UseTargets;
+
+void DelayThink()
+{
+       activator = self.enemy;
+       SUB_UseTargets ();
+       remove(self);
+}
+
+void FixSize(entity e)
+{
+       e.mins_x = rint(e.mins_x);
+       e.mins_y = rint(e.mins_y);
+       e.mins_z = rint(e.mins_z);
+
+       e.maxs_x = rint(e.maxs_x);
+       e.maxs_y = rint(e.maxs_y);
+       e.maxs_z = rint(e.maxs_z);
+}
+
+/*
+==============================
+SUB_UseTargets
+
+the global "activator" should be set to the entity that initiated the firing.
+
+If self.delay is set, a DelayedUse entity will be created that will actually
+do the SUB_UseTargets after that many seconds have passed.
+
+Centerprints any self.message to the activator.
+
+Removes all entities with a targetname that match self.killtarget,
+and removes them, so some events can remove other triggers.
+
+Search for (string)targetname in all entities that
+match (string)self.target and call their .use function
+
+==============================
+*/
+void SUB_UseTargets()
+{
+       entity t, stemp, otemp, act;
+       string s;
+       float i;
+
+//
+// check for a delay
+//
+       if (self.delay)
+       {
+       // create a temp object to fire at a later time
+               t = spawn();
+               t.classname = "DelayedUse";
+               t.nextthink = time + self.delay;
+               t.think = DelayThink;
+               t.enemy = activator;
+               t.message = self.message;
+               t.killtarget = self.killtarget;
+               t.target = self.target;
+               t.target2 = self.target2;
+               t.target3 = self.target3;
+               t.target4 = self.target4;
+               return;
+       }
+
+
+//
+// print the message
+//
+#ifdef SVQC
+       if(self)
+       if(IS_PLAYER(activator) && self.message != "")
+       if(IS_REAL_CLIENT(activator))
+       {
+               centerprint(activator, self.message);
+               if (self.noise == "")
+                       play2(activator, "misc/talk.wav");
+       }
+
+//
+// kill the killtagets
+//
+       s = self.killtarget;
+       if (s != "")
+       {
+               for(t = world; (t = find(t, targetname, s)); )
+                       remove(t);
+       }
+#endif
+
+//
+// fire targets
+//
+       act = activator;
+       stemp = self;
+       otemp = other;
+
+       if(stemp.target_random)
+               RandomSelection_Init();
+
+       for(i = 0; i < 4; ++i)
+       {
+               switch(i)
+               {
+                       default:
+                       case 0: s = stemp.target; break;
+                       case 1: s = stemp.target2; break;
+                       case 2: s = stemp.target3; break;
+                       case 3: s = stemp.target4; break;
+               }
+               if (s != "")
+               {
+                       for(t = world; (t = find(t, targetname, s)); )
+                       if(t.use)
+                       {
+                               if(stemp.target_random)
+                               {
+                                       RandomSelection_Add(t, 0, string_null, 1, 0);
+                               }
+                               else
+                               {
+                                       self = t;
+                                       other = stemp;
+                                       activator = act;
+                                       self.use();
+                               }
+                       }
+               }
+       }
+
+       if(stemp.target_random && RandomSelection_chosen_ent)
+       {
+               self = RandomSelection_chosen_ent;
+               other = stemp;
+               activator = act;
+               self.use();
+       }
+
+       activator = act;
+       self = stemp;
+       other = otemp;
+}
+
+#ifdef CSQC
+void trigger_touch_generic(void() touchfunc)
+{
+       entity e;
+       for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
+       if(e.isplayermodel)
+       {
+               vector emin = e.absmin, emax = e.absmax;
+               if(self.solid == SOLID_BSP)
+               {
+                       emin -= '1 1 1';
+                       emax += '1 1 1';
+               }
+               if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
+               if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
+               {
+                       other = e;
+                       touchfunc();
+               }
+       }
+}
+void trigger_draw_generic()
+{
+       float dt = time - self.move_time;
+       self.move_time = time;
+       if(dt <= 0) { return; }
+
+       if(self.trigger_touch) { trigger_touch_generic(self.trigger_touch); }
+}
+#endif
diff --git a/qcsrc/common/triggers/triggers.qh b/qcsrc/common/triggers/triggers.qh
new file mode 100644 (file)
index 0000000..d1a5ea3
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef TRIGGERS_H
+#define TRIGGERS_H
+
+const float SF_TRIGGER_INIT = 1;
+const float SF_TRIGGER_UPDATE = 2;
+const float SF_TRIGGER_RESET = 4;
+
+const float    SPAWNFLAG_NOMESSAGE = 1;
+const float    SPAWNFLAG_NOTOUCH = 1;
+
+.void() trigger_touch;
+
+.float height;
+
+.float nottargeted;
+#define IFTARGETED if(!self.nottargeted && self.targetname != "")
+
+.string bgmscript;
+.float bgmscriptattack;
+.float bgmscriptdecay;
+.float bgmscriptsustain;
+.float bgmscriptrelease;
+
+.float lip;
+
+// used elsewhere (will fix)
+#ifdef SVQC
+void spawnfunc_trigger_once();
+string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin);
+
+void target_voicescript_next(entity pl);
+void target_voicescript_clear(entity pl);
+#endif
+
+.float volume, atten;
+
+.vector dest;
+
+#ifdef CSQC
+float WarpZoneLib_ExactTrigger_Touch();
+#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
+
+.float active;
+.string target;
+.string targetname;
+
+const int ACTIVE_NOT           = 0;
+const int ACTIVE_ACTIVE        = 1;
+const int ACTIVE_IDLE          = 2;
+const int ACTIVE_BUSY          = 2;
+const int ACTIVE_TOGGLE                = 3;
+#endif
+
+#endif
index 0010449a025923feff26357579fc0a98fd2fe9d0..569a15d97fa68eed9818e6d66933927384f5f702 100644 (file)
@@ -38,6 +38,8 @@ PORTO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
 #endif
 #else
 #ifdef SVQC
+#include "../triggers/trigger/jumppads.qh"
+
 void spawnfunc_weapon_porto(void) { weapon_defaultspawnfunc(WEP_PORTO); }
 
 void W_Porto_Success(void)
index 0ecafaf1e87c7aa62fda4892d2c38ef300e05683..7ba2e1612e8530f9d51d8b73b79d55ce0a29b9da 100644 (file)
@@ -2,6 +2,7 @@
 #include "role_onslaught.qc"
 #include "role_keyhunt.qc"
 #include "roles.qc"
+#include "../../../common/triggers/trigger/jumppads.qh"
 
 void havocbot_ai()
 {
index 657ec4d7fd3c0e15f6477c5a56c766b5a5effcb1..3119182a64fde2027979ce62db82066dad90a4ab 100644 (file)
@@ -18,6 +18,7 @@
     #include "autocvars.qh"
     #include "defs.qh"
     #include "../common/deathtypes.qh"
+       #include "../common/triggers/subs.qh"
     #include "mutators/mutators_include.qh"
     #include "../csqcmodellib/sv_model.qh"
 #endif
index 53ec965030552c0bd76eb2af8811aa53ac90165a..5453f5199f6d4150ab93293c381db7c34e4e5293 100644 (file)
@@ -9,7 +9,6 @@
 #include "portals.qh"
 #include "teamplay.qh"
 #include "playerdemo.qh"
-#include "secret.qh"
 
 #include "bot/bot.qh"
 #include "bot/navigation.qh"
 #include "../common/net_notice.qh"
 #include "../common/physics.qh"
 
+#include "../common/triggers/subs.qh"
+#include "../common/triggers/triggers.qh"
+#include "../common/triggers/trigger/secret.qh"
+
 #include "../common/monsters/sv_monsters.qh"
 
 #include "../warpzonelib/server.qh"
index b2e680e831ed6b9d499469662ea31eedcd35eec1..49ae14a6a24a7339d6868bc66bd779a9260b54a6 100644 (file)
@@ -1,5 +1,4 @@
 #include "cl_player.qh"
-#include "g_triggers.qh"
 #include "g_violence.qh"
 #include "miscfunctions.qh"
 
index 1d4cc55232382a6fcd7f2491325b6364ec5a9736..c1def6b821e469d43d1183f371ff1039e0009d53 100644 (file)
@@ -23,5 +23,4 @@ const int NUM_PLAYERSKINS_TEAMPLAY = 3;
 
 const int ASSAULT_VALUE_INACTIVE = 1000;
 
-const int DOOR_NOSPLASH = 256; // generic anti-splashdamage spawnflag
 #endif
index fc85ed9d5b013f36d55444160a55e75a0ede4db7..f27f503408a6b2b24a39c93f3db063dbc658072b 100644 (file)
@@ -46,8 +46,6 @@ float g_jetpack;
 float sv_clones;
 float sv_foginterval;
 
-entity activator;
-
 float player_count;
 float currentbots;
 float bots_would_leave;
@@ -70,21 +68,6 @@ float server_is_dedicated;
 //.string      map;
 
 //.float       worldtype;
-.float delay;
-.float wait;
-.float lip;
-//.float       light_lev;
-.float speed;
-//.float       style;
-//.float       skill;
-.float sounds;
-.string  platmovetype;
-.float platmovetype_start, platmovetype_end;
-
-.string killtarget;
-
-.vector        pos1, pos2;
-.vector        mangle;
 
 .float pain_finished;                  //Added by Supajoe
 .float pain_frame;                     //"
@@ -94,14 +77,7 @@ float server_is_dedicated;
 .float invincible_finished;
 .float superweapons_finished;
 
-.vector                finaldest, finalangle;          //plat.qc stuff
-.void()                think1;
-.float state;
-.float         t_length, t_width;
-
-.vector destvec;               // for rain
-.vector destvec2;              // for train
-.float cnt;            // for rain
+.float cnt; // used in too many places
 .float count;
 //.float cnt2;
 
@@ -113,15 +89,6 @@ float server_is_dedicated;
 .float fade_time;
 .float fade_rate;
 
-// player animation state
-.float animstate_startframe;
-.float animstate_numframes;
-.float animstate_framerate;
-.float animstate_starttime;
-.float animstate_endtime;
-.float animstate_override;
-.float animstate_looping;
-
 // weapon animation vectors:
 .vector anim_fire1;
 .vector anim_fire2;
@@ -210,26 +177,13 @@ const int W_TICSPERFRAME = 2;
 
 void weapon_defaultspawnfunc(float wpn);
 
-.vector dest1, dest2;
-
 float gameover;
 float intermission_running;
 float intermission_exittime;
 float alreadychangedlevel;
 
-// Keys player is holding
-.float itemkeys;
-// message delay for func_door locked by keys and key locks
-// this field is used on player entities
-.float key_door_messagetime;
-
-
 .float version;
 
-//swamp
-.float in_swamp;              // bool
-.entity swampslug;            // Uses this to release from swamp ("untouch" fix)
-
 // footstep interval
 .float nextstep;
 
@@ -284,10 +238,6 @@ float default_weapon_alpha;
 
 .float version_nagtime;
 
-const int NUM_JUMPPADSUSED = 3;
-.float jumppadcount;
-.entity jumppadsused[NUM_JUMPPADSUSED];
-
 string gamemode_name;
 
 float startitem_failed;
@@ -459,16 +409,6 @@ void W_Porto_Remove (entity p);
 // may be useful to all weapons
 .float bulletcounter;
 
-void target_voicescript_next(entity pl);
-void target_voicescript_clear(entity pl);
-
-.string target2;
-.string target3;
-.string target4;
-.string curvetarget;
-.float target_random;
-.float trigger_reverse;
-
 // Nexball
 .entity ballcarried; // Also used for keepaway
 .float metertime;
diff --git a/qcsrc/server/func_breakable.qc b/qcsrc/server/func_breakable.qc
deleted file mode 100644 (file)
index c0e54d1..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-#include "weapons/common.qh"
-
-.entity sprite;
-
-.float dmg;
-.float dmg_edge;
-.float dmg_radius;
-.float dmg_force;
-.float debrismovetype;
-.float debrissolid;
-.vector debrisvelocity;
-.vector debrisvelocityjitter;
-.vector debrisavelocityjitter;
-.float debristime;
-.float debristimejitter;
-.float debrisfadetime;
-.float debrisdamageforcescale;
-.float debrisskin;
-
-.string mdl_dead; // or "" to hide when broken
-.string debris; // space separated list of debris models
-// other fields:
-//   mdl = particle effect name
-//   count = particle effect multiplier
-//   targetname = target to trigger to unbreak the model
-//   target = targets to trigger when broken
-//   health = amount of damage it can take
-//   spawnflags:
-//     1 = start disabled (needs to be triggered to activate)
-//     2 = indicate damage
-// notes:
-//   for mdl_dead to work, origin must be set (using a common/origin brush).
-//   Otherwise mdl_dead will be displayed at the map origin, and nobody would
-//   want that!
-
-void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
-
-//
-// func_breakable
-// - basically func_assault_destructible for general gameplay use
-//
-void LaunchDebris (string debrisname, vector force)
-{
-       entity dbr = spawn();
-       setorigin(dbr, self.absmin
-                  + '1 0 0' * random() * (self.absmax.x - self.absmin.x)
-                  + '0 1 0' * random() * (self.absmax.y - self.absmin.y)
-                  + '0 0 1' * random() * (self.absmax.z - self.absmin.z));
-       setmodel (dbr, debrisname );
-       dbr.skin = self.debrisskin;
-       dbr.colormap = self.colormap; // inherit team colors
-       dbr.owner = self; // do not be affected by our own explosion
-       dbr.movetype = self.debrismovetype;
-       dbr.solid = self.debrissolid;
-       if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out
-               setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it
-       dbr.velocity_x = self.debrisvelocity.x + self.debrisvelocityjitter.x * crandom();
-       dbr.velocity_y = self.debrisvelocity.y + self.debrisvelocityjitter.y * crandom();
-       dbr.velocity_z = self.debrisvelocity.z + self.debrisvelocityjitter.z * crandom();
-       self.velocity = self.velocity + force * self.debrisdamageforcescale;
-       dbr.avelocity_x = random()*self.debrisavelocityjitter.x;
-       dbr.avelocity_y = random()*self.debrisavelocityjitter.y;
-       dbr.avelocity_z = random()*self.debrisavelocityjitter.z;
-       dbr.damageforcescale = self.debrisdamageforcescale;
-       if(dbr.damageforcescale)
-               dbr.takedamage = DAMAGE_YES;
-       SUB_SetFade(dbr, time + self.debristime + crandom() * self.debristimejitter, self.debrisfadetime);
-}
-
-void func_breakable_colormod()
-{
-       float h;
-       if (!(self.spawnflags & 2))
-               return;
-       h = self.health / self.max_health;
-       if(h < 0.25)
-               self.colormod = '1 0 0';
-       else if(h <= 0.75)
-               self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5);
-       else
-               self.colormod = '1 1 1';
-
-       CSQCMODEL_AUTOUPDATE();
-}
-
-void func_breakable_look_destroyed()
-{
-       float floorZ;
-
-       if(self.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first
-               self.dropped_origin = self.origin;
-
-       if(self.mdl_dead == "")
-               self.model = "";
-       else {
-               if (self.origin == '0 0 0')     {       // probably no origin brush, so don't spawn in the middle of the map..
-                       floorZ = self.absmin.z;
-                       setorigin(self,((self.absmax+self.absmin)*.5));
-                       self.origin_z = floorZ;
-               }
-               setmodel(self, self.mdl_dead);
-       }
-
-       self.solid = SOLID_NOT;
-}
-
-void func_breakable_look_restore()
-{
-       setmodel(self, self.mdl);
-       if(self.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow
-               setorigin(self, self.dropped_origin);
-       self.solid = SOLID_BSP;
-}
-
-void func_breakable_behave_destroyed()
-{
-       self.health = self.max_health;
-       self.takedamage = DAMAGE_NO;
-       self.bot_attack = false;
-       self.event_damage = func_null;
-       self.state = 1;
-       func_breakable_colormod();
-}
-
-void func_breakable_behave_restore()
-{
-       self.health = self.max_health;
-       if(self.sprite)
-       {
-               WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
-               WaypointSprite_UpdateHealth(self.sprite, self.health);
-       }
-       self.takedamage = DAMAGE_AIM;
-       self.bot_attack = true;
-       self.event_damage = func_breakable_damage;
-       self.state = 0;
-       self.nextthink = 0; // cancel auto respawn
-       func_breakable_colormod();
-}
-
-void func_breakable_destroyed()
-{
-       func_breakable_look_destroyed();
-       func_breakable_behave_destroyed();
-
-       CSQCMODEL_AUTOUPDATE();
-}
-
-void func_breakable_restore()
-{
-       func_breakable_look_restore();
-       func_breakable_behave_restore();
-
-       CSQCMODEL_AUTOUPDATE();
-}
-
-vector debrisforce; // global, set before calling this
-void func_breakable_destroy() {
-       float n, i;
-       string oldmsg;
-
-       activator = self.owner;
-       self.owner = world; // set by W_PrepareExplosionByDamage
-
-       // now throw around the debris
-       n = tokenize_console(self.debris);
-       for(i = 0; i < n; ++i)
-               LaunchDebris(argv(i), debrisforce);
-
-       func_breakable_destroyed();
-
-       if(self.noise)
-               sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-
-       if(self.dmg)
-               RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world);
-
-       if(self.cnt)
-               pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
-
-       if(self.respawntime)
-       {
-               self.think = func_breakable_restore;
-               self.nextthink = time + self.respawntime + crandom() * self.respawntimejitter;
-       }
-
-       oldmsg = self.message;
-       self.message = "";
-       SUB_UseTargets();
-       self.message = oldmsg;
-}
-
-void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{
-       if(self.state == 1)
-               return;
-       if(self.spawnflags & DOOR_NOSPLASH)
-               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
-                       return;
-       if(self.team)
-               if(attacker.team == self.team)
-                       return;
-       self.health = self.health - damage;
-       if(self.sprite)
-       {
-               WaypointSprite_Ping(self.sprite);
-               WaypointSprite_UpdateHealth(self.sprite, self.health);
-       }
-       func_breakable_colormod();
-
-       if(self.health <= 0)
-       {
-               debrisforce = force;
-               W_PrepareExplosionByDamage(attacker, func_breakable_destroy);
-       }
-}
-
-void func_breakable_reset()
-{
-       self.team = self.team_saved;
-       func_breakable_look_restore();
-       if(self.spawnflags & 1)
-               func_breakable_behave_destroyed();
-       else
-               func_breakable_behave_restore();
-
-       CSQCMODEL_AUTOUPDATE();
-}
-
-// destructible walls that can be used to trigger target_objective_decrease
-void spawnfunc_func_breakable() {
-       float n, i;
-       if(!self.health)
-               self.health = 100;
-       self.max_health = self.health;
-
-       // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway
-       if(!self.debrismovetype) self.debrismovetype = MOVETYPE_BOUNCE;
-       if(!self.debrissolid) self.debrissolid = SOLID_NOT;
-       if(self.debrisvelocity == '0 0 0') self.debrisvelocity = '0 0 140';
-       if(self.debrisvelocityjitter == '0 0 0') self.debrisvelocityjitter = '70 70 70';
-       if(self.debrisavelocityjitter == '0 0 0') self.debrisavelocityjitter = '600 600 600';
-       if(!self.debristime) self.debristime = 3.5;
-       if(!self.debristimejitter) self.debristime = 2.5;
-
-       if(self.mdl != "")
-               self.cnt = particleeffectnum(self.mdl);
-       if(self.count == 0)
-               self.count = 1;
-
-       if(self.message == "")
-               self.message = "got too close to an explosion";
-       if(self.message2 == "")
-               self.message2 = "was pushed into an explosion by";
-       if(!self.dmg_radius)
-               self.dmg_radius = 150;
-       if(!self.dmg_force)
-               self.dmg_force = 200;
-
-       self.mdl = self.model;
-       SetBrushEntityModel();
-
-       self.use = func_breakable_restore;
-
-       // precache all the models
-       if (self.mdl_dead)
-               precache_model(self.mdl_dead);
-       n = tokenize_console(self.debris);
-       for(i = 0; i < n; ++i)
-               precache_model(argv(i));
-       if(self.noise)
-               precache_sound(self.noise);
-
-       self.team_saved = self.team;
-       self.dropped_origin = self.origin;
-
-       self.reset = func_breakable_reset;
-       func_breakable_reset();
-
-       CSQCMODEL_AUTOINIT();
-}
-
-// for use in maps with a "model" key set
-void spawnfunc_misc_breakablemodel() {
-       spawnfunc_func_breakable();
-}
index 310833e4367ce2f036ad10c4570be683bfb4f275..05032c2d057185334c2accde72c6f6b1ddfbde6c 100644 (file)
@@ -15,6 +15,7 @@
     #include "command/common.qh"
     #include "g_hook.qh"
     #include "round_handler.qh"
+       #include "weapons/common.qh"
 #endif
 
 /*============================================
index 6e1c06d056f2eb5bf3306240042ad3b933af56bc..95b83f3d7a9c1a5a076736dc7c1ff99a68066228 100644 (file)
@@ -4,6 +4,7 @@
        #include "../dpdefs/progsdefs.qh"
     #include "../dpdefs/dpextensions.qh"
     #include "../common/constants.qh"
+       #include "../common/triggers/subs.qh"
     #include "autocvars.qh"
     #include "constants.qh"
     #include "defs.qh"
index 74a8cf7f96126135f1ec45074a86d9d0036e3201..d42cc6750f6d9000ef8cc318c088770a30ac0906 100644 (file)
@@ -1,7 +1,5 @@
 #include "g_subs.qh"
 
-void SUB_NullThink(void) { }
-
 void spawnfunc_info_null (void)
 {
        remove(self);
@@ -52,380 +50,6 @@ void updateanim(entity e)
        //print(ftos(time), " -> ", ftos(e.frame), "\n");
 }
 
-/*
-==================
-SUB_Remove
-
-Remove self
-==================
-*/
-void SUB_Remove (void)
-{
-       remove (self);
-}
-
-/*
-==================
-SUB_Friction
-
-Applies some friction to self
-==================
-*/
-void SUB_Friction (void)
-{
-       self.nextthink = time;
-       if(self.flags & FL_ONGROUND)
-               self.velocity = self.velocity * (1 - frametime * self.friction);
-}
-
-/*
-==================
-SUB_VanishOrRemove
-
-Makes client invisible or removes non-client
-==================
-*/
-void SUB_VanishOrRemove (entity ent)
-{
-       if (IS_CLIENT(ent))
-       {
-               // vanish
-               ent.alpha = -1;
-               ent.effects = 0;
-               ent.glow_size = 0;
-               ent.pflags = 0;
-       }
-       else
-       {
-               // remove
-               remove (ent);
-       }
-}
-
-void SUB_SetFade_Think (void)
-{
-       if(self.alpha == 0)
-               self.alpha = 1;
-       self.think = SUB_SetFade_Think;
-       self.nextthink = time;
-       self.alpha -= frametime * self.fade_rate;
-       if (self.alpha < 0.01)
-               SUB_VanishOrRemove(self);
-       else
-               self.nextthink = time;
-}
-
-/*
-==================
-SUB_SetFade
-
-Fade 'ent' out when time >= 'when'
-==================
-*/
-void SUB_SetFade (entity ent, float when, float fadetime)
-{
-       ent.fade_rate = 1/fadetime;
-       ent.think = SUB_SetFade_Think;
-       ent.nextthink = when;
-}
-
-/*
-=============
-SUB_CalcMove
-
-calculate self.velocity and self.nextthink to reach dest from
-self.origin traveling at speed
-===============
-*/
-void SUB_CalcMoveDone (void)
-{
-       // After moving, set origin to exact final destination
-
-       setorigin (self, self.finaldest);
-       self.velocity = '0 0 0';
-       self.nextthink = -1;
-       if (self.think1)
-               self.think1 ();
-}
-
-void SUB_CalcMove_controller_think (void)
-{
-       entity oldself;
-       float traveltime;
-       float phasepos;
-       float nexttick;
-       vector delta;
-       vector delta2;
-       vector veloc;
-       vector angloc;
-       vector nextpos;
-       delta = self.destvec;
-       delta2 = self.destvec2;
-       if(time < self.animstate_endtime) {
-               nexttick = time + sys_frametime;
-
-               traveltime = self.animstate_endtime - self.animstate_starttime;
-               phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
-               phasepos = cubic_speedfunc(self.platmovetype_start, self.platmovetype_end, phasepos);
-               nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
-               // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
-
-               if(self.owner.platmovetype_turn)
-               {
-                       vector destangle;
-                       destangle = delta + 2 * delta2 * phasepos;
-                       destangle = vectoangles(destangle);
-                       destangle.x = -destangle.x; // flip up / down orientation
-
-                       // take the shortest distance for the angles
-                       self.owner.angles_x -= 360 * floor((self.owner.angles.x - destangle.x) / 360 + 0.5);
-                       self.owner.angles_y -= 360 * floor((self.owner.angles.y - destangle.y) / 360 + 0.5);
-                       self.owner.angles_z -= 360 * floor((self.owner.angles.z - destangle.z) / 360 + 0.5);
-                       angloc = destangle - self.owner.angles;
-                       angloc = angloc * (1 / sys_frametime); // so it arrives for the next frame
-                       self.owner.avelocity = angloc;
-               }
-               if(nexttick < self.animstate_endtime)
-                       veloc = nextpos - self.owner.origin;
-               else
-                       veloc = self.finaldest - self.owner.origin;
-               veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
-
-               self.owner.velocity = veloc;
-               self.nextthink = nexttick;
-       } else {
-               // derivative: delta + 2 * delta2 (e.g. for angle positioning)
-               oldself = self;
-               self.owner.think = self.think1;
-               self = self.owner;
-               remove(oldself);
-               self.think();
-       }
-}
-
-void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest)
-{
-       // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
-       // 2 * control * t - 2 * control * t * t + dest * t * t
-       // 2 * control * t + (dest - 2 * control) * t * t
-
-       controller.origin = org; // starting point
-       control -= org;
-       dest -= org;
-
-       controller.destvec = 2 * control; // control point
-       controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point
-       // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (dest - control)
-}
-
-void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest)
-{
-       // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
-       // 2 * control * t - 2 * control * t * t + dest * t * t
-       // 2 * control * t + (dest - 2 * control) * t * t
-
-       controller.origin = org; // starting point
-       dest -= org;
-
-       controller.destvec = dest; // end point
-       controller.destvec2 = '0 0 0';
-}
-
-void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func)
-{
-       float   traveltime;
-       entity controller;
-
-       if (!tspeed)
-               objerror ("No speed is defined!");
-
-       self.think1 = func;
-       self.finaldest = tdest;
-       self.think = SUB_CalcMoveDone;
-
-       switch(tspeedtype)
-       {
-               default:
-               case TSPEED_START:
-                       traveltime = 2 * vlen(tcontrol - self.origin) / tspeed;
-                       break;
-               case TSPEED_END:
-                       traveltime = 2 * vlen(tcontrol - tdest)       / tspeed;
-                       break;
-               case TSPEED_LINEAR:
-                       traveltime = vlen(tdest - self.origin)        / tspeed;
-                       break;
-               case TSPEED_TIME:
-                       traveltime = tspeed;
-                       break;
-       }
-
-       if (traveltime < 0.1) // useless anim
-       {
-               self.velocity = '0 0 0';
-               self.nextthink = self.ltime + 0.1;
-               return;
-       }
-
-       controller = spawn();
-       controller.classname = "SUB_CalcMove_controller";
-       controller.owner = self;
-       controller.platmovetype = self.platmovetype;
-       controller.platmovetype_start = self.platmovetype_start;
-       controller.platmovetype_end = self.platmovetype_end;
-       SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest);
-       controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
-       controller.animstate_starttime = time;
-       controller.animstate_endtime = time + traveltime;
-       controller.think = SUB_CalcMove_controller_think;
-       controller.think1 = self.think;
-
-       // the thinking is now done by the controller
-       self.think = SUB_NullThink; // for PushMove
-       self.nextthink = self.ltime + traveltime;
-
-       // invoke controller
-       self = controller;
-       self.think();
-       self = self.owner;
-}
-
-void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func)
-{
-       vector  delta;
-       float   traveltime;
-
-       if (!tspeed)
-               objerror ("No speed is defined!");
-
-       self.think1 = func;
-       self.finaldest = tdest;
-       self.think = SUB_CalcMoveDone;
-
-       if (tdest == self.origin)
-       {
-               self.velocity = '0 0 0';
-               self.nextthink = self.ltime + 0.1;
-               return;
-       }
-
-       delta = tdest - self.origin;
-
-       switch(tspeedtype)
-       {
-               default:
-               case TSPEED_START:
-               case TSPEED_END:
-               case TSPEED_LINEAR:
-                       traveltime = vlen (delta) / tspeed;
-                       break;
-               case TSPEED_TIME:
-                       traveltime = tspeed;
-                       break;
-       }
-
-       // Very short animations don't really show off the effect
-       // of controlled animation, so let's just use linear movement.
-       // Alternatively entities can choose to specify non-controlled movement.
-        // The only currently implemented alternative movement is linear (value 1)
-       if (traveltime < 0.15 || (self.platmovetype_start == 1 && self.platmovetype_end == 1)) // is this correct?
-       {
-               self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
-               self.nextthink = self.ltime + traveltime;
-               return;
-       }
-
-       // now just run like a bezier curve...
-       SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
-}
-
-void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func)
-{
-       entity  oldself;
-
-       oldself = self;
-       self = ent;
-
-       SUB_CalcMove (tdest, tspeedtype, tspeed, func);
-
-       self = oldself;
-}
-
-/*
-=============
-SUB_CalcAngleMove
-
-calculate self.avelocity and self.nextthink to reach destangle from
-self.angles rotating
-
-The calling function should make sure self.think is valid
-===============
-*/
-void SUB_CalcAngleMoveDone (void)
-{
-       // After rotating, set angle to exact final angle
-       self.angles = self.finalangle;
-       self.avelocity = '0 0 0';
-       self.nextthink = -1;
-       if (self.think1)
-               self.think1 ();
-}
-
-// FIXME: I fixed this function only for rotation around the main axes
-void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func)
-{
-       vector  delta;
-       float   traveltime;
-
-       if (!tspeed)
-               objerror ("No speed is defined!");
-
-       // take the shortest distance for the angles
-       self.angles_x -= 360 * floor((self.angles.x - destangle.x) / 360 + 0.5);
-       self.angles_y -= 360 * floor((self.angles.y - destangle.y) / 360 + 0.5);
-       self.angles_z -= 360 * floor((self.angles.z - destangle.z) / 360 + 0.5);
-       delta = destangle - self.angles;
-
-       switch(tspeedtype)
-       {
-               default:
-               case TSPEED_START:
-               case TSPEED_END:
-               case TSPEED_LINEAR:
-                       traveltime = vlen (delta) / tspeed;
-                       break;
-               case TSPEED_TIME:
-                       traveltime = tspeed;
-                       break;
-       }
-
-       self.think1 = func;
-       self.finalangle = destangle;
-       self.think = SUB_CalcAngleMoveDone;
-
-       if (traveltime < 0.1)
-       {
-               self.avelocity = '0 0 0';
-               self.nextthink = self.ltime + 0.1;
-               return;
-       }
-
-       self.avelocity = delta * (1 / traveltime);
-       self.nextthink = self.ltime + traveltime;
-}
-
-void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
-{
-       entity  oldself;
-
-       oldself = self;
-       self = ent;
-
-       SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);
-
-       self = oldself;
-}
-
 /*
 ==================
 main
index 04477e62919dc48477448f104366f39d2f8224e6..d62bbb6124e57bd014b2976bde0d5532990b5f98 100644 (file)
@@ -70,12 +70,6 @@ void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector co
 
 void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest);
 
-float TSPEED_TIME = -1;
-float TSPEED_LINEAR = 0;
-float TSPEED_START = 1;
-float TSPEED_END = 2;
-// TODO average too?
-
 void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func);
 
 void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func);
diff --git a/qcsrc/server/g_triggers.qc b/qcsrc/server/g_triggers.qc
deleted file mode 100644 (file)
index 9bb1e06..0000000
+++ /dev/null
@@ -1,2129 +0,0 @@
-#include "g_triggers.qh"
-#include "t_jumppads.qh"
-
-void SUB_DontUseTargets()
-{
-}
-
-
-void DelayThink()
-{
-       activator = self.enemy;
-       SUB_UseTargets ();
-       remove(self);
-}
-
-/*
-==============================
-SUB_UseTargets
-
-the global "activator" should be set to the entity that initiated the firing.
-
-If self.delay is set, a DelayedUse entity will be created that will actually
-do the SUB_UseTargets after that many seconds have passed.
-
-Centerprints any self.message to the activator.
-
-Removes all entities with a targetname that match self.killtarget,
-and removes them, so some events can remove other triggers.
-
-Search for (string)targetname in all entities that
-match (string)self.target and call their .use function
-
-==============================
-*/
-void SUB_UseTargets()
-{
-       entity t, stemp, otemp, act;
-       string s;
-       float i;
-
-//
-// check for a delay
-//
-       if (self.delay)
-       {
-       // create a temp object to fire at a later time
-               t = spawn();
-               t.classname = "DelayedUse";
-               t.nextthink = time + self.delay;
-               t.think = DelayThink;
-               t.enemy = activator;
-               t.message = self.message;
-               t.killtarget = self.killtarget;
-               t.target = self.target;
-               t.target2 = self.target2;
-               t.target3 = self.target3;
-               t.target4 = self.target4;
-               return;
-       }
-
-
-//
-// print the message
-//
-       if(self)
-       if(IS_PLAYER(activator) && self.message != "")
-       if(IS_REAL_CLIENT(activator))
-       {
-               centerprint(activator, self.message);
-               if (self.noise == "")
-                       play2(activator, "misc/talk.wav");
-       }
-
-//
-// kill the killtagets
-//
-       s = self.killtarget;
-       if (s != "")
-       {
-               for(t = world; (t = find(t, targetname, s)); )
-                       remove(t);
-       }
-
-//
-// fire targets
-//
-       act = activator;
-       stemp = self;
-       otemp = other;
-
-       if(stemp.target_random)
-               RandomSelection_Init();
-
-       for(i = 0; i < 4; ++i)
-       {
-               switch(i)
-               {
-                       default:
-                       case 0: s = stemp.target; break;
-                       case 1: s = stemp.target2; break;
-                       case 2: s = stemp.target3; break;
-                       case 3: s = stemp.target4; break;
-               }
-               if (s != "")
-               {
-                       for(t = world; (t = find(t, targetname, s)); )
-                       if(t.use)
-                       {
-                               if(stemp.target_random)
-                               {
-                                       RandomSelection_Add(t, 0, string_null, 1, 0);
-                               }
-                               else
-                               {
-                                       self = t;
-                                       other = stemp;
-                                       activator = act;
-                                       self.use();
-                               }
-                       }
-               }
-       }
-
-       if(stemp.target_random && RandomSelection_chosen_ent)
-       {
-               self = RandomSelection_chosen_ent;
-               other = stemp;
-               activator = act;
-               self.use();
-       }
-
-       activator = act;
-       self = stemp;
-       other = otemp;
-}
-
-
-//=============================================================================
-
-// the wait time has passed, so set back up for another activation
-void multi_wait()
-{
-       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()
-{
-       if (self.nextthink > time)
-       {
-               return;         // allready been triggered
-       }
-
-       if (self.classname == "trigger_secret")
-       {
-               if (!IS_PLAYER(self.enemy))
-                       return;
-               found_secrets = found_secrets + 1;
-               WriteByte (MSG_ALL, SVC_FOUNDSECRET);
-       }
-
-       if (self.noise)
-               sound (self.enemy, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-
-// don't trigger again until reset
-       self.takedamage = DAMAGE_NO;
-
-       activator = self.enemy;
-       other = self.goalentity;
-       SUB_UseTargets();
-
-       if (self.wait > 0)
-       {
-               self.think = multi_wait;
-               self.nextthink = time + self.wait;
-       }
-       else if (self.wait == 0)
-       {
-               multi_wait(); // waiting finished
-       }
-       else
-       {       // we can't just remove (self) here, because this is a touch function
-               // called wheil C code is looping through area links...
-               self.touch = func_null;
-       }
-}
-
-void multi_use()
-{
-       self.goalentity = other;
-       self.enemy = activator;
-       multi_trigger();
-}
-
-void multi_touch()
-{
-       if(!(self.spawnflags & 2))
-       if(!other.iscreature)
-                       return;
-
-       if(self.team)
-               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
-                       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
-       }
-
-       EXACTTRIGGER_TOUCH;
-
-       self.enemy = other;
-       self.goalentity = other;
-       multi_trigger ();
-}
-
-void multi_eventdamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       if (!self.takedamage)
-               return;
-       if(self.spawnflags & DOOR_NOSPLASH)
-               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
-                       return;
-       self.health = self.health - damage;
-       if (self.health <= 0)
-       {
-               self.enemy = attacker;
-               self.goalentity = inflictor;
-               multi_trigger();
-       }
-}
-
-void multi_reset()
-{
-       if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
-               self.touch = multi_touch;
-       if (self.max_health)
-       {
-               self.health = self.max_health;
-               self.takedamage = DAMAGE_YES;
-               self.solid = SOLID_BBOX;
-       }
-       self.think = func_null;
-       self.nextthink = 0;
-       self.team = self.team_saved;
-}
-
-/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
-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.
-NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
-sounds
-1)     secret
-2)     beep beep
-3)     large switch
-4)
-set "message" to text string
-*/
-void spawnfunc_trigger_multiple()
-{
-       self.reset = multi_reset;
-       if (self.sounds == 1)
-       {
-               precache_sound ("misc/secret.wav");
-               self.noise = "misc/secret.wav";
-       }
-       else if (self.sounds == 2)
-       {
-               precache_sound ("misc/talk.wav");
-               self.noise = "misc/talk.wav";
-       }
-       else if (self.sounds == 3)
-       {
-               precache_sound ("misc/trigger1.wav");
-               self.noise = "misc/trigger1.wav";
-       }
-
-       if (!self.wait)
-               self.wait = 0.2;
-       else if(self.wait < -1)
-               self.wait = 0;
-       self.use = multi_use;
-
-       EXACTTRIGGER_INIT;
-
-       self.team_saved = self.team;
-
-       if (self.health)
-       {
-               if (self.spawnflags & SPAWNFLAG_NOTOUCH)
-                       objerror ("health and notouch don't make sense\n");
-               self.max_health = self.health;
-               self.event_damage = multi_eventdamage;
-               self.takedamage = DAMAGE_YES;
-               self.solid = SOLID_BBOX;
-               setorigin (self, self.origin);  // make sure it links into the world
-       }
-       else
-       {
-               if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )
-               {
-                       self.touch = multi_touch;
-                       setorigin (self, self.origin);  // make sure it links into the world
-               }
-       }
-}
-
-
-/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
-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 "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.
-sounds
-1)     secret
-2)     beep beep
-3)     large switch
-4)
-set "message" to text string
-*/
-void spawnfunc_trigger_once()
-{
-       self.wait = -1;
-       spawnfunc_trigger_multiple();
-}
-
-//=============================================================================
-
-/*QUAKED spawnfunc_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 spawnfunc_trigger_relay()
-{
-       self.use = SUB_UseTargets;
-       self.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
-}
-
-void delay_use()
-{
-    self.think = SUB_UseTargets;
-    self.nextthink = self.wait;
-}
-
-void delay_reset()
-{
-       self.think = func_null;
-       self.nextthink = 0;
-}
-
-void spawnfunc_trigger_delay()
-{
-    if(!self.wait)
-        self.wait = 1;
-
-    self.use = delay_use;
-    self.reset = delay_reset;
-}
-
-//=============================================================================
-
-
-void counter_use()
-{
-       self.count -= 1;
-       if (self.count < 0)
-               return;
-
-       if (self.count == 0)
-       {
-               if(IS_PLAYER(activator) && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
-                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COMPLETED);
-
-               self.enemy = activator;
-               multi_trigger ();
-       }
-       else
-       {
-               if(IS_PLAYER(activator) && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)
-               if(self.count >= 4)
-                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COUNTER);
-               else
-                       Send_Notification(NOTIF_ONE, activator, MSG_CENTER, CENTER_SEQUENCE_COUNTER_FEWMORE, self.count);
-       }
-}
-
-void counter_reset()
-{
-       self.count = self.cnt;
-       multi_reset();
-}
-
-/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
-Acts as an intermediary for an action that takes multiple inputs.
-
-If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
-
-After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
-*/
-void spawnfunc_trigger_counter()
-{
-       self.wait = -1;
-       if (!self.count)
-               self.count = 2;
-       self.cnt = self.count;
-
-       self.use = counter_use;
-       self.reset = counter_reset;
-}
-
-void trigger_hurt_use()
-{
-       if(IS_PLAYER(activator))
-               self.enemy = activator;
-       else
-               self.enemy = world; // let's just destroy it, if taking over is too much work
-}
-
-void trigger_hurt_touch()
-{
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       if(self.team)
-               if(((self.spawnflags & 4) == 0) == (self.team != other.team))
-                       return;
-
-       // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
-       if (other.iscreature)
-       {
-               if (other.takedamage)
-               if (other.triggerhurttime < time)
-               {
-                       EXACTTRIGGER_TOUCH;
-                       other.triggerhurttime = time + 1;
-
-                       entity own;
-                       own = self.enemy;
-                       if (!IS_PLAYER(own))
-                       {
-                               own = self;
-                               self.enemy = world; // I still hate you all
-                       }
-
-                       Damage (other, self, own, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-               }
-       }
-       else if(other.damagedbytriggers)
-       {
-               if(other.takedamage)
-               {
-                       EXACTTRIGGER_TOUCH;
-                       Damage(other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-               }
-       }
-
-       return;
-}
-
-/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
-Any object touching this will be hurt
-set dmg to damage amount
-defalt dmg = 5
-*/
-void spawnfunc_trigger_hurt()
-{
-       EXACTTRIGGER_INIT;
-       self.active = ACTIVE_ACTIVE;
-       self.touch = trigger_hurt_touch;
-       self.use = trigger_hurt_use;
-       self.enemy = world; // I hate you all
-       if (!self.dmg)
-               self.dmg = 1000;
-       if (self.message == "")
-               self.message = "was in the wrong place";
-       if (self.message2 == "")
-               self.message2 = "was thrown into a world of hurt by";
-       // self.message = "someone like %s always gets wrongplaced";
-
-       if(!trigger_hurt_first)
-               trigger_hurt_first = self;
-       if(trigger_hurt_last)
-               trigger_hurt_last.trigger_hurt_next = self;
-       trigger_hurt_last = self;
-}
-
-float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
-{
-       entity th;
-
-       for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
-               if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
-                       return true;
-
-       return false;
-}
-
-//////////////////////////////////////////////////////////////
-//
-//
-//
-//Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
-//
-//////////////////////////////////////////////////////////////
-
-void trigger_heal_touch()
-{
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
-       if (other.iscreature)
-       {
-               if (other.takedamage)
-               if (!other.deadflag)
-               if (other.triggerhealtime < time)
-               {
-                       EXACTTRIGGER_TOUCH;
-                       other.triggerhealtime = time + 1;
-
-                       if (other.health < self.max_health)
-                       {
-                               other.health = min(other.health + self.health, self.max_health);
-                               other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
-                               sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-                       }
-               }
-       }
-}
-
-void spawnfunc_trigger_heal()
-{
-       self.active = ACTIVE_ACTIVE;
-
-       EXACTTRIGGER_INIT;
-       self.touch = trigger_heal_touch;
-       if (!self.health)
-               self.health = 10;
-       if (!self.max_health)
-               self.max_health = 200; //Max health topoff for field
-       if(self.noise == "")
-               self.noise = "misc/mediumhealth.wav";
-       precache_sound(self.noise);
-}
-
-
-//////////////////////////////////////////////////////////////
-//
-//
-//
-//End trigger_heal
-//
-//////////////////////////////////////////////////////////////
-
-void trigger_gravity_remove(entity own)
-{
-       if(own.trigger_gravity_check.owner == own)
-       {
-               UpdateCSQCProjectile(own);
-               own.gravity = own.trigger_gravity_check.gravity;
-               remove(own.trigger_gravity_check);
-       }
-       else
-               backtrace("Removing a trigger_gravity_check with no valid owner");
-       own.trigger_gravity_check = world;
-}
-void trigger_gravity_check_think()
-{
-       // This spawns when a player enters the gravity zone and checks if he left.
-       // Each frame, self.count is set to 2 by trigger_gravity_touch() and decreased by 1 here.
-       // It the player has left the gravity trigger, this will be allowed to reach 0 and indicate that.
-       if(self.count <= 0)
-       {
-               if(self.owner.trigger_gravity_check == self)
-                       trigger_gravity_remove(self.owner);
-               else
-                       remove(self);
-               return;
-       }
-       else
-       {
-               self.count -= 1;
-               self.nextthink = time;
-       }
-}
-
-void trigger_gravity_use()
-{
-       self.state = !self.state;
-}
-
-void trigger_gravity_touch()
-{
-       float g;
-
-       if(self.state != true)
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-       g = self.gravity;
-
-       if (!(self.spawnflags & 1))
-       {
-               if(other.trigger_gravity_check)
-               {
-                       if(self == other.trigger_gravity_check.enemy)
-                       {
-                               // same?
-                               other.trigger_gravity_check.count = 2; // gravity one more frame...
-                               return;
-                       }
-
-                       // compare prio
-                       if(self.cnt > other.trigger_gravity_check.enemy.cnt)
-                               trigger_gravity_remove(other);
-                       else
-                               return;
-               }
-               other.trigger_gravity_check = spawn();
-               other.trigger_gravity_check.enemy = self;
-               other.trigger_gravity_check.owner = other;
-               other.trigger_gravity_check.gravity = other.gravity;
-               other.trigger_gravity_check.think = trigger_gravity_check_think;
-               other.trigger_gravity_check.nextthink = time;
-               other.trigger_gravity_check.count = 2;
-               if(other.gravity)
-                       g *= other.gravity;
-       }
-
-       if (other.gravity != g)
-       {
-               other.gravity = g;
-               if(self.noise != "")
-                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-               UpdateCSQCProjectile(self.owner);
-       }
-}
-
-void spawnfunc_trigger_gravity()
-{
-       if(self.gravity == 1)
-               return;
-
-       EXACTTRIGGER_INIT;
-       self.touch = trigger_gravity_touch;
-       if(self.noise != "")
-               precache_sound(self.noise);
-
-       self.state = true;
-       IFTARGETED
-       {
-               self.use = trigger_gravity_use;
-               if(self.spawnflags & 2)
-                       self.state = false;
-       }
-}
-
-//=============================================================================
-
-// TODO add a way to do looped sounds with sound(); then complete this entity
-void target_speaker_use_activator()
-{
-       if (!IS_REAL_CLIENT(activator))
-               return;
-       string snd;
-       if(substring(self.noise, 0, 1) == "*")
-       {
-               var .string sample;
-               sample = GetVoiceMessageSampleField(substring(self.noise, 1, -1));
-               if(GetPlayerSoundSampleField_notFound)
-                       snd = "misc/null.wav";
-               else if(activator.sample == "")
-                       snd = "misc/null.wav";
-               else
-               {
-                       tokenize_console(activator.sample);
-                       float n;
-                       n = stof(argv(1));
-                       if(n > 0)
-                               snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
-                       else
-                               snd = strcat(argv(0), ".wav"); // randomization
-               }
-       }
-       else
-               snd = self.noise;
-       msg_entity = activator;
-       soundto(MSG_ONE, self, CH_TRIGGER, snd, VOL_BASE * self.volume, self.atten);
-}
-void target_speaker_use_on()
-{
-       string snd;
-       if(substring(self.noise, 0, 1) == "*")
-       {
-               var .string sample;
-               sample = GetVoiceMessageSampleField(substring(self.noise, 1, -1));
-               if(GetPlayerSoundSampleField_notFound)
-                       snd = "misc/null.wav";
-               else if(activator.sample == "")
-                       snd = "misc/null.wav";
-               else
-               {
-                       tokenize_console(activator.sample);
-                       float n;
-                       n = stof(argv(1));
-                       if(n > 0)
-                               snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
-                       else
-                               snd = strcat(argv(0), ".wav"); // randomization
-               }
-       }
-       else
-               snd = self.noise;
-       sound(self, CH_TRIGGER_SINGLE, snd, VOL_BASE * self.volume, self.atten);
-       if(self.spawnflags & 3)
-               self.use = target_speaker_use_off;
-}
-void target_speaker_use_off()
-{
-       sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASE * self.volume, self.atten);
-       self.use = target_speaker_use_on;
-}
-void target_speaker_reset()
-{
-       if(self.spawnflags & 1) // LOOPED_ON
-       {
-               if(self.use == target_speaker_use_on)
-                       target_speaker_use_on();
-       }
-       else if(self.spawnflags & 2)
-       {
-               if(self.use == target_speaker_use_off)
-                       target_speaker_use_off();
-       }
-}
-
-void spawnfunc_target_speaker()
-{
-       // TODO: "*" prefix to sound file name
-       // TODO: wait and random (just, HOW? random is not a field)
-       if(self.noise)
-               precache_sound (self.noise);
-
-       if(!self.atten && !(self.spawnflags & 4))
-       {
-               IFTARGETED
-                       self.atten = ATTEN_NORM;
-               else
-                       self.atten = ATTEN_STATIC;
-       }
-       else if(self.atten < 0)
-               self.atten = 0;
-
-       if(!self.volume)
-               self.volume = 1;
-
-       IFTARGETED
-       {
-               if(self.spawnflags & 8) // ACTIVATOR
-                       self.use = target_speaker_use_activator;
-               else if(self.spawnflags & 1) // LOOPED_ON
-               {
-                       target_speaker_use_on();
-                       self.reset = target_speaker_reset;
-               }
-               else if(self.spawnflags & 2) // LOOPED_OFF
-               {
-                       self.use = target_speaker_use_on;
-                       self.reset = target_speaker_reset;
-               }
-               else
-                       self.use = target_speaker_use_on;
-       }
-       else if(self.spawnflags & 1) // LOOPED_ON
-       {
-               ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
-               remove(self);
-       }
-       else if(self.spawnflags & 2) // LOOPED_OFF
-       {
-               objerror("This sound entity can never be activated");
-       }
-       else
-       {
-               // Quake/Nexuiz fallback
-               ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
-               remove(self);
-       }
-}
-
-
-void spawnfunc_func_stardust() {
-       self.effects = EF_STARDUST;
-}
-
-float pointparticles_SendEntity(entity to, float fl)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
-
-       // optional features to save space
-       fl = fl & 0x0F;
-       if(self.spawnflags & 2)
-               fl |= 0x10; // absolute count on toggle-on
-       if(self.movedir != '0 0 0' || self.velocity != '0 0 0')
-               fl |= 0x20; // 4 bytes - saves CPU
-       if(self.waterlevel || self.count != 1)
-               fl |= 0x40; // 4 bytes - obscure features almost never used
-       if(self.mins != '0 0 0' || self.maxs != '0 0 0')
-               fl |= 0x80; // 14 bytes - saves lots of space
-
-       WriteByte(MSG_ENTITY, fl);
-       if(fl & 2)
-       {
-               if(self.state)
-                       WriteCoord(MSG_ENTITY, self.impulse);
-               else
-                       WriteCoord(MSG_ENTITY, 0); // off
-       }
-       if(fl & 4)
-       {
-               WriteCoord(MSG_ENTITY, self.origin.x);
-               WriteCoord(MSG_ENTITY, self.origin.y);
-               WriteCoord(MSG_ENTITY, self.origin.z);
-       }
-       if(fl & 1)
-       {
-               if(self.model != "null")
-               {
-                       WriteShort(MSG_ENTITY, self.modelindex);
-                       if(fl & 0x80)
-                       {
-                               WriteCoord(MSG_ENTITY, self.mins.x);
-                               WriteCoord(MSG_ENTITY, self.mins.y);
-                               WriteCoord(MSG_ENTITY, self.mins.z);
-                               WriteCoord(MSG_ENTITY, self.maxs.x);
-                               WriteCoord(MSG_ENTITY, self.maxs.y);
-                               WriteCoord(MSG_ENTITY, self.maxs.z);
-                       }
-               }
-               else
-               {
-                       WriteShort(MSG_ENTITY, 0);
-                       if(fl & 0x80)
-                       {
-                               WriteCoord(MSG_ENTITY, self.maxs.x);
-                               WriteCoord(MSG_ENTITY, self.maxs.y);
-                               WriteCoord(MSG_ENTITY, self.maxs.z);
-                       }
-               }
-               WriteShort(MSG_ENTITY, self.cnt);
-               if(fl & 0x20)
-               {
-                       WriteShort(MSG_ENTITY, compressShortVector(self.velocity));
-                       WriteShort(MSG_ENTITY, compressShortVector(self.movedir));
-               }
-               if(fl & 0x40)
-               {
-                       WriteShort(MSG_ENTITY, self.waterlevel * 16.0);
-                       WriteByte(MSG_ENTITY, self.count * 16.0);
-               }
-               WriteString(MSG_ENTITY, self.noise);
-               if(self.noise != "")
-               {
-                       WriteByte(MSG_ENTITY, floor(self.atten * 64));
-                       WriteByte(MSG_ENTITY, floor(self.volume * 255));
-               }
-               WriteString(MSG_ENTITY, self.bgmscript);
-               if(self.bgmscript != "")
-               {
-                       WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64));
-                       WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64));
-                       WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255));
-                       WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64));
-               }
-       }
-       return 1;
-}
-
-void pointparticles_use()
-{
-       self.state = !self.state;
-       self.SendFlags |= 2;
-}
-
-void pointparticles_think()
-{
-       if(self.origin != self.oldorigin)
-       {
-               self.SendFlags |= 4;
-               self.oldorigin = self.origin;
-       }
-       self.nextthink = time;
-}
-
-void pointparticles_reset()
-{
-       if(self.spawnflags & 1)
-               self.state = 1;
-       else
-               self.state = 0;
-}
-
-void spawnfunc_func_pointparticles()
-{
-       if(self.model != "")
-               setmodel(self, self.model);
-       if(self.noise != "")
-               precache_sound (self.noise);
-
-       if(!self.bgmscriptsustain)
-               self.bgmscriptsustain = 1;
-       else if(self.bgmscriptsustain < 0)
-               self.bgmscriptsustain = 0;
-
-       if(!self.atten)
-               self.atten = ATTEN_NORM;
-       else if(self.atten < 0)
-               self.atten = 0;
-       if(!self.volume)
-               self.volume = 1;
-       if(!self.count)
-               self.count = 1;
-       if(!self.impulse)
-               self.impulse = 1;
-
-       if(!self.modelindex)
-       {
-               setorigin(self, self.origin + self.mins);
-               setsize(self, '0 0 0', self.maxs - self.mins);
-       }
-       if(!self.cnt)
-               self.cnt = particleeffectnum(self.mdl);
-
-       Net_LinkEntity(self, (self.spawnflags & 4), 0, pointparticles_SendEntity);
-
-       IFTARGETED
-       {
-               self.use = pointparticles_use;
-               self.reset = pointparticles_reset;
-               self.reset();
-       }
-       else
-               self.state = 1;
-       self.think = pointparticles_think;
-       self.nextthink = time;
-}
-
-void spawnfunc_func_sparks()
-{
-       // self.cnt is the amount of sparks that one burst will spawn
-       if(self.cnt < 1) {
-               self.cnt = 25.0; // nice default value
-       }
-
-       // self.wait is the probability that a sparkthink will spawn a spark shower
-       // range: 0 - 1, but 0 makes little sense, so...
-       if(self.wait < 0.05) {
-               self.wait = 0.25; // nice default value
-       }
-
-       self.count = self.cnt;
-       self.mins = '0 0 0';
-       self.maxs = '0 0 0';
-       self.velocity = '0 0 -1';
-       self.mdl = "TE_SPARK";
-       self.impulse = 10 * self.wait; // by default 2.5/sec
-       self.wait = 0;
-       self.cnt = 0; // use mdl
-
-       spawnfunc_func_pointparticles();
-}
-
-float rainsnow_SendEntity(entity to, float sf)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
-       WriteByte(MSG_ENTITY, self.state);
-       WriteCoord(MSG_ENTITY, self.origin.x + self.mins.x);
-       WriteCoord(MSG_ENTITY, self.origin.y + self.mins.y);
-       WriteCoord(MSG_ENTITY, self.origin.z + self.mins.z);
-       WriteCoord(MSG_ENTITY, self.maxs.x - self.mins.x);
-       WriteCoord(MSG_ENTITY, self.maxs.y - self.mins.y);
-       WriteCoord(MSG_ENTITY, self.maxs.z - self.mins.z);
-       WriteShort(MSG_ENTITY, compressShortVector(self.dest));
-       WriteShort(MSG_ENTITY, self.count);
-       WriteByte(MSG_ENTITY, self.cnt);
-       return 1;
-}
-
-/*QUAKED spawnfunc_func_rain (0 .5 .8) ?
-This is an invisible area like a trigger, which rain falls inside of.
-
-Keys:
-"velocity"
- falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
-"cnt"
- sets color of rain (default 12 - white)
-"count"
- adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
-*/
-void spawnfunc_func_rain()
-{
-       self.dest = self.velocity;
-       self.velocity = '0 0 0';
-       if (!self.dest)
-               self.dest = '0 0 -700';
-       self.angles = '0 0 0';
-       self.movetype = MOVETYPE_NONE;
-       self.solid = SOLID_NOT;
-       SetBrushEntityModel();
-       if (!self.cnt)
-               self.cnt = 12;
-       if (!self.count)
-               self.count = 2000;
-       self.count = 0.01 * self.count * (self.size.x / 1024) * (self.size.y / 1024);
-       if (self.count < 1)
-               self.count = 1;
-       if(self.count > 65535)
-               self.count = 65535;
-
-       self.state = 1; // 1 is rain, 0 is snow
-       self.Version = 1;
-
-       Net_LinkEntity(self, false, 0, rainsnow_SendEntity);
-}
-
-
-/*QUAKED spawnfunc_func_snow (0 .5 .8) ?
-This is an invisible area like a trigger, which snow falls inside of.
-
-Keys:
-"velocity"
- falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
-"cnt"
- sets color of rain (default 12 - white)
-"count"
- adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
-*/
-void spawnfunc_func_snow()
-{
-       self.dest = self.velocity;
-       self.velocity = '0 0 0';
-       if (!self.dest)
-               self.dest = '0 0 -300';
-       self.angles = '0 0 0';
-       self.movetype = MOVETYPE_NONE;
-       self.solid = SOLID_NOT;
-       SetBrushEntityModel();
-       if (!self.cnt)
-               self.cnt = 12;
-       if (!self.count)
-               self.count = 2000;
-       self.count = 0.01 * self.count * (self.size.x / 1024) * (self.size.y / 1024);
-       if (self.count < 1)
-               self.count = 1;
-       if(self.count > 65535)
-               self.count = 65535;
-
-       self.state = 0; // 1 is rain, 0 is snow
-       self.Version = 1;
-
-       Net_LinkEntity(self, false, 0, rainsnow_SendEntity);
-}
-
-void misc_laser_aim()
-{
-       vector a;
-       if(self.enemy)
-       {
-               if(self.spawnflags & 2)
-               {
-                       if(self.enemy.origin != self.mangle)
-                       {
-                               self.mangle = self.enemy.origin;
-                               self.SendFlags |= 2;
-                       }
-               }
-               else
-               {
-                       a = vectoangles(self.enemy.origin - self.origin);
-                       a.x = -a.x;
-                       if(a != self.mangle)
-                       {
-                               self.mangle = a;
-                               self.SendFlags |= 2;
-                       }
-               }
-       }
-       else
-       {
-               if(self.angles != self.mangle)
-               {
-                       self.mangle = self.angles;
-                       self.SendFlags |= 2;
-               }
-       }
-       if(self.origin != self.oldorigin)
-       {
-               self.SendFlags |= 1;
-               self.oldorigin = self.origin;
-       }
-}
-
-void misc_laser_init()
-{
-       if(self.target != "")
-               self.enemy = find(world, targetname, self.target);
-}
-
-void misc_laser_think()
-{
-       vector o;
-       entity oldself;
-       entity hitent;
-       vector hitloc;
-
-       self.nextthink = time;
-
-       if(!self.state)
-               return;
-
-       misc_laser_aim();
-
-       if(self.enemy)
-       {
-               o = self.enemy.origin;
-               if (!(self.spawnflags & 2))
-                       o = self.origin + normalize(o - self.origin) * 32768;
-       }
-       else
-       {
-               makevectors(self.mangle);
-               o = self.origin + v_forward * 32768;
-       }
-
-       if(self.dmg || self.enemy.target != "")
-       {
-               traceline(self.origin, o, MOVE_NORMAL, self);
-       }
-       hitent = trace_ent;
-       hitloc = trace_endpos;
-
-       if(self.enemy.target != "") // DETECTOR laser
-       {
-               if(trace_ent.iscreature)
-               {
-                       self.pusher = hitent;
-                       if(!self.count)
-                       {
-                               self.count = 1;
-
-                               oldself = self;
-                               self = self.enemy;
-                               activator = self.pusher;
-                               SUB_UseTargets();
-                               self = oldself;
-                       }
-               }
-               else
-               {
-                       if(self.count)
-                       {
-                               self.count = 0;
-
-                               oldself = self;
-                               self = self.enemy;
-                               activator = self.pusher;
-                               SUB_UseTargets();
-                               self = oldself;
-                       }
-               }
-       }
-
-       if(self.dmg)
-       {
-               if(self.team)
-                       if(((self.spawnflags & 8) == 0) == (self.team != hitent.team))
-                               return;
-               if(hitent.takedamage)
-                       Damage(hitent, self, self, ((self.dmg < 0) ? 100000 : (self.dmg * frametime)), DEATH_HURTTRIGGER, hitloc, '0 0 0');
-       }
-}
-
-float laser_SendEntity(entity to, float fl)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
-       fl = fl - (fl & 0xF0); // use that bit to indicate finite length laser
-       if(self.spawnflags & 2)
-               fl |= 0x80;
-       if(self.alpha)
-               fl |= 0x40;
-       if(self.scale != 1 || self.modelscale != 1)
-               fl |= 0x20;
-       if(self.spawnflags & 4)
-               fl |= 0x10;
-       WriteByte(MSG_ENTITY, fl);
-       if(fl & 1)
-       {
-               WriteCoord(MSG_ENTITY, self.origin.x);
-               WriteCoord(MSG_ENTITY, self.origin.y);
-               WriteCoord(MSG_ENTITY, self.origin.z);
-       }
-       if(fl & 8)
-       {
-               WriteByte(MSG_ENTITY, self.colormod.x * 255.0);
-               WriteByte(MSG_ENTITY, self.colormod.y * 255.0);
-               WriteByte(MSG_ENTITY, self.colormod.z * 255.0);
-               if(fl & 0x40)
-                       WriteByte(MSG_ENTITY, self.alpha * 255.0);
-               if(fl & 0x20)
-               {
-                       WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
-                       WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
-               }
-               if((fl & 0x80) || !(fl & 0x10)) // effect doesn't need sending if the laser is infinite and has collision testing turned off
-                       WriteShort(MSG_ENTITY, self.cnt + 1);
-       }
-       if(fl & 2)
-       {
-               if(fl & 0x80)
-               {
-                       WriteCoord(MSG_ENTITY, self.enemy.origin.x);
-                       WriteCoord(MSG_ENTITY, self.enemy.origin.y);
-                       WriteCoord(MSG_ENTITY, self.enemy.origin.z);
-               }
-               else
-               {
-                       WriteAngle(MSG_ENTITY, self.mangle.x);
-                       WriteAngle(MSG_ENTITY, self.mangle.y);
-               }
-       }
-       if(fl & 4)
-               WriteByte(MSG_ENTITY, self.state);
-       return 1;
-}
-
-/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
-Any object touching the beam will be hurt
-Keys:
-"target"
- spawnfunc_target_position where the laser ends
-"mdl"
- name of beam end effect to use
-"colormod"
- color of the beam (default: red)
-"dmg"
- damage per second (-1 for a laser that kills immediately)
-*/
-void laser_use()
-{
-       self.state = !self.state;
-       self.SendFlags |= 4;
-       misc_laser_aim();
-}
-
-void laser_reset()
-{
-       if(self.spawnflags & 1)
-               self.state = 1;
-       else
-               self.state = 0;
-}
-
-void spawnfunc_misc_laser()
-{
-       if(self.mdl)
-       {
-               if(self.mdl == "none")
-                       self.cnt = -1;
-               else
-               {
-                       self.cnt = particleeffectnum(self.mdl);
-                       if(self.cnt < 0)
-                               if(self.dmg)
-                                       self.cnt = particleeffectnum("laser_deadly");
-               }
-       }
-       else if(!self.cnt)
-       {
-               if(self.dmg)
-                       self.cnt = particleeffectnum("laser_deadly");
-               else
-                       self.cnt = -1;
-       }
-       if(self.cnt < 0)
-               self.cnt = -1;
-
-       if(self.colormod == '0 0 0')
-               if(!self.alpha)
-                       self.colormod = '1 0 0';
-       if(self.message == "")
-               self.message = "saw the light";
-       if (self.message2 == "")
-               self.message2 = "was pushed into a laser by";
-       if(!self.scale)
-               self.scale = 1;
-       if(!self.modelscale)
-               self.modelscale = 1;
-       else if(self.modelscale < 0)
-               self.modelscale = 0;
-       self.think = misc_laser_think;
-       self.nextthink = time;
-       InitializeEntity(self, misc_laser_init, INITPRIO_FINDTARGET);
-
-       self.mangle = self.angles;
-
-       Net_LinkEntity(self, false, 0, laser_SendEntity);
-
-       IFTARGETED
-       {
-               self.reset = laser_reset;
-               laser_reset();
-               self.use = laser_use;
-       }
-       else
-               self.state = 1;
-}
-
-// tZorks trigger impulse / gravity
-
-// targeted (directional) mode
-void trigger_impulse_touch1()
-{
-       entity targ;
-    float pushdeltatime;
-    float str;
-
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       if (!isPushable(other))
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-    targ = find(world, targetname, self.target);
-    if(!targ)
-    {
-        objerror("trigger_force without a (valid) .target!\n");
-        remove(self);
-        return;
-    }
-
-    str = min(self.radius, vlen(self.origin - other.origin));
-
-    if(self.falloff == 1)
-        str = (str / self.radius) * self.strength;
-    else if(self.falloff == 2)
-        str = (1 - (str / self.radius)) * self.strength;
-    else
-        str = self.strength;
-
-    pushdeltatime = time - other.lastpushtime;
-    if (pushdeltatime > 0.15) pushdeltatime = 0;
-    other.lastpushtime = time;
-    if(!pushdeltatime) return;
-
-    other.velocity = other.velocity + normalize(targ.origin - self.origin) * str * pushdeltatime;
-    other.flags &= ~FL_ONGROUND;
-    UpdateCSQCProjectile(other);
-}
-
-// Directionless (accelerator/decelerator) mode
-void trigger_impulse_touch2()
-{
-    float pushdeltatime;
-
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       if (!isPushable(other))
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-    pushdeltatime = time - other.lastpushtime;
-    if (pushdeltatime > 0.15) pushdeltatime = 0;
-    other.lastpushtime = time;
-    if(!pushdeltatime) return;
-
-    // div0: ticrate independent, 1 = identity (not 20)
-    other.velocity = other.velocity * pow(self.strength, pushdeltatime);
-    UpdateCSQCProjectile(other);
-}
-
-// Spherical (gravity/repulsor) mode
-void trigger_impulse_touch3()
-{
-    float pushdeltatime;
-    float str;
-
-       if (self.active != ACTIVE_ACTIVE)
-               return;
-
-       if (!isPushable(other))
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-    pushdeltatime = time - other.lastpushtime;
-    if (pushdeltatime > 0.15) pushdeltatime = 0;
-    other.lastpushtime = time;
-    if(!pushdeltatime) return;
-
-    setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
-
-       str = min(self.radius, vlen(self.origin - other.origin));
-
-    if(self.falloff == 1)
-        str = (1 - str / self.radius) * self.strength; // 1 in the inside
-    else if(self.falloff == 2)
-        str = (str / self.radius) * self.strength; // 0 in the inside
-    else
-        str = self.strength;
-
-    other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
-    UpdateCSQCProjectile(other);
-}
-
-/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
--------- KEYS --------
-target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
-         If not, this trigger acts like a damper/accelerator field.
-
-strength : This is how mutch force to add in the direction of .target each second
-           when .target is set. If not, this is hoe mutch to slow down/accelerate
-           someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
-
-radius   : If set, act as a spherical device rather then a liniar one.
-
-falloff : 0 = none, 1 = liniar, 2 = inverted liniar
-
--------- NOTES --------
-Use a brush textured with common/origin in the trigger entity to determine the origin of the force
-in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
-*/
-
-void spawnfunc_trigger_impulse()
-{
-       self.active = ACTIVE_ACTIVE;
-
-       EXACTTRIGGER_INIT;
-    if(self.radius)
-    {
-        if(!self.strength) self.strength = 2000 * autocvar_g_triggerimpulse_radial_multiplier;
-        setorigin(self, self.origin);
-        setsize(self, '-1 -1 -1' * self.radius,'1 1 1' * self.radius);
-        self.touch = trigger_impulse_touch3;
-    }
-    else
-    {
-        if(self.target)
-        {
-            if(!self.strength) self.strength = 950 * autocvar_g_triggerimpulse_directional_multiplier;
-            self.touch = trigger_impulse_touch1;
-        }
-        else
-        {
-            if(!self.strength) self.strength = 0.9;
-                       self.strength = pow(self.strength, autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier;
-            self.touch = trigger_impulse_touch2;
-        }
-    }
-}
-
-/*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
-"Flip-flop" trigger gate... lets only every second trigger event through
-*/
-void flipflop_use()
-{
-       self.state = !self.state;
-       if(self.state)
-               SUB_UseTargets();
-}
-
-void spawnfunc_trigger_flipflop()
-{
-       if(self.spawnflags & 1)
-               self.state = 1;
-       self.use = flipflop_use;
-       self.reset = spawnfunc_trigger_flipflop; // perfect resetter
-}
-
-/*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
-"Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
-*/
-void monoflop_use()
-{
-       self.nextthink = time + self.wait;
-       self.enemy = activator;
-       if(self.state)
-               return;
-       self.state = 1;
-       SUB_UseTargets();
-}
-void monoflop_fixed_use()
-{
-       if(self.state)
-               return;
-       self.nextthink = time + self.wait;
-       self.state = 1;
-       self.enemy = activator;
-       SUB_UseTargets();
-}
-
-void monoflop_think()
-{
-       self.state = 0;
-       activator = self.enemy;
-       SUB_UseTargets();
-}
-
-void monoflop_reset()
-{
-       self.state = 0;
-       self.nextthink = 0;
-}
-
-void spawnfunc_trigger_monoflop()
-{
-       if(!self.wait)
-               self.wait = 1;
-       if(self.spawnflags & 1)
-               self.use = monoflop_fixed_use;
-       else
-               self.use = monoflop_use;
-       self.think = monoflop_think;
-       self.state = 0;
-       self.reset = monoflop_reset;
-}
-
-void multivibrator_send()
-{
-       float newstate;
-       float cyclestart;
-
-       cyclestart = floor((time + self.phase) / (self.wait + self.respawntime)) * (self.wait + self.respawntime) - self.phase;
-
-       newstate = (time < cyclestart + self.wait);
-
-       activator = self;
-       if(self.state != newstate)
-               SUB_UseTargets();
-       self.state = newstate;
-
-       if(self.state)
-               self.nextthink = cyclestart + self.wait + 0.01;
-       else
-               self.nextthink = cyclestart + self.wait + self.respawntime + 0.01;
-}
-
-void multivibrator_toggle()
-{
-       if(self.nextthink == 0)
-       {
-               multivibrator_send();
-       }
-       else
-       {
-               if(self.state)
-               {
-                       SUB_UseTargets();
-                       self.state = 0;
-               }
-               self.nextthink = 0;
-       }
-}
-
-void multivibrator_reset()
-{
-       if(!(self.spawnflags & 1))
-               self.nextthink = 0; // wait for a trigger event
-       else
-               self.nextthink = max(1, time);
-}
-
-/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
-"Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
--------- KEYS --------
-target: trigger all entities with this targetname when it goes off
-targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
-phase: offset of the timing
-wait: "on" cycle time (default: 1)
-respawntime: "off" cycle time (default: same as wait)
--------- SPAWNFLAGS --------
-START_ON: assume it is already turned on (when targeted)
-*/
-void spawnfunc_trigger_multivibrator()
-{
-       if(!self.wait)
-               self.wait = 1;
-       if(!self.respawntime)
-               self.respawntime = self.wait;
-
-       self.state = 0;
-       self.use = multivibrator_toggle;
-       self.think = multivibrator_send;
-       self.nextthink = max(1, time);
-
-       IFTARGETED
-               multivibrator_reset();
-}
-
-
-void follow_init()
-{
-       entity src, dst;
-       src = world;
-       dst = world;
-       if(self.killtarget != "")
-               src = find(world, targetname, self.killtarget);
-       if(self.target != "")
-               dst = find(world, targetname, self.target);
-
-       if(!src && !dst)
-       {
-               objerror("follow: could not find target/killtarget");
-               return;
-       }
-
-       if(self.jointtype)
-       {
-               // already done :P entity must stay
-               self.aiment = src;
-               self.enemy = dst;
-       }
-       else if(!src || !dst)
-       {
-               objerror("follow: could not find target/killtarget");
-               return;
-       }
-       else if(self.spawnflags & 1)
-       {
-               // attach
-               if(self.spawnflags & 2)
-               {
-                       setattachment(dst, src, self.message);
-               }
-               else
-               {
-                       attach_sameorigin(dst, src, self.message);
-               }
-
-               dst.solid = SOLID_NOT; // solid doesn't work with attachment
-               remove(self);
-       }
-       else
-       {
-               if(self.spawnflags & 2)
-               {
-                       dst.movetype = MOVETYPE_FOLLOW;
-                       dst.aiment = src;
-                       // dst.punchangle = '0 0 0'; // keep unchanged
-                       dst.view_ofs = dst.origin;
-                       dst.v_angle = dst.angles;
-               }
-               else
-               {
-                       follow_sameorigin(dst, src);
-               }
-
-               remove(self);
-       }
-}
-
-void spawnfunc_misc_follow()
-{
-       InitializeEntity(self, follow_init, INITPRIO_FINDTARGET);
-}
-
-
-
-void gamestart_use() {
-       activator = self;
-       SUB_UseTargets();
-       remove(self);
-}
-
-void spawnfunc_trigger_gamestart() {
-       self.use = gamestart_use;
-       self.reset2 = spawnfunc_trigger_gamestart;
-
-       if(self.wait)
-       {
-               self.think = self.use;
-               self.nextthink = game_starttime + self.wait;
-       }
-       else
-               InitializeEntity(self, gamestart_use, INITPRIO_FINDTARGET);
-}
-
-
-
-
-void target_voicescript_clear(entity pl)
-{
-       pl.voicescript = world;
-}
-
-void target_voicescript_use()
-{
-       if(activator.voicescript != self)
-       {
-               activator.voicescript = self;
-               activator.voicescript_index = 0;
-               activator.voicescript_nextthink = time + self.delay;
-       }
-}
-
-void target_voicescript_next(entity pl)
-{
-       entity vs;
-       float i, n, dt;
-
-       vs = pl.voicescript;
-       if(!vs)
-               return;
-       if(vs.message == "")
-               return;
-       if (!IS_PLAYER(pl))
-               return;
-       if(gameover)
-               return;
-
-       if(time >= pl.voicescript_voiceend)
-       {
-               if(time >= pl.voicescript_nextthink)
-               {
-                       // get the next voice...
-                       n = tokenize_console(vs.message);
-
-                       if(pl.voicescript_index < vs.cnt)
-                               i = pl.voicescript_index * 2;
-                       else if(n > vs.cnt * 2)
-                               i = ((pl.voicescript_index - vs.cnt) % ((n - vs.cnt * 2 - 1) / 2)) * 2 + vs.cnt * 2 + 1;
-                       else
-                               i = -1;
-
-                       if(i >= 0)
-                       {
-                               play2(pl, strcat(vs.netname, "/", argv(i), ".wav"));
-                               dt = stof(argv(i + 1));
-                               if(dt >= 0)
-                               {
-                                       pl.voicescript_voiceend = time + dt;
-                                       pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random());
-                               }
-                               else
-                               {
-                                       pl.voicescript_voiceend = time - dt;
-                                       pl.voicescript_nextthink = pl.voicescript_voiceend;
-                               }
-
-                               pl.voicescript_index += 1;
-                       }
-                       else
-                       {
-                               pl.voicescript = world; // stop trying then
-                       }
-               }
-       }
-}
-
-void spawnfunc_target_voicescript()
-{
-       // netname: directory of the sound files
-       // message: list of "sound file" duration "sound file" duration, a *, and again a list
-       //          foo1 4.1 foo2 4.0 foo3 -3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7
-       //          Here, a - in front of the duration means that no delay is to be
-       //          added after this message
-       // wait: average time between messages
-       // delay: initial delay before the first message
-
-       float i, n;
-       self.use = target_voicescript_use;
-
-       n = tokenize_console(self.message);
-       self.cnt = n / 2;
-       for(i = 0; i+1 < n; i += 2)
-       {
-               if(argv(i) == "*")
-               {
-                       self.cnt = i / 2;
-                       ++i;
-               }
-               precache_sound(strcat(self.netname, "/", argv(i), ".wav"));
-       }
-}
-
-
-
-void trigger_relay_teamcheck_use()
-{
-       if(activator.team)
-       {
-               if(self.spawnflags & 2)
-               {
-                       if(activator.team != self.team)
-                               SUB_UseTargets();
-               }
-               else
-               {
-                       if(activator.team == self.team)
-                               SUB_UseTargets();
-               }
-       }
-       else
-       {
-               if(self.spawnflags & 1)
-                       SUB_UseTargets();
-       }
-}
-
-void trigger_relay_teamcheck_reset()
-{
-       self.team = self.team_saved;
-}
-
-void spawnfunc_trigger_relay_teamcheck()
-{
-       self.team_saved = self.team;
-       self.use = trigger_relay_teamcheck_use;
-       self.reset = trigger_relay_teamcheck_reset;
-}
-
-
-
-void trigger_disablerelay_use()
-{
-       entity e;
-
-       float a, b;
-       a = b = 0;
-
-       for(e = world; (e = find(e, targetname, self.target)); )
-       {
-               if(e.use == SUB_UseTargets)
-               {
-                       e.use = SUB_DontUseTargets;
-                       ++a;
-               }
-               else if(e.use == SUB_DontUseTargets)
-               {
-                       e.use = SUB_UseTargets;
-                       ++b;
-               }
-       }
-
-       if((!a) == (!b))
-               print("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!\n");
-}
-
-void spawnfunc_trigger_disablerelay()
-{
-       self.use = trigger_disablerelay_use;
-}
-
-string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin)
-{
-       float domatch, dotrigger, matchstart, l;
-       string s, msg;
-       entity oldself;
-       string savemessage;
-
-       magicear_matched = false;
-
-       dotrigger = ((IS_PLAYER(source)) && (source.deadflag == DEAD_NO) && ((ear.radius == 0) || (vlen(source.origin - ear.origin) <= ear.radius)));
-       domatch = ((ear.spawnflags & 32) || dotrigger);
-
-       if (!domatch)
-               return msgin;
-
-       if (!msgin)
-       {
-               // we are in TUBA mode!
-               if (!(ear.spawnflags & 256))
-                       return msgin;
-
-               if(!W_Tuba_HasPlayed(source, ear.message, ear.movedir.x, !(ear.spawnflags & 512), ear.movedir.y, ear.movedir.z))
-                       return msgin;
-
-               magicear_matched = true;
-
-               if(dotrigger)
-               {
-                       oldself = self;
-                       activator = source;
-                       self = ear;
-                       savemessage = self.message;
-                       self.message = string_null;
-                       SUB_UseTargets();
-                       self.message = savemessage;
-                       self = oldself;
-               }
-
-               if(ear.netname != "")
-                       return ear.netname;
-
-               return msgin;
-       }
-
-       if(ear.spawnflags & 256) // ENOTUBA
-               return msgin;
-
-       if(privatesay)
-       {
-               if(ear.spawnflags & 4)
-                       return msgin;
-       }
-       else
-       {
-               if(!teamsay)
-                       if(ear.spawnflags & 1)
-                               return msgin;
-               if(teamsay > 0)
-                       if(ear.spawnflags & 2)
-                               return msgin;
-               if(teamsay < 0)
-                       if(ear.spawnflags & 8)
-                               return msgin;
-       }
-
-       matchstart = -1;
-       l = strlen(ear.message);
-
-       if(ear.spawnflags & 128)
-               msg = msgin;
-       else
-               msg = strdecolorize(msgin);
-
-       if(substring(ear.message, 0, 1) == "*")
-       {
-               if(substring(ear.message, -1, 1) == "*")
-               {
-                       // two wildcards
-                       // as we need multi-replacement here...
-                       s = substring(ear.message, 1, -2);
-                       l -= 2;
-                       if(strstrofs(msg, s, 0) >= 0)
-                               matchstart = -2; // we use strreplace on s
-               }
-               else
-               {
-                       // match at start
-                       s = substring(ear.message, 1, -1);
-                       l -= 1;
-                       if(substring(msg, -l, l) == s)
-                               matchstart = strlen(msg) - l;
-               }
-       }
-       else
-       {
-               if(substring(ear.message, -1, 1) == "*")
-               {
-                       // match at end
-                       s = substring(ear.message, 0, -2);
-                       l -= 1;
-                       if(substring(msg, 0, l) == s)
-                               matchstart = 0;
-               }
-               else
-               {
-                       // full match
-                       s = ear.message;
-                       if(msg == ear.message)
-                               matchstart = 0;
-               }
-       }
-
-       if(matchstart == -1) // no match
-               return msgin;
-
-       magicear_matched = true;
-
-       if(dotrigger)
-       {
-               oldself = self;
-               activator = source;
-               self = ear;
-               savemessage = self.message;
-               self.message = string_null;
-               SUB_UseTargets();
-               self.message = savemessage;
-               self = oldself;
-       }
-
-       if(ear.spawnflags & 16)
-       {
-               return ear.netname;
-       }
-       else if(ear.netname != "")
-       {
-               if(matchstart < 0)
-                       return strreplace(s, ear.netname, msg);
-               else
-                       return strcat(
-                               substring(msg, 0, matchstart),
-                               ear.netname,
-                               substring(msg, matchstart + l, -1)
-                       );
-       }
-       else
-               return msgin;
-}
-
-string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin)
-{
-       entity ear;
-       string msgout;
-       for(ear = magicears; ear; ear = ear.enemy)
-       {
-               msgout = trigger_magicear_processmessage(ear, source, teamsay, privatesay, msgin);
-               if(!(ear.spawnflags & 64))
-               if(magicear_matched)
-                       return msgout;
-               msgin = msgout;
-       }
-       return msgin;
-}
-
-void spawnfunc_trigger_magicear()
-{
-       self.enemy = magicears;
-       magicears = self;
-
-       // actually handled in "say" processing
-       // spawnflags:
-       //    1 = ignore say
-       //    2 = ignore teamsay
-       //    4 = ignore tell
-       //    8 = ignore tell to unknown player
-       //   16 = let netname replace the whole message (otherwise, netname is a word replacement if set)
-       //   32 = perform the replacement even if outside the radius or dead
-       //   64 = continue replacing/triggering even if this one matched
-       //  128 = don't decolorize message before matching
-       //  256 = message is a tuba note sequence (pitch.duration pitch.duration ...)
-       //  512 = tuba notes must be exact right pitch, no transposing
-       // message: either
-       //   *pattern*
-       // or
-       //   *pattern
-       // or
-       //   pattern*
-       // or
-       //   pattern
-       // netname:
-       //   if set, replacement for the matched text
-       // radius:
-       //   "hearing distance"
-       // target:
-       //   what to trigger
-       // movedir:
-       //   for spawnflags 256, defines 'instrument+1 mintempo maxtempo' (zero component doesn't matter)
-
-       self.movedir_x -= 1; // map to tuba instrument numbers
-}
-
-void relay_activators_use()
-{
-       entity trg, os;
-
-       os = self;
-
-       for(trg = world; (trg = find(trg, targetname, os.target)); )
-       {
-               self = trg;
-               if (trg.setactive)
-                       trg.setactive(os.cnt);
-               else
-               {
-                       //bprint("Not using setactive\n");
-                       if(os.cnt == ACTIVE_TOGGLE)
-                               if(trg.active == ACTIVE_ACTIVE)
-                                       trg.active = ACTIVE_NOT;
-                               else
-                                       trg.active = ACTIVE_ACTIVE;
-                       else
-                               trg.active = os.cnt;
-               }
-       }
-       self = os;
-}
-
-void spawnfunc_relay_activate()
-{
-       self.cnt = ACTIVE_ACTIVE;
-       self.use = relay_activators_use;
-}
-
-void spawnfunc_relay_deactivate()
-{
-       self.cnt = ACTIVE_NOT;
-       self.use = relay_activators_use;
-}
-
-void spawnfunc_relay_activatetoggle()
-{
-       self.cnt = ACTIVE_TOGGLE;
-       self.use = relay_activators_use;
-}
-
-void spawnfunc_target_changelevel_use()
-{
-       if(self.gametype != "")
-               MapInfo_SwitchGameType(MapInfo_Type_FromString(self.gametype));
-
-       if (self.chmap == "")
-               localcmd("endmatch\n");
-       else
-               localcmd(strcat("changelevel ", self.chmap, "\n"));
-}
-
-void spawnfunc_target_changelevel()
-{
-       self.use = spawnfunc_target_changelevel_use;
-}
diff --git a/qcsrc/server/g_triggers.qh b/qcsrc/server/g_triggers.qh
deleted file mode 100644 (file)
index 35c39f2..0000000
+++ /dev/null
@@ -1,388 +0,0 @@
-#ifndef G_TRIGGERS_H
-#define G_TRIGGERS_H
-
-void SUB_DontUseTargets();
-
-
-void() SUB_UseTargets;
-
-void DelayThink();
-
-/*
-==============================
-SUB_UseTargets
-
-the global "activator" should be set to the entity that initiated the firing.
-
-If self.delay is set, a DelayedUse entity will be created that will actually
-do the SUB_UseTargets after that many seconds have passed.
-
-Centerprints any self.message to the activator.
-
-Removes all entities with a targetname that match self.killtarget,
-and removes them, so some events can remove other triggers.
-
-Search for (string)targetname in all entities that
-match (string)self.target and call their .use function
-
-==============================
-*/
-void SUB_UseTargets();
-
-
-//=============================================================================
-
-const float    SPAWNFLAG_NOMESSAGE = 1;
-const float    SPAWNFLAG_NOTOUCH = 1;
-
-// the wait time has passed, so set back up for another activation
-void multi_wait();
-
-
-// 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();
-
-void multi_use();
-
-void multi_touch();
-
-void multi_eventdamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
-
-void multi_reset();
-
-/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
-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.
-NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
-sounds
-1)     secret
-2)     beep beep
-3)     large switch
-4)
-set "message" to text string
-*/
-void spawnfunc_trigger_multiple();
-
-
-/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
-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 "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.
-sounds
-1)     secret
-2)     beep beep
-3)     large switch
-4)
-set "message" to text string
-*/
-void spawnfunc_trigger_once();
-
-//=============================================================================
-
-/*QUAKED spawnfunc_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 spawnfunc_trigger_relay();
-
-void delay_use();
-
-void delay_reset();
-
-void spawnfunc_trigger_delay();
-
-//=============================================================================
-
-
-void counter_use();
-
-void counter_reset();
-
-/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
-Acts as an intermediary for an action that takes multiple inputs.
-
-If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
-
-After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
-*/
-void spawnfunc_trigger_counter();
-
-void trigger_hurt_use();
-
-.float triggerhurttime;
-void trigger_hurt_touch();
-
-/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
-Any object touching this will be hurt
-set dmg to damage amount
-defalt dmg = 5
-*/
-.entity trigger_hurt_next;
-entity trigger_hurt_last;
-entity trigger_hurt_first;
-void spawnfunc_trigger_hurt();
-
-float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end);
-
-//////////////////////////////////////////////////////////////
-//
-//
-//
-//Trigger heal --a04191b92fbd93aa67214ef7e72d6d2e
-//
-//////////////////////////////////////////////////////////////
-
-.float triggerhealtime;
-void trigger_heal_touch();
-
-void spawnfunc_trigger_heal();
-
-
-//////////////////////////////////////////////////////////////
-//
-//
-//
-//End trigger_heal
-//
-//////////////////////////////////////////////////////////////
-
-.entity trigger_gravity_check;
-void trigger_gravity_remove(entity own);
-void trigger_gravity_check_think();
-
-void trigger_gravity_use();
-
-void trigger_gravity_touch();
-
-void spawnfunc_trigger_gravity();
-
-//=============================================================================
-
-// TODO add a way to do looped sounds with sound(); then complete this entity
-.float volume, atten;
-void target_speaker_use_off();
-void target_speaker_use_activator();
-void target_speaker_use_on();
-void target_speaker_use_off();
-void target_speaker_reset();
-
-void spawnfunc_target_speaker();
-
-
-void spawnfunc_func_stardust();
-
-.string bgmscript;
-.float bgmscriptattack;
-.float bgmscriptdecay;
-.float bgmscriptsustain;
-.float bgmscriptrelease;
-float pointparticles_SendEntity(entity to, float fl);
-
-void pointparticles_use();
-
-void pointparticles_think();
-
-void pointparticles_reset();
-
-void spawnfunc_func_pointparticles();
-
-void spawnfunc_func_sparks();
-
-float rainsnow_SendEntity(entity to, float sf);
-
-/*QUAKED spawnfunc_func_rain (0 .5 .8) ?
-This is an invisible area like a trigger, which rain falls inside of.
-
-Keys:
-"velocity"
- falling direction (should be something like '0 0 -700', use the X and Y velocity for wind)
-"cnt"
- sets color of rain (default 12 - white)
-"count"
- adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
-*/
-void spawnfunc_func_rain();
-
-
-/*QUAKED spawnfunc_func_snow (0 .5 .8) ?
-This is an invisible area like a trigger, which snow falls inside of.
-
-Keys:
-"velocity"
- falling direction (should be something like '0 0 -300', use the X and Y velocity for wind)
-"cnt"
- sets color of rain (default 12 - white)
-"count"
- adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000
-*/
-void spawnfunc_func_snow();
-
-.float modelscale;
-void misc_laser_aim();
-
-void misc_laser_init();
-
-.entity pusher;
-void misc_laser_think();
-
-float laser_SendEntity(entity to, float fl);
-
-/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
-Any object touching the beam will be hurt
-Keys:
-"target"
- spawnfunc_target_position where the laser ends
-"mdl"
- name of beam end effect to use
-"colormod"
- color of the beam (default: red)
-"dmg"
- damage per second (-1 for a laser that kills immediately)
-*/
-void laser_use();
-
-void laser_reset();
-
-void spawnfunc_misc_laser();
-
-// tZorks trigger impulse / gravity
-.float radius;
-.float falloff;
-.float strength;
-.float lastpushtime;
-
-// targeted (directional) mode
-void trigger_impulse_touch1();
-
-// Directionless (accelerator/decelerator) mode
-void trigger_impulse_touch2();
-
-// Spherical (gravity/repulsor) mode
-void trigger_impulse_touch3();
-
-/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
--------- KEYS --------
-target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
-         If not, this trigger acts like a damper/accelerator field.
-
-strength : This is how mutch force to add in the direction of .target each second
-           when .target is set. If not, this is hoe mutch to slow down/accelerate
-           someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
-
-radius   : If set, act as a spherical device rather then a liniar one.
-
-falloff : 0 = none, 1 = liniar, 2 = inverted liniar
-
--------- NOTES --------
-Use a brush textured with common/origin in the trigger entity to determine the origin of the force
-in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect).
-*/
-
-void spawnfunc_trigger_impulse();
-
-/*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
-"Flip-flop" trigger gate... lets only every second trigger event through
-*/
-void flipflop_use();
-
-void spawnfunc_trigger_flipflop();
-
-/*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8)
-"Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait"
-*/
-void monoflop_use();
-void monoflop_fixed_use();
-
-void monoflop_think();
-
-void monoflop_reset();
-
-void spawnfunc_trigger_monoflop();
-
-void multivibrator_send();
-
-void multivibrator_toggle();
-
-void multivibrator_reset();
-
-/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
-"Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
--------- KEYS --------
-target: trigger all entities with this targetname when it goes off
-targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state
-phase: offset of the timing
-wait: "on" cycle time (default: 1)
-respawntime: "off" cycle time (default: same as wait)
--------- SPAWNFLAGS --------
-START_ON: assume it is already turned on (when targeted)
-*/
-void spawnfunc_trigger_multivibrator();
-
-
-void follow_init();
-
-void spawnfunc_misc_follow();
-
-
-
-void gamestart_use();
-
-void spawnfunc_trigger_gamestart();
-
-
-
-
-.entity voicescript; // attached voice script
-.float voicescript_index; // index of next voice, or -1 to use the randomized ones
-.float voicescript_nextthink; // time to play next voice
-.float voicescript_voiceend; // time when this voice ends
-
-void target_voicescript_clear(entity pl);
-
-void target_voicescript_use();
-
-void target_voicescript_next(entity pl);
-
-void spawnfunc_target_voicescript();
-
-
-
-void trigger_relay_teamcheck_use();
-
-void trigger_relay_teamcheck_reset();
-
-void spawnfunc_trigger_relay_teamcheck();
-
-
-
-void trigger_disablerelay_use();
-
-void spawnfunc_trigger_disablerelay();
-
-float magicear_matched;
-float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo);
-string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin);
-
-entity magicears;
-string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin);
-
-void spawnfunc_trigger_magicear();
-
-void relay_activators_use();
-
-void spawnfunc_relay_activate();
-
-void spawnfunc_relay_deactivate();
-
-void spawnfunc_relay_activatetoggle();
-
-.string chmap, gametype;
-void spawnfunc_target_changelevel_use();
-
-void spawnfunc_target_changelevel();
-#endif
index 88e70233f89950fd81936bdaa2e4d8cfab75de6a..d1808d988d2e32bcb2fbdd8298bae5f62d6e53f8 100644 (file)
@@ -32,7 +32,6 @@
     #include "ipban.qh"
     #include "race.qh"
     #include "antilag.qh"
-    #include "secret.qh"
 #endif
 
 const float LATENCY_THINKRATE = 10;
index f4893094efb533c429902e0defbd2ad2ad82b1df..85159e49cf27699396c425a28b6e236bae332e43 100644 (file)
@@ -6,6 +6,7 @@
     #include "../warpzonelib/util_server.qh"
     #include "../common/util.qh"
     #include "../common/monsters/monsters.qh"
+       #include "../common/triggers/subs.qh"
     #include "defs.qh"
     #include "../common/notifications.qh"
     #include "item_key.qh"
@@ -431,6 +432,4 @@ void spawnfunc_trigger_keylock(void) {
        EXACTTRIGGER_INIT;
 
        self.touch = trigger_keylock_touch;
-};
-
-
+}
index 6ab56050368601b25496c20717744e5ff1ff9b29..4a2acd85b7d1c015d8303f3c4d120b4b25178292 100644 (file)
@@ -11,6 +11,7 @@
 /**
  * list of key names.
  */
+#ifdef SVQC
 string item_keys_names[ITEM_KEY_MAX];
 
 /**
@@ -24,3 +25,5 @@ float item_keys_usekey(entity l, entity p);
  */
 string item_keys_keylist(float keylist);
 #endif
+
+#endif
index bcbae77ea28c12118856361e1d002abed85d8140..8ef3018fd1e3469c5614bbf8e587c50504d88d53 100644 (file)
@@ -22,6 +22,7 @@
     #include "defs.qh"
     #include "../common/notifications.qh"
     #include "../common/deathtypes.qh"
+    #include "../common/triggers/subs.qh"
     #include "mutators/mutators_include.qh"
     #include "tturrets/include/turrets_early.qh"
     #include "../common/mapinfo.qh"
@@ -517,9 +518,7 @@ vector randompos(vector m1, vector m2)
     return  v;
 }
 
-
-
-float sound_allowed(float _dest, entity e)
+float sound_allowed(float destin, entity e)
 {
     // sounds from world may always pass
     for (;;)
@@ -534,7 +533,7 @@ float sound_allowed(float _dest, entity e)
             break;
     }
     // sounds to self may always pass
-    if (_dest == MSG_ONE)
+    if (destin == MSG_ONE)
         if (e == msg_entity)
             return true;
     // sounds by players can be removed
@@ -546,14 +545,14 @@ float sound_allowed(float _dest, entity e)
 }
 
 #undef sound
-void sound(entity e, float chan, string samp, float vol, float _atten)
+void sound(entity e, float chan, string samp, float vol, float attenu)
 {
     if (!sound_allowed(MSG_BROADCAST, e))
         return;
-    sound7(e, chan, samp, vol, _atten, 0, 0);
+    sound7(e, chan, samp, vol, attenu, 0, 0);
 }
 
-void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
+void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
 {
     float entno, idx;
 
@@ -566,12 +565,12 @@ void soundtoat(float _dest, entity e, vector o, float chan, string samp, float v
     float sflags;
     sflags = 0;
 
-    _atten = floor(_atten * 64);
+    attenu = floor(attenu * 64);
     vol = floor(vol * 255);
 
     if (vol != 255)
         sflags |= SND_VOLUME;
-    if (_atten != 64)
+    if (attenu != 64)
         sflags |= SND_ATTENUATION;
     if (entno >= 8192 || chan < 0 || chan > 7)
         sflags |= SND_LARGEENTITY;
@@ -583,7 +582,7 @@ void soundtoat(float _dest, entity e, vector o, float chan, string samp, float v
     if (sflags & SND_VOLUME)
         WriteByte(_dest, vol);
     if (sflags & SND_ATTENUATION)
-        WriteByte(_dest, _atten);
+        WriteByte(_dest, attenu);
     if (sflags & SND_LARGEENTITY)
     {
         WriteShort(_dest, entno);
@@ -1015,7 +1014,6 @@ void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
     e.uncustomizeentityforclient_set = !!uncustomizer;
 }
 
-
 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
 {
     vector mi, ma;
index 7cebe9c5d17d59cb213fbd53b2478a19dc601ca3..016285fb1650869060e0529385b60dd0b8349e68 100644 (file)
@@ -99,8 +99,6 @@ void WarpZone_crosshair_trace(entity pl);
 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
 
 
-#define IFTARGETED if(!self.nottargeted && self.targetname != "")
-
 #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
 #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER) || ((dt) == DEATH_SLIME) || ((dt) == DEATH_LAVA) || ((dt) == DEATH_SWAMP))
 
index 49deac0e8f350c1f73cfea58694a21ac7927af5b..de0932263a1065f2d6ea0dbc2300bd747aaf60a8 100644 (file)
@@ -1,3 +1,5 @@
+#include "../../common/triggers/subs.qh"
+
 // random functions
 void assault_objective_use()
 {
index dd6e8b2109be3b97ceab791a5874a8ec94edf880..314221a19a53ab34f0e7ef022122ee762f0639ee 100644 (file)
@@ -1,3 +1,5 @@
+#include "../../common/triggers/subs.qh"
+
 void dom_EventLog(string mode, float team_before, entity actor) // use an alias for easy changing and quick editing later
 {
        if(autocvar_sv_eventlog)
index d1f20f7ee950e5e3af577fe9dcbc154302cda51a..93ed9f7d8afdd9c4607e01cc2dbb730d35effb02 100644 (file)
@@ -1,3 +1,5 @@
+#include "../../common/triggers/subs.qh"
+
 float autocvar_g_onslaught_spawn_at_controlpoints;
 float autocvar_g_onslaught_spawn_at_generator;
 float autocvar_g_onslaught_cp_proxydecap;
index b2f800a27e06c1228d541cbabc47ef221c04ae9d..942c96513b22190b24a14b97f008f3832f4f25ee 100644 (file)
@@ -1,3 +1,5 @@
+#include "../../common/triggers/target/music.qh"
+
 float buffs_BuffModel_Customize()
 {
        entity player, myowner;
index 3932ce407d885c2ccb01d7c5f0a236aceac775a2..dc1b6bc3bf0c31ca30d74d826ec44abee4d0bc70 100644 (file)
@@ -1,3 +1,5 @@
+#include "../../common/triggers/target/music.qh"
+
 .entity nade_spawnloc;
 
 void nade_timer_think()
index a0170e4803f04ae82fad8c363f97f3fa5f27d454..c3bdec50c9d8e96e2d0ffa691802793f42bcf7e8 100644 (file)
@@ -74,7 +74,6 @@
     #include "../playerdemo.qh"
     #include "../round_handler.qh"
     #include "../item_key.qh"
-    #include "../secret.qh"
     #include "../pathlib/pathlib.qh"
     #include "../tturrets/include/turrets.qh"
     #include "../vehicles/vehicles.qh"
index 6d4c76b1bc6d807dd7a4bce0c8df5083eb5f5045..c45e3cb6a6565319d601d78a30648ace3b1ae0fe 100644 (file)
@@ -18,14 +18,12 @@ csqceffects.qc
 // ctf.qc
 // domination.qc
 ent_cs.qc
-func_breakable.qc
 g_casings.qc
 g_damage.qc
 g_hook.qc
 g_models.qc
 g_subs.qc
 g_tetris.qc
-g_triggers.qc
 g_violence.qc
 g_world.qc
 ipban.qc
@@ -42,20 +40,14 @@ round_handler.qc
 // runematch.qc
 scores.qc
 scores_rules.qc
-secret.qc
 spawnpoints.qc
 steerlib.qc
 sv_main.qc
-target_music.qc
-target_spawn.qc
 teamplay.qc
 t_halflife.qc
 t_items.qc
-t_jumppads.qc
-t_plats.qc
 t_quake3.qc
 t_quake.qc
-t_swamp.qc
 t_teleporters.qc
 waypointsprites.qc
 
@@ -100,6 +92,7 @@ weapons/weaponsystem.qc
 ../common/physics.qc
 ../common/playerstats.qc
 ../common/test.qc
+../common/triggers/include.qc
 ../common/urllib.qc
 ../common/util.qc
 ../common/weapons/config.qc
index bb7f0eccc2297ad547b06181fa225d3957ad318d..a42d24034404e06eda65045a8a517ae125a98fa7 100644 (file)
@@ -1,4 +1,5 @@
 #include "race.qh"
+#include "../common/triggers/subs.qh"
 
 void W_Porto_Fail(float failhard);
 
diff --git a/qcsrc/server/secret.qc b/qcsrc/server/secret.qc
deleted file mode 100644 (file)
index 30fbedc..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-       #include "../dpdefs/progsdefs.qh"
-    #include "../common/util.qh"
-    #include "defs.qh"
-    #include "secret.qh"
-#endif
-
-void secrets_setstatus() {
-       self.stat_secrets_total = secrets_total;
-       self.stat_secrets_found = secrets_found;
-}
-
-/**
- * A secret has been found (maybe :P)
- */
-void trigger_secret_touch() {
-       // only a player can trigger this
-       if (!IS_PLAYER(other))
-               return;
-
-       // update secrets found counter
-       secrets_found += 1;
-       //print("Secret found: ", ftos(secret_counter.cnt), "/");
-       //print(ftos(secret_counter.count), "\n");
-
-       // centerprint message (multi_touch() doesn't always call centerprint())
-       centerprint(other, self.message);
-       self.message = "";
-
-       // handle normal trigger features
-       multi_touch();
-       remove(self);
-}
-
-/*QUAKED trigger_secret (.5 .5 .5) ?
-Variable sized secret trigger. Can be targeted at one or more entities.
-Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found.
--------- KEYS --------
-sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1)
-noise: path to sound file, if you want to play something else
-target: trigger all entities with this targetname when triggered
-message: print this message to the player who activated the trigger instead of the standard 'You found a secret!'
-killtarget: remove all entities with this targetname when triggered
--------- NOTES --------
-You should create a common/trigger textured brush covering the entrance to a secret room/area.
-Trigger secret can only be trigger by a player's touch and can not be a target itself.
-*/
-void spawnfunc_trigger_secret() {
-       // FIXME: should it be disabled in most modes?
-
-       // update secrets count
-       secrets_total += 1;
-
-       // add default message
-       if (self.message == "")
-               self.message = "You found a secret!";
-
-       // set default sound
-       if (self.noise == "")
-       if (!self.sounds)
-               self.sounds = 1; // misc/secret.wav
-
-       // this entity can't be a target itself!!!!
-       self.targetname = "";
-
-       // you can't just shoot a room to find it, can you?
-       self.health = 0;
-
-       // a secret can not be delayed
-       self.delay = 0;
-
-       // convert this trigger to trigger_once
-       self.classname = "trigger_once";
-       spawnfunc_trigger_once();
-
-       // take over the touch() function, so we can mark secret as found
-       self.touch = trigger_secret_touch;
-       // ignore triggering;
-       self.use = func_null;
-}
-
diff --git a/qcsrc/server/secret.qh b/qcsrc/server/secret.qh
deleted file mode 100644 (file)
index 6e6ae0b..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef SECRET_H
-#define SECRET_H
-
-/**
- * Total number of secrets on the map.
- */
-float secrets_total;
-
-/**
- * Total numbe of secrets found on the map.
- */
-float secrets_found;
-
-
-.float stat_secrets_total;
-.float stat_secrets_found;
-
-/**
- * update secrets status.
- */
-void secrets_setstatus();
-#endif
index 253c70fbe071eff1be2f03a54c64bd18a7d06b58..aa5b09e1ea1d6e10daf279ca3c93d01bf548c7af 100644 (file)
@@ -6,6 +6,7 @@
     #include "../warpzonelib/util_server.qh"
     #include "../common/constants.qh"
     #include "../common/teams.qh"
+       #include "../common/triggers/subs.qh"
     #include "../common/util.qh"
     #include "autocvars.qh"
     #include "constants.qh"
index 4f6c1ca0e63585c07586d5356551989f1e8af445..f041dd39659aebd31a8b375b6e14d1fd51820f1c 100644 (file)
@@ -1,21 +1,3 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-       #include "../dpdefs/progsdefs.qh"
-    #include "defs.qh"
-    #include "vehicles/vehicles_def.qh"
-#endif
-#include "../warpzonelib/util_server.qh"
-
-#include "miscfunctions.qh"
-#ifdef CSQC
-#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
-#endif
-
-.float ladder_time;
-.entity ladder_entity;
-
-#ifdef SVQC
 .float  roomtype;
 .float  radius;
 .float  pitch;
@@ -50,120 +32,3 @@ void spawnfunc_info_node() {}
 void spawnfunc_env_sound() {}
 void spawnfunc_light_spot() {}
 void spawnfunc_func_healthcharger() {}
-#endif
-
-void func_ladder_touch()
-{
-#ifdef SVQC
-       if (!other.iscreature)
-               return;
-       if (other.vehicle_flags & VHF_ISVEHICLE)
-               return;
-#endif
-#ifdef CSQC
-       if(other.classname != "csqcmodel")
-               return;
-#endif
-
-       EXACTTRIGGER_TOUCH;
-
-       other.ladder_time = time + 0.1;
-       other.ladder_entity = self;
-}
-
-#ifdef SVQC
-float func_ladder_send(entity to, float sf)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER);
-
-       WriteString(MSG_ENTITY, self.classname);
-       WriteByte(MSG_ENTITY, self.warpzone_isboxy);
-       WriteByte(MSG_ENTITY, self.skin);
-       WriteByte(MSG_ENTITY, self.speed);
-       WriteByte(MSG_ENTITY, self.scale);
-       WriteCoord(MSG_ENTITY, self.origin_x);
-       WriteCoord(MSG_ENTITY, self.origin_y);
-       WriteCoord(MSG_ENTITY, self.origin_z);
-
-       WriteCoord(MSG_ENTITY, self.mins_x);
-       WriteCoord(MSG_ENTITY, self.mins_y);
-       WriteCoord(MSG_ENTITY, self.mins_z);
-       WriteCoord(MSG_ENTITY, self.maxs_x);
-       WriteCoord(MSG_ENTITY, self.maxs_y);
-       WriteCoord(MSG_ENTITY, self.maxs_z);
-
-       WriteCoord(MSG_ENTITY, self.movedir_x);
-       WriteCoord(MSG_ENTITY, self.movedir_y);
-       WriteCoord(MSG_ENTITY, self.movedir_z);
-
-       WriteCoord(MSG_ENTITY, self.angles_x);
-       WriteCoord(MSG_ENTITY, self.angles_y);
-       WriteCoord(MSG_ENTITY, self.angles_z);
-
-       return true;
-}
-
-void func_ladder_link()
-{
-       Net_LinkEntity(self, false, 0, func_ladder_send);
-}
-
-void spawnfunc_func_ladder()
-{
-       EXACTTRIGGER_INIT;
-       self.touch = func_ladder_touch;
-
-       func_ladder_link();
-}
-
-void spawnfunc_func_water()
-{
-       EXACTTRIGGER_INIT;
-       self.touch = func_ladder_touch;
-
-       func_ladder_link();
-}
-
-#elif defined(CSQC)
-.float speed;
-
-void func_ladder_draw()
-{
-       float dt = time - self.move_time;
-       self.move_time = time;
-       if(dt <= 0) { return; }
-
-       trigger_touch_generic(func_ladder_touch);
-}
-
-void ent_func_ladder()
-{
-       self.classname = strzone(ReadString());
-       self.warpzone_isboxy = ReadByte();
-       self.skin = ReadByte();
-       self.speed = ReadByte();
-       self.scale = ReadByte();
-       self.origin_x = ReadCoord();
-       self.origin_y = ReadCoord();
-       self.origin_z = ReadCoord();
-       setorigin(self, self.origin);
-       self.mins_x = ReadCoord();
-       self.mins_y = ReadCoord();
-       self.mins_z = ReadCoord();
-       self.maxs_x = ReadCoord();
-       self.maxs_y = ReadCoord();
-       self.maxs_z = ReadCoord();
-       setsize(self, self.mins, self.maxs);
-       self.movedir_x = ReadCoord();
-       self.movedir_y = ReadCoord();
-       self.movedir_z = ReadCoord();
-       self.angles_x = ReadCoord();
-       self.angles_y = ReadCoord();
-       self.angles_z = ReadCoord();
-
-       self.solid = SOLID_TRIGGER;
-       self.draw = func_ladder_draw;
-       self.drawmask = MASK_NORMAL;
-       self.move_time = time;
-}
-#endif
index 03af802e71c12d8b762c0e34df606ba8c096ee3d..ba0ee4410daddd992a6567dd644412e07db7db34 100644 (file)
@@ -17,6 +17,7 @@
     #include "../common/constants.qh"
     #include "../common/util.qh"
     #include "../common/monsters/monsters.qh"
+       #include "../common/triggers/subs.qh"
     #include "../common/weapons/weapons.qh"
     #include "weapons/weaponsystem.qh"
     #include "t_items.qh"
diff --git a/qcsrc/server/t_jumppads.qc b/qcsrc/server/t_jumppads.qc
deleted file mode 100644 (file)
index 480f5c3..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-#include "t_jumppads.qh"
-
-#ifdef SVQC
-
-void trigger_push_use()
-{
-       if(teamplay)
-       {
-               self.team = activator.team;
-               self.SendFlags |= 2;
-       }
-}
-#endif
-
-/*
-       trigger_push_calculatevelocity
-
-       Arguments:
-         org - origin of the object which is to be pushed
-         tgt - target entity (can be either a point or a model entity; if it is
-               the latter, its midpoint is used)
-         ht  - jump height, measured from the higher one of org and tgt's midpoint
-
-       Returns: velocity for the jump
-       the global trigger_push_calculatevelocity_flighttime is set to the total
-       jump time
- */
-
-vector trigger_push_calculatevelocity(vector org, entity tgt, float ht)
-{
-       float grav, sdist, zdist, vs, vz, jumpheight;
-       vector sdir, torg;
-
-       torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5;
-
-       grav = PHYS_GRAVITY;
-       if(PHYS_ENTGRAVITY(other))
-               grav *= PHYS_ENTGRAVITY(other);
-
-       zdist = torg.z - org.z;
-       sdist = vlen(torg - org - zdist * '0 0 1');
-       sdir = normalize(torg - org - zdist * '0 0 1');
-
-       // how high do we need to push the player?
-       jumpheight = fabs(ht);
-       if(zdist > 0)
-               jumpheight = jumpheight + zdist;
-
-       /*
-               STOP.
-
-               You will not understand the following equations anyway...
-               But here is what I did to get them.
-
-               I used the functions
-
-                 s(t) = t * vs
-                 z(t) = t * vz - 1/2 grav t^2
-
-               and solved for:
-
-                 s(ti) = sdist
-                 z(ti) = zdist
-                 max(z, ti) = jumpheight
-
-               From these three equations, you will find the three parameters vs, vz
-               and ti.
-        */
-
-       // push him so high...
-       vz = sqrt(fabs(2 * grav * jumpheight)); // NOTE: sqrt(positive)!
-
-       // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump!
-       if(ht < 0)
-               if(zdist < 0)
-                       vz = -vz;
-
-       vector solution;
-       solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist"
-       // ALWAYS solvable because jumpheight >= zdist
-       if(!solution.z)
-               solution.y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0)
-       if(zdist == 0)
-               solution.x = solution.y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually)
-
-       if(zdist < 0)
-       {
-               // down-jump
-               if(ht < 0)
-               {
-                       // almost straight line type
-                       // jump apex is before the jump
-                       // we must take the larger one
-                       trigger_push_calculatevelocity_flighttime = solution.y;
-               }
-               else
-               {
-                       // regular jump
-                       // jump apex is during the jump
-                       // we must take the larger one too
-                       trigger_push_calculatevelocity_flighttime = solution.y;
-               }
-       }
-       else
-       {
-               // up-jump
-               if(ht < 0)
-               {
-                       // almost straight line type
-                       // jump apex is after the jump
-                       // we must take the smaller one
-                       trigger_push_calculatevelocity_flighttime = solution.x;
-               }
-               else
-               {
-                       // regular jump
-                       // jump apex is during the jump
-                       // we must take the larger one
-                       trigger_push_calculatevelocity_flighttime = solution.y;
-               }
-       }
-       vs = sdist / trigger_push_calculatevelocity_flighttime;
-
-       // finally calculate the velocity
-       return sdir * vs + '0 0 1' * vz;
-}
-
-void trigger_push_touch()
-{
-       if (self.active == ACTIVE_NOT)
-               return;
-
-#ifdef SVQC
-       if (!isPushable(other))
-               return;
-#endif
-
-       if(self.team)
-               if(((self.spawnflags & 4) == 0) == (DIFF_TEAM(self, other)))
-                       return;
-
-       EXACTTRIGGER_TOUCH;
-
-       if(self.enemy)
-       {
-               other.velocity = trigger_push_calculatevelocity(other.origin, self.enemy, self.height);
-       }
-       else if(self.target)
-       {
-               entity e;
-               RandomSelection_Init();
-               for(e = world; (e = find(e, targetname, self.target)); )
-               {
-                       if(e.cnt)
-                               RandomSelection_Add(e, 0, string_null, e.cnt, 1);
-                       else
-                               RandomSelection_Add(e, 0, string_null, 1, 1);
-               }
-               other.velocity = trigger_push_calculatevelocity(other.origin, RandomSelection_chosen_ent, self.height);
-       }
-       else
-       {
-               other.velocity = self.movedir;
-       }
-
-       UNSET_ONGROUND(other);
-
-#ifdef SVQC
-       if (IS_PLAYER(other))
-       {
-               // reset tracking of oldvelocity for impact damage (sudden velocity changes)
-               other.oldvelocity = other.velocity;
-
-               if(self.pushltime < time)  // prevent "snorring" sound when a player hits the jumppad more than once
-               {
-                       // flash when activated
-                       pointparticles(particleeffectnum("jumppad_activate"), other.origin, other.velocity, 1);
-                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-                       self.pushltime = time + 0.2;
-               }
-               if(IS_REAL_CLIENT(other) || IS_BOT_CLIENT(other))
-               {
-                       bool found = false;
-                       for(int i = 0; i < other.jumppadcount && i < NUM_JUMPPADSUSED; ++i)
-                               if(other.(jumppadsused[i]) == self)
-                                       found = true;
-                       if(!found)
-                       {
-                               other.(jumppadsused[other.jumppadcount % NUM_JUMPPADSUSED]) = self;
-                               other.jumppadcount = other.jumppadcount + 1;
-                       }
-
-                       if(IS_REAL_CLIENT(other))
-                       {
-                               if(self.message)
-                                       centerprint(other, self.message);
-                       }
-                       else
-                               other.lastteleporttime = time;
-
-                       if (other.deadflag == DEAD_NO)
-                               animdecide_setaction(other, ANIMACTION_JUMP, true);
-               }
-               else
-                       other.jumppadcount = true;
-
-               // reset tracking of who pushed you into a hazard (for kill credit)
-               other.pushltime = 0;
-               other.istypefrag = 0;
-       }
-
-       if(self.enemy.target)
-       {
-               entity oldself;
-               oldself = self;
-               activator = other;
-               self = self.enemy;
-               SUB_UseTargets();
-               self = oldself;
-       }
-
-       if (other.flags & FL_PROJECTILE)
-       {
-               other.angles = vectoangles (other.velocity);
-               switch(other.movetype)
-               {
-                       case MOVETYPE_FLY:
-                               other.movetype = MOVETYPE_TOSS;
-                               other.gravity = 1;
-                               break;
-                       case MOVETYPE_BOUNCEMISSILE:
-                               other.movetype = MOVETYPE_BOUNCE;
-                               other.gravity = 1;
-                               break;
-               }
-               UpdateCSQCProjectile(other);
-       }
-
-       if (self.spawnflags & PUSH_ONCE)
-       {
-               self.touch = func_null;
-               self.think = SUB_Remove;
-               self.nextthink = time;
-       }
-#endif
-}
-
-.vector dest;
-#ifdef SVQC
-void trigger_push_link();
-void trigger_push_updatelink();
-#endif
-void trigger_push_findtarget()
-{
-       entity t;
-       vector org;
-
-       // first calculate a typical start point for the jump
-       org = (self.absmin + self.absmax) * 0.5;
-       org.z = self.absmax.z - PL_MIN_z;
-
-       if (self.target)
-       {
-               float n = 0;
-               for(t = world; (t = find(t, targetname, self.target)); )
-               {
-                       ++n;
-#ifdef SVQC
-                       entity e = spawn();
-                       setorigin(e, org);
-                       setsize(e, PL_MIN, PL_MAX);
-                       e.velocity = trigger_push_calculatevelocity(org, t, self.height);
-                       tracetoss(e, e);
-                       if(e.movetype == MOVETYPE_NONE)
-                               waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
-                       remove(e);
-#endif
-               }
-
-               if(!n)
-               {
-                       // no dest!
-#ifdef SVQC
-                       objerror ("Jumppad with nonexistant target");
-#endif
-                       return;
-               }
-               else if(n == 1)
-               {
-                       // exactly one dest - bots love that
-                       self.enemy = find(world, targetname, self.target);
-               }
-               else
-               {
-                       // have to use random selection every single time
-                       self.enemy = world;
-               }
-       }
-#ifdef SVQC
-       else
-       {
-               entity e = spawn();
-               setorigin(e, org);
-               setsize(e, PL_MIN, PL_MAX);
-               e.velocity = self.movedir;
-               tracetoss(e, e);
-               waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity));
-               remove(e);
-       }
-
-       trigger_push_link();
-       defer(0.1, trigger_push_updatelink);
-#endif
-}
-
-#ifdef SVQC
-float trigger_push_send(entity to, float sf)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH);
-       WriteByte(MSG_ENTITY, sf);
-
-       if(sf & 1)
-       {
-               WriteString(MSG_ENTITY, self.target);
-               WriteByte(MSG_ENTITY, self.team);
-               WriteInt24_t(MSG_ENTITY, self.spawnflags);
-               WriteByte(MSG_ENTITY, self.active);
-               WriteByte(MSG_ENTITY, self.warpzone_isboxy);
-               WriteByte(MSG_ENTITY, self.height);
-               WriteByte(MSG_ENTITY, self.scale);
-               WriteCoord(MSG_ENTITY, self.origin_x);
-               WriteCoord(MSG_ENTITY, self.origin_y);
-               WriteCoord(MSG_ENTITY, self.origin_z);
-
-               WriteCoord(MSG_ENTITY, self.mins_x);
-               WriteCoord(MSG_ENTITY, self.mins_y);
-               WriteCoord(MSG_ENTITY, self.mins_z);
-               WriteCoord(MSG_ENTITY, self.maxs_x);
-               WriteCoord(MSG_ENTITY, self.maxs_y);
-               WriteCoord(MSG_ENTITY, self.maxs_z);
-
-               WriteCoord(MSG_ENTITY, self.movedir_x);
-               WriteCoord(MSG_ENTITY, self.movedir_y);
-               WriteCoord(MSG_ENTITY, self.movedir_z);
-
-               WriteCoord(MSG_ENTITY, self.angles_x);
-               WriteCoord(MSG_ENTITY, self.angles_y);
-               WriteCoord(MSG_ENTITY, self.angles_z);
-       }
-
-       if(sf & 2)
-       {
-               WriteByte(MSG_ENTITY, self.team);
-               WriteByte(MSG_ENTITY, self.active);
-       }
-
-       return true;
-}
-
-void trigger_push_updatelink()
-{
-       self.SendFlags |= 1;
-}
-
-void trigger_push_link()
-{
-       Net_LinkEntity(self, false, 0, trigger_push_send);
-}
-#endif
-#ifdef SVQC
-/*
- * ENTITY PARAMETERS:
- *
- *   target:  target of jump
- *   height:  the absolute value is the height of the highest point of the jump
- *            trajectory above the higher one of the player and the target.
- *            the sign indicates whether the highest point is INSIDE (positive)
- *            or OUTSIDE (negative) of the jump trajectory. General rule: use
- *            positive values for targets mounted on the floor, and use negative
- *            values to target a point on the ceiling.
- *   movedir: if target is not set, this * speed * 10 is the velocity to be reached.
- */
-void spawnfunc_trigger_push()
-{
-       SetMovedir ();
-
-       EXACTTRIGGER_INIT;
-
-       self.active = ACTIVE_ACTIVE;
-       self.use = trigger_push_use;
-       self.touch = trigger_push_touch;
-
-       // normal push setup
-       if (!self.speed)
-               self.speed = 1000;
-       self.movedir = self.movedir * self.speed * 10;
-
-       if (!self.noise)
-               self.noise = "misc/jumppad.wav";
-       precache_sound (self.noise);
-
-       // this must be called to spawn the teleport waypoints for bots
-       InitializeEntity(self, trigger_push_findtarget, INITPRIO_FINDTARGET);
-}
-
-
-float target_push_send(entity to, float sf)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH);
-
-       WriteByte(MSG_ENTITY, self.cnt);
-       WriteString(MSG_ENTITY, self.targetname);
-       WriteCoord(MSG_ENTITY, self.origin_x);
-       WriteCoord(MSG_ENTITY, self.origin_y);
-       WriteCoord(MSG_ENTITY, self.origin_z);
-
-       return true;
-}
-
-void target_push_link()
-{
-       Net_LinkEntity(self, false, 0, target_push_send);
-       self.SendFlags |= 1; // update
-}
-
-void spawnfunc_target_push() { target_push_link(); }
-void spawnfunc_info_notnull() { target_push_link(); }
-void spawnfunc_target_position() { target_push_link(); }
-
-#endif
-
-#ifdef CSQC
-void trigger_push_draw()
-{
-       float dt = time - self.move_time;
-       self.move_time = time;
-       if(dt <= 0) { return; }
-
-       trigger_touch_generic(trigger_push_touch);
-}
-
-void ent_trigger_push()
-{
-       float sf = ReadByte();
-
-       if(sf & 1)
-       {
-               self.classname = "jumppad";
-               self.target = strzone(ReadString());
-               float mytm = ReadByte(); if(mytm) { self.team = mytm - 1; }
-               self.spawnflags = ReadInt24_t();
-               self.active = ReadByte();
-               self.warpzone_isboxy = ReadByte();
-               self.height = ReadByte();
-               self.scale = ReadByte();
-               self.origin_x = ReadCoord();
-               self.origin_y = ReadCoord();
-               self.origin_z = ReadCoord();
-               setorigin(self, self.origin);
-               self.mins_x = ReadCoord();
-               self.mins_y = ReadCoord();
-               self.mins_z = ReadCoord();
-               self.maxs_x = ReadCoord();
-               self.maxs_y = ReadCoord();
-               self.maxs_z = ReadCoord();
-               setsize(self, self.mins, self.maxs);
-               self.movedir_x = ReadCoord();
-               self.movedir_y = ReadCoord();
-               self.movedir_z = ReadCoord();
-               self.angles_x = ReadCoord();
-               self.angles_y = ReadCoord();
-               self.angles_z = ReadCoord();
-
-               self.solid = SOLID_TRIGGER;
-               //self.draw = trigger_push_draw;
-               self.drawmask = MASK_ENGINE;
-               self.move_time = time;
-               //self.touch = trigger_push_touch;
-               trigger_push_findtarget();
-       }
-
-       if(sf & 2)
-       {
-               self.team = ReadByte();
-               self.active = ReadByte();
-       }
-}
-
-void ent_target_push()
-{
-       self.classname = "push_target";
-       self.cnt = ReadByte();
-       self.targetname = strzone(ReadString());
-       self.origin_x = ReadCoord();
-       self.origin_y = ReadCoord();
-       self.origin_z = ReadCoord();
-       setorigin(self, self.origin);
-
-       self.drawmask = MASK_ENGINE;
-}
-#endif
index 36f203b9ed0c99a46d260388b9485d820244f4d1..48f4d5cab40ec25f7caae24e6351225e337f7bdf 100644 (file)
@@ -26,7 +26,6 @@ const int ACTIVE_ACTIVE       = 1;
 const int ACTIVE_IDLE          = 2;
 const int ACTIVE_BUSY          = 2;
 const int ACTIVE_TOGGLE                = 3;
-void trigger_push_draw();
 #endif
 
 /*
diff --git a/qcsrc/server/t_plats.qc b/qcsrc/server/t_plats.qc
deleted file mode 100644 (file)
index c22f87a..0000000
+++ /dev/null
@@ -1,2392 +0,0 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-       #include "../dpdefs/progsdefs.qh"
-    #include "../dpdefs/dpextensions.qh"
-    #include "../warpzonelib/mathlib.qh"
-    #include "../warpzonelib/common.qh"
-    #include "../warpzonelib/util_server.qh"
-    #include "../common/constants.qh"
-    #include "../common/util.qh"
-    #include "../common/weapons/weapons.qh"
-    #include "constants.qh"
-    #include "defs.qh"
-    #include "../common/notifications.qh"
-    #include "../common/deathtypes.qh"
-    #include "command/common.qh"
-    #include "../csqcmodellib/sv_model.qh"
-#endif
-
-#ifdef SVQC
-
-.float dmgtime2;
-void generic_plat_blocked()
-{
-    if(self.dmg && other.takedamage != DAMAGE_NO) {
-        if(self.dmgtime2 < time) {
-            Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-            self.dmgtime2 = time + self.dmgtime;
-        }
-
-        // Gib dead/dying stuff
-        if(other.deadflag != DEAD_NO)
-            Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-    }
-}
-
-
-.entity trigger_field;
-
-void() plat_center_touch;
-void() plat_outside_touch;
-void() plat_trigger_use;
-void() plat_go_up;
-void() plat_go_down;
-void() plat_crush;
-const float PLAT_LOW_TRIGGER = 1;
-
-void plat_spawn_inside_trigger()
-{
-       entity trigger;
-       vector tmin, tmax;
-
-       trigger = spawn();
-       trigger.touch = plat_center_touch;
-       trigger.movetype = MOVETYPE_NONE;
-       trigger.solid = SOLID_TRIGGER;
-       trigger.enemy = self;
-
-       tmin = self.absmin + '25 25 0';
-       tmax = self.absmax - '25 25 -8';
-       tmin.z = tmax.z - (self.pos1_z - self.pos2_z + 8);
-       if (self.spawnflags & PLAT_LOW_TRIGGER)
-               tmax.z = tmin.z + 8;
-
-       if (self.size.x <= 50)
-       {
-               tmin.x = (self.mins.x + self.maxs.x) / 2;
-               tmax.x = tmin.x + 1;
-       }
-       if (self.size.y <= 50)
-       {
-               tmin.y = (self.mins.y + self.maxs.y) / 2;
-               tmax.y = tmin.y + 1;
-       }
-
-       if(tmin.x < tmax.x)
-               if(tmin.y < tmax.y)
-                       if(tmin.z < tmax.z)
-                       {
-                               setsize (trigger, tmin, tmax);
-                               return;
-                       }
-
-       // otherwise, something is fishy...
-       remove(trigger);
-       objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
-}
-
-void plat_hit_top()
-{
-       sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
-       self.state = 1;
-       self.think = plat_go_down;
-       self.nextthink = self.ltime + 3;
-}
-
-void plat_hit_bottom()
-{
-       sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
-       self.state = 2;
-}
-
-void plat_go_down()
-{
-       sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
-       self.state = 3;
-       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, plat_hit_bottom);
-}
-
-void plat_go_up()
-{
-       sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM);
-       self.state = 4;
-       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, plat_hit_top);
-}
-
-void plat_center_touch()
-{
-       if (!other.iscreature)
-               return;
-
-       if (other.health <= 0)
-               return;
-
-       self = self.enemy;
-       if (self.state == 2)
-               plat_go_up ();
-       else if (self.state == 1)
-               self.nextthink = self.ltime + 1;        // delay going down
-}
-
-void plat_outside_touch()
-{
-       if (!other.iscreature)
-               return;
-
-       if (other.health <= 0)
-               return;
-
-       self = self.enemy;
-       if (self.state == 1)
-               plat_go_down ();
-}
-
-void plat_trigger_use()
-{
-       if (self.think)
-               return;         // already activated
-       plat_go_down();
-}
-
-
-void plat_crush()
-{
-    if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
-        Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-    } else {
-        if((self.dmg) && (other.takedamage != DAMAGE_NO)) {   // Shall we bite?
-            Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-            // Gib dead/dying stuff
-            if(other.deadflag != DEAD_NO)
-                Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-        }
-
-        if (self.state == 4)
-            plat_go_down ();
-        else if (self.state == 3)
-            plat_go_up ();
-       // when in other states, then the plat_crush event came delayed after
-       // plat state already had changed
-       // this isn't a bug per se!
-    }
-}
-
-void plat_use()
-{
-       self.use = func_null;
-       if (self.state != 4)
-               objerror ("plat_use: not in up state");
-       plat_go_down();
-}
-
-.string sound1, sound2;
-
-void plat_reset()
-{
-       IFTARGETED
-       {
-               setorigin (self, self.pos1);
-               self.state = 4;
-               self.use = plat_use;
-       }
-       else
-       {
-               setorigin (self, self.pos2);
-               self.state = 2;
-               self.use = plat_trigger_use;
-       }
-}
-
-.float platmovetype_start_default, platmovetype_end_default;
-float set_platmovetype(entity e, string s)
-{
-       // sets platmovetype_start and platmovetype_end based on a string consisting of two values
-
-       float n;
-       n = tokenize_console(s);
-       if(n > 0)
-               e.platmovetype_start = stof(argv(0));
-       else
-               e.platmovetype_start = 0;
-
-       if(n > 1)
-               e.platmovetype_end = stof(argv(1));
-       else
-               e.platmovetype_end = e.platmovetype_start;
-
-       if(n > 2)
-               if(argv(2) == "force")
-                       return true; // no checking, return immediately
-
-       if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end))
-       {
-               objerror("Invalid platform move type; platform would go in reverse, which is not allowed.");
-               return false;
-       }
-
-       return true;
-}
-
-void spawnfunc_path_corner()
-{
-       // setup values for overriding train movement
-       // if a second value does not exist, both start and end speeds are the single value specified
-       if(!set_platmovetype(self, self.platmovetype))
-               return;
-}
-void spawnfunc_func_plat()
-{
-       if (self.sounds == 0)
-               self.sounds = 2;
-
-    if(self.spawnflags & 4)
-        self.dmg = 10000;
-
-    if(self.dmg && (self.message == ""))
-               self.message = "was squished";
-    if(self.dmg && (self.message2 == ""))
-               self.message2 = "was squished by";
-
-       if (self.sounds == 1)
-       {
-               precache_sound ("plats/plat1.wav");
-               precache_sound ("plats/plat2.wav");
-               self.noise = "plats/plat1.wav";
-               self.noise1 = "plats/plat2.wav";
-       }
-
-       if (self.sounds == 2)
-       {
-               precache_sound ("plats/medplat1.wav");
-               precache_sound ("plats/medplat2.wav");
-               self.noise = "plats/medplat1.wav";
-               self.noise1 = "plats/medplat2.wav";
-       }
-
-       if (self.sound1)
-       {
-               precache_sound (self.sound1);
-               self.noise = self.sound1;
-       }
-       if (self.sound2)
-       {
-               precache_sound (self.sound2);
-               self.noise1 = self.sound2;
-       }
-
-       self.mangle = self.angles;
-       self.angles = '0 0 0';
-
-       self.classname = "plat";
-       if (!InitMovingBrushTrigger())
-               return;
-       self.effects |= EF_LOWPRECISION;
-       setsize (self, self.mins , self.maxs);
-
-       self.blocked = plat_crush;
-
-       if (!self.speed)
-               self.speed = 150;
-       if (!self.lip)
-               self.lip = 16;
-       if (!self.height)
-               self.height = self.size.z - self.lip;
-
-       self.pos1 = self.origin;
-       self.pos2 = self.origin;
-       self.pos2_z = self.origin.z - self.height;
-
-       self.reset = plat_reset;
-       plat_reset();
-
-       plat_spawn_inside_trigger ();   // the "start moving" trigger
-}
-
-.float train_wait_turning;
-void() train_next;
-void train_wait()
-{
-       entity oldself;
-       oldself = self;
-       self = self.enemy;
-       SUB_UseTargets();
-       self = oldself;
-       self.enemy = world;
-
-       // if turning is enabled, the train will turn toward the next point while waiting
-       if(self.platmovetype_turn && !self.train_wait_turning)
-       {
-               entity targ, cp;
-               vector ang;
-               targ = find(world, targetname, self.target);
-               if((self.spawnflags & 1) && targ.curvetarget)
-                       cp = find(world, targetname, targ.curvetarget);
-               else
-                       cp = world;
-
-               if(cp) // bezier curves movement
-                       ang = cp.origin - (self.origin - self.view_ofs); // use the origin of the control point of the next path_corner
-               else // linear movement
-                       ang = targ.origin - (self.origin - self.view_ofs); // use the origin of the next path_corner
-               ang = vectoangles(ang);
-               ang.x = -ang.x; // flip up / down orientation
-
-               if(self.wait > 0) // slow turning
-                       SUB_CalcAngleMove(ang, TSPEED_TIME, self.ltime - time + self.wait, train_wait);
-               else // instant turning
-                       SUB_CalcAngleMove(ang, TSPEED_TIME, 0.0000001, train_wait);
-               self.train_wait_turning = true;
-               return;
-       }
-
-       if(self.noise != "")
-               stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
-
-       if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning
-       {
-               self.train_wait_turning = false;
-               train_next();
-       }
-       else
-       {
-               self.think = train_next;
-               self.nextthink = self.ltime + self.wait;
-       }
-}
-
-void train_next()
-{
-       entity targ, cp = world;
-       vector cp_org = '0 0 0';
-
-       targ = find(world, targetname, self.target);
-       self.target = targ.target;
-       if (self.spawnflags & 1)
-       {
-               if(targ.curvetarget)
-               {
-                       cp = find(world, targetname, targ.curvetarget); // get its second target (the control point)
-                       cp_org = cp.origin - self.view_ofs; // no control point found, assume a straight line to the destination
-               }
-       }
-       if (self.target == "")
-               objerror("train_next: no next target");
-       self.wait = targ.wait;
-       if (!self.wait)
-               self.wait = 0.1;
-
-       if(targ.platmovetype)
-       {
-               // this path_corner contains a movetype overrider, apply it
-               self.platmovetype_start = targ.platmovetype_start;
-               self.platmovetype_end = targ.platmovetype_end;
-       }
-       else
-       {
-               // this path_corner doesn't contain a movetype overrider, use the train's defaults
-               self.platmovetype_start = self.platmovetype_start_default;
-               self.platmovetype_end = self.platmovetype_end_default;
-       }
-
-       if (targ.speed)
-       {
-               if (cp)
-                       SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
-               else
-                       SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait);
-       }
-       else
-       {
-               if (cp)
-                       SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
-               else
-                       SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait);
-       }
-
-       if(self.noise != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
-}
-
-void func_train_find()
-{
-       entity targ;
-       targ = find(world, targetname, self.target);
-       self.target = targ.target;
-       if (self.target == "")
-               objerror("func_train_find: no next target");
-       setorigin(self, targ.origin - self.view_ofs);
-       self.nextthink = self.ltime + 1;
-       self.think = train_next;
-}
-
-/*QUAKED spawnfunc_func_train (0 .5 .8) ?
-Ridable platform, targets spawnfunc_path_corner path to follow.
-speed : speed the train moves (can be overridden by each spawnfunc_path_corner)
-target : targetname of first spawnfunc_path_corner (starts here)
-*/
-void spawnfunc_func_train()
-{
-       if (self.noise != "")
-               precache_sound(self.noise);
-
-       if (self.target == "")
-               objerror("func_train without a target");
-       if (!self.speed)
-               self.speed = 100;
-
-       if (!InitMovingBrushTrigger())
-               return;
-       self.effects |= EF_LOWPRECISION;
-
-       if (self.spawnflags & 2)
-       {
-               self.platmovetype_turn = true;
-               self.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now
-       }
-       else
-               self.view_ofs = self.mins;
-
-       // wait for targets to spawn
-       InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION);
-
-       self.blocked = generic_plat_blocked;
-       if(self.dmg && (self.message == ""))
-               self.message = " was squished";
-    if(self.dmg && (self.message2 == ""))
-               self.message2 = "was squished by";
-       if(self.dmg && (!self.dmgtime))
-               self.dmgtime = 0.25;
-       self.dmgtime2 = time;
-
-       if(!set_platmovetype(self, self.platmovetype))
-               return;
-       self.platmovetype_start_default = self.platmovetype_start;
-       self.platmovetype_end_default = self.platmovetype_end;
-
-       // TODO make a reset function for this one
-}
-
-void func_rotating_setactive(float astate)
-{
-
-       if (astate == ACTIVE_TOGGLE)
-       {
-               if(self.active == ACTIVE_ACTIVE)
-                       self.active = ACTIVE_NOT;
-               else
-                       self.active = ACTIVE_ACTIVE;
-       }
-       else
-               self.active = astate;
-
-       if(self.active  == ACTIVE_NOT)
-               self.avelocity = '0 0 0';
-       else
-               self.avelocity = self.pos1;
-}
-
-/*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS
-Brush model that spins in place on one axis (default Z).
-speed   : speed to rotate (in degrees per second)
-noise   : path/name of looping .wav file to play.
-dmg     : Do this mutch dmg every .dmgtime intervall when blocked
-dmgtime : See above.
-*/
-
-void spawnfunc_func_rotating()
-{
-       if (self.noise != "")
-       {
-               precache_sound(self.noise);
-               ambientsound(self.origin, self.noise, VOL_BASE, ATTEN_IDLE);
-       }
-
-       self.active = ACTIVE_ACTIVE;
-       self.setactive = func_rotating_setactive;
-
-       if (!self.speed)
-               self.speed = 100;
-       // FIXME: test if this turns the right way, then remove this comment (negate as needed)
-       if (self.spawnflags & 4) // X (untested)
-               self.avelocity = '0 0 1' * self.speed;
-       // FIXME: test if this turns the right way, then remove this comment (negate as needed)
-       else if (self.spawnflags & 8) // Y (untested)
-               self.avelocity = '1 0 0' * self.speed;
-       // FIXME: test if this turns the right way, then remove this comment (negate as needed)
-       else // Z
-               self.avelocity = '0 1 0' * self.speed;
-
-       self.pos1 = self.avelocity;
-
-    if(self.dmg && (self.message == ""))
-        self.message = " was squished";
-    if(self.dmg && (self.message2 == ""))
-               self.message2 = "was squished by";
-
-
-    if(self.dmg && (!self.dmgtime))
-        self.dmgtime = 0.25;
-
-    self.dmgtime2 = time;
-
-       if (!InitMovingBrushTrigger())
-               return;
-       // no EF_LOWPRECISION here, as rounding angles is bad
-
-    self.blocked = generic_plat_blocked;
-
-       // wait for targets to spawn
-       self.nextthink = self.ltime + 999999999;
-       self.think = SUB_NullThink; // for PushMove
-
-       // TODO make a reset function for this one
-}
-
-.float height;
-void func_bobbing_controller_think()
-{
-       vector v;
-       self.nextthink = time + 0.1;
-
-       if(self.owner.active != ACTIVE_ACTIVE)
-       {
-               self.owner.velocity = '0 0 0';
-               return;
-       }
-
-       // calculate sinewave using makevectors
-       makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0');
-       v = self.owner.destvec + self.owner.movedir * v_forward.y;
-       if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed
-               // * 10 so it will arrive in 0.1 sec
-               self.owner.velocity = (v - self.owner.origin) * 10;
-}
-
-/*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
-Brush model that moves back and forth on one axis (default Z).
-speed : how long one cycle takes in seconds (default 4)
-height : how far the cycle moves (default 32)
-phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
-noise : path/name of looping .wav file to play.
-dmg : Do this mutch dmg every .dmgtime intervall when blocked
-dmgtime : See above.
-*/
-void spawnfunc_func_bobbing()
-{
-       entity controller;
-       if (self.noise != "")
-       {
-               precache_sound(self.noise);
-               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
-       }
-       if (!self.speed)
-               self.speed = 4;
-       if (!self.height)
-               self.height = 32;
-       // center of bobbing motion
-       self.destvec = self.origin;
-       // time scale to get degrees
-       self.cnt = 360 / self.speed;
-
-       self.active = ACTIVE_ACTIVE;
-
-       // damage when blocked
-       self.blocked = generic_plat_blocked;
-       if(self.dmg && (self.message == ""))
-               self.message = " was squished";
-    if(self.dmg && (self.message2 == ""))
-               self.message2 = "was squished by";
-       if(self.dmg && (!self.dmgtime))
-               self.dmgtime = 0.25;
-       self.dmgtime2 = time;
-
-       // how far to bob
-       if (self.spawnflags & 1) // X
-               self.movedir = '1 0 0' * self.height;
-       else if (self.spawnflags & 2) // Y
-               self.movedir = '0 1 0' * self.height;
-       else // Z
-               self.movedir = '0 0 1' * self.height;
-
-       if (!InitMovingBrushTrigger())
-               return;
-
-       // wait for targets to spawn
-       controller = spawn();
-       controller.classname = "func_bobbing_controller";
-       controller.owner = self;
-       controller.nextthink = time + 1;
-       controller.think = func_bobbing_controller_think;
-       self.nextthink = self.ltime + 999999999;
-       self.think = SUB_NullThink; // for PushMove
-
-       // Savage: Reduce bandwith, critical on e.g. nexdm02
-       self.effects |= EF_LOWPRECISION;
-
-       // TODO make a reset function for this one
-}
-
-.float freq;
-void func_pendulum_controller_think()
-{
-       float v;
-       self.nextthink = time + 0.1;
-
-       if (!(self.owner.active == ACTIVE_ACTIVE))
-       {
-               self.owner.avelocity_x = 0;
-               return;
-       }
-
-       // calculate sinewave using makevectors
-       makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
-       v = self.owner.speed * v_forward.y + self.cnt;
-       if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
-       {
-               // * 10 so it will arrive in 0.1 sec
-               self.owner.avelocity_z = (remainder(v - self.owner.angles.z, 360)) * 10;
-       }
-}
-
-void spawnfunc_func_pendulum()
-{
-       entity controller;
-       if (self.noise != "")
-       {
-               precache_sound(self.noise);
-               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
-       }
-
-       self.active = ACTIVE_ACTIVE;
-
-       // keys: angle, speed, phase, noise, freq
-
-       if(!self.speed)
-               self.speed = 30;
-       // not initializing self.dmg to 2, to allow damageless pendulum
-
-       if(self.dmg && (self.message == ""))
-               self.message = " was squished";
-       if(self.dmg && (self.message2 == ""))
-               self.message2 = "was squished by";
-       if(self.dmg && (!self.dmgtime))
-               self.dmgtime = 0.25;
-       self.dmgtime2 = time;
-
-       self.blocked = generic_plat_blocked;
-
-       self.avelocity_z = 0.0000001;
-       if (!InitMovingBrushTrigger())
-               return;
-
-       if(!self.freq)
-       {
-               // find pendulum length (same formula as Q3A)
-               self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins.z))));
-       }
-
-       // copy initial angle
-       self.cnt = self.angles.z;
-
-       // wait for targets to spawn
-       controller = spawn();
-       controller.classname = "func_pendulum_controller";
-       controller.owner = self;
-       controller.nextthink = time + 1;
-       controller.think = func_pendulum_controller_think;
-       self.nextthink = self.ltime + 999999999;
-       self.think = SUB_NullThink; // for PushMove
-
-       //self.effects |= EF_LOWPRECISION;
-
-       // TODO make a reset function for this one
-}
-
-// button and multiple button
-
-void() button_wait;
-void() button_return;
-
-void button_wait()
-{
-       self.state = STATE_TOP;
-       self.nextthink = self.ltime + self.wait;
-       self.think = button_return;
-       activator = self.enemy;
-       SUB_UseTargets();
-       self.frame = 1;                 // use alternate textures
-}
-
-void button_done()
-{
-       self.state = STATE_BOTTOM;
-}
-
-void button_return()
-{
-       self.state = STATE_DOWN;
-       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done);
-       self.frame = 0;                 // use normal textures
-       if (self.health)
-               self.takedamage = DAMAGE_YES;   // can be shot again
-}
-
-
-void button_blocked()
-{
-       // do nothing, just don't come all the way back out
-}
-
-
-void button_fire()
-{
-       self.health = self.max_health;
-       self.takedamage = DAMAGE_NO;    // will be reset upon return
-
-       if (self.state == STATE_UP || self.state == STATE_TOP)
-               return;
-
-       if (self.noise != "")
-               sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-
-       self.state = STATE_UP;
-       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait);
-}
-
-void button_reset()
-{
-       self.health = self.max_health;
-       setorigin(self, self.pos1);
-       self.frame = 0;                 // use normal textures
-       self.state = STATE_BOTTOM;
-       if (self.health)
-               self.takedamage = DAMAGE_YES;   // can be shot again
-}
-
-void button_use()
-{
-       if(self.active != ACTIVE_ACTIVE)
-               return;
-
-       self.enemy = activator;
-       button_fire ();
-}
-
-void button_touch()
-{
-       if (!other)
-               return;
-       if (!other.iscreature)
-               return;
-       if(other.velocity * self.movedir < 0)
-               return;
-       self.enemy = other;
-       if (other.owner)
-               self.enemy = other.owner;
-       button_fire ();
-}
-
-void button_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       if(self.spawnflags & DOOR_NOSPLASH)
-               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
-                       return;
-       self.health = self.health - damage;
-       if (self.health <= 0)
-       {
-               self.enemy = damage_attacker;
-               button_fire ();
-       }
-}
-
-
-/*QUAKED spawnfunc_func_button (0 .5 .8) ?
-When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
-
-"angle"                determines the opening direction
-"target"       all entities with a matching targetname will be used
-"speed"                override the default 40 speed
-"wait"         override the default 1 second wait (-1 = never return)
-"lip"          override the default 4 pixel lip remaining at end of move
-"health"       if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the InstaGib laser
-"sounds"
-0) steam metal
-1) wooden clunk
-2) metallic click
-3) in-out
-*/
-void spawnfunc_func_button()
-{
-       SetMovedir ();
-
-       if (!InitMovingBrushTrigger())
-               return;
-       self.effects |= EF_LOWPRECISION;
-
-       self.blocked = button_blocked;
-       self.use = button_use;
-
-//     if (self.health == 0) // all buttons are now shootable
-//             self.health = 10;
-       if (self.health)
-       {
-               self.max_health = self.health;
-               self.event_damage = button_damage;
-               self.takedamage = DAMAGE_YES;
-       }
-       else
-               self.touch = button_touch;
-
-       if (!self.speed)
-               self.speed = 40;
-       if (!self.wait)
-               self.wait = 1;
-       if (!self.lip)
-               self.lip = 4;
-
-    if(self.noise != "")
-        precache_sound(self.noise);
-
-       self.active = ACTIVE_ACTIVE;
-
-       self.pos1 = self.origin;
-       self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
-    self.flags |= FL_NOTARGET;
-
-       button_reset();
-}
-
-
-const float DOOR_START_OPEN = 1;
-const float DOOR_DONT_LINK = 4;
-const float DOOR_TOGGLE = 32;
-
-/*
-
-Doors are similar to buttons, but can spawn a fat trigger field around them
-to open without a touch, and they link together to form simultanious
-double/quad doors.
-
-Door.owner is the master door.  If there is only one door, it points to itself.
-If multiple doors, all will point to a single one.
-
-Door.enemy chains from the master door through all doors linked in the chain.
-
-*/
-
-/*
-=============================================================================
-
-THINK FUNCTIONS
-
-=============================================================================
-*/
-
-void() door_go_down;
-void() door_go_up;
-void() door_rotating_go_down;
-void() door_rotating_go_up;
-
-void door_blocked()
-{
-
-    if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
-        Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-    } else {
-
-        if((self.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
-            Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-
-         //Dont chamge direction for dead or dying stuff
-        if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
-            if (self.wait >= 0)
-            {
-                if (self.state == STATE_DOWN)
-                       if (self.classname == "door")
-                       {
-                               door_go_up ();
-                       } else
-                       {
-                               door_rotating_go_up ();
-                       }
-                else
-                       if (self.classname == "door")
-                       {
-                               door_go_down ();
-                       } else
-                       {
-                               door_rotating_go_down ();
-                       }
-            }
-        } else {
-            //gib dying stuff just to make sure
-            if((self.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
-                Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-        }
-    }
-
-       //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
-// if a door has a negative wait, it would never come back if blocked,
-// so let it just squash the object to death real fast
-/*     if (self.wait >= 0)
-       {
-               if (self.state == STATE_DOWN)
-                       door_go_up ();
-               else
-                       door_go_down ();
-       }
-*/
-}
-
-
-void door_hit_top()
-{
-       if (self.noise1 != "")
-               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
-       self.state = STATE_TOP;
-       if (self.spawnflags & DOOR_TOGGLE)
-               return;         // don't come down automatically
-       if (self.classname == "door")
-       {
-               self.think = door_go_down;
-       } else
-       {
-               self.think = door_rotating_go_down;
-       }
-       self.nextthink = self.ltime + self.wait;
-}
-
-void door_hit_bottom()
-{
-       if (self.noise1 != "")
-               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
-       self.state = STATE_BOTTOM;
-}
-
-void door_go_down()
-{
-       if (self.noise2 != "")
-               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       if (self.max_health)
-       {
-               self.takedamage = DAMAGE_YES;
-               self.health = self.max_health;
-       }
-
-       self.state = STATE_DOWN;
-       SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom);
-}
-
-void door_go_up()
-{
-       if (self.state == STATE_UP)
-               return;         // already going up
-
-       if (self.state == STATE_TOP)
-       {       // reset top wait time
-               self.nextthink = self.ltime + self.wait;
-               return;
-       }
-
-       if (self.noise2 != "")
-               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       self.state = STATE_UP;
-       SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top);
-
-       string oldmessage;
-       oldmessage = self.message;
-       self.message = "";
-       SUB_UseTargets();
-       self.message = oldmessage;
-}
-
-
-
-/*
-=============================================================================
-
-ACTIVATION FUNCTIONS
-
-=============================================================================
-*/
-
-float door_check_keys(void) {
-       entity door = self.owner ? self.owner : self;
-
-       // no key needed
-       if (!door.itemkeys)
-               return true;
-
-       // this door require a key
-       // only a player can have a key
-       if (!IS_PLAYER(other))
-               return false;
-
-       if (item_keys_usekey(door, other)) {
-               // some keys were used
-               if (other.key_door_messagetime <= time) {
-                       play2(other, "misc/talk.wav");
-                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys));
-                       other.key_door_messagetime = time + 2;
-               }
-       } else {
-               // no keys were used
-               if (other.key_door_messagetime <= time) {
-                       play2(other, "misc/talk.wav");
-                       Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys));
-                       other.key_door_messagetime = time + 2;
-               }
-       }
-
-       if (door.itemkeys) {
-               // door is now unlocked
-               play2(other, "misc/talk.wav");
-               Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_UNLOCKED);
-               return true;
-       } else
-               return false;
-}
-
-
-void door_fire()
-{
-       entity  oself;
-       entity  starte;
-
-       if (self.owner != self)
-               objerror ("door_fire: self.owner != self");
-
-       oself = self;
-
-       if (self.spawnflags & DOOR_TOGGLE)
-       {
-               if (self.state == STATE_UP || self.state == STATE_TOP)
-               {
-                       starte = self;
-                       do
-                       {
-                               if (self.classname == "door")
-                               {
-                                       door_go_down ();
-                               }
-                               else
-                               {
-                                       door_rotating_go_down ();
-                               }
-                               self = self.enemy;
-                       } while ( (self != starte) && (self != world) );
-                       self = oself;
-                       return;
-               }
-       }
-
-// trigger all paired doors
-       starte = self;
-       do
-       {
-               if (self.classname == "door")
-               {
-                       door_go_up ();
-               } else
-               {
-                       // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
-                       if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM)
-                       {
-                               self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating
-                               self.pos2 = '0 0 0' - self.pos2;
-                       }
-                       // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
-                       if (!((self.spawnflags & 2) &&  (self.spawnflags & 8) && self.state == STATE_DOWN
-                           && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0)))))
-                       {
-                               door_rotating_go_up ();
-                       }
-               }
-               self = self.enemy;
-       } while ( (self != starte) && (self != world) );
-       self = oself;
-}
-
-
-void door_use()
-{
-       entity oself;
-
-       //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
-
-       if (self.owner)
-       {
-               oself = self;
-               self = self.owner;
-               door_fire ();
-               self = oself;
-       }
-}
-
-
-void door_trigger_touch()
-{
-       if (other.health < 1)
-               if (!(other.iscreature && other.deadflag == DEAD_NO))
-                       return;
-
-       if (time < self.attack_finished_single)
-               return;
-
-       // check if door is locked
-       if (!door_check_keys())
-               return;
-
-       self.attack_finished_single = time + 1;
-
-       activator = other;
-
-       self = self.owner;
-       door_use ();
-}
-
-
-void door_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       entity oself;
-       if(self.spawnflags & DOOR_NOSPLASH)
-               if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
-                       return;
-       self.health = self.health - damage;
-
-       if (self.itemkeys) {
-               // don't allow opening doors through damage if keys are required
-               return;
-       }
-
-       if (self.health <= 0)
-       {
-               oself = self;
-               self = self.owner;
-               self.health = self.max_health;
-               self.takedamage = DAMAGE_NO;    // wil be reset upon return
-               door_use ();
-               self = oself;
-       }
-}
-
-
-/*
-================
-door_touch
-
-Prints messages
-================
-*/
-void door_touch()
-{
-       if (!IS_PLAYER(other))
-               return;
-       if (self.owner.attack_finished_single > time)
-               return;
-
-       self.owner.attack_finished_single = time + 2;
-
-       if (!(self.owner.dmg) && (self.owner.message != ""))
-       {
-               if (IS_CLIENT(other))
-                       centerprint(other, self.owner.message);
-               play2(other, "misc/talk.wav");
-       }
-}
-
-
-void door_generic_plat_blocked()
-{
-
-    if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!!
-        Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-    } else {
-
-        if((self.dmg) && (other.takedamage == DAMAGE_YES))    // Shall we bite?
-            Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-
-         //Dont chamge direction for dead or dying stuff
-        if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) {
-            if (self.wait >= 0)
-            {
-                if (self.state == STATE_DOWN)
-                    door_rotating_go_up ();
-                else
-                    door_rotating_go_down ();
-            }
-        } else {
-            //gib dying stuff just to make sure
-            if((self.dmg) && (other.takedamage != DAMAGE_NO))    // Shall we bite?
-                Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0');
-        }
-    }
-
-       //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
-// if a door has a negative wait, it would never come back if blocked,
-// so let it just squash the object to death real fast
-/*     if (self.wait >= 0)
-       {
-               if (self.state == STATE_DOWN)
-                       door_rotating_go_up ();
-               else
-                       door_rotating_go_down ();
-       }
-*/
-}
-
-
-void door_rotating_hit_top()
-{
-       if (self.noise1 != "")
-               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
-       self.state = STATE_TOP;
-       if (self.spawnflags & DOOR_TOGGLE)
-               return;         // don't come down automatically
-       self.think = door_rotating_go_down;
-       self.nextthink = self.ltime + self.wait;
-}
-
-void door_rotating_hit_bottom()
-{
-       if (self.noise1 != "")
-               sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
-       if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
-       {
-               self.pos2 = '0 0 0' - self.pos2;
-               self.lip = 0;
-       }
-       self.state = STATE_BOTTOM;
-}
-
-void door_rotating_go_down()
-{
-       if (self.noise2 != "")
-               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       if (self.max_health)
-       {
-               self.takedamage = DAMAGE_YES;
-               self.health = self.max_health;
-       }
-
-       self.state = STATE_DOWN;
-       SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom);
-}
-
-void door_rotating_go_up()
-{
-       if (self.state == STATE_UP)
-               return;         // already going up
-
-       if (self.state == STATE_TOP)
-       {       // reset top wait time
-               self.nextthink = self.ltime + self.wait;
-               return;
-       }
-       if (self.noise2 != "")
-               sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       self.state = STATE_UP;
-       SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top);
-
-       string oldmessage;
-       oldmessage = self.message;
-       self.message = "";
-       SUB_UseTargets();
-       self.message = oldmessage;
-}
-
-
-
-
-/*
-=============================================================================
-
-SPAWNING FUNCTIONS
-
-=============================================================================
-*/
-
-
-entity spawn_field(vector fmins, vector fmaxs)
-{
-       entity  trigger;
-       vector  t1, t2;
-
-       trigger = spawn();
-       trigger.classname = "doortriggerfield";
-       trigger.movetype = MOVETYPE_NONE;
-       trigger.solid = SOLID_TRIGGER;
-       trigger.owner = self;
-       trigger.touch = door_trigger_touch;
-
-       t1 = fmins;
-       t2 = fmaxs;
-       setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
-       return (trigger);
-}
-
-
-entity LinkDoors_nextent(entity cur, entity near, entity pass)
-{
-       while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
-       {
-       }
-       return cur;
-}
-
-float LinkDoors_isconnected(entity e1, entity e2, entity pass)
-{
-       float DELTA = 4;
-       if (e1.absmin.x > e2.absmax.x + DELTA)
-               return false;
-       if (e1.absmin.y > e2.absmax.y + DELTA)
-               return false;
-       if (e1.absmin.z > e2.absmax.z + DELTA)
-               return false;
-       if (e2.absmin.x > e1.absmax.x + DELTA)
-               return false;
-       if (e2.absmin.y > e1.absmax.y + DELTA)
-               return false;
-       if (e2.absmin.z > e1.absmax.z + DELTA)
-               return false;
-       return true;
-}
-
-/*
-=============
-LinkDoors
-
-
-=============
-*/
-void LinkDoors()
-{
-       entity  t;
-       vector  cmins, cmaxs;
-
-       if (self.enemy)
-               return;         // already linked by another door
-       if (self.spawnflags & 4)
-       {
-               self.owner = self.enemy = self;
-
-               if (self.health)
-                       return;
-               IFTARGETED
-                       return;
-               if (self.items)
-                       return;
-               self.trigger_field = spawn_field(self.absmin, self.absmax);
-
-               return;         // don't want to link this door
-       }
-
-       FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
-
-       // set owner, and make a loop of the chain
-       dprint("LinkDoors: linking doors:");
-       for(t = self; ; t = t.enemy)
-       {
-               dprint(" ", etos(t));
-               t.owner = self;
-               if(t.enemy == world)
-               {
-                       t.enemy = self;
-                       break;
-               }
-       }
-       dprint("\n");
-
-       // collect health, targetname, message, size
-       cmins = self.absmin;
-       cmaxs = self.absmax;
-       for(t = self; ; t = t.enemy)
-       {
-               if(t.health && !self.health)
-                       self.health = t.health;
-               if((t.targetname != "") && (self.targetname == ""))
-                       self.targetname = t.targetname;
-               if((t.message != "") && (self.message == ""))
-                       self.message = t.message;
-               if (t.absmin.x < cmins.x)
-                       cmins.x = t.absmin.x;
-               if (t.absmin.y < cmins.y)
-                       cmins.y = t.absmin.y;
-               if (t.absmin.z < cmins.z)
-                       cmins.z = t.absmin.z;
-               if (t.absmax.x > cmaxs.x)
-                       cmaxs.x = t.absmax.x;
-               if (t.absmax.y > cmaxs.y)
-                       cmaxs.y = t.absmax.y;
-               if (t.absmax.z > cmaxs.z)
-                       cmaxs.z = t.absmax.z;
-               if(t.enemy == self)
-                       break;
-       }
-
-       // distribute health, targetname, message
-       for(t = self; t; t = t.enemy)
-       {
-               t.health = self.health;
-               t.targetname = self.targetname;
-               t.message = self.message;
-               if(t.enemy == self)
-                       break;
-       }
-
-       // shootable, or triggered doors just needed the owner/enemy links,
-       // they don't spawn a field
-
-       if (self.health)
-               return;
-       IFTARGETED
-               return;
-       if (self.items)
-               return;
-
-       self.trigger_field = spawn_field(cmins, cmaxs);
-}
-
-
-/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
-if two doors touch, they are assumed to be connected and operate as a unit.
-
-TOGGLE causes the door to wait in both the start and end states for a trigger event.
-
-START_OPEN causes the door to move to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
-
-GOLD_KEY causes the door to open only if the activator holds a gold key.
-
-SILVER_KEY causes the door to open only if the activator holds a silver key.
-
-"message"      is printed when the door is touched if it is a trigger door and it hasn't been fired yet
-"angle"                determines the opening direction
-"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
-"health"       if set, door must be shot open
-"speed"                movement speed (100 default)
-"wait"         wait before returning (3 default, -1 = never return)
-"lip"          lip remaining at end of move (8 default)
-"dmg"          damage to inflict when blocked (2 default)
-"sounds"
-0)     no sound
-1)     stone
-2)     base
-3)     stone chain
-4)     screechy metal
-FIXME: only one sound set available at the time being
-
-*/
-
-void door_init_startopen()
-{
-       setorigin (self, self.pos2);
-       self.pos2 = self.pos1;
-       self.pos1 = self.origin;
-}
-
-void door_reset()
-{
-       setorigin(self, self.pos1);
-       self.velocity = '0 0 0';
-       self.state = STATE_BOTTOM;
-       self.think = func_null;
-       self.nextthink = 0;
-}
-
-// spawnflags require key (for now only func_door)
-const float SPAWNFLAGS_GOLD_KEY = 8;
-const float SPAWNFLAGS_SILVER_KEY = 16;
-void spawnfunc_func_door()
-{
-       // Quake 1 keys compatibility
-       if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
-               self.itemkeys |= ITEM_KEY_BIT(0);
-       if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
-               self.itemkeys |= ITEM_KEY_BIT(1);
-
-       //if (!self.deathtype) // map makers can override this
-       //      self.deathtype = " got in the way";
-       SetMovedir ();
-
-       self.max_health = self.health;
-       if (!InitMovingBrushTrigger())
-               return;
-       self.effects |= EF_LOWPRECISION;
-       self.classname = "door";
-
-       self.blocked = door_blocked;
-       self.use = door_use;
-
-       // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
-       // if(self.spawnflags & 8)
-       //      self.dmg = 10000;
-
-    if(self.dmg && (self.message == ""))
-               self.message = "was squished";
-    if(self.dmg && (self.message2 == ""))
-               self.message2 = "was squished by";
-
-       if (self.sounds > 0)
-       {
-               precache_sound ("plats/medplat1.wav");
-               precache_sound ("plats/medplat2.wav");
-               self.noise2 = "plats/medplat1.wav";
-               self.noise1 = "plats/medplat2.wav";
-       }
-
-       if (!self.speed)
-               self.speed = 100;
-       if (!self.wait)
-               self.wait = 3;
-       if (!self.lip)
-               self.lip = 8;
-
-       self.pos1 = self.origin;
-       self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
-
-// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
-// but spawn in the open position
-       if (self.spawnflags & DOOR_START_OPEN)
-               InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION);
-
-       self.state = STATE_BOTTOM;
-
-       if (self.health)
-       {
-               self.takedamage = DAMAGE_YES;
-               self.event_damage = door_damage;
-       }
-
-       if (self.items)
-               self.wait = -1;
-
-       self.touch = door_touch;
-
-// LinkDoors can't be done until all of the doors have been spawned, so
-// the sizes can be detected properly.
-       InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
-
-       self.reset = door_reset;
-}
-
-/*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
-if two doors touch, they are assumed to be connected and operate as a unit.
-
-TOGGLE causes the door to wait in both the start and end states for a trigger event.
-
-BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor.
-The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction
-must have set trigger_reverse to 1.
-BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side.
-
-START_OPEN causes the door to move to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).
-
-"message"      is printed when the door is touched if it is a trigger door and it hasn't been fired yet
-"angle"                determines the destination angle for opening. negative values reverse the direction.
-"targetname"    if set, no touch field will be spawned and a remote button or trigger field activates the door.
-"health"       if set, door must be shot open
-"speed"                movement speed (100 default)
-"wait"         wait before returning (3 default, -1 = never return)
-"dmg"          damage to inflict when blocked (2 default)
-"sounds"
-0)     no sound
-1)     stone
-2)     base
-3)     stone chain
-4)     screechy metal
-FIXME: only one sound set available at the time being
-*/
-
-void door_rotating_reset()
-{
-       self.angles = self.pos1;
-       self.avelocity = '0 0 0';
-       self.state = STATE_BOTTOM;
-       self.think = func_null;
-       self.nextthink = 0;
-}
-
-void door_rotating_init_startopen()
-{
-       self.angles = self.movedir;
-       self.pos2 = '0 0 0';
-       self.pos1 = self.movedir;
-}
-
-
-void spawnfunc_func_door_rotating()
-{
-
-       //if (!self.deathtype) // map makers can override this
-       //      self.deathtype = " got in the way";
-
-       // I abuse "movedir" for denoting the axis for now
-       if (self.spawnflags & 64) // X (untested)
-               self.movedir = '0 0 1';
-       else if (self.spawnflags & 128) // Y (untested)
-               self.movedir = '1 0 0';
-       else // Z
-               self.movedir = '0 1 0';
-
-       if (self.angles.y ==0) self.angles.y = 90;
-
-       self.movedir = self.movedir * self.angles.y;
-       self.angles = '0 0 0';
-
-       self.max_health = self.health;
-       self.avelocity = self.movedir;
-       if (!InitMovingBrushTrigger())
-               return;
-       self.velocity = '0 0 0';
-       //self.effects |= EF_LOWPRECISION;
-       self.classname = "door_rotating";
-
-       self.blocked = door_blocked;
-       self.use = door_use;
-
-    if(self.spawnflags & 8)
-        self.dmg = 10000;
-
-    if(self.dmg && (self.message == ""))
-               self.message = "was squished";
-    if(self.dmg && (self.message2 == ""))
-               self.message2 = "was squished by";
-
-    if (self.sounds > 0)
-       {
-               precache_sound ("plats/medplat1.wav");
-               precache_sound ("plats/medplat2.wav");
-               self.noise2 = "plats/medplat1.wav";
-               self.noise1 = "plats/medplat2.wav";
-       }
-
-       if (!self.speed)
-               self.speed = 50;
-       if (!self.wait)
-               self.wait = 1;
-       self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating
-
-       self.pos1 = '0 0 0';
-       self.pos2 = self.movedir;
-
-// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
-// but spawn in the open position
-       if (self.spawnflags & DOOR_START_OPEN)
-               InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION);
-
-       self.state = STATE_BOTTOM;
-
-       if (self.health)
-       {
-               self.takedamage = DAMAGE_YES;
-               self.event_damage = door_damage;
-       }
-
-       if (self.items)
-               self.wait = -1;
-
-       self.touch = door_touch;
-
-// LinkDoors can't be done until all of the doors have been spawned, so
-// the sizes can be detected properly.
-       InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS);
-
-       self.reset = door_rotating_reset;
-}
-
-/*
-=============================================================================
-
-SECRET DOORS
-
-=============================================================================
-*/
-
-void() fd_secret_move1;
-void() fd_secret_move2;
-void() fd_secret_move3;
-void() fd_secret_move4;
-void() fd_secret_move5;
-void() fd_secret_move6;
-void() fd_secret_done;
-
-const float SECRET_OPEN_ONCE = 1;              // stays open
-const float SECRET_1ST_LEFT = 2;               // 1st move is left of arrow
-const float SECRET_1ST_DOWN = 4;               // 1st move is down from arrow
-const float SECRET_NO_SHOOT = 8;               // only opened by trigger
-const float SECRET_YES_SHOOT = 16;     // shootable even if targeted
-
-void fd_secret_use()
-{
-       float temp;
-       string message_save;
-
-       self.health = 10000;
-       self.bot_attack = true;
-
-       // exit if still moving around...
-       if (self.origin != self.oldorigin)
-               return;
-
-       message_save = self.message;
-       self.message = ""; // no more message
-       SUB_UseTargets();                               // fire all targets / killtargets
-       self.message = message_save;
-
-       self.velocity = '0 0 0';
-
-       // Make a sound, wait a little...
-
-       if (self.noise1 != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM);
-       self.nextthink = self.ltime + 0.1;
-
-       temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
-       makevectors(self.mangle);
-
-       if (!self.t_width)
-       {
-               if (self.spawnflags & SECRET_1ST_DOWN)
-                       self.t_width = fabs(v_up * self.size);
-               else
-                       self.t_width = fabs(v_right * self.size);
-       }
-
-       if (!self.t_length)
-               self.t_length = fabs(v_forward * self.size);
-
-       if (self.spawnflags & SECRET_1ST_DOWN)
-               self.dest1 = self.origin - v_up * self.t_width;
-       else
-               self.dest1 = self.origin + v_right * (self.t_width * temp);
-
-       self.dest2 = self.dest1 + v_forward * self.t_length;
-       SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1);
-       if (self.noise2 != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-}
-
-void fd_secret_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       fd_secret_use();
-}
-
-// Wait after first movement...
-void fd_secret_move1()
-{
-       self.nextthink = self.ltime + 1.0;
-       self.think = fd_secret_move2;
-       if (self.noise3 != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
-}
-
-// Start moving sideways w/sound...
-void fd_secret_move2()
-{
-       if (self.noise2 != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3);
-}
-
-// Wait here until time to go back...
-void fd_secret_move3()
-{
-       if (self.noise3 != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
-       if (!(self.spawnflags & SECRET_OPEN_ONCE))
-       {
-               self.nextthink = self.ltime + self.wait;
-               self.think = fd_secret_move4;
-       }
-}
-
-// Move backward...
-void fd_secret_move4()
-{
-       if (self.noise2 != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5);
-}
-
-// Wait 1 second...
-void fd_secret_move5()
-{
-       self.nextthink = self.ltime + 1.0;
-       self.think = fd_secret_move6;
-       if (self.noise3 != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
-}
-
-void fd_secret_move6()
-{
-       if (self.noise2 != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM);
-       SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done);
-}
-
-void fd_secret_done()
-{
-       if (self.spawnflags&SECRET_YES_SHOOT)
-       {
-               self.health = 10000;
-               self.takedamage = DAMAGE_YES;
-               //self.th_pain = fd_secret_use;
-       }
-       if (self.noise3 != "")
-               sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
-}
-
-void secret_blocked()
-{
-       if (time < self.attack_finished_single)
-               return;
-       self.attack_finished_single = time + 0.5;
-       //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic);
-}
-
-/*
-==============
-secret_touch
-
-Prints messages
-================
-*/
-void secret_touch()
-{
-       if (!other.iscreature)
-               return;
-       if (self.attack_finished_single > time)
-               return;
-
-       self.attack_finished_single = time + 2;
-
-       if (self.message)
-       {
-               if (IS_CLIENT(other))
-                       centerprint(other, self.message);
-               play2(other, "misc/talk.wav");
-       }
-}
-
-void secret_reset()
-{
-       if (self.spawnflags&SECRET_YES_SHOOT)
-       {
-               self.health = 10000;
-               self.takedamage = DAMAGE_YES;
-       }
-       setorigin(self, self.oldorigin);
-       self.think = func_null;
-       self.nextthink = 0;
-}
-
-/*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot
-Basic secret door. Slides back, then to the side. Angle determines direction.
-wait  = # of seconds before coming back
-1st_left = 1st move is left of arrow
-1st_down = 1st move is down from arrow
-always_shoot = even if targeted, keep shootable
-t_width = override WIDTH to move back (or height if going down)
-t_length = override LENGTH to move sideways
-"dmg"          damage to inflict when blocked (2 default)
-
-If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
-"sounds"
-1) medieval
-2) metal
-3) base
-*/
-
-void spawnfunc_func_door_secret()
-{
-       /*if (!self.deathtype) // map makers can override this
-               self.deathtype = " got in the way";*/
-
-       if (!self.dmg)
-               self.dmg = 2;
-
-       // Magic formula...
-       self.mangle = self.angles;
-       self.angles = '0 0 0';
-       self.classname = "door";
-       if (!InitMovingBrushTrigger())
-               return;
-       self.effects |= EF_LOWPRECISION;
-
-       self.touch = secret_touch;
-       self.blocked = secret_blocked;
-       self.speed = 50;
-       self.use = fd_secret_use;
-       IFTARGETED
-       {
-       }
-       else
-               self.spawnflags |= SECRET_YES_SHOOT;
-
-       if(self.spawnflags&SECRET_YES_SHOOT)
-       {
-               self.health = 10000;
-               self.takedamage = DAMAGE_YES;
-               self.event_damage = fd_secret_damage;
-       }
-       self.oldorigin = self.origin;
-       if (!self.wait)
-               self.wait = 5;          // 5 seconds before closing
-
-       self.reset = secret_reset;
-       secret_reset();
-}
-
-/*QUAKED spawnfunc_func_fourier (0 .5 .8) ?
-Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions.
-netname: list of <frequencymultiplier> <phase> <x> <y> <z> quadruples, separated by spaces; note that phase 0 represents a sine wave, and phase 0.25 a cosine wave (by default, it uses 1 0 0 0 1, to match func_bobbing's defaults
-speed: how long one cycle of frequency multiplier 1 in seconds (default 4)
-height: amplitude modifier (default 32)
-phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0)
-noise: path/name of looping .wav file to play.
-dmg: Do this mutch dmg every .dmgtime intervall when blocked
-dmgtime: See above.
-*/
-
-void func_fourier_controller_think()
-{
-       vector v;
-       float n, i, t;
-
-       self.nextthink = time + 0.1;
-       if(self.owner.active != ACTIVE_ACTIVE)
-       {
-               self.owner.velocity = '0 0 0';
-               return;
-       }
-
-
-       n = floor((tokenize_console(self.owner.netname)) / 5);
-       t = self.nextthink * self.owner.cnt + self.owner.phase * 360;
-
-       v = self.owner.destvec;
-
-       for(i = 0; i < n; ++i)
-       {
-               makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0');
-               v = v + ('1 0 0' * stof(argv(i*5+2)) + '0 1 0' * stof(argv(i*5+3)) + '0 0 1' * stof(argv(i*5+4))) * self.owner.height * v_forward.y;
-       }
-
-       if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed
-               // * 10 so it will arrive in 0.1 sec
-               self.owner.velocity = (v - self.owner.origin) * 10;
-}
-
-void spawnfunc_func_fourier()
-{
-       entity controller;
-       if (self.noise != "")
-       {
-               precache_sound(self.noise);
-               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
-       }
-
-       if (!self.speed)
-               self.speed = 4;
-       if (!self.height)
-               self.height = 32;
-       self.destvec = self.origin;
-       self.cnt = 360 / self.speed;
-
-       self.blocked = generic_plat_blocked;
-       if(self.dmg && (self.message == ""))
-               self.message = " was squished";
-    if(self.dmg && (self.message2 == ""))
-               self.message2 = "was squished by";
-       if(self.dmg && (!self.dmgtime))
-               self.dmgtime = 0.25;
-       self.dmgtime2 = time;
-
-       if(self.netname == "")
-               self.netname = "1 0 0 0 1";
-
-       if (!InitMovingBrushTrigger())
-               return;
-
-       self.active = ACTIVE_ACTIVE;
-
-       // wait for targets to spawn
-       controller = spawn();
-       controller.classname = "func_fourier_controller";
-       controller.owner = self;
-       controller.nextthink = time + 1;
-       controller.think = func_fourier_controller_think;
-       self.nextthink = self.ltime + 999999999;
-       self.think = SUB_NullThink; // for PushMove
-
-       // Savage: Reduce bandwith, critical on e.g. nexdm02
-       self.effects |= EF_LOWPRECISION;
-
-       // TODO make a reset function for this one
-}
-
-// reusing some fields havocbots declared
-.entity wp00, wp01, wp02, wp03;
-
-.float targetfactor, target2factor, target3factor, target4factor;
-.vector targetnormal, target2normal, target3normal, target4normal;
-
-vector func_vectormamamam_origin(entity o, float t)
-{
-       vector v, p;
-       float f;
-       entity e;
-
-       f = o.spawnflags;
-       v = '0 0 0';
-
-       e = o.wp00;
-       if(e)
-       {
-               p = e.origin + t * e.velocity;
-               if(f & 1)
-                       v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
-               else
-                       v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
-       }
-
-       e = o.wp01;
-       if(e)
-       {
-               p = e.origin + t * e.velocity;
-               if(f & 2)
-                       v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
-               else
-                       v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
-       }
-
-       e = o.wp02;
-       if(e)
-       {
-               p = e.origin + t * e.velocity;
-               if(f & 4)
-                       v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
-               else
-                       v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
-       }
-
-       e = o.wp03;
-       if(e)
-       {
-               p = e.origin + t * e.velocity;
-               if(f & 8)
-                       v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
-               else
-                       v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
-       }
-
-       return v;
-}
-
-void func_vectormamamam_controller_think()
-{
-       self.nextthink = time + 0.1;
-
-       if(self.owner.active != ACTIVE_ACTIVE)
-       {
-               self.owner.velocity = '0 0 0';
-               return;
-       }
-
-       if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
-               self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10;
-}
-
-void func_vectormamamam_findtarget()
-{
-       if(self.target != "")
-               self.wp00 = find(world, targetname, self.target);
-
-       if(self.target2 != "")
-               self.wp01 = find(world, targetname, self.target2);
-
-       if(self.target3 != "")
-               self.wp02 = find(world, targetname, self.target3);
-
-       if(self.target4 != "")
-               self.wp03 = find(world, targetname, self.target4);
-
-       if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
-               objerror("No reference entity found, so there is nothing to move. Aborting.");
-
-       self.destvec = self.origin - func_vectormamamam_origin(self, 0);
-
-       entity controller;
-       controller = spawn();
-       controller.classname = "func_vectormamamam_controller";
-       controller.owner = self;
-       controller.nextthink = time + 1;
-       controller.think = func_vectormamamam_controller_think;
-}
-
-void spawnfunc_func_vectormamamam()
-{
-       if (self.noise != "")
-       {
-               precache_sound(self.noise);
-               soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
-       }
-
-       if(!self.targetfactor)
-               self.targetfactor = 1;
-
-       if(!self.target2factor)
-               self.target2factor = 1;
-
-       if(!self.target3factor)
-               self.target3factor = 1;
-
-       if(!self.target4factor)
-               self.target4factor = 1;
-
-       if(vlen(self.targetnormal))
-               self.targetnormal = normalize(self.targetnormal);
-
-       if(vlen(self.target2normal))
-               self.target2normal = normalize(self.target2normal);
-
-       if(vlen(self.target3normal))
-               self.target3normal = normalize(self.target3normal);
-
-       if(vlen(self.target4normal))
-               self.target4normal = normalize(self.target4normal);
-
-       self.blocked = generic_plat_blocked;
-       if(self.dmg && (self.message == ""))
-               self.message = " was squished";
-    if(self.dmg && (self.message == ""))
-               self.message2 = "was squished by";
-       if(self.dmg && (!self.dmgtime))
-               self.dmgtime = 0.25;
-       self.dmgtime2 = time;
-
-       if(self.netname == "")
-               self.netname = "1 0 0 0 1";
-
-       if (!InitMovingBrushTrigger())
-               return;
-
-       // wait for targets to spawn
-       self.nextthink = self.ltime + 999999999;
-       self.think = SUB_NullThink; // for PushMove
-
-       // Savage: Reduce bandwith, critical on e.g. nexdm02
-       self.effects |= EF_LOWPRECISION;
-
-       self.active = ACTIVE_ACTIVE;
-
-       InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
-}
-
-#endif
-
-void conveyor_think()
-{
-#ifdef CSQC
-       float dt = time - self.move_time;
-       self.move_time = time;
-       if(dt <= 0) { return; }
-#endif
-       entity e;
-
-       // set myself as current conveyor where possible
-       for(e = world; (e = findentity(e, conveyor, self)); )
-               e.conveyor = world;
-
-       if(self.state)
-       {
-               for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
-                       if(!e.conveyor.state)
-#ifdef SVQC
-                               if(isPushable(e))
-#elif defined(CSQC)
-                               if(e.isplayermodel)
-#endif
-                               {
-                                       vector emin = e.absmin;
-                                       vector emax = e.absmax;
-                                       if(self.solid == SOLID_BSP)
-                                       {
-                                               emin -= '1 1 1';
-                                               emax += '1 1 1';
-                                       }
-                                       if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
-                                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
-                                                       e.conveyor = self;
-                               }
-
-               for(e = world; (e = findentity(e, conveyor, self)); )
-               {
-#ifdef SVQC
-                       if(IS_CLIENT(e)) // doing it via velocity has quite some advantages
-                               continue; // done in SV_PlayerPhysics
-#elif defined(CSQC)
-                       if(e.isplayermodel)
-                               continue;
-#endif
-
-                       setorigin(e, e.origin + self.movedir * PHYS_INPUT_FRAMETIME);
-                       move_out_of_solid(e);
-#ifdef SVQC
-                       UpdateCSQCProjectile(e);
-#endif
-                       /*
-                       // stupid conveyor code
-                       tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
-                       if(trace_fraction > 0)
-                               setorigin(e, trace_endpos);
-                       */
-               }
-       }
-
-#ifdef SVQC
-       self.nextthink = time;
-#endif
-}
-
-#ifdef SVQC
-
-void conveyor_use()
-{
-       self.state = !self.state;
-
-       self.SendFlags |= 2;
-}
-
-void conveyor_reset()
-{
-       self.state = (self.spawnflags & 1);
-
-       self.SendFlags |= 2;
-}
-
-float conveyor_send(entity to, float sf)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_CONVEYOR);
-       WriteByte(MSG_ENTITY, sf);
-
-       if(sf & 1)
-       {
-               WriteByte(MSG_ENTITY, self.warpzone_isboxy);
-               WriteCoord(MSG_ENTITY, self.origin_x);
-               WriteCoord(MSG_ENTITY, self.origin_y);
-               WriteCoord(MSG_ENTITY, self.origin_z);
-
-               WriteCoord(MSG_ENTITY, self.mins_x);
-               WriteCoord(MSG_ENTITY, self.mins_y);
-               WriteCoord(MSG_ENTITY, self.mins_z);
-               WriteCoord(MSG_ENTITY, self.maxs_x);
-               WriteCoord(MSG_ENTITY, self.maxs_y);
-               WriteCoord(MSG_ENTITY, self.maxs_z);
-
-               WriteCoord(MSG_ENTITY, self.movedir_x);
-               WriteCoord(MSG_ENTITY, self.movedir_y);
-               WriteCoord(MSG_ENTITY, self.movedir_z);
-
-               WriteByte(MSG_ENTITY, self.speed);
-               WriteByte(MSG_ENTITY, self.state);
-
-               WriteString(MSG_ENTITY, self.targetname);
-               WriteString(MSG_ENTITY, self.target);
-       }
-
-       if(sf & 2)
-               WriteByte(MSG_ENTITY, self.state);
-
-       return true;
-}
-
-void conveyor_init()
-{
-       if (!self.speed)
-               self.speed = 200;
-       self.movedir = self.movedir * self.speed;
-       self.think = conveyor_think;
-       self.nextthink = time;
-       IFTARGETED
-       {
-               self.use = conveyor_use;
-               self.reset = conveyor_reset;
-               conveyor_reset();
-       }
-       else
-               self.state = 1;
-
-       Net_LinkEntity(self, 0, false, conveyor_send);
-
-       self.SendFlags |= 1;
-}
-
-void spawnfunc_trigger_conveyor()
-{
-       SetMovedir();
-       EXACTTRIGGER_INIT;
-       conveyor_init();
-}
-
-void spawnfunc_func_conveyor()
-{
-       SetMovedir();
-       InitMovingBrushTrigger();
-       self.movetype = MOVETYPE_NONE;
-       conveyor_init();
-}
-
-#elif defined(CSQC)
-
-void conveyor_init()
-{
-       self.draw = conveyor_think;
-       self.drawmask = MASK_NORMAL;
-
-       self.movetype = MOVETYPE_NONE;
-       self.model = "";
-       self.solid = SOLID_TRIGGER;
-       self.move_origin = self.origin;
-       self.move_time = time;
-}
-
-void ent_conveyor()
-{
-       float sf = ReadByte();
-
-       if(sf & 1)
-       {
-               self.warpzone_isboxy = ReadByte();
-               self.origin_x = ReadCoord();
-               self.origin_y = ReadCoord();
-               self.origin_z = ReadCoord();
-               setorigin(self, self.origin);
-
-               self.mins_x = ReadCoord();
-               self.mins_y = ReadCoord();
-               self.mins_z = ReadCoord();
-               self.maxs_x = ReadCoord();
-               self.maxs_y = ReadCoord();
-               self.maxs_z = ReadCoord();
-               setsize(self, self.mins, self.maxs);
-
-               self.movedir_x = ReadCoord();
-               self.movedir_y = ReadCoord();
-               self.movedir_z = ReadCoord();
-
-               self.speed = ReadByte();
-               self.state = ReadByte();
-
-               self.targetname = strzone(ReadString());
-               self.target = strzone(ReadString());
-
-               conveyor_init();
-       }
-
-       if(sf & 2)
-               self.state = ReadByte();
-}
-
-#endif
diff --git a/qcsrc/server/t_swamp.qc b/qcsrc/server/t_swamp.qc
deleted file mode 100644 (file)
index b63e7ff..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-       #include "../dpdefs/progsdefs.qh"
-    #include "../warpzonelib/util_server.qh"
-    #include "../common/weapons/weapons.qh"
-    #include "defs.qh"
-    #include "../common/deathtypes.qh"
-#endif
-
-/*
-*              t_swamp.c
-*              Adds spawnfunc_trigger_swamp and suppoart routines for xonotic 1.2.1+
-*              Author tZork (Jakob MG)
-*              jakob@games43.se
-*              2005 11 29
-*/
-
-.float swamp_interval; //Hurt players in swamp with this interval
-.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?)
-.entity swampslug;
-
-void spawnfunc_trigger_swamp(void);
-void swamp_touch(void);
-void swampslug_think();
-
-
-/*
-* Uses a entity calld swampslug to handle players in the swamp
-* It works like this: When the plyer enters teh swamp the spawnfunc_trigger_swamp
-* attaches a new "swampslug" to the player. As long as the plyer is inside
-* the swamp the swamp gives the slug new health. But the slug slowly kills itself
-* so when the player goes outside the swamp, it dies and releases the player from the
-* swamps curses (dmg/slowdown)
-*
-* I do it this way becuz there is no "untouch" event.
-*
-* --NOTE--
-* THE ACCTUAL slowdown is done in cl_physics.c on line 57-60
-* --NOTE--
-*/
-void swampslug_think(void)
-{
-       //Slowly kill the slug
-       self.health = self.health - 1;
-
-       //Slug dead? then remove curses.
-       if(self.health <= 0) {
-               self.owner.in_swamp = 0;
-               remove(self);
-               //centerprint(self.owner,"Killing slug...\n");
-               return;
-       }
-
-       // Slug still alive, so we are still in the swamp
-       // Or we have exited it very recently.
-       // Do the damage and renew the timer.
-       Damage (self.owner, self, self, self.dmg, DEATH_SWAMP, other.origin, '0 0 0');
-
-       self.nextthink = time + self.swamp_interval;
-}
-
-void swamp_touch(void)
-{
-       // If whatever thats touching the swamp is not a player
-       // or if its a dead player, just dont care abt it.
-       if(!IS_PLAYER(other) || other.deadflag != DEAD_NO)
-               return;
-
-       EXACTTRIGGER_TOUCH;
-
-       // Chech if player alredy got a swampslug.
-       if(other.in_swamp != 1) {
-               // If not attach one.
-               //centerprint(other,"Entering swamp!\n");
-               other.swampslug = spawn();
-               other.swampslug.health = 2;
-               other.swampslug.think = swampslug_think;
-               other.swampslug.nextthink = time;
-               other.swampslug.owner = other;
-               other.swampslug.dmg = self.dmg;
-               other.swampslug.swamp_interval = self.swamp_interval;
-               other.swamp_slowdown = self.swamp_slowdown;
-               other.in_swamp = 1;
-               return;
-       }
-
-       //other.in_swamp = 1;
-
-       //Revitalize players swampslug
-       other.swampslug.health = 2;
-}
-
-/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ?
-Players gettin into the swamp will
-get slowd down and damaged
-*/
-void spawnfunc_trigger_swamp(void)
-{
-       // Init stuff
-       EXACTTRIGGER_INIT;
-       self.touch = swamp_touch;
-
-       // Setup default keys, if missing
-       if(self.dmg <= 0)
-               self.dmg = 5;
-       if(self.swamp_interval <= 0)
-               self.swamp_interval = 1;
-       if(self.swamp_slowdown <= 0)
-               self.swamp_slowdown = 0.5;
-}
index 46df0eb78636d45f82d5cab7e64ff1f2cdfa412f..38d35bb033aa27f83a6e99ae1041a88e5569d910 100644 (file)
@@ -7,6 +7,7 @@
     #include "../warpzonelib/util_server.qh"
     #include "../warpzonelib/server.qh"
     #include "../common/constants.qh"
+       #include "../common/triggers/subs.qh"
     #include "../common/util.qh"
     #include "weapons/csqcprojectile.qh"
     #include "autocvars.qh"
diff --git a/qcsrc/server/target_music.qc b/qcsrc/server/target_music.qc
deleted file mode 100644 (file)
index 94bc92f..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-       #include "../dpdefs/progsdefs.qh"
-    #include "../dpdefs/dpextensions.qh"
-    #include "../common/constants.qh"
-    #include "constants.qh"
-    #include "defs.qh"
-#endif
-
-.float lifetime;
-// values:
-//   volume
-//   noise
-//   targetname
-//   lifetime
-//   fade_time
-//   fade_rate
-// when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0)
-// when targetname is not set, THIS ONE is default
-void target_music_sendto(float to, float is)
-{
-       WriteByte(to, SVC_TEMPENTITY);
-       WriteByte(to, TE_CSQC_TARGET_MUSIC);
-       WriteShort(to, num_for_edict(self));
-       WriteByte(to, self.volume * 255.0 * is);
-       WriteByte(to, self.fade_time * 16.0);
-       WriteByte(to, self.fade_rate * 16.0);
-       WriteByte(to, self.lifetime);
-       WriteString(to, self.noise);
-}
-void target_music_reset()
-{
-       if(self.targetname == "")
-               target_music_sendto(MSG_ALL, 1);
-}
-void target_music_use()
-{
-       if(!activator)
-               return;
-       if(IS_REAL_CLIENT(activator))
-       {
-               msg_entity = activator;
-               target_music_sendto(MSG_ONE, 1);
-       }
-       entity head;
-       FOR_EACH_SPEC(head) if(head.enemy == activator) { msg_entity = head; target_music_sendto(MSG_ONE, 1); }
-}
-void spawnfunc_target_music()
-{
-       self.use = target_music_use;
-       self.reset = target_music_reset;
-       if(!self.volume)
-               self.volume = 1;
-       if(self.targetname == "")
-               target_music_sendto(MSG_INIT, 1);
-       else
-               target_music_sendto(MSG_INIT, 0);
-}
-void TargetMusic_RestoreGame()
-{
-       for(self = world; (self = find(self, classname, "target_music")); )
-       {
-               if(self.targetname == "")
-                       target_music_sendto(MSG_INIT, 1);
-               else
-                       target_music_sendto(MSG_INIT, 0);
-       }
-}
-// values:
-//   volume
-//   noise
-//   targetname
-//   fade_time
-// spawnflags:
-//   1 = START_OFF
-// when triggered, it is disabled/enabled for everyone
-float trigger_music_SendEntity(entity to, float sf)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC);
-       sf &= ~0x80;
-       if(self.cnt)
-               sf |= 0x80;
-       WriteByte(MSG_ENTITY, sf);
-       if(sf & 4)
-       {
-               WriteCoord(MSG_ENTITY, self.origin.x);
-               WriteCoord(MSG_ENTITY, self.origin.y);
-               WriteCoord(MSG_ENTITY, self.origin.z);
-       }
-       if(sf & 1)
-       {
-               if(self.model != "null")
-               {
-                       WriteShort(MSG_ENTITY, self.modelindex);
-                       WriteCoord(MSG_ENTITY, self.mins.x);
-                       WriteCoord(MSG_ENTITY, self.mins.y);
-                       WriteCoord(MSG_ENTITY, self.mins.z);
-                       WriteCoord(MSG_ENTITY, self.maxs.x);
-                       WriteCoord(MSG_ENTITY, self.maxs.y);
-                       WriteCoord(MSG_ENTITY, self.maxs.z);
-               }
-               else
-               {
-                       WriteShort(MSG_ENTITY, 0);
-                       WriteCoord(MSG_ENTITY, self.maxs.x);
-                       WriteCoord(MSG_ENTITY, self.maxs.y);
-                       WriteCoord(MSG_ENTITY, self.maxs.z);
-               }
-               WriteByte(MSG_ENTITY, self.volume * 255.0);
-               WriteByte(MSG_ENTITY, self.fade_time * 16.0);
-               WriteByte(MSG_ENTITY, self.fade_rate * 16.0);
-               WriteString(MSG_ENTITY, self.noise);
-       }
-       return 1;
-}
-void trigger_music_reset()
-{
-       self.cnt = !(self.spawnflags & 1);
-       self.SendFlags |= 0x80;
-}
-void trigger_music_use()
-{
-       self.cnt = !self.cnt;
-       self.SendFlags |= 0x80;
-}
-void spawnfunc_trigger_music()
-{
-       if(self.model != "")
-               setmodel(self, self.model);
-       if(!self.volume)
-               self.volume = 1;
-       if(!self.modelindex)
-       {
-               setorigin(self, self.origin + self.mins);
-               setsize(self, '0 0 0', self.maxs - self.mins);
-       }
-       trigger_music_reset();
-
-       self.use = trigger_music_use;
-       self.reset = trigger_music_reset;
-
-       Net_LinkEntity(self, false, 0, trigger_music_SendEntity);
-}
diff --git a/qcsrc/server/target_spawn.qc b/qcsrc/server/target_spawn.qc
deleted file mode 100644 (file)
index 28145a2..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
-       #include "../dpdefs/progsdefs.qh"
-    #include "../dpdefs/dpextensions.qh"
-    #include "../common/util.qh"
-    #include "defs.qh"
-#endif
-
-// spawner entity
-// "classname" "target_spawn"
-// "message" "fieldname value fieldname value ..."
-// "spawnflags"
-//   1 = call the spawn function
-//   2 = trigger on map load
-
-float target_spawn_initialized;
-.void() target_spawn_spawnfunc;
-float target_spawn_spawnfunc_field;
-.entity target_spawn_activator;
-.float target_spawn_id;
-float target_spawn_count;
-
-void target_spawn_helper_setmodel()
-{
-       setmodel(self, self.model);
-}
-
-void target_spawn_helper_setsize()
-{
-       setsize(self, self.mins, self.maxs);
-}
-
-void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act)
-{
-       float i, n, valuefieldpos;
-       string key, value, valuefield, valueoffset, valueoffsetrandom;
-       entity valueent;
-       vector data, data2;
-       entity oldself;
-       entity oldactivator;
-
-       n = tokenize_console(msg);
-
-       for(i = 0; i < n-1; i += 2)
-       {
-               key = argv(i);
-               value = argv(i+1);
-               if(key == "$")
-               {
-                       data.x = -1;
-                       data.y = FIELD_STRING;
-               }
-               else
-               {
-                       data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key)));
-                       if(data.y == 0) // undefined field, i.e., invalid type
-                       {
-                               print("target_spawn: invalid/unknown entity key ", key, " specified, ignored!\n");
-                               continue;
-                       }
-               }
-               if(substring(value, 0, 1) == "$")
-               {
-                       value = substring(value, 1, strlen(value) - 1);
-                       if(substring(value, 0, 1) == "$")
-                       {
-                               // deferred replacement
-                               // do nothing
-                               // useful for creating target_spawns with this!
-                       }
-                       else
-                       {
-                               // replace me!
-                               valuefieldpos = strstrofs(value, "+", 0);
-                               valueoffset = "";
-                               if(valuefieldpos != -1)
-                               {
-                                       valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
-                                       value = substring(value, 0, valuefieldpos);
-                               }
-
-                               valuefieldpos = strstrofs(valueoffset, "+", 0);
-                               valueoffsetrandom = "";
-                               if(valuefieldpos != -1)
-                               {
-                                       valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1);
-                                       valueoffset = substring(valueoffset, 0, valuefieldpos);
-                               }
-
-                               valuefieldpos = strstrofs(value, ".", 0);
-                               valuefield = "";
-                               if(valuefieldpos != -1)
-                               {
-                                       valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
-                                       value = substring(value, 0, valuefieldpos);
-                               }
-
-                               if(value == "self")
-                               {
-                                       valueent = self;
-                                       value = "";
-                               }
-                               else if(value == "activator")
-                               {
-                                       valueent = act;
-                                       value = "";
-                               }
-                               else if(value == "other")
-                               {
-                                       valueent = other;
-                                       value = "";
-                               }
-                               else if(value == "pusher")
-                               {
-                                       if(time < act.pushltime)
-                                               valueent = act.pusher;
-                                       else
-                                               valueent = world;
-                                       value = "";
-                               }
-                               else if(value == "target")
-                               {
-                                       valueent = e;
-                                       value = "";
-                               }
-                               else if(value == "killtarget")
-                               {
-                                       valueent = kt;
-                                       value = "";
-                               }
-                               else if(value == "target2")
-                               {
-                                       valueent = t2;
-                                       value = "";
-                               }
-                               else if(value == "target3")
-                               {
-                                       valueent = t3;
-                                       value = "";
-                               }
-                               else if(value == "target4")
-                               {
-                                       valueent = t4;
-                                       value = "";
-                               }
-                               else if(value == "time")
-                               {
-                                       valueent = world;
-                                       value = ftos(time);
-                               }
-                               else
-                               {
-                                       print("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!\n");
-                                       continue;
-                               }
-
-                               if(valuefield == "")
-                               {
-                                       if(value == "")
-                                               value = ftos(num_for_edict(valueent));
-                               }
-                               else
-                               {
-                                       if(value != "")
-                                       {
-                                               print("target_spawn: try to get a field of a non-entity, ignored!\n");
-                                               continue;
-                                       }
-                                       data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield)));
-                                       if(data2_y == 0) // undefined field, i.e., invalid type
-                                       {
-                                               print("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!\n");
-                                               continue;
-                                       }
-                                       value = getentityfieldstring(data2_x, valueent);
-                               }
-
-                               if(valueoffset != "")
-                               {
-                                       switch(data.y)
-                                       {
-                                               case FIELD_STRING:
-                                                       value = strcat(value, valueoffset);
-                                                       break;
-                                               case FIELD_FLOAT:
-                                                       value = ftos(stof(value) + stof(valueoffset));
-                                                       break;
-                                               case FIELD_VECTOR:
-                                                       value = vtos(stov(value) + stov(valueoffset));
-                                                       break;
-                                               default:
-                                                       print("target_spawn: only string, float and vector fields can do calculations, calculation ignored!\n");
-                                                       break;
-                                       }
-                               }
-
-                               if(valueoffsetrandom != "")
-                               {
-                                       switch(data.y)
-                                       {
-                                               case FIELD_FLOAT:
-                                                       value = ftos(stof(value) + random() * stof(valueoffsetrandom));
-                                                       break;
-                                               case FIELD_VECTOR:
-                                                       data2 = stov(valueoffsetrandom);
-                                                       value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1');
-                                                       break;
-                                               default:
-                                                       print("target_spawn: only float and vector fields can do random calculations, calculation ignored!\n");
-                                                       break;
-                                       }
-                               }
-                       }
-               }
-               if(key == "$")
-               {
-                       if(substring(value, 0, 1) == "_")
-                               value = strcat("target_spawn_helper", value);
-                       putentityfieldstring(target_spawn_spawnfunc_field, e, value);
-
-                       oldself = self;
-                       oldactivator = activator;
-
-                       self = e;
-                       activator = act;
-
-                       self.target_spawn_spawnfunc();
-
-                       self = oldself;
-                       activator = oldactivator;
-               }
-               else
-               {
-                       if(data.y == FIELD_VECTOR)
-                               value = strreplace("'", "", value); // why?!?
-                       putentityfieldstring(data.x, e, value);
-               }
-       }
-}
-
-void target_spawn_useon(entity e)
-{
-       self.target_spawn_activator = activator;
-       target_spawn_edit_entity(
-               e,
-               self.message,
-               find(world, targetname, self.killtarget),
-               find(world, targetname, self.target2),
-               find(world, targetname, self.target3),
-               find(world, targetname, self.target4),
-               activator
-       );
-}
-
-float target_spawn_cancreate()
-{
-       float c;
-       entity e;
-
-       c = self.count;
-       if(c == 0) // no limit?
-               return 1;
-
-       ++c; // increase count to not include MYSELF
-       for(e = world; (e = findfloat(e, target_spawn_id, self.target_spawn_id)); --c)
-               ;
-
-       // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more
-       if(c == 0)
-               return 0;
-       return 1;
-}
-
-void target_spawn_use()
-{
-       entity e;
-
-       if(self.target == "")
-       {
-               // spawn new entity
-               if(!target_spawn_cancreate())
-                       return;
-               e = spawn();
-               target_spawn_useon(e);
-               e.target_spawn_id = self.target_spawn_id;
-       }
-       else if(self.target == "*activator")
-       {
-               // edit entity
-               if(activator)
-                       target_spawn_useon(activator);
-       }
-       else
-       {
-               // edit entity
-               for(e = world; (e = find(e, targetname, self.target)); )
-                       target_spawn_useon(e);
-       }
-}
-
-void target_spawn_spawnfirst()
-{
-       activator = self.target_spawn_activator;
-       if(self.spawnflags & 2)
-               target_spawn_use();
-}
-
-void initialize_field_db()
-{
-       if(!target_spawn_initialized)
-       {
-               float n, i;
-               string fn;
-               vector prev, new;
-               float ft;
-
-               n = numentityfields();
-               for(i = 0; i < n; ++i)
-               {
-                       fn = entityfieldname(i);
-                       ft = entityfieldtype(i);
-                       new = i * '1 0 0' + ft * '0 1 0' + '0 0 1';
-                       prev = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", fn)));
-                       if(prev.y == 0)
-                       {
-                               db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(new));
-                               if(fn == "target_spawn_spawnfunc")
-                                       target_spawn_spawnfunc_field = i;
-                       }
-               }
-
-               target_spawn_initialized = 1;
-       }
-}
-
-void spawnfunc_target_spawn()
-{
-       initialize_field_db();
-       self.use = target_spawn_use;
-       self.message = strzone(strreplace("'", "\"", self.message));
-       self.target_spawn_id = ++target_spawn_count;
-       InitializeEntity(self, target_spawn_spawnfirst, INITPRIO_LAST);
-}
-
-
-void trigger_relay_if_use()
-{
-       float n;
-       n = self.count;
-
-       // TODO make this generic AND faster than nextent()ing through all, if somehow possible
-       n = (cvar_string(self.netname) == cvar_string(self.message));
-       if(self.spawnflags & 1)
-               n = !n;
-
-       if(n)
-               SUB_UseTargets();
-}
-
-void spawnfunc_trigger_relay_if()
-{
-       self.use = trigger_relay_if_use;
-}
index f2b0c56ce846d596cdd486772c89faa98d3b2160..4c3a52324ae3237bcc844f67a9e833ce2cc22fd8 100644 (file)
@@ -1,3 +1,5 @@
+#include "../../../common/triggers/subs.qh"
+
 #define cvar_base "g_turrets_unit_"
 .float clientframe;
 void turrets_setframe(float _frame, float client_only)
index 787510694e9101eca0c0158564170fdf846b5d9f..eab1e1aaef0ad29eb6f3672bfceb6a309e26897c 100644 (file)
@@ -229,7 +229,6 @@ void turrets_precash()
 
 
 #ifdef TURRET_DEBUG
-void SUB_Remove();
 void marker_think()
 {
     if(self.cnt)
index 87dfe52a7e55f2d0a8b94dadbbb4dcacca51653a..06760b09f401e674941dd2ee68f29e273e0baeff 100644 (file)
@@ -2,6 +2,8 @@ const vector RACER_MIN = '-120 -120 -40';
 const vector RACER_MAX = '120 120 40';
 
 #ifdef SVQC
+#include "../../common/triggers/trigger/impulse.qh"
+
 void racer_exit(float eject);
 void racer_enter();
 
index 96d54e3de7bfa832da6a1d6ba8458031389deed8..5f639a88063f709743cc84a26d7e4c05e228d64f 100644 (file)
@@ -1,3 +1,5 @@
+#include "../../common/triggers/subs.qh"
+
 float autocvar_g_vehicles_crush_dmg;
 float autocvar_g_vehicles_crush_force;
 float autocvar_g_vehicles_delayspawn;
index 6481242dc69aeccea26cf57caed0cb7cf2a7537d..a6744f702492f7fa91a07d004b72d123a71644f5 100644 (file)
@@ -573,6 +573,45 @@ vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org)
        return nearest;
 }
 
+float WarpZoneLib_BadClassname(string myclassname)
+{
+       switch(myclassname)
+       {
+               case "weapon_info":
+               case "monster_info":
+               case "deathtype":
+               case "callback":
+               case "callbackchain":
+               case "weaponentity":
+               case "exteriorweaponentity":
+               case "csqc_score_team":
+               case "pingplreport":
+               case "ent_client_scoreinfo":
+               case "saved_cvar_value":
+               case "accuracy":
+               case "entcs_sender_v2":
+               case "entcs_receiver_v2":
+               case "clientinit":
+               case "sprite_waypoint":
+               case "waypoint":
+               case "gibsplash":
+               //case "net_linked": // actually some real entities are linked without classname, fail
+               case "":
+                       return true;
+       }
+
+       if(startsWith(myclassname, "msg_"))
+               return true;
+
+       if(startsWith(myclassname, "target_"))
+               return true;
+
+       if(startsWith(myclassname, "info_"))
+               return true;
+
+       return false;
+}
+
 .float WarpZone_findradius_hit;
 .entity WarpZone_findradius_next;
 void WarpZone_FindRadius_Recurse(vector org, float rad,        vector org0,               vector transform, vector shift, float needlineofsight)
@@ -591,6 +630,9 @@ void WarpZone_FindRadius_Recurse(vector org, float rad,        vector org0,
 
        for(e = e0; e; e = e.chain)
        {
+               if(WarpZoneLib_BadClassname(e.classname))
+                       continue;
+               print(e.classname, ", first check\n");
                p = WarpZoneLib_NearestPointOnBox(e.origin + e.mins, e.origin + e.maxs, org0);
                if(needlineofsight)
                {
@@ -626,6 +668,11 @@ void WarpZone_FindRadius_Recurse(vector org, float rad,        vector org0,
        }
        for(e = wz; e; e = e.WarpZone_findradius_next)
        {
+               if(WarpZoneLib_BadClassname(e.classname))
+                       continue;
+
+               print(e.classname, ", second check\n");
+
                org0_new = WarpZone_TransformOrigin(e, org);
                traceline(e.warpzone_targetorigin, org0_new, MOVE_NOMONSTERS, e);
                org_new = trace_endpos;
index 5d529c12e215ab78c66f85c48d7c79a8f3800147..8e25b983f60dcf52d701fcc5cf0cbaa5a61488fa 100644 (file)
@@ -6,6 +6,7 @@
        #include "common.qh"
        #include "server.qh"
        #include "../common/constants.qh"
+       #include "../common/triggers/subs.qh"
        #include "../common/util.qh"
        #include "../server/constants.qh"
        #include "../server/defs.qh"