From: Mario Date: Sat, 9 Jun 2018 15:52:03 +0000 (+1000) Subject: Rename triggers to mapobjects X-Git-Tag: xonotic-v0.8.5~2098 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=fbd313c7ceb26a09310d8062926f4ac2468623a8;p=xonotic%2Fxonotic-data.pk3dir.git Rename triggers to mapobjects --- diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 9222d976b..8e0c76d42 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index f2196cd1c..fa5a79207 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/qcsrc/common/_all.inc b/qcsrc/common/_all.inc index 32dbf5208..708b4e951 100644 --- a/qcsrc/common/_all.inc +++ b/qcsrc/common/_all.inc @@ -22,7 +22,7 @@ noref float autocvar_net_connecttimeout = 30; #ifdef GAMEQC #include "physics/all.inc" -#include "triggers/include.qc" +#include "mapobjects/include.qc" #include "viewloc.qc" #endif diff --git a/qcsrc/common/mapobjects/_mod.inc b/qcsrc/common/mapobjects/_mod.inc new file mode 100644 index 000000000..efc838487 --- /dev/null +++ b/qcsrc/common/mapobjects/_mod.inc @@ -0,0 +1,11 @@ +// generated file; do not modify +#include +#include +#include +#include +#include + +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/_mod.qh b/qcsrc/common/mapobjects/_mod.qh new file mode 100644 index 000000000..11854c091 --- /dev/null +++ b/qcsrc/common/mapobjects/_mod.qh @@ -0,0 +1,11 @@ +// generated file; do not modify +#include +#include +#include +#include +#include + +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/defs.qh b/qcsrc/common/mapobjects/defs.qh new file mode 100644 index 000000000..45afb51f9 --- /dev/null +++ b/qcsrc/common/mapobjects/defs.qh @@ -0,0 +1,41 @@ +#pragma once + +//----------- +// SPAWNFLAGS +//----------- +const int START_ENABLED = BIT(0); +const int START_DISABLED = BIT(0); +const int ALL_ENTITIES = BIT(1); +const int ON_MAPLOAD = BIT(1); +const int INVERT_TEAMS = BIT(2); +const int CRUSH = BIT(2); +const int NOSPLASH = BIT(8); // generic anti-splashdamage spawnflag +const int ONLY_PLAYERS = BIT(14); + +// triggers +const int SPAWNFLAG_NOMESSAGE = BIT(0); +const int SPAWNFLAG_NOTOUCH = BIT(0); + +//---------- +// SENDFLAGS +//---------- +const int SF_TRIGGER_INIT = BIT(0); +const int SF_TRIGGER_UPDATE = BIT(1); +const int SF_TRIGGER_RESET = BIT(2); + +//---------------- +// STATES & ACTIVE +//---------------- +#ifdef CSQC +// this stuff is defined in the server side engine VM, so we must define it separately here +const int STATE_TOP = 0; +const int STATE_BOTTOM = 1; +const int STATE_UP = 2; +const int STATE_DOWN = 3; + +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 diff --git a/qcsrc/common/mapobjects/func/_mod.inc b/qcsrc/common/mapobjects/func/_mod.inc new file mode 100644 index 000000000..e3425e34a --- /dev/null +++ b/qcsrc/common/mapobjects/func/_mod.inc @@ -0,0 +1,19 @@ +// generated file; do not modify +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/func/_mod.qh b/qcsrc/common/mapobjects/func/_mod.qh new file mode 100644 index 000000000..c0381a3f2 --- /dev/null +++ b/qcsrc/common/mapobjects/func/_mod.qh @@ -0,0 +1,19 @@ +// generated file; do not modify +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/func/bobbing.qc b/qcsrc/common/mapobjects/func/bobbing.qc new file mode 100644 index 000000000..b647e15a8 --- /dev/null +++ b/qcsrc/common/mapobjects/func/bobbing.qc @@ -0,0 +1,85 @@ +#include "bobbing.qh" +#ifdef SVQC +.float height; +void func_bobbing_controller_think(entity this) +{ + vector v; + this.nextthink = time + 0.1; + + if(this.owner.active != ACTIVE_ACTIVE) + { + this.owner.velocity = '0 0 0'; + return; + } + + // calculate sinewave using makevectors + makevectors((this.nextthink * this.owner.cnt + this.owner.phase * 360) * '0 1 0'); + v = this.owner.destvec + this.owner.movedir * v_forward_y; + if(this.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed + // * 10 so it will arrive in 0.1 sec + this.owner.velocity = (v - this.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. +*/ +spawnfunc(func_bobbing) +{ + entity controller; + if (this.noise != "") + { + precache_sound(this.noise); + soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + if (!this.speed) + this.speed = 4; + if (!this.height) + this.height = 32; + // center of bobbing motion + this.destvec = this.origin; + // time scale to get degrees + this.cnt = 360 / this.speed; + + this.active = ACTIVE_ACTIVE; + + // damage when blocked + setblocked(this, generic_plat_blocked); + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + // how far to bob + if (this.spawnflags & BOBBING_XAXIS) + this.movedir = '1 0 0' * this.height; + else if (this.spawnflags & BOBBING_YAXIS) + this.movedir = '0 1 0' * this.height; + else // Z + this.movedir = '0 0 1' * this.height; + + if (!InitMovingBrushTrigger(this)) + return; + + // wait for targets to spawn + controller = new(func_bobbing_controller); + controller.owner = this; + controller.nextthink = time + 1; + setthink(controller, func_bobbing_controller_think); + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); + + // Savage: Reduce bandwith, critical on e.g. nexdm02 + this.effects |= EF_LOWPRECISION; + + // TODO make a reset function for this one +} +#endif diff --git a/qcsrc/common/mapobjects/func/bobbing.qh b/qcsrc/common/mapobjects/func/bobbing.qh new file mode 100644 index 000000000..58f7bb714 --- /dev/null +++ b/qcsrc/common/mapobjects/func/bobbing.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int BOBBING_XAXIS = BIT(0); +const int BOBBING_YAXIS = BIT(1); diff --git a/qcsrc/common/mapobjects/func/breakable.qc b/qcsrc/common/mapobjects/func/breakable.qc new file mode 100644 index 000000000..d09ccd5e0 --- /dev/null +++ b/qcsrc/common/mapobjects/func/breakable.qc @@ -0,0 +1,374 @@ +#include "breakable.qh" +#ifdef SVQC + +#include +#include +#include +#include +#include +#include + +.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: +// START_DISABLED: needs to be triggered to activate +// BREAKABLE_INDICATE_DAMAGE: indicate damage +// BREAKABLE_NODAMAGE: don't take direct damage (needs to be triggered to 'explode', then triggered again to restore) +// NOSPLASH: don't take splash 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 this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force); + +// +// func_breakable +// - basically func_assault_destructible for general gameplay use +// +void LaunchDebris (entity this, string debrisname, vector force) +{ + entity dbr = spawn(); + vector org = this.absmin + + '1 0 0' * random() * (this.absmax.x - this.absmin.x) + + '0 1 0' * random() * (this.absmax.y - this.absmin.y) + + '0 0 1' * random() * (this.absmax.z - this.absmin.z); + setorigin(dbr, org); + _setmodel (dbr, debrisname ); + dbr.skin = this.debrisskin; + dbr.colormap = this.colormap; // inherit team colors + dbr.owner = this; // do not be affected by our own explosion + set_movetype(dbr, this.debrismovetype); + dbr.solid = this.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 = this.debrisvelocity.x + this.debrisvelocityjitter.x * crandom(); + dbr.velocity_y = this.debrisvelocity.y + this.debrisvelocityjitter.y * crandom(); + dbr.velocity_z = this.debrisvelocity.z + this.debrisvelocityjitter.z * crandom(); + dbr.velocity = dbr.velocity + force * this.debrisdamageforcescale; + dbr.angles = this.angles; + dbr.avelocity_x = random()*this.debrisavelocityjitter.x; + dbr.avelocity_y = random()*this.debrisavelocityjitter.y; + dbr.avelocity_z = random()*this.debrisavelocityjitter.z; + dbr.damageforcescale = this.debrisdamageforcescale; + if(dbr.damageforcescale) + dbr.takedamage = DAMAGE_YES; + SUB_SetFade(dbr, time + this.debristime + crandom() * this.debristimejitter, this.debrisfadetime); +} + +void func_breakable_colormod(entity this) +{ + float h; + if (!(this.spawnflags & BREAKABLE_INDICATE_DAMAGE)) + return; + h = this.health / this.max_health; + if(h < 0.25) + this.colormod = '1 0 0'; + else if(h <= 0.75) + this.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5); + else + this.colormod = '1 1 1'; +} + +void func_breakable_look_destroyed(entity this) +{ + float floorZ; + + if(this.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first + this.dropped_origin = this.origin; + + if(this.mdl_dead == "") + this.effects |= EF_NODRAW; + else { + if (this.origin == '0 0 0') { // probably no origin brush, so don't spawn in the middle of the map.. + floorZ = this.absmin.z; + setorigin(this, ((this.absmax + this.absmin) * 0.5)); + this.origin_z = floorZ; + } + _setmodel(this, this.mdl_dead); + ApplyMinMaxScaleAngles(this); + this.effects &= ~EF_NODRAW; + } + + this.solid = SOLID_NOT; +} + +void func_breakable_look_restore(entity this) +{ + _setmodel(this, this.mdl); + ApplyMinMaxScaleAngles(this); + this.effects &= ~EF_NODRAW; + + if(this.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow + setorigin(this, this.dropped_origin); + + this.solid = SOLID_BSP; +} + +void func_breakable_behave_destroyed(entity this) +{ + this.health = this.max_health; + this.takedamage = DAMAGE_NO; + if(this.bot_attack) + IL_REMOVE(g_bot_targets, this); + this.bot_attack = false; + this.event_damage = func_null; + this.state = STATE_BROKEN; + if(this.spawnflags & BREAKABLE_NODAMAGE) + this.use = func_null; + func_breakable_colormod(this); + if (this.noise1) + stopsound (this, CH_TRIGGER_SINGLE); +} + +void func_breakable_think(entity this) +{ + this.nextthink = time; + CSQCMODEL_AUTOUPDATE(this); +} + +void func_breakable_destroy(entity this, entity actor, entity trigger); +void func_breakable_behave_restore(entity this) +{ + this.health = this.max_health; + if(this.sprite) + { + WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); + WaypointSprite_UpdateHealth(this.sprite, this.health); + } + if(!(this.spawnflags & BREAKABLE_NODAMAGE)) + { + this.takedamage = DAMAGE_AIM; + if(!this.bot_attack) + IL_PUSH(g_bot_targets, this); + this.bot_attack = true; + this.event_damage = func_breakable_damage; + } + if(this.spawnflags & BREAKABLE_NODAMAGE) + this.use = func_breakable_destroy; // don't need to set it usually, as .use isn't reset + this.state = STATE_ALIVE; + //this.nextthink = 0; // cancel auto respawn + setthink(this, func_breakable_think); + this.nextthink = time + 0.1; + func_breakable_colormod(this); + if (this.noise1) + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); +} + +void func_breakable_init_for_player(entity this, entity player) +{ + if (this.noise1 && this.state == STATE_ALIVE && IS_REAL_CLIENT(player)) + { + msg_entity = player; + soundto (MSG_ONE, this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + } +} + +void func_breakable_destroyed(entity this) +{ + func_breakable_look_destroyed(this); + func_breakable_behave_destroyed(this); +} + +void func_breakable_restore(entity this, entity actor, entity trigger) +{ + func_breakable_look_restore(this); + func_breakable_behave_restore(this); +} + +void func_breakable_restore_self(entity this) +{ + func_breakable_restore(this, NULL, NULL); +} + +vector debrisforce; // global, set before calling this +void func_breakable_destroy(entity this, entity actor, entity trigger) +{ + float n, i; + string oldmsg; + + entity act = this.owner; + this.owner = NULL; // set by W_PrepareExplosionByDamage + + // now throw around the debris + n = tokenize_console(this.debris); + for(i = 0; i < n; ++i) + LaunchDebris(this, argv(i), debrisforce); + + func_breakable_destroyed(this); + + if(this.noise) + _sound (this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + + if(this.dmg) + RadiusDamage(this, act, this.dmg, this.dmg_edge, this.dmg_radius, this, NULL, this.dmg_force, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, NULL); + + if(this.cnt) // TODO + __pointparticles(this.cnt, this.absmin * 0.5 + this.absmax * 0.5, '0 0 0', this.count); + + if(this.respawntime) + { + CSQCMODEL_AUTOUPDATE(this); + setthink(this, func_breakable_restore_self); + this.nextthink = time + this.respawntime + crandom() * this.respawntimejitter; + } + + oldmsg = this.message; + this.message = ""; + SUB_UseTargets(this, act, trigger); + this.message = oldmsg; +} + +void func_breakable_destroy_self(entity this) +{ + func_breakable_destroy(this, NULL, NULL); +} + +void func_breakable_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if(this.state == STATE_BROKEN) + return; + if(this.spawnflags & NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + if(this.team) + if(attacker.team == this.team) + return; + this.pain_finished = time; + this.health = this.health - damage; + if(this.sprite) + { + WaypointSprite_Ping(this.sprite); + WaypointSprite_UpdateHealth(this.sprite, this.health); + } + func_breakable_colormod(this); + + if(this.health <= 0) + { + debrisforce = force; + + this.takedamage = DAMAGE_NO; + this.event_damage = func_null; + + if(IS_CLIENT(attacker)) //&& this.classname == "func_assault_destructible") + { + this.owner = attacker; + this.realowner = attacker; + } + + // do not explode NOW but in the NEXT FRAME! + // because recursive calls to RadiusDamage are not allowed + this.nextthink = time; + CSQCMODEL_AUTOUPDATE(this); + setthink(this, func_breakable_destroy_self); + } +} + +void func_breakable_reset(entity this) +{ + this.team = this.team_saved; + func_breakable_look_restore(this); + if(this.spawnflags & START_DISABLED) + func_breakable_behave_destroyed(this); + else + func_breakable_behave_restore(this); +} + +// destructible walls that can be used to trigger target_objective_decrease +spawnfunc(func_breakable) +{ + float n, i; + if(!this.health) + this.health = 100; + this.max_health = this.health; + + // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway + if(!this.debrismovetype) this.debrismovetype = MOVETYPE_BOUNCE; + if(!this.debrissolid) this.debrissolid = SOLID_NOT; + if(this.debrisvelocity == '0 0 0') this.debrisvelocity = '0 0 140'; + if(this.debrisvelocityjitter == '0 0 0') this.debrisvelocityjitter = '70 70 70'; + if(this.debrisavelocityjitter == '0 0 0') this.debrisavelocityjitter = '600 600 600'; + if(!this.debristime) this.debristime = 3.5; + if(!this.debristimejitter) this.debristime = 2.5; + + if(this.mdl != "") + this.cnt = _particleeffectnum(this.mdl); + if(this.count == 0) + this.count = 1; + + if(this.message == "") + this.message = "got too close to an explosion"; + if(this.message2 == "") + this.message2 = "was pushed into an explosion by"; + if(!this.dmg_radius) + this.dmg_radius = 150; + if(!this.dmg_force) + this.dmg_force = 200; + + this.mdl = this.model; + SetBrushEntityModel(this); + + if(this.spawnflags & BREAKABLE_NODAMAGE) + this.use = func_breakable_destroy; + else + this.use = func_breakable_restore; + + if(this.spawnflags & BREAKABLE_NODAMAGE) + { + this.takedamage = DAMAGE_NO; + this.event_damage = func_null; + this.bot_attack = false; + } + + // precache all the models + if (this.mdl_dead) + precache_model(this.mdl_dead); + n = tokenize_console(this.debris); + for(i = 0; i < n; ++i) + precache_model(argv(i)); + if(this.noise) + precache_sound(this.noise); + if(this.noise1) + precache_sound(this.noise1); + + this.team_saved = this.team; + IL_PUSH(g_saved_team, this); + this.dropped_origin = this.origin; + + this.reset = func_breakable_reset; + this.reset(this); + + IL_PUSH(g_initforplayer, this); + this.init_for_player = func_breakable_init_for_player; + + CSQCMODEL_AUTOINIT(this); +} + +// for use in maps with a "model" key set +spawnfunc(misc_breakablemodel) { + spawnfunc_func_breakable(this); +} +#endif diff --git a/qcsrc/common/mapobjects/func/breakable.qh b/qcsrc/common/mapobjects/func/breakable.qh new file mode 100644 index 000000000..0efbcfaed --- /dev/null +++ b/qcsrc/common/mapobjects/func/breakable.qh @@ -0,0 +1,12 @@ +#pragma once + + +const int BREAKABLE_INDICATE_DAMAGE = BIT(1); +const int BREAKABLE_NODAMAGE = BIT(2); + +const int STATE_ALIVE = 0; +const int STATE_BROKEN = 1; + +#ifdef SVQC +spawnfunc(func_breakable); +#endif diff --git a/qcsrc/common/mapobjects/func/button.qc b/qcsrc/common/mapobjects/func/button.qc new file mode 100644 index 000000000..28e6481c8 --- /dev/null +++ b/qcsrc/common/mapobjects/func/button.qc @@ -0,0 +1,170 @@ +#include "button.qh" +#ifdef SVQC +// button and multiple button + +void button_wait(entity this); +void button_return(entity this); + +void button_wait(entity this) +{ + this.state = STATE_TOP; + if(this.wait >= 0) + { + this.nextthink = this.ltime + this.wait; + setthink(this, button_return); + } + SUB_UseTargets(this, this.enemy, NULL); + this.frame = 1; // use alternate textures +} + +void button_done(entity this) +{ + this.state = STATE_BOTTOM; +} + +void button_return(entity this) +{ + this.state = STATE_DOWN; + SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, button_done); + this.frame = 0; // use normal textures + if (this.health) + this.takedamage = DAMAGE_YES; // can be shot again +} + + +void button_blocked(entity this, entity blocker) +{ + // do nothing, just don't come all the way back out +} + + +void button_fire(entity this) +{ + this.health = this.max_health; + this.takedamage = DAMAGE_NO; // will be reset upon return + + if (this.state == STATE_UP || this.state == STATE_TOP) + return; + + if (this.noise != "") + _sound (this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + + this.state = STATE_UP; + SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, button_wait); +} + +void button_reset(entity this) +{ + this.health = this.max_health; + setorigin(this, this.pos1); + this.frame = 0; // use normal textures + this.state = STATE_BOTTOM; + this.velocity = '0 0 0'; + setthink(this, func_null); + this.nextthink = 0; + if (this.health) + this.takedamage = DAMAGE_YES; // can be shot again +} + +void button_use(entity this, entity actor, entity trigger) +{ + if(this.active != ACTIVE_ACTIVE) + return; + + this.enemy = actor; + button_fire(this); +} + +void button_touch(entity this, entity toucher) +{ + if (!toucher) + return; + if (!toucher.iscreature) + return; + if(toucher.velocity * this.movedir < 0) + return; + this.enemy = toucher; + if (toucher.owner) + this.enemy = toucher.owner; + button_fire (this); +} + +void button_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if(this.spawnflags & NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + if (this.spawnflags & BUTTON_DONTACCUMULATEDMG) + { + if (this.health <= damage) + { + this.enemy = attacker; + button_fire(this); + } + } + else + { + this.health = this.health - damage; + if (this.health <= 0) + { + this.enemy = attacker; + button_fire(this); + } + } +} + + +/*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 +"noise" sound that is played when the button is activated +*/ +spawnfunc(func_button) +{ + SetMovedir(this); + + if (!InitMovingBrushTrigger(this)) + return; + this.effects |= EF_LOWPRECISION; + + setblocked(this, button_blocked); + this.use = button_use; + +// if (this.health == 0) // all buttons are now shootable +// this.health = 10; + if (this.health) + { + this.max_health = this.health; + this.event_damage = button_damage; + this.takedamage = DAMAGE_YES; + } + else + settouch(this, button_touch); + + if (!this.speed) + this.speed = 40; + if (!this.wait) + this.wait = 1; + if (!this.lip) + this.lip = 4; + + if(this.noise != "") + precache_sound(this.noise); + + this.active = ACTIVE_ACTIVE; + + this.pos1 = this.origin; + this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip); + this.flags |= FL_NOTARGET; + + this.reset = button_reset; + + button_reset(this); +} +#endif diff --git a/qcsrc/common/mapobjects/func/button.qh b/qcsrc/common/mapobjects/func/button.qh new file mode 100644 index 000000000..86a0fc918 --- /dev/null +++ b/qcsrc/common/mapobjects/func/button.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int BUTTON_DONTACCUMULATEDMG = BIT(7); diff --git a/qcsrc/common/mapobjects/func/conveyor.qc b/qcsrc/common/mapobjects/func/conveyor.qc new file mode 100644 index 000000000..9ad326cfa --- /dev/null +++ b/qcsrc/common/mapobjects/func/conveyor.qc @@ -0,0 +1,178 @@ +#include "conveyor.qh" +REGISTER_NET_LINKED(ENT_CLIENT_CONVEYOR) + +void conveyor_think(entity this) +{ +#ifdef CSQC + // TODO: check if this is what is causing the glitchiness when switching between them + float dt = time - this.move_time; + this.move_time = time; + if(dt <= 0) { return; } +#endif + + // set myself as current conveyor where possible + IL_EACH(g_conveyed, it.conveyor == this, + { + it.conveyor = NULL; + IL_REMOVE(g_conveyed, it); + }); + + if(this.active == ACTIVE_ACTIVE) + { + FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, it.conveyor.active == ACTIVE_NOT && isPushable(it), + { + vector emin = it.absmin; + vector emax = it.absmax; + if(this.solid == SOLID_BSP) + { + emin -= '1 1 1'; + emax += '1 1 1'; + } + if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick + if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate + { + if(!it.conveyor) + IL_PUSH(g_conveyed, it); + it.conveyor = this; + } + }); + + IL_EACH(g_conveyed, it.conveyor == this, + { + if(IS_CLIENT(it)) // doing it via velocity has quite some advantages + continue; // done in SV_PlayerPhysics continue; + + setorigin(it, it.origin + this.movedir * PHYS_INPUT_FRAMETIME); + move_out_of_solid(it); +#ifdef SVQC + UpdateCSQCProjectile(it); +#endif + /* + // stupid conveyor code + tracebox(it.origin, it.mins, it.maxs, it.origin + this.movedir * sys_frametime, MOVE_NORMAL, it); + if(trace_fraction > 0) + setorigin(it, trace_endpos); + */ + }); + } + +#ifdef SVQC + this.nextthink = time; +#endif +} + +#ifdef SVQC + +bool conveyor_send(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_CONVEYOR); + WriteByte(MSG_ENTITY, sendflags); + + if(sendflags & SF_TRIGGER_INIT) + { + WriteByte(MSG_ENTITY, this.warpzone_isboxy); + WriteVector(MSG_ENTITY, this.origin); + + WriteVector(MSG_ENTITY, this.mins); + WriteVector(MSG_ENTITY, this.maxs); + + WriteVector(MSG_ENTITY, this.movedir); + + WriteByte(MSG_ENTITY, this.speed); + WriteByte(MSG_ENTITY, this.active); + + WriteString(MSG_ENTITY, this.targetname); + WriteString(MSG_ENTITY, this.target); + } + + if(sendflags & SF_TRIGGER_UPDATE) + WriteByte(MSG_ENTITY, this.active); + + return true; +} + +void conveyor_init(entity this) +{ + if (!this.speed) this.speed = 200; + this.movedir *= this.speed; + setthink(this, conveyor_think); + this.nextthink = time; + this.setactive = generic_netlinked_setactive; + IFTARGETED + { + // backwards compatibility + this.use = generic_netlinked_legacy_use; + } + this.reset = generic_netlinked_reset; + this.reset(this); + + FixSize(this); + + Net_LinkEntity(this, 0, false, conveyor_send); + + this.SendFlags |= SF_TRIGGER_INIT; +} + +spawnfunc(trigger_conveyor) +{ + SetMovedir(this); + EXACTTRIGGER_INIT; + conveyor_init(this); +} + +spawnfunc(func_conveyor) +{ + SetMovedir(this); + InitMovingBrushTrigger(this); + set_movetype(this, MOVETYPE_NONE); + conveyor_init(this); +} + +#elif defined(CSQC) + +void conveyor_draw(entity this) { conveyor_think(this); } + +void conveyor_init(entity this, bool isnew) +{ + if(isnew) + IL_PUSH(g_drawables, this); + this.draw = conveyor_draw; + this.drawmask = MASK_NORMAL; + + set_movetype(this, MOVETYPE_NONE); + this.model = ""; + this.solid = SOLID_TRIGGER; + this.move_time = time; +} + +NET_HANDLE(ENT_CLIENT_CONVEYOR, bool isnew) +{ + int sendflags = ReadByte(); + + if(sendflags & SF_TRIGGER_INIT) + { + this.warpzone_isboxy = ReadByte(); + this.origin = ReadVector(); + setorigin(this, this.origin); + + this.mins = ReadVector(); + this.maxs = ReadVector(); + setsize(this, this.mins, this.maxs); + + this.movedir = ReadVector(); + + this.speed = ReadByte(); + this.active = ReadByte(); + + this.targetname = strzone(ReadString()); + this.target = strzone(ReadString()); + + conveyor_init(this, isnew); + } + + if(sendflags & SF_TRIGGER_UPDATE) + this.active = ReadByte(); + + return true; +} +#endif diff --git a/qcsrc/common/mapobjects/func/conveyor.qh b/qcsrc/common/mapobjects/func/conveyor.qh new file mode 100644 index 000000000..22b674ff3 --- /dev/null +++ b/qcsrc/common/mapobjects/func/conveyor.qh @@ -0,0 +1,5 @@ +#pragma once +#include "../defs.qh" + +IntrusiveList g_conveyed; +STATIC_INIT(g_conveyed) { g_conveyed = IL_NEW(); } diff --git a/qcsrc/common/mapobjects/func/door.qc b/qcsrc/common/mapobjects/func/door.qc new file mode 100644 index 000000000..c19041aa0 --- /dev/null +++ b/qcsrc/common/mapobjects/func/door.qc @@ -0,0 +1,801 @@ +#include "door.qh" +#include "door_rotating.qh" +/* + +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(entity this); +void door_go_up(entity this, entity actor, entity trigger); + +void door_blocked(entity this, entity blocker) +{ + if((this.spawnflags & DOOR_CRUSH) +#ifdef SVQC + && (blocker.takedamage != DAMAGE_NO) +#elif defined(CSQC) + && !IS_DEAD(blocker) +#endif + ) + { // KIll Kill Kill!! +#ifdef SVQC + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + } + else + { +#ifdef SVQC + if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + + // don't change direction for dead or dying stuff + if(IS_DEAD(blocker) +#ifdef SVQC + && (blocker.takedamage == DAMAGE_NO) +#endif + ) + { + if (this.wait >= 0) + { + if (this.state == STATE_DOWN) + { + if (this.classname == "door") + door_go_up(this, NULL, NULL); + else + door_rotating_go_up(this, blocker); + } + else + { + if (this.classname == "door") + door_go_down(this); + else + door_rotating_go_down(this); + } + } + } +#ifdef SVQC + else + { + //gib dying stuff just to make sure + if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + } +#endif + } +} + +void door_hit_top(entity this) +{ + if (this.noise1 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_TOP; + if (this.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + if (this.classname == "door") + { + setthink(this, door_go_down); + } else + { + setthink(this, door_rotating_go_down); + } + this.nextthink = this.ltime + this.wait; +} + +void door_hit_bottom(entity this) +{ + if (this.noise1 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_BOTTOM; +} + +void door_go_down(entity this) +{ + if (this.noise2 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + if (this.max_health) + { + this.takedamage = DAMAGE_YES; + this.health = this.max_health; + } + + this.state = STATE_DOWN; + SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_hit_bottom); +} + +void door_go_up(entity this, entity actor, entity trigger) +{ + if (this.state == STATE_UP) + return; // already going up + + if (this.state == STATE_TOP) + { // reset top wait time + this.nextthink = this.ltime + this.wait; + return; + } + + if (this.noise2 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + this.state = STATE_UP; + SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_hit_top); + + string oldmessage; + oldmessage = this.message; + this.message = ""; + SUB_UseTargets(this, actor, trigger); + this.message = oldmessage; +} + + +/* +============================================================================= + +ACTIVATION FUNCTIONS + +============================================================================= +*/ + +bool door_check_keys(entity door, entity player) +{ + if(door.owner) + door = door.owner; + + // no key needed + if(!door.itemkeys) + return true; + + // this door require a key + // only a player can have a key + if(!IS_PLAYER(player)) + return false; + + entity store = player; +#ifdef SVQC + store = PS(player); +#endif + int valid = (door.itemkeys & store.itemkeys); + door.itemkeys &= ~valid; // only some of the needed keys were given + + if(!door.itemkeys) + { +#ifdef SVQC + play2(player, door.noise); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_UNLOCKED); +#endif + return true; + } + + if(!valid) + { +#ifdef SVQC + if(player.key_door_messagetime <= time) + { + play2(player, door.noise3); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys)); + player.key_door_messagetime = time + 2; + } +#endif + return false; + } + + // door needs keys the player doesn't have +#ifdef SVQC + if(player.key_door_messagetime <= time) + { + play2(player, door.noise3); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys)); + player.key_door_messagetime = time + 2; + } +#endif + + return false; +} + +void door_fire(entity this, entity actor, entity trigger) +{ + if (this.owner != this) + objerror (this, "door_fire: this.owner != this"); + + if (this.spawnflags & DOOR_TOGGLE) + { + if (this.state == STATE_UP || this.state == STATE_TOP) + { + entity e = this; + do { + if (e.classname == "door") { + door_go_down(e); + } else { + door_rotating_go_down(e); + } + e = e.enemy; + } while ((e != this) && (e != NULL)); + return; + } + } + +// trigger all paired doors + entity e = this; + do { + if (e.classname == "door") { + door_go_up(e, actor, trigger); + } else { + // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction + if ((e.spawnflags & DOOR_ROTATING_BIDIR) && trigger.trigger_reverse!=0 && e.lip != 666 && e.state == STATE_BOTTOM) { + e.lip = 666; // e.lip is used to remember reverse opening direction for door_rotating + e.pos2 = '0 0 0' - e.pos2; + } + // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side + if (!((e.spawnflags & DOOR_ROTATING_BIDIR) && (e.spawnflags & DOOR_ROTATING_BIDIR_IN_DOWN) && e.state == STATE_DOWN + && (((e.lip == 666) && (trigger.trigger_reverse == 0)) || ((e.lip != 666) && (trigger.trigger_reverse != 0))))) + { + door_rotating_go_up(e, trigger); + } + } + e = e.enemy; + } while ((e != this) && (e != NULL)); +} + +void door_use(entity this, entity actor, entity trigger) +{ + //dprint("door_use (model: ");dprint(this.model);dprint(")\n"); + + if (this.owner) + door_fire(this.owner, actor, trigger); +} + +void door_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if(this.spawnflags & NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + this.health = this.health - damage; + + if (this.itemkeys) + { + // don't allow opening doors through damage if keys are required + return; + } + + if (this.health <= 0) + { + this.owner.health = this.owner.max_health; + this.owner.takedamage = DAMAGE_NO; // wil be reset upon return + door_use(this.owner, NULL, NULL); + } +} + +.float door_finished; + +/* +================ +door_touch + +Prints messages +================ +*/ + +void door_touch(entity this, entity toucher) +{ + if (!IS_PLAYER(toucher)) + return; + if (this.owner.door_finished > time) + return; + + this.owner.door_finished = time + 2; + +#ifdef SVQC + if (!(this.owner.dmg) && (this.owner.message != "")) + { + if (IS_CLIENT(toucher)) + centerprint(toucher, this.owner.message); + play2(toucher, this.owner.noise); + } +#endif +} + +void door_generic_plat_blocked(entity this, entity blocker) +{ + if((this.spawnflags & DOOR_CRUSH) && (blocker.takedamage != DAMAGE_NO)) { // Kill Kill Kill!! +#ifdef SVQC + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + } + else + { + +#ifdef SVQC + if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + + //Dont chamge direction for dead or dying stuff + if(IS_DEAD(blocker) && (blocker.takedamage == DAMAGE_NO)) + { + if (this.wait >= 0) + { + if (this.state == STATE_DOWN) + door_rotating_go_up (this, blocker); + else + door_rotating_go_down (this); + } + } +#ifdef SVQC + else + { + //gib dying stuff just to make sure + if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + } +#endif + } +} + +/* +========================================= +door trigger + +Spawned if a door lacks a real activator +========================================= +*/ + +void door_trigger_touch(entity this, entity toucher) +{ + if (toucher.health < 1) +#ifdef SVQC + if (!((toucher.iscreature || (toucher.flags & FL_PROJECTILE)) && !IS_DEAD(toucher))) +#elif defined(CSQC) + if(!((IS_CLIENT(toucher) || toucher.classname == "csqcprojectile") && !IS_DEAD(toucher))) +#endif + return; + + if (time < this.door_finished) + return; + + // check if door is locked + if (!door_check_keys(this, toucher)) + return; + + this.door_finished = time + 1; + + door_use(this.owner, toucher, NULL); +} + +void door_spawnfield(entity this, vector fmins, vector fmaxs) +{ + entity trigger; + vector t1 = fmins, t2 = fmaxs; + + trigger = new(doortriggerfield); + set_movetype(trigger, MOVETYPE_NONE); + trigger.solid = SOLID_TRIGGER; + trigger.owner = this; +#ifdef SVQC + settouch(trigger, door_trigger_touch); +#endif + + setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); +} + + +/* +============= +LinkDoors + + +============= +*/ + +entity LinkDoors_nextent(entity cur, entity near, entity pass) +{ + while((cur = find(cur, classname, pass.classname)) && ((cur.spawnflags & DOOR_DONT_LINK) || cur.enemy)) + { + } + return cur; +} + +bool 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; +} + +#ifdef SVQC +void door_link(); +#endif +void LinkDoors(entity this) +{ + entity t; + vector cmins, cmaxs; + +#ifdef SVQC + door_link(); +#endif + + if (this.enemy) + return; // already linked by another door + if (this.spawnflags & DOOR_DONT_LINK) + { + this.owner = this.enemy = this; + + if (this.health) + return; + IFTARGETED + return; + if (this.items) + return; + + door_spawnfield(this, this.absmin, this.absmax); + + return; // don't want to link this door + } + + FindConnectedComponent(this, enemy, LinkDoors_nextent, LinkDoors_isconnected, this); + + // set owner, and make a loop of the chain + LOG_TRACE("LinkDoors: linking doors:"); + for(t = this; ; t = t.enemy) + { + LOG_TRACE(" ", etos(t)); + t.owner = this; + if(t.enemy == NULL) + { + t.enemy = this; + break; + } + } + LOG_TRACE(""); + + // collect health, targetname, message, size + cmins = this.absmin; + cmaxs = this.absmax; + for(t = this; ; t = t.enemy) + { + if(t.health && !this.health) + this.health = t.health; + if((t.targetname != "") && (this.targetname == "")) + this.targetname = t.targetname; + if((t.message != "") && (this.message == "")) + this.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 == this) + break; + } + + // distribute health, targetname, message + for(t = this; t; t = t.enemy) + { + t.health = this.health; + t.targetname = this.targetname; + t.message = this.message; + if(t.enemy == this) + break; + } + + // shootable, or triggered doors just needed the owner/enemy links, + // they don't spawn a field + + if (this.health) + return; + IFTARGETED + return; + if (this.items) + return; + + door_spawnfield(this, cmins, cmaxs); +} + +REGISTER_NET_LINKED(ENT_CLIENT_DOOR) + +#ifdef SVQC +/*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 this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_DOOR); + WriteByte(MSG_ENTITY, sf); + + if(sf & SF_TRIGGER_INIT) + { + WriteString(MSG_ENTITY, this.classname); + WriteByte(MSG_ENTITY, this.spawnflags); + + WriteString(MSG_ENTITY, this.model); + + trigger_common_write(this, true); + + WriteVector(MSG_ENTITY, this.pos1); + WriteVector(MSG_ENTITY, this.pos2); + + WriteVector(MSG_ENTITY, this.size); + + WriteShort(MSG_ENTITY, this.wait); + WriteShort(MSG_ENTITY, this.speed); + WriteByte(MSG_ENTITY, this.lip); + WriteByte(MSG_ENTITY, this.state); + WriteCoord(MSG_ENTITY, this.ltime); + } + + if(sf & SF_TRIGGER_RESET) + { + // client makes use of this, we do not + } + + if(sf & SF_TRIGGER_UPDATE) + { + WriteVector(MSG_ENTITY, this.origin); + + WriteVector(MSG_ENTITY, this.pos1); + WriteVector(MSG_ENTITY, this.pos2); + } + + return true; +} + +void door_link() +{ + // set size now, as everything is loaded + //FixSize(this); + //Net_LinkEntity(this, false, 0, door_send); +} +#endif + +void door_init_startopen(entity this) +{ + setorigin(this, this.pos2); + this.pos2 = this.pos1; + this.pos1 = this.origin; + +#ifdef SVQC + this.SendFlags |= SF_TRIGGER_UPDATE; +#endif +} + +void door_reset(entity this) +{ + setorigin(this, this.pos1); + this.velocity = '0 0 0'; + this.state = STATE_BOTTOM; + setthink(this, func_null); + this.nextthink = 0; + +#ifdef SVQC + this.SendFlags |= SF_TRIGGER_RESET; +#endif +} + +#ifdef SVQC + +// common code for func_door and func_door_rotating spawnfuncs +void door_init_shared(entity this) +{ + this.max_health = this.health; + + // unlock sound + if(this.noise == "") + { + this.noise = "misc/talk.wav"; + } + // door still locked sound + if(this.noise3 == "") + { + this.noise3 = "misc/talk.wav"; + } + precache_sound(this.noise); + precache_sound(this.noise3); + + if((this.dmg || (this.spawnflags & DOOR_CRUSH)) && (this.message == "")) + { + this.message = "was squished"; + } + if((this.dmg || (this.spawnflags & DOOR_CRUSH)) && (this.message2 == "")) + { + this.message2 = "was squished by"; + } + + // TODO: other soundpacks + if (this.sounds > 0) + { + this.noise2 = "plats/medplat1.wav"; + this.noise1 = "plats/medplat2.wav"; + } + + // sound when door stops moving + if(this.noise1 && this.noise1 != "") + { + precache_sound(this.noise1); + } + // sound when door is moving + if(this.noise2 && this.noise2 != "") + { + precache_sound(this.noise2); + } + + if (!this.wait) + { + this.wait = 3; + } + if (!this.lip) + { + this.lip = 8; + } + + this.state = STATE_BOTTOM; + + if (this.health) + { + //this.canteamdamage = true; // TODO + this.takedamage = DAMAGE_YES; + this.event_damage = door_damage; + } + + if (this.items) + { + this.wait = -1; + } +} + +// spawnflags require key (for now only func_door) +spawnfunc(func_door) +{ + // Quake 1 keys compatibility + if (this.spawnflags & SPAWNFLAGS_GOLD_KEY) + this.itemkeys |= ITEM_KEY_BIT(0); + if (this.spawnflags & SPAWNFLAGS_SILVER_KEY) + this.itemkeys |= ITEM_KEY_BIT(1); + + SetMovedir(this); + + if (!InitMovingBrushTrigger(this)) + return; + this.effects |= EF_LOWPRECISION; + this.classname = "door"; + + setblocked(this, door_blocked); + this.use = door_use; + + this.pos1 = this.origin; + this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip); + + if(this.spawnflags & DOOR_NONSOLID) + this.solid = SOLID_NOT; + +// DOOR_START_OPEN is to allow an entity to be lighted in the closed position +// but spawn in the open position + if (this.spawnflags & DOOR_START_OPEN) + InitializeEntity(this, door_init_startopen, INITPRIO_SETLOCATION); + + door_init_shared(this); + + if (!this.speed) + { + this.speed = 100; + } + + settouch(this, door_touch); + +// LinkDoors can't be done until all of the doors have been spawned, so +// the sizes can be detected properly. + InitializeEntity(this, LinkDoors, INITPRIO_LINKDOORS); + + this.reset = door_reset; +} + +#elif defined(CSQC) + +NET_HANDLE(ENT_CLIENT_DOOR, bool isnew) +{ + int sf = ReadByte(); + + if(sf & SF_TRIGGER_INIT) + { + this.classname = strzone(ReadString()); + this.spawnflags = ReadByte(); + + this.mdl = strzone(ReadString()); + _setmodel(this, this.mdl); + + trigger_common_read(this, true); + + this.pos1 = ReadVector(); + this.pos2 = ReadVector(); + + this.size = ReadVector(); + + this.wait = ReadShort(); + this.speed = ReadShort(); + this.lip = ReadByte(); + this.state = ReadByte(); + this.ltime = ReadCoord(); + + this.solid = SOLID_BSP; + set_movetype(this, MOVETYPE_PUSH); + this.use = door_use; + + LinkDoors(this); + + if(this.spawnflags & DOOR_START_OPEN) + door_init_startopen(this); + + this.move_time = time; + set_movetype(this, MOVETYPE_PUSH); + } + + if(sf & SF_TRIGGER_RESET) + { + door_reset(this); + } + + if(sf & SF_TRIGGER_UPDATE) + { + this.origin = ReadVector(); + setorigin(this, this.origin); + + this.pos1 = ReadVector(); + this.pos2 = ReadVector(); + } + return true; +} + +#endif diff --git a/qcsrc/common/mapobjects/func/door.qh b/qcsrc/common/mapobjects/func/door.qh new file mode 100644 index 000000000..181de8b7b --- /dev/null +++ b/qcsrc/common/mapobjects/func/door.qh @@ -0,0 +1,18 @@ +#pragma once + + +const int DOOR_START_OPEN = BIT(0); +const int DOOR_DONT_LINK = BIT(2); +const int SPAWNFLAGS_GOLD_KEY = BIT(3); // Quake 1 compat, can only be used with func_door! +const int SPAWNFLAGS_SILVER_KEY = BIT(4); // Quake 1 compat, can only be used with func_door! +const int DOOR_TOGGLE = BIT(5); + +const int DOOR_NONSOLID = BIT(10); +const int DOOR_CRUSH = BIT(11); // can't use CRUSH cause that is the same as DOOR_DONT_LINK + + +#ifdef CSQC +// stuff for preload + +.float door_finished; +#endif diff --git a/qcsrc/common/mapobjects/func/door_rotating.qc b/qcsrc/common/mapobjects/func/door_rotating.qc new file mode 100644 index 000000000..41fd05e57 --- /dev/null +++ b/qcsrc/common/mapobjects/func/door_rotating.qc @@ -0,0 +1,159 @@ +#include "door_rotating.qh" +/*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 +*/ + +#ifdef GAMEQC +void door_rotating_hit_top(entity this) +{ + if (this.noise1 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_TOP; + if (this.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + setthink(this, door_rotating_go_down); + this.nextthink = this.ltime + this.wait; +} + +void door_rotating_hit_bottom(entity this) +{ + if (this.noise1 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + if (this.lip==666) // this.lip is used to remember reverse opening direction for door_rotating + { + this.pos2 = '0 0 0' - this.pos2; + this.lip = 0; + } + this.state = STATE_BOTTOM; +} + +void door_rotating_go_down(entity this) +{ + if (this.noise2 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + if (this.max_health) + { + this.takedamage = DAMAGE_YES; + this.health = this.max_health; + } + + this.state = STATE_DOWN; + SUB_CalcAngleMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_rotating_hit_bottom); +} + +void door_rotating_go_up(entity this, entity oth) +{ + if (this.state == STATE_UP) + return; // already going up + + if (this.state == STATE_TOP) + { // reset top wait time + this.nextthink = this.ltime + this.wait; + return; + } + if (this.noise2 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + this.state = STATE_UP; + SUB_CalcAngleMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_rotating_hit_top); + + string oldmessage; + oldmessage = this.message; + this.message = ""; + SUB_UseTargets(this, NULL, oth); // TODO: is oth needed here? + this.message = oldmessage; +} +#endif + +#ifdef SVQC +void door_rotating_reset(entity this) +{ + this.angles = this.pos1; + this.avelocity = '0 0 0'; + this.state = STATE_BOTTOM; + setthink(this, func_null); + this.nextthink = 0; +} + +void door_rotating_init_startopen(entity this) +{ + this.angles = this.movedir; + this.pos2 = '0 0 0'; + this.pos1 = this.movedir; +} + +spawnfunc(func_door_rotating) +{ + //if (!this.deathtype) // map makers can override this + // this.deathtype = " got in the way"; + + // I abuse "movedir" for denoting the axis for now + if (this.spawnflags & DOOR_ROTATING_XAXIS) + this.movedir = '0 0 1'; + else if (this.spawnflags & DOOR_ROTATING_YAXIS) + this.movedir = '1 0 0'; + else // Z + this.movedir = '0 1 0'; + + if (this.angles_y==0) this.angles_y = 90; + + this.movedir = this.movedir * this.angles_y; + this.angles = '0 0 0'; + + this.avelocity = this.movedir; + if (!InitMovingBrushTrigger(this)) + return; + this.velocity = '0 0 0'; + //this.effects |= EF_LOWPRECISION; + this.classname = "door_rotating"; + + setblocked(this, door_blocked); + this.use = door_use; + + this.pos1 = '0 0 0'; + this.pos2 = this.movedir; + +// DOOR_START_OPEN is to allow an entity to be lighted in the closed position +// but spawn in the open position + if (this.spawnflags & DOOR_START_OPEN) + InitializeEntity(this, door_rotating_init_startopen, INITPRIO_SETLOCATION); + + door_init_shared(this); + if (!this.speed) + { + this.speed = 50; + } + this.lip = 0; // this.lip is used to remember reverse opening direction for door_rotating + + settouch(this, door_touch); + +// LinkDoors can't be done until all of the doors have been spawned, so +// the sizes can be detected properly. + InitializeEntity(this, LinkDoors, INITPRIO_LINKDOORS); + + this.reset = door_rotating_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/func/door_rotating.qh b/qcsrc/common/mapobjects/func/door_rotating.qh new file mode 100644 index 000000000..5ff572859 --- /dev/null +++ b/qcsrc/common/mapobjects/func/door_rotating.qh @@ -0,0 +1,14 @@ +#pragma once + + +const int DOOR_ROTATING_BIDIR = BIT(1); +const int DOOR_ROTATING_BIDIR_IN_DOWN = BIT(3); + +const int DOOR_ROTATING_XAXIS = BIT(6); +const int DOOR_ROTATING_YAXIS = BIT(7); + + +#ifdef GAMEQC +void door_rotating_go_down(entity this); +void door_rotating_go_up(entity this, entity oth); +#endif diff --git a/qcsrc/common/mapobjects/func/door_secret.qc b/qcsrc/common/mapobjects/func/door_secret.qc new file mode 100644 index 000000000..78e0dd64e --- /dev/null +++ b/qcsrc/common/mapobjects/func/door_secret.qc @@ -0,0 +1,266 @@ +#include "door_secret.qh" +#ifdef SVQC +void fd_secret_move1(entity this); +void fd_secret_move2(entity this); +void fd_secret_move3(entity this); +void fd_secret_move4(entity this); +void fd_secret_move5(entity this); +void fd_secret_move6(entity this); +void fd_secret_done(entity this); + +void fd_secret_use(entity this, entity actor, entity trigger) +{ + float temp; + string message_save; + + this.health = 10000; + if(!this.bot_attack) + IL_PUSH(g_bot_targets, this); + this.bot_attack = true; + + // exit if still moving around... + if (this.origin != this.oldorigin) + return; + + message_save = this.message; + this.message = ""; // no more message + SUB_UseTargets(this, actor, trigger); // fire all targets / killtargets + this.message = message_save; + + this.velocity = '0 0 0'; + + // Make a sound, wait a little... + + if (this.noise1 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.nextthink = this.ltime + 0.1; + + temp = 1 - (this.spawnflags & DOOR_SECRET_1ST_LEFT); // 1 or -1 + makevectors(this.mangle); + + if (!this.t_width) + { + if (this.spawnflags & DOOR_SECRET_1ST_DOWN) + this.t_width = fabs(v_up * this.size); + else + this.t_width = fabs(v_right * this.size); + } + + if (!this.t_length) + this.t_length = fabs(v_forward * this.size); + + if (this.spawnflags & DOOR_SECRET_1ST_DOWN) + this.dest1 = this.origin - v_up * this.t_width; + else + this.dest1 = this.origin + v_right * (this.t_width * temp); + + this.dest2 = this.dest1 + v_forward * this.t_length; + SUB_CalcMove(this, this.dest1, TSPEED_LINEAR, this.speed, fd_secret_move1); + if (this.noise2 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); +} + +void fd_secret_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + fd_secret_use(this, NULL, NULL); +} + +// Wait after first movement... +void fd_secret_move1(entity this) +{ + this.nextthink = this.ltime + 1.0; + setthink(this, fd_secret_move2); + if (this.noise3 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); +} + +// Start moving sideways w/sound... +void fd_secret_move2(entity this) +{ + if (this.noise2 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(this, this.dest2, TSPEED_LINEAR, this.speed, fd_secret_move3); +} + +// Wait here until time to go back... +void fd_secret_move3(entity this) +{ + if (this.noise3 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); + if (!(this.spawnflags & DOOR_SECRET_OPEN_ONCE) && this.wait >= 0) + { + this.nextthink = this.ltime + this.wait; + setthink(this, fd_secret_move4); + } +} + +// Move backward... +void fd_secret_move4(entity this) +{ + if (this.noise2 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(this, this.dest1, TSPEED_LINEAR, this.speed, fd_secret_move5); +} + +// Wait 1 second... +void fd_secret_move5(entity this) +{ + this.nextthink = this.ltime + 1.0; + setthink(this, fd_secret_move6); + if (this.noise3 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); +} + +void fd_secret_move6(entity this) +{ + if (this.noise2 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(this, this.oldorigin, TSPEED_LINEAR, this.speed, fd_secret_done); +} + +void fd_secret_done(entity this) +{ + if (this.spawnflags&DOOR_SECRET_YES_SHOOT) + { + this.health = 10000; + this.takedamage = DAMAGE_YES; + //this.th_pain = fd_secret_use; + } + if (this.noise3 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); +} + +.float door_finished; + +void secret_blocked(entity this, entity blocker) +{ + if (time < this.door_finished) + return; + this.door_finished = time + 0.5; + //T_Damage (other, this, this, this.dmg, this.dmg, this.deathtype, DT_IMPACT, (this.absmin + this.absmax) * 0.5, '0 0 0', Obituary_Generic); +} + +/* +============== +secret_touch + +Prints messages +================ +*/ +void secret_touch(entity this, entity toucher) +{ + if (!toucher.iscreature) + return; + if (this.door_finished > time) + return; + + this.door_finished = time + 2; + + if (this.message) + { + if (IS_CLIENT(toucher)) + centerprint(toucher, this.message); + play2(toucher, this.noise); + } +} + +void secret_reset(entity this) +{ + if (this.spawnflags & DOOR_SECRET_YES_SHOOT) + { + this.health = 10000; + this.takedamage = DAMAGE_YES; + } + setorigin(this, this.oldorigin); + setthink(this, func_null); + this.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 +*/ + +spawnfunc(func_door_secret) +{ + /*if (!this.deathtype) // map makers can override this + this.deathtype = " got in the way";*/ + + if (!this.dmg) + { + this.dmg = 2; + } + + // Magic formula... + this.mangle = this.angles; + this.angles = '0 0 0'; + this.classname = "door"; + if (!InitMovingBrushTrigger(this)) return; + this.effects |= EF_LOWPRECISION; + + // TODO: other soundpacks + if (this.sounds > 0) + { + this.noise1 = "plats/medplat1.wav"; + this.noise2 = "plats/medplat1.wav"; + this.noise3 = "plats/medplat2.wav"; + } + + // sound on touch + if (this.noise == "") + { + this.noise = "misc/talk.wav"; + } + precache_sound(this.noise); + // sound while moving backwards + if (this.noise1 && this.noise1 != "") + { + precache_sound(this.noise1); + } + // sound while moving sideways + if (this.noise2 && this.noise2 != "") + { + precache_sound(this.noise2); + } + // sound when door stops moving + if (this.noise3 && this.noise3 != "") + { + precache_sound(this.noise3); + } + + settouch(this, secret_touch); + setblocked(this, secret_blocked); + this.speed = 50; + this.use = fd_secret_use; + IFTARGETED + { + } + else + this.spawnflags |= DOOR_SECRET_YES_SHOOT; + + if (this.spawnflags & DOOR_SECRET_YES_SHOOT) + { + //this.canteamdamage = true; // TODO + this.health = 10000; + this.takedamage = DAMAGE_YES; + this.event_damage = fd_secret_damage; + } + this.oldorigin = this.origin; + if (!this.wait) this.wait = 5; // seconds before closing + + this.reset = secret_reset; + this.reset(this); +} +#endif diff --git a/qcsrc/common/mapobjects/func/door_secret.qh b/qcsrc/common/mapobjects/func/door_secret.qh new file mode 100644 index 000000000..ee575bc42 --- /dev/null +++ b/qcsrc/common/mapobjects/func/door_secret.qh @@ -0,0 +1,8 @@ +#pragma once + + +const int DOOR_SECRET_OPEN_ONCE = BIT(0); // stays open - LEGACY, set wait to -1 instead +const int DOOR_SECRET_1ST_LEFT = BIT(1); // 1st move is left of arrow +const int DOOR_SECRET_1ST_DOWN = BIT(2); // 1st move is down from arrow +const int DOOR_SECRET_NO_SHOOT = BIT(3); // only opened by trigger +const int DOOR_SECRET_YES_SHOOT = BIT(4); // shootable even if targeted diff --git a/qcsrc/common/mapobjects/func/fourier.qc b/qcsrc/common/mapobjects/func/fourier.qc new file mode 100644 index 000000000..28e0f0f7c --- /dev/null +++ b/qcsrc/common/mapobjects/func/fourier.qc @@ -0,0 +1,89 @@ +#include "fourier.qh" +#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 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(entity this) +{ + vector v; + float n, i, t; + + this.nextthink = time + 0.1; + if(this.owner.active != ACTIVE_ACTIVE) + { + this.owner.velocity = '0 0 0'; + return; + } + + + n = floor((tokenize_console(this.owner.netname)) / 5); + t = this.nextthink * this.owner.cnt + this.owner.phase * 360; + + v = this.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))) * this.owner.height * v_forward_y; + } + + if(this.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed + // * 10 so it will arrive in 0.1 sec + this.owner.velocity = (v - this.owner.origin) * 10; +} + +spawnfunc(func_fourier) +{ + entity controller; + if (this.noise != "") + { + precache_sound(this.noise); + soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + + if (!this.speed) + this.speed = 4; + if (!this.height) + this.height = 32; + this.destvec = this.origin; + this.cnt = 360 / this.speed; + + setblocked(this, generic_plat_blocked); + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + if(this.netname == "") + this.netname = "1 0 0 0 1"; + + if (!InitMovingBrushTrigger(this)) + return; + + this.active = ACTIVE_ACTIVE; + + // wait for targets to spawn + controller = new(func_fourier_controller); + controller.owner = this; + controller.nextthink = time + 1; + setthink(controller, func_fourier_controller_think); + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); // for PushMove + + // Savage: Reduce bandwith, critical on e.g. nexdm02 + this.effects |= EF_LOWPRECISION; + + // TODO make a reset function for this one +} +#endif diff --git a/qcsrc/common/mapobjects/func/fourier.qh b/qcsrc/common/mapobjects/func/fourier.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/func/fourier.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/func/include.qc b/qcsrc/common/mapobjects/func/include.qc new file mode 100644 index 000000000..290c2e984 --- /dev/null +++ b/qcsrc/common/mapobjects/func/include.qc @@ -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/mapobjects/func/include.qh b/qcsrc/common/mapobjects/func/include.qh new file mode 100644 index 000000000..4e8b94cd1 --- /dev/null +++ b/qcsrc/common/mapobjects/func/include.qh @@ -0,0 +1,5 @@ +#pragma once + +#include "door.qh" +#include "ladder.qh" +#include "train.qh" diff --git a/qcsrc/common/mapobjects/func/ladder.qc b/qcsrc/common/mapobjects/func/ladder.qc new file mode 100644 index 000000000..020ecca08 --- /dev/null +++ b/qcsrc/common/mapobjects/func/ladder.qc @@ -0,0 +1,148 @@ +#include "ladder.qh" +REGISTER_NET_LINKED(ENT_CLIENT_LADDER) + +void func_ladder_touch(entity this, entity toucher) +{ +#ifdef SVQC + if (!toucher.iscreature) + return; + if(IS_VEHICLE(toucher)) + return; +#elif defined(CSQC) + if(!toucher.isplayermodel) + return; +#endif + + EXACTTRIGGER_TOUCH(this, toucher); + + toucher.ladder_time = time + 0.1; + toucher.ladder_entity = this; +} + +#ifdef SVQC +bool func_ladder_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_LADDER); + + WriteString(MSG_ENTITY, this.classname); + WriteByte(MSG_ENTITY, this.skin); + WriteCoord(MSG_ENTITY, this.speed); + + trigger_common_write(this, false); + + return true; +} + +void func_ladder_link(entity this) +{ + trigger_link(this, func_ladder_send); + //this.model = "null"; +} + +void func_ladder_init(entity this) +{ + settouch(this, func_ladder_touch); + trigger_init(this); + func_ladder_link(this); + + if(min(this.absmax.x - this.absmin.x, this.absmax.y - this.absmin.y) > 100) + return; + + entity tracetest_ent = spawn(); + setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST); + tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + + vector top_min = (this.absmin + this.absmax) / 2; + top_min.z = this.absmax.z; + vector top_max = top_min; + top_max.z += PL_MAX_CONST.z - PL_MIN_CONST.z; + tracebox(top_max + jumpstepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + if(trace_startsolid) + { + tracebox(top_max + stepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + if(trace_startsolid) + { + tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + if(trace_startsolid) + { + if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x + && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x) + { + // move top on one side + top_max.y = top_min.y = this.absmin.y + (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75; + } + else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y + && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y) + { + // move top on one side + top_max.x = top_min.x = this.absmin.x + (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75; + } + tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + if(trace_startsolid) + { + if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x + && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x) + { + // alternatively on the other side + top_max.y = top_min.y = this.absmax.y - (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75; + } + else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y + && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y) + { + // alternatively on the other side + top_max.x = top_min.x = this.absmax.x - (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75; + } + tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + } + } + } + } + if(trace_startsolid || trace_endpos.z < this.absmax.z) + { + delete(tracetest_ent); + return; + } + + this.bot_pickup = true; // allow bots to make use of this ladder + float cost = waypoint_getlinearcost(trace_endpos.z - this.absmin.z); + top_min = trace_endpos; + waypoint_spawnforteleporter_boxes(this, WAYPOINTFLAG_LADDER, this.absmin, this.absmax, top_min, top_min, cost); +} + +spawnfunc(func_ladder) +{ + IL_PUSH(g_ladders, this); // TODO: also func_water? bots currently loop through func_ladder only + + func_ladder_init(this); +} + +spawnfunc(func_water) +{ + func_ladder_init(this); +} + +#elif defined(CSQC) +.float speed; + +void func_ladder_remove(entity this) +{ + strfree(this.classname); +} + +NET_HANDLE(ENT_CLIENT_LADDER, bool isnew) +{ + this.classname = strzone(ReadString()); + this.skin = ReadByte(); + this.speed = ReadCoord(); + + trigger_common_read(this, false); + + this.solid = SOLID_TRIGGER; + settouch(this, func_ladder_touch); + this.drawmask = MASK_NORMAL; + this.move_time = time; + this.entremove = func_ladder_remove; + + return true; +} +#endif diff --git a/qcsrc/common/mapobjects/func/ladder.qh b/qcsrc/common/mapobjects/func/ladder.qh new file mode 100644 index 000000000..26cbbda03 --- /dev/null +++ b/qcsrc/common/mapobjects/func/ladder.qh @@ -0,0 +1,4 @@ +#pragma once + +.float ladder_time; +.entity ladder_entity; diff --git a/qcsrc/common/mapobjects/func/pendulum.qc b/qcsrc/common/mapobjects/func/pendulum.qc new file mode 100644 index 000000000..a59f7a93b --- /dev/null +++ b/qcsrc/common/mapobjects/func/pendulum.qc @@ -0,0 +1,77 @@ +#include "pendulum.qh" +#ifdef SVQC +.float freq; +void func_pendulum_controller_think(entity this) +{ + float v; + this.nextthink = time + 0.1; + + if (!(this.owner.active == ACTIVE_ACTIVE)) + { + this.owner.avelocity_x = 0; + return; + } + + // calculate sinewave using makevectors + makevectors((this.nextthink * this.owner.freq + this.owner.phase) * '0 360 0'); + v = this.owner.speed * v_forward_y + this.cnt; + if(this.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed + { + // * 10 so it will arrive in 0.1 sec + this.owner.avelocity_z = (remainder(v - this.owner.angles_z, 360)) * 10; + } +} + +spawnfunc(func_pendulum) +{ + entity controller; + if (this.noise != "") + { + precache_sound(this.noise); + soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + + this.active = ACTIVE_ACTIVE; + + // keys: angle, speed, phase, noise, freq + + if(!this.speed) + this.speed = 30; + // not initializing this.dmg to 2, to allow damageless pendulum + + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + setblocked(this, generic_plat_blocked); + + this.avelocity_z = 0.0000001; + if (!InitMovingBrushTrigger(this)) + return; + + if(!this.freq) + { + // find pendulum length (same formula as Q3A) + this.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(this.mins_z)))); + } + + // copy initial angle + this.cnt = this.angles_z; + + // wait for targets to spawn + controller = new(func_pendulum_controller); + controller.owner = this; + controller.nextthink = time + 1; + setthink(controller, func_pendulum_controller_think); + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); // for PushMove + + //this.effects |= EF_LOWPRECISION; + + // TODO make a reset function for this one +} +#endif diff --git a/qcsrc/common/mapobjects/func/pendulum.qh b/qcsrc/common/mapobjects/func/pendulum.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/func/pendulum.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/func/plat.qc b/qcsrc/common/mapobjects/func/plat.qc new file mode 100644 index 000000000..b05233621 --- /dev/null +++ b/qcsrc/common/mapobjects/func/plat.qc @@ -0,0 +1,190 @@ +#include "plat.qh" +REGISTER_NET_LINKED(ENT_CLIENT_PLAT) + +#ifdef SVQC +void plat_link(entity this); + +void plat_delayedinit(entity this) +{ + plat_link(this); + plat_spawn_inside_trigger(this); // the "start moving" trigger +} + +float plat_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_PLAT); + WriteByte(MSG_ENTITY, sf); + + if(sf & SF_TRIGGER_INIT) + { + WriteByte(MSG_ENTITY, this.platmovetype_start); + WriteByte(MSG_ENTITY, this.platmovetype_turn); + WriteByte(MSG_ENTITY, this.platmovetype_end); + WriteByte(MSG_ENTITY, this.spawnflags); + + WriteString(MSG_ENTITY, this.model); + + trigger_common_write(this, true); + + WriteVector(MSG_ENTITY, this.pos1); + WriteVector(MSG_ENTITY, this.pos2); + + WriteVector(MSG_ENTITY, this.size); + + WriteAngle(MSG_ENTITY, this.mangle_x); + WriteAngle(MSG_ENTITY, this.mangle_y); + WriteAngle(MSG_ENTITY, this.mangle_z); + + WriteShort(MSG_ENTITY, this.speed); + WriteShort(MSG_ENTITY, this.height); + WriteByte(MSG_ENTITY, this.lip); + WriteByte(MSG_ENTITY, this.state); + + WriteShort(MSG_ENTITY, this.dmg); + } + + if(sf & SF_TRIGGER_RESET) + { + // used on client + } + + return true; +} + +void plat_link(entity this) +{ + //Net_LinkEntity(this, 0, false, plat_send); +} + +spawnfunc(func_plat) +{ + if (this.spawnflags & CRUSH) + { + this.dmg = 10000; + } + + if (this.dmg && (this.message == "")) + { + this.message = "was squished"; + } + if (this.dmg && (this.message2 == "")) + { + this.message2 = "was squished by"; + } + + if (this.sounds == 1) + { + this.noise = "plats/plat1.wav"; + this.noise1 = "plats/plat2.wav"; + } + + if (this.sounds == 2) + { + this.noise = "plats/medplat1.wav"; + this.noise1 = "plats/medplat2.wav"; + } + + // WARNING: backwards compatibility because people don't use already existing fields :( + if (this.sound1) + this.noise = this.sound1; + if (this.sound2) + this.noise1 = this.sound2; + + if(this.noise && this.noise != "") + { + precache_sound(this.noise); + } + if(this.noise1 && this.noise1 != "") + { + precache_sound(this.noise1); + } + + this.mangle = this.angles; + this.angles = '0 0 0'; + + this.classname = "plat"; + if (!InitMovingBrushTrigger(this)) + return; + this.effects |= EF_LOWPRECISION; + setsize (this, this.mins , this.maxs); + + setblocked(this, plat_crush); + + if (!this.speed) this.speed = 150; + if (!this.lip) this.lip = 16; + if (!this.height) this.height = this.size.z - this.lip; + + this.pos1 = this.origin; + this.pos2 = this.origin; + this.pos2_z = this.origin.z - this.height; + + this.reset = plat_reset; + this.reset(this); + + InitializeEntity(this, plat_delayedinit, INITPRIO_FINDTARGET); +} +#elif defined(CSQC) +void plat_draw(entity this) +{ + Movetype_Physics_NoMatchServer(this); + //Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); +} + +NET_HANDLE(ENT_CLIENT_PLAT, bool isnew) +{ + float sf = ReadByte(); + + if(sf & SF_TRIGGER_INIT) + { + this.platmovetype_start = ReadByte(); + this.platmovetype_turn = ReadByte(); + this.platmovetype_end = ReadByte(); + this.spawnflags = ReadByte(); + + this.model = strzone(ReadString()); + _setmodel(this, this.model); + + trigger_common_read(this, true); + + this.pos1 = ReadVector(); + this.pos2 = ReadVector(); + + this.size = ReadVector(); + + this.mangle_x = ReadAngle(); + this.mangle_y = ReadAngle(); + this.mangle_z = ReadAngle(); + + this.speed = ReadShort(); + this.height = ReadShort(); + this.lip = ReadByte(); + this.state = ReadByte(); + + this.dmg = ReadShort(); + + this.classname = "plat"; + this.solid = SOLID_BSP; + set_movetype(this, MOVETYPE_PUSH); + this.drawmask = MASK_NORMAL; + this.draw = plat_draw; + if (isnew) IL_PUSH(g_drawables, this); + this.use = plat_use; + this.entremove = trigger_remove_generic; + + plat_reset(this); // also called here + + set_movetype(this, MOVETYPE_PUSH); + this.move_time = time; + + plat_spawn_inside_trigger(this); + } + + if(sf & SF_TRIGGER_RESET) + { + plat_reset(this); + + this.move_time = time; + } + return true; +} +#endif diff --git a/qcsrc/common/mapobjects/func/plat.qh b/qcsrc/common/mapobjects/func/plat.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/func/plat.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/func/pointparticles.qc b/qcsrc/common/mapobjects/func/pointparticles.qc new file mode 100644 index 000000000..7de5a03ef --- /dev/null +++ b/qcsrc/common/mapobjects/func/pointparticles.qc @@ -0,0 +1,341 @@ +#include "pointparticles.qh" +REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES) + +#ifdef SVQC +// NOTE: also contains func_sparks + +bool pointparticles_SendEntity(entity this, entity to, float sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES); + + // optional features to save space + sendflags = sendflags & 0x0F; + if(this.spawnflags & PARTICLES_IMPULSE) + sendflags |= SF_POINTPARTICLES_IMPULSE; // absolute count on toggle-on + if(this.movedir != '0 0 0' || this.velocity != '0 0 0') + sendflags |= SF_POINTPARTICLES_MOVING; // 4 bytes - saves CPU + if(this.waterlevel || this.count != 1) + sendflags |= SF_POINTPARTICLES_JITTER_AND_COUNT; // 4 bytes - obscure features almost never used + if(this.mins != '0 0 0' || this.maxs != '0 0 0') + sendflags |= SF_POINTPARTICLES_BOUNDS; // 14 bytes - saves lots of space + + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & SF_TRIGGER_UPDATE) + { + if(this.active == ACTIVE_ACTIVE) + WriteCoord(MSG_ENTITY, this.impulse); + else + WriteCoord(MSG_ENTITY, 0); // off + } + if(sendflags & SF_TRIGGER_RESET) + { + WriteVector(MSG_ENTITY, this.origin); + } + if(sendflags & SF_TRIGGER_INIT) + { + if(this.model != "null") + { + WriteShort(MSG_ENTITY, this.modelindex); + if(sendflags & SF_POINTPARTICLES_BOUNDS) + { + WriteVector(MSG_ENTITY, this.mins); + WriteVector(MSG_ENTITY, this.maxs); + } + } + else + { + WriteShort(MSG_ENTITY, 0); + if(sendflags & SF_POINTPARTICLES_BOUNDS) + { + WriteVector(MSG_ENTITY, this.maxs); + } + } + WriteShort(MSG_ENTITY, this.cnt); + WriteString(MSG_ENTITY, this.mdl); + if(sendflags & SF_POINTPARTICLES_MOVING) + { + WriteShort(MSG_ENTITY, compressShortVector(this.velocity)); + WriteShort(MSG_ENTITY, compressShortVector(this.movedir)); + } + if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT) + { + WriteShort(MSG_ENTITY, this.waterlevel * 16.0); + WriteByte(MSG_ENTITY, this.count * 16.0); + } + WriteString(MSG_ENTITY, this.noise); + if(this.noise != "") + { + WriteByte(MSG_ENTITY, floor(this.atten * 64)); + WriteByte(MSG_ENTITY, floor(this.volume * 255)); + } + WriteString(MSG_ENTITY, this.bgmscript); + if(this.bgmscript != "") + { + WriteByte(MSG_ENTITY, floor(this.bgmscriptattack * 64)); + WriteByte(MSG_ENTITY, floor(this.bgmscriptdecay * 64)); + WriteByte(MSG_ENTITY, floor(this.bgmscriptsustain * 255)); + WriteByte(MSG_ENTITY, floor(this.bgmscriptrelease * 64)); + } + } + return 1; +} + +void pointparticles_think(entity this) +{ + if(this.origin != this.oldorigin) + { + this.SendFlags |= SF_TRIGGER_RESET; + this.oldorigin = this.origin; + } + this.nextthink = time; +} + +spawnfunc(func_pointparticles) +{ + if(this.model != "") { precache_model(this.model); _setmodel(this, this.model); } + if(this.noise != "") precache_sound(this.noise); + if(this.mdl != "") this.cnt = 0; // use a good handler + + if(!this.bgmscriptsustain) this.bgmscriptsustain = 1; + else if(this.bgmscriptsustain < 0) this.bgmscriptsustain = 0; + + if(!this.atten) this.atten = ATTEN_NORM; + else if(this.atten < 0) this.atten = 0; + if(!this.volume) this.volume = 1; + if(!this.count) this.count = 1; + if(!this.impulse) this.impulse = 1; + + if(!this.modelindex) + { + setorigin(this, this.origin + this.mins); + setsize(this, '0 0 0', this.maxs - this.mins); + } + //if(!this.cnt) this.cnt = _particleeffectnum(this.mdl); + this.setactive = generic_netlinked_setactive; + + Net_LinkEntity(this, (this.spawnflags & PARTICLES_VISCULLING), 0, pointparticles_SendEntity); + + IFTARGETED + { + // backwards compatibility + this.use = generic_netlinked_legacy_use; + } + this.reset = generic_netlinked_reset; + this.reset(this); + setthink(this, pointparticles_think); + this.nextthink = time; +} + +spawnfunc(func_sparks) +{ + if(this.count < 1) { + this.count = 25.0; // nice default value + } + + if(this.impulse < 0.5) { + this.impulse = 2.5; // nice default value + } + + this.mins = '0 0 0'; + this.maxs = '0 0 0'; + this.velocity = '0 0 -1'; + this.mdl = "TE_SPARK"; + this.cnt = 0; // use mdl + + spawnfunc_func_pointparticles(this); +} +#elif defined(CSQC) + +.int dphitcontentsmask; + +entityclass(PointParticles); +classfield(PointParticles) .int cnt; // effect number +classfield(PointParticles) .vector velocity; // particle velocity +classfield(PointParticles) .float waterlevel; // direction jitter +classfield(PointParticles) .int count; // count multiplier +classfield(PointParticles) .int impulse; // density +classfield(PointParticles) .string noise; // sound +classfield(PointParticles) .float atten; +classfield(PointParticles) .float volume; +classfield(PointParticles) .float absolute; // 1 = count per second is absolute, ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = only spawn at toggle +classfield(PointParticles) .vector movedir; // trace direction +classfield(PointParticles) .float glow_color; // palette index + +const int ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = 2; + +void Draw_PointParticles(entity this) +{ + float n, i, fail; + vector p; + vector sz; + vector o; + o = this.origin; + sz = this.maxs - this.mins; + n = doBGMScript(this); + if(this.absolute == ABSOLUTE_ONLY_SPAWN_AT_TOGGLE) + { + if(n >= 0) + n = this.just_toggled ? this.impulse : 0; + else + n = this.impulse * drawframetime; + } + else + { + n *= this.impulse * drawframetime; + if(this.just_toggled) + if(n < 1) + n = 1; + } + if(n == 0) + return; + fail = 0; + for(i = random(); i <= n && fail <= 64*n; ++i) + { + p = o + this.mins; + p.x += random() * sz.x; + p.y += random() * sz.y; + p.z += random() * sz.z; + if(WarpZoneLib_BoxTouchesBrush(p, p, this, NULL)) + { + if(this.movedir != '0 0 0') + { + traceline(p, p + normalize(this.movedir) * 4096, 0, NULL); + p = trace_endpos; + int eff_num; + if(this.cnt) + eff_num = this.cnt; + else + eff_num = _particleeffectnum(this.mdl); + __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count); + } + else + { + int eff_num; + if(this.cnt) + eff_num = this.cnt; + else + eff_num = _particleeffectnum(this.mdl); + __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count); + } + if(this.noise != "") + { + setorigin(this, p); + _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten); + } + this.just_toggled = 0; + } + else if(this.absolute) + { + ++fail; + --i; + } + } + setorigin(this, o); +} + +void Ent_PointParticles_Remove(entity this) +{ + strfree(this.noise); + strfree(this.bgmscript); + strfree(this.mdl); +} + +NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew) +{ + float i; + vector v; + int sendflags = ReadByte(); + if(sendflags & SF_TRIGGER_UPDATE) + { + i = ReadCoord(); // density (<0: point, >0: volume) + if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed + this.just_toggled = 1; + this.impulse = i; + } + if(sendflags & SF_TRIGGER_RESET) + { + this.origin = ReadVector(); + } + if(sendflags & SF_TRIGGER_INIT) + { + this.modelindex = ReadShort(); + if(sendflags & SF_POINTPARTICLES_BOUNDS) + { + if(this.modelindex) + { + this.mins = ReadVector(); + this.maxs = ReadVector(); + } + else + { + this.mins = '0 0 0'; + this.maxs = ReadVector(); + } + } + else + { + this.mins = this.maxs = '0 0 0'; + } + + this.cnt = ReadShort(); // effect number + this.mdl = strzone(ReadString()); // effect string + + if(sendflags & SF_POINTPARTICLES_MOVING) + { + this.velocity = decompressShortVector(ReadShort()); + this.movedir = decompressShortVector(ReadShort()); + } + else + { + this.velocity = this.movedir = '0 0 0'; + } + if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT) + { + this.waterlevel = ReadShort() / 16.0; + this.count = ReadByte() / 16.0; + } + else + { + this.waterlevel = 0; + this.count = 1; + } + strcpy(this.noise, ReadString()); + if(this.noise != "") + { + this.atten = ReadByte() / 64.0; + this.volume = ReadByte() / 255.0; + } + strcpy(this.bgmscript, ReadString()); + if(this.bgmscript != "") + { + this.bgmscriptattack = ReadByte() / 64.0; + this.bgmscriptdecay = ReadByte() / 64.0; + this.bgmscriptsustain = ReadByte() / 255.0; + this.bgmscriptrelease = ReadByte() / 64.0; + } + BGMScript_InitEntity(this); + } + + return = true; + + if(sendflags & SF_TRIGGER_UPDATE) + { + this.absolute = (this.impulse >= 0); + if(!this.absolute) + { + v = this.maxs - this.mins; + this.impulse *= -v.x * v.y * v.z / (64**3); // relative: particles per 64^3 cube + } + } + + if(sendflags & SF_POINTPARTICLES_IMPULSE) + this.absolute = ABSOLUTE_ONLY_SPAWN_AT_TOGGLE; + + setorigin(this, this.origin); + setsize(this, this.mins, this.maxs); + this.solid = SOLID_NOT; + this.draw = Draw_PointParticles; + if (isnew) IL_PUSH(g_drawables, this); + this.entremove = Ent_PointParticles_Remove; +} +#endif diff --git a/qcsrc/common/mapobjects/func/pointparticles.qh b/qcsrc/common/mapobjects/func/pointparticles.qh new file mode 100644 index 000000000..b167527bc --- /dev/null +++ b/qcsrc/common/mapobjects/func/pointparticles.qh @@ -0,0 +1,11 @@ +#pragma once + +// spawnflags +const int PARTICLES_IMPULSE = BIT(1); +const int PARTICLES_VISCULLING = BIT(2); + +// sendflags +const int SF_POINTPARTICLES_IMPULSE = BIT(4); +const int SF_POINTPARTICLES_MOVING = BIT(5); // Send velocity and movedir +const int SF_POINTPARTICLES_JITTER_AND_COUNT = BIT(6); // Send waterlevel (=jitter) and count +const int SF_POINTPARTICLES_BOUNDS = BIT(7); // Send min and max of the brush diff --git a/qcsrc/common/mapobjects/func/rainsnow.qc b/qcsrc/common/mapobjects/func/rainsnow.qc new file mode 100644 index 000000000..c765a4293 --- /dev/null +++ b/qcsrc/common/mapobjects/func/rainsnow.qc @@ -0,0 +1,142 @@ +#include "rainsnow.qh" +REGISTER_NET_LINKED(ENT_CLIENT_RAINSNOW) + +#ifdef SVQC +bool rainsnow_SendEntity(entity this, entity to, float sf) +{ + vector myorg = this.origin + this.mins; + vector mysize = this.maxs - this.mins; + WriteHeader(MSG_ENTITY, ENT_CLIENT_RAINSNOW); + WriteByte(MSG_ENTITY, this.state); + WriteVector(MSG_ENTITY, myorg); + WriteVector(MSG_ENTITY, mysize); + WriteShort(MSG_ENTITY, compressShortVector(this.dest)); + WriteShort(MSG_ENTITY, this.count); + WriteByte(MSG_ENTITY, this.cnt); + return true; +} + +/*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 +*/ +spawnfunc(func_rain) +{ + this.dest = this.velocity; + this.velocity = '0 0 0'; + if (!this.dest) + this.dest = '0 0 -700'; + this.angles = '0 0 0'; + set_movetype(this, MOVETYPE_NONE); + this.solid = SOLID_NOT; + SetBrushEntityModel(this); + if (!this.cnt) + { + this.cnt = 12; + } + if (!this.count) + this.count = 2000; + // relative to absolute particle count + this.count = 0.1 * this.count * (this.size_x / 1024) * (this.size_y / 1024); + if (this.count < 1) + this.count = 1; + if(this.count > 65535) + this.count = 65535; + + this.state = RAINSNOW_RAIN; + + Net_LinkEntity(this, 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 +*/ +spawnfunc(func_snow) +{ + this.dest = this.velocity; + this.velocity = '0 0 0'; + if (!this.dest) + this.dest = '0 0 -300'; + this.angles = '0 0 0'; + set_movetype(this, MOVETYPE_NONE); + this.solid = SOLID_NOT; + SetBrushEntityModel(this); + if (!this.cnt) + { + this.cnt = 12; + } + if (!this.count) + this.count = 2000; + // relative to absolute particle count + this.count = 0.1 * this.count * (this.size_x / 1024) * (this.size_y / 1024); + if (this.count < 1) + this.count = 1; + if(this.count > 65535) + this.count = 65535; + + this.state = RAINSNOW_SNOW; + + Net_LinkEntity(this, false, 0, rainsnow_SendEntity); +} +#elif defined(CSQC) +float autocvar_cl_rainsnow_maxdrawdist = 2048; + +void Draw_Rain(entity this) +{ + vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist; + maxdist.z = 5; + if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5')) + //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist)) + te_particlerain(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); +} + +void Draw_Snow(entity this) +{ + vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist; + maxdist.z = 5; + if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5')) + //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist)) + te_particlesnow(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); +} + +NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew) +{ + this.state = ReadByte(); // Rain, Snow, or Whatever + this.origin = ReadVector(); + this.maxs = ReadVector(); + this.velocity = decompressShortVector(ReadShort()); + this.count = ReadShort(); + this.glow_color = ReadByte(); // color + + return = true; + + this.mins = -0.5 * this.maxs; + this.maxs = 0.5 * this.maxs; + this.origin = this.origin - this.mins; + + setorigin(this, this.origin); + setsize(this, this.mins, this.maxs); + this.solid = SOLID_NOT; + if (isnew) IL_PUSH(g_drawables, this); + if(this.state == RAINSNOW_RAIN) + this.draw = Draw_Rain; + else + this.draw = Draw_Snow; +} +#endif diff --git a/qcsrc/common/mapobjects/func/rainsnow.qh b/qcsrc/common/mapobjects/func/rainsnow.qh new file mode 100644 index 000000000..d60eb4f48 --- /dev/null +++ b/qcsrc/common/mapobjects/func/rainsnow.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int RAINSNOW_SNOW = 0; +const int RAINSNOW_RAIN = 1; diff --git a/qcsrc/common/mapobjects/func/rotating.qc b/qcsrc/common/mapobjects/func/rotating.qc new file mode 100644 index 000000000..35351ee08 --- /dev/null +++ b/qcsrc/common/mapobjects/func/rotating.qc @@ -0,0 +1,110 @@ +#include "rotating.qh" +#ifdef SVQC + +void func_rotating_setactive(entity this, int astate) +{ + if (astate == ACTIVE_TOGGLE) + { + if(this.active == ACTIVE_ACTIVE) + this.active = ACTIVE_NOT; + else + this.active = ACTIVE_ACTIVE; + } + else + this.active = astate; + + if(this.active == ACTIVE_NOT) + { + this.avelocity = '0 0 0'; + stopsound(this, CH_AMBIENT_SINGLE); + } + else + { + this.avelocity = this.pos1; + if(this.noise && this.noise != "") + { + _sound(this, CH_AMBIENT_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + } +} + +void func_rotating_reset(entity this) +{ + // TODO: reset angles as well? + + if(this.spawnflags & FUNC_ROTATING_STARTOFF) + { + this.setactive(this, ACTIVE_NOT); + } + else + { + this.setactive(this, ACTIVE_ACTIVE); + } +} + +void func_rotating_init_for_player(entity this, entity player) +{ + if (this.noise && this.noise != "" && this.active == ACTIVE_ACTIVE && IS_REAL_CLIENT(player)) + { + msg_entity = player; + soundto (MSG_ONE, this, CH_AMBIENT_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } +} + +/*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. +*/ + +spawnfunc(func_rotating) +{ + if (this.noise && this.noise != "") + { + precache_sound(this.noise); + } + + this.setactive = func_rotating_setactive; + + if (!this.speed) + this.speed = 100; + if (this.spawnflags & FUNC_ROTATING_XAXIS) + this.avelocity = '0 0 1' * this.speed; + else if (this.spawnflags & FUNC_ROTATING_YAXIS) + this.avelocity = '1 0 0' * this.speed; + else // Z + this.avelocity = '0 1 0' * this.speed; + + this.pos1 = this.avelocity; + + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + + + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + + this.dmgtime2 = time; + + if (!InitMovingBrushTrigger(this)) + return; + // no EF_LOWPRECISION here, as rounding angles is bad + + setblocked(this, generic_plat_blocked); + + // wait for targets to spawn + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); // for PushMove + + this.reset = func_rotating_reset; + this.reset(this); + + // maybe send sound to new players + IL_PUSH(g_initforplayer, this); + this.init_for_player = func_rotating_init_for_player; +} +#endif diff --git a/qcsrc/common/mapobjects/func/rotating.qh b/qcsrc/common/mapobjects/func/rotating.qh new file mode 100644 index 000000000..ad1b6ec92 --- /dev/null +++ b/qcsrc/common/mapobjects/func/rotating.qh @@ -0,0 +1,6 @@ +#pragma once + + +const int FUNC_ROTATING_XAXIS = BIT(2); +const int FUNC_ROTATING_YAXIS = BIT(3); +const int FUNC_ROTATING_STARTOFF = BIT(4); diff --git a/qcsrc/common/mapobjects/func/stardust.qc b/qcsrc/common/mapobjects/func/stardust.qc new file mode 100644 index 000000000..9c2fba8ad --- /dev/null +++ b/qcsrc/common/mapobjects/func/stardust.qc @@ -0,0 +1,9 @@ +#include "stardust.qh" +#ifdef SVQC +spawnfunc(func_stardust) +{ + this.effects = EF_STARDUST; + + CSQCMODEL_AUTOINIT(this); +} +#endif diff --git a/qcsrc/common/mapobjects/func/stardust.qh b/qcsrc/common/mapobjects/func/stardust.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/func/stardust.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/func/train.qc b/qcsrc/common/mapobjects/func/train.qc new file mode 100644 index 000000000..4e9c33456 --- /dev/null +++ b/qcsrc/common/mapobjects/func/train.qc @@ -0,0 +1,351 @@ +#include "train.qh" +.float train_wait_turning; +.entity future_target; +void train_next(entity this); +#ifdef SVQC +void train_use(entity this, entity actor, entity trigger); +#endif +void train_wait(entity this) +{ + SUB_UseTargets(this.enemy, NULL, NULL); + this.enemy = NULL; + + // if turning is enabled, the train will turn toward the next point while waiting + if(this.platmovetype_turn && !this.train_wait_turning) + { + entity targ, cp; + vector ang; + targ = this.future_target; + if((this.spawnflags & TRAIN_CURVE) && targ.curvetarget) + cp = find(NULL, targetname, targ.curvetarget); + else + cp = NULL; + + if(cp) // bezier curves movement + ang = cp.origin - (this.origin - this.view_ofs); // use the origin of the control point of the next path_corner + else // linear movement + ang = targ.origin - (this.origin - this.view_ofs); // use the origin of the next path_corner + ang = vectoangles(ang); + ang_x = -ang_x; // flip up / down orientation + + if(this.wait > 0) // slow turning + SUB_CalcAngleMove(this, ang, TSPEED_TIME, this.ltime - time + this.wait, train_wait); + else // instant turning + SUB_CalcAngleMove(this, ang, TSPEED_TIME, 0.0000001, train_wait); + this.train_wait_turning = true; + return; + } + +#ifdef SVQC + if(this.noise != "") + stopsoundto(MSG_BROADCAST, this, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway +#endif + +#ifdef SVQC + entity tg = this.future_target; + if(tg.spawnflags & TRAIN_NEEDACTIVATION) + { + this.use = train_use; + setthink(this, func_null); + this.nextthink = 0; + } + else +#endif + if(this.wait < 0 || this.train_wait_turning) // no waiting or we already waited while turning + { + this.train_wait_turning = false; + train_next(this); + } + else + { + setthink(this, train_next); + this.nextthink = this.ltime + this.wait; + } +} + +entity train_next_find(entity this) +{ + if(this.target_random) + { + RandomSelection_Init(); + for(entity t = NULL; (t = find(t, targetname, this.target));) + { + RandomSelection_AddEnt(t, 1, 0); + } + return RandomSelection_chosen_ent; + } + else + { + return find(NULL, targetname, this.target); + } +} + +void train_next(entity this) +{ + entity targ = NULL, cp = NULL; + vector cp_org = '0 0 0'; + + targ = this.future_target; + + this.target = targ.target; + this.target_random = targ.target_random; + this.future_target = train_next_find(targ); + + if (this.spawnflags & TRAIN_CURVE) + { + if(targ.curvetarget) + { + cp = find(NULL, targetname, targ.curvetarget); // get its second target (the control point) + cp_org = cp.origin - this.view_ofs; // no control point found, assume a straight line to the destination + } + } + if (this.target == "") + objerror(this, "train_next: no next target"); + this.wait = targ.wait; + if (!this.wait) + this.wait = 0.1; + + if(targ.platmovetype) + { + // this path_corner contains a movetype overrider, apply it + this.platmovetype_start = targ.platmovetype_start; + this.platmovetype_end = targ.platmovetype_end; + } + else + { + // this path_corner doesn't contain a movetype overrider, use the train's defaults + this.platmovetype_start = this.platmovetype_start_default; + this.platmovetype_end = this.platmovetype_end_default; + } + + if (targ.speed) + { + if (cp) + SUB_CalcMove_Bezier(this, cp_org, targ.origin - this.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); + else + SUB_CalcMove(this, targ.origin - this.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); + } + else + { + if (cp) + SUB_CalcMove_Bezier(this, cp_org, targ.origin - this.view_ofs, TSPEED_LINEAR, this.speed, train_wait); + else + SUB_CalcMove(this, targ.origin - this.view_ofs, TSPEED_LINEAR, this.speed, train_wait); + } + + if(this.noise != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); +} + +REGISTER_NET_LINKED(ENT_CLIENT_TRAIN) + +#ifdef SVQC +float train_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRAIN); + WriteByte(MSG_ENTITY, sf); + + if(sf & SF_TRIGGER_INIT) + { + WriteString(MSG_ENTITY, this.platmovetype); + WriteByte(MSG_ENTITY, this.platmovetype_turn); + WriteByte(MSG_ENTITY, this.spawnflags); + + WriteString(MSG_ENTITY, this.model); + + trigger_common_write(this, true); + + WriteString(MSG_ENTITY, this.curvetarget); + + WriteVector(MSG_ENTITY, this.pos1); + WriteVector(MSG_ENTITY, this.pos2); + + WriteVector(MSG_ENTITY, this.size); + + WriteVector(MSG_ENTITY, this.view_ofs); + + WriteAngle(MSG_ENTITY, this.mangle_x); + WriteAngle(MSG_ENTITY, this.mangle_y); + WriteAngle(MSG_ENTITY, this.mangle_z); + + WriteShort(MSG_ENTITY, this.speed); + WriteShort(MSG_ENTITY, this.height); + WriteByte(MSG_ENTITY, this.lip); + WriteByte(MSG_ENTITY, this.state); + WriteByte(MSG_ENTITY, this.wait); + + WriteShort(MSG_ENTITY, this.dmg); + WriteByte(MSG_ENTITY, this.dmgtime); + } + + if(sf & SF_TRIGGER_RESET) + { + // used on client + } + + return true; +} + +void train_link(entity this) +{ + //Net_LinkEntity(this, 0, false, train_send); +} + +void train_use(entity this, entity actor, entity trigger) +{ + this.nextthink = this.ltime + 1; + setthink(this, train_next); + this.use = func_null; // not again, next target can set it again if needed + if(trigger.target2 && trigger.target2 != "") + this.future_target = find(NULL, targetname, trigger.target2); +} + +void func_train_find(entity this) +{ + entity targ = train_next_find(this); + this.target = targ.target; + this.target_random = targ.target_random; + // save the future target for later + this.future_target = train_next_find(targ); + if (this.target == "") + objerror(this, "func_train_find: no next target"); + setorigin(this, targ.origin - this.view_ofs); + + if(!(this.spawnflags & TRAIN_NEEDACTIVATION)) + { + this.nextthink = this.ltime + 1; + setthink(this, train_next); + } + + train_link(this); +} + +#endif + +/*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) +*/ +#ifdef SVQC +spawnfunc(func_train) +{ + if (this.noise != "") + precache_sound(this.noise); + + if (this.target == "") + objerror(this, "func_train without a target"); + if (!this.speed) + this.speed = 100; + + if (!InitMovingBrushTrigger(this)) + return; + this.effects |= EF_LOWPRECISION; + + if(this.spawnflags & TRAIN_NEEDACTIVATION) + this.use = train_use; + + if (this.spawnflags & TRAIN_TURN) + { + this.platmovetype_turn = true; + this.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now + } + else + this.view_ofs = this.mins; + + // wait for targets to spawn + InitializeEntity(this, func_train_find, INITPRIO_FINDTARGET); + + setblocked(this, generic_plat_blocked); + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + if(!set_platmovetype(this, this.platmovetype)) + return; + this.platmovetype_start_default = this.platmovetype_start; + this.platmovetype_end_default = this.platmovetype_end; + + // TODO make a reset function for this one +} +#elif defined(CSQC) +void train_draw(entity this) +{ + //Movetype_Physics_NoMatchServer(); + Movetype_Physics_MatchServer(this, autocvar_cl_projectiles_sloppy); +} + +NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew) +{ + float sf = ReadByte(); + + if(sf & SF_TRIGGER_INIT) + { + this.platmovetype = strzone(ReadString()); + this.platmovetype_turn = ReadByte(); + this.spawnflags = ReadByte(); + + this.model = strzone(ReadString()); + _setmodel(this, this.model); + + trigger_common_read(this, true); + + this.curvetarget = strzone(ReadString()); + + this.pos1 = ReadVector(); + this.pos2 = ReadVector(); + + this.size = ReadVector(); + + this.view_ofs = ReadVector(); + + this.mangle_x = ReadAngle(); + this.mangle_y = ReadAngle(); + this.mangle_z = ReadAngle(); + + this.speed = ReadShort(); + this.height = ReadShort(); + this.lip = ReadByte(); + this.state = ReadByte(); + this.wait = ReadByte(); + + this.dmg = ReadShort(); + this.dmgtime = ReadByte(); + + this.classname = "func_train"; + this.solid = SOLID_BSP; + set_movetype(this, MOVETYPE_PUSH); + this.drawmask = MASK_NORMAL; + this.draw = train_draw; + if (isnew) IL_PUSH(g_drawables, this); + this.entremove = trigger_remove_generic; + + if(set_platmovetype(this, this.platmovetype)) + { + this.platmovetype_start_default = this.platmovetype_start; + this.platmovetype_end_default = this.platmovetype_end; + } + + // everything is set up by the time the train is linked, we shouldn't need this + //func_train_find(); + + // but we will need these + train_next(this); + + set_movetype(this, MOVETYPE_PUSH); + this.move_time = time; + } + + if(sf & SF_TRIGGER_RESET) + { + // TODO: make a reset function for trains + } + + return true; +} + +#endif diff --git a/qcsrc/common/mapobjects/func/train.qh b/qcsrc/common/mapobjects/func/train.qh new file mode 100644 index 000000000..0b2a099c5 --- /dev/null +++ b/qcsrc/common/mapobjects/func/train.qh @@ -0,0 +1,10 @@ +#pragma once + + +const int TRAIN_CURVE = BIT(0); +const int TRAIN_TURN = BIT(1); +const int TRAIN_NEEDACTIVATION = BIT(2); + +#ifdef CSQC +.float dmgtime; +#endif diff --git a/qcsrc/common/mapobjects/func/vectormamamam.qc b/qcsrc/common/mapobjects/func/vectormamamam.qc new file mode 100644 index 000000000..61da52acb --- /dev/null +++ b/qcsrc/common/mapobjects/func/vectormamamam.qc @@ -0,0 +1,194 @@ +#include "vectormamamam.qh" +#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 timestep) +{ + vector v, p; + float flags; + entity e; + + flags = o.spawnflags; + v = '0 0 0'; + + e = o.wp00; + if(e) + { + p = e.origin + timestep * e.velocity; + if(flags & PROJECT_ON_TARGETNORMAL) + 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 + timestep * e.velocity; + if(flags & PROJECT_ON_TARGET2NORMAL) + 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 + timestep * e.velocity; + if(flags & PROJECT_ON_TARGET3NORMAL) + 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 + timestep * e.velocity; + if(flags & PROJECT_ON_TARGET4NORMAL) + 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(entity this) +{ + this.nextthink = time + vectormamamam_timestep; + + if(this.owner.active != ACTIVE_ACTIVE) + { + this.owner.velocity = '0 0 0'; + return; + } + + if(this.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed + this.owner.velocity = (this.owner.destvec + func_vectormamamam_origin(this.owner, vectormamamam_timestep) - this.owner.origin) * 10; +} + +void func_vectormamamam_findtarget(entity this) +{ + if(this.target != "") + this.wp00 = find(NULL, targetname, this.target); + + if(this.target2 != "") + this.wp01 = find(NULL, targetname, this.target2); + + if(this.target3 != "") + this.wp02 = find(NULL, targetname, this.target3); + + if(this.target4 != "") + this.wp03 = find(NULL, targetname, this.target4); + + if(!this.wp00 && !this.wp01 && !this.wp02 && !this.wp03) + objerror(this, "No reference entity found, so there is nothing to move. Aborting."); + + this.destvec = this.origin - func_vectormamamam_origin(this, 0); + + entity controller; + controller = new(func_vectormamamam_controller); + controller.owner = this; + controller.nextthink = time + 1; + setthink(controller, func_vectormamamam_controller_think); +} + +void func_vectormamamam_setactive(entity this, int astate) +{ + if (astate == ACTIVE_TOGGLE) + { + if(this.active == ACTIVE_ACTIVE) + this.active = ACTIVE_NOT; + else + this.active = ACTIVE_ACTIVE; + } + else + this.active = astate; + + if(this.active == ACTIVE_NOT) + { + stopsound(this, CH_TRIGGER_SINGLE); + } + else + { + if(this.noise && this.noise != "") + { + _sound(this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + } +} + +void func_vectormamamam_init_for_player(entity this, entity player) +{ + if (this.noise && this.noise != "" && this.active == ACTIVE_ACTIVE && IS_REAL_CLIENT(player)) + { + msg_entity = player; + soundto(MSG_ONE, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } +} + +spawnfunc(func_vectormamamam) +{ + if (this.noise != "") + { + precache_sound(this.noise); + } + + if(!this.targetfactor) + this.targetfactor = 1; + + if(!this.target2factor) + this.target2factor = 1; + + if(!this.target3factor) + this.target3factor = 1; + + if(!this.target4factor) + this.target4factor = 1; + + if(this.targetnormal) + this.targetnormal = normalize(this.targetnormal); + + if(this.target2normal) + this.target2normal = normalize(this.target2normal); + + if(this.target3normal) + this.target3normal = normalize(this.target3normal); + + if(this.target4normal) + this.target4normal = normalize(this.target4normal); + + setblocked(this, generic_plat_blocked); + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + if (!InitMovingBrushTrigger(this)) + return; + + // wait for targets to spawn + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); // for PushMove + + // Savage: Reduce bandwith, critical on e.g. nexdm02 + this.effects |= EF_LOWPRECISION; + + this.setactive = func_vectormamamam_setactive; + this.setactive(this, ACTIVE_ACTIVE); + + // maybe send sound to new players + IL_PUSH(g_initforplayer, this); + this.init_for_player = func_vectormamamam_init_for_player; + + InitializeEntity(this, func_vectormamamam_findtarget, INITPRIO_FINDTARGET); +} +#endif diff --git a/qcsrc/common/mapobjects/func/vectormamamam.qh b/qcsrc/common/mapobjects/func/vectormamamam.qh new file mode 100644 index 000000000..7eb6b0a63 --- /dev/null +++ b/qcsrc/common/mapobjects/func/vectormamamam.qh @@ -0,0 +1,9 @@ +#pragma once + + +const int PROJECT_ON_TARGETNORMAL = BIT(0); +const int PROJECT_ON_TARGET2NORMAL = BIT(1); +const int PROJECT_ON_TARGET3NORMAL = BIT(2); +const int PROJECT_ON_TARGET4NORMAL = BIT(3); + +const float vectormamamam_timestep = 0.1; diff --git a/qcsrc/common/mapobjects/include.qc b/qcsrc/common/mapobjects/include.qc new file mode 100644 index 000000000..59da1f217 --- /dev/null +++ b/qcsrc/common/mapobjects/include.qc @@ -0,0 +1,20 @@ +#include "include.qh" + +// some required common stuff +#include "subs.qc" +#include "triggers.qc" +#include "platforms.qc" +#include "teleporters.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/mapobjects/include.qh b/qcsrc/common/mapobjects/include.qh new file mode 100644 index 000000000..87c07c14d --- /dev/null +++ b/qcsrc/common/mapobjects/include.qh @@ -0,0 +1,19 @@ +#pragma once + +// some required common stuff +#ifdef SVQC + #include +#endif +#include "triggers.qh" +#include "subs.qh" +#include "platforms.qh" +#include "teleporters.qh" + +// func +#include "func/include.qh" + +// target +#include "target/include.qh" + +// trigger +#include "trigger/include.qh" diff --git a/qcsrc/common/mapobjects/misc/_mod.inc b/qcsrc/common/mapobjects/misc/_mod.inc new file mode 100644 index 000000000..d8d5ea6a6 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/_mod.inc @@ -0,0 +1,6 @@ +// generated file; do not modify +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/misc/_mod.qh b/qcsrc/common/mapobjects/misc/_mod.qh new file mode 100644 index 000000000..5fefbe41b --- /dev/null +++ b/qcsrc/common/mapobjects/misc/_mod.qh @@ -0,0 +1,6 @@ +// generated file; do not modify +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/misc/corner.qc b/qcsrc/common/mapobjects/misc/corner.qc new file mode 100644 index 000000000..a0f67b759 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/corner.qc @@ -0,0 +1,75 @@ +#include "corner.qh" +REGISTER_NET_LINKED(ENT_CLIENT_CORNER) + +#ifdef SVQC +bool corner_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_CORNER); + + WriteString(MSG_ENTITY, this.platmovetype); + + WriteVector(MSG_ENTITY, this.origin); + + WriteString(MSG_ENTITY, this.target); + WriteString(MSG_ENTITY, this.target2); + WriteString(MSG_ENTITY, this.target3); + WriteString(MSG_ENTITY, this.target4); + WriteString(MSG_ENTITY, this.targetname); + WriteByte(MSG_ENTITY, this.target_random); + + WriteByte(MSG_ENTITY, this.wait); + + return true; +} + +void corner_link(entity this) +{ + //Net_LinkEntity(this, false, 0, corner_send); +} + +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 + set_platmovetype(this, this.platmovetype); + + corner_link(this); +} +#elif defined(CSQC) + +void corner_remove(entity this) +{ + strfree(this.target); + strfree(this.target2); + strfree(this.target3); + strfree(this.target4); + strfree(this.targetname); + strfree(this.platmovetype); +} + +NET_HANDLE(ENT_CLIENT_CORNER, bool isnew) +{ + this.platmovetype = strzone(ReadString()); + + this.origin = ReadVector(); + setorigin(this, this.origin); + + this.target = strzone(ReadString()); + this.target2 = strzone(ReadString()); + this.target3 = strzone(ReadString()); + this.target4 = strzone(ReadString()); + this.targetname = strzone(ReadString()); + this.target_random = ReadByte(); + + this.wait = ReadByte(); + + return = true; + + this.classname = "path_corner"; + this.drawmask = MASK_NORMAL; + this.entremove = corner_remove; + + set_platmovetype(this, this.platmovetype); +} + +#endif diff --git a/qcsrc/common/mapobjects/misc/corner.qh b/qcsrc/common/mapobjects/misc/corner.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/misc/corner.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/misc/follow.qc b/qcsrc/common/mapobjects/misc/follow.qc new file mode 100644 index 000000000..87619ca71 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/follow.qc @@ -0,0 +1,70 @@ +#include "follow.qh" +// the way this entity works makes it no use to CSQC, as it removes itself instantly + +#ifdef SVQC +void follow_init(entity this) +{ + entity src, dst; + src = NULL; + dst = NULL; + if(this.killtarget != "") + src = find(NULL, targetname, this.killtarget); + if(this.target != "") + dst = find(NULL, targetname, this.target); + + if(!src && !dst) + { + objerror(this, "follow: could not find target/killtarget"); + return; + } + + if(this.jointtype) + { + // already done :P entity must stay + this.aiment = src; + this.enemy = dst; + } + else if(!src || !dst) + { + objerror(this, "follow: could not find target/killtarget"); + return; + } + else if(this.spawnflags & FOLLOW_ATTACH) + { + // attach + if(this.spawnflags & FOLLOW_LOCAL) + { + setattachment(dst, src, this.message); + } + else + { + attach_sameorigin(dst, src, this.message); + } + + dst.solid = SOLID_NOT; // solid doesn't work with attachment + delete(this); + } + else + { + if(this.spawnflags & FOLLOW_LOCAL) + { + set_movetype(dst, 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); + } + + delete(this); + } +} + +spawnfunc(misc_follow) +{ + InitializeEntity(this, follow_init, INITPRIO_FINDTARGET); +} +#endif diff --git a/qcsrc/common/mapobjects/misc/follow.qh b/qcsrc/common/mapobjects/misc/follow.qh new file mode 100644 index 000000000..aef491ff3 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/follow.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int FOLLOW_ATTACH = BIT(0); +const int FOLLOW_LOCAL = BIT(1); diff --git a/qcsrc/common/mapobjects/misc/include.qc b/qcsrc/common/mapobjects/misc/include.qc new file mode 100644 index 000000000..bbe5ce03d --- /dev/null +++ b/qcsrc/common/mapobjects/misc/include.qc @@ -0,0 +1,5 @@ +#include "include.qh" +#include "corner.qc" +#include "follow.qc" +#include "laser.qc" +#include "teleport_dest.qc" diff --git a/qcsrc/common/mapobjects/misc/include.qh b/qcsrc/common/mapobjects/misc/include.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/misc/include.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/misc/laser.qc b/qcsrc/common/mapobjects/misc/laser.qc new file mode 100644 index 000000000..df88b750f --- /dev/null +++ b/qcsrc/common/mapobjects/misc/laser.qc @@ -0,0 +1,418 @@ +#include "laser.qh" +#if defined(CSQC) + #include + #include + #include +#elif defined(MENUQC) +#elif defined(SVQC) +#endif + +REGISTER_NET_LINKED(ENT_CLIENT_LASER) + +#ifdef SVQC +.float modelscale; +void misc_laser_aim(entity this) +{ + vector a; + if(this.enemy) + { + if(this.spawnflags & LASER_FINITE) + { + if(this.enemy.origin != this.mangle) + { + this.mangle = this.enemy.origin; + this.SendFlags |= SF_LASER_UPDATE_TARGET; + } + } + else + { + a = vectoangles(this.enemy.origin - this.origin); + a_x = -a_x; + if(a != this.mangle) + { + this.mangle = a; + this.SendFlags |= SF_LASER_UPDATE_TARGET; + } + } + } + else + { + if(this.angles != this.mangle) + { + this.mangle = this.angles; + this.SendFlags |= SF_LASER_UPDATE_TARGET; + } + } + if(this.origin != this.oldorigin) + { + this.SendFlags |= SF_LASER_UPDATE_ORIGIN; + this.oldorigin = this.origin; + } +} + +void misc_laser_init(entity this) +{ + if(this.target != "") + this.enemy = find(NULL, targetname, this.target); +} + +.entity pusher; +void misc_laser_think(entity this) +{ + vector o; + entity hitent; + vector hitloc; + + this.nextthink = time; + + if(this.active == ACTIVE_NOT) + return; + + misc_laser_aim(this); + + if(this.enemy) + { + o = this.enemy.origin; + if (!(this.spawnflags & LASER_FINITE)) + o = this.origin + normalize(o - this.origin) * LASER_BEAM_MAXLENGTH; + } + else + { + makevectors(this.mangle); + o = this.origin + v_forward * LASER_BEAM_MAXLENGTH; + } + + if(this.dmg || this.enemy.target != "") + { + traceline(this.origin, o, MOVE_NORMAL, this); + } + hitent = trace_ent; + hitloc = trace_endpos; + + if(this.enemy.target != "") // DETECTOR laser + { + if(trace_ent.iscreature) + { + this.pusher = hitent; + if(!this.count) + { + this.count = 1; + + SUB_UseTargets(this.enemy, this.enemy.pusher, NULL); + } + } + else + { + if(this.count) + { + this.count = 0; + + SUB_UseTargets(this.enemy, this.enemy.pusher, NULL); + } + } + } + + if(this.dmg) + { + if(this.team) + if(((this.spawnflags & LASER_INVERT_TEAM) == 0) == (this.team != hitent.team)) + return; + if(hitent.takedamage) + Damage(hitent, this, this, ((this.dmg < 0) ? 100000 : (this.dmg * frametime)), DEATH_HURTTRIGGER.m_id, DMG_NOWEP, hitloc, '0 0 0'); + } +} + +bool laser_SendEntity(entity this, entity to, float sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_LASER); + sendflags = sendflags & 0x0F; // use that bit to indicate finite length laser + if(this.spawnflags & LASER_FINITE) + sendflags |= SF_LASER_FINITE; + if(this.alpha) + sendflags |= SF_LASER_ALPHA; + if(this.scale != 1 || this.modelscale != 1) + sendflags |= SF_LASER_SCALE; + if(this.spawnflags & LASER_NOTRACE) + sendflags |= SF_LASER_NOTRACE; + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & SF_LASER_UPDATE_ORIGIN) + { + WriteVector(MSG_ENTITY, this.origin); + } + if(sendflags & SF_LASER_UPDATE_EFFECT) + { + WriteByte(MSG_ENTITY, this.beam_color.x * 255.0); + WriteByte(MSG_ENTITY, this.beam_color.y * 255.0); + WriteByte(MSG_ENTITY, this.beam_color.z * 255.0); + if(sendflags & SF_LASER_ALPHA) + WriteByte(MSG_ENTITY, this.alpha * 255.0); + if(sendflags & SF_LASER_SCALE) + { + WriteByte(MSG_ENTITY, bound(0, this.scale * 16.0, 255)); + WriteByte(MSG_ENTITY, bound(0, this.modelscale * 16.0, 255)); + } + if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE)) // effect doesn't need sending if the laser is infinite and has collision testing turned off + WriteShort(MSG_ENTITY, this.cnt); + } + if(sendflags & SF_LASER_UPDATE_TARGET) + { + if(sendflags & SF_LASER_FINITE) + { + WriteVector(MSG_ENTITY, this.enemy.origin); + } + else + { + WriteAngle(MSG_ENTITY, this.mangle_x); + WriteAngle(MSG_ENTITY, this.mangle_y); + } + } + if(sendflags & SF_LASER_UPDATE_ACTIVE) + WriteByte(MSG_ENTITY, this.active); + return true; +} + +/*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 +"beam_color" + color of the beam (default: red) +"dmg" + damage per second (-1 for a laser that kills immediately) +*/ + +void laser_setactive(entity this, int act) +{ + int old_status = this.active; + if(act == ACTIVE_TOGGLE) + { + if(this.active == ACTIVE_ACTIVE) + { + this.active = ACTIVE_NOT; + } + else + { + this.active = ACTIVE_ACTIVE; + } + } + else + { + this.active = act; + } + + if (this.active != old_status) + { + this.SendFlags |= SF_LASER_UPDATE_ACTIVE; + misc_laser_aim(this); + } +} + +void laser_use(entity this, entity actor, entity trigger) +{ + this.setactive(this, ACTIVE_TOGGLE); +} + +spawnfunc(misc_laser) +{ + if(this.mdl) + { + if(this.mdl == "none") + this.cnt = -1; + else + { + this.cnt = _particleeffectnum(this.mdl); + if(this.cnt < 0 && this.dmg) + this.cnt = particleeffectnum(EFFECT_LASER_DEADLY); + } + } + else if(!this.cnt) + { + if(this.dmg) + this.cnt = particleeffectnum(EFFECT_LASER_DEADLY); + else + this.cnt = -1; + } + if(this.cnt < 0) + this.cnt = -1; + + if(!this.beam_color && this.colormod) + { + LOG_WARN("misc_laser uses legacy field 'colormod', please use 'beam_color' instead"); + this.beam_color = this.colormod; + } + + if(this.beam_color == '0 0 0') + { + if(!this.alpha) + this.beam_color = '1 0 0'; + } + + if(this.message == "") + { + this.message = "saw the light"; + } + if (this.message2 == "") + { + this.message2 = "was pushed into a laser by"; + } + if(!this.scale) + { + this.scale = 1; + } + if(!this.modelscale) + { + this.modelscale = 1; + } + else if(this.modelscale < 0) + { + this.modelscale = 0; + } + setthink(this, misc_laser_think); + this.nextthink = time; + InitializeEntity(this, misc_laser_init, INITPRIO_FINDTARGET); + + this.mangle = this.angles; + + Net_LinkEntity(this, false, 0, laser_SendEntity); + + this.setactive = laser_setactive; + + IFTARGETED + { + // backwards compatibility + this.use = laser_use; + } + + this.reset = generic_netlinked_reset; + this.reset(this); +} +#elif defined(CSQC) + +// a laser goes from origin in direction angles +// it has color 'beam_color' +// and stops when something is in the way +entityclass(Laser); +classfield(Laser) .int cnt; // end effect +classfield(Laser) .vector colormod; +classfield(Laser) .int state; // on-off +classfield(Laser) .int count; // flags for the laser +classfield(Laser) .vector velocity; // laser endpoint if it is FINITE +classfield(Laser) .float alpha; +classfield(Laser) .float scale; // scaling factor of the thickness +classfield(Laser) .float modelscale; // scaling factor of the dlight + +void Draw_Laser(entity this) +{ + if(this.active == ACTIVE_NOT) + return; + InterpolateOrigin_Do(this); + if(this.count & SF_LASER_FINITE) + { + if(this.count & SF_LASER_NOTRACE) + { + trace_endpos = this.velocity; + trace_dphitq3surfaceflags = 0; + } + else + traceline(this.origin, this.velocity, 0, this); + } + else + { + if(this.count & SF_LASER_NOTRACE) + { + makevectors(this.angles); + trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE; + trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY; + } + else + { + makevectors(this.angles); + traceline(this.origin, this.origin + v_forward * LASER_BEAM_MAXLENGTH, 0, this); + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) + trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE; + } + } + if(this.scale != 0) + { + if(this.alpha) + { + Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, this.alpha, DRAWFLAG_NORMAL, view_origin); + } + else + { + Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, 0.5, DRAWFLAG_ADDITIVE, view_origin); + } + } + if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT))) + { + if(this.cnt >= 0) + __pointparticles(this.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000); + if(this.beam_color != '0 0 0' && this.modelscale != 0) + adddynamiclight(trace_endpos + trace_plane_normal * 1, this.modelscale, this.beam_color * 5); + } +} + +NET_HANDLE(ENT_CLIENT_LASER, bool isnew) +{ + InterpolateOrigin_Undo(this); + + // 30 bytes, or 13 bytes for just moving + int sendflags = ReadByte(); + this.count = (sendflags & 0xF0); + + if(this.count & SF_LASER_FINITE) + this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN; + else + this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN; + + if(sendflags & SF_LASER_UPDATE_ORIGIN) + { + this.origin = ReadVector(); + setorigin(this, this.origin); + } + if(sendflags & SF_LASER_UPDATE_EFFECT) + { + this.beam_color.x = ReadByte() / 255.0; + this.beam_color.y = ReadByte() / 255.0; + this.beam_color.z = ReadByte() / 255.0; + if(sendflags & SF_LASER_ALPHA) + this.alpha = ReadByte() / 255.0; + else + this.alpha = 0; + this.scale = 2; // NOTE: why 2? + this.modelscale = 50; // NOTE: why 50? + if(sendflags & SF_LASER_SCALE) + { + this.scale *= ReadByte() / 16.0; // beam radius + this.modelscale *= ReadByte() / 16.0; // dlight radius + } + if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE)) + this.cnt = ReadShort(); // effect number + else + this.cnt = 0; + } + if(sendflags & SF_LASER_UPDATE_TARGET) + { + if(sendflags & SF_LASER_FINITE) + { + this.velocity = ReadVector(); + } + else + { + this.angles_x = ReadAngle(); + this.angles_y = ReadAngle(); + } + } + if(sendflags & SF_LASER_UPDATE_ACTIVE) + this.active = ReadByte(); + + return = true; + + InterpolateOrigin_Note(this); + this.draw = Draw_Laser; + if (isnew) IL_PUSH(g_drawables, this); +} +#endif diff --git a/qcsrc/common/mapobjects/misc/laser.qh b/qcsrc/common/mapobjects/misc/laser.qh new file mode 100644 index 000000000..0ff57646a --- /dev/null +++ b/qcsrc/common/mapobjects/misc/laser.qh @@ -0,0 +1,22 @@ +#pragma once + + +const int LASER_FINITE = BIT(1); +const int LASER_NOTRACE = BIT(2); +const int LASER_INVERT_TEAM = BIT(3); + +const int SF_LASER_UPDATE_ORIGIN = BIT(0); +const int SF_LASER_UPDATE_TARGET = BIT(1); +const int SF_LASER_UPDATE_ACTIVE = BIT(2); +const int SF_LASER_UPDATE_EFFECT = BIT(3); + +const int SF_LASER_NOTRACE = BIT(4); +const int SF_LASER_SCALE = BIT(5); +const int SF_LASER_ALPHA = BIT(6); +const int SF_LASER_FINITE = BIT(7); + +.vector beam_color; + +const float LASER_BEAM_MAXLENGTH = 32768; // maximum length of a beam trace +// TODO: find a better way to do this +const float LASER_BEAM_MAXWORLDSIZE = 1048576; // to make sure the endpoint of the beam is not visible inside diff --git a/qcsrc/common/mapobjects/misc/teleport_dest.qc b/qcsrc/common/mapobjects/misc/teleport_dest.qc new file mode 100644 index 000000000..126a20ea2 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/teleport_dest.qc @@ -0,0 +1,89 @@ +#include "teleport_dest.qh" +REGISTER_NET_LINKED(ENT_CLIENT_TELEPORT_DEST) + +#ifdef SVQC + +bool teleport_dest_send(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TELEPORT_DEST); + WriteByte(MSG_ENTITY, sendflags); + + if(sendflags & SF_TRIGGER_INIT) + { + WriteByte(MSG_ENTITY, this.cnt); + WriteCoord(MSG_ENTITY, this.speed); + WriteString(MSG_ENTITY, this.targetname); + WriteVector(MSG_ENTITY, this.origin); + + WriteAngle(MSG_ENTITY, this.mangle_x); + WriteAngle(MSG_ENTITY, this.mangle_y); + WriteAngle(MSG_ENTITY, this.mangle_z); + } + + return true; +} + +void teleport_dest_link(entity this) +{ + Net_LinkEntity(this, false, 0, teleport_dest_send); + this.SendFlags |= SF_TRIGGER_INIT; +} + +spawnfunc(info_teleport_destination) +{ + this.classname = "info_teleport_destination"; + + this.mangle = this.angles; + this.angles = '0 0 0'; + + //setorigin(this, this.origin + '0 0 27'); // To fix a mappers' habit as old as Quake + setorigin(this, this.origin); + + IFTARGETED + { + } + else + objerror (this, "^3Teleport destination without a targetname"); + + teleport_dest_link(this); +} + +spawnfunc(misc_teleporter_dest) +{ + spawnfunc_info_teleport_destination(this); +} + +#elif defined(CSQC) + +void teleport_dest_remove(entity this) +{ + // strfree(this.classname); + strfree(this.targetname); +} + +NET_HANDLE(ENT_CLIENT_TELEPORT_DEST, bool isnew) +{ + int sendflags = ReadByte(); + + if(sendflags & SF_TRIGGER_INIT) + { + this.classname = "info_teleport_destination"; + this.cnt = ReadByte(); + this.speed = ReadCoord(); + this.targetname = strzone(ReadString()); + this.origin = ReadVector(); + + this.mangle_x = ReadAngle(); + this.mangle_y = ReadAngle(); + this.mangle_z = ReadAngle(); + + setorigin(this, this.origin); + + this.drawmask = MASK_NORMAL; + this.entremove = teleport_dest_remove; + } + + return = true; +} + +#endif diff --git a/qcsrc/common/mapobjects/misc/teleport_dest.qh b/qcsrc/common/mapobjects/misc/teleport_dest.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/misc/teleport_dest.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/platforms.qc b/qcsrc/common/mapobjects/platforms.qc new file mode 100644 index 000000000..474787731 --- /dev/null +++ b/qcsrc/common/mapobjects/platforms.qc @@ -0,0 +1,227 @@ +#include "platforms.qh" +void generic_plat_blocked(entity this, entity blocker) +{ +#ifdef SVQC + if(this.dmg && blocker.takedamage != DAMAGE_NO) + { + if(this.dmgtime2 < time) + { + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + this.dmgtime2 = time + this.dmgtime; + } + + // Gib dead/dying stuff + if(IS_DEAD(blocker)) + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + } +#endif +} + +void plat_spawn_inside_trigger(entity this) +{ + entity trigger; + vector tmin, tmax; + + trigger = spawn(); + settouch(trigger, plat_center_touch); + set_movetype(trigger, MOVETYPE_NONE); + trigger.solid = SOLID_TRIGGER; + trigger.enemy = this; + + tmin = this.absmin + '25 25 0'; + tmax = this.absmax - '25 25 -8'; + tmin_z = tmax_z - (this.pos1_z - this.pos2_z + 8); + if (this.spawnflags & PLAT_LOW_TRIGGER) + tmax_z = tmin_z + 8; + + if (this.size_x <= 50) + { + tmin_x = (this.mins_x + this.maxs_x) / 2; + tmax_x = tmin_x + 1; + } + if (this.size_y <= 50) + { + tmin_y = (this.mins_y + this.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... + delete(trigger); + objerror(this, "plat_spawn_inside_trigger: platform has odd size or lip, can't spawn"); +} + +void plat_hit_top(entity this) +{ + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_TOP; + + setthink(this, plat_go_down); + this.nextthink = this.ltime + 3; +} + +void plat_hit_bottom(entity this) +{ + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_BOTTOM; +} + +void plat_go_down(entity this) +{ + _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM); + this.state = STATE_DOWN; + SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, plat_hit_bottom); +} + +void plat_go_up(entity this) +{ + _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM); + this.state = STATE_UP; + SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, plat_hit_top); +} + +void plat_center_touch(entity this, entity toucher) +{ +#ifdef SVQC + if (!toucher.iscreature) + return; + + if (toucher.health <= 0) + return; +#elif defined(CSQC) + if (!IS_PLAYER(toucher)) + return; + if(IS_DEAD(toucher)) + return; +#endif + + if (this.enemy.state == STATE_BOTTOM) { + plat_go_up(this.enemy); + } else if (this.enemy.state == STATE_TOP) + this.enemy.nextthink = this.enemy.ltime + 1; +} + +void plat_outside_touch(entity this, entity toucher) +{ +#ifdef SVQC + if (!toucher.iscreature) + return; + + if (toucher.health <= 0) + return; +#elif defined(CSQC) + if (!IS_PLAYER(toucher)) + return; +#endif + + if (this.enemy.state == STATE_TOP) { + entity e = this.enemy; + plat_go_down(e); + } +} + +void plat_trigger_use(entity this, entity actor, entity trigger) +{ + if (getthink(this)) + return; // already activated + plat_go_down(this); +} + + +void plat_crush(entity this, entity blocker) +{ + if((this.spawnflags & CRUSH) && (blocker.takedamage != DAMAGE_NO)) + { // KIll Kill Kill!! +#ifdef SVQC + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + } + else + { +#ifdef SVQC + if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) + { // Shall we bite? + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + // Gib dead/dying stuff + if(IS_DEAD(blocker)) + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + } +#endif + + if (this.state == STATE_UP) + plat_go_down (this); + else if (this.state == STATE_DOWN) + plat_go_up (this); + // 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(entity this, entity actor, entity trigger) +{ + this.use = func_null; + if (this.state != STATE_UP) + objerror (this, "plat_use: not in up state"); + plat_go_down(this); +} + +// WARNING: backwards compatibility because people don't use already existing fields :( +// TODO: Check if any maps use these fields and remove these fields if it doesn't break maps +.string sound1, sound2; + +void plat_reset(entity this) +{ + IFTARGETED + { + setorigin(this, this.pos1); + this.state = STATE_UP; + this.use = plat_use; + } + else + { + setorigin(this, this.pos2); + this.state = STATE_BOTTOM; + this.use = plat_trigger_use; + } + +#ifdef SVQC + this.SendFlags |= SF_TRIGGER_RESET; +#endif +} + +.float platmovetype_start_default, platmovetype_end_default; +bool set_platmovetype(entity e, string s) +{ + // sets platmovetype_start and platmovetype_end based on a string consisting of two values + + int 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(e, "Invalid platform move type; platform would go in reverse, which is not allowed."); + return false; + } + + return true; +} diff --git a/qcsrc/common/mapobjects/platforms.qh b/qcsrc/common/mapobjects/platforms.qh new file mode 100644 index 000000000..346cebc71 --- /dev/null +++ b/qcsrc/common/mapobjects/platforms.qh @@ -0,0 +1,15 @@ +#pragma once + + +const int PLAT_LOW_TRIGGER = BIT(0); + +.float dmgtime2; + +void plat_center_touch(entity this, entity toucher); +void plat_outside_touch(entity this, entity toucher); +void plat_trigger_use(entity this, entity actor, entity trigger); +void plat_go_up(entity this); +void plat_go_down(entity this); +void plat_crush(entity this, entity blocker); + +.float dmg; diff --git a/qcsrc/common/mapobjects/subs.qc b/qcsrc/common/mapobjects/subs.qc new file mode 100644 index 000000000..2a237fdcb --- /dev/null +++ b/qcsrc/common/mapobjects/subs.qc @@ -0,0 +1,369 @@ +#include "subs.qh" +void SUB_NullThink(entity this) { } + +void SUB_CalcMoveDone(entity this); +void SUB_CalcAngleMoveDone(entity this); + +/* +================== +SUB_Friction + +Applies some friction to this +================== +*/ +.float friction; +void SUB_Friction (entity this) +{ + this.nextthink = time; + if(IS_ONGROUND(this)) + this.velocity = this.velocity * (1 - frametime * this.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 + delete(ent); + } +} + +void SUB_SetFade_Think (entity this) +{ + if(this.alpha == 0) + this.alpha = 1; + setthink(this, SUB_SetFade_Think); + this.nextthink = time; + this.alpha -= frametime * this.fade_rate; + if (this.alpha < 0.01) + SUB_VanishOrRemove(this); + else + this.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; + setthink(ent, SUB_SetFade_Think); + ent.nextthink = when; +} + +/* +============= +SUB_CalcMove + +calculate this.velocity and this.nextthink to reach dest from +this.origin traveling at speed +=============== +*/ +void SUB_CalcMoveDone(entity this) +{ + // After moving, set origin to exact final destination + + setorigin (this, this.finaldest); + this.velocity = '0 0 0'; + this.nextthink = -1; + if (this.think1 && this.think1 != SUB_CalcMoveDone) + this.think1 (this); +} + +.float platmovetype_turn; +void SUB_CalcMove_controller_think (entity this) +{ + float traveltime; + float phasepos; + float nexttick; + vector delta; + vector delta2; + vector veloc; + vector angloc; + vector nextpos; + delta = this.destvec; + delta2 = this.destvec2; + if(time < this.animstate_endtime) + { + nexttick = time + PHYS_INPUT_FRAMETIME; + + traveltime = this.animstate_endtime - this.animstate_starttime; + phasepos = (nexttick - this.animstate_starttime) / traveltime; // range: [0, 1] + phasepos = cubic_speedfunc(this.platmovetype_start, this.platmovetype_end, phasepos); + nextpos = this.origin + (delta * phasepos) + (delta2 * phasepos * phasepos); + // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning) + + if(this.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 + vector v = this.owner.angles; + v.x -= 360 * floor((v.x - destangle_x) / 360 + 0.5); + v.y -= 360 * floor((v.y - destangle_y) / 360 + 0.5); + v.z -= 360 * floor((v.z - destangle_z) / 360 + 0.5); + this.owner.angles = v; + angloc = destangle - this.owner.angles; + angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame + this.owner.avelocity = angloc; + } + if(nexttick < this.animstate_endtime) + veloc = nextpos - this.owner.origin; + else + veloc = this.finaldest - this.owner.origin; + veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame + + this.owner.velocity = veloc; + this.nextthink = nexttick; + } + else + { + // derivative: delta + 2 * delta2 (e.g. for angle positioning) + entity own = this.owner; + setthink(own, this.think1); + // set the owner's reference to this entity to NULL + own.move_controller = NULL; + delete(this); + getthink(own)(own); + } +} + +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 + + setorigin(controller, org); + 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 + + setorigin(controller, org); + 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 (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func) +{ + float traveltime; + entity controller; + + if (!tspeed) + objerror (this, "No speed is defined!"); + + this.think1 = func; + this.finaldest = tdest; + setthink(this, SUB_CalcMoveDone); + + switch(tspeedtype) + { + default: + case TSPEED_START: + traveltime = 2 * vlen(tcontrol - this.origin) / tspeed; + break; + case TSPEED_END: + traveltime = 2 * vlen(tcontrol - tdest) / tspeed; + break; + case TSPEED_LINEAR: + traveltime = vlen(tdest - this.origin) / tspeed; + break; + case TSPEED_TIME: + traveltime = tspeed; + break; + } + + if (traveltime < 0.1) // useless anim + { + this.velocity = '0 0 0'; + this.nextthink = this.ltime + 0.1; + return; + } + + // delete the previous controller, otherwise changing movement midway is glitchy + if (this.move_controller != NULL) + { + delete(this.move_controller); + } + controller = new(SUB_CalcMove_controller); + controller.owner = this; + this.move_controller = controller; + controller.platmovetype = this.platmovetype; + controller.platmovetype_start = this.platmovetype_start; + controller.platmovetype_end = this.platmovetype_end; + SUB_CalcMove_controller_setbezier(controller, this.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; + setthink(controller, SUB_CalcMove_controller_think); + controller.think1 = getthink(this); + + // the thinking is now done by the controller + setthink(this, SUB_NullThink); // for PushMove + this.nextthink = this.ltime + traveltime; + + // invoke controller + getthink(controller)(controller); +} + +void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func) +{ + vector delta; + float traveltime; + + if (!tspeed) + objerror (this, "No speed is defined!"); + + this.think1 = func; + this.finaldest = tdest; + setthink(this, SUB_CalcMoveDone); + + if (tdest == this.origin) + { + this.velocity = '0 0 0'; + this.nextthink = this.ltime + 0.1; + return; + } + + delta = tdest - this.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 || (this.platmovetype_start == 1 && this.platmovetype_end == 1)) // is this correct? + { + this.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division + this.nextthink = this.ltime + traveltime; + return; + } + + // now just run like a bezier curve... + SUB_CalcMove_Bezier(this, (this.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func); +} + +void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func) +{ + SUB_CalcMove(ent, tdest, tspeedtype, tspeed, func); +} + +/* +============= +SUB_CalcAngleMove + +calculate this.avelocity and this.nextthink to reach destangle from +this.angles rotating + +The calling function should make sure this.setthink is valid +=============== +*/ +void SUB_CalcAngleMoveDone(entity this) +{ + // After rotating, set angle to exact final angle + this.angles = this.finalangle; + this.avelocity = '0 0 0'; + this.nextthink = -1; + if (this.think1 && this.think1 != SUB_CalcAngleMoveDone) // avoid endless loops + this.think1 (this); +} + +// FIXME: I fixed this function only for rotation around the main axes +void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func) +{ + if (!tspeed) + objerror (this, "No speed is defined!"); + + // take the shortest distance for the angles + this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5); + this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5); + this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5); + vector delta = destangle - this.angles; + float traveltime; + + switch(tspeedtype) + { + default: + case TSPEED_START: + case TSPEED_END: + case TSPEED_LINEAR: + traveltime = vlen (delta) / tspeed; + break; + case TSPEED_TIME: + traveltime = tspeed; + break; + } + + this.think1 = func; + this.finalangle = destangle; + setthink(this, SUB_CalcAngleMoveDone); + + if (traveltime < 0.1) + { + this.avelocity = '0 0 0'; + this.nextthink = this.ltime + 0.1; + return; + } + + this.avelocity = delta * (1 / traveltime); + this.nextthink = this.ltime + traveltime; +} + +void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func) +{ + SUB_CalcAngleMove (ent, destangle, tspeedtype, tspeed, func); +} diff --git a/qcsrc/common/mapobjects/subs.qh b/qcsrc/common/mapobjects/subs.qh new file mode 100644 index 000000000..8d4e40650 --- /dev/null +++ b/qcsrc/common/mapobjects/subs.qh @@ -0,0 +1,57 @@ +#pragma once +#include "defs.qh" + +void SUB_SetFade (entity ent, float when, float fading_time); +void SUB_VanishOrRemove (entity ent); + +.vector finaldest, finalangle; //plat.qc stuff +.void(entity this) think1; +.float state; +.float t_length, t_width; + +.vector destvec; +.vector destvec2; + +.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; + +.entity move_controller; + +#ifdef CSQC +// this stuff is defined in the server side engine VM, so we must define it separately here +.float takedamage; +const int DAMAGE_NO = 0; +const int DAMAGE_YES = 1; +const int DAMAGE_AIM = 2; + +.string noise, noise1, noise2, noise3; // contains names of wavs to play + +.float max_health; // players maximum health is stored here +#endif diff --git a/qcsrc/common/mapobjects/target/_mod.inc b/qcsrc/common/mapobjects/target/_mod.inc new file mode 100644 index 000000000..5ac26d9cf --- /dev/null +++ b/qcsrc/common/mapobjects/target/_mod.inc @@ -0,0 +1,11 @@ +// generated file; do not modify +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/target/_mod.qh b/qcsrc/common/mapobjects/target/_mod.qh new file mode 100644 index 000000000..80a1a7f0c --- /dev/null +++ b/qcsrc/common/mapobjects/target/_mod.qh @@ -0,0 +1,11 @@ +// generated file; do not modify +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/target/changelevel.qc b/qcsrc/common/mapobjects/target/changelevel.qc new file mode 100644 index 000000000..114fd8718 --- /dev/null +++ b/qcsrc/common/mapobjects/target/changelevel.qc @@ -0,0 +1,55 @@ +#include "changelevel.qh" +#ifdef SVQC +.string chmap, gametype; +.entity chlevel_targ; + +void target_changelevel_use(entity this, entity actor, entity trigger) +{ + if(this.spawnflags & CHANGELEVEL_MULTIPLAYER) + { + // simply don't react if a non-player triggers it + if(!IS_PLAYER(actor)) { return; } + + actor.chlevel_targ = this; + + int plnum = 0; + int realplnum = 0; + // let's not count bots + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { + ++realplnum; + if(it.chlevel_targ == this) + ++plnum; + }); + if(plnum < ceil(realplnum * min(1, this.count))) // 70% of players + return; + } + + if(this.gametype != "") + MapInfo_SwitchGameType(MapInfo_Type_FromString(this.gametype)); + + if (this.chmap == "") + localcmd("endmatch\n"); + else + localcmd(strcat("changelevel ", this.chmap, "\n")); +} + +/*target_changelevel +Target to change/end level +KEYS: +chmap: map to switch to, leave empty for endmatch +gametype: gametype for the next map +count: fraction of real players that need to trigger this entity for levelchange +SPAWNFLAGS: +CHANGELEVEL_MULTIPLAYER: multiplayer support +*/ + +spawnfunc(target_changelevel) +{ + this.use = target_changelevel_use; + + if(!this.count) + { + this.count = 0.7; + } +} +#endif diff --git a/qcsrc/common/mapobjects/target/changelevel.qh b/qcsrc/common/mapobjects/target/changelevel.qh new file mode 100644 index 000000000..f6e206edc --- /dev/null +++ b/qcsrc/common/mapobjects/target/changelevel.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int CHANGELEVEL_MULTIPLAYER = BIT(1); diff --git a/qcsrc/common/mapobjects/target/include.qc b/qcsrc/common/mapobjects/target/include.qc new file mode 100644 index 000000000..a45c65ed0 --- /dev/null +++ b/qcsrc/common/mapobjects/target/include.qc @@ -0,0 +1,11 @@ +#include "include.qh" + +#include "changelevel.qc" +#include "kill.qc" +#include "levelwarp.qc" +#include "location.qc" +#include "music.qc" +#include "spawn.qc" +#include "spawnpoint.qc" +#include "speaker.qc" +#include "voicescript.qc" diff --git a/qcsrc/common/mapobjects/target/include.qh b/qcsrc/common/mapobjects/target/include.qh new file mode 100644 index 000000000..c0f7cad44 --- /dev/null +++ b/qcsrc/common/mapobjects/target/include.qh @@ -0,0 +1,3 @@ +#pragma once + +#include "music.qh" diff --git a/qcsrc/common/mapobjects/target/kill.qc b/qcsrc/common/mapobjects/target/kill.qc new file mode 100644 index 000000000..2751c600e --- /dev/null +++ b/qcsrc/common/mapobjects/target/kill.qc @@ -0,0 +1,26 @@ +#include "kill.qh" +#include "location.qh" +#ifdef SVQC + +void target_kill_use(entity this, entity actor, entity trigger) +{ + if(actor.takedamage == DAMAGE_NO) + return; + + if(!actor.iscreature && !actor.damagedbytriggers) + return; + + Damage(actor, this, trigger, 1000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, actor.origin, '0 0 0'); +} + +spawnfunc(target_kill) +{ + this.classname = "target_kill"; + + if (this.message == "") + this.message = "was in the wrong place"; + + this.use = target_kill_use; +} + +#endif diff --git a/qcsrc/common/mapobjects/target/kill.qh b/qcsrc/common/mapobjects/target/kill.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/target/kill.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/target/levelwarp.qc b/qcsrc/common/mapobjects/target/levelwarp.qc new file mode 100644 index 000000000..21419cf81 --- /dev/null +++ b/qcsrc/common/mapobjects/target/levelwarp.qc @@ -0,0 +1,21 @@ +#include "levelwarp.qh" + +#ifdef SVQC +void target_levelwarp_use(entity this, entity actor, entity trigger) +{ + if(!autocvar_g_campaign) + return; // only in campaign + + if(this.cnt) + CampaignLevelWarp(this.cnt - 1); // specific level + else + CampaignLevelWarp(-1); // next level +} + +spawnfunc(target_levelwarp) +{ + // this.cnt is index (starting from 1) of the campaign level to warp to + // 0 means next level + this.use = target_levelwarp_use; +} +#endif diff --git a/qcsrc/common/mapobjects/target/levelwarp.qh b/qcsrc/common/mapobjects/target/levelwarp.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/target/levelwarp.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/target/location.qc b/qcsrc/common/mapobjects/target/location.qc new file mode 100644 index 000000000..5774f45f9 --- /dev/null +++ b/qcsrc/common/mapobjects/target/location.qc @@ -0,0 +1,25 @@ +#include "location.qh" +#ifdef SVQC +void target_push_init(entity this); + +spawnfunc(target_location) +{ + this.classname = "target_location"; + // location name in netname + // eventually support: count, teamgame selectors, line of sight? + + target_push_init(this); + + IL_PUSH(g_locations, this); +} + +spawnfunc(info_location) +{ + this.classname = "target_location"; + this.message = this.netname; + + target_push_init(this); + + IL_PUSH(g_locations, this); +} +#endif diff --git a/qcsrc/common/mapobjects/target/location.qh b/qcsrc/common/mapobjects/target/location.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/target/location.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/target/music.qc b/qcsrc/common/mapobjects/target/music.qc new file mode 100644 index 000000000..5a63872db --- /dev/null +++ b/qcsrc/common/mapobjects/target/music.qc @@ -0,0 +1,344 @@ +#include "music.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include + #include + #include + #include +#endif + +REGISTER_NET_TEMP(TE_CSQC_TARGET_MUSIC) +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_MUSIC) + +#ifdef SVQC + +IntrusiveList g_targetmusic_list; +STATIC_INIT(g_targetmusic_list) +{ + g_targetmusic_list = IL_NEW(); +} + +// 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(entity this, int to, bool is) +{ + WriteHeader(to, TE_CSQC_TARGET_MUSIC); + WriteShort(to, etof(this)); + WriteByte(to, this.volume * 255.0 * is); + WriteByte(to, this.fade_time * 16.0); + WriteByte(to, this.fade_rate * 16.0); + WriteByte(to, this.lifetime); + WriteString(to, this.noise); +} +void target_music_reset(entity this) +{ + if (this.targetname == "") + { + target_music_sendto(this, MSG_ALL, true); + } +} +void target_music_kill() +{ + IL_EACH(g_targetmusic_list, true, + { + it.volume = 0; + if (it.targetname == "") + target_music_sendto(it, MSG_ALL, true); + else + target_music_sendto(it, MSG_ALL, false); + }); +} +void target_music_use(entity this, entity actor, entity trigger) +{ + if(!actor) + return; + if(IS_REAL_CLIENT(actor)) + { + msg_entity = actor; + target_music_sendto(this, MSG_ONE, true); + } + FOREACH_CLIENT(IS_SPEC(it) && it.enemy == actor, { + msg_entity = it; + target_music_sendto(this, MSG_ONE, true); + }); +} +spawnfunc(target_music) +{ + this.use = target_music_use; + this.reset = target_music_reset; + if(!this.volume) + this.volume = 1; + IL_PUSH(g_targetmusic_list, this); + if(this.targetname == "") + target_music_sendto(this, MSG_INIT, true); + else + target_music_sendto(this, MSG_INIT, false); +} +void TargetMusic_RestoreGame() +{ + IL_EACH(g_targetmusic_list, true, + { + if(it.targetname == "") + target_music_sendto(it, MSG_INIT, true); + else + target_music_sendto(it, MSG_INIT, false); + }); +} +// values: +// volume +// noise +// targetname +// fade_time +// spawnflags: +// START_DISABLED +// can be disabled/enabled for everyone with relays +bool trigger_music_SendEntity(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC); + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & SF_MUSIC_ORIGIN) + { + WriteVector(MSG_ENTITY, this.origin); + } + if(sendflags & SF_TRIGGER_INIT) + { + if(this.model != "null") + { + WriteShort(MSG_ENTITY, this.modelindex); + WriteVector(MSG_ENTITY, this.mins); + WriteVector(MSG_ENTITY, this.maxs); + } + else + { + WriteShort(MSG_ENTITY, 0); + WriteVector(MSG_ENTITY, this.maxs); + } + WriteByte(MSG_ENTITY, this.volume * 255.0); + WriteByte(MSG_ENTITY, this.fade_time * 16.0); + WriteByte(MSG_ENTITY, this.fade_rate * 16.0); + WriteString(MSG_ENTITY, this.noise); + } + if(sendflags & SF_TRIGGER_UPDATE) + { + WriteByte(MSG_ENTITY, this.active); + } + return true; +} +void trigger_music_reset(entity this) +{ + if(this.spawnflags & START_DISABLED) + { + this.setactive(this, ACTIVE_NOT); + } + else + { + this.setactive(this, ACTIVE_ACTIVE); + } +} + +spawnfunc(trigger_music) +{ + if(this.model != "") + { + _setmodel(this, this.model); + } + if(!this.volume) + { + this.volume = 1; + } + if(!this.modelindex) + { + setorigin(this, this.origin + this.mins); + setsize(this, '0 0 0', this.maxs - this.mins); + } + + this.setactive = generic_netlinked_setactive; + this.use = generic_netlinked_legacy_use; // backwards compatibility + this.reset = trigger_music_reset; + this.reset(this); + + Net_LinkEntity(this, false, 0, trigger_music_SendEntity); +} +#elif defined(CSQC) + +entity TargetMusic_list; +STATIC_INIT(TargetMusic_list) +{ + TargetMusic_list = LL_NEW(); +} + +void TargetMusic_Advance() +{ + // run AFTER all the thinks! + entity best = music_default; + if (music_target && time < music_target.lifetime) + { + best = music_target; + } + if (music_trigger) + { + best = music_trigger; + } + LL_EACH(TargetMusic_list, it.noise, { + const float vol0 = (getsoundtime(it, CH_BGM_SINGLE) >= 0) ? it.lastvol : -1; + if (it == best) + { + // increase volume + it.state = (it.fade_time > 0) ? bound(0, it.state + frametime / it.fade_time, 1) : 1; + } + else + { + // decrease volume + it.state = (it.fade_rate > 0) ? bound(0, it.state - frametime / it.fade_rate, 1) : 0; + } + const float vol = it.state * it.volume * autocvar_bgmvolume; + if (vol != vol0) + { + if(vol0 < 0) + sound7(it, CH_BGM_SINGLE, it.noise, vol, ATTEN_NONE, 0, BIT(4)); // restart + else + sound7(it, CH_BGM_SINGLE, "", vol, ATTEN_NONE, 0, BIT(4)); + it.lastvol = vol; + } + }); + music_trigger = NULL; + bgmtime = (best) ? getsoundtime(best, CH_BGM_SINGLE) : gettime(GETTIME_CDTRACK); +} + +NET_HANDLE(TE_CSQC_TARGET_MUSIC, bool isNew) +{ + Net_TargetMusic(); + return true; +} + +void Net_TargetMusic() +{ + const int id = ReadShort(); + const float vol = ReadByte() / 255.0; + const float fai = ReadByte() / 16.0; + const float fao = ReadByte() / 16.0; + const float tim = ReadByte(); + const string noi = ReadString(); + + entity e = NULL; + LL_EACH(TargetMusic_list, it.count == id, { e = it; break; }); + if (!e) + { + LL_PUSH(TargetMusic_list, e = new_pure(TargetMusic)); + e.count = id; + } + if(e.noise != noi) + { + strcpy(e.noise, noi); + precache_sound(e.noise); + _sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE); + if(getsoundtime(e, CH_BGM_SINGLE) < 0) + { + LOG_TRACEF("Cannot initialize sound %s", e.noise); + strfree(e.noise); + } + } + e.volume = vol; + e.fade_time = fai; + e.fade_rate = fao; + if(vol > 0) + { + if(tim == 0) + { + music_default = e; + if(!music_disabled) + { + e.state = 2; + cvar_settemp("music_playlist_index", "-1"); // don't use playlists + localcmd("cd stop\n"); // just in case + music_disabled = 1; + } + } + else + { + music_target = e; + e.lifetime = time + tim; + } + } +} + +void Ent_TriggerMusic_Think(entity this) +{ + if(this.active == ACTIVE_NOT) + { + return; + } + vector org = (csqcplayer) ? csqcplayer.origin : view_origin; + if(WarpZoneLib_BoxTouchesBrush(org + STAT(PL_MIN), org + STAT(PL_MAX), this, NULL)) + { + music_trigger = this; + } +} + +void Ent_TriggerMusic_Remove(entity this) +{ + strfree(this.noise); +} + +NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew) +{ + int sendflags = ReadByte(); + if(sendflags & SF_MUSIC_ORIGIN) + { + this.origin = ReadVector(); + } + if(sendflags & SF_TRIGGER_INIT) + { + this.modelindex = ReadShort(); + if(this.modelindex) + { + this.mins = ReadVector(); + this.maxs = ReadVector(); + } + else + { + this.mins = '0 0 0'; + this.maxs = ReadVector(); + } + + this.volume = ReadByte() / 255.0; + this.fade_time = ReadByte() / 16.0; + this.fade_rate = ReadByte() / 16.0; + string s = this.noise; + strcpy(this.noise, ReadString()); + if(this.noise != s) + { + precache_sound(this.noise); + sound7(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE, 0, BIT(4)); + if(getsoundtime(this, CH_BGM_SINGLE) < 0) + { + LOG_WARNF("Cannot initialize sound %s", this.noise); + strfree(this.noise); + } + } + } + if(sendflags & SF_TRIGGER_UPDATE) + { + this.active = ReadByte(); + } + + setorigin(this, this.origin); + setsize(this, this.mins, this.maxs); + this.draw = Ent_TriggerMusic_Think; + if(isnew) + { + LL_PUSH(TargetMusic_list, this); + IL_PUSH(g_drawables, this); + } + return true; +} + +#endif diff --git a/qcsrc/common/mapobjects/target/music.qh b/qcsrc/common/mapobjects/target/music.qh new file mode 100644 index 000000000..ccf3f674e --- /dev/null +++ b/qcsrc/common/mapobjects/target/music.qh @@ -0,0 +1,28 @@ +#pragma once + +.float lifetime; + +const int SF_MUSIC_ORIGIN = BIT(2); + +#ifdef CSQC +float music_disabled; +entity music_default; +entity music_target; +entity music_trigger; +// FIXME also control bgmvolume here, to not require a target_music for the default track. + +entityclass(TargetMusic); +classfield(TargetMusic) .int state; +classfield(TargetMusic) .float lastvol; + +void TargetMusic_Advance(); + +void Net_TargetMusic(); + +void Ent_TriggerMusic_Think(entity this); + +void Ent_TriggerMusic_Remove(entity this); + +#elif defined(SVQC) +void target_music_kill(); +#endif diff --git a/qcsrc/common/mapobjects/target/spawn.qc b/qcsrc/common/mapobjects/target/spawn.qc new file mode 100644 index 000000000..9c999ed4d --- /dev/null +++ b/qcsrc/common/mapobjects/target/spawn.qc @@ -0,0 +1,340 @@ +#include "spawn.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include + #include +#endif + +#ifdef SVQC + +// spawner entity +// "classname" "target_spawn" +// "message" "fieldname value fieldname value ..." +// "spawnflags" +// ON_MAPLOAD = trigger on map load + +float target_spawn_initialized; +.void(entity this) 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(entity this) +{ + _setmodel(this, this.model); +} + +void target_spawn_helper_setsize(entity this) +{ + setsize(this, this.mins, this.maxs); +} + +void target_spawn_edit_entity(entity this, entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act, entity trigger) +{ + float i, n, valuefieldpos; + string key, value, valuefield, valueoffset, valueoffsetrandom; + entity valueent; + vector data, data2; + + 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 + { + LOG_INFO("target_spawn: invalid/unknown entity key ", key, " specified, ignored!"); + 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 = this; + value = ""; + } + else if(value == "activator") + { + valueent = act; + value = ""; + } + else if(value == "other") + { + valueent = trigger; + value = ""; + } + else if(value == "pusher") + { + if(time < act.pushltime) + valueent = act.pusher; + else + valueent = NULL; + 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 = NULL; + value = ftos(time); + } + else + { + LOG_INFO("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!"); + continue; + } + + if(valuefield == "") + { + if(value == "") + value = ftos(etof(valueent)); + } + else + { + if(value != "") + { + LOG_INFO("target_spawn: try to get a field of a non-entity, ignored!"); + continue; + } + data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield))); + if(data2_y == 0) // undefined field, i.e., invalid type + { + LOG_INFO("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!"); + 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: + LOG_INFO("target_spawn: only string, float and vector fields can do calculations, calculation ignored!"); + 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: + LOG_INFO("target_spawn: only float and vector fields can do random calculations, calculation ignored!"); + break; + } + } + } + } + if(key == "$") + { + if(substring(value, 0, 1) == "_") + value = strcat("target_spawn_helper", value); + putentityfieldstring(target_spawn_spawnfunc_field, e, value); + + e.target_spawn_spawnfunc(e); + + // We called an external function, so we have to re-tokenize msg. + n = tokenize_console(msg); + } + else + { + if(data.y == FIELD_VECTOR) + value = strreplace("'", "", value); // why?!? + putentityfieldstring(data.x, e, value); + } + } +} + +void target_spawn_useon(entity e, entity this, entity actor, entity trigger) +{ + this.target_spawn_activator = actor; + target_spawn_edit_entity( + this, + e, + this.message, + find(NULL, targetname, this.killtarget), + find(NULL, targetname, this.target2), + find(NULL, targetname, this.target3), + find(NULL, targetname, this.target4), + actor, + trigger + ); +} + +bool target_spawn_cancreate(entity this) +{ + float c; + entity e; + + c = this.count; + if(c == 0) // no limit? + return true; + + ++c; // increase count to not include MYSELF + for(e = NULL; (e = findfloat(e, target_spawn_id, this.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 false; + return true; +} + +void target_spawn_use(entity this, entity actor, entity trigger) +{ + if(this.target == "") + { + // spawn new entity + if(!target_spawn_cancreate(this)) + return; + entity e = spawn(); + e.spawnfunc_checked = true; + target_spawn_useon(e, this, actor, trigger); + e.target_spawn_id = this.target_spawn_id; + } + else if(this.target == "*activator") + { + // edit entity + if(actor) + target_spawn_useon(actor, this, actor, trigger); + } + else + { + // edit entity + FOREACH_ENTITY_STRING(targetname, this.target, + { + target_spawn_useon(it, this, actor, trigger); + }); + } +} + +void target_spawn_spawnfirst(entity this) +{ + entity act = this.target_spawn_activator; + if(this.spawnflags & ON_MAPLOAD) + target_spawn_use(this, act, NULL); +} + +void initialize_field_db() +{ + if(!target_spawn_initialized) + { + float n, i; + string fn; + vector prev, next; + float ft; + + n = numentityfields(); + for(i = 0; i < n; ++i) + { + fn = entityfieldname(i); + ft = entityfieldtype(i); + next = 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(next)); + if(fn == "target_spawn_spawnfunc") + target_spawn_spawnfunc_field = i; + } + } + + target_spawn_initialized = 1; + } +} + +spawnfunc(target_spawn) +{ + initialize_field_db(); + this.use = target_spawn_use; + this.message = strzone(strreplace("'", "\"", this.message)); + this.target_spawn_id = ++target_spawn_count; + InitializeEntity(this, target_spawn_spawnfirst, INITPRIO_LAST); +} +#endif diff --git a/qcsrc/common/mapobjects/target/spawn.qh b/qcsrc/common/mapobjects/target/spawn.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/target/spawn.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/target/spawnpoint.qc b/qcsrc/common/mapobjects/target/spawnpoint.qc new file mode 100644 index 000000000..fe1538551 --- /dev/null +++ b/qcsrc/common/mapobjects/target/spawnpoint.qc @@ -0,0 +1,24 @@ +#include "spawnpoint.qh" + +#ifdef SVQC +void target_spawnpoint_use(entity this, entity actor, entity trigger) +{ + if(this.active != ACTIVE_ACTIVE) + return; + + actor.spawnpoint_targ = this; +} + +void target_spawnpoint_reset(entity this) +{ + this.active = ACTIVE_ACTIVE; +} + +// TODO: persistent spawnflag? +spawnfunc(target_spawnpoint) +{ + this.active = ACTIVE_ACTIVE; + this.use = target_spawnpoint_use; + this.reset = target_spawnpoint_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/target/spawnpoint.qh b/qcsrc/common/mapobjects/target/spawnpoint.qh new file mode 100644 index 000000000..2eeb8da62 --- /dev/null +++ b/qcsrc/common/mapobjects/target/spawnpoint.qh @@ -0,0 +1,5 @@ +#pragma once + +#ifdef SVQC +.entity spawnpoint_targ; +#endif diff --git a/qcsrc/common/mapobjects/target/speaker.qc b/qcsrc/common/mapobjects/target/speaker.qc new file mode 100644 index 000000000..11c9ad7ba --- /dev/null +++ b/qcsrc/common/mapobjects/target/speaker.qc @@ -0,0 +1,138 @@ +#include "speaker.qh" +#ifdef SVQC +// TODO add a way to do looped sounds with sound(); then complete this entity +void target_speaker_use_off(entity this, entity actor, entity trigger); +void target_speaker_use_activator(entity this, entity actor, entity trigger) +{ + if (!IS_REAL_CLIENT(actor)) + return; + string snd; + if(substring(this.noise, 0, 1) == "*") + { + var .string sample = GetVoiceMessageSampleField(substring(this.noise, 1, -1)); + if(GetPlayerSoundSampleField_notFound) + snd = SND(Null); + else if(actor.(sample) == "") + snd = SND(Null); + else + { + tokenize_console(actor.(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 = this.noise; + msg_entity = actor; + soundto(MSG_ONE, this, CH_TRIGGER, snd, VOL_BASE * this.volume, this.atten); +} +void target_speaker_use_on(entity this, entity actor, entity trigger) +{ + string snd; + if(substring(this.noise, 0, 1) == "*") + { + var .string sample = GetVoiceMessageSampleField(substring(this.noise, 1, -1)); + if(GetPlayerSoundSampleField_notFound) + snd = SND(Null); + else if(actor.(sample) == "") + snd = SND(Null); + else + { + tokenize_console(actor.(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 = this.noise; + _sound(this, CH_TRIGGER_SINGLE, snd, VOL_BASE * this.volume, this.atten); + if(this.spawnflags & (SPEAKER_LOOPED_ON + SPEAKER_LOOPED_OFF)) + this.use = target_speaker_use_off; +} +void target_speaker_use_off(entity this, entity actor, entity trigger) +{ + sound(this, CH_TRIGGER_SINGLE, SND_Null, VOL_BASE * this.volume, this.atten); + this.use = target_speaker_use_on; +} +void target_speaker_reset(entity this) +{ + if(this.spawnflags & SPEAKER_LOOPED_ON) + { + if(this.use == target_speaker_use_on) + target_speaker_use_on(this, NULL, NULL); + } + else if(this.spawnflags & SPEAKER_LOOPED_OFF) + { + if(this.use == target_speaker_use_off) + target_speaker_use_off(this, NULL, NULL); + } +} + +spawnfunc(target_speaker) +{ + // TODO: "*" prefix to sound file name + // TODO: wait and random (just, HOW? random is not a field) + if(this.noise) + precache_sound (this.noise); + + if(!this.atten && (this.spawnflags & SPEAKER_GLOBAL)) + { + LOG_WARN("target_speaker uses legacy spawnflag GLOBAL (BIT(2)), please set atten to -1 instead"); + this.atten = -1; + } + + if(!this.atten) + { + IFTARGETED + this.atten = ATTEN_NORM; + else + this.atten = ATTEN_STATIC; + } + else if(this.atten < 0) + this.atten = 0; + + if(!this.volume) + this.volume = 1; + + IFTARGETED + { + if(this.spawnflags & SPEAKER_ACTIVATOR) + this.use = target_speaker_use_activator; + else if(this.spawnflags & SPEAKER_LOOPED_ON) + { + target_speaker_use_on(this, NULL, NULL); + this.reset = target_speaker_reset; + } + else if(this.spawnflags & SPEAKER_LOOPED_OFF) + { + this.use = target_speaker_use_on; + this.reset = target_speaker_reset; + } + else + this.use = target_speaker_use_on; + } + else if(this.spawnflags & SPEAKER_LOOPED_ON) + { + ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); + delete(this); + } + else if(this.spawnflags & SPEAKER_LOOPED_OFF) + { + objerror(this, "This sound entity can never be activated"); + } + else + { + // Quake/Nexuiz fallback + ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); + delete(this); + } +} +#endif diff --git a/qcsrc/common/mapobjects/target/speaker.qh b/qcsrc/common/mapobjects/target/speaker.qh new file mode 100644 index 000000000..53e0194be --- /dev/null +++ b/qcsrc/common/mapobjects/target/speaker.qh @@ -0,0 +1,7 @@ +#pragma once + + +const int SPEAKER_LOOPED_ON = BIT(0); +const int SPEAKER_LOOPED_OFF = BIT(1); +const int SPEAKER_GLOBAL = BIT(2); // legacy, set speaker atten to -1 instead +const int SPEAKER_ACTIVATOR = BIT(3); diff --git a/qcsrc/common/mapobjects/target/voicescript.qc b/qcsrc/common/mapobjects/target/voicescript.qc new file mode 100644 index 000000000..6dfb568a8 --- /dev/null +++ b/qcsrc/common/mapobjects/target/voicescript.qc @@ -0,0 +1,102 @@ +#include "voicescript.qh" +#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 = NULL; +} + +void target_voicescript_use(entity this, entity actor, entity trigger) +{ + if(actor.voicescript != this) + { + actor.voicescript = this; + actor.voicescript_index = 0; + actor.voicescript_nextthink = time + this.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(game_stopped) + 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 = NULL; // stop trying then + } + } + } +} + +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; + this.use = target_voicescript_use; + + n = tokenize_console(this.message); + this.cnt = n / 2; + for(i = 0; i+1 < n; i += 2) + { + if(argv(i) == "*") + { + this.cnt = i / 2; + ++i; + } + precache_sound(strcat(this.netname, "/", argv(i), ".wav")); + } +} +#endif diff --git a/qcsrc/common/mapobjects/target/voicescript.qh b/qcsrc/common/mapobjects/target/voicescript.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/target/voicescript.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/teleporters.qc b/qcsrc/common/mapobjects/teleporters.qc new file mode 100644 index 000000000..ec6a26d18 --- /dev/null +++ b/qcsrc/common/mapobjects/teleporters.qc @@ -0,0 +1,315 @@ +#include "teleporters.qh" + +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include + #include + #include + #include "../constants.qh" + #include "../mapobjects/subs.qh" + #include "../util.qh" + #include + #include + #include + #include + #include "../deathtypes/all.qh" + #include "../turrets/sv_turrets.qh" + #include "../vehicles/all.qh" + #include "../mapinfo.qh" + #include +#endif + +#ifdef SVQC +float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax) +{ + if (IS_PLAYER(player) && !IS_DEAD(player)) + { + TDEATHLOOP(org) + { + #ifdef SVQC + if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) + #endif + if(IS_PLAYER(head)) + if(!IS_DEAD(head)) + return 1; + } + } + return 0; +} + +void trigger_teleport_link(entity this); + +void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax) +{ + TDEATHLOOP(player.origin) + { + if (IS_PLAYER(player) && player.health >= 1) + { + if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) + { + if(IS_PLAYER(head)) + if(head.health >= 1) + ++tdeath_hit; + Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG.m_id, DMG_NOWEP, head.origin, '0 0 0'); + } + } + else // dead bodies and monsters gib themselves instead of telefragging + Damage (telefragger, teleporter, telefragger, 10000, DEATH_TELEFRAG.m_id, DMG_NOWEP, telefragger.origin, '0 0 0'); + } +} + +void spawn_tdeath(vector v0, entity e, vector v) +{ + tdeath(e, e, e, '0 0 0', '0 0 0'); +} +#endif + +void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags) +{ + entity telefragger; + vector from; + + if(teleporter.owner) + telefragger = teleporter.owner; + else + telefragger = player; + + makevectors (to_angles); + +#ifdef SVQC + if(player.teleportable == TELEPORT_NORMAL) // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers + { + if(teleporter.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps + { + if(tflags & TELEPORT_FLAG_SOUND) + { + string thesound = SND(TELEPORT); + if(teleporter.noise != "") + { + RandomSelection_Init(); + FOREACH_WORD(teleporter.noise, true, + { + RandomSelection_AddString(it, 1, 1); + }); + thesound = RandomSelection_chosen_string; + } + _sound (player, CH_TRIGGER, thesound, VOL_BASE, ATTEN_NORM); + } + if(tflags & TELEPORT_FLAG_PARTICLES) + { + Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); + Send_Effect(EFFECT_TELEPORT, to + v_forward * 32, '0 0 0', 1); + } + teleporter.pushltime = time + 0.2; + } + } +#endif + + // Relocate the player + // assuming to allows PL_MIN to PL_MAX box and some more +#ifdef SVQC + from = player.origin; + setorigin(player, to); + player.oldorigin = to; // don't undo the teleport by unsticking + player.angles = to_angles; + player.fixangle = true; + player.velocity = to_velocity; + BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT); + + makevectors(player.angles); + Reset_ArcBeam(player, v_forward); + UpdateCSQCProjectileAfterTeleport(player); + UpdateItemAfterTeleport(player); +#elif defined(CSQC) + from = player.origin; + setorigin(player, to); + player.angles = to_angles; + player.velocity = to_velocity; + UNSET_ONGROUND(player); + player.iflags |= IFLAG_TELEPORTED | IFLAG_V_ANGLE | IFLAG_ANGLES; + player.csqcmodel_teleported = 1; + player.v_angle = to_angles; + + if(player == csqcplayer) // not for anything but the main player + { + setproperty(VF_ANGLES, player.angles); + setproperty(VF_CL_VIEWANGLES, player.angles); + } +#endif + +#ifdef SVQC + if(IS_PLAYER(player)) + { + if(tflags & TELEPORT_FLAG_TDEATH) + if(player.takedamage && !IS_DEAD(player) && !g_race && !g_cts && (autocvar_g_telefrags || (tflags & TELEPORT_FLAG_FORCE_TDEATH))) + tdeath(player, teleporter, telefragger, telefragmin, telefragmax); + + // player no longer is on ground + UNSET_ONGROUND(player); + + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + player.oldvelocity = player.velocity; + + // reset tracking of who pushed you into a hazard (for kill credit) + if(teleporter.owner) + { + player.pusher = teleporter.owner; + player.pushltime = time + autocvar_g_maxpushtime; + player.istypefrag = PHYS_INPUT_BUTTON_CHAT(player); + } + else + { + player.pushltime = 0; + player.istypefrag = 0; + } + + player.lastteleporttime = time; + player.lastteleport_origin = from; + } +#endif +} + +entity Simple_TeleportPlayer(entity teleporter, entity player) +{ + vector locout; + entity e = NULL; + + // Find the output teleporter + if(teleporter.enemy) + { + e = teleporter.enemy; + } + else + { + // sorry CSQC, random stuff ain't gonna happen +#ifdef SVQC + RandomSelection_Init(); + FOREACH_ENTITY_STRING(targetname, teleporter.target, + { + bool p = true; + if(STAT(TELEPORT_TELEFRAG_AVOID, player)) + { + #ifdef SVQC + locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); + #elif defined(CSQC) + locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); + #endif + if(check_tdeath(player, locout, '0 0 0', '0 0 0')) + p = false; + } + RandomSelection_AddEnt(it, (it.cnt ? it.cnt : 1), p); + }); + e = RandomSelection_chosen_ent; +#endif + } + +#ifdef SVQC + if(!e) { sprint(player, "Teleport destination vanished. Sorry... please complain to the mapper.\n"); } +#elif defined(CSQC) + if(!e) { LOG_INFO("Teleport destination could not be found from CSQC."); } +#endif + + makevectors(e.mangle); + + if(e.speed) + if(vdist(player.velocity, >, e.speed)) + player.velocity = normalize(player.velocity) * max(0, e.speed); + + if(STAT(TELEPORT_MAXSPEED, player)) + if(vdist(player.velocity, >, STAT(TELEPORT_MAXSPEED, player))) + player.velocity = normalize(player.velocity) * max(0, STAT(TELEPORT_MAXSPEED, player)); + + locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); + + TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); + + return e; +} + +void teleport_findtarget(entity this) +{ + bool istrigger = (this.solid == SOLID_TRIGGER); + + int n = 0; + for(entity e = NULL; (e = find(e, targetname, this.target)); ) + { + ++n; +#ifdef SVQC + if(e.move_movetype == MOVETYPE_NONE) + { + entity tracetest_ent = spawn(); + setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST); + tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + waypoint_spawnforteleporter(this, e.origin, 0, tracetest_ent); + delete(tracetest_ent); + } + if(e.classname != "info_teleport_destination") + LOG_INFO("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work."); +#endif + } + + if(n == 0) + { + // no dest! + objerror (this, "Teleporter with nonexistant target"); + return; + } + else if(n == 1) + { + // exactly one dest - bots love that + this.enemy = find(NULL, targetname, this.target); + } + else + { + // have to use random selection every single time + this.enemy = NULL; + } + + // now enable touch + if(istrigger) + settouch(this, Teleport_Touch); +#ifdef SVQC + if(istrigger) + trigger_teleport_link(this); +#endif +} + +entity Teleport_Find(vector mi, vector ma) +{ + IL_EACH(g_teleporters, WarpZoneLib_BoxTouchesBrush(mi, ma, it, NULL), + { + return it; + }); + return NULL; +} + +void WarpZone_PostTeleportPlayer_Callback(entity pl) +{ +#ifdef SVQC + makevectors(pl.angles); + Reset_ArcBeam(pl, v_forward); + UpdateCSQCProjectileAfterTeleport(pl); + UpdateItemAfterTeleport(pl); + if (IS_PLAYER(pl)) anticheat_fixangle(pl); +#endif + // "disown" projectiles after teleport + if(pl.owner) + if(pl.owner == pl.realowner) + { + #ifdef SVQC + if(!(pl.flags & FL_PROJECTILE)) + #elif defined(CSQC) + if(!(pl.flags & BIT(15))) // FL_PROJECTILE + #endif + LOG_INFO("A non-projectile got through a warpzone and its owner cleared. It's a ", pl.classname, "."); + pl.owner = NULL; + } + if(IS_PLAYER(pl)) + { + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + #ifdef SVQC + pl.oldvelocity = pl.velocity; + #endif + } +} diff --git a/qcsrc/common/mapobjects/teleporters.qh b/qcsrc/common/mapobjects/teleporters.qh new file mode 100644 index 000000000..68c5114f4 --- /dev/null +++ b/qcsrc/common/mapobjects/teleporters.qh @@ -0,0 +1,72 @@ +#pragma once +#include "defs.qh" + +IntrusiveList g_teleporters; +STATIC_INIT(g_teleporters) { g_teleporters = IL_NEW(); } + +.entity pusher; + +const int TELEPORT_FLAG_SOUND = BIT(0); +const int TELEPORT_FLAG_PARTICLES = BIT(1); +const int TELEPORT_FLAG_TDEATH = BIT(2); +const int TELEPORT_FLAG_FORCE_TDEATH = BIT(3); + +#define TELEPORT_FLAGS_WARPZONE 0 +#define TELEPORT_FLAGS_PORTAL (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH | TELEPORT_FLAG_FORCE_TDEATH) +#define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH) + +// types for .teleportable entity setting +const int TELEPORT_NORMAL = 1; // play sounds/effects etc +const int TELEPORT_SIMPLE = 2; // only do teleport, nothing special + +entity Simple_TeleportPlayer(entity teleporter, entity player); + +void Teleport_Touch(entity this, entity toucher); + +void teleport_findtarget(entity this); + +entity Teleport_Find(vector mi, vector ma); + +void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags); + +#ifdef SVQC + +void trigger_teleport_use(entity this, entity actor, entity trigger); + +#define TDEATHLOOP(o) \ + entity head; \ + vector deathmin; \ + vector deathmax; \ + float deathradius; \ + deathmin = (o) + player.mins; \ + deathmax = (o) + player.maxs; \ + if(telefragmin != telefragmax) \ + { \ + if(deathmin.x > telefragmin.x) deathmin.x = telefragmin.x; \ + if(deathmin.y > telefragmin.y) deathmin.y = telefragmin.y; \ + if(deathmin.z > telefragmin.z) deathmin.z = telefragmin.z; \ + if(deathmax.x < telefragmax.x) deathmax.x = telefragmax.x; \ + if(deathmax.y < telefragmax.y) deathmax.y = telefragmax.y; \ + if(deathmax.z < telefragmax.z) deathmax.z = telefragmax.z; \ + } \ + deathradius = max(vlen(deathmin), vlen(deathmax)); \ + for(head = findradius(o, deathradius); head; head = head.chain) \ + if(head != player) \ + if(head.takedamage) \ + if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax)) + +float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax); +float tdeath_hit; +void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax); + +void spawn_tdeath(vector v0, entity e, vector v); + +void Reset_ArcBeam(entity player, vector forward); + +#endif + +void WarpZone_PostTeleportPlayer_Callback(entity pl); + +#ifdef CSQC +.entity realowner; +#endif diff --git a/qcsrc/common/mapobjects/trigger/_mod.inc b/qcsrc/common/mapobjects/trigger/_mod.inc new file mode 100644 index 000000000..82eb9f4a9 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/_mod.inc @@ -0,0 +1,25 @@ +// generated file; do not modify +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/trigger/_mod.qh b/qcsrc/common/mapobjects/trigger/_mod.qh new file mode 100644 index 000000000..6250a76a7 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/_mod.qh @@ -0,0 +1,25 @@ +// generated file; do not modify +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mapobjects/trigger/counter.qc b/qcsrc/common/mapobjects/trigger/counter.qc new file mode 100644 index 000000000..4c89c4c27 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/counter.qc @@ -0,0 +1,67 @@ +#include "counter.qh" +#ifdef SVQC +void counter_reset(entity this); + +void counter_use(entity this, entity actor, entity trigger) +{ + this.count -= 1; + if (this.count < 0) + return; + + bool doactivate = (this.spawnflags & COUNTER_FIRE_AT_COUNT); + + if (this.count == 0) + { + if(IS_PLAYER(actor) && !(this.spawnflags & SPAWNFLAG_NOMESSAGE)) + Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COMPLETED); + + doactivate = true; + + if(this.respawntime) + { + setthink(this, counter_reset); + this.nextthink = time + this.respawntime; + } + } + else + { + if(IS_PLAYER(actor) && !(this.spawnflags & SPAWNFLAG_NOMESSAGE)) + { + if(this.count >= 4) + Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COUNTER); + else + Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COUNTER_FEWMORE, this.count); + } + } + + if(doactivate) + SUB_UseTargets(this, actor, trigger); +} + +void counter_reset(entity this) +{ + setthink(this, func_null); + this.nextthink = 0; + this.count = this.cnt; +} + +/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage COUNTER_FIRE_AT_COUNT +Acts as an intermediary for an action that takes multiple inputs. + +If nomessage is not set, it will print "1 more.. " etc when triggered and "sequence complete" when finished. +If COUNTER_FIRE_AT_COUNT is set, it will also fire all of its targets at countdown, making it behave like trigger_mulitple with limited shots + +If respawntime is set, it will re-enable itself after the time once the sequence has been completed + +After the counter has been triggered "count" times (default 2), it will fire all of its targets. +*/ +spawnfunc(trigger_counter) +{ + if (!this.count) + this.count = 2; + this.cnt = this.count; + + this.use = counter_use; + this.reset = counter_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/counter.qh b/qcsrc/common/mapobjects/trigger/counter.qh new file mode 100644 index 000000000..394d15472 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/counter.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int COUNTER_FIRE_AT_COUNT = BIT(2); diff --git a/qcsrc/common/mapobjects/trigger/delay.qc b/qcsrc/common/mapobjects/trigger/delay.qc new file mode 100644 index 000000000..2cd4cfd13 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/delay.qc @@ -0,0 +1,32 @@ +#include "delay.qh" +#ifdef SVQC +void delay_delayeduse(entity this) +{ + SUB_UseTargets(this, this.enemy, this.goalentity); + this.enemy = this.goalentity = NULL; +} + +void delay_use(entity this, entity actor, entity trigger) +{ + this.enemy = actor; + this.goalentity = trigger; + setthink(this, delay_delayeduse); + this.nextthink = time + this.wait; +} + +void delay_reset(entity this) +{ + this.enemy = this.goalentity = NULL; + setthink(this, func_null); + this.nextthink = 0; +} + +spawnfunc(trigger_delay) +{ + if(!this.wait) + this.wait = 1; + + this.use = delay_use; + this.reset = delay_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/delay.qh b/qcsrc/common/mapobjects/trigger/delay.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/delay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/disablerelay.qc b/qcsrc/common/mapobjects/trigger/disablerelay.qc new file mode 100644 index 000000000..eee61c993 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/disablerelay.qc @@ -0,0 +1,29 @@ +#include "disablerelay.qh" +#ifdef SVQC +void trigger_disablerelay_use(entity this, entity actor, entity trigger) +{ + int a = 0, b = 0; + + for(entity e = NULL; (e = find(e, targetname, this.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)) + LOG_INFO("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!"); +} + +spawnfunc(trigger_disablerelay) +{ + this.use = trigger_disablerelay_use; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/disablerelay.qh b/qcsrc/common/mapobjects/trigger/disablerelay.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/disablerelay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/flipflop.qc b/qcsrc/common/mapobjects/trigger/flipflop.qc new file mode 100644 index 000000000..141f3ea9f --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/flipflop.qc @@ -0,0 +1,23 @@ +#include "flipflop.qh" +#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(entity this, entity actor, entity trigger) +{ + this.state = !this.state; + if(this.state) + SUB_UseTargets(this, actor, trigger); +} + +spawnfunc(trigger_flipflop) +{ + if(this.spawnflags & START_ENABLED) + { + this.state = true; + } + this.use = flipflop_use; + this.reset = spawnfunc_trigger_flipflop; // perfect resetter +} + +#endif diff --git a/qcsrc/common/mapobjects/trigger/flipflop.qh b/qcsrc/common/mapobjects/trigger/flipflop.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/flipflop.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/gamestart.qc b/qcsrc/common/mapobjects/trigger/gamestart.qc new file mode 100644 index 000000000..72d76d183 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/gamestart.qc @@ -0,0 +1,28 @@ +#include "gamestart.qh" +#ifdef SVQC +void gamestart_use(entity this, entity actor, entity trigger) +{ + SUB_UseTargets(this, this, trigger); + delete(this); +} + +void gamestart_use_this(entity this) +{ + gamestart_use(this, NULL, NULL); +} + +spawnfunc(trigger_gamestart) +{ + this.use = gamestart_use; + this.reset2 = spawnfunc_trigger_gamestart; + + if(this.wait) + { + setthink(this, adaptor_think2use); + this.nextthink = game_starttime + this.wait; + } + else + InitializeEntity(this, gamestart_use_this, INITPRIO_FINDTARGET); +} + +#endif diff --git a/qcsrc/common/mapobjects/trigger/gamestart.qh b/qcsrc/common/mapobjects/trigger/gamestart.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/gamestart.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/gravity.qc b/qcsrc/common/mapobjects/trigger/gravity.qc new file mode 100644 index 000000000..1ac0f8768 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/gravity.qc @@ -0,0 +1,111 @@ +#include "gravity.qh" +#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; + delete(own.trigger_gravity_check); + } + else + backtrace("Removing a trigger_gravity_check with no valid owner"); + own.trigger_gravity_check = NULL; +} +void trigger_gravity_check_think(entity this) +{ + // This spawns when a player enters the gravity zone and checks if he left. + // Each frame, this.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(this.count <= 0) + { + if(this.owner.trigger_gravity_check == this) + trigger_gravity_remove(this.owner); + else + delete(this); + return; + } + else + { + this.count -= 1; + this.nextthink = time; + } +} + +// legacy +void trigger_gravity_use(entity this, entity actor, entity trigger) +{ + this.setactive(this, ACTIVE_TOGGLE); +} + +void trigger_gravity_touch(entity this, entity toucher) +{ + float g; + + if(this.active == ACTIVE_NOT) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + g = this.gravity; + + if (!(this.spawnflags & GRAVITY_STICKY)) + { + if(toucher.trigger_gravity_check) + { + if(this == toucher.trigger_gravity_check.enemy) + { + // same? + // NOTE: see explanation in trigger_gravity_check_think + toucher.trigger_gravity_check.count = 2; // gravity one more frame... + return; + } + + // compare prio + if(this.cnt > toucher.trigger_gravity_check.enemy.cnt) + trigger_gravity_remove(toucher); + else + return; + } + toucher.trigger_gravity_check = spawn(); + toucher.trigger_gravity_check.enemy = this; + toucher.trigger_gravity_check.owner = toucher; + toucher.trigger_gravity_check.gravity = toucher.gravity; + setthink(toucher.trigger_gravity_check, trigger_gravity_check_think); + toucher.trigger_gravity_check.nextthink = time; + toucher.trigger_gravity_check.count = 2; + if(toucher.gravity) + g *= toucher.gravity; + } + + if (toucher.gravity != g) + { + toucher.gravity = g; + if(this.noise != "") + _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + UpdateCSQCProjectile(this.owner); + } +} + +spawnfunc(trigger_gravity) +{ + if(this.gravity == 1) + return; + + EXACTTRIGGER_INIT; + settouch(this, trigger_gravity_touch); + if(this.noise != "") + precache_sound(this.noise); + + this.active = ACTIVE_ACTIVE; + this.setactive = generic_setactive; + IFTARGETED + { + // legacy use + this.use = trigger_gravity_use; + if(this.spawnflags & GRAVITY_START_DISABLED) + this.active = ACTIVE_NOT; + } +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/gravity.qh b/qcsrc/common/mapobjects/trigger/gravity.qh new file mode 100644 index 000000000..872f04ad9 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/gravity.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int GRAVITY_STICKY = BIT(0); // keep gravity multiplier even after exiting the trigger_gravity +const int GRAVITY_START_DISABLED = BIT(1); diff --git a/qcsrc/common/mapobjects/trigger/heal.qc b/qcsrc/common/mapobjects/trigger/heal.qc new file mode 100644 index 000000000..cfcd726fc --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/heal.qc @@ -0,0 +1,65 @@ +#include "heal.qh" +#ifdef SVQC +.float triggerhealtime; +void trigger_heal_touch(entity this, entity toucher) +{ + if (this.active != ACTIVE_ACTIVE) + return; + + // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) + if (toucher.iscreature) + { + if (toucher.takedamage && !IS_DEAD(toucher) && toucher.triggerhealtime < time) + { + bool is_trigger = this.targetname == ""; + if(is_trigger) + EXACTTRIGGER_TOUCH(this, toucher); + if(this.delay > 0) + toucher.triggerhealtime = time + this.delay; + + bool playthesound = (this.spawnflags & HEAL_SOUND_ALWAYS); + if (toucher.health < this.max_health) + { + playthesound = true; + toucher.health = min(toucher.health + this.health, this.max_health); + toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); + } + + if(playthesound) + _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + } + } +} + +void trigger_heal_use(entity this, entity actor, entity trigger) +{ + trigger_heal_touch(this, actor); +} + +void trigger_heal_init(entity this) +{ + this.active = ACTIVE_ACTIVE; + if(!this.delay) + this.delay = 1; + if(!this.health) + this.health = 10; + if(!this.max_health) + this.max_health = 200; // max health topoff for field + if(this.noise == "") + this.noise = "misc/mediumhealth.wav"; + precache_sound(this.noise); +} + +spawnfunc(trigger_heal) +{ + EXACTTRIGGER_INIT; + settouch(this, trigger_heal_touch); + trigger_heal_init(this); +} + +spawnfunc(target_heal) +{ + this.use = trigger_heal_use; + trigger_heal_init(this); +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/heal.qh b/qcsrc/common/mapobjects/trigger/heal.qh new file mode 100644 index 000000000..8dbeea545 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/heal.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int HEAL_SOUND_ALWAYS = BIT(2); diff --git a/qcsrc/common/mapobjects/trigger/hurt.qc b/qcsrc/common/mapobjects/trigger/hurt.qc new file mode 100644 index 000000000..966e0cfb0 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/hurt.qc @@ -0,0 +1,93 @@ +#include "hurt.qh" +#ifdef SVQC +void trigger_hurt_use(entity this, entity actor, entity trigger) +{ + if(IS_PLAYER(actor)) + this.enemy = actor; + else + this.enemy = NULL; // let's just destroy it, if taking over is too much work +} + +.float triggerhurttime; +void trigger_hurt_touch(entity this, entity toucher) +{ + if (this.active != ACTIVE_ACTIVE) + return; + + if(this.team) + if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team)) + return; + + // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) + if (toucher.iscreature) + { + if (toucher.takedamage) + if (toucher.triggerhurttime < time) + { + EXACTTRIGGER_TOUCH(this, toucher); + toucher.triggerhurttime = time + 1; + + entity own; + own = this.enemy; + if (!IS_PLAYER(own)) + { + own = this; + this.enemy = NULL; // I still hate you all + } + + Damage (toucher, this, own, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0'); + } + } + else if(toucher.damagedbytriggers) + { + if(toucher.takedamage) + { + EXACTTRIGGER_TOUCH(this, toucher); + Damage(toucher, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0'); + } + } + + return; +} + +/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ? +Any object touching this will be hurt +set dmg to damage amount +default dmg = 1000 +*/ +.entity trigger_hurt_next; +entity trigger_hurt_last; +entity trigger_hurt_first; +spawnfunc(trigger_hurt) +{ + EXACTTRIGGER_INIT; + this.active = ACTIVE_ACTIVE; + settouch(this, trigger_hurt_touch); + this.use = trigger_hurt_use; + this.enemy = world; // I hate you all + if (!this.dmg) + this.dmg = 1000; + if (this.message == "") + this.message = "was in the wrong place"; + if (this.message2 == "") + this.message2 = "was thrown into a world of hurt by"; + // this.message = "someone like %s always gets wrongplaced"; + + if(!trigger_hurt_first) + trigger_hurt_first = this; + if(trigger_hurt_last) + trigger_hurt_last.trigger_hurt_next = this; + trigger_hurt_last = this; +} + +bool tracebox_hits_trigger_hurt(vector start, vector e_min, vector e_max, vector end) +{ + entity th; + + for(th = trigger_hurt_first; th; th = th.trigger_hurt_next) + if(tracebox_hits_box(start, e_min, e_max, end, th.absmin, th.absmax)) + return true; + + return false; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/hurt.qh b/qcsrc/common/mapobjects/trigger/hurt.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/hurt.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/impulse.qc b/qcsrc/common/mapobjects/trigger/impulse.qc new file mode 100644 index 000000000..c4e7ae287 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/impulse.qc @@ -0,0 +1,243 @@ +#include "impulse.qh" +// targeted (directional) mode +void trigger_impulse_touch_directional(entity this, entity toucher) +{ + entity targ; + float pushdeltatime; + float str; + + if (this.active != ACTIVE_ACTIVE) + return; + + if (!isPushable(toucher)) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + targ = find(NULL, targetname, this.target); + if(!targ) + { + objerror(this, "trigger_force without a (valid) .target!\n"); + delete(this); + return; + } + + // falloff is not supported because radius is always 0 in directional mode + str = this.strength; + + pushdeltatime = time - toucher.lastpushtime; + if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME) + { + pushdeltatime = 0; + } + toucher.lastpushtime = time; + if(!pushdeltatime) + { + return; + } + + if(this.spawnflags & IMPULSE_DIRECTIONAL_SPEEDTARGET) + { + float addspeed = str - toucher.velocity * normalize(targ.origin - this.origin); + if (addspeed > 0) + { + float accelspeed = min(IMPULSE_DIRECTIONAL_MAX_ACCEL_FACTOR * pushdeltatime * str, addspeed); + toucher.velocity += accelspeed * normalize(targ.origin - this.origin); + } + } + else + toucher.velocity = toucher.velocity + normalize(targ.origin - this.origin) * str * pushdeltatime; + + UNSET_ONGROUND(toucher); + +#ifdef SVQC + UpdateCSQCProjectile(toucher); +#endif +} + +// Directionless (accelerator/decelerator) mode +void trigger_impulse_touch_accel(entity this, entity toucher) +{ + float pushdeltatime; + + if (this.active != ACTIVE_ACTIVE) + return; + + if (!isPushable(toucher)) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + pushdeltatime = time - toucher.lastpushtime; + if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME) + { + pushdeltatime = 0; + } + toucher.lastpushtime = time; + if(!pushdeltatime) + { + return; + } + + // div0: ticrate independent, 1 = identity (not 20) + toucher.velocity = toucher.velocity * (this.strength ** pushdeltatime); + +#ifdef SVQC + UpdateCSQCProjectile(toucher); +#endif +} + +// Spherical (gravity/repulsor) mode +void trigger_impulse_touch_radial(entity this, entity toucher) +{ + float pushdeltatime; + float str; + + if (this.active != ACTIVE_ACTIVE) + return; + + if (!isPushable(toucher)) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + pushdeltatime = time - toucher.lastpushtime; + if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME) + { + pushdeltatime = 0; + } + toucher.lastpushtime = time; + if(!pushdeltatime) + { + return; + } + + setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius); + + str = min(this.radius, vlen(this.origin - toucher.origin)); + + if(this.falloff == FALLOFF_LINEAR) + str = (1 - str / this.radius) * this.strength; // 1 in the inside + else if(this.falloff == FALLOFF_LINEAR_INV) + str = (str / this.radius) * this.strength; // 0 in the inside + else + str = this.strength; + + toucher.velocity = toucher.velocity + normalize(toucher.origin - this.origin) * str * pushdeltatime; + +#ifdef SVQC + UpdateCSQCProjectile(toucher); +#endif +} + +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_IMPULSE) + +/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ? +Force field +-------- 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 much force to add in the direction of .target each second + when .target is set. If not, this is how much to slow down/accelerate + something 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 linear 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). +*/ +#ifdef SVQC +bool trigger_impulse_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE); + + WriteByte(MSG_ENTITY, this.spawnflags); + WriteCoord(MSG_ENTITY, this.radius); + WriteCoord(MSG_ENTITY, this.strength); + WriteByte(MSG_ENTITY, this.falloff); + WriteByte(MSG_ENTITY, this.active); + + trigger_common_write(this, true); + + return true; +} + +void trigger_impulse_link(entity this) +{ + trigger_link(this, trigger_impulse_send); +} + +spawnfunc(trigger_impulse) +{ + this.active = ACTIVE_ACTIVE; + + trigger_init(this); + + if(this.radius) + { + if(!this.strength) + { + this.strength = IMPULSE_DEFAULT_RADIAL_STRENGTH * autocvar_g_triggerimpulse_radial_multiplier; + } + setorigin(this, this.origin); + setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius); + settouch(this, trigger_impulse_touch_radial); + } + else + { + if(this.target) + { + if(!this.strength) + { + this.strength = IMPULSE_DEFAULT_DIRECTIONAL_STRENGTH * autocvar_g_triggerimpulse_directional_multiplier; + } + settouch(this, trigger_impulse_touch_directional); + } + else + { + if(!this.strength) + { + this.strength = IMPULSE_DEFAULT_ACCEL_STRENGTH; + } + this.strength = (this.strength ** autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier; + settouch(this, trigger_impulse_touch_accel); + } + } + + trigger_impulse_link(this); +} +#elif defined(CSQC) +NET_HANDLE(ENT_CLIENT_TRIGGER_IMPULSE, bool isnew) +{ + this.spawnflags = ReadByte(); + this.radius = ReadCoord(); + this.strength = ReadCoord(); + this.falloff = ReadByte(); + this.active = ReadByte(); + + trigger_common_read(this, true); + return = true; + + this.classname = "trigger_impulse"; + this.solid = SOLID_TRIGGER; + this.entremove = trigger_remove_generic; + this.move_time = time; + + if (this.radius) + { + settouch(this, trigger_impulse_touch_radial); + } + else if (this.target) + { + settouch(this, trigger_impulse_touch_directional); + } + else + { + settouch(this, trigger_impulse_touch_accel); + } +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/impulse.qh b/qcsrc/common/mapobjects/trigger/impulse.qh new file mode 100644 index 000000000..e86de4a49 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/impulse.qh @@ -0,0 +1,21 @@ +#pragma once + +// tZorks trigger impulse / gravity +.float radius; +.int falloff; +.float strength; +.float lastpushtime; + +const int FALLOFF_NO = 0; +const int FALLOFF_LINEAR = 1; +const int FALLOFF_LINEAR_INV = 2; + +const int IMPULSE_DIRECTIONAL_SPEEDTARGET = BIT(6); + +const float IMPULSE_DEFAULT_RADIAL_STRENGTH = 2000; +const float IMPULSE_DEFAULT_DIRECTIONAL_STRENGTH = 950; +const float IMPULSE_DEFAULT_ACCEL_STRENGTH = 0.9; + +const float IMPULSE_MAX_PUSHDELTATIME = 0.15; + +const float IMPULSE_DIRECTIONAL_MAX_ACCEL_FACTOR = 8; diff --git a/qcsrc/common/mapobjects/trigger/include.qc b/qcsrc/common/mapobjects/trigger/include.qc new file mode 100644 index 000000000..1c762fc35 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/include.qc @@ -0,0 +1,25 @@ +#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 "keylock.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" +#include "teleport.qc" +#include "viewloc.qc" diff --git a/qcsrc/common/mapobjects/trigger/include.qh b/qcsrc/common/mapobjects/trigger/include.qh new file mode 100644 index 000000000..8aa6b2b17 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/include.qh @@ -0,0 +1,9 @@ +#pragma once + +#include "multi.qh" +#include "jumppads.qh" +#include "secret.qh" +#include "swamp.qh" +#include "keylock.qh" +#include "impulse.qh" +#include "viewloc.qh" diff --git a/qcsrc/common/mapobjects/trigger/jumppads.qc b/qcsrc/common/mapobjects/trigger/jumppads.qc new file mode 100644 index 000000000..5ffdf2d10 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/jumppads.qc @@ -0,0 +1,676 @@ +#include "jumppads.qh" +// TODO: split target_push and put it in the target folder +#ifdef SVQC +#include + +void trigger_push_use(entity this, entity actor, entity trigger) +{ + if(teamplay) + { + this.team = actor.team; + this.SendFlags |= SF_TRIGGER_UPDATE; + } +} +#endif + +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH) +REGISTER_NET_LINKED(ENT_CLIENT_TARGET_PUSH) + +/* + 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 + pushed_entity - object that is to be pushed + + Returns: velocity for the jump + */ +vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity) +{ + float grav, sdist, zdist, vs, vz, jumpheight; + vector sdir, torg; + + torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5; + + grav = PHYS_GRAVITY(NULL); + if(pushed_entity && PHYS_ENTGRAVITY(pushed_entity)) + grav *= PHYS_ENTGRAVITY(pushed_entity); + + 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) + + float flighttime; + if(zdist < 0) + { + // down-jump + if(ht < 0) + { + // almost straight line type + // jump apex is before the jump + // we must take the larger one + flighttime = solution.y; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one too + 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 + flighttime = solution.x; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one + flighttime = solution.y; + } + } + vs = sdist / flighttime; + + // finally calculate the velocity + return sdir * vs + '0 0 1' * vz; +} + +bool jumppad_push(entity this, entity targ) +{ + if (!isPushable(targ)) + return false; + + if(this.enemy) + { + targ.velocity = trigger_push_calculatevelocity(targ.origin, this.enemy, this.height, targ); + } + else if(this.target && this.target != "") + { + entity e; + RandomSelection_Init(); + for(e = NULL; (e = find(e, targetname, this.target)); ) + { + if(e.cnt) + RandomSelection_AddEnt(e, e.cnt, 1); + else + RandomSelection_AddEnt(e, 1, 1); + } + targ.velocity = trigger_push_calculatevelocity(targ.origin, RandomSelection_chosen_ent, this.height, targ); + } + else + { + targ.velocity = this.movedir; + } + + UNSET_ONGROUND(targ); + +#ifdef CSQC + if (targ.flags & FL_PROJECTILE) + { + targ.angles = vectoangles (targ.velocity); + switch(targ.move_movetype) + { + case MOVETYPE_FLY: + set_movetype(targ, MOVETYPE_TOSS); + targ.gravity = 1; + break; + case MOVETYPE_BOUNCEMISSILE: + set_movetype(targ, MOVETYPE_BOUNCE); + targ.gravity = 1; + break; + } + } +#endif + +#ifdef SVQC + if (IS_PLAYER(targ)) + { + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + targ.oldvelocity = targ.velocity; + + if(this.pushltime < time) // prevent "snorring" sound when a player hits the jumppad more than once + { + // flash when activated + Send_Effect(EFFECT_JUMPPAD, targ.origin, targ.velocity, 1); + _sound (targ, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + this.pushltime = time + 0.2; + } + if(IS_REAL_CLIENT(targ) || IS_BOT_CLIENT(targ)) + { + bool found = false; + for(int i = 0; i < targ.jumppadcount && i < NUM_JUMPPADSUSED; ++i) + if(targ.(jumppadsused[i]) == this) + found = true; + if(!found) + { + targ.(jumppadsused[targ.jumppadcount % NUM_JUMPPADSUSED]) = this; + targ.jumppadcount = targ.jumppadcount + 1; + } + + if(IS_REAL_CLIENT(targ)) + { + if(this.message) + centerprint(targ, this.message); + } + else + { + targ.lastteleporttime = time; + targ.lastteleport_origin = targ.origin; + } + + if (!IS_DEAD(targ)) + animdecide_setaction(targ, ANIMACTION_JUMP, true); + } + else + targ.jumppadcount = 1; + + // reset tracking of who pushed you into a hazard (for kill credit) + targ.pushltime = 0; + targ.istypefrag = 0; + } + + if(this.enemy.target) + SUB_UseTargets(this.enemy, targ, this); + + if (targ.flags & FL_PROJECTILE) + { + targ.angles = vectoangles (targ.velocity); + targ.com_phys_gravity_factor = 1; + switch(targ.move_movetype) + { + case MOVETYPE_FLY: + set_movetype(targ, MOVETYPE_TOSS); + targ.gravity = 1; + break; + case MOVETYPE_BOUNCEMISSILE: + set_movetype(targ, MOVETYPE_BOUNCE); + targ.gravity = 1; + break; + } + UpdateCSQCProjectile(targ); + } +#endif + + return true; +} + +void trigger_push_touch(entity this, entity toucher) +{ + if (this.active == ACTIVE_NOT) + return; + + if(this.team) + if(((this.spawnflags & INVERT_TEAMS) == 0) == (DIFF_TEAM(this, toucher))) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + noref bool success = jumppad_push(this, toucher); + +#ifdef SVQC + if (success && (this.spawnflags & PUSH_ONCE)) + { + settouch(this, func_null); + setthink(this, SUB_Remove); + this.nextthink = time; + } +#endif +} + +#ifdef SVQC +void trigger_push_link(entity this); +void trigger_push_updatelink(entity this); +bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org) +{ + setorigin(tracetest_ent, org); + tracetoss(tracetest_ent, tracetest_ent); + if(trace_startsolid) + return false; + + if (!jp.height) + { + // since tracetoss starting from jumppad's origin often fails when target + // is very close to real destination, start it directly from target's + // origin instead + vector ofs = '0 0 0'; + if (vdist(vec2(tracetest_ent.velocity), <, autocvar_sv_maxspeed)) + ofs = stepheightvec; + + tracetest_ent.velocity.z = 0; + setorigin(tracetest_ent, targ.origin + ofs); + tracetoss(tracetest_ent, tracetest_ent); + if (trace_startsolid && ofs.z) + { + setorigin(tracetest_ent, targ.origin + ofs / 2); + tracetoss(tracetest_ent, tracetest_ent); + if (trace_startsolid && ofs.z) + { + setorigin(tracetest_ent, targ.origin); + tracetoss(tracetest_ent, tracetest_ent); + if (trace_startsolid) + return false; + } + } + } + tracebox(trace_endpos, tracetest_ent.mins, tracetest_ent.maxs, trace_endpos - eZ * 1500, true, tracetest_ent); + return true; +} + +bool trigger_push_testorigin_for_item(entity tracetest_ent, entity item, vector org) +{ + setorigin(tracetest_ent, org); + tracetoss(tracetest_ent, tracetest_ent); + + if(trace_startsolid) + return false; + if (trace_ent == item) + return true; + + tracebox(trace_endpos, tracetest_ent.mins, tracetest_ent.maxs, trace_endpos - eZ * 1500, true, tracetest_ent); + + if (trace_ent == item) + return true; + + return false; +} +#endif + +/// if (item != NULL) returns true if the item can be reached by using this jumppad, false otherwise +/// if (item == NULL) tests jumppad's trajectory and eventually spawns waypoints for it (return value doesn't matter) +bool trigger_push_test(entity this, entity item) +{ + // first calculate a typical start point for the jump + vector org = (this.absmin + this.absmax) * 0.5; + org.z = this.absmax.z - PL_MIN_CONST.z - 7; + + if (this.target) + { + int n = 0; +#ifdef SVQC + vector vel = '0 0 0'; +#endif + for(entity t = NULL; (t = find(t, targetname, this.target)); ) + { + ++n; +#ifdef SVQC + if(t.move_movetype != MOVETYPE_NONE) + continue; + + entity e = spawn(); + setsize(e, PL_MIN_CONST, PL_MAX_CONST); + e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + e.velocity = trigger_push_calculatevelocity(org, t, this.height, e); + + vel = e.velocity; + vector best_target = '0 0 0'; + vector best_org = '0 0 0'; + vector best_vel = '0 0 0'; + bool valid_best_target = false; + if (item) + { + if (!trigger_push_testorigin_for_item(e, item, org)) + { + delete(e); + return false; + } + } + else + { + if (trigger_push_testorigin(e, t, this, org)) + { + best_target = trace_endpos; + best_org = org; + best_vel = e.velocity; + valid_best_target = true; + } + } + + vector new_org; + vector dist = t.origin - org; + if (dist.x || dist.y) // if not perfectly vertical + { + // test trajectory with different starting points, sometimes the trajectory + // starting from the jumppad origin can't reach the real destination + // and destination waypoint ends up near the jumppad itself + vector flatdir = normalize(dist - eZ * dist.z); + vector ofs = flatdir * 0.5 * min(fabs(this.absmax.x - this.absmin.x), fabs(this.absmax.y - this.absmin.y)); + new_org = org + ofs; + + LABEL(new_test) + e.velocity = trigger_push_calculatevelocity(new_org, t, this.height, e); + if (item) + { + if (!trigger_push_testorigin_for_item(e, item, new_org)) + { + delete(e); + return false; + } + } + else + { + vel = e.velocity; + if (vdist(vec2(e.velocity), <, autocvar_sv_maxspeed)) + e.velocity = autocvar_sv_maxspeed * flatdir; + if (trigger_push_testorigin(e, t, this, new_org) && (!valid_best_target || trace_endpos.z > best_target.z + 50)) + { + best_target = trace_endpos; + best_org = new_org; + best_vel = vel; + valid_best_target = true; + } + } + if (ofs && new_org != org - ofs) + { + new_org = org - ofs; + goto new_test; + } + } + + if (item) + { + delete(e); + return true; + } + + if (valid_best_target) + { + if (!(boxesoverlap(this.absmin, this.absmax + eZ * 50, best_target + PL_MIN_CONST, best_target + PL_MAX_CONST))) + { + float velxy = vlen(vec2(best_vel)); + float cost = vlen(vec2(t.origin - best_org)) / velxy; + if(velxy < autocvar_sv_maxspeed) + velxy = autocvar_sv_maxspeed; + cost += vlen(vec2(best_target - t.origin)) / velxy; + waypoint_spawnforteleporter(this, best_target, cost, e); + } + } + delete(e); +#endif + } + + if(item) + return false; + + if(!n) + { + // no dest! +#ifdef SVQC + objerror (this, "Jumppad with nonexistant target"); +#endif + return false; + } + else if(n == 1) + { + // exactly one dest - bots love that + this.enemy = find(NULL, targetname, this.target); + } + else + { + // have to use random selection every single time + this.enemy = NULL; + } + } +#ifdef SVQC + else + { + entity e = spawn(); + setsize(e, PL_MIN_CONST, PL_MAX_CONST); + e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + setorigin(e, org); + e.velocity = this.movedir; + tracetoss(e, e); + if (item) + { + bool r = (trace_ent == item); + delete(e); + return r; + } + if (!(boxesoverlap(this.absmin, this.absmax + eZ * 50, trace_endpos + PL_MIN_CONST, trace_endpos + PL_MAX_CONST))) + waypoint_spawnforteleporter(this, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity), e); + delete(e); + } + + defer(this, 0.1, trigger_push_updatelink); +#endif + return true; +} + +void trigger_push_findtarget(entity this) +{ + trigger_push_test(this, NULL); +} + +#ifdef SVQC +float trigger_push_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH); + + WriteByte(MSG_ENTITY, this.team); + WriteInt24_t(MSG_ENTITY, this.spawnflags); + WriteByte(MSG_ENTITY, this.active); + WriteCoord(MSG_ENTITY, this.height); + + WriteVector(MSG_ENTITY, this.movedir); + + trigger_common_write(this, true); + + return true; +} + +void trigger_push_updatelink(entity this) +{ + this.SendFlags |= SF_TRIGGER_INIT; +} + +void trigger_push_link(entity this) +{ + trigger_link(this, trigger_push_send); +} + +/* + * 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. + */ +spawnfunc(trigger_push) +{ + SetMovedir(this); + + trigger_init(this); + + this.active = ACTIVE_ACTIVE; + this.use = trigger_push_use; + settouch(this, trigger_push_touch); + + // normal push setup + if (!this.speed) + this.speed = 1000; + this.movedir = this.movedir * this.speed * 10; + + if (!this.noise) + this.noise = "misc/jumppad.wav"; + precache_sound (this.noise); + + trigger_push_link(this); // link it now + + IL_PUSH(g_jumppads, this); + + // this must be called to spawn the teleport waypoints for bots + InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET); +} + + +bool target_push_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH); + + WriteByte(MSG_ENTITY, this.cnt); + WriteString(MSG_ENTITY, this.targetname); + WriteVector(MSG_ENTITY, this.origin); + + WriteAngle(MSG_ENTITY, this.angles_x); + WriteAngle(MSG_ENTITY, this.angles_y); + WriteAngle(MSG_ENTITY, this.angles_z); + + return true; +} + +void target_push_use(entity this, entity actor, entity trigger) +{ + if(trigger.classname == "trigger_push" || trigger == this) + return; // WTF, why is this a thing + + jumppad_push(this, actor); +} + +void target_push_link(entity this) +{ + BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); + Net_LinkEntity(this, false, 0, target_push_send); + //this.SendFlags |= 1; // update +} + +void target_push_init(entity this) +{ + this.mangle = this.angles; + setorigin(this, this.origin); + target_push_link(this); +} + +void target_push_init2(entity this) +{ + if(this.target && this.target != "") // we have an old style pusher! + { + InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET); + this.use = target_push_use; + } + + target_push_init(this); // normal push target behaviour can be combined with a legacy pusher? +} + +spawnfunc(target_push) +{ + target_push_init2(this); +} + +spawnfunc(info_notnull) +{ + target_push_init(this); +} +spawnfunc(target_position) +{ + target_push_init(this); +} + +#elif defined(CSQC) + +NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH, bool isnew) +{ + this.classname = "jumppad"; + int mytm = ReadByte(); + if(mytm) + { + this.team = mytm - 1; + } + this.spawnflags = ReadInt24_t(); + this.active = ReadByte(); + this.height = ReadCoord(); + + this.movedir = ReadVector(); + + trigger_common_read(this, true); + + this.entremove = trigger_remove_generic; + this.solid = SOLID_TRIGGER; + settouch(this, trigger_push_touch); + this.move_time = time; + defer(this, 0.25, trigger_push_findtarget); + + return true; +} + +void target_push_remove(entity this) +{ + // strfree(this.classname); + strfree(this.targetname); +} + +NET_HANDLE(ENT_CLIENT_TARGET_PUSH, bool isnew) +{ + this.classname = "push_target"; + this.cnt = ReadByte(); + this.targetname = strzone(ReadString()); + this.origin = ReadVector(); + + this.angles_x = ReadAngle(); + this.angles_y = ReadAngle(); + this.angles_z = ReadAngle(); + + return = true; + + setorigin(this, this.origin); + + this.drawmask = MASK_NORMAL; + this.entremove = target_push_remove; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/jumppads.qh b/qcsrc/common/mapobjects/trigger/jumppads.qh new file mode 100644 index 000000000..cd6adec31 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/jumppads.qh @@ -0,0 +1,63 @@ +#pragma once + + +const int PUSH_ONCE = BIT(0); // legacy, deactivate with relay instead +const int PUSH_SILENT = BIT(1); // not used? + +IntrusiveList g_jumppads; +STATIC_INIT(g_jumppads) { g_jumppads = IL_NEW(); } + +.float pushltime; +.float istypefrag; +.float height; + +const int NUM_JUMPPADSUSED = 3; +.float jumppadcount; +.entity jumppadsused[NUM_JUMPPADSUSED]; + +#ifdef SVQC +void SUB_UseTargets(entity this, entity actor, entity trigger); +void trigger_push_use(entity this, entity actor, entity trigger); +bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org); +bool trigger_push_testorigin_for_item(entity tracetest_ent, entity item, vector org); +#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 + pushed_entity - object that is to be pushed + + Returns: velocity for the jump + */ +vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity); + +void trigger_push_touch(entity this, entity toucher); + +.vector dest; +bool trigger_push_test(entity this, entity item); +void trigger_push_findtarget(entity this); + +/* + * 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 +spawnfunc(trigger_push); + +spawnfunc(target_push); +spawnfunc(info_notnull); +spawnfunc(target_position); +#endif diff --git a/qcsrc/common/mapobjects/trigger/keylock.qc b/qcsrc/common/mapobjects/trigger/keylock.qc new file mode 100644 index 000000000..67db14421 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/keylock.qc @@ -0,0 +1,187 @@ +#include "keylock.qh" +/** + * trigger given targets + */ +void trigger_keylock_trigger(entity this, entity actor, string s) +{ + for(entity t = NULL; (t = find(t, targetname, s)); ) + if(t.use) + t.use(t, actor, this); +} + +/** + * kill killtarget of trigger keylock. + */ +void trigger_keylock_kill(string s) +{ + entity t; + for(t = NULL; (t = find(t, targetname, s)); ) + delete(t); +} + +void trigger_keylock_touch(entity this, entity toucher) +{ + bool key_used = false; + bool started_delay = false; + + // only player may trigger the lock + if(!IS_PLAYER(toucher)) + return; + + // check silver key + if(this.itemkeys) + key_used = item_keys_usekey(this, toucher); + + if(this.itemkeys) + { +#ifdef SVQC + // at least one of the keys is missing + if(key_used) + { + // one or more keys were given, but others are still missing! + play2(toucher, this.noise1); + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(this.itemkeys)); + toucher.key_door_messagetime = time + 2; + } + else if(toucher.key_door_messagetime <= time) + { + // no keys were given + play2(toucher, this.noise2); + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(this.itemkeys)); + toucher.key_door_messagetime = time + 2; + } +#endif + + // trigger target2 + if(this.delay <= time || started_delay == true) + if(this.target2) + { + trigger_keylock_trigger(this, toucher, this.target2); + started_delay = true; + this.delay = time + this.wait; + } + } + else + { +#ifdef SVQC + // all keys were given! + play2(toucher, this.noise); + centerprint(toucher, this.message); +#endif + + if(this.target) + trigger_keylock_trigger(this, toucher, this.target); + + if(this.killtarget) + trigger_keylock_kill(this.killtarget); + + delete(this); + } + +} + +REGISTER_NET_LINKED(ENT_CLIENT_KEYLOCK) + +#ifdef SVQC +bool trigger_keylock_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_KEYLOCK); + + WriteInt24_t(MSG_ENTITY, this.itemkeys); + WriteByte(MSG_ENTITY, this.height); + + trigger_common_write(this, true); + + return true; +} + +void trigger_keylock_link(entity this) +{ + // uncomment to network keylocks + //Net_LinkEntity(this, false, 0, trigger_keylock_send); +} + +/*QUAKED trigger_keylock (.0 .5 .8) ? +Keylock trigger. Must target other entities. +This trigger will trigger target entities when all required keys are provided. +-------- KEYS -------- +itemkeys: A bit field with key IDs that are needed to open this lock. +sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default) +target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger +target2: trigger all entities with this targetname when triggered without giving it all the required keys. +killtarget: remove all entities with this targetname when triggered with all the needed keys. +message: print this message to the player who activated the trigger when all needed keys have been given. +message2: print this message to the player who activated the trigger when not all of the needed keys have been given. +noise: sound to play when lock gets unlocked (default: see sounds) +noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav) +noise2: sound to play when a key is missing (default: misc/talk.wav) +wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4. +---------NOTES---------- +If spawned without any key specified in itemkeys, this trigger will display an error and remove itself. +message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone. +*/ +spawnfunc(trigger_keylock) +{ + if(!this.itemkeys) { delete(this); return; } + + // set unlocked message + if(this.message == "") + this.message = "Unlocked!"; + + // set default unlock noise + if(this.noise == "") + { + if(this.sounds == 1) + this.noise = "misc/secret.wav"; + else if(this.sounds == 2) + this.noise = strzone(SND(TALK)); + else //if (this.sounds == 3) { + this.noise = "misc/trigger1.wav"; + } + + // set default use key sound + if(this.noise1 == "") + this.noise1 = "misc/decreasevalue.wav"; + + // set closed sourd + if(this.noise2 == "") + this.noise2 = SND(TALK); + + // delay between triggering message2 and trigger2 + if(!this.wait) { this.wait = 5; } + + // precache sounds + precache_sound(this.noise); + precache_sound(this.noise1); + precache_sound(this.noise2); + + EXACTTRIGGER_INIT; + + settouch(this, trigger_keylock_touch); + + trigger_keylock_link(this); +} +#elif defined(CSQC) +void keylock_remove(entity this) +{ + strfree(this.target); + strfree(this.target2); + strfree(this.target3); + strfree(this.target4); + strfree(this.killtarget); + strfree(this.targetname); +} + +NET_HANDLE(ENT_CLIENT_KEYLOCK, bool isnew) +{ + this.itemkeys = ReadInt24_t(); + this.height = ReadByte(); + + trigger_common_read(this, true); + + return = true; + + this.classname = "trigger_keylock"; + this.entremove = keylock_remove; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/keylock.qh b/qcsrc/common/mapobjects/trigger/keylock.qh new file mode 100644 index 000000000..904c3fa3d --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/keylock.qh @@ -0,0 +1,10 @@ +#pragma once + +#ifdef CSQC +bool item_keys_usekey(entity l, entity p) +{ + int valid = (l.itemkeys & p.itemkeys); // TODO: itemkeys isn't networked or anything! + l.itemkeys &= ~valid; // only some of the needed keys were given + return valid != 0; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/magicear.qc b/qcsrc/common/mapobjects/trigger/magicear.qc new file mode 100644 index 000000000..16118cb9d --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/magicear.qc @@ -0,0 +1,200 @@ +#include "magicear.qh" +#ifdef SVQC +float magicear_matched; +float W_Tuba_HasPlayed(entity pl, .entity weaponentity, 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; + string savemessage; + + magicear_matched = false; + + dotrigger = ((IS_PLAYER(source)) && (!IS_DEAD(source)) && ((ear.radius == 0) || (vdist(source.origin - ear.origin, <=, ear.radius)))); + domatch = ((ear.spawnflags & MAGICEAR_REPLACE_OUTSIDE) || dotrigger); + + if (!domatch) + return msgin; + + if (!msgin) + { + // we are in TUBA mode! + if (!(ear.spawnflags & MAGICEAR_TUBA)) + return msgin; + + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(!W_Tuba_HasPlayed(source, weaponentity, ear.message, ear.movedir_x, !(ear.spawnflags & MAGICEAR_TUBA_EXACTPITCH), ear.movedir_y, ear.movedir_z)) + return msgin; + } + + magicear_matched = true; + + if(dotrigger) + { + savemessage = ear.message; + ear.message = string_null; + SUB_UseTargets(ear, source, NULL); + ear.message = savemessage; + } + + if(ear.netname != "") + return ear.netname; + + return msgin; + } + + if(ear.spawnflags & MAGICEAR_TUBA) // ENOTUBA + return msgin; + + if(privatesay) + { + if(ear.spawnflags & MAGICEAR_IGNORE_TELL) + return msgin; + } + else + { + if(!teamsay) + if(ear.spawnflags & MAGICEAR_IGNORE_SAY) + return msgin; + if(teamsay > 0) + if(ear.spawnflags & MAGICEAR_IGNORE_TEAMSAY) + return msgin; + if(teamsay < 0) + if(ear.spawnflags & MAGICEAR_IGNORE_INVALIDTELL) + return msgin; + } + + matchstart = -1; + l = strlen(ear.message); + + if(ear.spawnflags & MAGICEAR_NODECOLORIZE) + 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) + { + savemessage = ear.message; + ear.message = string_null; + SUB_UseTargets(ear, source, NULL); + ear.message = savemessage; + } + + if(ear.spawnflags & MAGICEAR_REPLACE_WHOLE_MESSAGE) + { + 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 & MAGICEAR_CONTINUE)) + if(magicear_matched) + return msgout; + msgin = msgout; + } + return msgin; +} + +spawnfunc(trigger_magicear) +{ + this.enemy = magicears; + magicears = this; + + // 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) + + this.movedir_x -= 1; // map to tuba instrument numbers +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/magicear.qh b/qcsrc/common/mapobjects/trigger/magicear.qh new file mode 100644 index 000000000..4e705868c --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/magicear.qh @@ -0,0 +1,13 @@ +#pragma once + + +const int MAGICEAR_IGNORE_SAY = BIT(0); +const int MAGICEAR_IGNORE_TEAMSAY = BIT(1); +const int MAGICEAR_IGNORE_TELL = BIT(2); +const int MAGICEAR_IGNORE_INVALIDTELL = BIT(3); +const int MAGICEAR_REPLACE_WHOLE_MESSAGE = BIT(4); +const int MAGICEAR_REPLACE_OUTSIDE = BIT(5); +const int MAGICEAR_CONTINUE = BIT(6); +const int MAGICEAR_NODECOLORIZE = BIT(7); +const int MAGICEAR_TUBA = BIT(8); +const int MAGICEAR_TUBA_EXACTPITCH = BIT(9); diff --git a/qcsrc/common/mapobjects/trigger/monoflop.qc b/qcsrc/common/mapobjects/trigger/monoflop.qc new file mode 100644 index 000000000..0c960ba8a --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/monoflop.qc @@ -0,0 +1,49 @@ +#include "monoflop.qh" +#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(entity this, entity actor, entity trigger) +{ + this.nextthink = time + this.wait; + this.enemy = actor; + if(this.state) + return; + this.state = 1; + SUB_UseTargets(this, actor, trigger); +} +void monoflop_fixed_use(entity this, entity actor, entity trigger) +{ + if(this.state) + return; + this.nextthink = time + this.wait; + this.state = 1; + this.enemy = actor; + SUB_UseTargets(this, actor, trigger); +} + +void monoflop_think(entity this) +{ + this.state = 0; + SUB_UseTargets(this, this.enemy, NULL); +} + +void monoflop_reset(entity this) +{ + this.state = 0; + this.nextthink = 0; +} + +spawnfunc(trigger_monoflop) +{ + if(!this.wait) + this.wait = 1; + if(this.spawnflags & MONOFLOP_FIXED) + this.use = monoflop_fixed_use; + else + this.use = monoflop_use; + setthink(this, monoflop_think); + this.state = 0; + this.reset = monoflop_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/monoflop.qh b/qcsrc/common/mapobjects/trigger/monoflop.qh new file mode 100644 index 000000000..c64dffdee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/monoflop.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int MONOFLOP_FIXED = BIT(0); diff --git a/qcsrc/common/mapobjects/trigger/multi.qc b/qcsrc/common/mapobjects/trigger/multi.qc new file mode 100644 index 000000000..accfbe8ac --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/multi.qc @@ -0,0 +1,224 @@ +#include "multi.qh" +// NOTE: also contains trigger_once at bottom + +#ifdef SVQC +// the wait time has passed, so set back up for another activation +void multi_wait(entity this) +{ + if (this.max_health) + { + this.health = this.max_health; + this.takedamage = DAMAGE_YES; + this.solid = SOLID_BBOX; + } +} + + +// the trigger was just touched/killed/used +// this.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(entity this) +{ + if (this.nextthink > time) + { + return; // allready been triggered + } + + if(this.spawnflags & ONLY_PLAYERS && !IS_PLAYER(this.enemy)) + { + return; // only players + } + + // TODO: restructure this so that trigger_secret is more independent + if (this.classname == "trigger_secret") + { + if (!IS_PLAYER(this.enemy)) + return; + found_secrets = found_secrets + 1; + WriteByte (MSG_ALL, SVC_FOUNDSECRET); + } + + if (this.noise) + { + _sound (this.enemy, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + } + + // don't trigger again until reset + this.takedamage = DAMAGE_NO; + + SUB_UseTargets(this, this.enemy, this.goalentity); + + if (this.wait > 0) + { + setthink(this, multi_wait); + this.nextthink = time + this.wait; + } + else if (this.wait == 0) + { + multi_wait(this); // waiting finished + } + else + { // we can't just delete(this) here, because this is a touch function + // called while C code is looping through area links... + settouch(this, func_null); + } +} + +void multi_use(entity this, entity actor, entity trigger) +{ + this.goalentity = trigger; + this.enemy = actor; + multi_trigger(this); +} + +void multi_touch(entity this, entity toucher) +{ + if(!(this.spawnflags & ALL_ENTITIES) && !toucher.iscreature) + { + return; + } + + if(this.team) + { + if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team)) + { + return; + } + } + + // if the trigger has an angles field, check player's facing direction + if (this.movedir != '0 0 0') + { + makevectors (toucher.angles); + if (v_forward * this.movedir < 0) + return; // not facing the right way + } + + // if the trigger has pressed keys, check that the player is pressing those keys + if(this.pressedkeys && IS_PLAYER(toucher)) // only for players + { + if(!(CS(toucher).pressedkeys & this.pressedkeys)) + { + return; + } + } + + EXACTTRIGGER_TOUCH(this, toucher); + + this.enemy = toucher; + this.goalentity = toucher; + multi_trigger(this); +} + +void multi_eventdamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if(!this.takedamage) + return; + if(this.spawnflags & NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + if(this.team) + if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != attacker.team)) + return; + this.health = this.health - damage; + if (this.health <= 0) + { + this.enemy = attacker; + this.goalentity = inflictor; + multi_trigger(this); + } +} + +void multi_reset(entity this) +{ + if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) ) + settouch(this, multi_touch); + if (this.max_health) + { + this.health = this.max_health; + this.takedamage = DAMAGE_YES; + this.solid = SOLID_BBOX; + } + setthink(this, func_null); + this.nextthink = 0; + this.team = this.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 +*/ +spawnfunc(trigger_multiple) +{ + this.reset = multi_reset; + if (this.sounds == 1) + this.noise = "misc/secret.wav"; + else if (this.sounds == 2) + this.noise = strzone(SND(TALK)); + else if (this.sounds == 3) + this.noise = "misc/trigger1.wav"; + + if(this.noise) + precache_sound(this.noise); + + if (!this.wait) + this.wait = 0.2; + else if(this.wait < -1) + this.wait = 0; + this.use = multi_use; + + EXACTTRIGGER_INIT; + + this.team_saved = this.team; + IL_PUSH(g_saved_team, this); + + if (this.health) + { + if (this.spawnflags & SPAWNFLAG_NOTOUCH) + objerror (this, "health and notouch don't make sense\n"); + this.canteamdamage = true; + this.max_health = this.health; + this.event_damage = multi_eventdamage; + this.takedamage = DAMAGE_YES; + this.solid = SOLID_BBOX; + setorigin(this, this.origin); // make sure it links into the world + } + else + { + if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) ) + { + settouch(this, multi_touch); + setorigin(this, this.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 +*/ +spawnfunc(trigger_once) +{ + this.wait = -1; + spawnfunc_trigger_multiple(this); +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/multi.qh b/qcsrc/common/mapobjects/trigger/multi.qh new file mode 100644 index 000000000..43358c274 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/multi.qh @@ -0,0 +1,8 @@ +#pragma once + +#ifdef SVQC +void multi_trigger(entity this); +void multi_reset(entity this); + +spawnfunc(trigger_once); +#endif diff --git a/qcsrc/common/mapobjects/trigger/multivibrator.qc b/qcsrc/common/mapobjects/trigger/multivibrator.qc new file mode 100644 index 000000000..932fda13c --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/multivibrator.qc @@ -0,0 +1,78 @@ +#include "multivibrator.qh" +#ifdef SVQC +void multivibrator_send(entity this) +{ + float newstate; + float cyclestart; + + cyclestart = floor((time + this.phase) / (this.wait + this.respawntime)) * (this.wait + this.respawntime) - this.phase; + + newstate = (time < cyclestart + this.wait); + + if(this.state != newstate) + SUB_UseTargets(this, this, NULL); + this.state = newstate; + + if(this.state) + this.nextthink = cyclestart + this.wait + 0.01; + else + this.nextthink = cyclestart + this.wait + this.respawntime + 0.01; +} + +void multivibrator_send_think(entity this) +{ + multivibrator_send(this); +} + +void multivibrator_toggle(entity this, entity actor, entity trigger) +{ + if(this.nextthink == 0) + { + multivibrator_send(this); + } + else + { + if(this.state) + { + SUB_UseTargets(this, actor, trigger); + this.state = 0; + } + this.nextthink = 0; + } +} + +void multivibrator_reset(entity this) +{ + if(!(this.spawnflags & START_ENABLED)) + this.nextthink = 0; // wait for a trigger event + else + this.nextthink = max(1, time); +} + +/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED +"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_ENABLED: assume it is already turned on (when targeted) +*/ +spawnfunc(trigger_multivibrator) +{ + if(!this.wait) + this.wait = 1; + if(!this.respawntime) + this.respawntime = this.wait; + + this.state = 0; + this.use = multivibrator_toggle; + setthink(this, multivibrator_send_think); + this.nextthink = max(1, time); + + IFTARGETED + multivibrator_reset(this); +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/multivibrator.qh b/qcsrc/common/mapobjects/trigger/multivibrator.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/multivibrator.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/relay.qc b/qcsrc/common/mapobjects/trigger/relay.qc new file mode 100644 index 000000000..f99d364ae --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay.qc @@ -0,0 +1,26 @@ +#include "relay.qh" +#ifdef SVQC + +void relay_use(entity this, entity actor, entity trigger) +{ + if(this.active != ACTIVE_ACTIVE) + return; + + SUB_UseTargets(this, actor, trigger); +} + +/*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. +*/ +spawnfunc(trigger_relay) +{ + this.active = ACTIVE_ACTIVE; + this.use = relay_use; + this.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully +} + +spawnfunc(target_relay) +{ + spawnfunc_trigger_relay(this); +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/relay.qh b/qcsrc/common/mapobjects/trigger/relay.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/relay_activators.qc b/qcsrc/common/mapobjects/trigger/relay_activators.qc new file mode 100644 index 000000000..18c2a40d0 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_activators.qc @@ -0,0 +1,34 @@ +#include "relay_activators.qh" +#ifdef SVQC +void relay_activators_use(entity this, entity actor, entity trigger) +{ + for(entity trg = NULL; (trg = find(trg, targetname, this.target)); ) + { + if (trg.setactive) + trg.setactive(trg, this.cnt); + else + { + //bprint("Not using setactive\n"); + generic_setactive(trg, this.cnt); + } + } +} + +spawnfunc(relay_activate) +{ + this.cnt = ACTIVE_ACTIVE; + this.use = relay_activators_use; +} + +spawnfunc(relay_deactivate) +{ + this.cnt = ACTIVE_NOT; + this.use = relay_activators_use; +} + +spawnfunc(relay_activatetoggle) +{ + this.cnt = ACTIVE_TOGGLE; + this.use = relay_activators_use; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/relay_activators.qh b/qcsrc/common/mapobjects/trigger/relay_activators.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_activators.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/relay_if.qc b/qcsrc/common/mapobjects/trigger/relay_if.qc new file mode 100644 index 000000000..9adcd666e --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_if.qc @@ -0,0 +1,20 @@ +#include "relay_if.qh" +#ifdef SVQC +void trigger_relay_if_use(entity this, entity actor, entity trigger) +{ + int n = this.count; + + // TODO make this generic AND faster than nextent()ing through all, if somehow possible + n = (cvar_string(this.netname) == cvar_string(this.message)); + if(this.spawnflags & RELAYIF_NEGATE) + n = !n; + + if(n) + SUB_UseTargets(this, actor, trigger); +} + +spawnfunc(trigger_relay_if) +{ + this.use = trigger_relay_if_use; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/relay_if.qh b/qcsrc/common/mapobjects/trigger/relay_if.qh new file mode 100644 index 000000000..6f37aa71d --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_if.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int RELAYIF_NEGATE = BIT(0); diff --git a/qcsrc/common/mapobjects/trigger/relay_teamcheck.qc b/qcsrc/common/mapobjects/trigger/relay_teamcheck.qc new file mode 100644 index 000000000..bf03b1542 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_teamcheck.qc @@ -0,0 +1,37 @@ +#include "relay_teamcheck.qh" +#ifdef SVQC +void trigger_relay_teamcheck_use(entity this, entity actor, entity trigger) +{ + if(actor.team) + { + if(this.spawnflags & RELAYTEAMCHECK_INVERT) + { + if(DIFF_TEAM(actor, this)) + SUB_UseTargets(this, actor, trigger); + } + else + { + if(SAME_TEAM(actor, this)) + SUB_UseTargets(this, actor, trigger); + } + } + else + { + if(this.spawnflags & RELAYTEAMCHECK_NOTEAM) + SUB_UseTargets(this, actor, trigger); + } +} + +void trigger_relay_teamcheck_reset(entity this) +{ + this.team = this.team_saved; +} + +spawnfunc(trigger_relay_teamcheck) +{ + this.team_saved = this.team; + IL_PUSH(g_saved_team, this); + this.use = trigger_relay_teamcheck_use; + this.reset = trigger_relay_teamcheck_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/relay_teamcheck.qh b/qcsrc/common/mapobjects/trigger/relay_teamcheck.qh new file mode 100644 index 000000000..602d25356 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_teamcheck.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int RELAYTEAMCHECK_NOTEAM = BIT(0); +const int RELAYTEAMCHECK_INVERT = BIT(1); diff --git a/qcsrc/common/mapobjects/trigger/secret.qc b/qcsrc/common/mapobjects/trigger/secret.qc new file mode 100644 index 000000000..9377332e2 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/secret.qc @@ -0,0 +1,90 @@ +#include "secret.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include + #include +#endif + +#ifdef SVQC + +void secrets_setstatus(entity this) +{ + // TODO: use global stats! + STAT(SECRETS_TOTAL, this) = secrets_total; + STAT(SECRETS_FOUND, this) = secrets_found; +} + +/** + * A secret has been found (maybe :P) + */ +void trigger_secret_touch(entity this, entity toucher) +{ + // only a player can trigger this + if (!IS_PLAYER(toucher)) + 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(toucher, this.message); + this.message = ""; + + // handle normal trigger features + multi_touch(this, toucher); + // we can't just delete(this) here, because this is a touch function + // called while C code is looping through area links... + //delete(this); +} + +/*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. +*/ +spawnfunc(trigger_secret) +{ + // FIXME: should it be disabled in most modes? + + // update secrets count + secrets_total += 1; + + // add default message + if (this.message == "") + this.message = "You found a secret!"; + + // set default sound + if (this.noise == "") + if (!this.sounds) + this.sounds = 1; // misc/secret.wav + + // this entity can't be a target itself!!!! + this.targetname = ""; + + // you can't just shoot a room to find it, can you? + this.health = 0; + + // a secret can not be delayed + this.delay = 0; + + // convert this trigger to trigger_once + //this.classname = "trigger_once"; + spawnfunc_trigger_once(this); + + // take over the touch() function, so we can mark secret as found + settouch(this, trigger_secret_touch); + // ignore triggering; + this.use = func_null; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/secret.qh b/qcsrc/common/mapobjects/trigger/secret.qh new file mode 100644 index 000000000..fcc55c395 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/secret.qh @@ -0,0 +1,19 @@ +#pragma once +#ifdef SVQC + +/** + * Total number of secrets on the map. + */ +float secrets_total; + +/** + * Total numbe of secrets found on the map. + */ +float secrets_found; + + +/** + * update secrets status. + */ +void secrets_setstatus(entity this); +#endif diff --git a/qcsrc/common/mapobjects/trigger/swamp.qc b/qcsrc/common/mapobjects/trigger/swamp.qc new file mode 100644 index 000000000..058e41ca2 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/swamp.qc @@ -0,0 +1,157 @@ +#include "swamp.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include + #include + #include + #include +#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 +spawnfunc(trigger_swamp); +#endif +void swamp_touch(entity this, entity toucher); +void swampslug_think(entity this); + + +/* +* 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(entity this) +{ + //Slowly kill the slug + this.health = this.health - 1; + + //Slug dead? then remove curses. + if(this.health <= 0) + { + this.owner.in_swamp = 0; + delete(this); + //centerprint(this.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 (this.owner, this, this, this.dmg, DEATH_SWAMP.m_id, DMG_NOWEP, this.owner.origin, '0 0 0'); +#endif + + this.nextthink = time + this.swamp_interval; +} + +void swamp_touch(entity this, entity toucher) +{ + // If whatever thats touching the swamp is not a player + // or if its a dead player, just dont care abt it. + if(!IS_PLAYER(toucher) || IS_DEAD(toucher)) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + // Chech if player alredy got a swampslug. + if(toucher.in_swamp != 1) + { + // If not attach one. + //centerprint(toucher,"Entering swamp!\n"); + toucher.swampslug = spawn(); + toucher.swampslug.health = 2; + setthink(toucher.swampslug, swampslug_think); + toucher.swampslug.nextthink = time; + toucher.swampslug.owner = toucher; + toucher.swampslug.dmg = this.dmg; + toucher.swampslug.swamp_interval = this.swamp_interval; + toucher.swamp_slowdown = this.swamp_slowdown; + toucher.in_swamp = 1; + return; + } + + //toucher.in_swamp = 1; + + //Revitalize players swampslug + toucher.swampslug.health = 2; +} + +REGISTER_NET_LINKED(ENT_CLIENT_SWAMP) + +#ifdef SVQC +float swamp_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_SWAMP); + + WriteByte(MSG_ENTITY, this.dmg); // can probably get away with using a single byte here + WriteByte(MSG_ENTITY, this.swamp_slowdown); + WriteByte(MSG_ENTITY, this.swamp_interval); + + trigger_common_write(this, false); + + return true; +} + +void swamp_link(entity this) +{ + trigger_link(this, swamp_send); +} + +/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ? +Players gettin into the swamp will +get slowd down and damaged +*/ +spawnfunc(trigger_swamp) +{ + // Init stuff + trigger_init(this); + settouch(this, swamp_touch); + + // Setup default keys, if missing + if(this.dmg <= 0) + this.dmg = 5; + if(this.swamp_interval <= 0) + this.swamp_interval = 1; + if(this.swamp_slowdown <= 0) + this.swamp_slowdown = 0.5; + + swamp_link(this); +} + +#elif defined(CSQC) + +NET_HANDLE(ENT_CLIENT_SWAMP, bool isnew) +{ + this.dmg = ReadByte(); + this.swamp_slowdown = ReadByte(); + this.swamp_interval = ReadByte(); + + trigger_common_read(this, false); + + return = true; + + this.classname = "trigger_swamp"; + this.solid = SOLID_TRIGGER; + settouch(this, swamp_touch); + this.drawmask = MASK_NORMAL; + this.move_time = time; + this.entremove = trigger_remove_generic; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/swamp.qh b/qcsrc/common/mapobjects/trigger/swamp.qh new file mode 100644 index 000000000..f4df98378 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/swamp.qh @@ -0,0 +1,8 @@ +#pragma once + +.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) diff --git a/qcsrc/common/mapobjects/trigger/teleport.qc b/qcsrc/common/mapobjects/trigger/teleport.qc new file mode 100644 index 000000000..825dd01dd --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/teleport.qc @@ -0,0 +1,183 @@ +#include "teleport.qh" +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_TELEPORT) + +#ifdef SVQC +void trigger_teleport_use(entity this, entity actor, entity trigger) +{ + if(teamplay) + this.team = actor.team; +#ifdef SVQC + this.SendFlags |= SF_TRIGGER_UPDATE; +#endif +} +#endif + +bool Teleport_Active(entity this, entity player) +{ + if (this.active != ACTIVE_ACTIVE) + return false; + +#ifdef SVQC + if (!player.teleportable) + return false; + + if(player.vehicle) + if(!player.vehicle.teleportable) + return false; + + if(IS_TURRET(player)) + return false; +#elif defined(CSQC) + if(!IS_PLAYER(player)) + return false; +#endif + + if(IS_DEAD(player)) + return false; + + if(this.team) + if(((this.spawnflags & INVERT_TEAMS) == 0) == (DIFF_TEAM(this, player))) + return false; + + return true; +} + +void Teleport_Touch(entity this, entity toucher) +{ + entity player = toucher; + + if(!Teleport_Active(this, player)) + return; + + EXACTTRIGGER_TOUCH(this, player); + +#ifdef SVQC + if(IS_PLAYER(player)) + RemoveGrapplingHooks(player); +#endif + + entity e; + e = Simple_TeleportPlayer(this, player); + +#ifdef SVQC + string s = this.target; this.target = string_null; + SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too? + if (!this.target) this.target = s; + + SUB_UseTargets(e, player, player); +#endif +} + +#ifdef SVQC +void target_teleport_use(entity this, entity actor, entity trigger) +{ + entity player = actor; + + if(!Teleport_Active(this, player)) + return; + + if(IS_PLAYER(player)) + RemoveGrapplingHooks(player); + + entity e = Simple_TeleportPlayer(this, player); + + string s = this.target; this.target = string_null; + SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too? + if (!this.target) + { + this.target = s; + } + + SUB_UseTargets(e, player, player); +} +#endif + +#ifdef SVQC +float trigger_teleport_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_TELEPORT); + + WriteByte(MSG_ENTITY, this.team); + WriteInt24_t(MSG_ENTITY, this.spawnflags); + WriteByte(MSG_ENTITY, this.active); + WriteCoord(MSG_ENTITY, this.speed); + + trigger_common_write(this, true); + + return true; +} + +void trigger_teleport_link(entity this) +{ + //trigger_link(this, trigger_teleport_send); +} + +spawnfunc(trigger_teleport) +{ + this.angles = '0 0 0'; + + this.active = ACTIVE_ACTIVE; + //trigger_init(this); // only for predicted triggers? + EXACTTRIGGER_INIT; + this.use = trigger_teleport_use; + + if(this.noise != "") + FOREACH_WORD(this.noise, true, precache_sound(it)); + + // this must be called to spawn the teleport waypoints for bots + InitializeEntity(this, teleport_findtarget, INITPRIO_FINDTARGET); + + if (this.target == "") + { + objerror (this, "Teleporter with no target"); + return; + } + + IL_PUSH(g_teleporters, this); +} + +spawnfunc(target_teleporter) +{ + if(this.target == "") + { + // actually a destination! + spawnfunc_info_teleport_destination(this); + return; + } + + this.active = ACTIVE_ACTIVE; + + this.use = target_teleport_use; + + if(this.noise != "") + FOREACH_WORD(this.noise, true, precache_sound(it)); + + InitializeEntity(this, teleport_findtarget, INITPRIO_FINDTARGET); +} +#elif defined(CSQC) +NET_HANDLE(ENT_CLIENT_TRIGGER_TELEPORT, bool isnew) +{ + this.classname = "trigger_teleport"; + if(isnew) + IL_PUSH(g_teleporters, this); + int mytm = ReadByte(); + if(mytm) + { + this.team = mytm - 1; + } + this.spawnflags = ReadInt24_t(); + this.active = ReadByte(); + this.speed = ReadCoord(); + + trigger_common_read(this, true); + + this.entremove = trigger_remove_generic; + this.solid = SOLID_TRIGGER; + //settouch(this, trigger_push_touch); + this.move_time = time; + defer(this, 0.25, teleport_findtarget); + + return true; +} + +#endif diff --git a/qcsrc/common/mapobjects/trigger/teleport.qh b/qcsrc/common/mapobjects/trigger/teleport.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/teleport.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/viewloc.qc b/qcsrc/common/mapobjects/trigger/viewloc.qc new file mode 100644 index 000000000..ba5dcbe44 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/viewloc.qc @@ -0,0 +1,213 @@ +#include "viewloc.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include + #include +#endif + +REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC) +REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC_TRIGGER) + +#ifdef SVQC + +void viewloc_think(entity this) +{ + // we abuse this method, rather than using normal .touch, because touch isn't reliable with multiple clients inside the same trigger, and can't "untouch" entities + + // set myself as current viewloc where possible +#if 1 + FOREACH_CLIENT(IS_PLAYER(it) && it.viewloc == this, + { + it.viewloc = NULL; + }); +#else + entity e; + for(e = NULL; (e = findentity(e, viewloc, this)); ) + e.viewloc = NULL; +#endif + +#if 1 + FOREACH_CLIENT(!it.viewloc && IS_PLAYER(it), + { + vector emin = it.absmin; + vector emax = it.absmax; + if(this.solid == SOLID_BSP) + { + emin -= '1 1 1'; + emax += '1 1 1'; + } + if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick + { + if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate + it.viewloc = this; + } + }); +#else + + for(e = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); e; e = e.chain) + if(!e.viewloc) + if(IS_PLAYER(e)) // should we support non-player entities with this? + //if(!IS_DEAD(e)) // death view is handled separately, we can't override this just yet + { + vector emin = e.absmin; + vector emax = e.absmax; + if(this.solid == SOLID_BSP) + { + emin -= '1 1 1'; + emax += '1 1 1'; + } + if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick + if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, e)) // accurate + e.viewloc = this; + } +#endif + + this.nextthink = time; +} + +bool trigger_viewloc_send(entity this, entity to, int sf) +{ + // CSQC doesn't need to know our origin (yet), as we're only available for referencing + WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER); + + WriteByte(MSG_ENTITY, this.spawnflags); + + WriteEntity(MSG_ENTITY, this.enemy); + WriteEntity(MSG_ENTITY, this.goalentity); + + WriteVector(MSG_ENTITY, this.origin); + + return true; +} + +void viewloc_init(entity this) +{ + entity e; + for(e = NULL; (e = find(e, targetname, this.target)); ) + if(e.classname == "target_viewlocation_start") + { + this.enemy = e; + break; + } + for(e = NULL; (e = find(e, targetname, this.target2)); ) + if(e.classname == "target_viewlocation_end") + { + this.goalentity = e; + break; + } + + if(!this.enemy) { LOG_INFO("^1FAIL!"); delete(this); return; } + + if(!this.goalentity) + this.goalentity = this.enemy; // make them match so CSQC knows what to do + + Net_LinkEntity(this, false, 0, trigger_viewloc_send); + + setthink(this, viewloc_think); + this.nextthink = time; +} + +spawnfunc(trigger_viewlocation) +{ + // we won't check target2 here yet, as it may not even need to exist + if(this.target == "") { LOG_INFO("^1FAIL!"); delete(this); return; } + + EXACTTRIGGER_INIT; + InitializeEntity(this, viewloc_init, INITPRIO_FINDTARGET); +} + +bool viewloc_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC); + + WriteByte(MSG_ENTITY, this.cnt); + + WriteVector(MSG_ENTITY, this.origin); + + WriteAngle(MSG_ENTITY, this.angles_x); + WriteAngle(MSG_ENTITY, this.angles_y); + WriteAngle(MSG_ENTITY, this.angles_z); + + return true; +} + +.float angle; +void viewloc_link(entity this) +{ + if(this.angle) + this.angles_y = this.angle; + Net_LinkEntity(this, false, 0, viewloc_send); +} + +spawnfunc(target_viewlocation_start) +{ + this.classname = "target_viewlocation_start"; + this.cnt = 1; + viewloc_link(this); +} +spawnfunc(target_viewlocation_end) +{ + this.classname = "target_viewlocation_end"; + this.cnt = 2; + viewloc_link(this); +} + +// compatibility +spawnfunc(target_viewlocation) +{ + spawnfunc_target_viewlocation_start(this); +} + +#elif defined(CSQC) + +void trigger_viewloc_updatelink(entity this) +{ + this.enemy = findfloat(NULL, entnum, this.cnt); + this.goalentity = findfloat(NULL, entnum, this.count); +} + +NET_HANDLE(ENT_CLIENT_VIEWLOC_TRIGGER, bool isnew) +{ + this.spawnflags = ReadByte(); + + float point1 = ReadShort(); + float point2 = ReadShort(); + + this.enemy = findfloat(NULL, entnum, point1); + this.goalentity = findfloat(NULL, entnum, point2); + + this.origin = ReadVector(); + + return = true; + + setorigin(this, this.origin); + + this.cnt = point1; + this.count = point2; + + setthink(this, trigger_viewloc_updatelink); + this.nextthink = time + 1; // we need to delay this or else + + this.classname = "trigger_viewlocation"; + this.drawmask = MASK_NORMAL; // not so concerned, but better keep it alive +} + +NET_HANDLE(ENT_CLIENT_VIEWLOC, bool isnew) +{ + this.cnt = ReadByte(); + + this.origin = ReadVector(); + setorigin(this, this.origin); + + this.movedir_x = ReadAngle(); + this.movedir_y = ReadAngle(); + this.movedir_z = ReadAngle(); + + return = true; + + this.classname = ((this.cnt == 2) ? "target_viewlocation_end" : "target_viewlocation_start"); + this.drawmask = MASK_NORMAL; // don't cull it +} + +#endif diff --git a/qcsrc/common/mapobjects/trigger/viewloc.qh b/qcsrc/common/mapobjects/trigger/viewloc.qh new file mode 100644 index 000000000..3c393afd3 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/viewloc.qh @@ -0,0 +1,14 @@ +#pragma once + + +const int VIEWLOC_NOSIDESCROLL = BIT(0); // NOTE: currently unimplemented +const int VIEWLOC_FREEAIM = BIT(1); +const int VIEWLOC_FREEMOVE = BIT(2); + +.entity viewloc; + +#ifdef CSQC +.entity goalentity; +.entity enemy; +.vector movedir; +#endif diff --git a/qcsrc/common/mapobjects/triggers.qc b/qcsrc/common/mapobjects/triggers.qc new file mode 100644 index 000000000..9db38a10b --- /dev/null +++ b/qcsrc/common/mapobjects/triggers.qc @@ -0,0 +1,349 @@ +#include "triggers.qh" +void SUB_DontUseTargets(entity this, entity actor, entity trigger) { } + +void SUB_UseTargets(entity this, entity actor, entity trigger); + +void DelayThink(entity this) +{ + SUB_UseTargets (this, this.enemy, NULL); + delete(this); +} + +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); +} + +#ifdef SVQC +void generic_setactive(entity this, int act) +{ + if(act == ACTIVE_TOGGLE) + { + if(this.active == ACTIVE_ACTIVE) + { + this.active = ACTIVE_NOT; + } + else + { + this.active = ACTIVE_ACTIVE; + } + } + else + { + this.active = act; + } +} + +void generic_netlinked_setactive(entity this, int act) +{ + int old_status = this.active; + generic_setactive(this, act); + + if (this.active != old_status) + { + this.SendFlags |= SF_TRIGGER_UPDATE; + } +} + +void generic_netlinked_reset(entity this) +{ + IFTARGETED + { + if(this.spawnflags & START_ENABLED) + { + this.active = ACTIVE_ACTIVE; + } + else + { + this.active = ACTIVE_NOT; + } + } + else + { + this.active = ACTIVE_ACTIVE; + } + + this.SendFlags |= SF_TRIGGER_UPDATE; +} + +// Compatibility with old maps +void generic_netlinked_legacy_use(entity this, entity actor, entity trigger) +{ + LOG_WARNF("Entity %s was (de)activated by a trigger, please update map to use relays", this.targetname); + this.setactive(this, ACTIVE_TOGGLE); +} + +bool autocvar_g_triggers_debug = true; + +void trigger_init(entity this) +{ + string m = this.model; + EXACTTRIGGER_INIT; + if(autocvar_g_triggers_debug) + { + if(m != "") + { + precache_model(m); + _setmodel(this, m); // no precision needed + } + setorigin(this, this.origin); + if(this.scale) + setsize(this, this.mins * this.scale, this.maxs * this.scale); + else + setsize(this, this.mins, this.maxs); + } + + if(autocvar_g_triggers_debug) + BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); +} + +void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc) +{ + setSendEntity(this, sendfunc); + this.SendFlags = 0xFFFFFF; +} + +void trigger_common_write(entity this, bool withtarget) +{ + int f = 0; + if(this.warpzone_isboxy) + BITSET_ASSIGN(f, 1); + if(this.origin != '0 0 0') + BITSET_ASSIGN(f, 4); + if(this.movedir != '0 0 0') + BITSET_ASSIGN(f, 8); + if(this.angles != '0 0 0') + BITSET_ASSIGN(f, 16); + WriteByte(MSG_ENTITY, f); + + if(withtarget) + { + // probably some way to clean this up... + int targbits = 0; + if(this.target && this.target != "") targbits |= BIT(0); + if(this.target2 && this.target2 != "") targbits |= BIT(1); + if(this.target3 && this.target3 != "") targbits |= BIT(2); + if(this.target4 && this.target4 != "") targbits |= BIT(3); + if(this.targetname && this.targetname != "") targbits |= BIT(4); + if(this.killtarget && this.killtarget != "") targbits |= BIT(5); + + WriteByte(MSG_ENTITY, targbits); + + if(targbits & BIT(0)) + WriteString(MSG_ENTITY, this.target); + if(targbits & BIT(1)) + WriteString(MSG_ENTITY, this.target2); + if(targbits & BIT(2)) + WriteString(MSG_ENTITY, this.target3); + if(targbits & BIT(3)) + WriteString(MSG_ENTITY, this.target4); + if(targbits & BIT(4)) + WriteString(MSG_ENTITY, this.targetname); + if(targbits & BIT(5)) + WriteString(MSG_ENTITY, this.killtarget); + } + + if(f & 4) + WriteVector(MSG_ENTITY, this.origin); + + if(f & 8) + WriteVector(MSG_ENTITY, this.movedir); + + if(f & 16) + WriteVector(MSG_ENTITY, this.angles); + + WriteShort(MSG_ENTITY, this.modelindex); + WriteVector(MSG_ENTITY, this.mins); + WriteVector(MSG_ENTITY, this.maxs); + WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255)); +} + +#elif defined(CSQC) + +void trigger_common_read(entity this, bool withtarget) +{ + int f = ReadByte(); + this.warpzone_isboxy = (f & 1); + + if(withtarget) + { + strfree(this.target); + strfree(this.target2); + strfree(this.target3); + strfree(this.target4); + strfree(this.targetname); + strfree(this.killtarget); + + int targbits = ReadByte(); + + this.target = ((targbits & BIT(0)) ? strzone(ReadString()) : string_null); + this.target2 = ((targbits & BIT(1)) ? strzone(ReadString()) : string_null); + this.target3 = ((targbits & BIT(2)) ? strzone(ReadString()) : string_null); + this.target4 = ((targbits & BIT(3)) ? strzone(ReadString()) : string_null); + this.targetname = ((targbits & BIT(4)) ? strzone(ReadString()) : string_null); + this.killtarget = ((targbits & BIT(5)) ? strzone(ReadString()) : string_null); + } + + if(f & 4) + this.origin = ReadVector(); + else + this.origin = '0 0 0'; + setorigin(this, this.origin); + + if(f & 8) + this.movedir = ReadVector(); + else + this.movedir = '0 0 0'; + + if(f & 16) + this.angles = ReadVector(); + else + this.angles = '0 0 0'; + + this.modelindex = ReadShort(); + this.mins = ReadVector(); + this.maxs = ReadVector(); + this.scale = ReadByte() / 16; + setsize(this, this.mins, this.maxs); +} + +void trigger_remove_generic(entity this) +{ + strfree(this.target); + strfree(this.target2); + strfree(this.target3); + strfree(this.target4); + strfree(this.targetname); + strfree(this.killtarget); +} +#endif + + +/* +============================== +SUB_UseTargets + +the global "activator" should be set to the entity that initiated the firing. + +If this.delay is set, a DelayedUse entity will be created that will actually +do the SUB_UseTargets after that many seconds have passed. + +Centerprints any this.message to the activator. + +Removes all entities with a targetname that match this.killtarget, +and removes them, so some events can remove other triggers. + +Search for (string)targetname in all entities that +match (string)this.target and call their .use function + +============================== +*/ + +void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse) +{ +// +// check for a delay +// + if (this.delay) + { + // create a temp object to fire at a later time + entity t = new(DelayedUse); + t.nextthink = time + this.delay; + setthink(t, DelayThink); + t.enemy = actor; + t.message = this.message; + t.killtarget = this.killtarget; + t.target = this.target; + t.target2 = this.target2; + t.target3 = this.target3; + t.target4 = this.target4; + t.antiwall_flag = this.antiwall_flag; + return; + } + + string s; + +// +// print the message +// +#ifdef SVQC + if(this) + if(IS_PLAYER(actor) && this.message != "") + if(IS_REAL_CLIENT(actor)) + { + centerprint(actor, this.message); + if (this.noise == "") + play2(actor, SND(TALK)); + } + +// +// kill the killtagets +// + s = this.killtarget; + if (s != "") + { + for(entity t = NULL; (t = find(t, targetname, s)); ) + delete(t); + } +#endif + +// +// fire targets +// + + if(this.target_random) + RandomSelection_Init(); + + for(int i = 0; i < 4; ++i) + { + switch(i) + { + default: + case 0: s = this.target; break; + case 1: s = this.target2; break; + case 2: s = this.target3; break; + case 3: s = this.target4; break; + } + if (s != "") + { + // Flag to set func_clientwall state + // 1 == deactivate, 2 == activate, 0 == do nothing + int aw_flag = this.antiwall_flag; + for(entity t = NULL; (t = find(t, targetname, s)); ) + { + if(t.use && (t.sub_target_used != time || !preventReuse)) + { + if(this.target_random) + { + RandomSelection_AddEnt(t, 1, 0); + } + else + { + if (t.classname == "func_clientwall" || t.classname == "func_clientillusionary") + t.antiwall_flag = aw_flag; + + t.use(t, actor, this); + if(preventReuse) + t.sub_target_used = time; + } + } + } + } + } + + if(this.target_random && RandomSelection_chosen_ent) + { + RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this); + if(preventReuse) + RandomSelection_chosen_ent.sub_target_used = time; + } +} + +void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); } +void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); } diff --git a/qcsrc/common/mapobjects/triggers.qh b/qcsrc/common/mapobjects/triggers.qh new file mode 100644 index 000000000..bfb32696a --- /dev/null +++ b/qcsrc/common/mapobjects/triggers.qh @@ -0,0 +1,49 @@ +#pragma once +#include "defs.qh" + +.bool pushable; + +.float antiwall_flag; // Variable to define what to do with func_clientwall +// 0 == do nothing, 1 == deactivate, 2 == activate + +.float height; + +#define IFTARGETED if(this.targetname && this.targetname != "") + +.float lip; + +// used elsewhere (will fix) +#ifdef SVQC +void trigger_common_write(entity this, bool withtarget); + +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); + +void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger); + +void generic_setactive(entity this, int act); +// generic methods for netlinked entities +void generic_netlinked_reset(entity this); +void generic_netlinked_setactive(entity this, int act); +// WARNING: DON'T USE, ONLY TO KEEP COMPATIBILITY BECAUSE OF SWITCH FROM .state TO .alive!!!! +void generic_netlinked_legacy_use(entity this, entity actor, entity trigger); +#endif + +.float sub_target_used; + +.float volume, atten; + +.vector dest; + +void FixSize(entity e); + +#ifdef CSQC +void trigger_common_read(entity this, bool withtarget); +void trigger_remove_generic(entity this); + +.float active; +.string target; +.string targetname; +#endif diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index ccd10ee45..718838cde 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -18,7 +18,7 @@ #include "../vehicles/all.qh" #include #include -#include "../triggers/triggers.qh" +#include "../mapobjects/triggers.qh" #include #include #include diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc index 5afb96507..4be441dc1 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc @@ -1,6 +1,6 @@ #include "sv_buffs.qh" -#include +#include #include void buffs_DelayedInit(entity this); diff --git a/qcsrc/common/physics/player.qc b/qcsrc/common/physics/player.qc index 53765f419..04dc20770 100644 --- a/qcsrc/common/physics/player.qc +++ b/qcsrc/common/physics/player.qc @@ -1,11 +1,11 @@ #include "player.qh" -#include "../triggers/include.qh" +#include "../mapobjects/include.qh" #include "../viewloc.qh" #ifdef SVQC #include -#include "../triggers/trigger/viewloc.qh" +#include "../mapobjects/trigger/viewloc.qh" // client side physics bool Physics_Valid(string thecvar) diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index 8be48b530..5a849ee14 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -15,7 +15,7 @@ #include "constants.qh" #include #include - #include "triggers/subs.qh" + #include "mapobjects/subs.qh" #include "util.qh" #include diff --git a/qcsrc/common/triggers/_mod.inc b/qcsrc/common/triggers/_mod.inc deleted file mode 100644 index 9b327de55..000000000 --- a/qcsrc/common/triggers/_mod.inc +++ /dev/null @@ -1,11 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include - -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/_mod.qh b/qcsrc/common/triggers/_mod.qh deleted file mode 100644 index d3bb2dc3a..000000000 --- a/qcsrc/common/triggers/_mod.qh +++ /dev/null @@ -1,11 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include - -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/defs.qh b/qcsrc/common/triggers/defs.qh deleted file mode 100644 index 45afb51f9..000000000 --- a/qcsrc/common/triggers/defs.qh +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -//----------- -// SPAWNFLAGS -//----------- -const int START_ENABLED = BIT(0); -const int START_DISABLED = BIT(0); -const int ALL_ENTITIES = BIT(1); -const int ON_MAPLOAD = BIT(1); -const int INVERT_TEAMS = BIT(2); -const int CRUSH = BIT(2); -const int NOSPLASH = BIT(8); // generic anti-splashdamage spawnflag -const int ONLY_PLAYERS = BIT(14); - -// triggers -const int SPAWNFLAG_NOMESSAGE = BIT(0); -const int SPAWNFLAG_NOTOUCH = BIT(0); - -//---------- -// SENDFLAGS -//---------- -const int SF_TRIGGER_INIT = BIT(0); -const int SF_TRIGGER_UPDATE = BIT(1); -const int SF_TRIGGER_RESET = BIT(2); - -//---------------- -// STATES & ACTIVE -//---------------- -#ifdef CSQC -// this stuff is defined in the server side engine VM, so we must define it separately here -const int STATE_TOP = 0; -const int STATE_BOTTOM = 1; -const int STATE_UP = 2; -const int STATE_DOWN = 3; - -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 diff --git a/qcsrc/common/triggers/func/_mod.inc b/qcsrc/common/triggers/func/_mod.inc deleted file mode 100644 index 675e3689b..000000000 --- a/qcsrc/common/triggers/func/_mod.inc +++ /dev/null @@ -1,19 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/func/_mod.qh b/qcsrc/common/triggers/func/_mod.qh deleted file mode 100644 index fb179a42c..000000000 --- a/qcsrc/common/triggers/func/_mod.qh +++ /dev/null @@ -1,19 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/func/bobbing.qc b/qcsrc/common/triggers/func/bobbing.qc deleted file mode 100644 index b647e15a8..000000000 --- a/qcsrc/common/triggers/func/bobbing.qc +++ /dev/null @@ -1,85 +0,0 @@ -#include "bobbing.qh" -#ifdef SVQC -.float height; -void func_bobbing_controller_think(entity this) -{ - vector v; - this.nextthink = time + 0.1; - - if(this.owner.active != ACTIVE_ACTIVE) - { - this.owner.velocity = '0 0 0'; - return; - } - - // calculate sinewave using makevectors - makevectors((this.nextthink * this.owner.cnt + this.owner.phase * 360) * '0 1 0'); - v = this.owner.destvec + this.owner.movedir * v_forward_y; - if(this.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed - // * 10 so it will arrive in 0.1 sec - this.owner.velocity = (v - this.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. -*/ -spawnfunc(func_bobbing) -{ - entity controller; - if (this.noise != "") - { - precache_sound(this.noise); - soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } - if (!this.speed) - this.speed = 4; - if (!this.height) - this.height = 32; - // center of bobbing motion - this.destvec = this.origin; - // time scale to get degrees - this.cnt = 360 / this.speed; - - this.active = ACTIVE_ACTIVE; - - // damage when blocked - setblocked(this, generic_plat_blocked); - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - // how far to bob - if (this.spawnflags & BOBBING_XAXIS) - this.movedir = '1 0 0' * this.height; - else if (this.spawnflags & BOBBING_YAXIS) - this.movedir = '0 1 0' * this.height; - else // Z - this.movedir = '0 0 1' * this.height; - - if (!InitMovingBrushTrigger(this)) - return; - - // wait for targets to spawn - controller = new(func_bobbing_controller); - controller.owner = this; - controller.nextthink = time + 1; - setthink(controller, func_bobbing_controller_think); - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); - - // Savage: Reduce bandwith, critical on e.g. nexdm02 - this.effects |= EF_LOWPRECISION; - - // TODO make a reset function for this one -} -#endif diff --git a/qcsrc/common/triggers/func/bobbing.qh b/qcsrc/common/triggers/func/bobbing.qh deleted file mode 100644 index 58f7bb714..000000000 --- a/qcsrc/common/triggers/func/bobbing.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - - -const int BOBBING_XAXIS = BIT(0); -const int BOBBING_YAXIS = BIT(1); diff --git a/qcsrc/common/triggers/func/breakable.qc b/qcsrc/common/triggers/func/breakable.qc deleted file mode 100644 index d09ccd5e0..000000000 --- a/qcsrc/common/triggers/func/breakable.qc +++ /dev/null @@ -1,374 +0,0 @@ -#include "breakable.qh" -#ifdef SVQC - -#include -#include -#include -#include -#include -#include - -.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: -// START_DISABLED: needs to be triggered to activate -// BREAKABLE_INDICATE_DAMAGE: indicate damage -// BREAKABLE_NODAMAGE: don't take direct damage (needs to be triggered to 'explode', then triggered again to restore) -// NOSPLASH: don't take splash 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 this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force); - -// -// func_breakable -// - basically func_assault_destructible for general gameplay use -// -void LaunchDebris (entity this, string debrisname, vector force) -{ - entity dbr = spawn(); - vector org = this.absmin - + '1 0 0' * random() * (this.absmax.x - this.absmin.x) - + '0 1 0' * random() * (this.absmax.y - this.absmin.y) - + '0 0 1' * random() * (this.absmax.z - this.absmin.z); - setorigin(dbr, org); - _setmodel (dbr, debrisname ); - dbr.skin = this.debrisskin; - dbr.colormap = this.colormap; // inherit team colors - dbr.owner = this; // do not be affected by our own explosion - set_movetype(dbr, this.debrismovetype); - dbr.solid = this.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 = this.debrisvelocity.x + this.debrisvelocityjitter.x * crandom(); - dbr.velocity_y = this.debrisvelocity.y + this.debrisvelocityjitter.y * crandom(); - dbr.velocity_z = this.debrisvelocity.z + this.debrisvelocityjitter.z * crandom(); - dbr.velocity = dbr.velocity + force * this.debrisdamageforcescale; - dbr.angles = this.angles; - dbr.avelocity_x = random()*this.debrisavelocityjitter.x; - dbr.avelocity_y = random()*this.debrisavelocityjitter.y; - dbr.avelocity_z = random()*this.debrisavelocityjitter.z; - dbr.damageforcescale = this.debrisdamageforcescale; - if(dbr.damageforcescale) - dbr.takedamage = DAMAGE_YES; - SUB_SetFade(dbr, time + this.debristime + crandom() * this.debristimejitter, this.debrisfadetime); -} - -void func_breakable_colormod(entity this) -{ - float h; - if (!(this.spawnflags & BREAKABLE_INDICATE_DAMAGE)) - return; - h = this.health / this.max_health; - if(h < 0.25) - this.colormod = '1 0 0'; - else if(h <= 0.75) - this.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5); - else - this.colormod = '1 1 1'; -} - -void func_breakable_look_destroyed(entity this) -{ - float floorZ; - - if(this.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first - this.dropped_origin = this.origin; - - if(this.mdl_dead == "") - this.effects |= EF_NODRAW; - else { - if (this.origin == '0 0 0') { // probably no origin brush, so don't spawn in the middle of the map.. - floorZ = this.absmin.z; - setorigin(this, ((this.absmax + this.absmin) * 0.5)); - this.origin_z = floorZ; - } - _setmodel(this, this.mdl_dead); - ApplyMinMaxScaleAngles(this); - this.effects &= ~EF_NODRAW; - } - - this.solid = SOLID_NOT; -} - -void func_breakable_look_restore(entity this) -{ - _setmodel(this, this.mdl); - ApplyMinMaxScaleAngles(this); - this.effects &= ~EF_NODRAW; - - if(this.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow - setorigin(this, this.dropped_origin); - - this.solid = SOLID_BSP; -} - -void func_breakable_behave_destroyed(entity this) -{ - this.health = this.max_health; - this.takedamage = DAMAGE_NO; - if(this.bot_attack) - IL_REMOVE(g_bot_targets, this); - this.bot_attack = false; - this.event_damage = func_null; - this.state = STATE_BROKEN; - if(this.spawnflags & BREAKABLE_NODAMAGE) - this.use = func_null; - func_breakable_colormod(this); - if (this.noise1) - stopsound (this, CH_TRIGGER_SINGLE); -} - -void func_breakable_think(entity this) -{ - this.nextthink = time; - CSQCMODEL_AUTOUPDATE(this); -} - -void func_breakable_destroy(entity this, entity actor, entity trigger); -void func_breakable_behave_restore(entity this) -{ - this.health = this.max_health; - if(this.sprite) - { - WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.sprite, this.health); - } - if(!(this.spawnflags & BREAKABLE_NODAMAGE)) - { - this.takedamage = DAMAGE_AIM; - if(!this.bot_attack) - IL_PUSH(g_bot_targets, this); - this.bot_attack = true; - this.event_damage = func_breakable_damage; - } - if(this.spawnflags & BREAKABLE_NODAMAGE) - this.use = func_breakable_destroy; // don't need to set it usually, as .use isn't reset - this.state = STATE_ALIVE; - //this.nextthink = 0; // cancel auto respawn - setthink(this, func_breakable_think); - this.nextthink = time + 0.1; - func_breakable_colormod(this); - if (this.noise1) - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); -} - -void func_breakable_init_for_player(entity this, entity player) -{ - if (this.noise1 && this.state == STATE_ALIVE && IS_REAL_CLIENT(player)) - { - msg_entity = player; - soundto (MSG_ONE, this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - } -} - -void func_breakable_destroyed(entity this) -{ - func_breakable_look_destroyed(this); - func_breakable_behave_destroyed(this); -} - -void func_breakable_restore(entity this, entity actor, entity trigger) -{ - func_breakable_look_restore(this); - func_breakable_behave_restore(this); -} - -void func_breakable_restore_self(entity this) -{ - func_breakable_restore(this, NULL, NULL); -} - -vector debrisforce; // global, set before calling this -void func_breakable_destroy(entity this, entity actor, entity trigger) -{ - float n, i; - string oldmsg; - - entity act = this.owner; - this.owner = NULL; // set by W_PrepareExplosionByDamage - - // now throw around the debris - n = tokenize_console(this.debris); - for(i = 0; i < n; ++i) - LaunchDebris(this, argv(i), debrisforce); - - func_breakable_destroyed(this); - - if(this.noise) - _sound (this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - - if(this.dmg) - RadiusDamage(this, act, this.dmg, this.dmg_edge, this.dmg_radius, this, NULL, this.dmg_force, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, NULL); - - if(this.cnt) // TODO - __pointparticles(this.cnt, this.absmin * 0.5 + this.absmax * 0.5, '0 0 0', this.count); - - if(this.respawntime) - { - CSQCMODEL_AUTOUPDATE(this); - setthink(this, func_breakable_restore_self); - this.nextthink = time + this.respawntime + crandom() * this.respawntimejitter; - } - - oldmsg = this.message; - this.message = ""; - SUB_UseTargets(this, act, trigger); - this.message = oldmsg; -} - -void func_breakable_destroy_self(entity this) -{ - func_breakable_destroy(this, NULL, NULL); -} - -void func_breakable_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(this.state == STATE_BROKEN) - return; - if(this.spawnflags & NOSPLASH) - if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) - return; - if(this.team) - if(attacker.team == this.team) - return; - this.pain_finished = time; - this.health = this.health - damage; - if(this.sprite) - { - WaypointSprite_Ping(this.sprite); - WaypointSprite_UpdateHealth(this.sprite, this.health); - } - func_breakable_colormod(this); - - if(this.health <= 0) - { - debrisforce = force; - - this.takedamage = DAMAGE_NO; - this.event_damage = func_null; - - if(IS_CLIENT(attacker)) //&& this.classname == "func_assault_destructible") - { - this.owner = attacker; - this.realowner = attacker; - } - - // do not explode NOW but in the NEXT FRAME! - // because recursive calls to RadiusDamage are not allowed - this.nextthink = time; - CSQCMODEL_AUTOUPDATE(this); - setthink(this, func_breakable_destroy_self); - } -} - -void func_breakable_reset(entity this) -{ - this.team = this.team_saved; - func_breakable_look_restore(this); - if(this.spawnflags & START_DISABLED) - func_breakable_behave_destroyed(this); - else - func_breakable_behave_restore(this); -} - -// destructible walls that can be used to trigger target_objective_decrease -spawnfunc(func_breakable) -{ - float n, i; - if(!this.health) - this.health = 100; - this.max_health = this.health; - - // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway - if(!this.debrismovetype) this.debrismovetype = MOVETYPE_BOUNCE; - if(!this.debrissolid) this.debrissolid = SOLID_NOT; - if(this.debrisvelocity == '0 0 0') this.debrisvelocity = '0 0 140'; - if(this.debrisvelocityjitter == '0 0 0') this.debrisvelocityjitter = '70 70 70'; - if(this.debrisavelocityjitter == '0 0 0') this.debrisavelocityjitter = '600 600 600'; - if(!this.debristime) this.debristime = 3.5; - if(!this.debristimejitter) this.debristime = 2.5; - - if(this.mdl != "") - this.cnt = _particleeffectnum(this.mdl); - if(this.count == 0) - this.count = 1; - - if(this.message == "") - this.message = "got too close to an explosion"; - if(this.message2 == "") - this.message2 = "was pushed into an explosion by"; - if(!this.dmg_radius) - this.dmg_radius = 150; - if(!this.dmg_force) - this.dmg_force = 200; - - this.mdl = this.model; - SetBrushEntityModel(this); - - if(this.spawnflags & BREAKABLE_NODAMAGE) - this.use = func_breakable_destroy; - else - this.use = func_breakable_restore; - - if(this.spawnflags & BREAKABLE_NODAMAGE) - { - this.takedamage = DAMAGE_NO; - this.event_damage = func_null; - this.bot_attack = false; - } - - // precache all the models - if (this.mdl_dead) - precache_model(this.mdl_dead); - n = tokenize_console(this.debris); - for(i = 0; i < n; ++i) - precache_model(argv(i)); - if(this.noise) - precache_sound(this.noise); - if(this.noise1) - precache_sound(this.noise1); - - this.team_saved = this.team; - IL_PUSH(g_saved_team, this); - this.dropped_origin = this.origin; - - this.reset = func_breakable_reset; - this.reset(this); - - IL_PUSH(g_initforplayer, this); - this.init_for_player = func_breakable_init_for_player; - - CSQCMODEL_AUTOINIT(this); -} - -// for use in maps with a "model" key set -spawnfunc(misc_breakablemodel) { - spawnfunc_func_breakable(this); -} -#endif diff --git a/qcsrc/common/triggers/func/breakable.qh b/qcsrc/common/triggers/func/breakable.qh deleted file mode 100644 index 0efbcfaed..000000000 --- a/qcsrc/common/triggers/func/breakable.qh +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - - -const int BREAKABLE_INDICATE_DAMAGE = BIT(1); -const int BREAKABLE_NODAMAGE = BIT(2); - -const int STATE_ALIVE = 0; -const int STATE_BROKEN = 1; - -#ifdef SVQC -spawnfunc(func_breakable); -#endif diff --git a/qcsrc/common/triggers/func/button.qc b/qcsrc/common/triggers/func/button.qc deleted file mode 100644 index 28e6481c8..000000000 --- a/qcsrc/common/triggers/func/button.qc +++ /dev/null @@ -1,170 +0,0 @@ -#include "button.qh" -#ifdef SVQC -// button and multiple button - -void button_wait(entity this); -void button_return(entity this); - -void button_wait(entity this) -{ - this.state = STATE_TOP; - if(this.wait >= 0) - { - this.nextthink = this.ltime + this.wait; - setthink(this, button_return); - } - SUB_UseTargets(this, this.enemy, NULL); - this.frame = 1; // use alternate textures -} - -void button_done(entity this) -{ - this.state = STATE_BOTTOM; -} - -void button_return(entity this) -{ - this.state = STATE_DOWN; - SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, button_done); - this.frame = 0; // use normal textures - if (this.health) - this.takedamage = DAMAGE_YES; // can be shot again -} - - -void button_blocked(entity this, entity blocker) -{ - // do nothing, just don't come all the way back out -} - - -void button_fire(entity this) -{ - this.health = this.max_health; - this.takedamage = DAMAGE_NO; // will be reset upon return - - if (this.state == STATE_UP || this.state == STATE_TOP) - return; - - if (this.noise != "") - _sound (this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - - this.state = STATE_UP; - SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, button_wait); -} - -void button_reset(entity this) -{ - this.health = this.max_health; - setorigin(this, this.pos1); - this.frame = 0; // use normal textures - this.state = STATE_BOTTOM; - this.velocity = '0 0 0'; - setthink(this, func_null); - this.nextthink = 0; - if (this.health) - this.takedamage = DAMAGE_YES; // can be shot again -} - -void button_use(entity this, entity actor, entity trigger) -{ - if(this.active != ACTIVE_ACTIVE) - return; - - this.enemy = actor; - button_fire(this); -} - -void button_touch(entity this, entity toucher) -{ - if (!toucher) - return; - if (!toucher.iscreature) - return; - if(toucher.velocity * this.movedir < 0) - return; - this.enemy = toucher; - if (toucher.owner) - this.enemy = toucher.owner; - button_fire (this); -} - -void button_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(this.spawnflags & NOSPLASH) - if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) - return; - if (this.spawnflags & BUTTON_DONTACCUMULATEDMG) - { - if (this.health <= damage) - { - this.enemy = attacker; - button_fire(this); - } - } - else - { - this.health = this.health - damage; - if (this.health <= 0) - { - this.enemy = attacker; - button_fire(this); - } - } -} - - -/*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 -"noise" sound that is played when the button is activated -*/ -spawnfunc(func_button) -{ - SetMovedir(this); - - if (!InitMovingBrushTrigger(this)) - return; - this.effects |= EF_LOWPRECISION; - - setblocked(this, button_blocked); - this.use = button_use; - -// if (this.health == 0) // all buttons are now shootable -// this.health = 10; - if (this.health) - { - this.max_health = this.health; - this.event_damage = button_damage; - this.takedamage = DAMAGE_YES; - } - else - settouch(this, button_touch); - - if (!this.speed) - this.speed = 40; - if (!this.wait) - this.wait = 1; - if (!this.lip) - this.lip = 4; - - if(this.noise != "") - precache_sound(this.noise); - - this.active = ACTIVE_ACTIVE; - - this.pos1 = this.origin; - this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip); - this.flags |= FL_NOTARGET; - - this.reset = button_reset; - - button_reset(this); -} -#endif diff --git a/qcsrc/common/triggers/func/button.qh b/qcsrc/common/triggers/func/button.qh deleted file mode 100644 index 86a0fc918..000000000 --- a/qcsrc/common/triggers/func/button.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - - -const int BUTTON_DONTACCUMULATEDMG = BIT(7); diff --git a/qcsrc/common/triggers/func/conveyor.qc b/qcsrc/common/triggers/func/conveyor.qc deleted file mode 100644 index 9ad326cfa..000000000 --- a/qcsrc/common/triggers/func/conveyor.qc +++ /dev/null @@ -1,178 +0,0 @@ -#include "conveyor.qh" -REGISTER_NET_LINKED(ENT_CLIENT_CONVEYOR) - -void conveyor_think(entity this) -{ -#ifdef CSQC - // TODO: check if this is what is causing the glitchiness when switching between them - float dt = time - this.move_time; - this.move_time = time; - if(dt <= 0) { return; } -#endif - - // set myself as current conveyor where possible - IL_EACH(g_conveyed, it.conveyor == this, - { - it.conveyor = NULL; - IL_REMOVE(g_conveyed, it); - }); - - if(this.active == ACTIVE_ACTIVE) - { - FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, it.conveyor.active == ACTIVE_NOT && isPushable(it), - { - vector emin = it.absmin; - vector emax = it.absmax; - if(this.solid == SOLID_BSP) - { - emin -= '1 1 1'; - emax += '1 1 1'; - } - if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick - if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate - { - if(!it.conveyor) - IL_PUSH(g_conveyed, it); - it.conveyor = this; - } - }); - - IL_EACH(g_conveyed, it.conveyor == this, - { - if(IS_CLIENT(it)) // doing it via velocity has quite some advantages - continue; // done in SV_PlayerPhysics continue; - - setorigin(it, it.origin + this.movedir * PHYS_INPUT_FRAMETIME); - move_out_of_solid(it); -#ifdef SVQC - UpdateCSQCProjectile(it); -#endif - /* - // stupid conveyor code - tracebox(it.origin, it.mins, it.maxs, it.origin + this.movedir * sys_frametime, MOVE_NORMAL, it); - if(trace_fraction > 0) - setorigin(it, trace_endpos); - */ - }); - } - -#ifdef SVQC - this.nextthink = time; -#endif -} - -#ifdef SVQC - -bool conveyor_send(entity this, entity to, int sendflags) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_CONVEYOR); - WriteByte(MSG_ENTITY, sendflags); - - if(sendflags & SF_TRIGGER_INIT) - { - WriteByte(MSG_ENTITY, this.warpzone_isboxy); - WriteVector(MSG_ENTITY, this.origin); - - WriteVector(MSG_ENTITY, this.mins); - WriteVector(MSG_ENTITY, this.maxs); - - WriteVector(MSG_ENTITY, this.movedir); - - WriteByte(MSG_ENTITY, this.speed); - WriteByte(MSG_ENTITY, this.active); - - WriteString(MSG_ENTITY, this.targetname); - WriteString(MSG_ENTITY, this.target); - } - - if(sendflags & SF_TRIGGER_UPDATE) - WriteByte(MSG_ENTITY, this.active); - - return true; -} - -void conveyor_init(entity this) -{ - if (!this.speed) this.speed = 200; - this.movedir *= this.speed; - setthink(this, conveyor_think); - this.nextthink = time; - this.setactive = generic_netlinked_setactive; - IFTARGETED - { - // backwards compatibility - this.use = generic_netlinked_legacy_use; - } - this.reset = generic_netlinked_reset; - this.reset(this); - - FixSize(this); - - Net_LinkEntity(this, 0, false, conveyor_send); - - this.SendFlags |= SF_TRIGGER_INIT; -} - -spawnfunc(trigger_conveyor) -{ - SetMovedir(this); - EXACTTRIGGER_INIT; - conveyor_init(this); -} - -spawnfunc(func_conveyor) -{ - SetMovedir(this); - InitMovingBrushTrigger(this); - set_movetype(this, MOVETYPE_NONE); - conveyor_init(this); -} - -#elif defined(CSQC) - -void conveyor_draw(entity this) { conveyor_think(this); } - -void conveyor_init(entity this, bool isnew) -{ - if(isnew) - IL_PUSH(g_drawables, this); - this.draw = conveyor_draw; - this.drawmask = MASK_NORMAL; - - set_movetype(this, MOVETYPE_NONE); - this.model = ""; - this.solid = SOLID_TRIGGER; - this.move_time = time; -} - -NET_HANDLE(ENT_CLIENT_CONVEYOR, bool isnew) -{ - int sendflags = ReadByte(); - - if(sendflags & SF_TRIGGER_INIT) - { - this.warpzone_isboxy = ReadByte(); - this.origin = ReadVector(); - setorigin(this, this.origin); - - this.mins = ReadVector(); - this.maxs = ReadVector(); - setsize(this, this.mins, this.maxs); - - this.movedir = ReadVector(); - - this.speed = ReadByte(); - this.active = ReadByte(); - - this.targetname = strzone(ReadString()); - this.target = strzone(ReadString()); - - conveyor_init(this, isnew); - } - - if(sendflags & SF_TRIGGER_UPDATE) - this.active = ReadByte(); - - return true; -} -#endif diff --git a/qcsrc/common/triggers/func/conveyor.qh b/qcsrc/common/triggers/func/conveyor.qh deleted file mode 100644 index 22b674ff3..000000000 --- a/qcsrc/common/triggers/func/conveyor.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include "../defs.qh" - -IntrusiveList g_conveyed; -STATIC_INIT(g_conveyed) { g_conveyed = IL_NEW(); } diff --git a/qcsrc/common/triggers/func/door.qc b/qcsrc/common/triggers/func/door.qc deleted file mode 100644 index c19041aa0..000000000 --- a/qcsrc/common/triggers/func/door.qc +++ /dev/null @@ -1,801 +0,0 @@ -#include "door.qh" -#include "door_rotating.qh" -/* - -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(entity this); -void door_go_up(entity this, entity actor, entity trigger); - -void door_blocked(entity this, entity blocker) -{ - if((this.spawnflags & DOOR_CRUSH) -#ifdef SVQC - && (blocker.takedamage != DAMAGE_NO) -#elif defined(CSQC) - && !IS_DEAD(blocker) -#endif - ) - { // KIll Kill Kill!! -#ifdef SVQC - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - } - else - { -#ifdef SVQC - if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? - Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - - // don't change direction for dead or dying stuff - if(IS_DEAD(blocker) -#ifdef SVQC - && (blocker.takedamage == DAMAGE_NO) -#endif - ) - { - if (this.wait >= 0) - { - if (this.state == STATE_DOWN) - { - if (this.classname == "door") - door_go_up(this, NULL, NULL); - else - door_rotating_go_up(this, blocker); - } - else - { - if (this.classname == "door") - door_go_down(this); - else - door_rotating_go_down(this); - } - } - } -#ifdef SVQC - else - { - //gib dying stuff just to make sure - if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - } -#endif - } -} - -void door_hit_top(entity this) -{ - if (this.noise1 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = STATE_TOP; - if (this.spawnflags & DOOR_TOGGLE) - return; // don't come down automatically - if (this.classname == "door") - { - setthink(this, door_go_down); - } else - { - setthink(this, door_rotating_go_down); - } - this.nextthink = this.ltime + this.wait; -} - -void door_hit_bottom(entity this) -{ - if (this.noise1 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = STATE_BOTTOM; -} - -void door_go_down(entity this) -{ - if (this.noise2 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - if (this.max_health) - { - this.takedamage = DAMAGE_YES; - this.health = this.max_health; - } - - this.state = STATE_DOWN; - SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_hit_bottom); -} - -void door_go_up(entity this, entity actor, entity trigger) -{ - if (this.state == STATE_UP) - return; // already going up - - if (this.state == STATE_TOP) - { // reset top wait time - this.nextthink = this.ltime + this.wait; - return; - } - - if (this.noise2 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - this.state = STATE_UP; - SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_hit_top); - - string oldmessage; - oldmessage = this.message; - this.message = ""; - SUB_UseTargets(this, actor, trigger); - this.message = oldmessage; -} - - -/* -============================================================================= - -ACTIVATION FUNCTIONS - -============================================================================= -*/ - -bool door_check_keys(entity door, entity player) -{ - if(door.owner) - door = door.owner; - - // no key needed - if(!door.itemkeys) - return true; - - // this door require a key - // only a player can have a key - if(!IS_PLAYER(player)) - return false; - - entity store = player; -#ifdef SVQC - store = PS(player); -#endif - int valid = (door.itemkeys & store.itemkeys); - door.itemkeys &= ~valid; // only some of the needed keys were given - - if(!door.itemkeys) - { -#ifdef SVQC - play2(player, door.noise); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_UNLOCKED); -#endif - return true; - } - - if(!valid) - { -#ifdef SVQC - if(player.key_door_messagetime <= time) - { - play2(player, door.noise3); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys)); - player.key_door_messagetime = time + 2; - } -#endif - return false; - } - - // door needs keys the player doesn't have -#ifdef SVQC - if(player.key_door_messagetime <= time) - { - play2(player, door.noise3); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys)); - player.key_door_messagetime = time + 2; - } -#endif - - return false; -} - -void door_fire(entity this, entity actor, entity trigger) -{ - if (this.owner != this) - objerror (this, "door_fire: this.owner != this"); - - if (this.spawnflags & DOOR_TOGGLE) - { - if (this.state == STATE_UP || this.state == STATE_TOP) - { - entity e = this; - do { - if (e.classname == "door") { - door_go_down(e); - } else { - door_rotating_go_down(e); - } - e = e.enemy; - } while ((e != this) && (e != NULL)); - return; - } - } - -// trigger all paired doors - entity e = this; - do { - if (e.classname == "door") { - door_go_up(e, actor, trigger); - } else { - // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction - if ((e.spawnflags & DOOR_ROTATING_BIDIR) && trigger.trigger_reverse!=0 && e.lip != 666 && e.state == STATE_BOTTOM) { - e.lip = 666; // e.lip is used to remember reverse opening direction for door_rotating - e.pos2 = '0 0 0' - e.pos2; - } - // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side - if (!((e.spawnflags & DOOR_ROTATING_BIDIR) && (e.spawnflags & DOOR_ROTATING_BIDIR_IN_DOWN) && e.state == STATE_DOWN - && (((e.lip == 666) && (trigger.trigger_reverse == 0)) || ((e.lip != 666) && (trigger.trigger_reverse != 0))))) - { - door_rotating_go_up(e, trigger); - } - } - e = e.enemy; - } while ((e != this) && (e != NULL)); -} - -void door_use(entity this, entity actor, entity trigger) -{ - //dprint("door_use (model: ");dprint(this.model);dprint(")\n"); - - if (this.owner) - door_fire(this.owner, actor, trigger); -} - -void door_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(this.spawnflags & NOSPLASH) - if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) - return; - this.health = this.health - damage; - - if (this.itemkeys) - { - // don't allow opening doors through damage if keys are required - return; - } - - if (this.health <= 0) - { - this.owner.health = this.owner.max_health; - this.owner.takedamage = DAMAGE_NO; // wil be reset upon return - door_use(this.owner, NULL, NULL); - } -} - -.float door_finished; - -/* -================ -door_touch - -Prints messages -================ -*/ - -void door_touch(entity this, entity toucher) -{ - if (!IS_PLAYER(toucher)) - return; - if (this.owner.door_finished > time) - return; - - this.owner.door_finished = time + 2; - -#ifdef SVQC - if (!(this.owner.dmg) && (this.owner.message != "")) - { - if (IS_CLIENT(toucher)) - centerprint(toucher, this.owner.message); - play2(toucher, this.owner.noise); - } -#endif -} - -void door_generic_plat_blocked(entity this, entity blocker) -{ - if((this.spawnflags & DOOR_CRUSH) && (blocker.takedamage != DAMAGE_NO)) { // Kill Kill Kill!! -#ifdef SVQC - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - } - else - { - -#ifdef SVQC - if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? - Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - - //Dont chamge direction for dead or dying stuff - if(IS_DEAD(blocker) && (blocker.takedamage == DAMAGE_NO)) - { - if (this.wait >= 0) - { - if (this.state == STATE_DOWN) - door_rotating_go_up (this, blocker); - else - door_rotating_go_down (this); - } - } -#ifdef SVQC - else - { - //gib dying stuff just to make sure - if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - } -#endif - } -} - -/* -========================================= -door trigger - -Spawned if a door lacks a real activator -========================================= -*/ - -void door_trigger_touch(entity this, entity toucher) -{ - if (toucher.health < 1) -#ifdef SVQC - if (!((toucher.iscreature || (toucher.flags & FL_PROJECTILE)) && !IS_DEAD(toucher))) -#elif defined(CSQC) - if(!((IS_CLIENT(toucher) || toucher.classname == "csqcprojectile") && !IS_DEAD(toucher))) -#endif - return; - - if (time < this.door_finished) - return; - - // check if door is locked - if (!door_check_keys(this, toucher)) - return; - - this.door_finished = time + 1; - - door_use(this.owner, toucher, NULL); -} - -void door_spawnfield(entity this, vector fmins, vector fmaxs) -{ - entity trigger; - vector t1 = fmins, t2 = fmaxs; - - trigger = new(doortriggerfield); - set_movetype(trigger, MOVETYPE_NONE); - trigger.solid = SOLID_TRIGGER; - trigger.owner = this; -#ifdef SVQC - settouch(trigger, door_trigger_touch); -#endif - - setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); -} - - -/* -============= -LinkDoors - - -============= -*/ - -entity LinkDoors_nextent(entity cur, entity near, entity pass) -{ - while((cur = find(cur, classname, pass.classname)) && ((cur.spawnflags & DOOR_DONT_LINK) || cur.enemy)) - { - } - return cur; -} - -bool 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; -} - -#ifdef SVQC -void door_link(); -#endif -void LinkDoors(entity this) -{ - entity t; - vector cmins, cmaxs; - -#ifdef SVQC - door_link(); -#endif - - if (this.enemy) - return; // already linked by another door - if (this.spawnflags & DOOR_DONT_LINK) - { - this.owner = this.enemy = this; - - if (this.health) - return; - IFTARGETED - return; - if (this.items) - return; - - door_spawnfield(this, this.absmin, this.absmax); - - return; // don't want to link this door - } - - FindConnectedComponent(this, enemy, LinkDoors_nextent, LinkDoors_isconnected, this); - - // set owner, and make a loop of the chain - LOG_TRACE("LinkDoors: linking doors:"); - for(t = this; ; t = t.enemy) - { - LOG_TRACE(" ", etos(t)); - t.owner = this; - if(t.enemy == NULL) - { - t.enemy = this; - break; - } - } - LOG_TRACE(""); - - // collect health, targetname, message, size - cmins = this.absmin; - cmaxs = this.absmax; - for(t = this; ; t = t.enemy) - { - if(t.health && !this.health) - this.health = t.health; - if((t.targetname != "") && (this.targetname == "")) - this.targetname = t.targetname; - if((t.message != "") && (this.message == "")) - this.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 == this) - break; - } - - // distribute health, targetname, message - for(t = this; t; t = t.enemy) - { - t.health = this.health; - t.targetname = this.targetname; - t.message = this.message; - if(t.enemy == this) - break; - } - - // shootable, or triggered doors just needed the owner/enemy links, - // they don't spawn a field - - if (this.health) - return; - IFTARGETED - return; - if (this.items) - return; - - door_spawnfield(this, cmins, cmaxs); -} - -REGISTER_NET_LINKED(ENT_CLIENT_DOOR) - -#ifdef SVQC -/*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 this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_DOOR); - WriteByte(MSG_ENTITY, sf); - - if(sf & SF_TRIGGER_INIT) - { - WriteString(MSG_ENTITY, this.classname); - WriteByte(MSG_ENTITY, this.spawnflags); - - WriteString(MSG_ENTITY, this.model); - - trigger_common_write(this, true); - - WriteVector(MSG_ENTITY, this.pos1); - WriteVector(MSG_ENTITY, this.pos2); - - WriteVector(MSG_ENTITY, this.size); - - WriteShort(MSG_ENTITY, this.wait); - WriteShort(MSG_ENTITY, this.speed); - WriteByte(MSG_ENTITY, this.lip); - WriteByte(MSG_ENTITY, this.state); - WriteCoord(MSG_ENTITY, this.ltime); - } - - if(sf & SF_TRIGGER_RESET) - { - // client makes use of this, we do not - } - - if(sf & SF_TRIGGER_UPDATE) - { - WriteVector(MSG_ENTITY, this.origin); - - WriteVector(MSG_ENTITY, this.pos1); - WriteVector(MSG_ENTITY, this.pos2); - } - - return true; -} - -void door_link() -{ - // set size now, as everything is loaded - //FixSize(this); - //Net_LinkEntity(this, false, 0, door_send); -} -#endif - -void door_init_startopen(entity this) -{ - setorigin(this, this.pos2); - this.pos2 = this.pos1; - this.pos1 = this.origin; - -#ifdef SVQC - this.SendFlags |= SF_TRIGGER_UPDATE; -#endif -} - -void door_reset(entity this) -{ - setorigin(this, this.pos1); - this.velocity = '0 0 0'; - this.state = STATE_BOTTOM; - setthink(this, func_null); - this.nextthink = 0; - -#ifdef SVQC - this.SendFlags |= SF_TRIGGER_RESET; -#endif -} - -#ifdef SVQC - -// common code for func_door and func_door_rotating spawnfuncs -void door_init_shared(entity this) -{ - this.max_health = this.health; - - // unlock sound - if(this.noise == "") - { - this.noise = "misc/talk.wav"; - } - // door still locked sound - if(this.noise3 == "") - { - this.noise3 = "misc/talk.wav"; - } - precache_sound(this.noise); - precache_sound(this.noise3); - - if((this.dmg || (this.spawnflags & DOOR_CRUSH)) && (this.message == "")) - { - this.message = "was squished"; - } - if((this.dmg || (this.spawnflags & DOOR_CRUSH)) && (this.message2 == "")) - { - this.message2 = "was squished by"; - } - - // TODO: other soundpacks - if (this.sounds > 0) - { - this.noise2 = "plats/medplat1.wav"; - this.noise1 = "plats/medplat2.wav"; - } - - // sound when door stops moving - if(this.noise1 && this.noise1 != "") - { - precache_sound(this.noise1); - } - // sound when door is moving - if(this.noise2 && this.noise2 != "") - { - precache_sound(this.noise2); - } - - if (!this.wait) - { - this.wait = 3; - } - if (!this.lip) - { - this.lip = 8; - } - - this.state = STATE_BOTTOM; - - if (this.health) - { - //this.canteamdamage = true; // TODO - this.takedamage = DAMAGE_YES; - this.event_damage = door_damage; - } - - if (this.items) - { - this.wait = -1; - } -} - -// spawnflags require key (for now only func_door) -spawnfunc(func_door) -{ - // Quake 1 keys compatibility - if (this.spawnflags & SPAWNFLAGS_GOLD_KEY) - this.itemkeys |= ITEM_KEY_BIT(0); - if (this.spawnflags & SPAWNFLAGS_SILVER_KEY) - this.itemkeys |= ITEM_KEY_BIT(1); - - SetMovedir(this); - - if (!InitMovingBrushTrigger(this)) - return; - this.effects |= EF_LOWPRECISION; - this.classname = "door"; - - setblocked(this, door_blocked); - this.use = door_use; - - this.pos1 = this.origin; - this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip); - - if(this.spawnflags & DOOR_NONSOLID) - this.solid = SOLID_NOT; - -// DOOR_START_OPEN is to allow an entity to be lighted in the closed position -// but spawn in the open position - if (this.spawnflags & DOOR_START_OPEN) - InitializeEntity(this, door_init_startopen, INITPRIO_SETLOCATION); - - door_init_shared(this); - - if (!this.speed) - { - this.speed = 100; - } - - settouch(this, door_touch); - -// LinkDoors can't be done until all of the doors have been spawned, so -// the sizes can be detected properly. - InitializeEntity(this, LinkDoors, INITPRIO_LINKDOORS); - - this.reset = door_reset; -} - -#elif defined(CSQC) - -NET_HANDLE(ENT_CLIENT_DOOR, bool isnew) -{ - int sf = ReadByte(); - - if(sf & SF_TRIGGER_INIT) - { - this.classname = strzone(ReadString()); - this.spawnflags = ReadByte(); - - this.mdl = strzone(ReadString()); - _setmodel(this, this.mdl); - - trigger_common_read(this, true); - - this.pos1 = ReadVector(); - this.pos2 = ReadVector(); - - this.size = ReadVector(); - - this.wait = ReadShort(); - this.speed = ReadShort(); - this.lip = ReadByte(); - this.state = ReadByte(); - this.ltime = ReadCoord(); - - this.solid = SOLID_BSP; - set_movetype(this, MOVETYPE_PUSH); - this.use = door_use; - - LinkDoors(this); - - if(this.spawnflags & DOOR_START_OPEN) - door_init_startopen(this); - - this.move_time = time; - set_movetype(this, MOVETYPE_PUSH); - } - - if(sf & SF_TRIGGER_RESET) - { - door_reset(this); - } - - if(sf & SF_TRIGGER_UPDATE) - { - this.origin = ReadVector(); - setorigin(this, this.origin); - - this.pos1 = ReadVector(); - this.pos2 = ReadVector(); - } - return true; -} - -#endif diff --git a/qcsrc/common/triggers/func/door.qh b/qcsrc/common/triggers/func/door.qh deleted file mode 100644 index 181de8b7b..000000000 --- a/qcsrc/common/triggers/func/door.qh +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - - -const int DOOR_START_OPEN = BIT(0); -const int DOOR_DONT_LINK = BIT(2); -const int SPAWNFLAGS_GOLD_KEY = BIT(3); // Quake 1 compat, can only be used with func_door! -const int SPAWNFLAGS_SILVER_KEY = BIT(4); // Quake 1 compat, can only be used with func_door! -const int DOOR_TOGGLE = BIT(5); - -const int DOOR_NONSOLID = BIT(10); -const int DOOR_CRUSH = BIT(11); // can't use CRUSH cause that is the same as DOOR_DONT_LINK - - -#ifdef CSQC -// stuff for preload - -.float door_finished; -#endif diff --git a/qcsrc/common/triggers/func/door_rotating.qc b/qcsrc/common/triggers/func/door_rotating.qc deleted file mode 100644 index 41fd05e57..000000000 --- a/qcsrc/common/triggers/func/door_rotating.qc +++ /dev/null @@ -1,159 +0,0 @@ -#include "door_rotating.qh" -/*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 -*/ - -#ifdef GAMEQC -void door_rotating_hit_top(entity this) -{ - if (this.noise1 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = STATE_TOP; - if (this.spawnflags & DOOR_TOGGLE) - return; // don't come down automatically - setthink(this, door_rotating_go_down); - this.nextthink = this.ltime + this.wait; -} - -void door_rotating_hit_bottom(entity this) -{ - if (this.noise1 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - if (this.lip==666) // this.lip is used to remember reverse opening direction for door_rotating - { - this.pos2 = '0 0 0' - this.pos2; - this.lip = 0; - } - this.state = STATE_BOTTOM; -} - -void door_rotating_go_down(entity this) -{ - if (this.noise2 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - if (this.max_health) - { - this.takedamage = DAMAGE_YES; - this.health = this.max_health; - } - - this.state = STATE_DOWN; - SUB_CalcAngleMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_rotating_hit_bottom); -} - -void door_rotating_go_up(entity this, entity oth) -{ - if (this.state == STATE_UP) - return; // already going up - - if (this.state == STATE_TOP) - { // reset top wait time - this.nextthink = this.ltime + this.wait; - return; - } - if (this.noise2 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - this.state = STATE_UP; - SUB_CalcAngleMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_rotating_hit_top); - - string oldmessage; - oldmessage = this.message; - this.message = ""; - SUB_UseTargets(this, NULL, oth); // TODO: is oth needed here? - this.message = oldmessage; -} -#endif - -#ifdef SVQC -void door_rotating_reset(entity this) -{ - this.angles = this.pos1; - this.avelocity = '0 0 0'; - this.state = STATE_BOTTOM; - setthink(this, func_null); - this.nextthink = 0; -} - -void door_rotating_init_startopen(entity this) -{ - this.angles = this.movedir; - this.pos2 = '0 0 0'; - this.pos1 = this.movedir; -} - -spawnfunc(func_door_rotating) -{ - //if (!this.deathtype) // map makers can override this - // this.deathtype = " got in the way"; - - // I abuse "movedir" for denoting the axis for now - if (this.spawnflags & DOOR_ROTATING_XAXIS) - this.movedir = '0 0 1'; - else if (this.spawnflags & DOOR_ROTATING_YAXIS) - this.movedir = '1 0 0'; - else // Z - this.movedir = '0 1 0'; - - if (this.angles_y==0) this.angles_y = 90; - - this.movedir = this.movedir * this.angles_y; - this.angles = '0 0 0'; - - this.avelocity = this.movedir; - if (!InitMovingBrushTrigger(this)) - return; - this.velocity = '0 0 0'; - //this.effects |= EF_LOWPRECISION; - this.classname = "door_rotating"; - - setblocked(this, door_blocked); - this.use = door_use; - - this.pos1 = '0 0 0'; - this.pos2 = this.movedir; - -// DOOR_START_OPEN is to allow an entity to be lighted in the closed position -// but spawn in the open position - if (this.spawnflags & DOOR_START_OPEN) - InitializeEntity(this, door_rotating_init_startopen, INITPRIO_SETLOCATION); - - door_init_shared(this); - if (!this.speed) - { - this.speed = 50; - } - this.lip = 0; // this.lip is used to remember reverse opening direction for door_rotating - - settouch(this, door_touch); - -// LinkDoors can't be done until all of the doors have been spawned, so -// the sizes can be detected properly. - InitializeEntity(this, LinkDoors, INITPRIO_LINKDOORS); - - this.reset = door_rotating_reset; -} -#endif diff --git a/qcsrc/common/triggers/func/door_rotating.qh b/qcsrc/common/triggers/func/door_rotating.qh deleted file mode 100644 index 5ff572859..000000000 --- a/qcsrc/common/triggers/func/door_rotating.qh +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - - -const int DOOR_ROTATING_BIDIR = BIT(1); -const int DOOR_ROTATING_BIDIR_IN_DOWN = BIT(3); - -const int DOOR_ROTATING_XAXIS = BIT(6); -const int DOOR_ROTATING_YAXIS = BIT(7); - - -#ifdef GAMEQC -void door_rotating_go_down(entity this); -void door_rotating_go_up(entity this, entity oth); -#endif diff --git a/qcsrc/common/triggers/func/door_secret.qc b/qcsrc/common/triggers/func/door_secret.qc deleted file mode 100644 index 78e0dd64e..000000000 --- a/qcsrc/common/triggers/func/door_secret.qc +++ /dev/null @@ -1,266 +0,0 @@ -#include "door_secret.qh" -#ifdef SVQC -void fd_secret_move1(entity this); -void fd_secret_move2(entity this); -void fd_secret_move3(entity this); -void fd_secret_move4(entity this); -void fd_secret_move5(entity this); -void fd_secret_move6(entity this); -void fd_secret_done(entity this); - -void fd_secret_use(entity this, entity actor, entity trigger) -{ - float temp; - string message_save; - - this.health = 10000; - if(!this.bot_attack) - IL_PUSH(g_bot_targets, this); - this.bot_attack = true; - - // exit if still moving around... - if (this.origin != this.oldorigin) - return; - - message_save = this.message; - this.message = ""; // no more message - SUB_UseTargets(this, actor, trigger); // fire all targets / killtargets - this.message = message_save; - - this.velocity = '0 0 0'; - - // Make a sound, wait a little... - - if (this.noise1 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.nextthink = this.ltime + 0.1; - - temp = 1 - (this.spawnflags & DOOR_SECRET_1ST_LEFT); // 1 or -1 - makevectors(this.mangle); - - if (!this.t_width) - { - if (this.spawnflags & DOOR_SECRET_1ST_DOWN) - this.t_width = fabs(v_up * this.size); - else - this.t_width = fabs(v_right * this.size); - } - - if (!this.t_length) - this.t_length = fabs(v_forward * this.size); - - if (this.spawnflags & DOOR_SECRET_1ST_DOWN) - this.dest1 = this.origin - v_up * this.t_width; - else - this.dest1 = this.origin + v_right * (this.t_width * temp); - - this.dest2 = this.dest1 + v_forward * this.t_length; - SUB_CalcMove(this, this.dest1, TSPEED_LINEAR, this.speed, fd_secret_move1); - if (this.noise2 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); -} - -void fd_secret_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - fd_secret_use(this, NULL, NULL); -} - -// Wait after first movement... -void fd_secret_move1(entity this) -{ - this.nextthink = this.ltime + 1.0; - setthink(this, fd_secret_move2); - if (this.noise3 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); -} - -// Start moving sideways w/sound... -void fd_secret_move2(entity this) -{ - if (this.noise2 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - SUB_CalcMove(this, this.dest2, TSPEED_LINEAR, this.speed, fd_secret_move3); -} - -// Wait here until time to go back... -void fd_secret_move3(entity this) -{ - if (this.noise3 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); - if (!(this.spawnflags & DOOR_SECRET_OPEN_ONCE) && this.wait >= 0) - { - this.nextthink = this.ltime + this.wait; - setthink(this, fd_secret_move4); - } -} - -// Move backward... -void fd_secret_move4(entity this) -{ - if (this.noise2 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - SUB_CalcMove(this, this.dest1, TSPEED_LINEAR, this.speed, fd_secret_move5); -} - -// Wait 1 second... -void fd_secret_move5(entity this) -{ - this.nextthink = this.ltime + 1.0; - setthink(this, fd_secret_move6); - if (this.noise3 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); -} - -void fd_secret_move6(entity this) -{ - if (this.noise2 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - SUB_CalcMove(this, this.oldorigin, TSPEED_LINEAR, this.speed, fd_secret_done); -} - -void fd_secret_done(entity this) -{ - if (this.spawnflags&DOOR_SECRET_YES_SHOOT) - { - this.health = 10000; - this.takedamage = DAMAGE_YES; - //this.th_pain = fd_secret_use; - } - if (this.noise3 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); -} - -.float door_finished; - -void secret_blocked(entity this, entity blocker) -{ - if (time < this.door_finished) - return; - this.door_finished = time + 0.5; - //T_Damage (other, this, this, this.dmg, this.dmg, this.deathtype, DT_IMPACT, (this.absmin + this.absmax) * 0.5, '0 0 0', Obituary_Generic); -} - -/* -============== -secret_touch - -Prints messages -================ -*/ -void secret_touch(entity this, entity toucher) -{ - if (!toucher.iscreature) - return; - if (this.door_finished > time) - return; - - this.door_finished = time + 2; - - if (this.message) - { - if (IS_CLIENT(toucher)) - centerprint(toucher, this.message); - play2(toucher, this.noise); - } -} - -void secret_reset(entity this) -{ - if (this.spawnflags & DOOR_SECRET_YES_SHOOT) - { - this.health = 10000; - this.takedamage = DAMAGE_YES; - } - setorigin(this, this.oldorigin); - setthink(this, func_null); - this.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 -*/ - -spawnfunc(func_door_secret) -{ - /*if (!this.deathtype) // map makers can override this - this.deathtype = " got in the way";*/ - - if (!this.dmg) - { - this.dmg = 2; - } - - // Magic formula... - this.mangle = this.angles; - this.angles = '0 0 0'; - this.classname = "door"; - if (!InitMovingBrushTrigger(this)) return; - this.effects |= EF_LOWPRECISION; - - // TODO: other soundpacks - if (this.sounds > 0) - { - this.noise1 = "plats/medplat1.wav"; - this.noise2 = "plats/medplat1.wav"; - this.noise3 = "plats/medplat2.wav"; - } - - // sound on touch - if (this.noise == "") - { - this.noise = "misc/talk.wav"; - } - precache_sound(this.noise); - // sound while moving backwards - if (this.noise1 && this.noise1 != "") - { - precache_sound(this.noise1); - } - // sound while moving sideways - if (this.noise2 && this.noise2 != "") - { - precache_sound(this.noise2); - } - // sound when door stops moving - if (this.noise3 && this.noise3 != "") - { - precache_sound(this.noise3); - } - - settouch(this, secret_touch); - setblocked(this, secret_blocked); - this.speed = 50; - this.use = fd_secret_use; - IFTARGETED - { - } - else - this.spawnflags |= DOOR_SECRET_YES_SHOOT; - - if (this.spawnflags & DOOR_SECRET_YES_SHOOT) - { - //this.canteamdamage = true; // TODO - this.health = 10000; - this.takedamage = DAMAGE_YES; - this.event_damage = fd_secret_damage; - } - this.oldorigin = this.origin; - if (!this.wait) this.wait = 5; // seconds before closing - - this.reset = secret_reset; - this.reset(this); -} -#endif diff --git a/qcsrc/common/triggers/func/door_secret.qh b/qcsrc/common/triggers/func/door_secret.qh deleted file mode 100644 index ee575bc42..000000000 --- a/qcsrc/common/triggers/func/door_secret.qh +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - - -const int DOOR_SECRET_OPEN_ONCE = BIT(0); // stays open - LEGACY, set wait to -1 instead -const int DOOR_SECRET_1ST_LEFT = BIT(1); // 1st move is left of arrow -const int DOOR_SECRET_1ST_DOWN = BIT(2); // 1st move is down from arrow -const int DOOR_SECRET_NO_SHOOT = BIT(3); // only opened by trigger -const int DOOR_SECRET_YES_SHOOT = BIT(4); // shootable even if targeted diff --git a/qcsrc/common/triggers/func/fourier.qc b/qcsrc/common/triggers/func/fourier.qc deleted file mode 100644 index 28e0f0f7c..000000000 --- a/qcsrc/common/triggers/func/fourier.qc +++ /dev/null @@ -1,89 +0,0 @@ -#include "fourier.qh" -#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 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(entity this) -{ - vector v; - float n, i, t; - - this.nextthink = time + 0.1; - if(this.owner.active != ACTIVE_ACTIVE) - { - this.owner.velocity = '0 0 0'; - return; - } - - - n = floor((tokenize_console(this.owner.netname)) / 5); - t = this.nextthink * this.owner.cnt + this.owner.phase * 360; - - v = this.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))) * this.owner.height * v_forward_y; - } - - if(this.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed - // * 10 so it will arrive in 0.1 sec - this.owner.velocity = (v - this.owner.origin) * 10; -} - -spawnfunc(func_fourier) -{ - entity controller; - if (this.noise != "") - { - precache_sound(this.noise); - soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } - - if (!this.speed) - this.speed = 4; - if (!this.height) - this.height = 32; - this.destvec = this.origin; - this.cnt = 360 / this.speed; - - setblocked(this, generic_plat_blocked); - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - if(this.netname == "") - this.netname = "1 0 0 0 1"; - - if (!InitMovingBrushTrigger(this)) - return; - - this.active = ACTIVE_ACTIVE; - - // wait for targets to spawn - controller = new(func_fourier_controller); - controller.owner = this; - controller.nextthink = time + 1; - setthink(controller, func_fourier_controller_think); - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); // for PushMove - - // Savage: Reduce bandwith, critical on e.g. nexdm02 - this.effects |= EF_LOWPRECISION; - - // TODO make a reset function for this one -} -#endif diff --git a/qcsrc/common/triggers/func/fourier.qh b/qcsrc/common/triggers/func/fourier.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/func/fourier.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/include.qc b/qcsrc/common/triggers/func/include.qc deleted file mode 100644 index 290c2e984..000000000 --- a/qcsrc/common/triggers/func/include.qc +++ /dev/null @@ -1,19 +0,0 @@ -#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 deleted file mode 100644 index 4e8b94cd1..000000000 --- a/qcsrc/common/triggers/func/include.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "door.qh" -#include "ladder.qh" -#include "train.qh" diff --git a/qcsrc/common/triggers/func/ladder.qc b/qcsrc/common/triggers/func/ladder.qc deleted file mode 100644 index 020ecca08..000000000 --- a/qcsrc/common/triggers/func/ladder.qc +++ /dev/null @@ -1,148 +0,0 @@ -#include "ladder.qh" -REGISTER_NET_LINKED(ENT_CLIENT_LADDER) - -void func_ladder_touch(entity this, entity toucher) -{ -#ifdef SVQC - if (!toucher.iscreature) - return; - if(IS_VEHICLE(toucher)) - return; -#elif defined(CSQC) - if(!toucher.isplayermodel) - return; -#endif - - EXACTTRIGGER_TOUCH(this, toucher); - - toucher.ladder_time = time + 0.1; - toucher.ladder_entity = this; -} - -#ifdef SVQC -bool func_ladder_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_LADDER); - - WriteString(MSG_ENTITY, this.classname); - WriteByte(MSG_ENTITY, this.skin); - WriteCoord(MSG_ENTITY, this.speed); - - trigger_common_write(this, false); - - return true; -} - -void func_ladder_link(entity this) -{ - trigger_link(this, func_ladder_send); - //this.model = "null"; -} - -void func_ladder_init(entity this) -{ - settouch(this, func_ladder_touch); - trigger_init(this); - func_ladder_link(this); - - if(min(this.absmax.x - this.absmin.x, this.absmax.y - this.absmin.y) > 100) - return; - - entity tracetest_ent = spawn(); - setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST); - tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - - vector top_min = (this.absmin + this.absmax) / 2; - top_min.z = this.absmax.z; - vector top_max = top_min; - top_max.z += PL_MAX_CONST.z - PL_MIN_CONST.z; - tracebox(top_max + jumpstepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - if(trace_startsolid) - { - tracebox(top_max + stepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - if(trace_startsolid) - { - tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - if(trace_startsolid) - { - if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x - && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x) - { - // move top on one side - top_max.y = top_min.y = this.absmin.y + (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75; - } - else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y - && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y) - { - // move top on one side - top_max.x = top_min.x = this.absmin.x + (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75; - } - tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - if(trace_startsolid) - { - if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x - && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x) - { - // alternatively on the other side - top_max.y = top_min.y = this.absmax.y - (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75; - } - else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y - && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y) - { - // alternatively on the other side - top_max.x = top_min.x = this.absmax.x - (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75; - } - tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - } - } - } - } - if(trace_startsolid || trace_endpos.z < this.absmax.z) - { - delete(tracetest_ent); - return; - } - - this.bot_pickup = true; // allow bots to make use of this ladder - float cost = waypoint_getlinearcost(trace_endpos.z - this.absmin.z); - top_min = trace_endpos; - waypoint_spawnforteleporter_boxes(this, WAYPOINTFLAG_LADDER, this.absmin, this.absmax, top_min, top_min, cost); -} - -spawnfunc(func_ladder) -{ - IL_PUSH(g_ladders, this); // TODO: also func_water? bots currently loop through func_ladder only - - func_ladder_init(this); -} - -spawnfunc(func_water) -{ - func_ladder_init(this); -} - -#elif defined(CSQC) -.float speed; - -void func_ladder_remove(entity this) -{ - strfree(this.classname); -} - -NET_HANDLE(ENT_CLIENT_LADDER, bool isnew) -{ - this.classname = strzone(ReadString()); - this.skin = ReadByte(); - this.speed = ReadCoord(); - - trigger_common_read(this, false); - - this.solid = SOLID_TRIGGER; - settouch(this, func_ladder_touch); - this.drawmask = MASK_NORMAL; - this.move_time = time; - this.entremove = func_ladder_remove; - - return true; -} -#endif diff --git a/qcsrc/common/triggers/func/ladder.qh b/qcsrc/common/triggers/func/ladder.qh deleted file mode 100644 index 26cbbda03..000000000 --- a/qcsrc/common/triggers/func/ladder.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -.float ladder_time; -.entity ladder_entity; diff --git a/qcsrc/common/triggers/func/pendulum.qc b/qcsrc/common/triggers/func/pendulum.qc deleted file mode 100644 index a59f7a93b..000000000 --- a/qcsrc/common/triggers/func/pendulum.qc +++ /dev/null @@ -1,77 +0,0 @@ -#include "pendulum.qh" -#ifdef SVQC -.float freq; -void func_pendulum_controller_think(entity this) -{ - float v; - this.nextthink = time + 0.1; - - if (!(this.owner.active == ACTIVE_ACTIVE)) - { - this.owner.avelocity_x = 0; - return; - } - - // calculate sinewave using makevectors - makevectors((this.nextthink * this.owner.freq + this.owner.phase) * '0 360 0'); - v = this.owner.speed * v_forward_y + this.cnt; - if(this.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed - { - // * 10 so it will arrive in 0.1 sec - this.owner.avelocity_z = (remainder(v - this.owner.angles_z, 360)) * 10; - } -} - -spawnfunc(func_pendulum) -{ - entity controller; - if (this.noise != "") - { - precache_sound(this.noise); - soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } - - this.active = ACTIVE_ACTIVE; - - // keys: angle, speed, phase, noise, freq - - if(!this.speed) - this.speed = 30; - // not initializing this.dmg to 2, to allow damageless pendulum - - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - setblocked(this, generic_plat_blocked); - - this.avelocity_z = 0.0000001; - if (!InitMovingBrushTrigger(this)) - return; - - if(!this.freq) - { - // find pendulum length (same formula as Q3A) - this.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(this.mins_z)))); - } - - // copy initial angle - this.cnt = this.angles_z; - - // wait for targets to spawn - controller = new(func_pendulum_controller); - controller.owner = this; - controller.nextthink = time + 1; - setthink(controller, func_pendulum_controller_think); - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); // for PushMove - - //this.effects |= EF_LOWPRECISION; - - // TODO make a reset function for this one -} -#endif diff --git a/qcsrc/common/triggers/func/pendulum.qh b/qcsrc/common/triggers/func/pendulum.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/func/pendulum.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc deleted file mode 100644 index b05233621..000000000 --- a/qcsrc/common/triggers/func/plat.qc +++ /dev/null @@ -1,190 +0,0 @@ -#include "plat.qh" -REGISTER_NET_LINKED(ENT_CLIENT_PLAT) - -#ifdef SVQC -void plat_link(entity this); - -void plat_delayedinit(entity this) -{ - plat_link(this); - plat_spawn_inside_trigger(this); // the "start moving" trigger -} - -float plat_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_PLAT); - WriteByte(MSG_ENTITY, sf); - - if(sf & SF_TRIGGER_INIT) - { - WriteByte(MSG_ENTITY, this.platmovetype_start); - WriteByte(MSG_ENTITY, this.platmovetype_turn); - WriteByte(MSG_ENTITY, this.platmovetype_end); - WriteByte(MSG_ENTITY, this.spawnflags); - - WriteString(MSG_ENTITY, this.model); - - trigger_common_write(this, true); - - WriteVector(MSG_ENTITY, this.pos1); - WriteVector(MSG_ENTITY, this.pos2); - - WriteVector(MSG_ENTITY, this.size); - - WriteAngle(MSG_ENTITY, this.mangle_x); - WriteAngle(MSG_ENTITY, this.mangle_y); - WriteAngle(MSG_ENTITY, this.mangle_z); - - WriteShort(MSG_ENTITY, this.speed); - WriteShort(MSG_ENTITY, this.height); - WriteByte(MSG_ENTITY, this.lip); - WriteByte(MSG_ENTITY, this.state); - - WriteShort(MSG_ENTITY, this.dmg); - } - - if(sf & SF_TRIGGER_RESET) - { - // used on client - } - - return true; -} - -void plat_link(entity this) -{ - //Net_LinkEntity(this, 0, false, plat_send); -} - -spawnfunc(func_plat) -{ - if (this.spawnflags & CRUSH) - { - this.dmg = 10000; - } - - if (this.dmg && (this.message == "")) - { - this.message = "was squished"; - } - if (this.dmg && (this.message2 == "")) - { - this.message2 = "was squished by"; - } - - if (this.sounds == 1) - { - this.noise = "plats/plat1.wav"; - this.noise1 = "plats/plat2.wav"; - } - - if (this.sounds == 2) - { - this.noise = "plats/medplat1.wav"; - this.noise1 = "plats/medplat2.wav"; - } - - // WARNING: backwards compatibility because people don't use already existing fields :( - if (this.sound1) - this.noise = this.sound1; - if (this.sound2) - this.noise1 = this.sound2; - - if(this.noise && this.noise != "") - { - precache_sound(this.noise); - } - if(this.noise1 && this.noise1 != "") - { - precache_sound(this.noise1); - } - - this.mangle = this.angles; - this.angles = '0 0 0'; - - this.classname = "plat"; - if (!InitMovingBrushTrigger(this)) - return; - this.effects |= EF_LOWPRECISION; - setsize (this, this.mins , this.maxs); - - setblocked(this, plat_crush); - - if (!this.speed) this.speed = 150; - if (!this.lip) this.lip = 16; - if (!this.height) this.height = this.size.z - this.lip; - - this.pos1 = this.origin; - this.pos2 = this.origin; - this.pos2_z = this.origin.z - this.height; - - this.reset = plat_reset; - this.reset(this); - - InitializeEntity(this, plat_delayedinit, INITPRIO_FINDTARGET); -} -#elif defined(CSQC) -void plat_draw(entity this) -{ - Movetype_Physics_NoMatchServer(this); - //Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); -} - -NET_HANDLE(ENT_CLIENT_PLAT, bool isnew) -{ - float sf = ReadByte(); - - if(sf & SF_TRIGGER_INIT) - { - this.platmovetype_start = ReadByte(); - this.platmovetype_turn = ReadByte(); - this.platmovetype_end = ReadByte(); - this.spawnflags = ReadByte(); - - this.model = strzone(ReadString()); - _setmodel(this, this.model); - - trigger_common_read(this, true); - - this.pos1 = ReadVector(); - this.pos2 = ReadVector(); - - this.size = ReadVector(); - - this.mangle_x = ReadAngle(); - this.mangle_y = ReadAngle(); - this.mangle_z = ReadAngle(); - - this.speed = ReadShort(); - this.height = ReadShort(); - this.lip = ReadByte(); - this.state = ReadByte(); - - this.dmg = ReadShort(); - - this.classname = "plat"; - this.solid = SOLID_BSP; - set_movetype(this, MOVETYPE_PUSH); - this.drawmask = MASK_NORMAL; - this.draw = plat_draw; - if (isnew) IL_PUSH(g_drawables, this); - this.use = plat_use; - this.entremove = trigger_remove_generic; - - plat_reset(this); // also called here - - set_movetype(this, MOVETYPE_PUSH); - this.move_time = time; - - plat_spawn_inside_trigger(this); - } - - if(sf & SF_TRIGGER_RESET) - { - plat_reset(this); - - this.move_time = time; - } - return true; -} -#endif diff --git a/qcsrc/common/triggers/func/plat.qh b/qcsrc/common/triggers/func/plat.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/func/plat.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/pointparticles.qc b/qcsrc/common/triggers/func/pointparticles.qc deleted file mode 100644 index 7de5a03ef..000000000 --- a/qcsrc/common/triggers/func/pointparticles.qc +++ /dev/null @@ -1,341 +0,0 @@ -#include "pointparticles.qh" -REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES) - -#ifdef SVQC -// NOTE: also contains func_sparks - -bool pointparticles_SendEntity(entity this, entity to, float sendflags) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES); - - // optional features to save space - sendflags = sendflags & 0x0F; - if(this.spawnflags & PARTICLES_IMPULSE) - sendflags |= SF_POINTPARTICLES_IMPULSE; // absolute count on toggle-on - if(this.movedir != '0 0 0' || this.velocity != '0 0 0') - sendflags |= SF_POINTPARTICLES_MOVING; // 4 bytes - saves CPU - if(this.waterlevel || this.count != 1) - sendflags |= SF_POINTPARTICLES_JITTER_AND_COUNT; // 4 bytes - obscure features almost never used - if(this.mins != '0 0 0' || this.maxs != '0 0 0') - sendflags |= SF_POINTPARTICLES_BOUNDS; // 14 bytes - saves lots of space - - WriteByte(MSG_ENTITY, sendflags); - if(sendflags & SF_TRIGGER_UPDATE) - { - if(this.active == ACTIVE_ACTIVE) - WriteCoord(MSG_ENTITY, this.impulse); - else - WriteCoord(MSG_ENTITY, 0); // off - } - if(sendflags & SF_TRIGGER_RESET) - { - WriteVector(MSG_ENTITY, this.origin); - } - if(sendflags & SF_TRIGGER_INIT) - { - if(this.model != "null") - { - WriteShort(MSG_ENTITY, this.modelindex); - if(sendflags & SF_POINTPARTICLES_BOUNDS) - { - WriteVector(MSG_ENTITY, this.mins); - WriteVector(MSG_ENTITY, this.maxs); - } - } - else - { - WriteShort(MSG_ENTITY, 0); - if(sendflags & SF_POINTPARTICLES_BOUNDS) - { - WriteVector(MSG_ENTITY, this.maxs); - } - } - WriteShort(MSG_ENTITY, this.cnt); - WriteString(MSG_ENTITY, this.mdl); - if(sendflags & SF_POINTPARTICLES_MOVING) - { - WriteShort(MSG_ENTITY, compressShortVector(this.velocity)); - WriteShort(MSG_ENTITY, compressShortVector(this.movedir)); - } - if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT) - { - WriteShort(MSG_ENTITY, this.waterlevel * 16.0); - WriteByte(MSG_ENTITY, this.count * 16.0); - } - WriteString(MSG_ENTITY, this.noise); - if(this.noise != "") - { - WriteByte(MSG_ENTITY, floor(this.atten * 64)); - WriteByte(MSG_ENTITY, floor(this.volume * 255)); - } - WriteString(MSG_ENTITY, this.bgmscript); - if(this.bgmscript != "") - { - WriteByte(MSG_ENTITY, floor(this.bgmscriptattack * 64)); - WriteByte(MSG_ENTITY, floor(this.bgmscriptdecay * 64)); - WriteByte(MSG_ENTITY, floor(this.bgmscriptsustain * 255)); - WriteByte(MSG_ENTITY, floor(this.bgmscriptrelease * 64)); - } - } - return 1; -} - -void pointparticles_think(entity this) -{ - if(this.origin != this.oldorigin) - { - this.SendFlags |= SF_TRIGGER_RESET; - this.oldorigin = this.origin; - } - this.nextthink = time; -} - -spawnfunc(func_pointparticles) -{ - if(this.model != "") { precache_model(this.model); _setmodel(this, this.model); } - if(this.noise != "") precache_sound(this.noise); - if(this.mdl != "") this.cnt = 0; // use a good handler - - if(!this.bgmscriptsustain) this.bgmscriptsustain = 1; - else if(this.bgmscriptsustain < 0) this.bgmscriptsustain = 0; - - if(!this.atten) this.atten = ATTEN_NORM; - else if(this.atten < 0) this.atten = 0; - if(!this.volume) this.volume = 1; - if(!this.count) this.count = 1; - if(!this.impulse) this.impulse = 1; - - if(!this.modelindex) - { - setorigin(this, this.origin + this.mins); - setsize(this, '0 0 0', this.maxs - this.mins); - } - //if(!this.cnt) this.cnt = _particleeffectnum(this.mdl); - this.setactive = generic_netlinked_setactive; - - Net_LinkEntity(this, (this.spawnflags & PARTICLES_VISCULLING), 0, pointparticles_SendEntity); - - IFTARGETED - { - // backwards compatibility - this.use = generic_netlinked_legacy_use; - } - this.reset = generic_netlinked_reset; - this.reset(this); - setthink(this, pointparticles_think); - this.nextthink = time; -} - -spawnfunc(func_sparks) -{ - if(this.count < 1) { - this.count = 25.0; // nice default value - } - - if(this.impulse < 0.5) { - this.impulse = 2.5; // nice default value - } - - this.mins = '0 0 0'; - this.maxs = '0 0 0'; - this.velocity = '0 0 -1'; - this.mdl = "TE_SPARK"; - this.cnt = 0; // use mdl - - spawnfunc_func_pointparticles(this); -} -#elif defined(CSQC) - -.int dphitcontentsmask; - -entityclass(PointParticles); -classfield(PointParticles) .int cnt; // effect number -classfield(PointParticles) .vector velocity; // particle velocity -classfield(PointParticles) .float waterlevel; // direction jitter -classfield(PointParticles) .int count; // count multiplier -classfield(PointParticles) .int impulse; // density -classfield(PointParticles) .string noise; // sound -classfield(PointParticles) .float atten; -classfield(PointParticles) .float volume; -classfield(PointParticles) .float absolute; // 1 = count per second is absolute, ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = only spawn at toggle -classfield(PointParticles) .vector movedir; // trace direction -classfield(PointParticles) .float glow_color; // palette index - -const int ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = 2; - -void Draw_PointParticles(entity this) -{ - float n, i, fail; - vector p; - vector sz; - vector o; - o = this.origin; - sz = this.maxs - this.mins; - n = doBGMScript(this); - if(this.absolute == ABSOLUTE_ONLY_SPAWN_AT_TOGGLE) - { - if(n >= 0) - n = this.just_toggled ? this.impulse : 0; - else - n = this.impulse * drawframetime; - } - else - { - n *= this.impulse * drawframetime; - if(this.just_toggled) - if(n < 1) - n = 1; - } - if(n == 0) - return; - fail = 0; - for(i = random(); i <= n && fail <= 64*n; ++i) - { - p = o + this.mins; - p.x += random() * sz.x; - p.y += random() * sz.y; - p.z += random() * sz.z; - if(WarpZoneLib_BoxTouchesBrush(p, p, this, NULL)) - { - if(this.movedir != '0 0 0') - { - traceline(p, p + normalize(this.movedir) * 4096, 0, NULL); - p = trace_endpos; - int eff_num; - if(this.cnt) - eff_num = this.cnt; - else - eff_num = _particleeffectnum(this.mdl); - __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count); - } - else - { - int eff_num; - if(this.cnt) - eff_num = this.cnt; - else - eff_num = _particleeffectnum(this.mdl); - __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count); - } - if(this.noise != "") - { - setorigin(this, p); - _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten); - } - this.just_toggled = 0; - } - else if(this.absolute) - { - ++fail; - --i; - } - } - setorigin(this, o); -} - -void Ent_PointParticles_Remove(entity this) -{ - strfree(this.noise); - strfree(this.bgmscript); - strfree(this.mdl); -} - -NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew) -{ - float i; - vector v; - int sendflags = ReadByte(); - if(sendflags & SF_TRIGGER_UPDATE) - { - i = ReadCoord(); // density (<0: point, >0: volume) - if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed - this.just_toggled = 1; - this.impulse = i; - } - if(sendflags & SF_TRIGGER_RESET) - { - this.origin = ReadVector(); - } - if(sendflags & SF_TRIGGER_INIT) - { - this.modelindex = ReadShort(); - if(sendflags & SF_POINTPARTICLES_BOUNDS) - { - if(this.modelindex) - { - this.mins = ReadVector(); - this.maxs = ReadVector(); - } - else - { - this.mins = '0 0 0'; - this.maxs = ReadVector(); - } - } - else - { - this.mins = this.maxs = '0 0 0'; - } - - this.cnt = ReadShort(); // effect number - this.mdl = strzone(ReadString()); // effect string - - if(sendflags & SF_POINTPARTICLES_MOVING) - { - this.velocity = decompressShortVector(ReadShort()); - this.movedir = decompressShortVector(ReadShort()); - } - else - { - this.velocity = this.movedir = '0 0 0'; - } - if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT) - { - this.waterlevel = ReadShort() / 16.0; - this.count = ReadByte() / 16.0; - } - else - { - this.waterlevel = 0; - this.count = 1; - } - strcpy(this.noise, ReadString()); - if(this.noise != "") - { - this.atten = ReadByte() / 64.0; - this.volume = ReadByte() / 255.0; - } - strcpy(this.bgmscript, ReadString()); - if(this.bgmscript != "") - { - this.bgmscriptattack = ReadByte() / 64.0; - this.bgmscriptdecay = ReadByte() / 64.0; - this.bgmscriptsustain = ReadByte() / 255.0; - this.bgmscriptrelease = ReadByte() / 64.0; - } - BGMScript_InitEntity(this); - } - - return = true; - - if(sendflags & SF_TRIGGER_UPDATE) - { - this.absolute = (this.impulse >= 0); - if(!this.absolute) - { - v = this.maxs - this.mins; - this.impulse *= -v.x * v.y * v.z / (64**3); // relative: particles per 64^3 cube - } - } - - if(sendflags & SF_POINTPARTICLES_IMPULSE) - this.absolute = ABSOLUTE_ONLY_SPAWN_AT_TOGGLE; - - setorigin(this, this.origin); - setsize(this, this.mins, this.maxs); - this.solid = SOLID_NOT; - this.draw = Draw_PointParticles; - if (isnew) IL_PUSH(g_drawables, this); - this.entremove = Ent_PointParticles_Remove; -} -#endif diff --git a/qcsrc/common/triggers/func/pointparticles.qh b/qcsrc/common/triggers/func/pointparticles.qh deleted file mode 100644 index b167527bc..000000000 --- a/qcsrc/common/triggers/func/pointparticles.qh +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -// spawnflags -const int PARTICLES_IMPULSE = BIT(1); -const int PARTICLES_VISCULLING = BIT(2); - -// sendflags -const int SF_POINTPARTICLES_IMPULSE = BIT(4); -const int SF_POINTPARTICLES_MOVING = BIT(5); // Send velocity and movedir -const int SF_POINTPARTICLES_JITTER_AND_COUNT = BIT(6); // Send waterlevel (=jitter) and count -const int SF_POINTPARTICLES_BOUNDS = BIT(7); // Send min and max of the brush diff --git a/qcsrc/common/triggers/func/rainsnow.qc b/qcsrc/common/triggers/func/rainsnow.qc deleted file mode 100644 index c765a4293..000000000 --- a/qcsrc/common/triggers/func/rainsnow.qc +++ /dev/null @@ -1,142 +0,0 @@ -#include "rainsnow.qh" -REGISTER_NET_LINKED(ENT_CLIENT_RAINSNOW) - -#ifdef SVQC -bool rainsnow_SendEntity(entity this, entity to, float sf) -{ - vector myorg = this.origin + this.mins; - vector mysize = this.maxs - this.mins; - WriteHeader(MSG_ENTITY, ENT_CLIENT_RAINSNOW); - WriteByte(MSG_ENTITY, this.state); - WriteVector(MSG_ENTITY, myorg); - WriteVector(MSG_ENTITY, mysize); - WriteShort(MSG_ENTITY, compressShortVector(this.dest)); - WriteShort(MSG_ENTITY, this.count); - WriteByte(MSG_ENTITY, this.cnt); - return true; -} - -/*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 -*/ -spawnfunc(func_rain) -{ - this.dest = this.velocity; - this.velocity = '0 0 0'; - if (!this.dest) - this.dest = '0 0 -700'; - this.angles = '0 0 0'; - set_movetype(this, MOVETYPE_NONE); - this.solid = SOLID_NOT; - SetBrushEntityModel(this); - if (!this.cnt) - { - this.cnt = 12; - } - if (!this.count) - this.count = 2000; - // relative to absolute particle count - this.count = 0.1 * this.count * (this.size_x / 1024) * (this.size_y / 1024); - if (this.count < 1) - this.count = 1; - if(this.count > 65535) - this.count = 65535; - - this.state = RAINSNOW_RAIN; - - Net_LinkEntity(this, 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 -*/ -spawnfunc(func_snow) -{ - this.dest = this.velocity; - this.velocity = '0 0 0'; - if (!this.dest) - this.dest = '0 0 -300'; - this.angles = '0 0 0'; - set_movetype(this, MOVETYPE_NONE); - this.solid = SOLID_NOT; - SetBrushEntityModel(this); - if (!this.cnt) - { - this.cnt = 12; - } - if (!this.count) - this.count = 2000; - // relative to absolute particle count - this.count = 0.1 * this.count * (this.size_x / 1024) * (this.size_y / 1024); - if (this.count < 1) - this.count = 1; - if(this.count > 65535) - this.count = 65535; - - this.state = RAINSNOW_SNOW; - - Net_LinkEntity(this, false, 0, rainsnow_SendEntity); -} -#elif defined(CSQC) -float autocvar_cl_rainsnow_maxdrawdist = 2048; - -void Draw_Rain(entity this) -{ - vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist; - maxdist.z = 5; - if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5')) - //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist)) - te_particlerain(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); -} - -void Draw_Snow(entity this) -{ - vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist; - maxdist.z = 5; - if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5')) - //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist)) - te_particlesnow(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); -} - -NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew) -{ - this.state = ReadByte(); // Rain, Snow, or Whatever - this.origin = ReadVector(); - this.maxs = ReadVector(); - this.velocity = decompressShortVector(ReadShort()); - this.count = ReadShort(); - this.glow_color = ReadByte(); // color - - return = true; - - this.mins = -0.5 * this.maxs; - this.maxs = 0.5 * this.maxs; - this.origin = this.origin - this.mins; - - setorigin(this, this.origin); - setsize(this, this.mins, this.maxs); - this.solid = SOLID_NOT; - if (isnew) IL_PUSH(g_drawables, this); - if(this.state == RAINSNOW_RAIN) - this.draw = Draw_Rain; - else - this.draw = Draw_Snow; -} -#endif diff --git a/qcsrc/common/triggers/func/rainsnow.qh b/qcsrc/common/triggers/func/rainsnow.qh deleted file mode 100644 index d60eb4f48..000000000 --- a/qcsrc/common/triggers/func/rainsnow.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - - -const int RAINSNOW_SNOW = 0; -const int RAINSNOW_RAIN = 1; diff --git a/qcsrc/common/triggers/func/rotating.qc b/qcsrc/common/triggers/func/rotating.qc deleted file mode 100644 index 35351ee08..000000000 --- a/qcsrc/common/triggers/func/rotating.qc +++ /dev/null @@ -1,110 +0,0 @@ -#include "rotating.qh" -#ifdef SVQC - -void func_rotating_setactive(entity this, int astate) -{ - if (astate == ACTIVE_TOGGLE) - { - if(this.active == ACTIVE_ACTIVE) - this.active = ACTIVE_NOT; - else - this.active = ACTIVE_ACTIVE; - } - else - this.active = astate; - - if(this.active == ACTIVE_NOT) - { - this.avelocity = '0 0 0'; - stopsound(this, CH_AMBIENT_SINGLE); - } - else - { - this.avelocity = this.pos1; - if(this.noise && this.noise != "") - { - _sound(this, CH_AMBIENT_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } - } -} - -void func_rotating_reset(entity this) -{ - // TODO: reset angles as well? - - if(this.spawnflags & FUNC_ROTATING_STARTOFF) - { - this.setactive(this, ACTIVE_NOT); - } - else - { - this.setactive(this, ACTIVE_ACTIVE); - } -} - -void func_rotating_init_for_player(entity this, entity player) -{ - if (this.noise && this.noise != "" && this.active == ACTIVE_ACTIVE && IS_REAL_CLIENT(player)) - { - msg_entity = player; - soundto (MSG_ONE, this, CH_AMBIENT_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } -} - -/*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. -*/ - -spawnfunc(func_rotating) -{ - if (this.noise && this.noise != "") - { - precache_sound(this.noise); - } - - this.setactive = func_rotating_setactive; - - if (!this.speed) - this.speed = 100; - if (this.spawnflags & FUNC_ROTATING_XAXIS) - this.avelocity = '0 0 1' * this.speed; - else if (this.spawnflags & FUNC_ROTATING_YAXIS) - this.avelocity = '1 0 0' * this.speed; - else // Z - this.avelocity = '0 1 0' * this.speed; - - this.pos1 = this.avelocity; - - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - - - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - - this.dmgtime2 = time; - - if (!InitMovingBrushTrigger(this)) - return; - // no EF_LOWPRECISION here, as rounding angles is bad - - setblocked(this, generic_plat_blocked); - - // wait for targets to spawn - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); // for PushMove - - this.reset = func_rotating_reset; - this.reset(this); - - // maybe send sound to new players - IL_PUSH(g_initforplayer, this); - this.init_for_player = func_rotating_init_for_player; -} -#endif diff --git a/qcsrc/common/triggers/func/rotating.qh b/qcsrc/common/triggers/func/rotating.qh deleted file mode 100644 index ad1b6ec92..000000000 --- a/qcsrc/common/triggers/func/rotating.qh +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - - -const int FUNC_ROTATING_XAXIS = BIT(2); -const int FUNC_ROTATING_YAXIS = BIT(3); -const int FUNC_ROTATING_STARTOFF = BIT(4); diff --git a/qcsrc/common/triggers/func/stardust.qc b/qcsrc/common/triggers/func/stardust.qc deleted file mode 100644 index 9c2fba8ad..000000000 --- a/qcsrc/common/triggers/func/stardust.qc +++ /dev/null @@ -1,9 +0,0 @@ -#include "stardust.qh" -#ifdef SVQC -spawnfunc(func_stardust) -{ - this.effects = EF_STARDUST; - - CSQCMODEL_AUTOINIT(this); -} -#endif diff --git a/qcsrc/common/triggers/func/stardust.qh b/qcsrc/common/triggers/func/stardust.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/func/stardust.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/train.qc b/qcsrc/common/triggers/func/train.qc deleted file mode 100644 index 4e9c33456..000000000 --- a/qcsrc/common/triggers/func/train.qc +++ /dev/null @@ -1,351 +0,0 @@ -#include "train.qh" -.float train_wait_turning; -.entity future_target; -void train_next(entity this); -#ifdef SVQC -void train_use(entity this, entity actor, entity trigger); -#endif -void train_wait(entity this) -{ - SUB_UseTargets(this.enemy, NULL, NULL); - this.enemy = NULL; - - // if turning is enabled, the train will turn toward the next point while waiting - if(this.platmovetype_turn && !this.train_wait_turning) - { - entity targ, cp; - vector ang; - targ = this.future_target; - if((this.spawnflags & TRAIN_CURVE) && targ.curvetarget) - cp = find(NULL, targetname, targ.curvetarget); - else - cp = NULL; - - if(cp) // bezier curves movement - ang = cp.origin - (this.origin - this.view_ofs); // use the origin of the control point of the next path_corner - else // linear movement - ang = targ.origin - (this.origin - this.view_ofs); // use the origin of the next path_corner - ang = vectoangles(ang); - ang_x = -ang_x; // flip up / down orientation - - if(this.wait > 0) // slow turning - SUB_CalcAngleMove(this, ang, TSPEED_TIME, this.ltime - time + this.wait, train_wait); - else // instant turning - SUB_CalcAngleMove(this, ang, TSPEED_TIME, 0.0000001, train_wait); - this.train_wait_turning = true; - return; - } - -#ifdef SVQC - if(this.noise != "") - stopsoundto(MSG_BROADCAST, this, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway -#endif - -#ifdef SVQC - entity tg = this.future_target; - if(tg.spawnflags & TRAIN_NEEDACTIVATION) - { - this.use = train_use; - setthink(this, func_null); - this.nextthink = 0; - } - else -#endif - if(this.wait < 0 || this.train_wait_turning) // no waiting or we already waited while turning - { - this.train_wait_turning = false; - train_next(this); - } - else - { - setthink(this, train_next); - this.nextthink = this.ltime + this.wait; - } -} - -entity train_next_find(entity this) -{ - if(this.target_random) - { - RandomSelection_Init(); - for(entity t = NULL; (t = find(t, targetname, this.target));) - { - RandomSelection_AddEnt(t, 1, 0); - } - return RandomSelection_chosen_ent; - } - else - { - return find(NULL, targetname, this.target); - } -} - -void train_next(entity this) -{ - entity targ = NULL, cp = NULL; - vector cp_org = '0 0 0'; - - targ = this.future_target; - - this.target = targ.target; - this.target_random = targ.target_random; - this.future_target = train_next_find(targ); - - if (this.spawnflags & TRAIN_CURVE) - { - if(targ.curvetarget) - { - cp = find(NULL, targetname, targ.curvetarget); // get its second target (the control point) - cp_org = cp.origin - this.view_ofs; // no control point found, assume a straight line to the destination - } - } - if (this.target == "") - objerror(this, "train_next: no next target"); - this.wait = targ.wait; - if (!this.wait) - this.wait = 0.1; - - if(targ.platmovetype) - { - // this path_corner contains a movetype overrider, apply it - this.platmovetype_start = targ.platmovetype_start; - this.platmovetype_end = targ.platmovetype_end; - } - else - { - // this path_corner doesn't contain a movetype overrider, use the train's defaults - this.platmovetype_start = this.platmovetype_start_default; - this.platmovetype_end = this.platmovetype_end_default; - } - - if (targ.speed) - { - if (cp) - SUB_CalcMove_Bezier(this, cp_org, targ.origin - this.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); - else - SUB_CalcMove(this, targ.origin - this.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); - } - else - { - if (cp) - SUB_CalcMove_Bezier(this, cp_org, targ.origin - this.view_ofs, TSPEED_LINEAR, this.speed, train_wait); - else - SUB_CalcMove(this, targ.origin - this.view_ofs, TSPEED_LINEAR, this.speed, train_wait); - } - - if(this.noise != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); -} - -REGISTER_NET_LINKED(ENT_CLIENT_TRAIN) - -#ifdef SVQC -float train_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRAIN); - WriteByte(MSG_ENTITY, sf); - - if(sf & SF_TRIGGER_INIT) - { - WriteString(MSG_ENTITY, this.platmovetype); - WriteByte(MSG_ENTITY, this.platmovetype_turn); - WriteByte(MSG_ENTITY, this.spawnflags); - - WriteString(MSG_ENTITY, this.model); - - trigger_common_write(this, true); - - WriteString(MSG_ENTITY, this.curvetarget); - - WriteVector(MSG_ENTITY, this.pos1); - WriteVector(MSG_ENTITY, this.pos2); - - WriteVector(MSG_ENTITY, this.size); - - WriteVector(MSG_ENTITY, this.view_ofs); - - WriteAngle(MSG_ENTITY, this.mangle_x); - WriteAngle(MSG_ENTITY, this.mangle_y); - WriteAngle(MSG_ENTITY, this.mangle_z); - - WriteShort(MSG_ENTITY, this.speed); - WriteShort(MSG_ENTITY, this.height); - WriteByte(MSG_ENTITY, this.lip); - WriteByte(MSG_ENTITY, this.state); - WriteByte(MSG_ENTITY, this.wait); - - WriteShort(MSG_ENTITY, this.dmg); - WriteByte(MSG_ENTITY, this.dmgtime); - } - - if(sf & SF_TRIGGER_RESET) - { - // used on client - } - - return true; -} - -void train_link(entity this) -{ - //Net_LinkEntity(this, 0, false, train_send); -} - -void train_use(entity this, entity actor, entity trigger) -{ - this.nextthink = this.ltime + 1; - setthink(this, train_next); - this.use = func_null; // not again, next target can set it again if needed - if(trigger.target2 && trigger.target2 != "") - this.future_target = find(NULL, targetname, trigger.target2); -} - -void func_train_find(entity this) -{ - entity targ = train_next_find(this); - this.target = targ.target; - this.target_random = targ.target_random; - // save the future target for later - this.future_target = train_next_find(targ); - if (this.target == "") - objerror(this, "func_train_find: no next target"); - setorigin(this, targ.origin - this.view_ofs); - - if(!(this.spawnflags & TRAIN_NEEDACTIVATION)) - { - this.nextthink = this.ltime + 1; - setthink(this, train_next); - } - - train_link(this); -} - -#endif - -/*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) -*/ -#ifdef SVQC -spawnfunc(func_train) -{ - if (this.noise != "") - precache_sound(this.noise); - - if (this.target == "") - objerror(this, "func_train without a target"); - if (!this.speed) - this.speed = 100; - - if (!InitMovingBrushTrigger(this)) - return; - this.effects |= EF_LOWPRECISION; - - if(this.spawnflags & TRAIN_NEEDACTIVATION) - this.use = train_use; - - if (this.spawnflags & TRAIN_TURN) - { - this.platmovetype_turn = true; - this.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now - } - else - this.view_ofs = this.mins; - - // wait for targets to spawn - InitializeEntity(this, func_train_find, INITPRIO_FINDTARGET); - - setblocked(this, generic_plat_blocked); - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - if(!set_platmovetype(this, this.platmovetype)) - return; - this.platmovetype_start_default = this.platmovetype_start; - this.platmovetype_end_default = this.platmovetype_end; - - // TODO make a reset function for this one -} -#elif defined(CSQC) -void train_draw(entity this) -{ - //Movetype_Physics_NoMatchServer(); - Movetype_Physics_MatchServer(this, autocvar_cl_projectiles_sloppy); -} - -NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew) -{ - float sf = ReadByte(); - - if(sf & SF_TRIGGER_INIT) - { - this.platmovetype = strzone(ReadString()); - this.platmovetype_turn = ReadByte(); - this.spawnflags = ReadByte(); - - this.model = strzone(ReadString()); - _setmodel(this, this.model); - - trigger_common_read(this, true); - - this.curvetarget = strzone(ReadString()); - - this.pos1 = ReadVector(); - this.pos2 = ReadVector(); - - this.size = ReadVector(); - - this.view_ofs = ReadVector(); - - this.mangle_x = ReadAngle(); - this.mangle_y = ReadAngle(); - this.mangle_z = ReadAngle(); - - this.speed = ReadShort(); - this.height = ReadShort(); - this.lip = ReadByte(); - this.state = ReadByte(); - this.wait = ReadByte(); - - this.dmg = ReadShort(); - this.dmgtime = ReadByte(); - - this.classname = "func_train"; - this.solid = SOLID_BSP; - set_movetype(this, MOVETYPE_PUSH); - this.drawmask = MASK_NORMAL; - this.draw = train_draw; - if (isnew) IL_PUSH(g_drawables, this); - this.entremove = trigger_remove_generic; - - if(set_platmovetype(this, this.platmovetype)) - { - this.platmovetype_start_default = this.platmovetype_start; - this.platmovetype_end_default = this.platmovetype_end; - } - - // everything is set up by the time the train is linked, we shouldn't need this - //func_train_find(); - - // but we will need these - train_next(this); - - set_movetype(this, MOVETYPE_PUSH); - this.move_time = time; - } - - if(sf & SF_TRIGGER_RESET) - { - // TODO: make a reset function for trains - } - - return true; -} - -#endif diff --git a/qcsrc/common/triggers/func/train.qh b/qcsrc/common/triggers/func/train.qh deleted file mode 100644 index 0b2a099c5..000000000 --- a/qcsrc/common/triggers/func/train.qh +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - - -const int TRAIN_CURVE = BIT(0); -const int TRAIN_TURN = BIT(1); -const int TRAIN_NEEDACTIVATION = BIT(2); - -#ifdef CSQC -.float dmgtime; -#endif diff --git a/qcsrc/common/triggers/func/vectormamamam.qc b/qcsrc/common/triggers/func/vectormamamam.qc deleted file mode 100644 index 61da52acb..000000000 --- a/qcsrc/common/triggers/func/vectormamamam.qc +++ /dev/null @@ -1,194 +0,0 @@ -#include "vectormamamam.qh" -#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 timestep) -{ - vector v, p; - float flags; - entity e; - - flags = o.spawnflags; - v = '0 0 0'; - - e = o.wp00; - if(e) - { - p = e.origin + timestep * e.velocity; - if(flags & PROJECT_ON_TARGETNORMAL) - 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 + timestep * e.velocity; - if(flags & PROJECT_ON_TARGET2NORMAL) - 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 + timestep * e.velocity; - if(flags & PROJECT_ON_TARGET3NORMAL) - 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 + timestep * e.velocity; - if(flags & PROJECT_ON_TARGET4NORMAL) - 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(entity this) -{ - this.nextthink = time + vectormamamam_timestep; - - if(this.owner.active != ACTIVE_ACTIVE) - { - this.owner.velocity = '0 0 0'; - return; - } - - if(this.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed - this.owner.velocity = (this.owner.destvec + func_vectormamamam_origin(this.owner, vectormamamam_timestep) - this.owner.origin) * 10; -} - -void func_vectormamamam_findtarget(entity this) -{ - if(this.target != "") - this.wp00 = find(NULL, targetname, this.target); - - if(this.target2 != "") - this.wp01 = find(NULL, targetname, this.target2); - - if(this.target3 != "") - this.wp02 = find(NULL, targetname, this.target3); - - if(this.target4 != "") - this.wp03 = find(NULL, targetname, this.target4); - - if(!this.wp00 && !this.wp01 && !this.wp02 && !this.wp03) - objerror(this, "No reference entity found, so there is nothing to move. Aborting."); - - this.destvec = this.origin - func_vectormamamam_origin(this, 0); - - entity controller; - controller = new(func_vectormamamam_controller); - controller.owner = this; - controller.nextthink = time + 1; - setthink(controller, func_vectormamamam_controller_think); -} - -void func_vectormamamam_setactive(entity this, int astate) -{ - if (astate == ACTIVE_TOGGLE) - { - if(this.active == ACTIVE_ACTIVE) - this.active = ACTIVE_NOT; - else - this.active = ACTIVE_ACTIVE; - } - else - this.active = astate; - - if(this.active == ACTIVE_NOT) - { - stopsound(this, CH_TRIGGER_SINGLE); - } - else - { - if(this.noise && this.noise != "") - { - _sound(this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } - } -} - -void func_vectormamamam_init_for_player(entity this, entity player) -{ - if (this.noise && this.noise != "" && this.active == ACTIVE_ACTIVE && IS_REAL_CLIENT(player)) - { - msg_entity = player; - soundto(MSG_ONE, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } -} - -spawnfunc(func_vectormamamam) -{ - if (this.noise != "") - { - precache_sound(this.noise); - } - - if(!this.targetfactor) - this.targetfactor = 1; - - if(!this.target2factor) - this.target2factor = 1; - - if(!this.target3factor) - this.target3factor = 1; - - if(!this.target4factor) - this.target4factor = 1; - - if(this.targetnormal) - this.targetnormal = normalize(this.targetnormal); - - if(this.target2normal) - this.target2normal = normalize(this.target2normal); - - if(this.target3normal) - this.target3normal = normalize(this.target3normal); - - if(this.target4normal) - this.target4normal = normalize(this.target4normal); - - setblocked(this, generic_plat_blocked); - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - if (!InitMovingBrushTrigger(this)) - return; - - // wait for targets to spawn - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); // for PushMove - - // Savage: Reduce bandwith, critical on e.g. nexdm02 - this.effects |= EF_LOWPRECISION; - - this.setactive = func_vectormamamam_setactive; - this.setactive(this, ACTIVE_ACTIVE); - - // maybe send sound to new players - IL_PUSH(g_initforplayer, this); - this.init_for_player = func_vectormamamam_init_for_player; - - InitializeEntity(this, func_vectormamamam_findtarget, INITPRIO_FINDTARGET); -} -#endif diff --git a/qcsrc/common/triggers/func/vectormamamam.qh b/qcsrc/common/triggers/func/vectormamamam.qh deleted file mode 100644 index 7eb6b0a63..000000000 --- a/qcsrc/common/triggers/func/vectormamamam.qh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - - -const int PROJECT_ON_TARGETNORMAL = BIT(0); -const int PROJECT_ON_TARGET2NORMAL = BIT(1); -const int PROJECT_ON_TARGET3NORMAL = BIT(2); -const int PROJECT_ON_TARGET4NORMAL = BIT(3); - -const float vectormamamam_timestep = 0.1; diff --git a/qcsrc/common/triggers/include.qc b/qcsrc/common/triggers/include.qc deleted file mode 100644 index 59da1f217..000000000 --- a/qcsrc/common/triggers/include.qc +++ /dev/null @@ -1,20 +0,0 @@ -#include "include.qh" - -// some required common stuff -#include "subs.qc" -#include "triggers.qc" -#include "platforms.qc" -#include "teleporters.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 deleted file mode 100644 index 87c07c14d..000000000 --- a/qcsrc/common/triggers/include.qh +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -// some required common stuff -#ifdef SVQC - #include -#endif -#include "triggers.qh" -#include "subs.qh" -#include "platforms.qh" -#include "teleporters.qh" - -// func -#include "func/include.qh" - -// target -#include "target/include.qh" - -// trigger -#include "trigger/include.qh" diff --git a/qcsrc/common/triggers/misc/_mod.inc b/qcsrc/common/triggers/misc/_mod.inc deleted file mode 100644 index 4a8ec06ef..000000000 --- a/qcsrc/common/triggers/misc/_mod.inc +++ /dev/null @@ -1,6 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/misc/_mod.qh b/qcsrc/common/triggers/misc/_mod.qh deleted file mode 100644 index 98615ccb0..000000000 --- a/qcsrc/common/triggers/misc/_mod.qh +++ /dev/null @@ -1,6 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/misc/corner.qc b/qcsrc/common/triggers/misc/corner.qc deleted file mode 100644 index a0f67b759..000000000 --- a/qcsrc/common/triggers/misc/corner.qc +++ /dev/null @@ -1,75 +0,0 @@ -#include "corner.qh" -REGISTER_NET_LINKED(ENT_CLIENT_CORNER) - -#ifdef SVQC -bool corner_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_CORNER); - - WriteString(MSG_ENTITY, this.platmovetype); - - WriteVector(MSG_ENTITY, this.origin); - - WriteString(MSG_ENTITY, this.target); - WriteString(MSG_ENTITY, this.target2); - WriteString(MSG_ENTITY, this.target3); - WriteString(MSG_ENTITY, this.target4); - WriteString(MSG_ENTITY, this.targetname); - WriteByte(MSG_ENTITY, this.target_random); - - WriteByte(MSG_ENTITY, this.wait); - - return true; -} - -void corner_link(entity this) -{ - //Net_LinkEntity(this, false, 0, corner_send); -} - -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 - set_platmovetype(this, this.platmovetype); - - corner_link(this); -} -#elif defined(CSQC) - -void corner_remove(entity this) -{ - strfree(this.target); - strfree(this.target2); - strfree(this.target3); - strfree(this.target4); - strfree(this.targetname); - strfree(this.platmovetype); -} - -NET_HANDLE(ENT_CLIENT_CORNER, bool isnew) -{ - this.platmovetype = strzone(ReadString()); - - this.origin = ReadVector(); - setorigin(this, this.origin); - - this.target = strzone(ReadString()); - this.target2 = strzone(ReadString()); - this.target3 = strzone(ReadString()); - this.target4 = strzone(ReadString()); - this.targetname = strzone(ReadString()); - this.target_random = ReadByte(); - - this.wait = ReadByte(); - - return = true; - - this.classname = "path_corner"; - this.drawmask = MASK_NORMAL; - this.entremove = corner_remove; - - set_platmovetype(this, this.platmovetype); -} - -#endif diff --git a/qcsrc/common/triggers/misc/corner.qh b/qcsrc/common/triggers/misc/corner.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/misc/corner.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/misc/follow.qc b/qcsrc/common/triggers/misc/follow.qc deleted file mode 100644 index 87619ca71..000000000 --- a/qcsrc/common/triggers/misc/follow.qc +++ /dev/null @@ -1,70 +0,0 @@ -#include "follow.qh" -// the way this entity works makes it no use to CSQC, as it removes itself instantly - -#ifdef SVQC -void follow_init(entity this) -{ - entity src, dst; - src = NULL; - dst = NULL; - if(this.killtarget != "") - src = find(NULL, targetname, this.killtarget); - if(this.target != "") - dst = find(NULL, targetname, this.target); - - if(!src && !dst) - { - objerror(this, "follow: could not find target/killtarget"); - return; - } - - if(this.jointtype) - { - // already done :P entity must stay - this.aiment = src; - this.enemy = dst; - } - else if(!src || !dst) - { - objerror(this, "follow: could not find target/killtarget"); - return; - } - else if(this.spawnflags & FOLLOW_ATTACH) - { - // attach - if(this.spawnflags & FOLLOW_LOCAL) - { - setattachment(dst, src, this.message); - } - else - { - attach_sameorigin(dst, src, this.message); - } - - dst.solid = SOLID_NOT; // solid doesn't work with attachment - delete(this); - } - else - { - if(this.spawnflags & FOLLOW_LOCAL) - { - set_movetype(dst, 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); - } - - delete(this); - } -} - -spawnfunc(misc_follow) -{ - InitializeEntity(this, follow_init, INITPRIO_FINDTARGET); -} -#endif diff --git a/qcsrc/common/triggers/misc/follow.qh b/qcsrc/common/triggers/misc/follow.qh deleted file mode 100644 index aef491ff3..000000000 --- a/qcsrc/common/triggers/misc/follow.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - - -const int FOLLOW_ATTACH = BIT(0); -const int FOLLOW_LOCAL = BIT(1); diff --git a/qcsrc/common/triggers/misc/include.qc b/qcsrc/common/triggers/misc/include.qc deleted file mode 100644 index bbe5ce03d..000000000 --- a/qcsrc/common/triggers/misc/include.qc +++ /dev/null @@ -1,5 +0,0 @@ -#include "include.qh" -#include "corner.qc" -#include "follow.qc" -#include "laser.qc" -#include "teleport_dest.qc" diff --git a/qcsrc/common/triggers/misc/include.qh b/qcsrc/common/triggers/misc/include.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/misc/include.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/misc/laser.qc b/qcsrc/common/triggers/misc/laser.qc deleted file mode 100644 index df88b750f..000000000 --- a/qcsrc/common/triggers/misc/laser.qc +++ /dev/null @@ -1,418 +0,0 @@ -#include "laser.qh" -#if defined(CSQC) - #include - #include - #include -#elif defined(MENUQC) -#elif defined(SVQC) -#endif - -REGISTER_NET_LINKED(ENT_CLIENT_LASER) - -#ifdef SVQC -.float modelscale; -void misc_laser_aim(entity this) -{ - vector a; - if(this.enemy) - { - if(this.spawnflags & LASER_FINITE) - { - if(this.enemy.origin != this.mangle) - { - this.mangle = this.enemy.origin; - this.SendFlags |= SF_LASER_UPDATE_TARGET; - } - } - else - { - a = vectoangles(this.enemy.origin - this.origin); - a_x = -a_x; - if(a != this.mangle) - { - this.mangle = a; - this.SendFlags |= SF_LASER_UPDATE_TARGET; - } - } - } - else - { - if(this.angles != this.mangle) - { - this.mangle = this.angles; - this.SendFlags |= SF_LASER_UPDATE_TARGET; - } - } - if(this.origin != this.oldorigin) - { - this.SendFlags |= SF_LASER_UPDATE_ORIGIN; - this.oldorigin = this.origin; - } -} - -void misc_laser_init(entity this) -{ - if(this.target != "") - this.enemy = find(NULL, targetname, this.target); -} - -.entity pusher; -void misc_laser_think(entity this) -{ - vector o; - entity hitent; - vector hitloc; - - this.nextthink = time; - - if(this.active == ACTIVE_NOT) - return; - - misc_laser_aim(this); - - if(this.enemy) - { - o = this.enemy.origin; - if (!(this.spawnflags & LASER_FINITE)) - o = this.origin + normalize(o - this.origin) * LASER_BEAM_MAXLENGTH; - } - else - { - makevectors(this.mangle); - o = this.origin + v_forward * LASER_BEAM_MAXLENGTH; - } - - if(this.dmg || this.enemy.target != "") - { - traceline(this.origin, o, MOVE_NORMAL, this); - } - hitent = trace_ent; - hitloc = trace_endpos; - - if(this.enemy.target != "") // DETECTOR laser - { - if(trace_ent.iscreature) - { - this.pusher = hitent; - if(!this.count) - { - this.count = 1; - - SUB_UseTargets(this.enemy, this.enemy.pusher, NULL); - } - } - else - { - if(this.count) - { - this.count = 0; - - SUB_UseTargets(this.enemy, this.enemy.pusher, NULL); - } - } - } - - if(this.dmg) - { - if(this.team) - if(((this.spawnflags & LASER_INVERT_TEAM) == 0) == (this.team != hitent.team)) - return; - if(hitent.takedamage) - Damage(hitent, this, this, ((this.dmg < 0) ? 100000 : (this.dmg * frametime)), DEATH_HURTTRIGGER.m_id, DMG_NOWEP, hitloc, '0 0 0'); - } -} - -bool laser_SendEntity(entity this, entity to, float sendflags) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_LASER); - sendflags = sendflags & 0x0F; // use that bit to indicate finite length laser - if(this.spawnflags & LASER_FINITE) - sendflags |= SF_LASER_FINITE; - if(this.alpha) - sendflags |= SF_LASER_ALPHA; - if(this.scale != 1 || this.modelscale != 1) - sendflags |= SF_LASER_SCALE; - if(this.spawnflags & LASER_NOTRACE) - sendflags |= SF_LASER_NOTRACE; - WriteByte(MSG_ENTITY, sendflags); - if(sendflags & SF_LASER_UPDATE_ORIGIN) - { - WriteVector(MSG_ENTITY, this.origin); - } - if(sendflags & SF_LASER_UPDATE_EFFECT) - { - WriteByte(MSG_ENTITY, this.beam_color.x * 255.0); - WriteByte(MSG_ENTITY, this.beam_color.y * 255.0); - WriteByte(MSG_ENTITY, this.beam_color.z * 255.0); - if(sendflags & SF_LASER_ALPHA) - WriteByte(MSG_ENTITY, this.alpha * 255.0); - if(sendflags & SF_LASER_SCALE) - { - WriteByte(MSG_ENTITY, bound(0, this.scale * 16.0, 255)); - WriteByte(MSG_ENTITY, bound(0, this.modelscale * 16.0, 255)); - } - if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE)) // effect doesn't need sending if the laser is infinite and has collision testing turned off - WriteShort(MSG_ENTITY, this.cnt); - } - if(sendflags & SF_LASER_UPDATE_TARGET) - { - if(sendflags & SF_LASER_FINITE) - { - WriteVector(MSG_ENTITY, this.enemy.origin); - } - else - { - WriteAngle(MSG_ENTITY, this.mangle_x); - WriteAngle(MSG_ENTITY, this.mangle_y); - } - } - if(sendflags & SF_LASER_UPDATE_ACTIVE) - WriteByte(MSG_ENTITY, this.active); - return true; -} - -/*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 -"beam_color" - color of the beam (default: red) -"dmg" - damage per second (-1 for a laser that kills immediately) -*/ - -void laser_setactive(entity this, int act) -{ - int old_status = this.active; - if(act == ACTIVE_TOGGLE) - { - if(this.active == ACTIVE_ACTIVE) - { - this.active = ACTIVE_NOT; - } - else - { - this.active = ACTIVE_ACTIVE; - } - } - else - { - this.active = act; - } - - if (this.active != old_status) - { - this.SendFlags |= SF_LASER_UPDATE_ACTIVE; - misc_laser_aim(this); - } -} - -void laser_use(entity this, entity actor, entity trigger) -{ - this.setactive(this, ACTIVE_TOGGLE); -} - -spawnfunc(misc_laser) -{ - if(this.mdl) - { - if(this.mdl == "none") - this.cnt = -1; - else - { - this.cnt = _particleeffectnum(this.mdl); - if(this.cnt < 0 && this.dmg) - this.cnt = particleeffectnum(EFFECT_LASER_DEADLY); - } - } - else if(!this.cnt) - { - if(this.dmg) - this.cnt = particleeffectnum(EFFECT_LASER_DEADLY); - else - this.cnt = -1; - } - if(this.cnt < 0) - this.cnt = -1; - - if(!this.beam_color && this.colormod) - { - LOG_WARN("misc_laser uses legacy field 'colormod', please use 'beam_color' instead"); - this.beam_color = this.colormod; - } - - if(this.beam_color == '0 0 0') - { - if(!this.alpha) - this.beam_color = '1 0 0'; - } - - if(this.message == "") - { - this.message = "saw the light"; - } - if (this.message2 == "") - { - this.message2 = "was pushed into a laser by"; - } - if(!this.scale) - { - this.scale = 1; - } - if(!this.modelscale) - { - this.modelscale = 1; - } - else if(this.modelscale < 0) - { - this.modelscale = 0; - } - setthink(this, misc_laser_think); - this.nextthink = time; - InitializeEntity(this, misc_laser_init, INITPRIO_FINDTARGET); - - this.mangle = this.angles; - - Net_LinkEntity(this, false, 0, laser_SendEntity); - - this.setactive = laser_setactive; - - IFTARGETED - { - // backwards compatibility - this.use = laser_use; - } - - this.reset = generic_netlinked_reset; - this.reset(this); -} -#elif defined(CSQC) - -// a laser goes from origin in direction angles -// it has color 'beam_color' -// and stops when something is in the way -entityclass(Laser); -classfield(Laser) .int cnt; // end effect -classfield(Laser) .vector colormod; -classfield(Laser) .int state; // on-off -classfield(Laser) .int count; // flags for the laser -classfield(Laser) .vector velocity; // laser endpoint if it is FINITE -classfield(Laser) .float alpha; -classfield(Laser) .float scale; // scaling factor of the thickness -classfield(Laser) .float modelscale; // scaling factor of the dlight - -void Draw_Laser(entity this) -{ - if(this.active == ACTIVE_NOT) - return; - InterpolateOrigin_Do(this); - if(this.count & SF_LASER_FINITE) - { - if(this.count & SF_LASER_NOTRACE) - { - trace_endpos = this.velocity; - trace_dphitq3surfaceflags = 0; - } - else - traceline(this.origin, this.velocity, 0, this); - } - else - { - if(this.count & SF_LASER_NOTRACE) - { - makevectors(this.angles); - trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE; - trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY; - } - else - { - makevectors(this.angles); - traceline(this.origin, this.origin + v_forward * LASER_BEAM_MAXLENGTH, 0, this); - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) - trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE; - } - } - if(this.scale != 0) - { - if(this.alpha) - { - Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, this.alpha, DRAWFLAG_NORMAL, view_origin); - } - else - { - Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, 0.5, DRAWFLAG_ADDITIVE, view_origin); - } - } - if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT))) - { - if(this.cnt >= 0) - __pointparticles(this.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000); - if(this.beam_color != '0 0 0' && this.modelscale != 0) - adddynamiclight(trace_endpos + trace_plane_normal * 1, this.modelscale, this.beam_color * 5); - } -} - -NET_HANDLE(ENT_CLIENT_LASER, bool isnew) -{ - InterpolateOrigin_Undo(this); - - // 30 bytes, or 13 bytes for just moving - int sendflags = ReadByte(); - this.count = (sendflags & 0xF0); - - if(this.count & SF_LASER_FINITE) - this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN; - else - this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN; - - if(sendflags & SF_LASER_UPDATE_ORIGIN) - { - this.origin = ReadVector(); - setorigin(this, this.origin); - } - if(sendflags & SF_LASER_UPDATE_EFFECT) - { - this.beam_color.x = ReadByte() / 255.0; - this.beam_color.y = ReadByte() / 255.0; - this.beam_color.z = ReadByte() / 255.0; - if(sendflags & SF_LASER_ALPHA) - this.alpha = ReadByte() / 255.0; - else - this.alpha = 0; - this.scale = 2; // NOTE: why 2? - this.modelscale = 50; // NOTE: why 50? - if(sendflags & SF_LASER_SCALE) - { - this.scale *= ReadByte() / 16.0; // beam radius - this.modelscale *= ReadByte() / 16.0; // dlight radius - } - if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE)) - this.cnt = ReadShort(); // effect number - else - this.cnt = 0; - } - if(sendflags & SF_LASER_UPDATE_TARGET) - { - if(sendflags & SF_LASER_FINITE) - { - this.velocity = ReadVector(); - } - else - { - this.angles_x = ReadAngle(); - this.angles_y = ReadAngle(); - } - } - if(sendflags & SF_LASER_UPDATE_ACTIVE) - this.active = ReadByte(); - - return = true; - - InterpolateOrigin_Note(this); - this.draw = Draw_Laser; - if (isnew) IL_PUSH(g_drawables, this); -} -#endif diff --git a/qcsrc/common/triggers/misc/laser.qh b/qcsrc/common/triggers/misc/laser.qh deleted file mode 100644 index 0ff57646a..000000000 --- a/qcsrc/common/triggers/misc/laser.qh +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - - -const int LASER_FINITE = BIT(1); -const int LASER_NOTRACE = BIT(2); -const int LASER_INVERT_TEAM = BIT(3); - -const int SF_LASER_UPDATE_ORIGIN = BIT(0); -const int SF_LASER_UPDATE_TARGET = BIT(1); -const int SF_LASER_UPDATE_ACTIVE = BIT(2); -const int SF_LASER_UPDATE_EFFECT = BIT(3); - -const int SF_LASER_NOTRACE = BIT(4); -const int SF_LASER_SCALE = BIT(5); -const int SF_LASER_ALPHA = BIT(6); -const int SF_LASER_FINITE = BIT(7); - -.vector beam_color; - -const float LASER_BEAM_MAXLENGTH = 32768; // maximum length of a beam trace -// TODO: find a better way to do this -const float LASER_BEAM_MAXWORLDSIZE = 1048576; // to make sure the endpoint of the beam is not visible inside diff --git a/qcsrc/common/triggers/misc/teleport_dest.qc b/qcsrc/common/triggers/misc/teleport_dest.qc deleted file mode 100644 index 126a20ea2..000000000 --- a/qcsrc/common/triggers/misc/teleport_dest.qc +++ /dev/null @@ -1,89 +0,0 @@ -#include "teleport_dest.qh" -REGISTER_NET_LINKED(ENT_CLIENT_TELEPORT_DEST) - -#ifdef SVQC - -bool teleport_dest_send(entity this, entity to, int sendflags) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TELEPORT_DEST); - WriteByte(MSG_ENTITY, sendflags); - - if(sendflags & SF_TRIGGER_INIT) - { - WriteByte(MSG_ENTITY, this.cnt); - WriteCoord(MSG_ENTITY, this.speed); - WriteString(MSG_ENTITY, this.targetname); - WriteVector(MSG_ENTITY, this.origin); - - WriteAngle(MSG_ENTITY, this.mangle_x); - WriteAngle(MSG_ENTITY, this.mangle_y); - WriteAngle(MSG_ENTITY, this.mangle_z); - } - - return true; -} - -void teleport_dest_link(entity this) -{ - Net_LinkEntity(this, false, 0, teleport_dest_send); - this.SendFlags |= SF_TRIGGER_INIT; -} - -spawnfunc(info_teleport_destination) -{ - this.classname = "info_teleport_destination"; - - this.mangle = this.angles; - this.angles = '0 0 0'; - - //setorigin(this, this.origin + '0 0 27'); // To fix a mappers' habit as old as Quake - setorigin(this, this.origin); - - IFTARGETED - { - } - else - objerror (this, "^3Teleport destination without a targetname"); - - teleport_dest_link(this); -} - -spawnfunc(misc_teleporter_dest) -{ - spawnfunc_info_teleport_destination(this); -} - -#elif defined(CSQC) - -void teleport_dest_remove(entity this) -{ - // strfree(this.classname); - strfree(this.targetname); -} - -NET_HANDLE(ENT_CLIENT_TELEPORT_DEST, bool isnew) -{ - int sendflags = ReadByte(); - - if(sendflags & SF_TRIGGER_INIT) - { - this.classname = "info_teleport_destination"; - this.cnt = ReadByte(); - this.speed = ReadCoord(); - this.targetname = strzone(ReadString()); - this.origin = ReadVector(); - - this.mangle_x = ReadAngle(); - this.mangle_y = ReadAngle(); - this.mangle_z = ReadAngle(); - - setorigin(this, this.origin); - - this.drawmask = MASK_NORMAL; - this.entremove = teleport_dest_remove; - } - - return = true; -} - -#endif diff --git a/qcsrc/common/triggers/misc/teleport_dest.qh b/qcsrc/common/triggers/misc/teleport_dest.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/misc/teleport_dest.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/platforms.qc b/qcsrc/common/triggers/platforms.qc deleted file mode 100644 index 474787731..000000000 --- a/qcsrc/common/triggers/platforms.qc +++ /dev/null @@ -1,227 +0,0 @@ -#include "platforms.qh" -void generic_plat_blocked(entity this, entity blocker) -{ -#ifdef SVQC - if(this.dmg && blocker.takedamage != DAMAGE_NO) - { - if(this.dmgtime2 < time) - { - Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - this.dmgtime2 = time + this.dmgtime; - } - - // Gib dead/dying stuff - if(IS_DEAD(blocker)) - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - } -#endif -} - -void plat_spawn_inside_trigger(entity this) -{ - entity trigger; - vector tmin, tmax; - - trigger = spawn(); - settouch(trigger, plat_center_touch); - set_movetype(trigger, MOVETYPE_NONE); - trigger.solid = SOLID_TRIGGER; - trigger.enemy = this; - - tmin = this.absmin + '25 25 0'; - tmax = this.absmax - '25 25 -8'; - tmin_z = tmax_z - (this.pos1_z - this.pos2_z + 8); - if (this.spawnflags & PLAT_LOW_TRIGGER) - tmax_z = tmin_z + 8; - - if (this.size_x <= 50) - { - tmin_x = (this.mins_x + this.maxs_x) / 2; - tmax_x = tmin_x + 1; - } - if (this.size_y <= 50) - { - tmin_y = (this.mins_y + this.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... - delete(trigger); - objerror(this, "plat_spawn_inside_trigger: platform has odd size or lip, can't spawn"); -} - -void plat_hit_top(entity this) -{ - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = STATE_TOP; - - setthink(this, plat_go_down); - this.nextthink = this.ltime + 3; -} - -void plat_hit_bottom(entity this) -{ - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = STATE_BOTTOM; -} - -void plat_go_down(entity this) -{ - _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM); - this.state = STATE_DOWN; - SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, plat_hit_bottom); -} - -void plat_go_up(entity this) -{ - _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM); - this.state = STATE_UP; - SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, plat_hit_top); -} - -void plat_center_touch(entity this, entity toucher) -{ -#ifdef SVQC - if (!toucher.iscreature) - return; - - if (toucher.health <= 0) - return; -#elif defined(CSQC) - if (!IS_PLAYER(toucher)) - return; - if(IS_DEAD(toucher)) - return; -#endif - - if (this.enemy.state == STATE_BOTTOM) { - plat_go_up(this.enemy); - } else if (this.enemy.state == STATE_TOP) - this.enemy.nextthink = this.enemy.ltime + 1; -} - -void plat_outside_touch(entity this, entity toucher) -{ -#ifdef SVQC - if (!toucher.iscreature) - return; - - if (toucher.health <= 0) - return; -#elif defined(CSQC) - if (!IS_PLAYER(toucher)) - return; -#endif - - if (this.enemy.state == STATE_TOP) { - entity e = this.enemy; - plat_go_down(e); - } -} - -void plat_trigger_use(entity this, entity actor, entity trigger) -{ - if (getthink(this)) - return; // already activated - plat_go_down(this); -} - - -void plat_crush(entity this, entity blocker) -{ - if((this.spawnflags & CRUSH) && (blocker.takedamage != DAMAGE_NO)) - { // KIll Kill Kill!! -#ifdef SVQC - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - } - else - { -#ifdef SVQC - if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) - { // Shall we bite? - Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - // Gib dead/dying stuff - if(IS_DEAD(blocker)) - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - } -#endif - - if (this.state == STATE_UP) - plat_go_down (this); - else if (this.state == STATE_DOWN) - plat_go_up (this); - // 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(entity this, entity actor, entity trigger) -{ - this.use = func_null; - if (this.state != STATE_UP) - objerror (this, "plat_use: not in up state"); - plat_go_down(this); -} - -// WARNING: backwards compatibility because people don't use already existing fields :( -// TODO: Check if any maps use these fields and remove these fields if it doesn't break maps -.string sound1, sound2; - -void plat_reset(entity this) -{ - IFTARGETED - { - setorigin(this, this.pos1); - this.state = STATE_UP; - this.use = plat_use; - } - else - { - setorigin(this, this.pos2); - this.state = STATE_BOTTOM; - this.use = plat_trigger_use; - } - -#ifdef SVQC - this.SendFlags |= SF_TRIGGER_RESET; -#endif -} - -.float platmovetype_start_default, platmovetype_end_default; -bool set_platmovetype(entity e, string s) -{ - // sets platmovetype_start and platmovetype_end based on a string consisting of two values - - int 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(e, "Invalid platform move type; platform would go in reverse, which is not allowed."); - return false; - } - - return true; -} diff --git a/qcsrc/common/triggers/platforms.qh b/qcsrc/common/triggers/platforms.qh deleted file mode 100644 index 346cebc71..000000000 --- a/qcsrc/common/triggers/platforms.qh +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - - -const int PLAT_LOW_TRIGGER = BIT(0); - -.float dmgtime2; - -void plat_center_touch(entity this, entity toucher); -void plat_outside_touch(entity this, entity toucher); -void plat_trigger_use(entity this, entity actor, entity trigger); -void plat_go_up(entity this); -void plat_go_down(entity this); -void plat_crush(entity this, entity blocker); - -.float dmg; diff --git a/qcsrc/common/triggers/subs.qc b/qcsrc/common/triggers/subs.qc deleted file mode 100644 index 2a237fdcb..000000000 --- a/qcsrc/common/triggers/subs.qc +++ /dev/null @@ -1,369 +0,0 @@ -#include "subs.qh" -void SUB_NullThink(entity this) { } - -void SUB_CalcMoveDone(entity this); -void SUB_CalcAngleMoveDone(entity this); - -/* -================== -SUB_Friction - -Applies some friction to this -================== -*/ -.float friction; -void SUB_Friction (entity this) -{ - this.nextthink = time; - if(IS_ONGROUND(this)) - this.velocity = this.velocity * (1 - frametime * this.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 - delete(ent); - } -} - -void SUB_SetFade_Think (entity this) -{ - if(this.alpha == 0) - this.alpha = 1; - setthink(this, SUB_SetFade_Think); - this.nextthink = time; - this.alpha -= frametime * this.fade_rate; - if (this.alpha < 0.01) - SUB_VanishOrRemove(this); - else - this.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; - setthink(ent, SUB_SetFade_Think); - ent.nextthink = when; -} - -/* -============= -SUB_CalcMove - -calculate this.velocity and this.nextthink to reach dest from -this.origin traveling at speed -=============== -*/ -void SUB_CalcMoveDone(entity this) -{ - // After moving, set origin to exact final destination - - setorigin (this, this.finaldest); - this.velocity = '0 0 0'; - this.nextthink = -1; - if (this.think1 && this.think1 != SUB_CalcMoveDone) - this.think1 (this); -} - -.float platmovetype_turn; -void SUB_CalcMove_controller_think (entity this) -{ - float traveltime; - float phasepos; - float nexttick; - vector delta; - vector delta2; - vector veloc; - vector angloc; - vector nextpos; - delta = this.destvec; - delta2 = this.destvec2; - if(time < this.animstate_endtime) - { - nexttick = time + PHYS_INPUT_FRAMETIME; - - traveltime = this.animstate_endtime - this.animstate_starttime; - phasepos = (nexttick - this.animstate_starttime) / traveltime; // range: [0, 1] - phasepos = cubic_speedfunc(this.platmovetype_start, this.platmovetype_end, phasepos); - nextpos = this.origin + (delta * phasepos) + (delta2 * phasepos * phasepos); - // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning) - - if(this.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 - vector v = this.owner.angles; - v.x -= 360 * floor((v.x - destangle_x) / 360 + 0.5); - v.y -= 360 * floor((v.y - destangle_y) / 360 + 0.5); - v.z -= 360 * floor((v.z - destangle_z) / 360 + 0.5); - this.owner.angles = v; - angloc = destangle - this.owner.angles; - angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame - this.owner.avelocity = angloc; - } - if(nexttick < this.animstate_endtime) - veloc = nextpos - this.owner.origin; - else - veloc = this.finaldest - this.owner.origin; - veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame - - this.owner.velocity = veloc; - this.nextthink = nexttick; - } - else - { - // derivative: delta + 2 * delta2 (e.g. for angle positioning) - entity own = this.owner; - setthink(own, this.think1); - // set the owner's reference to this entity to NULL - own.move_controller = NULL; - delete(this); - getthink(own)(own); - } -} - -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 - - setorigin(controller, org); - 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 - - setorigin(controller, org); - 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 (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func) -{ - float traveltime; - entity controller; - - if (!tspeed) - objerror (this, "No speed is defined!"); - - this.think1 = func; - this.finaldest = tdest; - setthink(this, SUB_CalcMoveDone); - - switch(tspeedtype) - { - default: - case TSPEED_START: - traveltime = 2 * vlen(tcontrol - this.origin) / tspeed; - break; - case TSPEED_END: - traveltime = 2 * vlen(tcontrol - tdest) / tspeed; - break; - case TSPEED_LINEAR: - traveltime = vlen(tdest - this.origin) / tspeed; - break; - case TSPEED_TIME: - traveltime = tspeed; - break; - } - - if (traveltime < 0.1) // useless anim - { - this.velocity = '0 0 0'; - this.nextthink = this.ltime + 0.1; - return; - } - - // delete the previous controller, otherwise changing movement midway is glitchy - if (this.move_controller != NULL) - { - delete(this.move_controller); - } - controller = new(SUB_CalcMove_controller); - controller.owner = this; - this.move_controller = controller; - controller.platmovetype = this.platmovetype; - controller.platmovetype_start = this.platmovetype_start; - controller.platmovetype_end = this.platmovetype_end; - SUB_CalcMove_controller_setbezier(controller, this.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; - setthink(controller, SUB_CalcMove_controller_think); - controller.think1 = getthink(this); - - // the thinking is now done by the controller - setthink(this, SUB_NullThink); // for PushMove - this.nextthink = this.ltime + traveltime; - - // invoke controller - getthink(controller)(controller); -} - -void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func) -{ - vector delta; - float traveltime; - - if (!tspeed) - objerror (this, "No speed is defined!"); - - this.think1 = func; - this.finaldest = tdest; - setthink(this, SUB_CalcMoveDone); - - if (tdest == this.origin) - { - this.velocity = '0 0 0'; - this.nextthink = this.ltime + 0.1; - return; - } - - delta = tdest - this.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 || (this.platmovetype_start == 1 && this.platmovetype_end == 1)) // is this correct? - { - this.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division - this.nextthink = this.ltime + traveltime; - return; - } - - // now just run like a bezier curve... - SUB_CalcMove_Bezier(this, (this.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func); -} - -void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func) -{ - SUB_CalcMove(ent, tdest, tspeedtype, tspeed, func); -} - -/* -============= -SUB_CalcAngleMove - -calculate this.avelocity and this.nextthink to reach destangle from -this.angles rotating - -The calling function should make sure this.setthink is valid -=============== -*/ -void SUB_CalcAngleMoveDone(entity this) -{ - // After rotating, set angle to exact final angle - this.angles = this.finalangle; - this.avelocity = '0 0 0'; - this.nextthink = -1; - if (this.think1 && this.think1 != SUB_CalcAngleMoveDone) // avoid endless loops - this.think1 (this); -} - -// FIXME: I fixed this function only for rotation around the main axes -void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func) -{ - if (!tspeed) - objerror (this, "No speed is defined!"); - - // take the shortest distance for the angles - this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5); - this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5); - this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5); - vector delta = destangle - this.angles; - float traveltime; - - switch(tspeedtype) - { - default: - case TSPEED_START: - case TSPEED_END: - case TSPEED_LINEAR: - traveltime = vlen (delta) / tspeed; - break; - case TSPEED_TIME: - traveltime = tspeed; - break; - } - - this.think1 = func; - this.finalangle = destangle; - setthink(this, SUB_CalcAngleMoveDone); - - if (traveltime < 0.1) - { - this.avelocity = '0 0 0'; - this.nextthink = this.ltime + 0.1; - return; - } - - this.avelocity = delta * (1 / traveltime); - this.nextthink = this.ltime + traveltime; -} - -void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func) -{ - SUB_CalcAngleMove (ent, destangle, tspeedtype, tspeed, func); -} diff --git a/qcsrc/common/triggers/subs.qh b/qcsrc/common/triggers/subs.qh deleted file mode 100644 index 8d4e40650..000000000 --- a/qcsrc/common/triggers/subs.qh +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once -#include "defs.qh" - -void SUB_SetFade (entity ent, float when, float fading_time); -void SUB_VanishOrRemove (entity ent); - -.vector finaldest, finalangle; //plat.qc stuff -.void(entity this) think1; -.float state; -.float t_length, t_width; - -.vector destvec; -.vector destvec2; - -.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; - -.entity move_controller; - -#ifdef CSQC -// this stuff is defined in the server side engine VM, so we must define it separately here -.float takedamage; -const int DAMAGE_NO = 0; -const int DAMAGE_YES = 1; -const int DAMAGE_AIM = 2; - -.string noise, noise1, noise2, noise3; // contains names of wavs to play - -.float max_health; // players maximum health is stored here -#endif diff --git a/qcsrc/common/triggers/target/_mod.inc b/qcsrc/common/triggers/target/_mod.inc deleted file mode 100644 index afd1050b9..000000000 --- a/qcsrc/common/triggers/target/_mod.inc +++ /dev/null @@ -1,11 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/target/_mod.qh b/qcsrc/common/triggers/target/_mod.qh deleted file mode 100644 index 7c9fbeec5..000000000 --- a/qcsrc/common/triggers/target/_mod.qh +++ /dev/null @@ -1,11 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/target/changelevel.qc b/qcsrc/common/triggers/target/changelevel.qc deleted file mode 100644 index 114fd8718..000000000 --- a/qcsrc/common/triggers/target/changelevel.qc +++ /dev/null @@ -1,55 +0,0 @@ -#include "changelevel.qh" -#ifdef SVQC -.string chmap, gametype; -.entity chlevel_targ; - -void target_changelevel_use(entity this, entity actor, entity trigger) -{ - if(this.spawnflags & CHANGELEVEL_MULTIPLAYER) - { - // simply don't react if a non-player triggers it - if(!IS_PLAYER(actor)) { return; } - - actor.chlevel_targ = this; - - int plnum = 0; - int realplnum = 0; - // let's not count bots - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - ++realplnum; - if(it.chlevel_targ == this) - ++plnum; - }); - if(plnum < ceil(realplnum * min(1, this.count))) // 70% of players - return; - } - - if(this.gametype != "") - MapInfo_SwitchGameType(MapInfo_Type_FromString(this.gametype)); - - if (this.chmap == "") - localcmd("endmatch\n"); - else - localcmd(strcat("changelevel ", this.chmap, "\n")); -} - -/*target_changelevel -Target to change/end level -KEYS: -chmap: map to switch to, leave empty for endmatch -gametype: gametype for the next map -count: fraction of real players that need to trigger this entity for levelchange -SPAWNFLAGS: -CHANGELEVEL_MULTIPLAYER: multiplayer support -*/ - -spawnfunc(target_changelevel) -{ - this.use = target_changelevel_use; - - if(!this.count) - { - this.count = 0.7; - } -} -#endif diff --git a/qcsrc/common/triggers/target/changelevel.qh b/qcsrc/common/triggers/target/changelevel.qh deleted file mode 100644 index f6e206edc..000000000 --- a/qcsrc/common/triggers/target/changelevel.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - - -const int CHANGELEVEL_MULTIPLAYER = BIT(1); diff --git a/qcsrc/common/triggers/target/include.qc b/qcsrc/common/triggers/target/include.qc deleted file mode 100644 index a45c65ed0..000000000 --- a/qcsrc/common/triggers/target/include.qc +++ /dev/null @@ -1,11 +0,0 @@ -#include "include.qh" - -#include "changelevel.qc" -#include "kill.qc" -#include "levelwarp.qc" -#include "location.qc" -#include "music.qc" -#include "spawn.qc" -#include "spawnpoint.qc" -#include "speaker.qc" -#include "voicescript.qc" diff --git a/qcsrc/common/triggers/target/include.qh b/qcsrc/common/triggers/target/include.qh deleted file mode 100644 index c0f7cad44..000000000 --- a/qcsrc/common/triggers/target/include.qh +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "music.qh" diff --git a/qcsrc/common/triggers/target/kill.qc b/qcsrc/common/triggers/target/kill.qc deleted file mode 100644 index 2751c600e..000000000 --- a/qcsrc/common/triggers/target/kill.qc +++ /dev/null @@ -1,26 +0,0 @@ -#include "kill.qh" -#include "location.qh" -#ifdef SVQC - -void target_kill_use(entity this, entity actor, entity trigger) -{ - if(actor.takedamage == DAMAGE_NO) - return; - - if(!actor.iscreature && !actor.damagedbytriggers) - return; - - Damage(actor, this, trigger, 1000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, actor.origin, '0 0 0'); -} - -spawnfunc(target_kill) -{ - this.classname = "target_kill"; - - if (this.message == "") - this.message = "was in the wrong place"; - - this.use = target_kill_use; -} - -#endif diff --git a/qcsrc/common/triggers/target/kill.qh b/qcsrc/common/triggers/target/kill.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/target/kill.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/levelwarp.qc b/qcsrc/common/triggers/target/levelwarp.qc deleted file mode 100644 index 21419cf81..000000000 --- a/qcsrc/common/triggers/target/levelwarp.qc +++ /dev/null @@ -1,21 +0,0 @@ -#include "levelwarp.qh" - -#ifdef SVQC -void target_levelwarp_use(entity this, entity actor, entity trigger) -{ - if(!autocvar_g_campaign) - return; // only in campaign - - if(this.cnt) - CampaignLevelWarp(this.cnt - 1); // specific level - else - CampaignLevelWarp(-1); // next level -} - -spawnfunc(target_levelwarp) -{ - // this.cnt is index (starting from 1) of the campaign level to warp to - // 0 means next level - this.use = target_levelwarp_use; -} -#endif diff --git a/qcsrc/common/triggers/target/levelwarp.qh b/qcsrc/common/triggers/target/levelwarp.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/target/levelwarp.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/location.qc b/qcsrc/common/triggers/target/location.qc deleted file mode 100644 index 5774f45f9..000000000 --- a/qcsrc/common/triggers/target/location.qc +++ /dev/null @@ -1,25 +0,0 @@ -#include "location.qh" -#ifdef SVQC -void target_push_init(entity this); - -spawnfunc(target_location) -{ - this.classname = "target_location"; - // location name in netname - // eventually support: count, teamgame selectors, line of sight? - - target_push_init(this); - - IL_PUSH(g_locations, this); -} - -spawnfunc(info_location) -{ - this.classname = "target_location"; - this.message = this.netname; - - target_push_init(this); - - IL_PUSH(g_locations, this); -} -#endif diff --git a/qcsrc/common/triggers/target/location.qh b/qcsrc/common/triggers/target/location.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/target/location.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/music.qc b/qcsrc/common/triggers/target/music.qc deleted file mode 100644 index 5a63872db..000000000 --- a/qcsrc/common/triggers/target/music.qc +++ /dev/null @@ -1,344 +0,0 @@ -#include "music.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include - #include - #include -#endif - -REGISTER_NET_TEMP(TE_CSQC_TARGET_MUSIC) -REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_MUSIC) - -#ifdef SVQC - -IntrusiveList g_targetmusic_list; -STATIC_INIT(g_targetmusic_list) -{ - g_targetmusic_list = IL_NEW(); -} - -// 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(entity this, int to, bool is) -{ - WriteHeader(to, TE_CSQC_TARGET_MUSIC); - WriteShort(to, etof(this)); - WriteByte(to, this.volume * 255.0 * is); - WriteByte(to, this.fade_time * 16.0); - WriteByte(to, this.fade_rate * 16.0); - WriteByte(to, this.lifetime); - WriteString(to, this.noise); -} -void target_music_reset(entity this) -{ - if (this.targetname == "") - { - target_music_sendto(this, MSG_ALL, true); - } -} -void target_music_kill() -{ - IL_EACH(g_targetmusic_list, true, - { - it.volume = 0; - if (it.targetname == "") - target_music_sendto(it, MSG_ALL, true); - else - target_music_sendto(it, MSG_ALL, false); - }); -} -void target_music_use(entity this, entity actor, entity trigger) -{ - if(!actor) - return; - if(IS_REAL_CLIENT(actor)) - { - msg_entity = actor; - target_music_sendto(this, MSG_ONE, true); - } - FOREACH_CLIENT(IS_SPEC(it) && it.enemy == actor, { - msg_entity = it; - target_music_sendto(this, MSG_ONE, true); - }); -} -spawnfunc(target_music) -{ - this.use = target_music_use; - this.reset = target_music_reset; - if(!this.volume) - this.volume = 1; - IL_PUSH(g_targetmusic_list, this); - if(this.targetname == "") - target_music_sendto(this, MSG_INIT, true); - else - target_music_sendto(this, MSG_INIT, false); -} -void TargetMusic_RestoreGame() -{ - IL_EACH(g_targetmusic_list, true, - { - if(it.targetname == "") - target_music_sendto(it, MSG_INIT, true); - else - target_music_sendto(it, MSG_INIT, false); - }); -} -// values: -// volume -// noise -// targetname -// fade_time -// spawnflags: -// START_DISABLED -// can be disabled/enabled for everyone with relays -bool trigger_music_SendEntity(entity this, entity to, int sendflags) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC); - WriteByte(MSG_ENTITY, sendflags); - if(sendflags & SF_MUSIC_ORIGIN) - { - WriteVector(MSG_ENTITY, this.origin); - } - if(sendflags & SF_TRIGGER_INIT) - { - if(this.model != "null") - { - WriteShort(MSG_ENTITY, this.modelindex); - WriteVector(MSG_ENTITY, this.mins); - WriteVector(MSG_ENTITY, this.maxs); - } - else - { - WriteShort(MSG_ENTITY, 0); - WriteVector(MSG_ENTITY, this.maxs); - } - WriteByte(MSG_ENTITY, this.volume * 255.0); - WriteByte(MSG_ENTITY, this.fade_time * 16.0); - WriteByte(MSG_ENTITY, this.fade_rate * 16.0); - WriteString(MSG_ENTITY, this.noise); - } - if(sendflags & SF_TRIGGER_UPDATE) - { - WriteByte(MSG_ENTITY, this.active); - } - return true; -} -void trigger_music_reset(entity this) -{ - if(this.spawnflags & START_DISABLED) - { - this.setactive(this, ACTIVE_NOT); - } - else - { - this.setactive(this, ACTIVE_ACTIVE); - } -} - -spawnfunc(trigger_music) -{ - if(this.model != "") - { - _setmodel(this, this.model); - } - if(!this.volume) - { - this.volume = 1; - } - if(!this.modelindex) - { - setorigin(this, this.origin + this.mins); - setsize(this, '0 0 0', this.maxs - this.mins); - } - - this.setactive = generic_netlinked_setactive; - this.use = generic_netlinked_legacy_use; // backwards compatibility - this.reset = trigger_music_reset; - this.reset(this); - - Net_LinkEntity(this, false, 0, trigger_music_SendEntity); -} -#elif defined(CSQC) - -entity TargetMusic_list; -STATIC_INIT(TargetMusic_list) -{ - TargetMusic_list = LL_NEW(); -} - -void TargetMusic_Advance() -{ - // run AFTER all the thinks! - entity best = music_default; - if (music_target && time < music_target.lifetime) - { - best = music_target; - } - if (music_trigger) - { - best = music_trigger; - } - LL_EACH(TargetMusic_list, it.noise, { - const float vol0 = (getsoundtime(it, CH_BGM_SINGLE) >= 0) ? it.lastvol : -1; - if (it == best) - { - // increase volume - it.state = (it.fade_time > 0) ? bound(0, it.state + frametime / it.fade_time, 1) : 1; - } - else - { - // decrease volume - it.state = (it.fade_rate > 0) ? bound(0, it.state - frametime / it.fade_rate, 1) : 0; - } - const float vol = it.state * it.volume * autocvar_bgmvolume; - if (vol != vol0) - { - if(vol0 < 0) - sound7(it, CH_BGM_SINGLE, it.noise, vol, ATTEN_NONE, 0, BIT(4)); // restart - else - sound7(it, CH_BGM_SINGLE, "", vol, ATTEN_NONE, 0, BIT(4)); - it.lastvol = vol; - } - }); - music_trigger = NULL; - bgmtime = (best) ? getsoundtime(best, CH_BGM_SINGLE) : gettime(GETTIME_CDTRACK); -} - -NET_HANDLE(TE_CSQC_TARGET_MUSIC, bool isNew) -{ - Net_TargetMusic(); - return true; -} - -void Net_TargetMusic() -{ - const int id = ReadShort(); - const float vol = ReadByte() / 255.0; - const float fai = ReadByte() / 16.0; - const float fao = ReadByte() / 16.0; - const float tim = ReadByte(); - const string noi = ReadString(); - - entity e = NULL; - LL_EACH(TargetMusic_list, it.count == id, { e = it; break; }); - if (!e) - { - LL_PUSH(TargetMusic_list, e = new_pure(TargetMusic)); - e.count = id; - } - if(e.noise != noi) - { - strcpy(e.noise, noi); - precache_sound(e.noise); - _sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE); - if(getsoundtime(e, CH_BGM_SINGLE) < 0) - { - LOG_TRACEF("Cannot initialize sound %s", e.noise); - strfree(e.noise); - } - } - e.volume = vol; - e.fade_time = fai; - e.fade_rate = fao; - if(vol > 0) - { - if(tim == 0) - { - music_default = e; - if(!music_disabled) - { - e.state = 2; - cvar_settemp("music_playlist_index", "-1"); // don't use playlists - localcmd("cd stop\n"); // just in case - music_disabled = 1; - } - } - else - { - music_target = e; - e.lifetime = time + tim; - } - } -} - -void Ent_TriggerMusic_Think(entity this) -{ - if(this.active == ACTIVE_NOT) - { - return; - } - vector org = (csqcplayer) ? csqcplayer.origin : view_origin; - if(WarpZoneLib_BoxTouchesBrush(org + STAT(PL_MIN), org + STAT(PL_MAX), this, NULL)) - { - music_trigger = this; - } -} - -void Ent_TriggerMusic_Remove(entity this) -{ - strfree(this.noise); -} - -NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew) -{ - int sendflags = ReadByte(); - if(sendflags & SF_MUSIC_ORIGIN) - { - this.origin = ReadVector(); - } - if(sendflags & SF_TRIGGER_INIT) - { - this.modelindex = ReadShort(); - if(this.modelindex) - { - this.mins = ReadVector(); - this.maxs = ReadVector(); - } - else - { - this.mins = '0 0 0'; - this.maxs = ReadVector(); - } - - this.volume = ReadByte() / 255.0; - this.fade_time = ReadByte() / 16.0; - this.fade_rate = ReadByte() / 16.0; - string s = this.noise; - strcpy(this.noise, ReadString()); - if(this.noise != s) - { - precache_sound(this.noise); - sound7(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE, 0, BIT(4)); - if(getsoundtime(this, CH_BGM_SINGLE) < 0) - { - LOG_WARNF("Cannot initialize sound %s", this.noise); - strfree(this.noise); - } - } - } - if(sendflags & SF_TRIGGER_UPDATE) - { - this.active = ReadByte(); - } - - setorigin(this, this.origin); - setsize(this, this.mins, this.maxs); - this.draw = Ent_TriggerMusic_Think; - if(isnew) - { - LL_PUSH(TargetMusic_list, this); - IL_PUSH(g_drawables, this); - } - return true; -} - -#endif diff --git a/qcsrc/common/triggers/target/music.qh b/qcsrc/common/triggers/target/music.qh deleted file mode 100644 index ccf3f674e..000000000 --- a/qcsrc/common/triggers/target/music.qh +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -.float lifetime; - -const int SF_MUSIC_ORIGIN = BIT(2); - -#ifdef CSQC -float music_disabled; -entity music_default; -entity music_target; -entity music_trigger; -// FIXME also control bgmvolume here, to not require a target_music for the default track. - -entityclass(TargetMusic); -classfield(TargetMusic) .int state; -classfield(TargetMusic) .float lastvol; - -void TargetMusic_Advance(); - -void Net_TargetMusic(); - -void Ent_TriggerMusic_Think(entity this); - -void Ent_TriggerMusic_Remove(entity this); - -#elif defined(SVQC) -void target_music_kill(); -#endif diff --git a/qcsrc/common/triggers/target/spawn.qc b/qcsrc/common/triggers/target/spawn.qc deleted file mode 100644 index 9c999ed4d..000000000 --- a/qcsrc/common/triggers/target/spawn.qc +++ /dev/null @@ -1,340 +0,0 @@ -#include "spawn.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include -#endif - -#ifdef SVQC - -// spawner entity -// "classname" "target_spawn" -// "message" "fieldname value fieldname value ..." -// "spawnflags" -// ON_MAPLOAD = trigger on map load - -float target_spawn_initialized; -.void(entity this) 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(entity this) -{ - _setmodel(this, this.model); -} - -void target_spawn_helper_setsize(entity this) -{ - setsize(this, this.mins, this.maxs); -} - -void target_spawn_edit_entity(entity this, entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act, entity trigger) -{ - float i, n, valuefieldpos; - string key, value, valuefield, valueoffset, valueoffsetrandom; - entity valueent; - vector data, data2; - - 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 - { - LOG_INFO("target_spawn: invalid/unknown entity key ", key, " specified, ignored!"); - 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 = this; - value = ""; - } - else if(value == "activator") - { - valueent = act; - value = ""; - } - else if(value == "other") - { - valueent = trigger; - value = ""; - } - else if(value == "pusher") - { - if(time < act.pushltime) - valueent = act.pusher; - else - valueent = NULL; - 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 = NULL; - value = ftos(time); - } - else - { - LOG_INFO("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!"); - continue; - } - - if(valuefield == "") - { - if(value == "") - value = ftos(etof(valueent)); - } - else - { - if(value != "") - { - LOG_INFO("target_spawn: try to get a field of a non-entity, ignored!"); - continue; - } - data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield))); - if(data2_y == 0) // undefined field, i.e., invalid type - { - LOG_INFO("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!"); - 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: - LOG_INFO("target_spawn: only string, float and vector fields can do calculations, calculation ignored!"); - 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: - LOG_INFO("target_spawn: only float and vector fields can do random calculations, calculation ignored!"); - break; - } - } - } - } - if(key == "$") - { - if(substring(value, 0, 1) == "_") - value = strcat("target_spawn_helper", value); - putentityfieldstring(target_spawn_spawnfunc_field, e, value); - - e.target_spawn_spawnfunc(e); - - // We called an external function, so we have to re-tokenize msg. - n = tokenize_console(msg); - } - else - { - if(data.y == FIELD_VECTOR) - value = strreplace("'", "", value); // why?!? - putentityfieldstring(data.x, e, value); - } - } -} - -void target_spawn_useon(entity e, entity this, entity actor, entity trigger) -{ - this.target_spawn_activator = actor; - target_spawn_edit_entity( - this, - e, - this.message, - find(NULL, targetname, this.killtarget), - find(NULL, targetname, this.target2), - find(NULL, targetname, this.target3), - find(NULL, targetname, this.target4), - actor, - trigger - ); -} - -bool target_spawn_cancreate(entity this) -{ - float c; - entity e; - - c = this.count; - if(c == 0) // no limit? - return true; - - ++c; // increase count to not include MYSELF - for(e = NULL; (e = findfloat(e, target_spawn_id, this.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 false; - return true; -} - -void target_spawn_use(entity this, entity actor, entity trigger) -{ - if(this.target == "") - { - // spawn new entity - if(!target_spawn_cancreate(this)) - return; - entity e = spawn(); - e.spawnfunc_checked = true; - target_spawn_useon(e, this, actor, trigger); - e.target_spawn_id = this.target_spawn_id; - } - else if(this.target == "*activator") - { - // edit entity - if(actor) - target_spawn_useon(actor, this, actor, trigger); - } - else - { - // edit entity - FOREACH_ENTITY_STRING(targetname, this.target, - { - target_spawn_useon(it, this, actor, trigger); - }); - } -} - -void target_spawn_spawnfirst(entity this) -{ - entity act = this.target_spawn_activator; - if(this.spawnflags & ON_MAPLOAD) - target_spawn_use(this, act, NULL); -} - -void initialize_field_db() -{ - if(!target_spawn_initialized) - { - float n, i; - string fn; - vector prev, next; - float ft; - - n = numentityfields(); - for(i = 0; i < n; ++i) - { - fn = entityfieldname(i); - ft = entityfieldtype(i); - next = 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(next)); - if(fn == "target_spawn_spawnfunc") - target_spawn_spawnfunc_field = i; - } - } - - target_spawn_initialized = 1; - } -} - -spawnfunc(target_spawn) -{ - initialize_field_db(); - this.use = target_spawn_use; - this.message = strzone(strreplace("'", "\"", this.message)); - this.target_spawn_id = ++target_spawn_count; - InitializeEntity(this, target_spawn_spawnfirst, INITPRIO_LAST); -} -#endif diff --git a/qcsrc/common/triggers/target/spawn.qh b/qcsrc/common/triggers/target/spawn.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/target/spawn.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/spawnpoint.qc b/qcsrc/common/triggers/target/spawnpoint.qc deleted file mode 100644 index fe1538551..000000000 --- a/qcsrc/common/triggers/target/spawnpoint.qc +++ /dev/null @@ -1,24 +0,0 @@ -#include "spawnpoint.qh" - -#ifdef SVQC -void target_spawnpoint_use(entity this, entity actor, entity trigger) -{ - if(this.active != ACTIVE_ACTIVE) - return; - - actor.spawnpoint_targ = this; -} - -void target_spawnpoint_reset(entity this) -{ - this.active = ACTIVE_ACTIVE; -} - -// TODO: persistent spawnflag? -spawnfunc(target_spawnpoint) -{ - this.active = ACTIVE_ACTIVE; - this.use = target_spawnpoint_use; - this.reset = target_spawnpoint_reset; -} -#endif diff --git a/qcsrc/common/triggers/target/spawnpoint.qh b/qcsrc/common/triggers/target/spawnpoint.qh deleted file mode 100644 index 2eeb8da62..000000000 --- a/qcsrc/common/triggers/target/spawnpoint.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#ifdef SVQC -.entity spawnpoint_targ; -#endif diff --git a/qcsrc/common/triggers/target/speaker.qc b/qcsrc/common/triggers/target/speaker.qc deleted file mode 100644 index 11c9ad7ba..000000000 --- a/qcsrc/common/triggers/target/speaker.qc +++ /dev/null @@ -1,138 +0,0 @@ -#include "speaker.qh" -#ifdef SVQC -// TODO add a way to do looped sounds with sound(); then complete this entity -void target_speaker_use_off(entity this, entity actor, entity trigger); -void target_speaker_use_activator(entity this, entity actor, entity trigger) -{ - if (!IS_REAL_CLIENT(actor)) - return; - string snd; - if(substring(this.noise, 0, 1) == "*") - { - var .string sample = GetVoiceMessageSampleField(substring(this.noise, 1, -1)); - if(GetPlayerSoundSampleField_notFound) - snd = SND(Null); - else if(actor.(sample) == "") - snd = SND(Null); - else - { - tokenize_console(actor.(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 = this.noise; - msg_entity = actor; - soundto(MSG_ONE, this, CH_TRIGGER, snd, VOL_BASE * this.volume, this.atten); -} -void target_speaker_use_on(entity this, entity actor, entity trigger) -{ - string snd; - if(substring(this.noise, 0, 1) == "*") - { - var .string sample = GetVoiceMessageSampleField(substring(this.noise, 1, -1)); - if(GetPlayerSoundSampleField_notFound) - snd = SND(Null); - else if(actor.(sample) == "") - snd = SND(Null); - else - { - tokenize_console(actor.(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 = this.noise; - _sound(this, CH_TRIGGER_SINGLE, snd, VOL_BASE * this.volume, this.atten); - if(this.spawnflags & (SPEAKER_LOOPED_ON + SPEAKER_LOOPED_OFF)) - this.use = target_speaker_use_off; -} -void target_speaker_use_off(entity this, entity actor, entity trigger) -{ - sound(this, CH_TRIGGER_SINGLE, SND_Null, VOL_BASE * this.volume, this.atten); - this.use = target_speaker_use_on; -} -void target_speaker_reset(entity this) -{ - if(this.spawnflags & SPEAKER_LOOPED_ON) - { - if(this.use == target_speaker_use_on) - target_speaker_use_on(this, NULL, NULL); - } - else if(this.spawnflags & SPEAKER_LOOPED_OFF) - { - if(this.use == target_speaker_use_off) - target_speaker_use_off(this, NULL, NULL); - } -} - -spawnfunc(target_speaker) -{ - // TODO: "*" prefix to sound file name - // TODO: wait and random (just, HOW? random is not a field) - if(this.noise) - precache_sound (this.noise); - - if(!this.atten && (this.spawnflags & SPEAKER_GLOBAL)) - { - LOG_WARN("target_speaker uses legacy spawnflag GLOBAL (BIT(2)), please set atten to -1 instead"); - this.atten = -1; - } - - if(!this.atten) - { - IFTARGETED - this.atten = ATTEN_NORM; - else - this.atten = ATTEN_STATIC; - } - else if(this.atten < 0) - this.atten = 0; - - if(!this.volume) - this.volume = 1; - - IFTARGETED - { - if(this.spawnflags & SPEAKER_ACTIVATOR) - this.use = target_speaker_use_activator; - else if(this.spawnflags & SPEAKER_LOOPED_ON) - { - target_speaker_use_on(this, NULL, NULL); - this.reset = target_speaker_reset; - } - else if(this.spawnflags & SPEAKER_LOOPED_OFF) - { - this.use = target_speaker_use_on; - this.reset = target_speaker_reset; - } - else - this.use = target_speaker_use_on; - } - else if(this.spawnflags & SPEAKER_LOOPED_ON) - { - ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); - delete(this); - } - else if(this.spawnflags & SPEAKER_LOOPED_OFF) - { - objerror(this, "This sound entity can never be activated"); - } - else - { - // Quake/Nexuiz fallback - ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); - delete(this); - } -} -#endif diff --git a/qcsrc/common/triggers/target/speaker.qh b/qcsrc/common/triggers/target/speaker.qh deleted file mode 100644 index 53e0194be..000000000 --- a/qcsrc/common/triggers/target/speaker.qh +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - - -const int SPEAKER_LOOPED_ON = BIT(0); -const int SPEAKER_LOOPED_OFF = BIT(1); -const int SPEAKER_GLOBAL = BIT(2); // legacy, set speaker atten to -1 instead -const int SPEAKER_ACTIVATOR = BIT(3); diff --git a/qcsrc/common/triggers/target/voicescript.qc b/qcsrc/common/triggers/target/voicescript.qc deleted file mode 100644 index 6dfb568a8..000000000 --- a/qcsrc/common/triggers/target/voicescript.qc +++ /dev/null @@ -1,102 +0,0 @@ -#include "voicescript.qh" -#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 = NULL; -} - -void target_voicescript_use(entity this, entity actor, entity trigger) -{ - if(actor.voicescript != this) - { - actor.voicescript = this; - actor.voicescript_index = 0; - actor.voicescript_nextthink = time + this.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(game_stopped) - 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 = NULL; // stop trying then - } - } - } -} - -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; - this.use = target_voicescript_use; - - n = tokenize_console(this.message); - this.cnt = n / 2; - for(i = 0; i+1 < n; i += 2) - { - if(argv(i) == "*") - { - this.cnt = i / 2; - ++i; - } - precache_sound(strcat(this.netname, "/", argv(i), ".wav")); - } -} -#endif diff --git a/qcsrc/common/triggers/target/voicescript.qh b/qcsrc/common/triggers/target/voicescript.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/target/voicescript.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/teleporters.qc b/qcsrc/common/triggers/teleporters.qc deleted file mode 100644 index 25626e01b..000000000 --- a/qcsrc/common/triggers/teleporters.qc +++ /dev/null @@ -1,315 +0,0 @@ -#include "teleporters.qh" - -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include - #include - #include "../constants.qh" - #include "../triggers/subs.qh" - #include "../util.qh" - #include - #include - #include - #include - #include "../deathtypes/all.qh" - #include "../turrets/sv_turrets.qh" - #include "../vehicles/all.qh" - #include "../mapinfo.qh" - #include -#endif - -#ifdef SVQC -float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax) -{ - if (IS_PLAYER(player) && !IS_DEAD(player)) - { - TDEATHLOOP(org) - { - #ifdef SVQC - if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) - #endif - if(IS_PLAYER(head)) - if(!IS_DEAD(head)) - return 1; - } - } - return 0; -} - -void trigger_teleport_link(entity this); - -void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax) -{ - TDEATHLOOP(player.origin) - { - if (IS_PLAYER(player) && player.health >= 1) - { - if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) - { - if(IS_PLAYER(head)) - if(head.health >= 1) - ++tdeath_hit; - Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG.m_id, DMG_NOWEP, head.origin, '0 0 0'); - } - } - else // dead bodies and monsters gib themselves instead of telefragging - Damage (telefragger, teleporter, telefragger, 10000, DEATH_TELEFRAG.m_id, DMG_NOWEP, telefragger.origin, '0 0 0'); - } -} - -void spawn_tdeath(vector v0, entity e, vector v) -{ - tdeath(e, e, e, '0 0 0', '0 0 0'); -} -#endif - -void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags) -{ - entity telefragger; - vector from; - - if(teleporter.owner) - telefragger = teleporter.owner; - else - telefragger = player; - - makevectors (to_angles); - -#ifdef SVQC - if(player.teleportable == TELEPORT_NORMAL) // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers - { - if(teleporter.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps - { - if(tflags & TELEPORT_FLAG_SOUND) - { - string thesound = SND(TELEPORT); - if(teleporter.noise != "") - { - RandomSelection_Init(); - FOREACH_WORD(teleporter.noise, true, - { - RandomSelection_AddString(it, 1, 1); - }); - thesound = RandomSelection_chosen_string; - } - _sound (player, CH_TRIGGER, thesound, VOL_BASE, ATTEN_NORM); - } - if(tflags & TELEPORT_FLAG_PARTICLES) - { - Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); - Send_Effect(EFFECT_TELEPORT, to + v_forward * 32, '0 0 0', 1); - } - teleporter.pushltime = time + 0.2; - } - } -#endif - - // Relocate the player - // assuming to allows PL_MIN to PL_MAX box and some more -#ifdef SVQC - from = player.origin; - setorigin(player, to); - player.oldorigin = to; // don't undo the teleport by unsticking - player.angles = to_angles; - player.fixangle = true; - player.velocity = to_velocity; - BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT); - - makevectors(player.angles); - Reset_ArcBeam(player, v_forward); - UpdateCSQCProjectileAfterTeleport(player); - UpdateItemAfterTeleport(player); -#elif defined(CSQC) - from = player.origin; - setorigin(player, to); - player.angles = to_angles; - player.velocity = to_velocity; - UNSET_ONGROUND(player); - player.iflags |= IFLAG_TELEPORTED | IFLAG_V_ANGLE | IFLAG_ANGLES; - player.csqcmodel_teleported = 1; - player.v_angle = to_angles; - - if(player == csqcplayer) // not for anything but the main player - { - setproperty(VF_ANGLES, player.angles); - setproperty(VF_CL_VIEWANGLES, player.angles); - } -#endif - -#ifdef SVQC - if(IS_PLAYER(player)) - { - if(tflags & TELEPORT_FLAG_TDEATH) - if(player.takedamage && !IS_DEAD(player) && !g_race && !g_cts && (autocvar_g_telefrags || (tflags & TELEPORT_FLAG_FORCE_TDEATH))) - tdeath(player, teleporter, telefragger, telefragmin, telefragmax); - - // player no longer is on ground - UNSET_ONGROUND(player); - - // reset tracking of oldvelocity for impact damage (sudden velocity changes) - player.oldvelocity = player.velocity; - - // reset tracking of who pushed you into a hazard (for kill credit) - if(teleporter.owner) - { - player.pusher = teleporter.owner; - player.pushltime = time + autocvar_g_maxpushtime; - player.istypefrag = PHYS_INPUT_BUTTON_CHAT(player); - } - else - { - player.pushltime = 0; - player.istypefrag = 0; - } - - player.lastteleporttime = time; - player.lastteleport_origin = from; - } -#endif -} - -entity Simple_TeleportPlayer(entity teleporter, entity player) -{ - vector locout; - entity e = NULL; - - // Find the output teleporter - if(teleporter.enemy) - { - e = teleporter.enemy; - } - else - { - // sorry CSQC, random stuff ain't gonna happen -#ifdef SVQC - RandomSelection_Init(); - FOREACH_ENTITY_STRING(targetname, teleporter.target, - { - bool p = true; - if(STAT(TELEPORT_TELEFRAG_AVOID, player)) - { - #ifdef SVQC - locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); - #elif defined(CSQC) - locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); - #endif - if(check_tdeath(player, locout, '0 0 0', '0 0 0')) - p = false; - } - RandomSelection_AddEnt(it, (it.cnt ? it.cnt : 1), p); - }); - e = RandomSelection_chosen_ent; -#endif - } - -#ifdef SVQC - if(!e) { sprint(player, "Teleport destination vanished. Sorry... please complain to the mapper.\n"); } -#elif defined(CSQC) - if(!e) { LOG_INFO("Teleport destination could not be found from CSQC."); } -#endif - - makevectors(e.mangle); - - if(e.speed) - if(vdist(player.velocity, >, e.speed)) - player.velocity = normalize(player.velocity) * max(0, e.speed); - - if(STAT(TELEPORT_MAXSPEED, player)) - if(vdist(player.velocity, >, STAT(TELEPORT_MAXSPEED, player))) - player.velocity = normalize(player.velocity) * max(0, STAT(TELEPORT_MAXSPEED, player)); - - locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); - - TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); - - return e; -} - -void teleport_findtarget(entity this) -{ - bool istrigger = (this.solid == SOLID_TRIGGER); - - int n = 0; - for(entity e = NULL; (e = find(e, targetname, this.target)); ) - { - ++n; -#ifdef SVQC - if(e.move_movetype == MOVETYPE_NONE) - { - entity tracetest_ent = spawn(); - setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST); - tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - waypoint_spawnforteleporter(this, e.origin, 0, tracetest_ent); - delete(tracetest_ent); - } - if(e.classname != "info_teleport_destination") - LOG_INFO("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work."); -#endif - } - - if(n == 0) - { - // no dest! - objerror (this, "Teleporter with nonexistant target"); - return; - } - else if(n == 1) - { - // exactly one dest - bots love that - this.enemy = find(NULL, targetname, this.target); - } - else - { - // have to use random selection every single time - this.enemy = NULL; - } - - // now enable touch - if(istrigger) - settouch(this, Teleport_Touch); -#ifdef SVQC - if(istrigger) - trigger_teleport_link(this); -#endif -} - -entity Teleport_Find(vector mi, vector ma) -{ - IL_EACH(g_teleporters, WarpZoneLib_BoxTouchesBrush(mi, ma, it, NULL), - { - return it; - }); - return NULL; -} - -void WarpZone_PostTeleportPlayer_Callback(entity pl) -{ -#ifdef SVQC - makevectors(pl.angles); - Reset_ArcBeam(pl, v_forward); - UpdateCSQCProjectileAfterTeleport(pl); - UpdateItemAfterTeleport(pl); - if (IS_PLAYER(pl)) anticheat_fixangle(pl); -#endif - // "disown" projectiles after teleport - if(pl.owner) - if(pl.owner == pl.realowner) - { - #ifdef SVQC - if(!(pl.flags & FL_PROJECTILE)) - #elif defined(CSQC) - if(!(pl.flags & BIT(15))) // FL_PROJECTILE - #endif - LOG_INFO("A non-projectile got through a warpzone and its owner cleared. It's a ", pl.classname, "."); - pl.owner = NULL; - } - if(IS_PLAYER(pl)) - { - // reset tracking of oldvelocity for impact damage (sudden velocity changes) - #ifdef SVQC - pl.oldvelocity = pl.velocity; - #endif - } -} diff --git a/qcsrc/common/triggers/teleporters.qh b/qcsrc/common/triggers/teleporters.qh deleted file mode 100644 index 68c5114f4..000000000 --- a/qcsrc/common/triggers/teleporters.qh +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once -#include "defs.qh" - -IntrusiveList g_teleporters; -STATIC_INIT(g_teleporters) { g_teleporters = IL_NEW(); } - -.entity pusher; - -const int TELEPORT_FLAG_SOUND = BIT(0); -const int TELEPORT_FLAG_PARTICLES = BIT(1); -const int TELEPORT_FLAG_TDEATH = BIT(2); -const int TELEPORT_FLAG_FORCE_TDEATH = BIT(3); - -#define TELEPORT_FLAGS_WARPZONE 0 -#define TELEPORT_FLAGS_PORTAL (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH | TELEPORT_FLAG_FORCE_TDEATH) -#define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH) - -// types for .teleportable entity setting -const int TELEPORT_NORMAL = 1; // play sounds/effects etc -const int TELEPORT_SIMPLE = 2; // only do teleport, nothing special - -entity Simple_TeleportPlayer(entity teleporter, entity player); - -void Teleport_Touch(entity this, entity toucher); - -void teleport_findtarget(entity this); - -entity Teleport_Find(vector mi, vector ma); - -void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags); - -#ifdef SVQC - -void trigger_teleport_use(entity this, entity actor, entity trigger); - -#define TDEATHLOOP(o) \ - entity head; \ - vector deathmin; \ - vector deathmax; \ - float deathradius; \ - deathmin = (o) + player.mins; \ - deathmax = (o) + player.maxs; \ - if(telefragmin != telefragmax) \ - { \ - if(deathmin.x > telefragmin.x) deathmin.x = telefragmin.x; \ - if(deathmin.y > telefragmin.y) deathmin.y = telefragmin.y; \ - if(deathmin.z > telefragmin.z) deathmin.z = telefragmin.z; \ - if(deathmax.x < telefragmax.x) deathmax.x = telefragmax.x; \ - if(deathmax.y < telefragmax.y) deathmax.y = telefragmax.y; \ - if(deathmax.z < telefragmax.z) deathmax.z = telefragmax.z; \ - } \ - deathradius = max(vlen(deathmin), vlen(deathmax)); \ - for(head = findradius(o, deathradius); head; head = head.chain) \ - if(head != player) \ - if(head.takedamage) \ - if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax)) - -float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax); -float tdeath_hit; -void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax); - -void spawn_tdeath(vector v0, entity e, vector v); - -void Reset_ArcBeam(entity player, vector forward); - -#endif - -void WarpZone_PostTeleportPlayer_Callback(entity pl); - -#ifdef CSQC -.entity realowner; -#endif diff --git a/qcsrc/common/triggers/trigger/_mod.inc b/qcsrc/common/triggers/trigger/_mod.inc deleted file mode 100644 index 05a496eb3..000000000 --- a/qcsrc/common/triggers/trigger/_mod.inc +++ /dev/null @@ -1,25 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/trigger/_mod.qh b/qcsrc/common/triggers/trigger/_mod.qh deleted file mode 100644 index 2c7477b1d..000000000 --- a/qcsrc/common/triggers/trigger/_mod.qh +++ /dev/null @@ -1,25 +0,0 @@ -// generated file; do not modify -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/qcsrc/common/triggers/trigger/counter.qc b/qcsrc/common/triggers/trigger/counter.qc deleted file mode 100644 index 4c89c4c27..000000000 --- a/qcsrc/common/triggers/trigger/counter.qc +++ /dev/null @@ -1,67 +0,0 @@ -#include "counter.qh" -#ifdef SVQC -void counter_reset(entity this); - -void counter_use(entity this, entity actor, entity trigger) -{ - this.count -= 1; - if (this.count < 0) - return; - - bool doactivate = (this.spawnflags & COUNTER_FIRE_AT_COUNT); - - if (this.count == 0) - { - if(IS_PLAYER(actor) && !(this.spawnflags & SPAWNFLAG_NOMESSAGE)) - Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COMPLETED); - - doactivate = true; - - if(this.respawntime) - { - setthink(this, counter_reset); - this.nextthink = time + this.respawntime; - } - } - else - { - if(IS_PLAYER(actor) && !(this.spawnflags & SPAWNFLAG_NOMESSAGE)) - { - if(this.count >= 4) - Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COUNTER); - else - Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COUNTER_FEWMORE, this.count); - } - } - - if(doactivate) - SUB_UseTargets(this, actor, trigger); -} - -void counter_reset(entity this) -{ - setthink(this, func_null); - this.nextthink = 0; - this.count = this.cnt; -} - -/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage COUNTER_FIRE_AT_COUNT -Acts as an intermediary for an action that takes multiple inputs. - -If nomessage is not set, it will print "1 more.. " etc when triggered and "sequence complete" when finished. -If COUNTER_FIRE_AT_COUNT is set, it will also fire all of its targets at countdown, making it behave like trigger_mulitple with limited shots - -If respawntime is set, it will re-enable itself after the time once the sequence has been completed - -After the counter has been triggered "count" times (default 2), it will fire all of its targets. -*/ -spawnfunc(trigger_counter) -{ - if (!this.count) - this.count = 2; - this.cnt = this.count; - - this.use = counter_use; - this.reset = counter_reset; -} -#endif diff --git a/qcsrc/common/triggers/trigger/counter.qh b/qcsrc/common/triggers/trigger/counter.qh deleted file mode 100644 index 394d15472..000000000 --- a/qcsrc/common/triggers/trigger/counter.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - - -const int COUNTER_FIRE_AT_COUNT = BIT(2); diff --git a/qcsrc/common/triggers/trigger/delay.qc b/qcsrc/common/triggers/trigger/delay.qc deleted file mode 100644 index 2cd4cfd13..000000000 --- a/qcsrc/common/triggers/trigger/delay.qc +++ /dev/null @@ -1,32 +0,0 @@ -#include "delay.qh" -#ifdef SVQC -void delay_delayeduse(entity this) -{ - SUB_UseTargets(this, this.enemy, this.goalentity); - this.enemy = this.goalentity = NULL; -} - -void delay_use(entity this, entity actor, entity trigger) -{ - this.enemy = actor; - this.goalentity = trigger; - setthink(this, delay_delayeduse); - this.nextthink = time + this.wait; -} - -void delay_reset(entity this) -{ - this.enemy = this.goalentity = NULL; - setthink(this, func_null); - this.nextthink = 0; -} - -spawnfunc(trigger_delay) -{ - if(!this.wait) - this.wait = 1; - - this.use = delay_use; - this.reset = delay_reset; -} -#endif diff --git a/qcsrc/common/triggers/trigger/delay.qh b/qcsrc/common/triggers/trigger/delay.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/trigger/delay.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/disablerelay.qc b/qcsrc/common/triggers/trigger/disablerelay.qc deleted file mode 100644 index eee61c993..000000000 --- a/qcsrc/common/triggers/trigger/disablerelay.qc +++ /dev/null @@ -1,29 +0,0 @@ -#include "disablerelay.qh" -#ifdef SVQC -void trigger_disablerelay_use(entity this, entity actor, entity trigger) -{ - int a = 0, b = 0; - - for(entity e = NULL; (e = find(e, targetname, this.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)) - LOG_INFO("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!"); -} - -spawnfunc(trigger_disablerelay) -{ - this.use = trigger_disablerelay_use; -} -#endif diff --git a/qcsrc/common/triggers/trigger/disablerelay.qh b/qcsrc/common/triggers/trigger/disablerelay.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/trigger/disablerelay.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/flipflop.qc b/qcsrc/common/triggers/trigger/flipflop.qc deleted file mode 100644 index 141f3ea9f..000000000 --- a/qcsrc/common/triggers/trigger/flipflop.qc +++ /dev/null @@ -1,23 +0,0 @@ -#include "flipflop.qh" -#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(entity this, entity actor, entity trigger) -{ - this.state = !this.state; - if(this.state) - SUB_UseTargets(this, actor, trigger); -} - -spawnfunc(trigger_flipflop) -{ - if(this.spawnflags & START_ENABLED) - { - this.state = true; - } - this.use = flipflop_use; - this.reset = spawnfunc_trigger_flipflop; // perfect resetter -} - -#endif diff --git a/qcsrc/common/triggers/trigger/flipflop.qh b/qcsrc/common/triggers/trigger/flipflop.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/trigger/flipflop.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/gamestart.qc b/qcsrc/common/triggers/trigger/gamestart.qc deleted file mode 100644 index 72d76d183..000000000 --- a/qcsrc/common/triggers/trigger/gamestart.qc +++ /dev/null @@ -1,28 +0,0 @@ -#include "gamestart.qh" -#ifdef SVQC -void gamestart_use(entity this, entity actor, entity trigger) -{ - SUB_UseTargets(this, this, trigger); - delete(this); -} - -void gamestart_use_this(entity this) -{ - gamestart_use(this, NULL, NULL); -} - -spawnfunc(trigger_gamestart) -{ - this.use = gamestart_use; - this.reset2 = spawnfunc_trigger_gamestart; - - if(this.wait) - { - setthink(this, adaptor_think2use); - this.nextthink = game_starttime + this.wait; - } - else - InitializeEntity(this, gamestart_use_this, INITPRIO_FINDTARGET); -} - -#endif diff --git a/qcsrc/common/triggers/trigger/gamestart.qh b/qcsrc/common/triggers/trigger/gamestart.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/trigger/gamestart.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/gravity.qc b/qcsrc/common/triggers/trigger/gravity.qc deleted file mode 100644 index 1ac0f8768..000000000 --- a/qcsrc/common/triggers/trigger/gravity.qc +++ /dev/null @@ -1,111 +0,0 @@ -#include "gravity.qh" -#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; - delete(own.trigger_gravity_check); - } - else - backtrace("Removing a trigger_gravity_check with no valid owner"); - own.trigger_gravity_check = NULL; -} -void trigger_gravity_check_think(entity this) -{ - // This spawns when a player enters the gravity zone and checks if he left. - // Each frame, this.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(this.count <= 0) - { - if(this.owner.trigger_gravity_check == this) - trigger_gravity_remove(this.owner); - else - delete(this); - return; - } - else - { - this.count -= 1; - this.nextthink = time; - } -} - -// legacy -void trigger_gravity_use(entity this, entity actor, entity trigger) -{ - this.setactive(this, ACTIVE_TOGGLE); -} - -void trigger_gravity_touch(entity this, entity toucher) -{ - float g; - - if(this.active == ACTIVE_NOT) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - g = this.gravity; - - if (!(this.spawnflags & GRAVITY_STICKY)) - { - if(toucher.trigger_gravity_check) - { - if(this == toucher.trigger_gravity_check.enemy) - { - // same? - // NOTE: see explanation in trigger_gravity_check_think - toucher.trigger_gravity_check.count = 2; // gravity one more frame... - return; - } - - // compare prio - if(this.cnt > toucher.trigger_gravity_check.enemy.cnt) - trigger_gravity_remove(toucher); - else - return; - } - toucher.trigger_gravity_check = spawn(); - toucher.trigger_gravity_check.enemy = this; - toucher.trigger_gravity_check.owner = toucher; - toucher.trigger_gravity_check.gravity = toucher.gravity; - setthink(toucher.trigger_gravity_check, trigger_gravity_check_think); - toucher.trigger_gravity_check.nextthink = time; - toucher.trigger_gravity_check.count = 2; - if(toucher.gravity) - g *= toucher.gravity; - } - - if (toucher.gravity != g) - { - toucher.gravity = g; - if(this.noise != "") - _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - UpdateCSQCProjectile(this.owner); - } -} - -spawnfunc(trigger_gravity) -{ - if(this.gravity == 1) - return; - - EXACTTRIGGER_INIT; - settouch(this, trigger_gravity_touch); - if(this.noise != "") - precache_sound(this.noise); - - this.active = ACTIVE_ACTIVE; - this.setactive = generic_setactive; - IFTARGETED - { - // legacy use - this.use = trigger_gravity_use; - if(this.spawnflags & GRAVITY_START_DISABLED) - this.active = ACTIVE_NOT; - } -} -#endif diff --git a/qcsrc/common/triggers/trigger/gravity.qh b/qcsrc/common/triggers/trigger/gravity.qh deleted file mode 100644 index 872f04ad9..000000000 --- a/qcsrc/common/triggers/trigger/gravity.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - - -const int GRAVITY_STICKY = BIT(0); // keep gravity multiplier even after exiting the trigger_gravity -const int GRAVITY_START_DISABLED = BIT(1); diff --git a/qcsrc/common/triggers/trigger/heal.qc b/qcsrc/common/triggers/trigger/heal.qc deleted file mode 100644 index cfcd726fc..000000000 --- a/qcsrc/common/triggers/trigger/heal.qc +++ /dev/null @@ -1,65 +0,0 @@ -#include "heal.qh" -#ifdef SVQC -.float triggerhealtime; -void trigger_heal_touch(entity this, entity toucher) -{ - if (this.active != ACTIVE_ACTIVE) - return; - - // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) - if (toucher.iscreature) - { - if (toucher.takedamage && !IS_DEAD(toucher) && toucher.triggerhealtime < time) - { - bool is_trigger = this.targetname == ""; - if(is_trigger) - EXACTTRIGGER_TOUCH(this, toucher); - if(this.delay > 0) - toucher.triggerhealtime = time + this.delay; - - bool playthesound = (this.spawnflags & HEAL_SOUND_ALWAYS); - if (toucher.health < this.max_health) - { - playthesound = true; - toucher.health = min(toucher.health + this.health, this.max_health); - toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); - } - - if(playthesound) - _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - } - } -} - -void trigger_heal_use(entity this, entity actor, entity trigger) -{ - trigger_heal_touch(this, actor); -} - -void trigger_heal_init(entity this) -{ - this.active = ACTIVE_ACTIVE; - if(!this.delay) - this.delay = 1; - if(!this.health) - this.health = 10; - if(!this.max_health) - this.max_health = 200; // max health topoff for field - if(this.noise == "") - this.noise = "misc/mediumhealth.wav"; - precache_sound(this.noise); -} - -spawnfunc(trigger_heal) -{ - EXACTTRIGGER_INIT; - settouch(this, trigger_heal_touch); - trigger_heal_init(this); -} - -spawnfunc(target_heal) -{ - this.use = trigger_heal_use; - trigger_heal_init(this); -} -#endif diff --git a/qcsrc/common/triggers/trigger/heal.qh b/qcsrc/common/triggers/trigger/heal.qh deleted file mode 100644 index 8dbeea545..000000000 --- a/qcsrc/common/triggers/trigger/heal.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - - -const int HEAL_SOUND_ALWAYS = BIT(2); diff --git a/qcsrc/common/triggers/trigger/hurt.qc b/qcsrc/common/triggers/trigger/hurt.qc deleted file mode 100644 index 966e0cfb0..000000000 --- a/qcsrc/common/triggers/trigger/hurt.qc +++ /dev/null @@ -1,93 +0,0 @@ -#include "hurt.qh" -#ifdef SVQC -void trigger_hurt_use(entity this, entity actor, entity trigger) -{ - if(IS_PLAYER(actor)) - this.enemy = actor; - else - this.enemy = NULL; // let's just destroy it, if taking over is too much work -} - -.float triggerhurttime; -void trigger_hurt_touch(entity this, entity toucher) -{ - if (this.active != ACTIVE_ACTIVE) - return; - - if(this.team) - if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team)) - return; - - // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) - if (toucher.iscreature) - { - if (toucher.takedamage) - if (toucher.triggerhurttime < time) - { - EXACTTRIGGER_TOUCH(this, toucher); - toucher.triggerhurttime = time + 1; - - entity own; - own = this.enemy; - if (!IS_PLAYER(own)) - { - own = this; - this.enemy = NULL; // I still hate you all - } - - Damage (toucher, this, own, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0'); - } - } - else if(toucher.damagedbytriggers) - { - if(toucher.takedamage) - { - EXACTTRIGGER_TOUCH(this, toucher); - Damage(toucher, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0'); - } - } - - return; -} - -/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ? -Any object touching this will be hurt -set dmg to damage amount -default dmg = 1000 -*/ -.entity trigger_hurt_next; -entity trigger_hurt_last; -entity trigger_hurt_first; -spawnfunc(trigger_hurt) -{ - EXACTTRIGGER_INIT; - this.active = ACTIVE_ACTIVE; - settouch(this, trigger_hurt_touch); - this.use = trigger_hurt_use; - this.enemy = world; // I hate you all - if (!this.dmg) - this.dmg = 1000; - if (this.message == "") - this.message = "was in the wrong place"; - if (this.message2 == "") - this.message2 = "was thrown into a world of hurt by"; - // this.message = "someone like %s always gets wrongplaced"; - - if(!trigger_hurt_first) - trigger_hurt_first = this; - if(trigger_hurt_last) - trigger_hurt_last.trigger_hurt_next = this; - trigger_hurt_last = this; -} - -bool tracebox_hits_trigger_hurt(vector start, vector e_min, vector e_max, vector end) -{ - entity th; - - for(th = trigger_hurt_first; th; th = th.trigger_hurt_next) - if(tracebox_hits_box(start, e_min, e_max, end, th.absmin, th.absmax)) - return true; - - return false; -} -#endif diff --git a/qcsrc/common/triggers/trigger/hurt.qh b/qcsrc/common/triggers/trigger/hurt.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/trigger/hurt.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/impulse.qc b/qcsrc/common/triggers/trigger/impulse.qc deleted file mode 100644 index c4e7ae287..000000000 --- a/qcsrc/common/triggers/trigger/impulse.qc +++ /dev/null @@ -1,243 +0,0 @@ -#include "impulse.qh" -// targeted (directional) mode -void trigger_impulse_touch_directional(entity this, entity toucher) -{ - entity targ; - float pushdeltatime; - float str; - - if (this.active != ACTIVE_ACTIVE) - return; - - if (!isPushable(toucher)) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - targ = find(NULL, targetname, this.target); - if(!targ) - { - objerror(this, "trigger_force without a (valid) .target!\n"); - delete(this); - return; - } - - // falloff is not supported because radius is always 0 in directional mode - str = this.strength; - - pushdeltatime = time - toucher.lastpushtime; - if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME) - { - pushdeltatime = 0; - } - toucher.lastpushtime = time; - if(!pushdeltatime) - { - return; - } - - if(this.spawnflags & IMPULSE_DIRECTIONAL_SPEEDTARGET) - { - float addspeed = str - toucher.velocity * normalize(targ.origin - this.origin); - if (addspeed > 0) - { - float accelspeed = min(IMPULSE_DIRECTIONAL_MAX_ACCEL_FACTOR * pushdeltatime * str, addspeed); - toucher.velocity += accelspeed * normalize(targ.origin - this.origin); - } - } - else - toucher.velocity = toucher.velocity + normalize(targ.origin - this.origin) * str * pushdeltatime; - - UNSET_ONGROUND(toucher); - -#ifdef SVQC - UpdateCSQCProjectile(toucher); -#endif -} - -// Directionless (accelerator/decelerator) mode -void trigger_impulse_touch_accel(entity this, entity toucher) -{ - float pushdeltatime; - - if (this.active != ACTIVE_ACTIVE) - return; - - if (!isPushable(toucher)) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - pushdeltatime = time - toucher.lastpushtime; - if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME) - { - pushdeltatime = 0; - } - toucher.lastpushtime = time; - if(!pushdeltatime) - { - return; - } - - // div0: ticrate independent, 1 = identity (not 20) - toucher.velocity = toucher.velocity * (this.strength ** pushdeltatime); - -#ifdef SVQC - UpdateCSQCProjectile(toucher); -#endif -} - -// Spherical (gravity/repulsor) mode -void trigger_impulse_touch_radial(entity this, entity toucher) -{ - float pushdeltatime; - float str; - - if (this.active != ACTIVE_ACTIVE) - return; - - if (!isPushable(toucher)) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - pushdeltatime = time - toucher.lastpushtime; - if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME) - { - pushdeltatime = 0; - } - toucher.lastpushtime = time; - if(!pushdeltatime) - { - return; - } - - setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius); - - str = min(this.radius, vlen(this.origin - toucher.origin)); - - if(this.falloff == FALLOFF_LINEAR) - str = (1 - str / this.radius) * this.strength; // 1 in the inside - else if(this.falloff == FALLOFF_LINEAR_INV) - str = (str / this.radius) * this.strength; // 0 in the inside - else - str = this.strength; - - toucher.velocity = toucher.velocity + normalize(toucher.origin - this.origin) * str * pushdeltatime; - -#ifdef SVQC - UpdateCSQCProjectile(toucher); -#endif -} - -REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_IMPULSE) - -/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ? -Force field --------- 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 much force to add in the direction of .target each second - when .target is set. If not, this is how much to slow down/accelerate - something 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 linear 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). -*/ -#ifdef SVQC -bool trigger_impulse_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE); - - WriteByte(MSG_ENTITY, this.spawnflags); - WriteCoord(MSG_ENTITY, this.radius); - WriteCoord(MSG_ENTITY, this.strength); - WriteByte(MSG_ENTITY, this.falloff); - WriteByte(MSG_ENTITY, this.active); - - trigger_common_write(this, true); - - return true; -} - -void trigger_impulse_link(entity this) -{ - trigger_link(this, trigger_impulse_send); -} - -spawnfunc(trigger_impulse) -{ - this.active = ACTIVE_ACTIVE; - - trigger_init(this); - - if(this.radius) - { - if(!this.strength) - { - this.strength = IMPULSE_DEFAULT_RADIAL_STRENGTH * autocvar_g_triggerimpulse_radial_multiplier; - } - setorigin(this, this.origin); - setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius); - settouch(this, trigger_impulse_touch_radial); - } - else - { - if(this.target) - { - if(!this.strength) - { - this.strength = IMPULSE_DEFAULT_DIRECTIONAL_STRENGTH * autocvar_g_triggerimpulse_directional_multiplier; - } - settouch(this, trigger_impulse_touch_directional); - } - else - { - if(!this.strength) - { - this.strength = IMPULSE_DEFAULT_ACCEL_STRENGTH; - } - this.strength = (this.strength ** autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier; - settouch(this, trigger_impulse_touch_accel); - } - } - - trigger_impulse_link(this); -} -#elif defined(CSQC) -NET_HANDLE(ENT_CLIENT_TRIGGER_IMPULSE, bool isnew) -{ - this.spawnflags = ReadByte(); - this.radius = ReadCoord(); - this.strength = ReadCoord(); - this.falloff = ReadByte(); - this.active = ReadByte(); - - trigger_common_read(this, true); - return = true; - - this.classname = "trigger_impulse"; - this.solid = SOLID_TRIGGER; - this.entremove = trigger_remove_generic; - this.move_time = time; - - if (this.radius) - { - settouch(this, trigger_impulse_touch_radial); - } - else if (this.target) - { - settouch(this, trigger_impulse_touch_directional); - } - else - { - settouch(this, trigger_impulse_touch_accel); - } -} -#endif diff --git a/qcsrc/common/triggers/trigger/impulse.qh b/qcsrc/common/triggers/trigger/impulse.qh deleted file mode 100644 index e86de4a49..000000000 --- a/qcsrc/common/triggers/trigger/impulse.qh +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -// tZorks trigger impulse / gravity -.float radius; -.int falloff; -.float strength; -.float lastpushtime; - -const int FALLOFF_NO = 0; -const int FALLOFF_LINEAR = 1; -const int FALLOFF_LINEAR_INV = 2; - -const int IMPULSE_DIRECTIONAL_SPEEDTARGET = BIT(6); - -const float IMPULSE_DEFAULT_RADIAL_STRENGTH = 2000; -const float IMPULSE_DEFAULT_DIRECTIONAL_STRENGTH = 950; -const float IMPULSE_DEFAULT_ACCEL_STRENGTH = 0.9; - -const float IMPULSE_MAX_PUSHDELTATIME = 0.15; - -const float IMPULSE_DIRECTIONAL_MAX_ACCEL_FACTOR = 8; diff --git a/qcsrc/common/triggers/trigger/include.qc b/qcsrc/common/triggers/trigger/include.qc deleted file mode 100644 index 1c762fc35..000000000 --- a/qcsrc/common/triggers/trigger/include.qc +++ /dev/null @@ -1,25 +0,0 @@ -#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 "keylock.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" -#include "teleport.qc" -#include "viewloc.qc" diff --git a/qcsrc/common/triggers/trigger/include.qh b/qcsrc/common/triggers/trigger/include.qh deleted file mode 100644 index 8aa6b2b17..000000000 --- a/qcsrc/common/triggers/trigger/include.qh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "multi.qh" -#include "jumppads.qh" -#include "secret.qh" -#include "swamp.qh" -#include "keylock.qh" -#include "impulse.qh" -#include "viewloc.qh" diff --git a/qcsrc/common/triggers/trigger/jumppads.qc b/qcsrc/common/triggers/trigger/jumppads.qc deleted file mode 100644 index 5ffdf2d10..000000000 --- a/qcsrc/common/triggers/trigger/jumppads.qc +++ /dev/null @@ -1,676 +0,0 @@ -#include "jumppads.qh" -// TODO: split target_push and put it in the target folder -#ifdef SVQC -#include - -void trigger_push_use(entity this, entity actor, entity trigger) -{ - if(teamplay) - { - this.team = actor.team; - this.SendFlags |= SF_TRIGGER_UPDATE; - } -} -#endif - -REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH) -REGISTER_NET_LINKED(ENT_CLIENT_TARGET_PUSH) - -/* - 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 - pushed_entity - object that is to be pushed - - Returns: velocity for the jump - */ -vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity) -{ - float grav, sdist, zdist, vs, vz, jumpheight; - vector sdir, torg; - - torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5; - - grav = PHYS_GRAVITY(NULL); - if(pushed_entity && PHYS_ENTGRAVITY(pushed_entity)) - grav *= PHYS_ENTGRAVITY(pushed_entity); - - 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) - - float flighttime; - if(zdist < 0) - { - // down-jump - if(ht < 0) - { - // almost straight line type - // jump apex is before the jump - // we must take the larger one - flighttime = solution.y; - } - else - { - // regular jump - // jump apex is during the jump - // we must take the larger one too - 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 - flighttime = solution.x; - } - else - { - // regular jump - // jump apex is during the jump - // we must take the larger one - flighttime = solution.y; - } - } - vs = sdist / flighttime; - - // finally calculate the velocity - return sdir * vs + '0 0 1' * vz; -} - -bool jumppad_push(entity this, entity targ) -{ - if (!isPushable(targ)) - return false; - - if(this.enemy) - { - targ.velocity = trigger_push_calculatevelocity(targ.origin, this.enemy, this.height, targ); - } - else if(this.target && this.target != "") - { - entity e; - RandomSelection_Init(); - for(e = NULL; (e = find(e, targetname, this.target)); ) - { - if(e.cnt) - RandomSelection_AddEnt(e, e.cnt, 1); - else - RandomSelection_AddEnt(e, 1, 1); - } - targ.velocity = trigger_push_calculatevelocity(targ.origin, RandomSelection_chosen_ent, this.height, targ); - } - else - { - targ.velocity = this.movedir; - } - - UNSET_ONGROUND(targ); - -#ifdef CSQC - if (targ.flags & FL_PROJECTILE) - { - targ.angles = vectoangles (targ.velocity); - switch(targ.move_movetype) - { - case MOVETYPE_FLY: - set_movetype(targ, MOVETYPE_TOSS); - targ.gravity = 1; - break; - case MOVETYPE_BOUNCEMISSILE: - set_movetype(targ, MOVETYPE_BOUNCE); - targ.gravity = 1; - break; - } - } -#endif - -#ifdef SVQC - if (IS_PLAYER(targ)) - { - // reset tracking of oldvelocity for impact damage (sudden velocity changes) - targ.oldvelocity = targ.velocity; - - if(this.pushltime < time) // prevent "snorring" sound when a player hits the jumppad more than once - { - // flash when activated - Send_Effect(EFFECT_JUMPPAD, targ.origin, targ.velocity, 1); - _sound (targ, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - this.pushltime = time + 0.2; - } - if(IS_REAL_CLIENT(targ) || IS_BOT_CLIENT(targ)) - { - bool found = false; - for(int i = 0; i < targ.jumppadcount && i < NUM_JUMPPADSUSED; ++i) - if(targ.(jumppadsused[i]) == this) - found = true; - if(!found) - { - targ.(jumppadsused[targ.jumppadcount % NUM_JUMPPADSUSED]) = this; - targ.jumppadcount = targ.jumppadcount + 1; - } - - if(IS_REAL_CLIENT(targ)) - { - if(this.message) - centerprint(targ, this.message); - } - else - { - targ.lastteleporttime = time; - targ.lastteleport_origin = targ.origin; - } - - if (!IS_DEAD(targ)) - animdecide_setaction(targ, ANIMACTION_JUMP, true); - } - else - targ.jumppadcount = 1; - - // reset tracking of who pushed you into a hazard (for kill credit) - targ.pushltime = 0; - targ.istypefrag = 0; - } - - if(this.enemy.target) - SUB_UseTargets(this.enemy, targ, this); - - if (targ.flags & FL_PROJECTILE) - { - targ.angles = vectoangles (targ.velocity); - targ.com_phys_gravity_factor = 1; - switch(targ.move_movetype) - { - case MOVETYPE_FLY: - set_movetype(targ, MOVETYPE_TOSS); - targ.gravity = 1; - break; - case MOVETYPE_BOUNCEMISSILE: - set_movetype(targ, MOVETYPE_BOUNCE); - targ.gravity = 1; - break; - } - UpdateCSQCProjectile(targ); - } -#endif - - return true; -} - -void trigger_push_touch(entity this, entity toucher) -{ - if (this.active == ACTIVE_NOT) - return; - - if(this.team) - if(((this.spawnflags & INVERT_TEAMS) == 0) == (DIFF_TEAM(this, toucher))) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - noref bool success = jumppad_push(this, toucher); - -#ifdef SVQC - if (success && (this.spawnflags & PUSH_ONCE)) - { - settouch(this, func_null); - setthink(this, SUB_Remove); - this.nextthink = time; - } -#endif -} - -#ifdef SVQC -void trigger_push_link(entity this); -void trigger_push_updatelink(entity this); -bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org) -{ - setorigin(tracetest_ent, org); - tracetoss(tracetest_ent, tracetest_ent); - if(trace_startsolid) - return false; - - if (!jp.height) - { - // since tracetoss starting from jumppad's origin often fails when target - // is very close to real destination, start it directly from target's - // origin instead - vector ofs = '0 0 0'; - if (vdist(vec2(tracetest_ent.velocity), <, autocvar_sv_maxspeed)) - ofs = stepheightvec; - - tracetest_ent.velocity.z = 0; - setorigin(tracetest_ent, targ.origin + ofs); - tracetoss(tracetest_ent, tracetest_ent); - if (trace_startsolid && ofs.z) - { - setorigin(tracetest_ent, targ.origin + ofs / 2); - tracetoss(tracetest_ent, tracetest_ent); - if (trace_startsolid && ofs.z) - { - setorigin(tracetest_ent, targ.origin); - tracetoss(tracetest_ent, tracetest_ent); - if (trace_startsolid) - return false; - } - } - } - tracebox(trace_endpos, tracetest_ent.mins, tracetest_ent.maxs, trace_endpos - eZ * 1500, true, tracetest_ent); - return true; -} - -bool trigger_push_testorigin_for_item(entity tracetest_ent, entity item, vector org) -{ - setorigin(tracetest_ent, org); - tracetoss(tracetest_ent, tracetest_ent); - - if(trace_startsolid) - return false; - if (trace_ent == item) - return true; - - tracebox(trace_endpos, tracetest_ent.mins, tracetest_ent.maxs, trace_endpos - eZ * 1500, true, tracetest_ent); - - if (trace_ent == item) - return true; - - return false; -} -#endif - -/// if (item != NULL) returns true if the item can be reached by using this jumppad, false otherwise -/// if (item == NULL) tests jumppad's trajectory and eventually spawns waypoints for it (return value doesn't matter) -bool trigger_push_test(entity this, entity item) -{ - // first calculate a typical start point for the jump - vector org = (this.absmin + this.absmax) * 0.5; - org.z = this.absmax.z - PL_MIN_CONST.z - 7; - - if (this.target) - { - int n = 0; -#ifdef SVQC - vector vel = '0 0 0'; -#endif - for(entity t = NULL; (t = find(t, targetname, this.target)); ) - { - ++n; -#ifdef SVQC - if(t.move_movetype != MOVETYPE_NONE) - continue; - - entity e = spawn(); - setsize(e, PL_MIN_CONST, PL_MAX_CONST); - e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - e.velocity = trigger_push_calculatevelocity(org, t, this.height, e); - - vel = e.velocity; - vector best_target = '0 0 0'; - vector best_org = '0 0 0'; - vector best_vel = '0 0 0'; - bool valid_best_target = false; - if (item) - { - if (!trigger_push_testorigin_for_item(e, item, org)) - { - delete(e); - return false; - } - } - else - { - if (trigger_push_testorigin(e, t, this, org)) - { - best_target = trace_endpos; - best_org = org; - best_vel = e.velocity; - valid_best_target = true; - } - } - - vector new_org; - vector dist = t.origin - org; - if (dist.x || dist.y) // if not perfectly vertical - { - // test trajectory with different starting points, sometimes the trajectory - // starting from the jumppad origin can't reach the real destination - // and destination waypoint ends up near the jumppad itself - vector flatdir = normalize(dist - eZ * dist.z); - vector ofs = flatdir * 0.5 * min(fabs(this.absmax.x - this.absmin.x), fabs(this.absmax.y - this.absmin.y)); - new_org = org + ofs; - - LABEL(new_test) - e.velocity = trigger_push_calculatevelocity(new_org, t, this.height, e); - if (item) - { - if (!trigger_push_testorigin_for_item(e, item, new_org)) - { - delete(e); - return false; - } - } - else - { - vel = e.velocity; - if (vdist(vec2(e.velocity), <, autocvar_sv_maxspeed)) - e.velocity = autocvar_sv_maxspeed * flatdir; - if (trigger_push_testorigin(e, t, this, new_org) && (!valid_best_target || trace_endpos.z > best_target.z + 50)) - { - best_target = trace_endpos; - best_org = new_org; - best_vel = vel; - valid_best_target = true; - } - } - if (ofs && new_org != org - ofs) - { - new_org = org - ofs; - goto new_test; - } - } - - if (item) - { - delete(e); - return true; - } - - if (valid_best_target) - { - if (!(boxesoverlap(this.absmin, this.absmax + eZ * 50, best_target + PL_MIN_CONST, best_target + PL_MAX_CONST))) - { - float velxy = vlen(vec2(best_vel)); - float cost = vlen(vec2(t.origin - best_org)) / velxy; - if(velxy < autocvar_sv_maxspeed) - velxy = autocvar_sv_maxspeed; - cost += vlen(vec2(best_target - t.origin)) / velxy; - waypoint_spawnforteleporter(this, best_target, cost, e); - } - } - delete(e); -#endif - } - - if(item) - return false; - - if(!n) - { - // no dest! -#ifdef SVQC - objerror (this, "Jumppad with nonexistant target"); -#endif - return false; - } - else if(n == 1) - { - // exactly one dest - bots love that - this.enemy = find(NULL, targetname, this.target); - } - else - { - // have to use random selection every single time - this.enemy = NULL; - } - } -#ifdef SVQC - else - { - entity e = spawn(); - setsize(e, PL_MIN_CONST, PL_MAX_CONST); - e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - setorigin(e, org); - e.velocity = this.movedir; - tracetoss(e, e); - if (item) - { - bool r = (trace_ent == item); - delete(e); - return r; - } - if (!(boxesoverlap(this.absmin, this.absmax + eZ * 50, trace_endpos + PL_MIN_CONST, trace_endpos + PL_MAX_CONST))) - waypoint_spawnforteleporter(this, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity), e); - delete(e); - } - - defer(this, 0.1, trigger_push_updatelink); -#endif - return true; -} - -void trigger_push_findtarget(entity this) -{ - trigger_push_test(this, NULL); -} - -#ifdef SVQC -float trigger_push_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH); - - WriteByte(MSG_ENTITY, this.team); - WriteInt24_t(MSG_ENTITY, this.spawnflags); - WriteByte(MSG_ENTITY, this.active); - WriteCoord(MSG_ENTITY, this.height); - - WriteVector(MSG_ENTITY, this.movedir); - - trigger_common_write(this, true); - - return true; -} - -void trigger_push_updatelink(entity this) -{ - this.SendFlags |= SF_TRIGGER_INIT; -} - -void trigger_push_link(entity this) -{ - trigger_link(this, trigger_push_send); -} - -/* - * 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. - */ -spawnfunc(trigger_push) -{ - SetMovedir(this); - - trigger_init(this); - - this.active = ACTIVE_ACTIVE; - this.use = trigger_push_use; - settouch(this, trigger_push_touch); - - // normal push setup - if (!this.speed) - this.speed = 1000; - this.movedir = this.movedir * this.speed * 10; - - if (!this.noise) - this.noise = "misc/jumppad.wav"; - precache_sound (this.noise); - - trigger_push_link(this); // link it now - - IL_PUSH(g_jumppads, this); - - // this must be called to spawn the teleport waypoints for bots - InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET); -} - - -bool target_push_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH); - - WriteByte(MSG_ENTITY, this.cnt); - WriteString(MSG_ENTITY, this.targetname); - WriteVector(MSG_ENTITY, this.origin); - - WriteAngle(MSG_ENTITY, this.angles_x); - WriteAngle(MSG_ENTITY, this.angles_y); - WriteAngle(MSG_ENTITY, this.angles_z); - - return true; -} - -void target_push_use(entity this, entity actor, entity trigger) -{ - if(trigger.classname == "trigger_push" || trigger == this) - return; // WTF, why is this a thing - - jumppad_push(this, actor); -} - -void target_push_link(entity this) -{ - BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); - Net_LinkEntity(this, false, 0, target_push_send); - //this.SendFlags |= 1; // update -} - -void target_push_init(entity this) -{ - this.mangle = this.angles; - setorigin(this, this.origin); - target_push_link(this); -} - -void target_push_init2(entity this) -{ - if(this.target && this.target != "") // we have an old style pusher! - { - InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET); - this.use = target_push_use; - } - - target_push_init(this); // normal push target behaviour can be combined with a legacy pusher? -} - -spawnfunc(target_push) -{ - target_push_init2(this); -} - -spawnfunc(info_notnull) -{ - target_push_init(this); -} -spawnfunc(target_position) -{ - target_push_init(this); -} - -#elif defined(CSQC) - -NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH, bool isnew) -{ - this.classname = "jumppad"; - int mytm = ReadByte(); - if(mytm) - { - this.team = mytm - 1; - } - this.spawnflags = ReadInt24_t(); - this.active = ReadByte(); - this.height = ReadCoord(); - - this.movedir = ReadVector(); - - trigger_common_read(this, true); - - this.entremove = trigger_remove_generic; - this.solid = SOLID_TRIGGER; - settouch(this, trigger_push_touch); - this.move_time = time; - defer(this, 0.25, trigger_push_findtarget); - - return true; -} - -void target_push_remove(entity this) -{ - // strfree(this.classname); - strfree(this.targetname); -} - -NET_HANDLE(ENT_CLIENT_TARGET_PUSH, bool isnew) -{ - this.classname = "push_target"; - this.cnt = ReadByte(); - this.targetname = strzone(ReadString()); - this.origin = ReadVector(); - - this.angles_x = ReadAngle(); - this.angles_y = ReadAngle(); - this.angles_z = ReadAngle(); - - return = true; - - setorigin(this, this.origin); - - this.drawmask = MASK_NORMAL; - this.entremove = target_push_remove; -} -#endif diff --git a/qcsrc/common/triggers/trigger/jumppads.qh b/qcsrc/common/triggers/trigger/jumppads.qh deleted file mode 100644 index cd6adec31..000000000 --- a/qcsrc/common/triggers/trigger/jumppads.qh +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - - -const int PUSH_ONCE = BIT(0); // legacy, deactivate with relay instead -const int PUSH_SILENT = BIT(1); // not used? - -IntrusiveList g_jumppads; -STATIC_INIT(g_jumppads) { g_jumppads = IL_NEW(); } - -.float pushltime; -.float istypefrag; -.float height; - -const int NUM_JUMPPADSUSED = 3; -.float jumppadcount; -.entity jumppadsused[NUM_JUMPPADSUSED]; - -#ifdef SVQC -void SUB_UseTargets(entity this, entity actor, entity trigger); -void trigger_push_use(entity this, entity actor, entity trigger); -bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org); -bool trigger_push_testorigin_for_item(entity tracetest_ent, entity item, vector org); -#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 - pushed_entity - object that is to be pushed - - Returns: velocity for the jump - */ -vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity); - -void trigger_push_touch(entity this, entity toucher); - -.vector dest; -bool trigger_push_test(entity this, entity item); -void trigger_push_findtarget(entity this); - -/* - * 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 -spawnfunc(trigger_push); - -spawnfunc(target_push); -spawnfunc(info_notnull); -spawnfunc(target_position); -#endif diff --git a/qcsrc/common/triggers/trigger/keylock.qc b/qcsrc/common/triggers/trigger/keylock.qc deleted file mode 100644 index 67db14421..000000000 --- a/qcsrc/common/triggers/trigger/keylock.qc +++ /dev/null @@ -1,187 +0,0 @@ -#include "keylock.qh" -/** - * trigger given targets - */ -void trigger_keylock_trigger(entity this, entity actor, string s) -{ - for(entity t = NULL; (t = find(t, targetname, s)); ) - if(t.use) - t.use(t, actor, this); -} - -/** - * kill killtarget of trigger keylock. - */ -void trigger_keylock_kill(string s) -{ - entity t; - for(t = NULL; (t = find(t, targetname, s)); ) - delete(t); -} - -void trigger_keylock_touch(entity this, entity toucher) -{ - bool key_used = false; - bool started_delay = false; - - // only player may trigger the lock - if(!IS_PLAYER(toucher)) - return; - - // check silver key - if(this.itemkeys) - key_used = item_keys_usekey(this, toucher); - - if(this.itemkeys) - { -#ifdef SVQC - // at least one of the keys is missing - if(key_used) - { - // one or more keys were given, but others are still missing! - play2(toucher, this.noise1); - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(this.itemkeys)); - toucher.key_door_messagetime = time + 2; - } - else if(toucher.key_door_messagetime <= time) - { - // no keys were given - play2(toucher, this.noise2); - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(this.itemkeys)); - toucher.key_door_messagetime = time + 2; - } -#endif - - // trigger target2 - if(this.delay <= time || started_delay == true) - if(this.target2) - { - trigger_keylock_trigger(this, toucher, this.target2); - started_delay = true; - this.delay = time + this.wait; - } - } - else - { -#ifdef SVQC - // all keys were given! - play2(toucher, this.noise); - centerprint(toucher, this.message); -#endif - - if(this.target) - trigger_keylock_trigger(this, toucher, this.target); - - if(this.killtarget) - trigger_keylock_kill(this.killtarget); - - delete(this); - } - -} - -REGISTER_NET_LINKED(ENT_CLIENT_KEYLOCK) - -#ifdef SVQC -bool trigger_keylock_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_KEYLOCK); - - WriteInt24_t(MSG_ENTITY, this.itemkeys); - WriteByte(MSG_ENTITY, this.height); - - trigger_common_write(this, true); - - return true; -} - -void trigger_keylock_link(entity this) -{ - // uncomment to network keylocks - //Net_LinkEntity(this, false, 0, trigger_keylock_send); -} - -/*QUAKED trigger_keylock (.0 .5 .8) ? -Keylock trigger. Must target other entities. -This trigger will trigger target entities when all required keys are provided. --------- KEYS -------- -itemkeys: A bit field with key IDs that are needed to open this lock. -sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default) -target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger -target2: trigger all entities with this targetname when triggered without giving it all the required keys. -killtarget: remove all entities with this targetname when triggered with all the needed keys. -message: print this message to the player who activated the trigger when all needed keys have been given. -message2: print this message to the player who activated the trigger when not all of the needed keys have been given. -noise: sound to play when lock gets unlocked (default: see sounds) -noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav) -noise2: sound to play when a key is missing (default: misc/talk.wav) -wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4. ----------NOTES---------- -If spawned without any key specified in itemkeys, this trigger will display an error and remove itself. -message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone. -*/ -spawnfunc(trigger_keylock) -{ - if(!this.itemkeys) { delete(this); return; } - - // set unlocked message - if(this.message == "") - this.message = "Unlocked!"; - - // set default unlock noise - if(this.noise == "") - { - if(this.sounds == 1) - this.noise = "misc/secret.wav"; - else if(this.sounds == 2) - this.noise = strzone(SND(TALK)); - else //if (this.sounds == 3) { - this.noise = "misc/trigger1.wav"; - } - - // set default use key sound - if(this.noise1 == "") - this.noise1 = "misc/decreasevalue.wav"; - - // set closed sourd - if(this.noise2 == "") - this.noise2 = SND(TALK); - - // delay between triggering message2 and trigger2 - if(!this.wait) { this.wait = 5; } - - // precache sounds - precache_sound(this.noise); - precache_sound(this.noise1); - precache_sound(this.noise2); - - EXACTTRIGGER_INIT; - - settouch(this, trigger_keylock_touch); - - trigger_keylock_link(this); -} -#elif defined(CSQC) -void keylock_remove(entity this) -{ - strfree(this.target); - strfree(this.target2); - strfree(this.target3); - strfree(this.target4); - strfree(this.killtarget); - strfree(this.targetname); -} - -NET_HANDLE(ENT_CLIENT_KEYLOCK, bool isnew) -{ - this.itemkeys = ReadInt24_t(); - this.height = ReadByte(); - - trigger_common_read(this, true); - - return = true; - - this.classname = "trigger_keylock"; - this.entremove = keylock_remove; -} -#endif diff --git a/qcsrc/common/triggers/trigger/keylock.qh b/qcsrc/common/triggers/trigger/keylock.qh deleted file mode 100644 index 904c3fa3d..000000000 --- a/qcsrc/common/triggers/trigger/keylock.qh +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#ifdef CSQC -bool item_keys_usekey(entity l, entity p) -{ - int valid = (l.itemkeys & p.itemkeys); // TODO: itemkeys isn't networked or anything! - l.itemkeys &= ~valid; // only some of the needed keys were given - return valid != 0; -} -#endif diff --git a/qcsrc/common/triggers/trigger/magicear.qc b/qcsrc/common/triggers/trigger/magicear.qc deleted file mode 100644 index 16118cb9d..000000000 --- a/qcsrc/common/triggers/trigger/magicear.qc +++ /dev/null @@ -1,200 +0,0 @@ -#include "magicear.qh" -#ifdef SVQC -float magicear_matched; -float W_Tuba_HasPlayed(entity pl, .entity weaponentity, 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; - string savemessage; - - magicear_matched = false; - - dotrigger = ((IS_PLAYER(source)) && (!IS_DEAD(source)) && ((ear.radius == 0) || (vdist(source.origin - ear.origin, <=, ear.radius)))); - domatch = ((ear.spawnflags & MAGICEAR_REPLACE_OUTSIDE) || dotrigger); - - if (!domatch) - return msgin; - - if (!msgin) - { - // we are in TUBA mode! - if (!(ear.spawnflags & MAGICEAR_TUBA)) - return msgin; - - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - if(!W_Tuba_HasPlayed(source, weaponentity, ear.message, ear.movedir_x, !(ear.spawnflags & MAGICEAR_TUBA_EXACTPITCH), ear.movedir_y, ear.movedir_z)) - return msgin; - } - - magicear_matched = true; - - if(dotrigger) - { - savemessage = ear.message; - ear.message = string_null; - SUB_UseTargets(ear, source, NULL); - ear.message = savemessage; - } - - if(ear.netname != "") - return ear.netname; - - return msgin; - } - - if(ear.spawnflags & MAGICEAR_TUBA) // ENOTUBA - return msgin; - - if(privatesay) - { - if(ear.spawnflags & MAGICEAR_IGNORE_TELL) - return msgin; - } - else - { - if(!teamsay) - if(ear.spawnflags & MAGICEAR_IGNORE_SAY) - return msgin; - if(teamsay > 0) - if(ear.spawnflags & MAGICEAR_IGNORE_TEAMSAY) - return msgin; - if(teamsay < 0) - if(ear.spawnflags & MAGICEAR_IGNORE_INVALIDTELL) - return msgin; - } - - matchstart = -1; - l = strlen(ear.message); - - if(ear.spawnflags & MAGICEAR_NODECOLORIZE) - 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) - { - savemessage = ear.message; - ear.message = string_null; - SUB_UseTargets(ear, source, NULL); - ear.message = savemessage; - } - - if(ear.spawnflags & MAGICEAR_REPLACE_WHOLE_MESSAGE) - { - 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 & MAGICEAR_CONTINUE)) - if(magicear_matched) - return msgout; - msgin = msgout; - } - return msgin; -} - -spawnfunc(trigger_magicear) -{ - this.enemy = magicears; - magicears = this; - - // 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) - - this.movedir_x -= 1; // map to tuba instrument numbers -} -#endif diff --git a/qcsrc/common/triggers/trigger/magicear.qh b/qcsrc/common/triggers/trigger/magicear.qh deleted file mode 100644 index 4e705868c..000000000 --- a/qcsrc/common/triggers/trigger/magicear.qh +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - - -const int MAGICEAR_IGNORE_SAY = BIT(0); -const int MAGICEAR_IGNORE_TEAMSAY = BIT(1); -const int MAGICEAR_IGNORE_TELL = BIT(2); -const int MAGICEAR_IGNORE_INVALIDTELL = BIT(3); -const int MAGICEAR_REPLACE_WHOLE_MESSAGE = BIT(4); -const int MAGICEAR_REPLACE_OUTSIDE = BIT(5); -const int MAGICEAR_CONTINUE = BIT(6); -const int MAGICEAR_NODECOLORIZE = BIT(7); -const int MAGICEAR_TUBA = BIT(8); -const int MAGICEAR_TUBA_EXACTPITCH = BIT(9); diff --git a/qcsrc/common/triggers/trigger/monoflop.qc b/qcsrc/common/triggers/trigger/monoflop.qc deleted file mode 100644 index 0c960ba8a..000000000 --- a/qcsrc/common/triggers/trigger/monoflop.qc +++ /dev/null @@ -1,49 +0,0 @@ -#include "monoflop.qh" -#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(entity this, entity actor, entity trigger) -{ - this.nextthink = time + this.wait; - this.enemy = actor; - if(this.state) - return; - this.state = 1; - SUB_UseTargets(this, actor, trigger); -} -void monoflop_fixed_use(entity this, entity actor, entity trigger) -{ - if(this.state) - return; - this.nextthink = time + this.wait; - this.state = 1; - this.enemy = actor; - SUB_UseTargets(this, actor, trigger); -} - -void monoflop_think(entity this) -{ - this.state = 0; - SUB_UseTargets(this, this.enemy, NULL); -} - -void monoflop_reset(entity this) -{ - this.state = 0; - this.nextthink = 0; -} - -spawnfunc(trigger_monoflop) -{ - if(!this.wait) - this.wait = 1; - if(this.spawnflags & MONOFLOP_FIXED) - this.use = monoflop_fixed_use; - else - this.use = monoflop_use; - setthink(this, monoflop_think); - this.state = 0; - this.reset = monoflop_reset; -} -#endif diff --git a/qcsrc/common/triggers/trigger/monoflop.qh b/qcsrc/common/triggers/trigger/monoflop.qh deleted file mode 100644 index c64dffdee..000000000 --- a/qcsrc/common/triggers/trigger/monoflop.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - - -const int MONOFLOP_FIXED = BIT(0); diff --git a/qcsrc/common/triggers/trigger/multi.qc b/qcsrc/common/triggers/trigger/multi.qc deleted file mode 100644 index accfbe8ac..000000000 --- a/qcsrc/common/triggers/trigger/multi.qc +++ /dev/null @@ -1,224 +0,0 @@ -#include "multi.qh" -// NOTE: also contains trigger_once at bottom - -#ifdef SVQC -// the wait time has passed, so set back up for another activation -void multi_wait(entity this) -{ - if (this.max_health) - { - this.health = this.max_health; - this.takedamage = DAMAGE_YES; - this.solid = SOLID_BBOX; - } -} - - -// the trigger was just touched/killed/used -// this.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(entity this) -{ - if (this.nextthink > time) - { - return; // allready been triggered - } - - if(this.spawnflags & ONLY_PLAYERS && !IS_PLAYER(this.enemy)) - { - return; // only players - } - - // TODO: restructure this so that trigger_secret is more independent - if (this.classname == "trigger_secret") - { - if (!IS_PLAYER(this.enemy)) - return; - found_secrets = found_secrets + 1; - WriteByte (MSG_ALL, SVC_FOUNDSECRET); - } - - if (this.noise) - { - _sound (this.enemy, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - } - - // don't trigger again until reset - this.takedamage = DAMAGE_NO; - - SUB_UseTargets(this, this.enemy, this.goalentity); - - if (this.wait > 0) - { - setthink(this, multi_wait); - this.nextthink = time + this.wait; - } - else if (this.wait == 0) - { - multi_wait(this); // waiting finished - } - else - { // we can't just delete(this) here, because this is a touch function - // called while C code is looping through area links... - settouch(this, func_null); - } -} - -void multi_use(entity this, entity actor, entity trigger) -{ - this.goalentity = trigger; - this.enemy = actor; - multi_trigger(this); -} - -void multi_touch(entity this, entity toucher) -{ - if(!(this.spawnflags & ALL_ENTITIES) && !toucher.iscreature) - { - return; - } - - if(this.team) - { - if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team)) - { - return; - } - } - - // if the trigger has an angles field, check player's facing direction - if (this.movedir != '0 0 0') - { - makevectors (toucher.angles); - if (v_forward * this.movedir < 0) - return; // not facing the right way - } - - // if the trigger has pressed keys, check that the player is pressing those keys - if(this.pressedkeys && IS_PLAYER(toucher)) // only for players - { - if(!(CS(toucher).pressedkeys & this.pressedkeys)) - { - return; - } - } - - EXACTTRIGGER_TOUCH(this, toucher); - - this.enemy = toucher; - this.goalentity = toucher; - multi_trigger(this); -} - -void multi_eventdamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(!this.takedamage) - return; - if(this.spawnflags & NOSPLASH) - if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) - return; - if(this.team) - if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != attacker.team)) - return; - this.health = this.health - damage; - if (this.health <= 0) - { - this.enemy = attacker; - this.goalentity = inflictor; - multi_trigger(this); - } -} - -void multi_reset(entity this) -{ - if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) ) - settouch(this, multi_touch); - if (this.max_health) - { - this.health = this.max_health; - this.takedamage = DAMAGE_YES; - this.solid = SOLID_BBOX; - } - setthink(this, func_null); - this.nextthink = 0; - this.team = this.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 -*/ -spawnfunc(trigger_multiple) -{ - this.reset = multi_reset; - if (this.sounds == 1) - this.noise = "misc/secret.wav"; - else if (this.sounds == 2) - this.noise = strzone(SND(TALK)); - else if (this.sounds == 3) - this.noise = "misc/trigger1.wav"; - - if(this.noise) - precache_sound(this.noise); - - if (!this.wait) - this.wait = 0.2; - else if(this.wait < -1) - this.wait = 0; - this.use = multi_use; - - EXACTTRIGGER_INIT; - - this.team_saved = this.team; - IL_PUSH(g_saved_team, this); - - if (this.health) - { - if (this.spawnflags & SPAWNFLAG_NOTOUCH) - objerror (this, "health and notouch don't make sense\n"); - this.canteamdamage = true; - this.max_health = this.health; - this.event_damage = multi_eventdamage; - this.takedamage = DAMAGE_YES; - this.solid = SOLID_BBOX; - setorigin(this, this.origin); // make sure it links into the world - } - else - { - if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) ) - { - settouch(this, multi_touch); - setorigin(this, this.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 -*/ -spawnfunc(trigger_once) -{ - this.wait = -1; - spawnfunc_trigger_multiple(this); -} -#endif diff --git a/qcsrc/common/triggers/trigger/multi.qh b/qcsrc/common/triggers/trigger/multi.qh deleted file mode 100644 index 43358c274..000000000 --- a/qcsrc/common/triggers/trigger/multi.qh +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#ifdef SVQC -void multi_trigger(entity this); -void multi_reset(entity this); - -spawnfunc(trigger_once); -#endif diff --git a/qcsrc/common/triggers/trigger/multivibrator.qc b/qcsrc/common/triggers/trigger/multivibrator.qc deleted file mode 100644 index 932fda13c..000000000 --- a/qcsrc/common/triggers/trigger/multivibrator.qc +++ /dev/null @@ -1,78 +0,0 @@ -#include "multivibrator.qh" -#ifdef SVQC -void multivibrator_send(entity this) -{ - float newstate; - float cyclestart; - - cyclestart = floor((time + this.phase) / (this.wait + this.respawntime)) * (this.wait + this.respawntime) - this.phase; - - newstate = (time < cyclestart + this.wait); - - if(this.state != newstate) - SUB_UseTargets(this, this, NULL); - this.state = newstate; - - if(this.state) - this.nextthink = cyclestart + this.wait + 0.01; - else - this.nextthink = cyclestart + this.wait + this.respawntime + 0.01; -} - -void multivibrator_send_think(entity this) -{ - multivibrator_send(this); -} - -void multivibrator_toggle(entity this, entity actor, entity trigger) -{ - if(this.nextthink == 0) - { - multivibrator_send(this); - } - else - { - if(this.state) - { - SUB_UseTargets(this, actor, trigger); - this.state = 0; - } - this.nextthink = 0; - } -} - -void multivibrator_reset(entity this) -{ - if(!(this.spawnflags & START_ENABLED)) - this.nextthink = 0; // wait for a trigger event - else - this.nextthink = max(1, time); -} - -/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED -"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_ENABLED: assume it is already turned on (when targeted) -*/ -spawnfunc(trigger_multivibrator) -{ - if(!this.wait) - this.wait = 1; - if(!this.respawntime) - this.respawntime = this.wait; - - this.state = 0; - this.use = multivibrator_toggle; - setthink(this, multivibrator_send_think); - this.nextthink = max(1, time); - - IFTARGETED - multivibrator_reset(this); -} -#endif diff --git a/qcsrc/common/triggers/trigger/multivibrator.qh b/qcsrc/common/triggers/trigger/multivibrator.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/trigger/multivibrator.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/relay.qc b/qcsrc/common/triggers/trigger/relay.qc deleted file mode 100644 index f99d364ae..000000000 --- a/qcsrc/common/triggers/trigger/relay.qc +++ /dev/null @@ -1,26 +0,0 @@ -#include "relay.qh" -#ifdef SVQC - -void relay_use(entity this, entity actor, entity trigger) -{ - if(this.active != ACTIVE_ACTIVE) - return; - - SUB_UseTargets(this, actor, trigger); -} - -/*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. -*/ -spawnfunc(trigger_relay) -{ - this.active = ACTIVE_ACTIVE; - this.use = relay_use; - this.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully -} - -spawnfunc(target_relay) -{ - spawnfunc_trigger_relay(this); -} -#endif diff --git a/qcsrc/common/triggers/trigger/relay.qh b/qcsrc/common/triggers/trigger/relay.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/trigger/relay.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_activators.qc b/qcsrc/common/triggers/trigger/relay_activators.qc deleted file mode 100644 index 18c2a40d0..000000000 --- a/qcsrc/common/triggers/trigger/relay_activators.qc +++ /dev/null @@ -1,34 +0,0 @@ -#include "relay_activators.qh" -#ifdef SVQC -void relay_activators_use(entity this, entity actor, entity trigger) -{ - for(entity trg = NULL; (trg = find(trg, targetname, this.target)); ) - { - if (trg.setactive) - trg.setactive(trg, this.cnt); - else - { - //bprint("Not using setactive\n"); - generic_setactive(trg, this.cnt); - } - } -} - -spawnfunc(relay_activate) -{ - this.cnt = ACTIVE_ACTIVE; - this.use = relay_activators_use; -} - -spawnfunc(relay_deactivate) -{ - this.cnt = ACTIVE_NOT; - this.use = relay_activators_use; -} - -spawnfunc(relay_activatetoggle) -{ - this.cnt = ACTIVE_TOGGLE; - this.use = relay_activators_use; -} -#endif diff --git a/qcsrc/common/triggers/trigger/relay_activators.qh b/qcsrc/common/triggers/trigger/relay_activators.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/trigger/relay_activators.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_if.qc b/qcsrc/common/triggers/trigger/relay_if.qc deleted file mode 100644 index 9adcd666e..000000000 --- a/qcsrc/common/triggers/trigger/relay_if.qc +++ /dev/null @@ -1,20 +0,0 @@ -#include "relay_if.qh" -#ifdef SVQC -void trigger_relay_if_use(entity this, entity actor, entity trigger) -{ - int n = this.count; - - // TODO make this generic AND faster than nextent()ing through all, if somehow possible - n = (cvar_string(this.netname) == cvar_string(this.message)); - if(this.spawnflags & RELAYIF_NEGATE) - n = !n; - - if(n) - SUB_UseTargets(this, actor, trigger); -} - -spawnfunc(trigger_relay_if) -{ - this.use = trigger_relay_if_use; -} -#endif diff --git a/qcsrc/common/triggers/trigger/relay_if.qh b/qcsrc/common/triggers/trigger/relay_if.qh deleted file mode 100644 index 6f37aa71d..000000000 --- a/qcsrc/common/triggers/trigger/relay_if.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - - -const int RELAYIF_NEGATE = BIT(0); diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qc b/qcsrc/common/triggers/trigger/relay_teamcheck.qc deleted file mode 100644 index bf03b1542..000000000 --- a/qcsrc/common/triggers/trigger/relay_teamcheck.qc +++ /dev/null @@ -1,37 +0,0 @@ -#include "relay_teamcheck.qh" -#ifdef SVQC -void trigger_relay_teamcheck_use(entity this, entity actor, entity trigger) -{ - if(actor.team) - { - if(this.spawnflags & RELAYTEAMCHECK_INVERT) - { - if(DIFF_TEAM(actor, this)) - SUB_UseTargets(this, actor, trigger); - } - else - { - if(SAME_TEAM(actor, this)) - SUB_UseTargets(this, actor, trigger); - } - } - else - { - if(this.spawnflags & RELAYTEAMCHECK_NOTEAM) - SUB_UseTargets(this, actor, trigger); - } -} - -void trigger_relay_teamcheck_reset(entity this) -{ - this.team = this.team_saved; -} - -spawnfunc(trigger_relay_teamcheck) -{ - this.team_saved = this.team; - IL_PUSH(g_saved_team, this); - this.use = trigger_relay_teamcheck_use; - this.reset = trigger_relay_teamcheck_reset; -} -#endif diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qh b/qcsrc/common/triggers/trigger/relay_teamcheck.qh deleted file mode 100644 index 602d25356..000000000 --- a/qcsrc/common/triggers/trigger/relay_teamcheck.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - - -const int RELAYTEAMCHECK_NOTEAM = BIT(0); -const int RELAYTEAMCHECK_INVERT = BIT(1); diff --git a/qcsrc/common/triggers/trigger/secret.qc b/qcsrc/common/triggers/trigger/secret.qc deleted file mode 100644 index 9377332e2..000000000 --- a/qcsrc/common/triggers/trigger/secret.qc +++ /dev/null @@ -1,90 +0,0 @@ -#include "secret.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include -#endif - -#ifdef SVQC - -void secrets_setstatus(entity this) -{ - // TODO: use global stats! - STAT(SECRETS_TOTAL, this) = secrets_total; - STAT(SECRETS_FOUND, this) = secrets_found; -} - -/** - * A secret has been found (maybe :P) - */ -void trigger_secret_touch(entity this, entity toucher) -{ - // only a player can trigger this - if (!IS_PLAYER(toucher)) - 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(toucher, this.message); - this.message = ""; - - // handle normal trigger features - multi_touch(this, toucher); - // we can't just delete(this) here, because this is a touch function - // called while C code is looping through area links... - //delete(this); -} - -/*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. -*/ -spawnfunc(trigger_secret) -{ - // FIXME: should it be disabled in most modes? - - // update secrets count - secrets_total += 1; - - // add default message - if (this.message == "") - this.message = "You found a secret!"; - - // set default sound - if (this.noise == "") - if (!this.sounds) - this.sounds = 1; // misc/secret.wav - - // this entity can't be a target itself!!!! - this.targetname = ""; - - // you can't just shoot a room to find it, can you? - this.health = 0; - - // a secret can not be delayed - this.delay = 0; - - // convert this trigger to trigger_once - //this.classname = "trigger_once"; - spawnfunc_trigger_once(this); - - // take over the touch() function, so we can mark secret as found - settouch(this, trigger_secret_touch); - // ignore triggering; - this.use = func_null; -} -#endif diff --git a/qcsrc/common/triggers/trigger/secret.qh b/qcsrc/common/triggers/trigger/secret.qh deleted file mode 100644 index fcc55c395..000000000 --- a/qcsrc/common/triggers/trigger/secret.qh +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#ifdef SVQC - -/** - * Total number of secrets on the map. - */ -float secrets_total; - -/** - * Total numbe of secrets found on the map. - */ -float secrets_found; - - -/** - * update secrets status. - */ -void secrets_setstatus(entity this); -#endif diff --git a/qcsrc/common/triggers/trigger/swamp.qc b/qcsrc/common/triggers/trigger/swamp.qc deleted file mode 100644 index 058e41ca2..000000000 --- a/qcsrc/common/triggers/trigger/swamp.qc +++ /dev/null @@ -1,157 +0,0 @@ -#include "swamp.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include - #include - #include -#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 -spawnfunc(trigger_swamp); -#endif -void swamp_touch(entity this, entity toucher); -void swampslug_think(entity this); - - -/* -* 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(entity this) -{ - //Slowly kill the slug - this.health = this.health - 1; - - //Slug dead? then remove curses. - if(this.health <= 0) - { - this.owner.in_swamp = 0; - delete(this); - //centerprint(this.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 (this.owner, this, this, this.dmg, DEATH_SWAMP.m_id, DMG_NOWEP, this.owner.origin, '0 0 0'); -#endif - - this.nextthink = time + this.swamp_interval; -} - -void swamp_touch(entity this, entity toucher) -{ - // If whatever thats touching the swamp is not a player - // or if its a dead player, just dont care abt it. - if(!IS_PLAYER(toucher) || IS_DEAD(toucher)) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - // Chech if player alredy got a swampslug. - if(toucher.in_swamp != 1) - { - // If not attach one. - //centerprint(toucher,"Entering swamp!\n"); - toucher.swampslug = spawn(); - toucher.swampslug.health = 2; - setthink(toucher.swampslug, swampslug_think); - toucher.swampslug.nextthink = time; - toucher.swampslug.owner = toucher; - toucher.swampslug.dmg = this.dmg; - toucher.swampslug.swamp_interval = this.swamp_interval; - toucher.swamp_slowdown = this.swamp_slowdown; - toucher.in_swamp = 1; - return; - } - - //toucher.in_swamp = 1; - - //Revitalize players swampslug - toucher.swampslug.health = 2; -} - -REGISTER_NET_LINKED(ENT_CLIENT_SWAMP) - -#ifdef SVQC -float swamp_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_SWAMP); - - WriteByte(MSG_ENTITY, this.dmg); // can probably get away with using a single byte here - WriteByte(MSG_ENTITY, this.swamp_slowdown); - WriteByte(MSG_ENTITY, this.swamp_interval); - - trigger_common_write(this, false); - - return true; -} - -void swamp_link(entity this) -{ - trigger_link(this, swamp_send); -} - -/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ? -Players gettin into the swamp will -get slowd down and damaged -*/ -spawnfunc(trigger_swamp) -{ - // Init stuff - trigger_init(this); - settouch(this, swamp_touch); - - // Setup default keys, if missing - if(this.dmg <= 0) - this.dmg = 5; - if(this.swamp_interval <= 0) - this.swamp_interval = 1; - if(this.swamp_slowdown <= 0) - this.swamp_slowdown = 0.5; - - swamp_link(this); -} - -#elif defined(CSQC) - -NET_HANDLE(ENT_CLIENT_SWAMP, bool isnew) -{ - this.dmg = ReadByte(); - this.swamp_slowdown = ReadByte(); - this.swamp_interval = ReadByte(); - - trigger_common_read(this, false); - - return = true; - - this.classname = "trigger_swamp"; - this.solid = SOLID_TRIGGER; - settouch(this, swamp_touch); - this.drawmask = MASK_NORMAL; - this.move_time = time; - this.entremove = trigger_remove_generic; -} -#endif diff --git a/qcsrc/common/triggers/trigger/swamp.qh b/qcsrc/common/triggers/trigger/swamp.qh deleted file mode 100644 index f4df98378..000000000 --- a/qcsrc/common/triggers/trigger/swamp.qh +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -.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) diff --git a/qcsrc/common/triggers/trigger/teleport.qc b/qcsrc/common/triggers/trigger/teleport.qc deleted file mode 100644 index 825dd01dd..000000000 --- a/qcsrc/common/triggers/trigger/teleport.qc +++ /dev/null @@ -1,183 +0,0 @@ -#include "teleport.qh" -REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_TELEPORT) - -#ifdef SVQC -void trigger_teleport_use(entity this, entity actor, entity trigger) -{ - if(teamplay) - this.team = actor.team; -#ifdef SVQC - this.SendFlags |= SF_TRIGGER_UPDATE; -#endif -} -#endif - -bool Teleport_Active(entity this, entity player) -{ - if (this.active != ACTIVE_ACTIVE) - return false; - -#ifdef SVQC - if (!player.teleportable) - return false; - - if(player.vehicle) - if(!player.vehicle.teleportable) - return false; - - if(IS_TURRET(player)) - return false; -#elif defined(CSQC) - if(!IS_PLAYER(player)) - return false; -#endif - - if(IS_DEAD(player)) - return false; - - if(this.team) - if(((this.spawnflags & INVERT_TEAMS) == 0) == (DIFF_TEAM(this, player))) - return false; - - return true; -} - -void Teleport_Touch(entity this, entity toucher) -{ - entity player = toucher; - - if(!Teleport_Active(this, player)) - return; - - EXACTTRIGGER_TOUCH(this, player); - -#ifdef SVQC - if(IS_PLAYER(player)) - RemoveGrapplingHooks(player); -#endif - - entity e; - e = Simple_TeleportPlayer(this, player); - -#ifdef SVQC - string s = this.target; this.target = string_null; - SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too? - if (!this.target) this.target = s; - - SUB_UseTargets(e, player, player); -#endif -} - -#ifdef SVQC -void target_teleport_use(entity this, entity actor, entity trigger) -{ - entity player = actor; - - if(!Teleport_Active(this, player)) - return; - - if(IS_PLAYER(player)) - RemoveGrapplingHooks(player); - - entity e = Simple_TeleportPlayer(this, player); - - string s = this.target; this.target = string_null; - SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too? - if (!this.target) - { - this.target = s; - } - - SUB_UseTargets(e, player, player); -} -#endif - -#ifdef SVQC -float trigger_teleport_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_TELEPORT); - - WriteByte(MSG_ENTITY, this.team); - WriteInt24_t(MSG_ENTITY, this.spawnflags); - WriteByte(MSG_ENTITY, this.active); - WriteCoord(MSG_ENTITY, this.speed); - - trigger_common_write(this, true); - - return true; -} - -void trigger_teleport_link(entity this) -{ - //trigger_link(this, trigger_teleport_send); -} - -spawnfunc(trigger_teleport) -{ - this.angles = '0 0 0'; - - this.active = ACTIVE_ACTIVE; - //trigger_init(this); // only for predicted triggers? - EXACTTRIGGER_INIT; - this.use = trigger_teleport_use; - - if(this.noise != "") - FOREACH_WORD(this.noise, true, precache_sound(it)); - - // this must be called to spawn the teleport waypoints for bots - InitializeEntity(this, teleport_findtarget, INITPRIO_FINDTARGET); - - if (this.target == "") - { - objerror (this, "Teleporter with no target"); - return; - } - - IL_PUSH(g_teleporters, this); -} - -spawnfunc(target_teleporter) -{ - if(this.target == "") - { - // actually a destination! - spawnfunc_info_teleport_destination(this); - return; - } - - this.active = ACTIVE_ACTIVE; - - this.use = target_teleport_use; - - if(this.noise != "") - FOREACH_WORD(this.noise, true, precache_sound(it)); - - InitializeEntity(this, teleport_findtarget, INITPRIO_FINDTARGET); -} -#elif defined(CSQC) -NET_HANDLE(ENT_CLIENT_TRIGGER_TELEPORT, bool isnew) -{ - this.classname = "trigger_teleport"; - if(isnew) - IL_PUSH(g_teleporters, this); - int mytm = ReadByte(); - if(mytm) - { - this.team = mytm - 1; - } - this.spawnflags = ReadInt24_t(); - this.active = ReadByte(); - this.speed = ReadCoord(); - - trigger_common_read(this, true); - - this.entremove = trigger_remove_generic; - this.solid = SOLID_TRIGGER; - //settouch(this, trigger_push_touch); - this.move_time = time; - defer(this, 0.25, teleport_findtarget); - - return true; -} - -#endif diff --git a/qcsrc/common/triggers/trigger/teleport.qh b/qcsrc/common/triggers/trigger/teleport.qh deleted file mode 100644 index 6f70f09be..000000000 --- a/qcsrc/common/triggers/trigger/teleport.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/viewloc.qc b/qcsrc/common/triggers/trigger/viewloc.qc deleted file mode 100644 index ba5dcbe44..000000000 --- a/qcsrc/common/triggers/trigger/viewloc.qc +++ /dev/null @@ -1,213 +0,0 @@ -#include "viewloc.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include -#endif - -REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC) -REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC_TRIGGER) - -#ifdef SVQC - -void viewloc_think(entity this) -{ - // we abuse this method, rather than using normal .touch, because touch isn't reliable with multiple clients inside the same trigger, and can't "untouch" entities - - // set myself as current viewloc where possible -#if 1 - FOREACH_CLIENT(IS_PLAYER(it) && it.viewloc == this, - { - it.viewloc = NULL; - }); -#else - entity e; - for(e = NULL; (e = findentity(e, viewloc, this)); ) - e.viewloc = NULL; -#endif - -#if 1 - FOREACH_CLIENT(!it.viewloc && IS_PLAYER(it), - { - vector emin = it.absmin; - vector emax = it.absmax; - if(this.solid == SOLID_BSP) - { - emin -= '1 1 1'; - emax += '1 1 1'; - } - if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick - { - if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate - it.viewloc = this; - } - }); -#else - - for(e = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); e; e = e.chain) - if(!e.viewloc) - if(IS_PLAYER(e)) // should we support non-player entities with this? - //if(!IS_DEAD(e)) // death view is handled separately, we can't override this just yet - { - vector emin = e.absmin; - vector emax = e.absmax; - if(this.solid == SOLID_BSP) - { - emin -= '1 1 1'; - emax += '1 1 1'; - } - if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick - if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, e)) // accurate - e.viewloc = this; - } -#endif - - this.nextthink = time; -} - -bool trigger_viewloc_send(entity this, entity to, int sf) -{ - // CSQC doesn't need to know our origin (yet), as we're only available for referencing - WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER); - - WriteByte(MSG_ENTITY, this.spawnflags); - - WriteEntity(MSG_ENTITY, this.enemy); - WriteEntity(MSG_ENTITY, this.goalentity); - - WriteVector(MSG_ENTITY, this.origin); - - return true; -} - -void viewloc_init(entity this) -{ - entity e; - for(e = NULL; (e = find(e, targetname, this.target)); ) - if(e.classname == "target_viewlocation_start") - { - this.enemy = e; - break; - } - for(e = NULL; (e = find(e, targetname, this.target2)); ) - if(e.classname == "target_viewlocation_end") - { - this.goalentity = e; - break; - } - - if(!this.enemy) { LOG_INFO("^1FAIL!"); delete(this); return; } - - if(!this.goalentity) - this.goalentity = this.enemy; // make them match so CSQC knows what to do - - Net_LinkEntity(this, false, 0, trigger_viewloc_send); - - setthink(this, viewloc_think); - this.nextthink = time; -} - -spawnfunc(trigger_viewlocation) -{ - // we won't check target2 here yet, as it may not even need to exist - if(this.target == "") { LOG_INFO("^1FAIL!"); delete(this); return; } - - EXACTTRIGGER_INIT; - InitializeEntity(this, viewloc_init, INITPRIO_FINDTARGET); -} - -bool viewloc_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC); - - WriteByte(MSG_ENTITY, this.cnt); - - WriteVector(MSG_ENTITY, this.origin); - - WriteAngle(MSG_ENTITY, this.angles_x); - WriteAngle(MSG_ENTITY, this.angles_y); - WriteAngle(MSG_ENTITY, this.angles_z); - - return true; -} - -.float angle; -void viewloc_link(entity this) -{ - if(this.angle) - this.angles_y = this.angle; - Net_LinkEntity(this, false, 0, viewloc_send); -} - -spawnfunc(target_viewlocation_start) -{ - this.classname = "target_viewlocation_start"; - this.cnt = 1; - viewloc_link(this); -} -spawnfunc(target_viewlocation_end) -{ - this.classname = "target_viewlocation_end"; - this.cnt = 2; - viewloc_link(this); -} - -// compatibility -spawnfunc(target_viewlocation) -{ - spawnfunc_target_viewlocation_start(this); -} - -#elif defined(CSQC) - -void trigger_viewloc_updatelink(entity this) -{ - this.enemy = findfloat(NULL, entnum, this.cnt); - this.goalentity = findfloat(NULL, entnum, this.count); -} - -NET_HANDLE(ENT_CLIENT_VIEWLOC_TRIGGER, bool isnew) -{ - this.spawnflags = ReadByte(); - - float point1 = ReadShort(); - float point2 = ReadShort(); - - this.enemy = findfloat(NULL, entnum, point1); - this.goalentity = findfloat(NULL, entnum, point2); - - this.origin = ReadVector(); - - return = true; - - setorigin(this, this.origin); - - this.cnt = point1; - this.count = point2; - - setthink(this, trigger_viewloc_updatelink); - this.nextthink = time + 1; // we need to delay this or else - - this.classname = "trigger_viewlocation"; - this.drawmask = MASK_NORMAL; // not so concerned, but better keep it alive -} - -NET_HANDLE(ENT_CLIENT_VIEWLOC, bool isnew) -{ - this.cnt = ReadByte(); - - this.origin = ReadVector(); - setorigin(this, this.origin); - - this.movedir_x = ReadAngle(); - this.movedir_y = ReadAngle(); - this.movedir_z = ReadAngle(); - - return = true; - - this.classname = ((this.cnt == 2) ? "target_viewlocation_end" : "target_viewlocation_start"); - this.drawmask = MASK_NORMAL; // don't cull it -} - -#endif diff --git a/qcsrc/common/triggers/trigger/viewloc.qh b/qcsrc/common/triggers/trigger/viewloc.qh deleted file mode 100644 index 3c393afd3..000000000 --- a/qcsrc/common/triggers/trigger/viewloc.qh +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - - -const int VIEWLOC_NOSIDESCROLL = BIT(0); // NOTE: currently unimplemented -const int VIEWLOC_FREEAIM = BIT(1); -const int VIEWLOC_FREEMOVE = BIT(2); - -.entity viewloc; - -#ifdef CSQC -.entity goalentity; -.entity enemy; -.vector movedir; -#endif diff --git a/qcsrc/common/triggers/triggers.qc b/qcsrc/common/triggers/triggers.qc deleted file mode 100644 index 9db38a10b..000000000 --- a/qcsrc/common/triggers/triggers.qc +++ /dev/null @@ -1,349 +0,0 @@ -#include "triggers.qh" -void SUB_DontUseTargets(entity this, entity actor, entity trigger) { } - -void SUB_UseTargets(entity this, entity actor, entity trigger); - -void DelayThink(entity this) -{ - SUB_UseTargets (this, this.enemy, NULL); - delete(this); -} - -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); -} - -#ifdef SVQC -void generic_setactive(entity this, int act) -{ - if(act == ACTIVE_TOGGLE) - { - if(this.active == ACTIVE_ACTIVE) - { - this.active = ACTIVE_NOT; - } - else - { - this.active = ACTIVE_ACTIVE; - } - } - else - { - this.active = act; - } -} - -void generic_netlinked_setactive(entity this, int act) -{ - int old_status = this.active; - generic_setactive(this, act); - - if (this.active != old_status) - { - this.SendFlags |= SF_TRIGGER_UPDATE; - } -} - -void generic_netlinked_reset(entity this) -{ - IFTARGETED - { - if(this.spawnflags & START_ENABLED) - { - this.active = ACTIVE_ACTIVE; - } - else - { - this.active = ACTIVE_NOT; - } - } - else - { - this.active = ACTIVE_ACTIVE; - } - - this.SendFlags |= SF_TRIGGER_UPDATE; -} - -// Compatibility with old maps -void generic_netlinked_legacy_use(entity this, entity actor, entity trigger) -{ - LOG_WARNF("Entity %s was (de)activated by a trigger, please update map to use relays", this.targetname); - this.setactive(this, ACTIVE_TOGGLE); -} - -bool autocvar_g_triggers_debug = true; - -void trigger_init(entity this) -{ - string m = this.model; - EXACTTRIGGER_INIT; - if(autocvar_g_triggers_debug) - { - if(m != "") - { - precache_model(m); - _setmodel(this, m); // no precision needed - } - setorigin(this, this.origin); - if(this.scale) - setsize(this, this.mins * this.scale, this.maxs * this.scale); - else - setsize(this, this.mins, this.maxs); - } - - if(autocvar_g_triggers_debug) - BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); -} - -void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc) -{ - setSendEntity(this, sendfunc); - this.SendFlags = 0xFFFFFF; -} - -void trigger_common_write(entity this, bool withtarget) -{ - int f = 0; - if(this.warpzone_isboxy) - BITSET_ASSIGN(f, 1); - if(this.origin != '0 0 0') - BITSET_ASSIGN(f, 4); - if(this.movedir != '0 0 0') - BITSET_ASSIGN(f, 8); - if(this.angles != '0 0 0') - BITSET_ASSIGN(f, 16); - WriteByte(MSG_ENTITY, f); - - if(withtarget) - { - // probably some way to clean this up... - int targbits = 0; - if(this.target && this.target != "") targbits |= BIT(0); - if(this.target2 && this.target2 != "") targbits |= BIT(1); - if(this.target3 && this.target3 != "") targbits |= BIT(2); - if(this.target4 && this.target4 != "") targbits |= BIT(3); - if(this.targetname && this.targetname != "") targbits |= BIT(4); - if(this.killtarget && this.killtarget != "") targbits |= BIT(5); - - WriteByte(MSG_ENTITY, targbits); - - if(targbits & BIT(0)) - WriteString(MSG_ENTITY, this.target); - if(targbits & BIT(1)) - WriteString(MSG_ENTITY, this.target2); - if(targbits & BIT(2)) - WriteString(MSG_ENTITY, this.target3); - if(targbits & BIT(3)) - WriteString(MSG_ENTITY, this.target4); - if(targbits & BIT(4)) - WriteString(MSG_ENTITY, this.targetname); - if(targbits & BIT(5)) - WriteString(MSG_ENTITY, this.killtarget); - } - - if(f & 4) - WriteVector(MSG_ENTITY, this.origin); - - if(f & 8) - WriteVector(MSG_ENTITY, this.movedir); - - if(f & 16) - WriteVector(MSG_ENTITY, this.angles); - - WriteShort(MSG_ENTITY, this.modelindex); - WriteVector(MSG_ENTITY, this.mins); - WriteVector(MSG_ENTITY, this.maxs); - WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255)); -} - -#elif defined(CSQC) - -void trigger_common_read(entity this, bool withtarget) -{ - int f = ReadByte(); - this.warpzone_isboxy = (f & 1); - - if(withtarget) - { - strfree(this.target); - strfree(this.target2); - strfree(this.target3); - strfree(this.target4); - strfree(this.targetname); - strfree(this.killtarget); - - int targbits = ReadByte(); - - this.target = ((targbits & BIT(0)) ? strzone(ReadString()) : string_null); - this.target2 = ((targbits & BIT(1)) ? strzone(ReadString()) : string_null); - this.target3 = ((targbits & BIT(2)) ? strzone(ReadString()) : string_null); - this.target4 = ((targbits & BIT(3)) ? strzone(ReadString()) : string_null); - this.targetname = ((targbits & BIT(4)) ? strzone(ReadString()) : string_null); - this.killtarget = ((targbits & BIT(5)) ? strzone(ReadString()) : string_null); - } - - if(f & 4) - this.origin = ReadVector(); - else - this.origin = '0 0 0'; - setorigin(this, this.origin); - - if(f & 8) - this.movedir = ReadVector(); - else - this.movedir = '0 0 0'; - - if(f & 16) - this.angles = ReadVector(); - else - this.angles = '0 0 0'; - - this.modelindex = ReadShort(); - this.mins = ReadVector(); - this.maxs = ReadVector(); - this.scale = ReadByte() / 16; - setsize(this, this.mins, this.maxs); -} - -void trigger_remove_generic(entity this) -{ - strfree(this.target); - strfree(this.target2); - strfree(this.target3); - strfree(this.target4); - strfree(this.targetname); - strfree(this.killtarget); -} -#endif - - -/* -============================== -SUB_UseTargets - -the global "activator" should be set to the entity that initiated the firing. - -If this.delay is set, a DelayedUse entity will be created that will actually -do the SUB_UseTargets after that many seconds have passed. - -Centerprints any this.message to the activator. - -Removes all entities with a targetname that match this.killtarget, -and removes them, so some events can remove other triggers. - -Search for (string)targetname in all entities that -match (string)this.target and call their .use function - -============================== -*/ - -void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse) -{ -// -// check for a delay -// - if (this.delay) - { - // create a temp object to fire at a later time - entity t = new(DelayedUse); - t.nextthink = time + this.delay; - setthink(t, DelayThink); - t.enemy = actor; - t.message = this.message; - t.killtarget = this.killtarget; - t.target = this.target; - t.target2 = this.target2; - t.target3 = this.target3; - t.target4 = this.target4; - t.antiwall_flag = this.antiwall_flag; - return; - } - - string s; - -// -// print the message -// -#ifdef SVQC - if(this) - if(IS_PLAYER(actor) && this.message != "") - if(IS_REAL_CLIENT(actor)) - { - centerprint(actor, this.message); - if (this.noise == "") - play2(actor, SND(TALK)); - } - -// -// kill the killtagets -// - s = this.killtarget; - if (s != "") - { - for(entity t = NULL; (t = find(t, targetname, s)); ) - delete(t); - } -#endif - -// -// fire targets -// - - if(this.target_random) - RandomSelection_Init(); - - for(int i = 0; i < 4; ++i) - { - switch(i) - { - default: - case 0: s = this.target; break; - case 1: s = this.target2; break; - case 2: s = this.target3; break; - case 3: s = this.target4; break; - } - if (s != "") - { - // Flag to set func_clientwall state - // 1 == deactivate, 2 == activate, 0 == do nothing - int aw_flag = this.antiwall_flag; - for(entity t = NULL; (t = find(t, targetname, s)); ) - { - if(t.use && (t.sub_target_used != time || !preventReuse)) - { - if(this.target_random) - { - RandomSelection_AddEnt(t, 1, 0); - } - else - { - if (t.classname == "func_clientwall" || t.classname == "func_clientillusionary") - t.antiwall_flag = aw_flag; - - t.use(t, actor, this); - if(preventReuse) - t.sub_target_used = time; - } - } - } - } - } - - if(this.target_random && RandomSelection_chosen_ent) - { - RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this); - if(preventReuse) - RandomSelection_chosen_ent.sub_target_used = time; - } -} - -void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); } -void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); } diff --git a/qcsrc/common/triggers/triggers.qh b/qcsrc/common/triggers/triggers.qh deleted file mode 100644 index bfb32696a..000000000 --- a/qcsrc/common/triggers/triggers.qh +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once -#include "defs.qh" - -.bool pushable; - -.float antiwall_flag; // Variable to define what to do with func_clientwall -// 0 == do nothing, 1 == deactivate, 2 == activate - -.float height; - -#define IFTARGETED if(this.targetname && this.targetname != "") - -.float lip; - -// used elsewhere (will fix) -#ifdef SVQC -void trigger_common_write(entity this, bool withtarget); - -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); - -void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger); - -void generic_setactive(entity this, int act); -// generic methods for netlinked entities -void generic_netlinked_reset(entity this); -void generic_netlinked_setactive(entity this, int act); -// WARNING: DON'T USE, ONLY TO KEEP COMPATIBILITY BECAUSE OF SWITCH FROM .state TO .alive!!!! -void generic_netlinked_legacy_use(entity this, entity actor, entity trigger); -#endif - -.float sub_target_used; - -.float volume, atten; - -.vector dest; - -void FixSize(entity e); - -#ifdef CSQC -void trigger_common_read(entity this, bool withtarget); -void trigger_remove_generic(entity this); - -.float active; -.string target; -.string targetname; -#endif diff --git a/qcsrc/common/vehicles/vehicle/racer.qc b/qcsrc/common/vehicles/vehicle/racer.qc index 4f61de816..93ed6d31d 100644 --- a/qcsrc/common/vehicles/vehicle/racer.qc +++ b/qcsrc/common/vehicles/vehicle/racer.qc @@ -1,7 +1,7 @@ #include "racer.qh" #ifdef SVQC -#include +#include bool autocvar_g_vehicle_racer = true; diff --git a/qcsrc/common/weapons/weapon/porto.qc b/qcsrc/common/weapons/weapon/porto.qc index ca460f089..9f79bf440 100644 --- a/qcsrc/common/weapons/weapon/porto.qc +++ b/qcsrc/common/weapons/weapon/porto.qc @@ -1,7 +1,7 @@ #include "porto.qh" #ifdef SVQC -#include +#include REGISTER_MUTATOR(porto_ticker, true); MUTATOR_HOOKFUNCTION(porto_ticker, SV_StartFrame) { diff --git a/qcsrc/lib/warpzone/server.qc b/qcsrc/lib/warpzone/server.qc index ce4535452..2805c0050 100644 --- a/qcsrc/lib/warpzone/server.qc +++ b/qcsrc/lib/warpzone/server.qc @@ -6,7 +6,7 @@ #elif defined(SVQC) #include #include - #include + #include #include #include #include diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index f52974020..91b5c463d 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -18,8 +18,8 @@ #include #include -#include -#include +#include +#include #include diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc index 36f25d79f..9334f011a 100644 --- a/qcsrc/server/bot/default/navigation.qc +++ b/qcsrc/server/bot/default/navigation.qc @@ -13,7 +13,7 @@ #include #include -#include +#include .float speed; diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index 22e77b4ea..aa3890452 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -6,7 +6,7 @@ #include "g_damage.qh" #include "race.qh" -#include "../common/triggers/teleporters.qh" +#include "../common/mapobjects/teleporters.qh" #include "mutators/_mod.qh" @@ -22,9 +22,9 @@ #include -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/subs.qh" -#include "../common/triggers/func/breakable.qh" +#include "../common/mapobjects/func/breakable.qh" #include "../lib/csqcmodel/sv_model.qh" diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index bd71d982d..b8cd3cdab 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -33,9 +33,9 @@ #include -#include "../common/triggers/func/conveyor.qh" -#include "../common/triggers/teleporters.qh" -#include "../common/triggers/target/spawnpoint.qh" +#include "../common/mapobjects/func/conveyor.qh" +#include "../common/mapobjects/teleporters.qh" +#include "../common/mapobjects/target/spawnpoint.qh" #include "../common/vehicles/all.qh" @@ -51,9 +51,9 @@ #include "../common/mutators/mutator/waypoints/all.qh" #include "../common/mutators/mutator/instagib/sv_instagib.qh" -#include "../common/triggers/subs.qh" -#include "../common/triggers/triggers.qh" -#include "../common/triggers/trigger/secret.qh" +#include "../common/mapobjects/subs.qh" +#include "../common/mapobjects/triggers.qh" +#include "../common/mapobjects/trigger/secret.qh" #include "../common/minigames/sv_minigames.qh" diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index e0c72dbc2..291d8b178 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include diff --git a/qcsrc/server/g_models.qc b/qcsrc/server/g_models.qc index c8f85247c..b7779368d 100644 --- a/qcsrc/server/g_models.qc +++ b/qcsrc/server/g_models.qc @@ -4,8 +4,8 @@ #include #include "g_subs.qh" #include -#include "../common/triggers/subs.qh" -#include "../common/triggers/triggers.qh" +#include "../common/mapobjects/subs.qh" +#include "../common/mapobjects/triggers.qh" entityclass(BGMScript); classfield(BGMScript) .string bgmscript; diff --git a/qcsrc/server/g_subs.qc b/qcsrc/server/g_subs.qc index d9372e0aa..21c43ea10 100644 --- a/qcsrc/server/g_subs.qc +++ b/qcsrc/server/g_subs.qc @@ -6,7 +6,7 @@ #include "command/common.qh" #include "../common/state.qh" #include "../lib/warpzone/common.qh" -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/subs.qh" spawnfunc(info_null) { diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index a613edc14..ba0b5e96b 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -30,8 +30,8 @@ #include "../common/playerstats.qh" #include "../common/stats.qh" #include "../common/teams.qh" -#include "../common/triggers/trigger/secret.qh" -#include "../common/triggers/target/music.qh" +#include "../common/mapobjects/trigger/secret.qh" +#include "../common/mapobjects/target/music.qh" #include "../common/util.qh" #include "../common/items/_mod.qh" #include diff --git a/qcsrc/server/item_key.qc b/qcsrc/server/item_key.qc index c645c7fac..066fef345 100644 --- a/qcsrc/server/item_key.qc +++ b/qcsrc/server/item_key.qc @@ -1,6 +1,6 @@ #include "item_key.qh" -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/subs.qh" #include "../common/monsters/_mod.qh" #include "../common/notifications/all.qh" #include "../common/util.qh" diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index bdd226b5c..4ba5efb09 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -20,7 +20,7 @@ #include "../common/notifications/all.qh" #include "../common/playerstats.qh" #include "../common/teams.qh" -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/subs.qh" #include "../common/util.qh" #include "../common/turrets/sv_turrets.qh" #include diff --git a/qcsrc/server/mutators/gamemode.qh b/qcsrc/server/mutators/gamemode.qh index b0f42f59e..270667a5d 100644 --- a/qcsrc/server/mutators/gamemode.qh +++ b/qcsrc/server/mutators/gamemode.qh @@ -95,8 +95,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/qcsrc/server/mutators/mutator.qh b/qcsrc/server/mutators/mutator.qh index e051cd697..bea16f7a3 100644 --- a/qcsrc/server/mutators/mutator.qh +++ b/qcsrc/server/mutators/mutator.qh @@ -22,8 +22,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc index 2fcd30163..fa2cd48da 100644 --- a/qcsrc/server/player.qc +++ b/qcsrc/server/player.qc @@ -16,7 +16,7 @@ #include "../common/animdecide.qh" #include "../common/csqcmodel_settings.qh" #include "../common/deathtypes/all.qh" -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/subs.qh" #include "../common/playerstats.qh" #include "../lib/csqcmodel/sv_model.qh" @@ -25,7 +25,7 @@ #include "../common/physics/player.qh" #include "../common/effects/qc/_mod.qh" #include "../common/mutators/mutator/waypoints/waypointsprites.qh" -#include "../common/triggers/include.qh" +#include "../common/mapobjects/include.qh" #include "../common/wepent.qh" #include "weapons/weaponstats.qh" diff --git a/qcsrc/server/portals.qc b/qcsrc/server/portals.qc index ca0dc20fd..758c69bcc 100644 --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@ -6,8 +6,8 @@ #include "../common/constants.qh" #include "../common/deathtypes/all.qh" #include "../common/notifications/all.qh" -#include "../common/triggers/teleporters.qh" -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/teleporters.qh" +#include "../common/mapobjects/subs.qh" #include "../common/util.qh" #include #include "../lib/csqcmodel/sv_model.qh" diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index da86f76f0..c1b272cea 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -14,7 +14,7 @@ #include #include #include -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/subs.qh" #include "../lib/warpzone/util_server.qh" #include "../lib/warpzone/common.qh" #include "../common/mutators/mutator/waypoints/waypointsprites.qh" diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc index e48840883..3a6d36d31 100644 --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@ -6,8 +6,8 @@ #include "../common/constants.qh" #include #include "../common/teams.qh" -#include "../common/triggers/subs.qh" -#include "../common/triggers/target/spawnpoint.qh" +#include "../common/mapobjects/subs.qh" +#include "../common/mapobjects/target/spawnpoint.qh" #include "../common/util.qh" #include "../lib/warpzone/common.qh" #include "../lib/warpzone/util_server.qh" diff --git a/qcsrc/server/weapons/throwing.qc b/qcsrc/server/weapons/throwing.qc index 821a02327..8eb8beeca 100644 --- a/qcsrc/server/weapons/throwing.qc +++ b/qcsrc/server/weapons/throwing.qc @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include