-
-//=============================================================================
-
-// 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()
-{
- 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);
-}
-
-.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;
-}
-
-// tZorks trigger impulse / gravity
-.float radius;
-.float falloff;
-.float strength;
-.float lastpushtime;
-
-// 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);
-}
-
-
-
-
-.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"));
- }
-}
-
-
-
-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;
-}
-
-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
-}
-
-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;
-}
-
-#endif