]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/g_triggers.qc
make team/invert logic identical to most other ents for trigger_multiple
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / g_triggers.qc
index 446a4b7eb7fb22ada8c233293155eea2c252a269..0a70de75f802fb13561c7ccf6eb259788b901023 100644 (file)
@@ -51,6 +51,9 @@ void SUB_UseTargets()
                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;
        }
 
@@ -186,14 +189,12 @@ void multi_use()
 void multi_touch()
 {
        if not(self.spawnflags & 2)
-       {
                if not(other.iscreature)
                        return;
 
-               if(self.team)
-               if(self.team == other.team)
+       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')
@@ -413,6 +414,13 @@ void spawnfunc_trigger_counter()
 .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)
        {
@@ -455,6 +463,7 @@ entity trigger_hurt_first;
 void spawnfunc_trigger_hurt()
 {
        EXACTTRIGGER_INIT;
+       self.active = ACTIVE_ACTIVE;
        self.touch = trigger_hurt_touch;
        if (!self.dmg)
                self.dmg = 1000;
@@ -462,6 +471,7 @@ void spawnfunc_trigger_hurt()
                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;
@@ -492,6 +502,9 @@ float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
 .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)
        {
@@ -504,7 +517,7 @@ void trigger_heal_touch()
                        if (other.health < self.max_health)
                        {
                                other.health = min(other.health + self.health, self.max_health);
-                               other.pauserothealth_finished = max(other.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));
+                               other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
                                sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
                        }
                }
@@ -513,6 +526,8 @@ void trigger_heal_touch()
 
 void spawnfunc_trigger_heal()
 {
+       self.active = ACTIVE_ACTIVE;
+       
        EXACTTRIGGER_INIT;
        self.touch = trigger_heal_touch;
        if (!self.health)
@@ -533,88 +548,243 @@ void spawnfunc_trigger_heal()
 //
 //////////////////////////////////////////////////////////////
 
-.float trigger_gravity_active;
 .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.cnt is set to 2 by trigger_gravity_touch() and decreased by 1 here.
+       // 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.cnt <= 0)
+       if(self.count <= 0)
        {
-               self.owner.gravity = 0;
-               self.owner.trigger_gravity_active = 0;
-               remove(self);
+               if(self.owner.trigger_gravity_check == self)
+                       trigger_gravity_remove(self.owner);
+               else
+                       remove(self);
+               return;
        }
        else
        {
-               self.cnt -= 1;
+               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 not(self.spawnflags & 1)
        {
-               if(!other.trigger_gravity_active)
+               if(other.trigger_gravity_check)
                {
-                       other.trigger_gravity_active = 1;
-                       other.trigger_gravity_check = spawn();
-                       other.trigger_gravity_check.owner = other;
-                       other.trigger_gravity_check.think = trigger_gravity_check_think;
-                       other.trigger_gravity_check.nextthink = time;
+                       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.cnt = 2;
+               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 != self.gravity)
+       if (other.gravity != g)
        {
-               other.gravity = self.gravity;
+               other.gravity = g;
                if(self.noise != "")
                        sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM);
+               UpdateCSQCProjectile(self.owner);
        }
 };
 
 void spawnfunc_trigger_gravity()
 {
-       if(!self.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
 .float volume, atten;
-void target_speaker_use() {sound(self, CHAN_TRIGGER, self.noise, VOL_BASE * self.volume, self.atten);}
+void target_speaker_use_off();
+void target_speaker_use_activator()
+{
+       if(clienttype(activator) != CLIENTTYPE_REAL)
+               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, CHAN_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, CHAN_TRIGGER, snd, VOL_BASE * self.volume, self.atten);
+       if(self.spawnflags & 3)
+               self.use = target_speaker_use_off;
+}
+void target_speaker_use_off()
+{
+       sound(self, CHAN_TRIGGER, "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);
-       IFTARGETED
+
+       if(!self.atten && !(self.spawnflags & 4))
        {
-               if(!self.atten)
+               IFTARGETED
                        self.atten = ATTN_NORM;
-               else if(self.atten < 0)
-                       self.atten = 0;
-               if(!self.volume)
-                       self.volume = 1;
-               self.use = target_speaker_use;
+               else
+                       self.atten = ATTN_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
        {
-               if(!self.atten)
-                       self.atten = ATTN_STATIC;
-               else if(self.atten < 0)
-                       self.atten = 0;
-               if(!self.volume)
-                       self.volume = 1;
+               // Quake/Nexuiz fallback
                ambientsound (self.origin, self.noise, VOL_BASE * self.volume, self.atten);
+               remove(self);
        }
 };
 
@@ -766,7 +936,7 @@ void spawnfunc_func_pointparticles()
        if(!self.cnt)
                self.cnt = particleeffectnum(self.mdl);
 
-       Net_LinkEntity(self, FALSE, 0, pointparticles_SendEntity);
+       Net_LinkEntity(self, (self.spawnflags & 4), 0, pointparticles_SendEntity);
 
        IFTARGETED
        {
@@ -950,6 +1120,8 @@ void misc_laser_think()
 {
        vector o;
        entity oldself;
+       entity hitent;
+       vector hitloc;
 
        self.nextthink = time;
 
@@ -970,20 +1142,18 @@ void misc_laser_think()
                o = self.origin + v_forward * 32768;
        }
 
-       if(self.dmg)
+       if(self.dmg || self.enemy.target != "")
        {
-               if(self.dmg < 0)
-                       FireRailgunBullet(self.origin, o, 100000, 0, 0, 0, 0, 0, DEATH_HURTTRIGGER);
-               else
-                       FireRailgunBullet(self.origin, o, self.dmg * frametime, 0, 0, 0, 0, 0, DEATH_HURTTRIGGER);
+               traceline(self.origin, o, MOVE_NORMAL, self);
        }
+       hitent = trace_ent;
+       hitloc = trace_endpos;
 
        if(self.enemy.target != "") // DETECTOR laser
        {
-               traceline(self.origin, o, MOVE_NORMAL, self);
                if(trace_ent.iscreature)
                {
-                       self.pusher = trace_ent;
+                       self.pusher = hitent;
                        if(!self.count)
                        {
                                self.count = 1;
@@ -1009,18 +1179,29 @@ void misc_laser_think()
                        }
                }
        }
+
+       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 & 0xE0); // use that bit to indicate finite length 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)
        {
@@ -1040,7 +1221,8 @@ float laser_SendEntity(entity to, float fl)
                        WriteByte(MSG_ENTITY, bound(0, self.scale * 16.0, 255));
                        WriteByte(MSG_ENTITY, bound(0, self.modelscale * 16.0, 255));
                }
-               WriteShort(MSG_ENTITY, self.cnt + 1);
+               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)
        {
@@ -1123,6 +1305,8 @@ void spawnfunc_misc_laser()
                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);
@@ -1154,6 +1338,9 @@ void trigger_impulse_touch1()
     float pushdeltatime;
     float str;
 
+       if (self.active != ACTIVE_ACTIVE) 
+               return;
+
        // FIXME: Better checking for what to push and not.
        if not(other.iscreature)
        if (other.classname != "corpse")
@@ -1197,7 +1384,8 @@ void trigger_impulse_touch1()
     if(!pushdeltatime) return;
 
     other.velocity = other.velocity + normalize(targ.origin - self.origin) * str * pushdeltatime;
-       other.flags &~= FL_ONGROUND;
+    other.flags &~= FL_ONGROUND;
+    UpdateCSQCProjectile(other);
 }
 
 // Directionless (accelerator/decelerator) mode
@@ -1205,6 +1393,9 @@ void trigger_impulse_touch2()
 {
     float pushdeltatime;
 
+       if (self.active != ACTIVE_ACTIVE) 
+               return;
+
        // FIXME: Better checking for what to push and not.
        if not(other.iscreature)
        if (other.classname != "corpse")
@@ -1234,6 +1425,7 @@ void trigger_impulse_touch2()
 
     // div0: ticrate independent, 1 = identity (not 20)
     other.velocity = other.velocity * pow(self.strength, pushdeltatime);
+    UpdateCSQCProjectile(other);
 }
 
 // Spherical (gravity/repulsor) mode
@@ -1242,6 +1434,9 @@ void trigger_impulse_touch3()
     float pushdeltatime;
     float str;
 
+       if (self.active != ACTIVE_ACTIVE) 
+               return;
+
        // FIXME: Better checking for what to push and not.
        if not(other.iscreature)
        if (other.classname != "corpse")
@@ -1281,6 +1476,7 @@ void trigger_impulse_touch3()
         str = self.strength;
 
     other.velocity = other.velocity + normalize(other.origin - self.origin) * str * pushdeltatime;
+    UpdateCSQCProjectile(other);
 }
 
 /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
@@ -1303,10 +1499,12 @@ in directional and sperical mode. For damper/accelerator mode this is not nesses
 
 void spawnfunc_trigger_impulse()
 {
+       self.active = ACTIVE_ACTIVE;
+
        EXACTTRIGGER_INIT;
     if(self.radius)
     {
-        if(!self.strength) self.strength = 2000 * cvar("g_triggerimpulse_radial_multiplier");
+        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;
@@ -1315,13 +1513,13 @@ void spawnfunc_trigger_impulse()
     {
         if(self.target)
         {
-            if(!self.strength) self.strength = 950 * cvar("g_triggerimpulse_directional_multiplier");
+            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, cvar("g_triggerimpulse_accel_power")) * cvar("g_triggerimpulse_accel_multiplier");
+                       self.strength = pow(self.strength, autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier;
             self.touch = trigger_impulse_touch2;
         }
     }
@@ -1879,3 +2077,64 @@ void spawnfunc_trigger_magicear()
        // target:
        //   what to trigger
 }
+
+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;        
+}
+
+.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;
+};