X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fg_triggers.qc;h=0a70de75f802fb13561c7ccf6eb259788b901023;hb=834876e6b179cf33ce95102ba25faf754e9a8773;hp=d97413f10efb94ff15770a6faf4b5af981d0aeb0;hpb=81046f42baf4ec980a069a5e9ba88b1e5fc7f2c9;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/g_triggers.qc b/qcsrc/server/g_triggers.qc index d97413f10..af7c10561 100644 --- a/qcsrc/server/g_triggers.qc +++ b/qcsrc/server/g_triggers.qc @@ -10,7 +10,7 @@ void DelayThink() activator = self.enemy; SUB_UseTargets (); remove(self); -}; +} /* ============================== @@ -33,7 +33,7 @@ match (string)self.target and call their .use function */ void SUB_UseTargets() { - local entity t, stemp, otemp, act; + entity t, stemp, otemp, act; string s; float i; @@ -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; } @@ -85,6 +88,9 @@ void SUB_UseTargets() stemp = self; otemp = other; + if(stemp.target_random) + RandomSelection_Init(); + for(i = 0; i < 4; ++i) { switch(i) @@ -100,19 +106,33 @@ void SUB_UseTargets() for(t = world; (t = find(t, targetname, s)); ) if(t.use) { - //print(stemp.classname, " ", stemp.targetname, " -> ", t.classname, " ", t.targetname, "\n"); - self = t; - other = stemp; - activator = act; - self.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; -}; +} //============================================================================= @@ -129,7 +149,7 @@ void multi_wait() self.takedamage = DAMAGE_YES; self.solid = SOLID_BBOX; } -}; +} // the trigger was just touched/killed/used @@ -151,7 +171,7 @@ void multi_trigger() } if (self.noise) - sound (self.enemy, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM); + sound (self.enemy, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM); // don't trigger again until reset self.takedamage = DAMAGE_NO; @@ -174,26 +194,24 @@ void multi_trigger() // called wheil C code is looping through area links... self.touch = SUB_Null; } -}; +} void multi_use() { self.goalentity = other; self.enemy = activator; multi_trigger(); -}; +} 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') @@ -208,7 +226,7 @@ void multi_touch() self.enemy = other; self.goalentity = other; multi_trigger (); -}; +} void multi_eventdamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) { @@ -300,7 +318,7 @@ void spawnfunc_trigger_multiple() setorigin (self, self.origin); // make sure it links into the world } } -}; +} /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch @@ -320,7 +338,7 @@ void spawnfunc_trigger_once() { self.wait = -1; spawnfunc_trigger_multiple(); -}; +} //============================================================================= @@ -331,7 +349,7 @@ void spawnfunc_trigger_relay() { self.use = SUB_UseTargets; self.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully -}; +} void delay_use() { @@ -384,7 +402,7 @@ void counter_use() centerprint(activator, "Sequence completed!"); self.enemy = activator; multi_trigger (); -}; +} void counter_reset() { @@ -408,7 +426,15 @@ void spawnfunc_trigger_counter() self.use = counter_use; self.reset = counter_reset; -}; +} + +void trigger_hurt_use() +{ + if(activator.classname == "player") + 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() @@ -416,6 +442,10 @@ 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) { @@ -424,7 +454,16 @@ void trigger_hurt_touch() { EXACTTRIGGER_TOUCH; other.triggerhurttime = time + 1; - Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + + entity own; + own = self.enemy; + if(own.classname != "player") + { + own = self; + self.enemy = world; // I still hate you all + } + + Damage (other, self, own, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); } } else @@ -445,7 +484,7 @@ void trigger_hurt_touch() } return; -}; +} /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ? Any object touching this will be hurt @@ -460,6 +499,8 @@ 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) @@ -473,7 +514,7 @@ void spawnfunc_trigger_hurt() 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) { @@ -512,12 +553,12 @@ 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")); - sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM); + other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); + sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM); } } } -}; +} void spawnfunc_trigger_heal() { @@ -532,7 +573,7 @@ void spawnfunc_trigger_heal() if(self.noise == "") self.noise = "misc/mediumhealth.wav"; precache_sound(self.noise); -}; +} ////////////////////////////////////////////////////////////// @@ -574,12 +615,12 @@ void trigger_gravity_check_think() self.count -= 1; self.nextthink = time; } -}; +} void trigger_gravity_use() { self.state = !self.state; -}; +} void trigger_gravity_touch() { @@ -624,10 +665,10 @@ void trigger_gravity_touch() { other.gravity = g; if(self.noise != "") - sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NORM); + sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM); UpdateCSQCProjectile(self.owner); } -}; +} void spawnfunc_trigger_gravity() { @@ -646,39 +687,142 @@ void spawnfunc_trigger_gravity() 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, 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); - 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); } -}; +} void spawnfunc_func_stardust() { @@ -881,7 +1025,7 @@ float rainsnow_SendEntity(entity to, float sf) 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. @@ -918,7 +1062,7 @@ void spawnfunc_func_rain() self.Version = 1; Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity); -}; +} /*QUAKED spawnfunc_func_snow (0 .5 .8) ? @@ -956,7 +1100,7 @@ void spawnfunc_func_snow() self.Version = 1; Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity); -}; +} void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype); @@ -1012,6 +1156,8 @@ void misc_laser_think() { vector o; entity oldself; + entity hitent; + vector hitloc; self.nextthink = time; @@ -1032,20 +1178,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; @@ -1071,6 +1215,15 @@ 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) @@ -1188,6 +1341,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); @@ -1222,24 +1377,7 @@ void trigger_impulse_touch1() if (self.active != ACTIVE_ACTIVE) return; - // FIXME: Better checking for what to push and not. - if not(other.iscreature) - if (other.classname != "corpse") - if (other.classname != "body") - if (other.classname != "gib") - if (other.classname != "missile") - if (other.classname != "rocket") - if (other.classname != "casing") - if (other.classname != "grenade") - if (other.classname != "plasma") - if (other.classname != "plasma_prim") - if (other.classname != "plasma_chain") - if (other.classname != "droppedweapon") - if (other.classname != "nexball_basketball") - if (other.classname != "nexball_football") - return; - - if (other.deadflag && other.iscreature) + if (!isPushable(other)) return; EXACTTRIGGER_TOUCH; @@ -1252,6 +1390,8 @@ void trigger_impulse_touch1() 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) @@ -1277,24 +1417,7 @@ void trigger_impulse_touch2() if (self.active != ACTIVE_ACTIVE) return; - // FIXME: Better checking for what to push and not. - if not(other.iscreature) - if (other.classname != "corpse") - if (other.classname != "body") - if (other.classname != "gib") - if (other.classname != "missile") - if (other.classname != "rocket") - if (other.classname != "casing") - if (other.classname != "grenade") - if (other.classname != "plasma") - if (other.classname != "plasma_prim") - if (other.classname != "plasma_chain") - if (other.classname != "droppedweapon") - if (other.classname != "nexball_basketball") - if (other.classname != "nexball_football") - return; - - if (other.deadflag && other.iscreature) + if (!isPushable(other)) return; EXACTTRIGGER_TOUCH; @@ -1318,24 +1441,7 @@ void trigger_impulse_touch3() if (self.active != ACTIVE_ACTIVE) return; - // FIXME: Better checking for what to push and not. - if not(other.iscreature) - if (other.classname != "corpse") - if (other.classname != "body") - if (other.classname != "gib") - if (other.classname != "missile") - if (other.classname != "rocket") - if (other.classname != "casing") - if (other.classname != "grenade") - if (other.classname != "plasma") - if (other.classname != "plasma_prim") - if (other.classname != "plasma_chain") - if (other.classname != "droppedweapon") - if (other.classname != "nexball_basketball") - if (other.classname != "nexball_football") - return; - - if (other.deadflag && other.iscreature) + if (!isPushable(other)) return; EXACTTRIGGER_TOUCH; @@ -1385,7 +1491,7 @@ void spawnfunc_trigger_impulse() 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; @@ -1394,13 +1500,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; } } @@ -1538,7 +1644,7 @@ void spawnfunc_trigger_multivibrator() self.state = 0; self.use = multivibrator_toggle; self.think = multivibrator_send; - self.nextthink = time; + self.nextthink = max(1, time); IFTARGETED multivibrator_reset(); @@ -1584,6 +1690,7 @@ void follow_init() attach_sameorigin(dst, src, self.message); } + dst.solid = SOLID_NOT; // solid doesn't work with attachment remove(self); } else @@ -1803,19 +1910,54 @@ 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) { float domatch, dotrigger, matchstart, l; string s, msg; entity oldself; + string savemessage; magicear_matched = FALSE; - dotrigger = ((self.classname == "player") && (self.deadflag == DEAD_NO) && ((ear.radius == 0) || (vlen(source.origin - ear.origin) <= ear.radius))); + dotrigger = ((source.classname == "player") && (source.deadflag == DEAD_NO) && ((ear.radius == 0) || (vlen(source.origin - ear.origin) <= ear.radius))); domatch = ((ear.spawnflags & 32) || dotrigger); + if not(domatch) return msgin; + if not(msgin) + { + // we are in TUBA mode! + if not(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) @@ -1837,7 +1979,7 @@ string trigger_magicear_processmessage(entity ear, entity source, float teamsay, matchstart = -1; l = strlen(ear.message); - if(self.spawnflags & 128) + if(ear.spawnflags & 128) msg = msgin; else msg = strdecolorize(msgin); @@ -1888,9 +2030,13 @@ string trigger_magicear_processmessage(entity ear, entity source, float teamsay, if(dotrigger) { - oldself = activator = self; + oldself = self; + activator = source; self = ear; + savemessage = self.message; + self.message = string_null; SUB_UseTargets(); + self.message = savemessage; self = oldself; } @@ -1936,13 +2082,16 @@ void spawnfunc_trigger_magicear() // actually handled in "say" processing // spawnflags: - // 1 = ignore say - // 2 = ignore teamsay - // 4 = ignore tell - // 8 = ignore tell to unknown player + // 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 @@ -1957,6 +2106,10 @@ void spawnfunc_trigger_magicear() // "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() @@ -2013,9 +2166,9 @@ void spawnfunc_target_changelevel_use() localcmd("endmatch\n"); else localcmd(strcat("changelevel ", self.chmap, "\n")); -}; +} void spawnfunc_target_changelevel() { self.use = spawnfunc_target_changelevel_use; -}; +}