From: Mario Date: Wed, 8 Jul 2015 14:15:25 +0000 (+1000) Subject: Merge master into qc_physics_prehax (blame TimePath if it's completely broken) X-Git-Tag: xonotic-v0.8.1~38^2~5 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=125d619e9ab2a307b15b7ee1a2ededed32c7e84d Merge master into qc_physics_prehax (blame TimePath if it's completely broken) --- 125d619e9ab2a307b15b7ee1a2ededed32c7e84d diff --cc qcsrc/client/bgmscript.qh index fba28762a3,cd70955cb4..5921bb4bc7 --- a/qcsrc/client/bgmscript.qh +++ b/qcsrc/client/bgmscript.qh @@@ -1,8 -1,15 +1,18 @@@ #ifndef BGMSCRIPT_H #define BGMSCRIPT_H - .float just_toggled; + entityclass(BGMScript); + class(BGMScript) .string bgmscript; + class(BGMScript) .float bgmscriptattack; + class(BGMScript) .float bgmscriptdecay; + class(BGMScript) .float bgmscriptsustain; + class(BGMScript) .float bgmscriptrelease; + class(BGMScript) .float just_toggled; + ++#ifdef CSQC void BGMScript_InitEntity(entity e); - float BGMScript(entity e); + float doBGMScript(entity e); + #endif ++ +#endif diff --cc qcsrc/client/casings.qc index 38c5c67517,f9c58957e5..8961af2241 --- a/qcsrc/client/casings.qc +++ b/qcsrc/client/casings.qc @@@ -1,17 -1,18 +1,18 @@@ - #if defined(CSQC) - #include "../common/movetypes/movetypes.qh" - #include "prandom.qh" - #include "rubble.qh" + #include "casings.qh" + #include "_all.qh" - .float cnt; - .float alpha; - .int state; - #elif defined(MENUQC) - #elif defined(SVQC) - #endif -#include "movetypes.qh" ++#include "../common/movetypes/movetypes.qh" + #include "prandom.qh" + #include "rubble.qh" + #include "../common/util.qh" - .bool silent; + .float cnt; + .float alpha; + .int state; + + entityclass(Casing); + class(Casing) .bool silent; void Casing_Delete() { diff --cc qcsrc/client/damage.qc index 836c9163a4,39c422e1cc..3626c44c61 --- a/qcsrc/client/damage.qc +++ b/qcsrc/client/damage.qc @@@ -1,18 -1,23 +1,23 @@@ - #if defined(CSQC) - #include "../dpdefs/csprogsdefs.qh" - #include "defs.qh" - #include "../common/constants.qh" - #include "../common/util.qh" - #include "../common/weapons/weapons.qh" - #include "autocvars.qh" - #include "../common/deathtypes.qh" - #include "damage.qh" - #include "../common/movetypes/movetypes.qh" - #include "prandom.qh" - #include "vehicles/vehicles.qh" - #elif defined(MENUQC) - #elif defined(SVQC) - #endif + #include "damage.qh" + #include "_all.qh" + + #include "gibs.qh" -#include "movetypes.qh" + #include "prandom.qh" + + #include "vehicles/all.qh" + + #include "../common/constants.qh" + #include "../common/deathtypes.qh" ++#include "../common/movetypes/movetypes.qh" + #include "../common/util.qh" + + #include "../common/weapons/all.qh" + + .entity tag_entity; + + .float cnt; + .int state; + .bool isplayermodel; void DamageEffect_Think() { diff --cc qcsrc/client/gibs.qc index eb7d478777,c617106019..298e9d5e67 --- a/qcsrc/client/gibs.qc +++ b/qcsrc/client/gibs.qc @@@ -1,4 -1,17 +1,17 @@@ #include "gibs.qh" + #include "_all.qh" + -#include "movetypes.qh" + #include "prandom.qh" + #include "rubble.qh" + + #include "../common/constants.qh" ++#include "../common/movetypes/movetypes.qh" + #include "../common/util.qh" + + .float scale; + .float alpha; + .float cnt; + .float gravity; void Gib_Delete() { diff --cc qcsrc/client/main.qc index 121438e29f,7648b12977..982056fcbb --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@@ -1,23 -13,39 +13,40 @@@ #include "mapvoting.qh" #include "modeleffects.qh" #include "particles.qh" + #include "prandom.qh" #include "scoreboard.qh" #include "shownames.qh" + #include "sortlist.qh" -#include "target_music.qh" #include "tturrets.qh" #include "tuba.qh" + #include "t_items.qh" #include "wall.qh" #include "waypointsprites.qh" - #include "vehicles/vehicles.qh" + #include "vehicles/bumblebee.qh" + #include "vehicles/all.qh" - #include "../server/vehicles/bumblebee.qh" + #include "weapons/projectile.qh" + #include "../common/buffs.qh" + #include "../common/deathtypes.qh" + #include "../common/mapinfo.qh" + #include "../common/monsters/all.qh" + #include "../common/nades.qh" #include "../common/net_notice.qh" + #include "../common/notifications.qh" + #include "../common/stats.qh" + #include "../common/teams.qh" - #include "../common/monsters/monsters.qh" + #include "../common/items/all.qh" + + #include "../common/weapons/all.qh" + + #include "../csqcmodellib/cl_model.qh" + #include "../csqcmodellib/interpolate.qh" +#include "../common/triggers/include.qh" + #include "../warpzonelib/client.qh" // -------------------------------------------------------------------------- diff --cc qcsrc/client/miscfunctions.qh index 9b97b88c21,6c643962a6..0c2be7fc54 --- a/qcsrc/client/miscfunctions.qh +++ b/qcsrc/client/miscfunctions.qh @@@ -5,24 -5,6 +5,15 @@@ entity players entity teams; float team_count; // real teams +const int INITPRIO_FIRST = 0; +const int INITPRIO_GAMETYPE = 0; +const int INITPRIO_GAMETYPE_FALLBACK = 1; +const int INITPRIO_FINDTARGET = 10; +const int INITPRIO_DROPTOFLOOR = 20; +const int INITPRIO_SETLOCATION = 90; +const int INITPRIO_LINKDOORS = 91; +const int INITPRIO_LAST = 99; + - .void(void) initialize_entity; - .int initialize_entity_order; - .entity initialize_entity_next; - entity initialize_entity_first; - - void InitializeEntity(entity e, void(void) func, int order); - - void InitializeEntitiesRun(); - void AuditLists(); float RegisterPlayer(entity player); diff --cc qcsrc/client/particles.qc index 59b9b75f46,e61071137d..e997d15600 --- a/qcsrc/client/particles.qc +++ b/qcsrc/client/particles.qc @@@ -1,5 -1,226 +1,11 @@@ #include "particles.qh" + #include "_all.qh" + -#include "bgmscript.qh" - + #include "../common/stats.qh" + #include "../common/util.qh" + + #include "../warpzonelib/common.qh" -void Draw_PointParticles() -{ - float n, i, fail; - vector p; - vector sz; - vector o; - o = self.origin; - sz = self.maxs - self.mins; - n = doBGMScript(self); - if(self.absolute == 2) - { - if(n >= 0) - n = self.just_toggled ? self.impulse : 0; - else - n = self.impulse * drawframetime; - } - else - { - n *= self.impulse * drawframetime; - if(self.just_toggled) - if(n < 1) - n = 1; - } - if(n == 0) - return; - fail = 0; - for(i = random(); i <= n && fail <= 64*n; ++i) - { - p = o + self.mins; - p.x += random() * sz.x; - p.y += random() * sz.y; - p.z += random() * sz.z; - if(WarpZoneLib_BoxTouchesBrush(p, p, self, world)) - { - if(self.movedir != '0 0 0') - { - traceline(p, p + normalize(self.movedir) * 4096, 0, world); - p = trace_endpos; - pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count); - } - else - { - pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count); - } - if(self.noise != "") - { - setorigin(self, p); - sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten); - } - self.just_toggled = 0; - } - else if(self.absolute) - { - ++fail; - --i; - } - } - setorigin(self, o); -} - -void Ent_PointParticles_Remove() -{ - if(self.noise) - strunzone(self.noise); - self.noise = string_null; - if(self.bgmscript) - strunzone(self.bgmscript); - self.bgmscript = string_null; -} - -void Ent_PointParticles() -{ - float i; - vector v; - int f = ReadByte(); - if(f & 2) - { - i = ReadCoord(); // density (<0: point, >0: volume) - if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed - self.just_toggled = 1; - self.impulse = i; - } - if(f & 4) - { - self.origin_x = ReadCoord(); - self.origin_y = ReadCoord(); - self.origin_z = ReadCoord(); - } - if(f & 1) - { - self.modelindex = ReadShort(); - if(f & 0x80) - { - if(self.modelindex) - { - self.mins_x = ReadCoord(); - self.mins_y = ReadCoord(); - self.mins_z = ReadCoord(); - self.maxs_x = ReadCoord(); - self.maxs_y = ReadCoord(); - self.maxs_z = ReadCoord(); - } - else - { - self.mins = '0 0 0'; - self.maxs_x = ReadCoord(); - self.maxs_y = ReadCoord(); - self.maxs_z = ReadCoord(); - } - } - else - { - self.mins = self.maxs = '0 0 0'; - } - - self.cnt = ReadShort(); // effect number - - if(f & 0x20) - { - self.velocity = decompressShortVector(ReadShort()); - self.movedir = decompressShortVector(ReadShort()); - } - else - { - self.velocity = self.movedir = '0 0 0'; - } - if(f & 0x40) - { - self.waterlevel = ReadShort() / 16.0; - self.count = ReadByte() / 16.0; - } - else - { - self.waterlevel = 0; - self.count = 1; - } - if(self.noise) - strunzone(self.noise); - if(self.bgmscript) - strunzone(self.bgmscript); - self.noise = strzone(ReadString()); - if(self.noise != "") - { - self.atten = ReadByte() / 64.0; - self.volume = ReadByte() / 255.0; - } - self.bgmscript = strzone(ReadString()); - if(self.bgmscript != "") - { - self.bgmscriptattack = ReadByte() / 64.0; - self.bgmscriptdecay = ReadByte() / 64.0; - self.bgmscriptsustain = ReadByte() / 255.0; - self.bgmscriptrelease = ReadByte() / 64.0; - } - BGMScript_InitEntity(self); - } - - if(f & 2) - { - self.absolute = (self.impulse >= 0); - if(!self.absolute) - { - v = self.maxs - self.mins; - self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube - } - } - - if(f & 0x10) - self.absolute = 2; - - setorigin(self, self.origin); - setsize(self, self.mins, self.maxs); - self.solid = SOLID_NOT; - self.draw = Draw_PointParticles; - self.entremove = Ent_PointParticles_Remove; -} - -void Draw_Rain() -{ - te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color); -} - -void Draw_Snow() -{ - te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color); -} - -void Ent_RainOrSnow() -{ - self.impulse = ReadByte(); // Rain, Snow, or Whatever - self.origin_x = ReadCoord(); - self.origin_y = ReadCoord(); - self.origin_z = ReadCoord(); - self.maxs_x = ReadCoord(); - self.maxs_y = ReadCoord(); - self.maxs_z = ReadCoord(); - self.velocity = decompressShortVector(ReadShort()); - self.count = ReadShort() * 10; - self.glow_color = ReadByte(); // color - - self.mins = -0.5 * self.maxs; - self.maxs = 0.5 * self.maxs; - self.origin = self.origin - self.mins; - - setorigin(self, self.origin); - setsize(self, self.mins, self.maxs); - self.solid = SOLID_NOT; - if(self.impulse) - self.draw = Draw_Rain; - else - self.draw = Draw_Snow; -} - void Net_ReadVortexBeamParticle() { vector shotorg, endpos; diff --cc qcsrc/client/particles.qh index a70aef86d7,586f3f0000..791313f504 --- a/qcsrc/client/particles.qh +++ b/qcsrc/client/particles.qh @@@ -1,19 -1,33 +1,26 @@@ #ifndef PARTICLES_H #define PARTICLES_H - .int dphitcontentsmask; - .int cnt; // effect number - .vector velocity; // particle velocity - .float waterlevel; // direction jitter - .int count; // count multiplier - .int impulse; // density - .string noise; // sound - .float atten; - .float volume; - .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle - .vector movedir; // trace direction + entityclass(PointParticles); + class(PointParticles) .int cnt; // effect number + class(PointParticles) .vector velocity; // particle velocity + class(PointParticles) .float waterlevel; // direction jitter + class(PointParticles) .int count; // count multiplier + class(PointParticles) .int impulse; // density + class(PointParticles) .string noise; // sound + class(PointParticles) .float atten; + class(PointParticles) .float volume; + class(PointParticles) .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle + class(PointParticles) .vector movedir; // trace direction - .float glow_color; // palette index + void Draw_PointParticles(); + + void Ent_PointParticles_Remove(); + + void Ent_PointParticles(); + + class(PointParticles) .float glow_color; // palette index -void Draw_Rain(); - -void Draw_Snow(); - -void Ent_RainOrSnow(); - void Net_ReadVortexBeamParticle(); #endif diff --cc qcsrc/client/progs.src index d221cfc5c1,8ba12b9e71..a9d0c565a7 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@@ -11,12 -11,15 +11,13 @@@ damage.q effects.qc gibs.qc hook.qc - hud_config.qc hud.qc + hud_config.qc -laser.qc main.qc mapvoting.qc miscfunctions.qc modeleffects.qc + movelib.qc -movetypes.qc noise.qc particles.qc player_skeleton.qc @@@ -50,28 -55,16 +54,20 @@@ weapons/projectile.qc // TOD ../common/urllib.qc ../common/util.qc - ../common/command/generic.qc - ../common/command/markup.qc - ../common/command/rpn.qc + ../common/items/all.qc - ../common/monsters/monsters.qc + ../common/monsters/all.qc - ../common/weapons/weapons.qc // TODO + ../common/weapons/all.qc // TODO +../common/triggers/include.qc + ../csqcmodellib/cl_model.qc ../csqcmodellib/cl_player.qc ../csqcmodellib/interpolate.qc - ../server/movelib.qc - +../server/mutators/mutator_multijump.qc + - ../server/vehicles/bumblebee.qc - - ../server/t_items.qc - ../warpzonelib/anglestransform.qc ../warpzonelib/client.qc ../warpzonelib/common.qc diff --cc qcsrc/client/t_items.qc index 0000000000,ca631fcbd5..3981b29eb8 mode 000000,100644..100644 --- a/qcsrc/client/t_items.qc +++ b/qcsrc/client/t_items.qc @@@ -1,0 -1,10 +1,10 @@@ + #include "_all.qh" + -#include "movetypes.qh" + #include "../common/buffs.qh" ++#include "../common/movetypes/movetypes.qh" + #include "../common/util.qh" + #include "../common/weapons/all.qh" + #include "../csqcmodellib/cl_model.qh" + #include "../csqcmodellib/common.qh" + + #include "../server/t_items.qc" diff --cc qcsrc/client/tturrets.qc index 24506bef0c,b74024d508..58c031e4d8 --- a/qcsrc/client/tturrets.qc +++ b/qcsrc/client/tturrets.qc @@@ -1,7 -1,24 +1,25 @@@ #include "tturrets.qh" + #include "_all.qh" + + #include "hud.qh" + #include "movelib.qh" -#include "movetypes.qh" + #include "prandom.qh" + #include "teamradar.qh" #include "waypointsprites.qh" - #include "../server/movelib.qh" + #include "../common/teams.qh" + ++#include "../common/movetypes/movetypes.qh" ++ + #include "../server/tturrets/include/turrets_early.qh" + + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/mathlib.qh" + + .vector colormod; + .float cnt; + .float alpha; + .float gravity; string tid2info_base; string tid2info_head; diff --cc qcsrc/client/vehicles/all.qc index 0000000000,0af4d4b967..ba92d7c319 mode 000000,100644..100644 --- a/qcsrc/client/vehicles/all.qc +++ b/qcsrc/client/vehicles/all.qc @@@ -1,0 -1,1054 +1,1054 @@@ + #include "all.qh" + #include "../_all.qh" + -#include "../movetypes.qh" -#include "../movetypes.qh" ++#include "../../common/movetypes/movetypes.qh" + #include "../prandom.qh" + #include "../scoreboard.qh" + #include "../t_items.qh" + + #include "../../common/buffs.qh" + #include "../../common/constants.qh" ++#include "../../common/movetypes/movetypes.qh" + #include "../../common/stats.qh" + #include "../../common/util.qh" + + #include "../../csqcmodellib/cl_model.qh" + + .float cnt; + + const string hud_bg = "gfx/vehicles/frame.tga"; + const string hud_sh = "gfx/vehicles/vh-shield.tga"; + + const string hud_hp_bar = "gfx/vehicles/bar_up_left.tga"; + const string hud_hp_ico = "gfx/vehicles/health.tga"; + const string hud_sh_bar = "gfx/vehicles/bar_dwn_left.tga"; + const string hud_sh_ico = "gfx/vehicles/shield.tga"; + + const string hud_ammo1_bar = "gfx/vehicles/bar_up_right.tga"; + const string hud_ammo1_ico = "gfx/vehicles/bullets.tga"; + const string hud_ammo2_bar = "gfx/vehicles/bar_dwn_right.tga"; + const string hud_ammo2_ico = "gfx/vehicles/rocket.tga"; + const string hud_energy = "gfx/vehicles/energy.tga"; + + const int SBRM_FIRST = 1; + const int SBRM_VOLLY = 1; + const int SBRM_GUIDE = 2; + const int SBRM_ARTILLERY = 3; + const int SBRM_LAST = 3; + + const int RSM_FIRST = 1; + const int RSM_BOMB = 1; + const int RSM_FLARE = 2; + const int RSM_LAST = 2; + + entity dropmark; + float autocvar_cl_vehicles_hudscale = 0.5; + float autocvar_cl_vehicles_hudalpha = 0.75; + + const string raptor_ico = "gfx/vehicles/raptor.tga"; + const string raptor_gun = "gfx/vehicles/raptor_guns.tga"; + const string raptor_bomb = "gfx/vehicles/raptor_bombs.tga"; + const string raptor_drop = "gfx/vehicles/axh-dropcross.tga"; + string raptor_xhair; + + + + const int MAX_AXH = 4; + entity AuxiliaryXhairs[MAX_AXH]; + + entityclass(AuxiliaryXhair); + class(AuxiliaryXhair) .string axh_image; + class(AuxiliaryXhair) .float axh_fadetime; + class(AuxiliaryXhair) .float axh_drawflag; + class(AuxiliaryXhair) .float axh_scale; + + const string bumb_ico = "gfx/vehicles/bumb.tga"; + const string bumb_lgun = "gfx/vehicles/bumb_lgun.tga"; + const string bumb_rgun = "gfx/vehicles/bumb_rgun.tga"; + + const string bumb_gun_ico = "gfx/vehicles/bumb_side.tga"; + const string bumb_gun_gun = "gfx/vehicles/bumb_side_gun.tga"; + + const string spider_ico = "gfx/vehicles/sbot.tga"; + const string spider_rkt = "gfx/vehicles/sbot_rpods.tga"; + const string spider_mgun = "gfx/vehicles/sbot_mguns.tga"; + string spider_xhair; // = "gfx/vehicles/axh-special1.tga"; + + const string waki_ico = "gfx/vehicles/waki.tga"; + const string waki_eng = "gfx/vehicles/waki_e.tga"; + const string waki_gun = "gfx/vehicles/waki_guns.tga"; + const string waki_rkt = "gfx/vehicles/waki_rockets.tga"; + const string waki_xhair = "gfx/vehicles/axh-special1.tga"; + + float alarm1time; + float alarm2time; + int weapon2mode; + + void AuxiliaryXhair_Draw2D() + { + vector loc, psize; + + psize = self.axh_scale * draw_getimagesize(self.axh_image); + loc = project_3d_to_2d(self.move_origin) - 0.5 * psize; + if (!(loc.z < 0 || loc.x < 0 || loc.y < 0 || loc.x > vid_conwidth || loc.y > vid_conheight)) + { + loc.z = 0; + psize.z = 0; + drawpic(loc, self.axh_image, psize, self.colormod, self.alpha, self.axh_drawflag); + } + + if(time - self.cnt > self.axh_fadetime) + self.draw2d = func_null; + } + + void Net_AuXair2(bool bIsNew) + { + int axh_id = bound(0, ReadByte(), MAX_AXH); + entity axh = AuxiliaryXhairs[axh_id]; + + if(axh == world || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) + { + axh = spawn(); + axh.draw2d = func_null; + axh.drawmask = MASK_NORMAL; + axh.axh_drawflag = DRAWFLAG_ADDITIVE; + axh.axh_fadetime = 0.1; + axh.axh_image = "gfx/vehicles/axh-ring.tga"; + axh.axh_scale = 1; + axh.alpha = 1; + AuxiliaryXhairs[axh_id] = axh; + } + + axh.move_origin_x = ReadCoord(); + axh.move_origin_y = ReadCoord(); + axh.move_origin_z = ReadCoord(); + axh.colormod_x = ReadByte() / 255; + axh.colormod_y = ReadByte() / 255; + axh.colormod_z = ReadByte() / 255; + axh.cnt = time; + axh.draw2d = AuxiliaryXhair_Draw2D; + } + + void Net_VehicleSetup() + { + int hud_id = ReadByte(); + + // Weapon update? + if(hud_id > HUD_VEHICLE_LAST) + { + weapon2mode = hud_id - HUD_VEHICLE_LAST; + return; + } + + // hud_id == 0 means we exited a vehicle, so stop alarm sound/s + if(hud_id == 0) + { + sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + return; + } + + hud_id = bound(HUD_VEHICLE_FIRST, hud_id, HUD_VEHICLE_LAST); + + // Init auxiliary crosshairs + int i; + for(i = 0; i < MAX_AXH; ++i) + { + entity axh = AuxiliaryXhairs[i]; + if(axh != world && !wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) + remove(axh); + + axh = spawn(); + axh.draw2d = func_null; + axh.drawmask = MASK_NORMAL; + axh.axh_drawflag = DRAWFLAG_NORMAL; + axh.axh_fadetime = 0.1; + axh.axh_image = "gfx/vehicles/axh-ring.tga"; + axh.axh_scale = 1; + axh.alpha = 1; + AuxiliaryXhairs[i] = axh; + } + + switch(hud_id) + { + case HUD_SPIDERBOT: + // Minigun1 + AuxiliaryXhairs[0].axh_image = "gfx/vehicles/axh-ring.tga"; + AuxiliaryXhairs[0].axh_scale = 0.25; + // Minigun2 + AuxiliaryXhairs[1].axh_image = "gfx/vehicles/axh-ring.tga"; + AuxiliaryXhairs[1].axh_scale = 0.25; + // Rocket + AuxiliaryXhairs[2].axh_image = "gfx/vehicles/axh-special1.tga"; + AuxiliaryXhairs[2].axh_scale = 0.5; + break; + + case HUD_WAKIZASHI: + AuxiliaryXhairs[0].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhairs[0].axh_scale = 0.25; + break; + + case HUD_RAPTOR: + AuxiliaryXhairs[0].axh_image = "gfx/vehicles/axh-special2.tga"; + AuxiliaryXhairs[0].axh_scale = 0.5; + //AuxiliaryXhair[0].alpha = 0.5; + + AuxiliaryXhairs[1].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhairs[1].axh_scale = 0.25; + //AuxiliaryXhair[1].alpha = 0.75; + //AuxiliaryXhair[1].axh_drawflag = DRAWFLAG_NORMAL; + break; + + case HUD_BUMBLEBEE: + // Raygun-locked + AuxiliaryXhairs[0].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhairs[0].axh_scale = 0.5; + + // Gunner1 + AuxiliaryXhairs[1].axh_image = "gfx/vehicles/axh-target.tga"; + AuxiliaryXhairs[1].axh_scale = 0.75; + + // Gunner2 + AuxiliaryXhairs[2].axh_image = "gfx/vehicles/axh-target.tga"; + AuxiliaryXhairs[2].axh_scale = 0.75; + break; + case HUD_BUMBLEBEE_GUN: + // Plasma cannons + AuxiliaryXhairs[0].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhairs[0].axh_scale = 0.25; + // Raygun + AuxiliaryXhairs[1].axh_image = "gfx/vehicles/axh-bracket.tga"; + AuxiliaryXhairs[1].axh_scale = 0.25; + break; + } + } + #define HUD_GETSTATS \ + int vh_health = getstati(STAT_VEHICLESTAT_HEALTH); \ + float shield = getstati(STAT_VEHICLESTAT_SHIELD); \ + noref int energy = getstati(STAT_VEHICLESTAT_ENERGY); \ + noref float ammo1 = getstati(STAT_VEHICLESTAT_AMMO1); \ + noref float reload1 = getstati(STAT_VEHICLESTAT_RELOAD1); \ + noref int ammo2 = getstati(STAT_VEHICLESTAT_AMMO2); \ + noref int reload2 = getstati(STAT_VEHICLESTAT_RELOAD2); + + void CSQC_BUMBLE_HUD() + { + /* + drawpic(hudloc, waki_s, picsize, '1 1 1', shield, DRAWFLAG_NORMAL); + drawpic(hudloc, waki_b, picsize, '0 1 0' * health + '1 0 0' * (1 - health), 1, DRAWFLAG_NORMAL); + drawpic(hudloc, waki_r, picsize, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc, waki_e, picsize, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + */ + if(autocvar_r_letterbox) + return; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + + // Fetch health & ammo stats + HUD_GETSTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc.y = vid_conheight - picsize.y; + hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + shield *= 0.01; + vh_health *= 0.01; + energy *= 0.01; + reload1 *= 0.01; + + pic2size = draw_getimagesize(bumb_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + + if(vh_health < 0.25) + drawpic(hudloc + picloc, bumb_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, bumb_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + + drawpic(hudloc + picloc, bumb_lgun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, bumb_lgun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); + } + + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm1time = 0; + } + } + + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm2time = 0; + } + } + + ammo1 *= 0.01; + ammo2 *= 0.01; + + // Gunner1 bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * ammo1, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + + // Right gunner slot occupied? + if(!AuxiliaryXhairs[1].draw2d) + { + shield = (picsize.x * 0.5) - (0.5 * stringwidth(_("No right gunner!"), false, '1 0 0' * picsize.y + '0 1 0' * picsize.y)); + drawfill(hudloc + picloc - '0.2 0.2 0', picsize + '0.4 0.4 0', '0.25 0.25 0.25', 0.75, DRAWFLAG_NORMAL); + drawstring(hudloc + picloc + '1 0 0' * shield, _("No right gunner!"), '1 0 0' * picsize.y + '0 1 0' * picsize.y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL); + } + + // .. and icon + picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(ammo1 < 0.2) + drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + // Gunner2 bar + picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * ammo2, vid_conheight); + drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // Left gunner slot occupied? + if(!AuxiliaryXhairs[2].draw2d) + { + shield = (picsize.x * 0.5) - (0.5 * stringwidth(_("No left gunner!"), false, '1 0 0' * picsize.y + '0 1 0' * picsize.y)); + drawfill(hudloc + picloc - '0.2 0.2 0', picsize + '0.4 0.4 0', '0.25 0.25 0.25', 0.75, DRAWFLAG_NORMAL); + drawstring(hudloc + picloc + '1 0 0' * shield, _("No left gunner!"), '1 0 0' * picsize.y + '0 1 0' * picsize.y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL); + } + + // .. and icon + picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; + picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; + if(ammo2 < 0.2) + drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + else + { + picsize = draw_getimagesize(waki_xhair); + picsize.x *= 0.5; + picsize.y *= 0.5; + drawpic('0.5 0 0' * (vid_conwidth - picsize.x) + '0 0.5 0' * (vid_conheight - picsize.y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + } + + } + + void CSQC_BUMBLE_GUN_HUD() + { + + if(autocvar_r_letterbox) + return; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + + // Fetch health & ammo stats + HUD_GETSTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc.y = vid_conheight - picsize.y; + hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + shield *= 0.01; + vh_health *= 0.01; + energy *= 0.01; + reload1 *= 0.01; + + pic2size = draw_getimagesize(bumb_gun_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + + if(vh_health < 0.25) + drawpic(hudloc + picloc, bumb_gun_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, bumb_gun_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + + drawpic(hudloc + picloc, bumb_gun_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); + } + + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm1time = 0; + } + } + + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm2time = 0; + } + } + + // Gun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * energy, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + + // .. and icon + picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(energy < 0.2) + drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + /* + else + { + picsize = draw_getimagesize(waki_xhair); + picsize_x *= 0.5; + picsize_y *= 0.5; + + + drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + } + */ + } + + + + void CSQC_SPIDER_HUD() + { + if(autocvar_r_letterbox) + return; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + int i; + + // Fetch health & ammo stats + HUD_GETSTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc.y = vid_conheight - picsize.y; + hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + ammo1 *= 0.01; + shield *= 0.01; + vh_health *= 0.01; + reload2 *= 0.01; + + pic2size = draw_getimagesize(spider_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + if(vh_health < 0.25) + drawpic(hudloc + picloc, spider_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, spider_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, spider_rkt, pic2size, '1 1 1' * reload2 + '1 0 0' * (1 - reload2), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, spider_mgun, pic2size, '1 1 1' * ammo1 + '1 0 0' * (1 - ammo1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); + } + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm1time = 0; + } + } + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm2time = 0; + } + } + + // Minigun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * ammo1, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(ammo1 < 0.2) + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + // Rocket ammo bar + picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; + ammo1 = picsize.x / 8; + picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x, hudloc.y + picloc.y, picsize.x * reload2, vid_conheight); + drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + + // .. and icons + pic2size = 0.35 * draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; + picloc.x -= pic2size.x; + picloc.y += pic2size.y * 2.25; + if(ammo2 == 9) + { + for(i = 1; i < 9; ++i) + { + picloc.x += ammo1; + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, ((8 * reload2 <= i) ? '0 0 0' : '1 1 1'), 0.75, DRAWFLAG_NORMAL); + } + } + else + { + for(i = 1; i < 9; ++i) + { + picloc.x += ammo1; + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, ((i >= ammo2) ? '1 1 1' : '0 0 0'), 0.75, DRAWFLAG_NORMAL); + } + } + pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; + if(ammo2 == 9) + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + else + { + switch(weapon2mode) + { + case SBRM_VOLLY: + spider_xhair = "gfx/vehicles/axh-bracket.tga"; + break; + case SBRM_GUIDE: + spider_xhair = "gfx/vehicles/axh-cross.tga"; + break; + case SBRM_ARTILLERY: + spider_xhair = "gfx/vehicles/axh-tag.tga"; + break; + default: + spider_xhair= "gfx/vehicles/axh-tag.tga"; + } + + picsize = draw_getimagesize(spider_xhair); + picsize.x *= autocvar_cl_vehicle_spiderbot_cross_size; + picsize.y *= autocvar_cl_vehicle_spiderbot_cross_size; + + drawpic('0.5 0 0' * (vid_conwidth - picsize.x) + '0 0.5 0' * (vid_conheight - picsize.y), spider_xhair, picsize, '1 1 1', autocvar_cl_vehicle_spiderbot_cross_alpha, DRAWFLAG_ADDITIVE); + } + } + + void CSQC_RAPTOR_HUD() + { + if(autocvar_r_letterbox) + return; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + + // Fetch health & ammo stats + HUD_GETSTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc.y = vid_conheight - picsize.y; + hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + ammo1 *= 0.01; + ammo2 *= 0.01; + shield *= 0.01; + vh_health *= 0.01; + energy *= 0.01; + reload1 = reload2 * 0.01; + //reload2 *= 0.01; + + pic2size = draw_getimagesize(spider_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + if(vh_health < 0.25) + drawpic(hudloc + picloc, raptor_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, raptor_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, raptor_bomb, pic2size, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, raptor_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); + } + + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm1time = 0; + } + } + + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm2time = 0; + } + } + + // Gun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * energy, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(energy < 0.2) + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + // Bomb bar + picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x, hudloc.y + picloc.y, picsize.x * reload1, vid_conheight); + drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; + if(reload1 != 1) + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL); + + if(weapon2mode == RSM_FLARE) + { + raptor_xhair = "gfx/vehicles/axh-bracket.tga"; + } + else + { + raptor_xhair = "gfx/vehicles/axh-ring.tga"; + + // Bombing crosshair + if(!dropmark) + { + dropmark = spawn(); + dropmark.owner = self; + dropmark.gravity = 1; + } + + if(reload2 == 100) + { + vector where; + + setorigin(dropmark, pmove_org); + dropmark.velocity = pmove_vel; + tracetoss(dropmark, self); + + where = project_3d_to_2d(trace_endpos); + + setorigin(dropmark, trace_endpos); + picsize = draw_getimagesize(raptor_drop) * 0.2; + + if (!(where.z < 0 || where.x < 0 || where.y < 0 || where.x > vid_conwidth || where.y > vid_conheight)) + { + where.x -= picsize.x * 0.5; + where.y -= picsize.y * 0.5; + where.z = 0; + drawpic(where, raptor_drop, picsize, '0 2 0', 1, DRAWFLAG_ADDITIVE); + } + dropmark.cnt = time + 5; + } + else + { + vector where; + if(dropmark.cnt > time) + { + where = project_3d_to_2d(dropmark.origin); + picsize = draw_getimagesize(raptor_drop) * 0.25; + + if (!(where.z < 0 || where.x < 0 || where.y < 0 || where.x > vid_conwidth || where.y > vid_conheight)) + { + where.x -= picsize.x * 0.5; + where.y -= picsize.y * 0.5; + where.z = 0; + drawpic(where, raptor_drop, picsize, '2 0 0', 1, DRAWFLAG_ADDITIVE); + } + } + } + } + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + else + { + picsize = draw_getimagesize(raptor_xhair); + picsize.x *= 0.5; + picsize.y *= 0.5; + + drawpic('0.5 0 0' * (vid_conwidth - picsize.x) + '0 0.5 0' * (vid_conheight - picsize.y), raptor_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + } + } + + void CSQC_WAKIZASHI_HUD() + { + /* + drawpic(hudloc, waki_s, picsize, '1 1 1', shield, DRAWFLAG_NORMAL); + drawpic(hudloc, waki_b, picsize, '0 1 0' * health + '1 0 0' * (1 - health), 1, DRAWFLAG_NORMAL); + drawpic(hudloc, waki_r, picsize, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc, waki_e, picsize, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + */ + if(autocvar_r_letterbox) + return; + + vector picsize, hudloc = '0 0 0', pic2size, picloc; + + // Fetch health & ammo stats + HUD_GETSTATS + + picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale; + hudloc.y = vid_conheight - picsize.y; + hudloc.x = vid_conwidth * 0.5 - picsize.x * 0.5; + + drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL); + + shield *= 0.01; + vh_health *= 0.01; + energy *= 0.01; + reload1 *= 0.01; + + pic2size = draw_getimagesize(spider_ico) * (autocvar_cl_vehicles_hudscale * 0.8); + picloc = picsize * 0.5 - pic2size * 0.5; + if(vh_health < 0.25) + drawpic(hudloc + picloc, waki_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, waki_ico, pic2size, '1 1 1' * vh_health + '1 0 0' * (1 - vh_health), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, waki_eng, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, waki_gun, pic2size, '1 1 1' * energy + '1 0 0' * (1 - energy), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, waki_rkt, pic2size, '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL); + drawpic(hudloc + picloc, hud_sh, pic2size, '1 1 1', shield, DRAWFLAG_NORMAL); + + // Health bar + picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - vh_health)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale; + picloc = '37 65 0' * autocvar_cl_vehicles_hudscale; + if(vh_health < 0.25) + { + if(alarm1time < time) + { + alarm1time = time + 2; + sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE); + } + + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm1time) + { + sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm1time = 0; + } + } + + + // Shield bar + picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale; + picloc = '69 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x + (picsize.x * (1 - shield)), 0, vid_conwidth, vid_conheight); + drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picloc = '40 136 0' * autocvar_cl_vehicles_hudscale; + picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale; + if(shield < 0.25) + { + if(alarm2time < time) + { + alarm2time = time + 1; + sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE); + } + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + } + else + { + drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + if(alarm2time) + { + sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE); + alarm2time = 0; + } + } + + // Gun bar + picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 69 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x, picloc.y, picsize.x * energy, vid_conheight); + drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 60 0' * autocvar_cl_vehicles_hudscale; + if(energy < 0.2) + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + + // Bomb bar + picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale; + picloc = '450 140 0' * autocvar_cl_vehicles_hudscale; + drawsetcliparea(hudloc.x + picloc.x, hudloc.y + picloc.y, picsize.x * reload1, vid_conheight); + drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + drawresetcliparea(); + // .. and icon + pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale; + picloc = '664 130 0' * autocvar_cl_vehicles_hudscale; + if(reload1 != 1) + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL); + else + drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL); + + if (scoreboard_showscores) + HUD_DrawScoreboard(); + else + { + picsize = draw_getimagesize(waki_xhair); + picsize.x *= 0.5; + picsize.y *= 0.5; + + + drawpic('0.5 0 0' * (vid_conwidth - picsize.x) + '0 0.5 0' * (vid_conheight - picsize.y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL); + } + } + + void Vehicles_Precache() + { + precache_model("models/vehicles/bomblet.md3"); + precache_model("models/vehicles/clusterbomb.md3"); + precache_model("models/vehicles/clusterbomb_fragment.md3"); + precache_model("models/vehicles/rocket01.md3"); + precache_model("models/vehicles/rocket02.md3"); + + precache_sound ("vehicles/alarm.wav"); + precache_sound ("vehicles/alarm_shield.wav"); + } + + void RaptorCBShellfragDraw() + { + if(wasfreed(self)) + return; + + Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); + self.move_avelocity += randomvec() * 15; + self.renderflags = 0; + + if(self.cnt < time) + self.alpha = bound(0, self.nextthink - time, 1); + + if(self.alpha < ALPHA_MIN_VISIBLE) + remove(self); + } + + void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang) + { + entity sfrag; + + sfrag = spawn(); + setmodel(sfrag, "models/vehicles/clusterbomb_fragment.md3"); + setorigin(sfrag, _org); + + sfrag.move_movetype = MOVETYPE_BOUNCE; + sfrag.gravity = 0.15; + sfrag.solid = SOLID_CORPSE; + + sfrag.draw = RaptorCBShellfragDraw; + + sfrag.move_origin = sfrag.origin = _org; + sfrag.move_velocity = _vel; + sfrag.move_avelocity = prandomvec() * vlen(sfrag.move_velocity); + sfrag.angles = self.move_angles = _ang; + + sfrag.move_time = time; + sfrag.damageforcescale = 4; + + sfrag.nextthink = time + 3; + sfrag.cnt = time + 2; + sfrag.alpha = 1; + sfrag.drawmask = MASK_NORMAL; + } diff --cc qcsrc/client/vehicles/bumblebee.qc index 0000000000,8430668cda..1304d3a2e6 mode 000000,100644..100644 --- a/qcsrc/client/vehicles/bumblebee.qc +++ b/qcsrc/client/vehicles/bumblebee.qc @@@ -1,0 -1,11 +1,12 @@@ + #include "../damage.qh" + #include "../defs.qh" + #include "../gibs.qh" + #include "../hook.qh" + #include "../main.qh" -#include "../movetypes.qh" + #include "../wall.qh" + + #include "../weapons/projectile.qh" + ++#include "../../common/movetypes/movetypes.qh" ++ + #include "../../server/vehicles/bumblebee.qc" diff --cc qcsrc/client/view.qc index a9e362f84b,d3bcf92447..ffdde41a9e --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@@ -1,25 -1,31 +1,31 @@@ - #if defined(CSQC) - #include "../dpdefs/csprogsdefs.qh" - #include "defs.qh" - #include "../common/constants.qh" - #include "../common/stats.qh" - #include "../warpzonelib/mathlib.qh" - #include "../warpzonelib/common.qh" - #include "../warpzonelib/client.qh" - #include "../common/teams.qh" - #include "../common/util.qh" - #include "../common/nades.qh" - #include "../common/weapons/weapons.qh" - #include "../common/mapinfo.qh" - #include "autocvars.qh" - #include "hud.qh" - #include "scoreboard.qh" - #include "noise.qh" - #include "main.qh" - #include "../csqcmodellib/cl_player.qh" - #elif defined(MENUQC) - #elif defined(SVQC) - #endif + #include "_all.qh" + + #include "announcer.qh" + #include "hook.qh" + #include "hud.qh" + #include "hud_config.qh" + #include "mapvoting.qh" + #include "noise.qh" + #include "scoreboard.qh" + #include "shownames.qh" -#include "target_music.qh" + #include "vehicles/all.qh" + #include "waypointsprites.qh" + + #include "../common/constants.qh" + #include "../common/mapinfo.qh" + #include "../common/nades.qh" + #include "../common/stats.qh" ++#include "../common/triggers/target/music.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + + #include "../common/weapons/all.qh" + + #include "../csqcmodellib/cl_player.qh" + + #include "../warpzonelib/client.qh" + #include "../warpzonelib/common.qh" + #include "../warpzonelib/mathlib.qh" entity porto; vector polyline[16]; diff --cc qcsrc/client/weapons/projectile.qc index 51236fdbba,cd4f9b7441..efa6509c70 --- a/qcsrc/client/weapons/projectile.qc +++ b/qcsrc/client/weapons/projectile.qc @@@ -1,5 -1,22 +1,22 @@@ #include "projectile.qh" + #include "../autocvars.qh" + #include "../defs.qh" + #include "../main.qh" -#include "../movetypes.qh" + + #include "../../common/constants.qh" + #include "../../common/nades.qh" ++#include "../../common/movetypes/movetypes.qh" + #include "../../common/util.qh" + + #include "../../csqcmodellib/interpolate.qh" + + #include "../../warpzonelib/anglestransform.qh" + + .float alpha; + .float scale; + .vector colormod; + void SUB_Stop() { self.move_velocity = self.move_avelocity = '0 0 0'; diff --cc qcsrc/common/physics.qc index 6b7a2224b0,0000000000..ea217c5a18 mode 100644,000000..100644 --- a/qcsrc/common/physics.qc +++ b/qcsrc/common/physics.qc @@@ -1,1844 -1,0 +1,1844 @@@ +#include "physics.qh" +#include "triggers/trigger/swamp.qh" +#include "triggers/trigger/jumppads.qh" + +#ifdef SVQC + +#include "../server/miscfunctions.qh" + +void Physics_AddStats() +{ + // static view offset and hitbox vectors + // networked for all you bandwidth pigs out there + addstat(STAT_PL_VIEW_OFS1, AS_FLOAT, stat_pl_view_ofs_x); + addstat(STAT_PL_VIEW_OFS2, AS_FLOAT, stat_pl_view_ofs_y); + addstat(STAT_PL_VIEW_OFS3, AS_FLOAT, stat_pl_view_ofs_z); + addstat(STAT_PL_CROUCH_VIEW_OFS1, AS_FLOAT, stat_pl_crouch_view_ofs_x); + addstat(STAT_PL_CROUCH_VIEW_OFS2, AS_FLOAT, stat_pl_crouch_view_ofs_y); + addstat(STAT_PL_CROUCH_VIEW_OFS3, AS_FLOAT, stat_pl_crouch_view_ofs_z); + + addstat(STAT_PL_MIN1, AS_FLOAT, stat_pl_min_x); + addstat(STAT_PL_MIN2, AS_FLOAT, stat_pl_min_y); + addstat(STAT_PL_MIN3, AS_FLOAT, stat_pl_min_z); + addstat(STAT_PL_MAX1, AS_FLOAT, stat_pl_max_x); + addstat(STAT_PL_MAX2, AS_FLOAT, stat_pl_max_y); + addstat(STAT_PL_MAX3, AS_FLOAT, stat_pl_max_z); + addstat(STAT_PL_CROUCH_MIN1, AS_FLOAT, stat_pl_crouch_min_x); + addstat(STAT_PL_CROUCH_MIN2, AS_FLOAT, stat_pl_crouch_min_y); + addstat(STAT_PL_CROUCH_MIN3, AS_FLOAT, stat_pl_crouch_min_z); + addstat(STAT_PL_CROUCH_MAX1, AS_FLOAT, stat_pl_crouch_max_x); + addstat(STAT_PL_CROUCH_MAX2, AS_FLOAT, stat_pl_crouch_max_y); + addstat(STAT_PL_CROUCH_MAX3, AS_FLOAT, stat_pl_crouch_max_z); + + // g_movementspeed hack + addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw); + addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed); + addstat(STAT_MOVEVARS_AIRACCEL_QW, AS_FLOAT, stat_sv_airaccel_qw); + addstat(STAT_MOVEVARS_AIRSTRAFEACCEL_QW, AS_FLOAT, stat_sv_airstrafeaccel_qw); + addstat(STAT_MOVEVARS_HIGHSPEED, AS_FLOAT, stat_movement_highspeed); + + // jet pack + addstat(STAT_JETPACK_ACCEL_SIDE, AS_FLOAT, stat_jetpack_accel_side); + addstat(STAT_JETPACK_ACCEL_UP, AS_FLOAT, stat_jetpack_accel_up); + addstat(STAT_JETPACK_ANTIGRAVITY, AS_FLOAT, stat_jetpack_antigravity); + addstat(STAT_JETPACK_FUEL, AS_FLOAT, stat_jetpack_fuel); + addstat(STAT_JETPACK_MAXSPEED_UP, AS_FLOAT, stat_jetpack_maxspeed_up); + addstat(STAT_JETPACK_MAXSPEED_SIDE, AS_FLOAT, stat_jetpack_maxspeed_side); + + // hack to fix track_canjump + addstat(STAT_MOVEVARS_TRACK_CANJUMP, AS_INT, cvar_cl_movement_track_canjump); + + // double jump + addstat(STAT_DOUBLEJUMP, AS_INT, stat_doublejump); + + // jump speed caps + addstat(STAT_MOVEVARS_JUMPSPEEDCAP_MIN, AS_FLOAT, stat_jumpspeedcap_min); + addstat(STAT_MOVEVARS_JUMPSPEEDCAP_MIN, AS_FLOAT, stat_jumpspeedcap_min); + addstat(STAT_MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, AS_INT, stat_jumpspeedcap_disable_onramps); + + // hacks + addstat(STAT_MOVEVARS_FRICTION_ONLAND, AS_FLOAT, stat_sv_friction_on_land); + addstat(STAT_MOVEVARS_FRICTION_SLICK, AS_FLOAT, stat_sv_friction_slick); + addstat(STAT_GAMEPLAYFIX_EASIERWATERJUMP, AS_INT, stat_gameplayfix_easierwaterjump); + + addstat(STAT_GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, AS_INT, stat_gameplayfix_upvelocityclearsonground); +} + +void Physics_UpdateStats(float maxspd_mod) +{ + // blah + self.stat_pl_view_ofs = PL_VIEW_OFS; + self.stat_pl_crouch_view_ofs = PL_CROUCH_VIEW_OFS; + + self.stat_pl_min = PL_MIN; + self.stat_pl_max = PL_MAX; + self.stat_pl_crouch_min = PL_CROUCH_MIN; + self.stat_pl_crouch_max = PL_CROUCH_MAX; + + self.stat_sv_airaccel_qw = AdjustAirAccelQW(autocvar_sv_airaccel_qw, maxspd_mod); + if (autocvar_sv_airstrafeaccel_qw) + self.stat_sv_airstrafeaccel_qw = AdjustAirAccelQW(autocvar_sv_airstrafeaccel_qw, maxspd_mod); + else + self.stat_sv_airstrafeaccel_qw = 0; + self.stat_sv_airspeedlimit_nonqw = autocvar_sv_airspeedlimit_nonqw * maxspd_mod; + self.stat_sv_maxspeed = autocvar_sv_maxspeed * maxspd_mod; // also slow walking + self.stat_movement_highspeed = PHYS_HIGHSPEED; // TODO: remove this! + + self.stat_doublejump = PHYS_DOUBLEJUMP; + + self.stat_jetpack_antigravity = PHYS_JETPACK_ANTIGRAVITY; + self.stat_jetpack_accel_up = PHYS_JETPACK_ACCEL_UP; + self.stat_jetpack_accel_side = PHYS_JETPACK_ACCEL_SIDE; + self.stat_jetpack_maxspeed_side = PHYS_JETPACK_MAXSPEED_SIDE; + self.stat_jetpack_maxspeed_up = PHYS_JETPACK_MAXSPEED_UP; + self.stat_jetpack_fuel = PHYS_JETPACK_FUEL; + + self.stat_jumpspeedcap_min = PHYS_JUMPSPEEDCAP_MIN; + self.stat_jumpspeedcap_max = PHYS_JUMPSPEEDCAP_MAX; + self.stat_jumpspeedcap_disable_onramps = PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS; + + self.stat_sv_friction_on_land = PHYS_FRICTION_ONLAND; + self.stat_sv_friction_slick = PHYS_FRICTION_SLICK; + + self.stat_gameplayfix_easierwaterjump = GAMEPLAYFIX_EASIERWATERJUMP; + + self.stat_gameplayfix_upvelocityclearsonground = UPWARD_VELOCITY_CLEARS_ONGROUND; +} +#endif + +float IsMoveInDirection(vector mv, float ang) // key mix factor +{ + if (mv_x == 0 && mv_y == 0) + return 0; // avoid division by zero + ang -= RAD2DEG * atan2(mv_y, mv_x); + ang = remainder(ang, 360) / 45; + return ang > 1 ? 0 : ang < -1 ? 0 : 1 - fabs(ang); +} + +float GeomLerp(float a, float lerp, float b) +{ + return a == 0 ? (lerp < 1 ? 0 : b) + : b == 0 ? (lerp > 0 ? 0 : a) + : a * pow(fabs(b / a), lerp); +} + +noref float pmove_waterjumptime; + +const float unstick_count = 27; +vector unstick_offsets[unstick_count] = +{ +// 1 no nudge (just return the original if this test passes) + '0.000 0.000 0.000', +// 6 simple nudges + ' 0.000 0.000 0.125', '0.000 0.000 -0.125', + '-0.125 0.000 0.000', '0.125 0.000 0.000', + ' 0.000 -0.125 0.000', '0.000 0.125 0.000', +// 4 diagonal flat nudges + '-0.125 -0.125 0.000', '0.125 -0.125 0.000', + '-0.125 0.125 0.000', '0.125 0.125 0.000', +// 8 diagonal upward nudges + '-0.125 0.000 0.125', '0.125 0.000 0.125', + ' 0.000 -0.125 0.125', '0.000 0.125 0.125', + '-0.125 -0.125 0.125', '0.125 -0.125 0.125', + '-0.125 0.125 0.125', '0.125 0.125 0.125', +// 8 diagonal downward nudges + '-0.125 0.000 -0.125', '0.125 0.000 -0.125', + ' 0.000 -0.125 -0.125', '0.000 0.125 -0.125', + '-0.125 -0.125 -0.125', '0.125 -0.125 -0.125', + '-0.125 0.125 -0.125', '0.125 0.125 -0.125', +}; + +void PM_ClientMovement_Unstick() +{ + float i; + for (i = 0; i < unstick_count; i++) + { + vector neworigin = unstick_offsets[i] + self.origin; + tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, self); + if (!trace_startsolid) + { + setorigin(self, neworigin); + return;// true; + } + } +} + +void PM_ClientMovement_UpdateStatus(bool ground) +{ + // make sure player is not stuck + PM_ClientMovement_Unstick(); + + // set crouched + if (PHYS_INPUT_BUTTON_CROUCH(self)) + { + // wants to crouch, this always works.. + if (!IS_DUCKED(self)) + SET_DUCKED(self); + } + else + { + // wants to stand, if currently crouching we need to check for a + // low ceiling first + if (IS_DUCKED(self)) + { + tracebox(self.origin, PL_MIN, PL_MAX, self.origin, MOVE_NORMAL, self); + if (!trace_startsolid) + UNSET_DUCKED(self); + } + } + + // set onground + vector origin1 = self.origin + '0 0 1'; + vector origin2 = self.origin - '0 0 1'; + + if(ground) + { + tracebox(origin1, self.mins, self.maxs, origin2, MOVE_NORMAL, self); + if (trace_fraction < 1.0 && trace_plane_normal_z > 0.7) + { + SET_ONGROUND(self); + + // this code actually "predicts" an impact; so let's clip velocity first + float f = self.velocity * trace_plane_normal; + self.velocity -= f * trace_plane_normal; + } + else + UNSET_ONGROUND(self); + } + + // set watertype/waterlevel + origin1 = self.origin; + origin1_z += self.mins_z + 1; + self.waterlevel = WATERLEVEL_NONE; + + int thepoint = pointcontents(origin1); + + self.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME); + + if(self.watertype) + { + self.waterlevel = WATERLEVEL_WETFEET; + origin1_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5; + thepoint = pointcontents(origin1); + if(thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME) + { + self.waterlevel = WATERLEVEL_SWIMMING; + origin1_z = self.origin_z + 22; + thepoint = pointcontents(origin1); + if(thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME) + self.waterlevel = WATERLEVEL_SUBMERGED; + } + } + + if(IS_ONGROUND(self) || self.velocity_z <= 0 || pmove_waterjumptime <= 0) + pmove_waterjumptime = 0; +} + +void PM_ClientMovement_Move() +{ +#ifdef CSQC + int bump; + float t; + float f; + vector neworigin; + vector currentorigin2; + vector neworigin2; + vector primalvelocity; + + vector trace1_endpos = '0 0 0'; + vector trace2_endpos = '0 0 0'; + vector trace3_endpos = '0 0 0'; + float trace1_fraction = 0; + float trace2_fraction = 0; + float trace3_fraction = 0; + vector trace1_plane_normal = '0 0 0'; + vector trace2_plane_normal = '0 0 0'; + vector trace3_plane_normal = '0 0 0'; + + + PM_ClientMovement_UpdateStatus(false); + primalvelocity = self.velocity; + for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (self.velocity * self.velocity) > 0; bump++) + { + neworigin = self.origin + t * self.velocity; + tracebox(self.origin, self.mins, self.maxs, neworigin, MOVE_NORMAL, self); + trace1_endpos = trace_endpos; + trace1_fraction = trace_fraction; + trace1_plane_normal = trace_plane_normal; + if(trace1_fraction < 1 && trace1_plane_normal_z == 0) + { + // may be a step or wall, try stepping up + // first move forward at a higher level + currentorigin2 = self.origin; + currentorigin2_z += PHYS_STEPHEIGHT; + neworigin2 = neworigin; + neworigin2_z += PHYS_STEPHEIGHT; + tracebox(currentorigin2, self.mins, self.maxs, neworigin2, MOVE_NORMAL, self); + trace2_endpos = trace_endpos; + trace2_fraction = trace_fraction; + trace2_plane_normal = trace_plane_normal; + if(!trace_startsolid) + { + // then move down from there + currentorigin2 = trace2_endpos; + neworigin2 = trace2_endpos; + neworigin2_z = self.origin_z; + tracebox(currentorigin2, self.mins, self.maxs, neworigin2, MOVE_NORMAL, self); + trace3_endpos = trace_endpos; + trace3_fraction = trace_fraction; + trace3_plane_normal = trace_plane_normal; + // accept the new trace if it made some progress + if(fabs(trace3_endpos_x - trace1_endpos_x) >= 0.03125 || fabs(trace3_endpos_y - trace1_endpos_y) >= 0.03125) + { + trace1_endpos = trace2_endpos; + trace1_fraction = trace2_fraction; + trace1_plane_normal = trace2_plane_normal; + trace1_endpos = trace3_endpos; + } + } + } + + // check if it moved at all + if(trace1_fraction >= 0.001) + setorigin(self, trace1_endpos); + + // check if it moved all the way + if(trace1_fraction == 1) + break; + + // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate + // I'm pretty sure I commented it out solely because it seemed redundant + // this got commented out in a change that supposedly makes the code match QW better + // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block + if(trace1_plane_normal_z > 0.7) + SET_ONGROUND(self); + + t -= t * trace1_fraction; + + f = (self.velocity * trace1_plane_normal); + self.velocity = self.velocity + -f * trace1_plane_normal; + } + if(pmove_waterjumptime > 0) + self.velocity = primalvelocity; +#endif +} + +void CPM_PM_Aircontrol(vector wishdir, float wishspeed) +{ + float k = 32 * (2 * IsMoveInDirection(self.movement, 0) - 1); + if (k <= 0) + return; + + k *= bound(0, wishspeed / PHYS_MAXAIRSPEED, 1); + + float zspeed = self.velocity_z; + self.velocity_z = 0; + float xyspeed = vlen(self.velocity); + self.velocity = normalize(self.velocity); + + float dot = self.velocity * wishdir; + + if (dot > 0) // we can't change direction while slowing down + { + k *= pow(dot, PHYS_AIRCONTROL_POWER) * PHYS_INPUT_TIMELENGTH; + xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY * sqrt(max(0, 1 - dot*dot)) * k/32); + k *= PHYS_AIRCONTROL; + self.velocity = normalize(self.velocity * xyspeed + wishdir * k); + } + + self.velocity = self.velocity * xyspeed; + self.velocity_z = zspeed; +} + +float AdjustAirAccelQW(float accelqw, float factor) +{ + return copysign(bound(0.000001, 1 - (1 - fabs(accelqw)) * factor, 1), accelqw); +} + +// example config for alternate speed clamping: +// sv_airaccel_qw 0.8 +// sv_airaccel_sideways_friction 0 +// prvm_globalset server speedclamp_mode 1 +// (or 2) +void PM_Accelerate(vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit) +{ + float speedclamp = stretchfactor > 0 ? stretchfactor + : accelqw < 0 ? 1 // full clamping, no stretch + : -1; // no clamping + + accelqw = fabs(accelqw); + + if (GAMEPLAYFIX_Q2AIRACCELERATE) + wishspeed0 = wishspeed; // don't need to emulate this Q1 bug + + float vel_straight = self.velocity * wishdir; + float vel_z = self.velocity_z; + vector vel_xy = vec2(self.velocity); + vector vel_perpend = vel_xy - vel_straight * wishdir; + + float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0; + + float vel_xy_current = vlen(vel_xy); + if (speedlimit) + accelqw = AdjustAirAccelQW(accelqw, (speedlimit - bound(wishspeed, vel_xy_current, speedlimit)) / max(1, speedlimit - wishspeed)); + float vel_xy_forward = vel_xy_current + bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw); + float vel_xy_backward = vel_xy_current - bound(0, wishspeed + vel_xy_current, step) * accelqw - step * (1 - accelqw); + vel_xy_backward = max(0, vel_xy_backward); // not that it REALLY occurs that this would cause wrong behaviour afterwards + vel_straight = vel_straight + bound(0, wishspeed - vel_straight, step) * accelqw + step * (1 - accelqw); + + if (sidefric < 0 && (vel_perpend*vel_perpend)) + // negative: only apply so much sideways friction to stay below the speed you could get by "braking" + { + float f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric); + float fmin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend); + // assume: fmin > 1 + // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend + // vel_xy_backward*vel_xy_backward > vel_straight*vel_straight + vel_perpend*vel_perpend + // vel_xy_backward*vel_xy_backward > vel_xy * vel_xy + // obviously, this cannot be + if (fmin <= 0) + vel_perpend *= f; + else + { + fmin = sqrt(fmin); + vel_perpend *= max(fmin, f); + } + } + else + vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric); + + vel_xy = vel_straight * wishdir + vel_perpend; + + if (speedclamp >= 0) + { + float vel_xy_preclamp; + vel_xy_preclamp = vlen(vel_xy); + if (vel_xy_preclamp > 0) // prevent division by zero + { + vel_xy_current += (vel_xy_forward - vel_xy_current) * speedclamp; + if (vel_xy_current < vel_xy_preclamp) + vel_xy *= (vel_xy_current / vel_xy_preclamp); + } + } + + self.velocity = vel_xy + vel_z * '0 0 1'; +} + +void PM_AirAccelerate(vector wishdir, float wishspeed) +{ + if (wishspeed == 0) + return; + + vector curvel = self.velocity; + curvel_z = 0; + float curspeed = vlen(curvel); + + if (wishspeed > curspeed * 1.01) + wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL * PHYS_MAXSPEED(self) * PHYS_INPUT_TIMELENGTH); + else + { + float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED - PHYS_MAXSPEED(self))); + wishspeed = max(curspeed, PHYS_MAXSPEED(self)) + PHYS_WARSOWBUNNY_ACCEL * f * PHYS_MAXSPEED(self) * PHYS_INPUT_TIMELENGTH; + } + vector wishvel = wishdir * wishspeed; + vector acceldir = wishvel - curvel; + float addspeed = vlen(acceldir); + acceldir = normalize(acceldir); + + float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL * PHYS_MAXSPEED(self) * PHYS_INPUT_TIMELENGTH); + + if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO < 1) + { + vector curdir = normalize(curvel); + float dot = acceldir * curdir; + if (dot < 0) + acceldir -= (1 - PHYS_WARSOWBUNNY_BACKTOSIDERATIO) * dot * curdir; + } + + self.velocity += accelspeed * acceldir; +} + + +/* +============= +PlayerJump + +When you press the jump key +returns true if handled +============= +*/ +bool PlayerJump (void) +{ + if (PHYS_FROZEN(self)) + return true; // no jumping in freezetag when frozen + +#ifdef SVQC + if (self.player_blocked) + return true; // no jumping while blocked +#endif + + bool doublejump = false; + float mjumpheight = PHYS_JUMPVELOCITY; + + player_multijump = doublejump; + player_jumpheight = mjumpheight; +#ifdef SVQC + if (MUTATOR_CALLHOOK(PlayerJump)) +#elif defined(CSQC) + if(PM_multijump_checkjump()) +#endif + return true; + + doublejump = player_multijump; + mjumpheight = player_jumpheight; + + if (PHYS_DOUBLEJUMP) + { + tracebox(self.origin + '0 0 0.01', self.mins, self.maxs, self.origin - '0 0 0.01', MOVE_NORMAL, self); + if (trace_fraction < 1 && trace_plane_normal_z > 0.7) + { + doublejump = true; + + // we MUST clip velocity here! + float f; + f = self.velocity * trace_plane_normal; + if (f < 0) + self.velocity -= f * trace_plane_normal; + } + } + + if (self.waterlevel >= WATERLEVEL_SWIMMING) + { + self.velocity_z = PHYS_MAXSPEED(self) * 0.7; + return true; + } + + if (!doublejump) + if (!IS_ONGROUND(self)) + return IS_JUMP_HELD(self); + + if (PHYS_TRACK_CANJUMP(self)) + if (IS_JUMP_HELD(self)) + return true; + + // sv_jumpspeedcap_min/sv_jumpspeedcap_max act as baseline + // velocity bounds. Final velocity is bound between (jumpheight * + // min + jumpheight) and (jumpheight * max + jumpheight); + + if(PHYS_JUMPSPEEDCAP_MIN) + { + float minjumpspeed = mjumpheight * PHYS_JUMPSPEEDCAP_MIN; + + if (self.velocity_z < minjumpspeed) + mjumpheight += minjumpspeed - self.velocity_z; + } + + if(PHYS_JUMPSPEEDCAP_MAX) + { + // don't do jump speedcaps on ramps to preserve old xonotic ramjump style + tracebox(self.origin + '0 0 0.01', self.mins, self.maxs, self.origin - '0 0 0.01', MOVE_NORMAL, self); + + if (!(trace_fraction < 1 && trace_plane_normal_z < 0.98 && PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS)) + { + float maxjumpspeed = mjumpheight * PHYS_JUMPSPEEDCAP_MAX; + + if (self.velocity_z > maxjumpspeed) + mjumpheight -= self.velocity_z - maxjumpspeed; + } + } + + if (!WAS_ONGROUND(self)) + { +#ifdef SVQC + if(autocvar_speedmeter) + dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n")); +#endif + if(self.lastground < time - 0.3) + { + self.velocity_x *= (1 - PHYS_FRICTION_ONLAND); + self.velocity_y *= (1 - PHYS_FRICTION_ONLAND); + } +#ifdef SVQC + if(self.jumppadcount > 1) + dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n")); + self.jumppadcount = 0; +#endif + } + + self.velocity_z += mjumpheight; + + UNSET_ONGROUND(self); + SET_JUMP_HELD(self); + +#ifdef SVQC + + self.oldvelocity_z = self.velocity_z; + + animdecide_setaction(self, ANIMACTION_JUMP, true); + + if (autocvar_g_jump_grunt) + PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); +#endif + return true; +} + +void CheckWaterJump() +{ +// check for a jump-out-of-water + makevectors(self.v_angle); + vector start = self.origin; + start_z += 8; + v_forward_z = 0; + normalize(v_forward); + vector end = start + v_forward*24; + traceline (start, end, true, self); + if (trace_fraction < 1) + { // solid at waist + start_z = start_z + self.maxs_z - 8; + end = start + v_forward*24; + self.movedir = trace_plane_normal * -50; + traceline(start, end, true, self); + if (trace_fraction == 1) + { // open at eye level + self.velocity_z = 225; + self.flags |= FL_WATERJUMP; + SET_JUMP_HELD(self); +#ifdef SVQC + self.teleport_time = time + 2; // safety net +#elif defined(CSQC) + pmove_waterjumptime = time + 2; +#endif + } + } +} + + +#ifdef SVQC + #define JETPACK_JUMP(s) s.cvar_cl_jetpack_jump +#elif defined(CSQC) + float autocvar_cl_jetpack_jump; + #define JETPACK_JUMP(s) autocvar_cl_jetpack_jump +#endif +.float jetpack_stopped; +// Hack: shouldn't need to know about this +.float multijump_count; +void CheckPlayerJump() +{ +#ifdef SVQC - float was_flying = ITEMS(self) & IT_USING_JETPACK; ++ float was_flying = ITEMS_STAT(self) & IT_USING_JETPACK; +#endif + if (JETPACK_JUMP(self) < 2) - ITEMS(self) &= ~IT_USING_JETPACK; ++ ITEMS_STAT(self) &= ~IT_USING_JETPACK; + + if(PHYS_INPUT_BUTTON_JUMP(self) || PHYS_INPUT_BUTTON_JETPACK(self)) + { + float air_jump = !PlayerJump() || self.multijump_count > 0; // PlayerJump() has important side effects + float activate = JETPACK_JUMP(self) && air_jump && PHYS_INPUT_BUTTON_JUMP(self) || PHYS_INPUT_BUTTON_JETPACK(self); - float has_fuel = !PHYS_JETPACK_FUEL || PHYS_AMMO_FUEL(self) || ITEMS(self) & IT_UNLIMITED_WEAPON_AMMO; ++ float has_fuel = !PHYS_JETPACK_FUEL || PHYS_AMMO_FUEL(self) || ITEMS_STAT(self) & IT_UNLIMITED_WEAPON_AMMO; + - if (!(ITEMS(self) & IT_JETPACK)) { } ++ if (!(ITEMS_STAT(self) & IT_JETPACK)) { } + else if (self.jetpack_stopped) { } + else if (!has_fuel) + { +#ifdef SVQC + if (was_flying) // TODO: ran out of fuel message + Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_JETPACK_NOFUEL); + else if (activate) + Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_JETPACK_NOFUEL); +#endif + self.jetpack_stopped = true; - ITEMS(self) &= ~IT_USING_JETPACK; ++ ITEMS_STAT(self) &= ~IT_USING_JETPACK; + } + else if (activate && !PHYS_FROZEN(self)) - ITEMS(self) |= IT_USING_JETPACK; ++ ITEMS_STAT(self) |= IT_USING_JETPACK; + } + else + { + self.jetpack_stopped = false; - ITEMS(self) &= ~IT_USING_JETPACK; ++ ITEMS_STAT(self) &= ~IT_USING_JETPACK; + } + if (!PHYS_INPUT_BUTTON_JUMP(self)) + UNSET_JUMP_HELD(self); + + if (self.waterlevel == WATERLEVEL_SWIMMING) + CheckWaterJump(); +} + +float racecar_angle(float forward, float down) +{ + if (forward < 0) + { + forward = -forward; + down = -down; + } + + float ret = vectoyaw('0 1 0' * down + '1 0 0' * forward); + + float angle_mult = forward / (800 + forward); + + if (ret > 180) + return ret * angle_mult + 360 * (1 - angle_mult); + else + return ret * angle_mult; +} + +void RaceCarPhysics() +{ +#ifdef SVQC + // using this move type for "big rigs" + // the engine does not push the entity! + + vector rigvel; + + vector angles_save = self.angles; + float accel = bound(-1, self.movement.x / PHYS_MAXSPEED(self), 1); + float steer = bound(-1, self.movement.y / PHYS_MAXSPEED(self), 1); + + if (g_bugrigs_reverse_speeding) + { + if (accel < 0) + { + // back accel is DIGITAL + // to prevent speedhack + if (accel < -0.5) + accel = -1; + else + accel = 0; + } + } + + self.angles_x = 0; + self.angles_z = 0; + makevectors(self.angles); // new forward direction! + + if (IS_ONGROUND(self) || g_bugrigs_air_steering) + { + float myspeed = self.velocity * v_forward; + float upspeed = self.velocity * v_up; + + // responsiveness factor for steering and acceleration + float f = 1 / (1 + pow(max(-myspeed, myspeed) / g_bugrigs_speed_ref, g_bugrigs_speed_pow)); + //MAXIMA: f(v) := 1 / (1 + (v / g_bugrigs_speed_ref) ^ g_bugrigs_speed_pow); + + float steerfactor; + if (myspeed < 0 && g_bugrigs_reverse_spinning) + steerfactor = -myspeed * g_bugrigs_steer; + else + steerfactor = -myspeed * f * g_bugrigs_steer; + + float accelfactor; + if (myspeed < 0 && g_bugrigs_reverse_speeding) + accelfactor = g_bugrigs_accel; + else + accelfactor = f * g_bugrigs_accel; + //MAXIMA: accel(v) := f(v) * g_bugrigs_accel; + + if (accel < 0) + { + if (myspeed > 0) + { + myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * (g_bugrigs_friction_floor - g_bugrigs_friction_brake * accel)); + } + else + { + if (!g_bugrigs_reverse_speeding) + myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * g_bugrigs_friction_floor); + } + } + else + { + if (myspeed >= 0) + { + myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * g_bugrigs_friction_floor); + } + else + { + if (g_bugrigs_reverse_stopping) + myspeed = 0; + else + myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * (g_bugrigs_friction_floor + g_bugrigs_friction_brake * accel)); + } + } + // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec + //MAXIMA: friction(v) := g_bugrigs_friction_floor; + + self.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering + makevectors(self.angles); // new forward direction! + + myspeed += accel * accelfactor * PHYS_INPUT_TIMELENGTH; + + rigvel = myspeed * v_forward + '0 0 1' * upspeed; + } + else + { + float myspeed = vlen(self.velocity); + + // responsiveness factor for steering and acceleration + float f = 1 / (1 + pow(max(0, myspeed / g_bugrigs_speed_ref), g_bugrigs_speed_pow)); + float steerfactor = -myspeed * f; + self.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering + + rigvel = self.velocity; + makevectors(self.angles); // new forward direction! + } + + rigvel *= max(0, 1 - vlen(rigvel) * g_bugrigs_friction_air * PHYS_INPUT_TIMELENGTH); + //MAXIMA: airfriction(v) := v * v * g_bugrigs_friction_air; + //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v); + //MAXIMA: solve(total_acceleration(v) = 0, v); + + if (g_bugrigs_planar_movement) + { + vector rigvel_xy, neworigin, up; + float mt; + + rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY; // 4x gravity plays better + rigvel_xy = vec2(rigvel); + + if (g_bugrigs_planar_movement_car_jumping) + mt = MOVE_NORMAL; + else + mt = MOVE_NOMONSTERS; + + tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 1024', mt, self); + up = trace_endpos - self.origin; + + // BUG RIGS: align the move to the surface instead of doing collision testing + // can we move? + tracebox(trace_endpos, self.mins, self.maxs, trace_endpos + rigvel_xy * PHYS_INPUT_TIMELENGTH, mt, self); + + // align to surface + tracebox(trace_endpos, self.mins, self.maxs, trace_endpos - up + '0 0 1' * rigvel_z * PHYS_INPUT_TIMELENGTH, mt, self); + + if (trace_fraction < 0.5) + { + trace_fraction = 1; + neworigin = self.origin; + } + else + neworigin = trace_endpos; + + if (trace_fraction < 1) + { + // now set angles_x so that the car points parallel to the surface + self.angles = vectoangles( + '1 0 0' * v_forward_x * trace_plane_normal_z + + + '0 1 0' * v_forward_y * trace_plane_normal_z + + + '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y) + ); + SET_ONGROUND(self); + } + else + { + // now set angles_x so that the car points forward, but is tilted in velocity direction + UNSET_ONGROUND(self); + } + + self.velocity = (neworigin - self.origin) * (1.0 / PHYS_INPUT_TIMELENGTH); + self.movetype = MOVETYPE_NOCLIP; + } + else + { + rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY; // 4x gravity plays better + self.velocity = rigvel; + self.movetype = MOVETYPE_FLY; + } + + trace_fraction = 1; + tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 4', MOVE_NORMAL, self); + if (trace_fraction != 1) + { + self.angles = vectoangles2( + '1 0 0' * v_forward_x * trace_plane_normal_z + + + '0 1 0' * v_forward_y * trace_plane_normal_z + + + '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y), + trace_plane_normal + ); + } + else + { + vector vel_local; + + vel_local_x = v_forward * self.velocity; + vel_local_y = v_right * self.velocity; + vel_local_z = v_up * self.velocity; + + self.angles_x = racecar_angle(vel_local_x, vel_local_z); + self.angles_z = racecar_angle(-vel_local_y, vel_local_z); + } + + // smooth the angles + vector vf1, vu1, smoothangles; + makevectors(self.angles); + float f = bound(0, PHYS_INPUT_TIMELENGTH * g_bugrigs_angle_smoothing, 1); + if (f == 0) + f = 1; + vf1 = v_forward * f; + vu1 = v_up * f; + makevectors(angles_save); + vf1 = vf1 + v_forward * (1 - f); + vu1 = vu1 + v_up * (1 - f); + smoothangles = vectoangles2(vf1, vu1); + self.angles_x = -smoothangles_x; + self.angles_z = smoothangles_z; +#endif +} + +string specialcommand = "xwxwxsxsxaxdxaxdx1x "; +.float specialcommand_pos; +void SpecialCommand() +{ +#ifdef SVQC +#ifdef TETRIS + TetrisImpulse(); +#else + if (!CheatImpulse(99)) + print("A hollow voice says \"Plugh\".\n"); +#endif +#endif +} + +float PM_check_keepaway(void) +{ +#ifdef SVQC + return (self.ballcarried && g_keepaway) ? autocvar_g_keepaway_ballcarrier_highspeed : 1; +#else + return 1; +#endif +} + +void PM_check_race_movetime(void) +{ +#ifdef SVQC + self.race_movetime_frac += PHYS_INPUT_TIMELENGTH; + float f = floor(self.race_movetime_frac); + self.race_movetime_frac -= f; + self.race_movetime_count += f; + self.race_movetime = self.race_movetime_frac + self.race_movetime_count; +#endif +} + +float PM_check_specialcommand(float buttons) +{ +#ifdef SVQC + string c; + if (!buttons) + c = "x"; + else if (buttons == 1) + c = "1"; + else if (buttons == 2) + c = " "; + else if (buttons == 128) + c = "s"; + else if (buttons == 256) + c = "w"; + else if (buttons == 512) + c = "a"; + else if (buttons == 1024) + c = "d"; + else + c = "?"; + + if (c == substring(specialcommand, self.specialcommand_pos, 1)) + { + self.specialcommand_pos += 1; + if (self.specialcommand_pos >= strlen(specialcommand)) + { + self.specialcommand_pos = 0; + SpecialCommand(); + return true; + } + } + else if (self.specialcommand_pos && (c != substring(specialcommand, self.specialcommand_pos - 1, 1))) + self.specialcommand_pos = 0; +#endif + return false; +} + +void PM_check_nickspam(void) +{ +#ifdef SVQC + if (time >= self.nickspamtime) + return; + if (self.nickspamcount >= autocvar_g_nick_flood_penalty_yellow) + { + // slight annoyance for nick change scripts + self.movement = -1 * self.movement; + self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = self.BUTTON_ZOOM = self.BUTTON_CROUCH = self.BUTTON_HOOK = self.BUTTON_USE = 0; + + if (self.nickspamcount >= autocvar_g_nick_flood_penalty_red) // if you are persistent and the slight annoyance above does not stop you, I'll show you! + { + self.v_angle_x = random() * 360; + self.v_angle_y = random() * 360; + // at least I'm not forcing retardedview by also assigning to angles_z + self.fixangle = true; + } + } +#endif +} + +void PM_check_punch() +{ +#ifdef SVQC + if (self.punchangle != '0 0 0') + { + float f = vlen(self.punchangle) - 10 * PHYS_INPUT_TIMELENGTH; + if (f > 0) + self.punchangle = normalize(self.punchangle) * f; + else + self.punchangle = '0 0 0'; + } + + if (self.punchvector != '0 0 0') + { + float f = vlen(self.punchvector) - 30 * PHYS_INPUT_TIMELENGTH; + if (f > 0) + self.punchvector = normalize(self.punchvector) * f; + else + self.punchvector = '0 0 0'; + } +#endif +} + +void PM_check_spider(void) +{ +#ifdef SVQC + if (time >= self.spider_slowness) + return; + PHYS_MAXSPEED(self) *= 0.5; // half speed while slow from spider + self.stat_sv_airspeedlimit_nonqw *= 0.5; +#endif +} + +// predict frozen movement, as frozen players CAN move in some cases +void PM_check_frozen(void) +{ + if (!PHYS_FROZEN(self)) + return; + if (PHYS_DODGING_FROZEN +#ifdef SVQC + && IS_REAL_CLIENT(self) +#endif + ) + { + self.movement_x = bound(-5, self.movement.x, 5); + self.movement_y = bound(-5, self.movement.y, 5); + self.movement_z = bound(-5, self.movement.z, 5); + } + else + self.movement = '0 0 0'; + + vector midpoint = ((self.absmin + self.absmax) * 0.5); + if (pointcontents(midpoint) == CONTENT_WATER) + { + self.velocity = self.velocity * 0.5; + + if (pointcontents(midpoint + '0 0 16') == CONTENT_WATER) + self.velocity_z = 200; + } +} + +void PM_check_hitground() +{ +#ifdef SVQC + if (IS_ONGROUND(self)) + if (IS_PLAYER(self)) // no fall sounds for observers thank you very much + if (self.wasFlying) + { + self.wasFlying = 0; + if (self.waterlevel < WATERLEVEL_SWIMMING) + if (time >= self.ladder_time) + if (!self.hook) + { + self.nextstep = time + 0.3 + random() * 0.1; + trace_dphitq3surfaceflags = 0; + tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 1', MOVE_NOMONSTERS, self); + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS)) + { + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) + GlobalSound(globalsound_metalfall, CH_PLAYER, VOICETYPE_PLAYERSOUND); + else + GlobalSound(globalsound_fall, CH_PLAYER, VOICETYPE_PLAYERSOUND); + } + } + } +#endif +} + +void PM_check_blocked(void) +{ +#ifdef SVQC + if (!self.player_blocked) + return; + self.movement = '0 0 0'; + self.disableclientprediction = 1; +#endif +} + +#ifdef SVQC +float speedaward_lastsent; +float speedaward_lastupdate; +#endif +void PM_check_race(void) +{ +#ifdef SVQC + if(!(g_cts || g_race)) + return; + if (vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed) + { + speedaward_speed = vlen(self.velocity - self.velocity_z * '0 0 1'); + speedaward_holder = self.netname; + speedaward_uid = self.crypto_idfp; + speedaward_lastupdate = time; + } + if (speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) + { + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; + race_send_speedaward(MSG_ALL); + speedaward_lastsent = speedaward_speed; + if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "") + { + speedaward_alltimebest = speedaward_speed; + speedaward_alltimebest_holder = speedaward_holder; + speedaward_alltimebest_uid = speedaward_uid; + db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"), ftos(speedaward_alltimebest)); + db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"), speedaward_alltimebest_uid); + race_send_speedaward_alltimebest(MSG_ALL); + } + } +#endif +} + +void PM_check_vortex(void) +{ +#ifdef SVQC + // WEAPONTODO + float xyspeed = vlen(vec2(self.velocity)); + if (self.weapon == WEP_VORTEX && WEP_CVAR(vortex, charge) && WEP_CVAR(vortex, charge_velocity_rate) && xyspeed > WEP_CVAR(vortex, charge_minspeed)) + { + // add a maximum of charge_velocity_rate when going fast (f = 1), gradually increasing from minspeed (f = 0) to maxspeed + xyspeed = min(xyspeed, WEP_CVAR(vortex, charge_maxspeed)); + float f = (xyspeed - WEP_CVAR(vortex, charge_minspeed)) / (WEP_CVAR(vortex, charge_maxspeed) - WEP_CVAR(vortex, charge_minspeed)); + // add the extra charge + self.vortex_charge = min(1, self.vortex_charge + WEP_CVAR(vortex, charge_velocity_rate) * f * PHYS_INPUT_TIMELENGTH); + } +#endif +} + +void PM_fly(float maxspd_mod) +{ + // noclipping or flying + UNSET_ONGROUND(self); + + self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION); + makevectors(self.v_angle); + //wishvel = v_forward * self.movement.x + v_right * self.movement.y + v_up * self.movement.z; + vector wishvel = v_forward * self.movement.x + + v_right * self.movement.y + + '0 0 1' * self.movement.z; + // acceleration + vector wishdir = normalize(wishvel); + float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(self) * maxspd_mod); +#ifdef SVQC + if (time >= self.teleport_time) +#endif + PM_Accelerate(wishdir, wishspeed, wishspeed, PHYS_ACCELERATE * maxspd_mod, 1, 0, 0, 0); + PM_ClientMovement_Move(); +} + +void PM_swim(float maxspd_mod) +{ + // swimming + UNSET_ONGROUND(self); + + float jump = PHYS_INPUT_BUTTON_JUMP(self); + // water jump only in certain situations + // this mimics quakeworld code + if (jump && self.waterlevel == WATERLEVEL_SWIMMING && self.velocity_z >= -180) + { + vector yawangles = '0 1 0' * self.v_angle.y; + makevectors(yawangles); + vector forward = v_forward; + vector spot = self.origin + 24 * forward; + spot_z += 8; + traceline(spot, spot, MOVE_NOMONSTERS, self); + if (trace_startsolid) + { + spot_z += 24; + traceline(spot, spot, MOVE_NOMONSTERS, self); + if (!trace_startsolid) + { + self.velocity = forward * 50; + self.velocity_z = 310; + pmove_waterjumptime = 2; + UNSET_ONGROUND(self); + SET_JUMP_HELD(self); + } + } + } + makevectors(self.v_angle); + //wishvel = v_forward * self.movement.x + v_right * self.movement.y + v_up * self.movement.z; + vector wishvel = v_forward * self.movement.x + + v_right * self.movement.y + + '0 0 1' * self.movement.z; + if (wishvel == '0 0 0') + wishvel = '0 0 -60'; // drift towards bottom + + vector wishdir = normalize(wishvel); + float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(self) * maxspd_mod) * 0.7; + + if (IS_DUCKED(self)) + wishspeed *= 0.5; + +// if (pmove_waterjumptime <= 0) // TODO: use + { + // water friction + float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION; + f = min(max(0, f), 1); + self.velocity *= f; + + f = wishspeed - self.velocity * wishdir; + if (f > 0) + { + float accelspeed = min(PHYS_ACCELERATE * PHYS_INPUT_TIMELENGTH * wishspeed, f); + self.velocity += accelspeed * wishdir; + } + + // holding jump button swims upward slowly + if (jump) + { +#if 0 + if (self.watertype & CONTENT_LAVA) + self.velocity_z = 50; + else if (self.watertype & CONTENT_SLIME) + self.velocity_z = 80; + else + { + if (IS_NEXUIZ_DERIVED(gamemode)) +#endif + self.velocity_z = 200; +#if 0 + else + self.velocity_z = 100; + } +#endif + } + } + // water acceleration + PM_Accelerate(wishdir, wishspeed, wishspeed, PHYS_ACCELERATE * maxspd_mod, 1, 0, 0, 0); + PM_ClientMovement_Move(); +} + +void PM_ladder(float maxspd_mod) +{ + // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water + UNSET_ONGROUND(self); + + float g; + g = PHYS_GRAVITY * PHYS_INPUT_TIMELENGTH; + if (PHYS_ENTGRAVITY(self)) + g *= PHYS_ENTGRAVITY(self); + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + { + g *= 0.5; + self.velocity_z += g; + } + + self.velocity = self.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION); + makevectors(self.v_angle); + //wishvel = v_forward * self.movement.x + v_right * self.movement.y + v_up * self.movement.z; + vector wishvel = v_forward * self.movement_x + + v_right * self.movement_y + + '0 0 1' * self.movement_z; + self.velocity_z += g; + if (self.ladder_entity.classname == "func_water") + { + float f = vlen(wishvel); + if (f > self.ladder_entity.speed) + wishvel *= (self.ladder_entity.speed / f); + + self.watertype = self.ladder_entity.skin; + f = self.ladder_entity.origin_z + self.ladder_entity.maxs_z; + if ((self.origin_z + self.view_ofs_z) < f) + self.waterlevel = WATERLEVEL_SUBMERGED; + else if ((self.origin_z + (self.mins_z + self.maxs_z) * 0.5) < f) + self.waterlevel = WATERLEVEL_SWIMMING; + else if ((self.origin_z + self.mins_z + 1) < f) + self.waterlevel = WATERLEVEL_WETFEET; + else + { + self.waterlevel = WATERLEVEL_NONE; + self.watertype = CONTENT_EMPTY; + } + } + // acceleration + vector wishdir = normalize(wishvel); + float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(self) * maxspd_mod); +#ifdef SVQC + if (time >= self.teleport_time) +#endif + // water acceleration + PM_Accelerate(wishdir, wishspeed, wishspeed, PHYS_ACCELERATE*maxspd_mod, 1, 0, 0, 0); + PM_ClientMovement_Move(); +} + +void PM_jetpack(float maxspd_mod) +{ + //makevectors(self.v_angle.y * '0 1 0'); + makevectors(self.v_angle); + vector wishvel = v_forward * self.movement_x + + v_right * self.movement_y; + // add remaining speed as Z component + float maxairspd = PHYS_MAXAIRSPEED * max(1, maxspd_mod); + // fix speedhacks :P + wishvel = normalize(wishvel) * min(1, vlen(wishvel) / maxairspd); + // add the unused velocity as up component + wishvel_z = 0; + + // if (self.BUTTON_JUMP) + wishvel_z = sqrt(max(0, 1 - wishvel * wishvel)); + + // it is now normalized, so... + float a_side = PHYS_JETPACK_ACCEL_SIDE; + float a_up = PHYS_JETPACK_ACCEL_UP; + float a_add = PHYS_JETPACK_ANTIGRAVITY * PHYS_GRAVITY; + + wishvel_x *= a_side; + wishvel_y *= a_side; + wishvel_z *= a_up; + wishvel_z += a_add; + + float best = 0; + ////////////////////////////////////////////////////////////////////////////////////// + // finding the maximum over all vectors of above form + // with wishvel having an absolute value of 1 + ////////////////////////////////////////////////////////////////////////////////////// + // we're finding the maximum over + // f(a_side, a_up, a_add, z) := a_side * (1 - z^2) + (a_add + a_up * z)^2; + // for z in the range from -1 to 1 + ////////////////////////////////////////////////////////////////////////////////////// + // maximum is EITHER attained at the single extreme point: + float a_diff = a_side * a_side - a_up * a_up; + float f; + if (a_diff != 0) + { + f = a_add * a_up / a_diff; // this is the zero of diff(f(a_side, a_up, a_add, z), z) + if (f > -1 && f < 1) // can it be attained? + { + best = (a_diff + a_add * a_add) * (a_diff + a_up * a_up) / a_diff; + //print("middle\n"); + } + } + // OR attained at z = 1: + f = (a_up + a_add) * (a_up + a_add); + if (f > best) + { + best = f; + //print("top\n"); + } + // OR attained at z = -1: + f = (a_up - a_add) * (a_up - a_add); + if (f > best) + { + best = f; + //print("bottom\n"); + } + best = sqrt(best); + ////////////////////////////////////////////////////////////////////////////////////// + + //print("best possible acceleration: ", ftos(best), "\n"); + + float fxy, fz; + fxy = bound(0, 1 - (self.velocity * normalize(wishvel_x * '1 0 0' + wishvel_y * '0 1 0')) / PHYS_JETPACK_MAXSPEED_SIDE, 1); + if (wishvel_z - PHYS_GRAVITY > 0) + fz = bound(0, 1 - self.velocity_z / PHYS_JETPACK_MAXSPEED_UP, 1); + else + fz = bound(0, 1 + self.velocity_z / PHYS_JETPACK_MAXSPEED_UP, 1); + + float fvel; + fvel = vlen(wishvel); + wishvel_x *= fxy; + wishvel_y *= fxy; + wishvel_z = (wishvel_z - PHYS_GRAVITY) * fz + PHYS_GRAVITY; + + fvel = min(1, vlen(wishvel) / best); - if (PHYS_JETPACK_FUEL && !(ITEMS(self) & IT_UNLIMITED_WEAPON_AMMO)) ++ if (PHYS_JETPACK_FUEL && !(ITEMS_STAT(self) & IT_UNLIMITED_WEAPON_AMMO)) + f = min(1, PHYS_AMMO_FUEL(self) / (PHYS_JETPACK_FUEL * PHYS_INPUT_TIMELENGTH * fvel)); + else + f = 1; + + //print("this acceleration: ", ftos(vlen(wishvel) * f), "\n"); + + if (f > 0 && wishvel != '0 0 0') + { + self.velocity = self.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH; + UNSET_ONGROUND(self); + +#ifdef SVQC - if (!(ITEMS(self) & IT_UNLIMITED_WEAPON_AMMO)) ++ if (!(ITEMS_STAT(self) & IT_UNLIMITED_WEAPON_AMMO)) + self.ammo_fuel -= PHYS_JETPACK_FUEL * PHYS_INPUT_TIMELENGTH * fvel * f; + - ITEMS(self) |= IT_USING_JETPACK; ++ ITEMS_STAT(self) |= IT_USING_JETPACK; + + // jetpack also inhibits health regeneration, but only for 1 second + self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen); +#endif + } + +#ifdef CSQC + float g = PHYS_GRAVITY * PHYS_ENTGRAVITY(self) * PHYS_INPUT_TIMELENGTH; + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + self.velocity_z -= g * 0.5; + else + self.velocity_z -= g; + PM_ClientMovement_Move(); + if (!IS_ONGROUND(self) || !(GAMEPLAYFIX_NOGRAVITYONGROUND)) + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + self.velocity_z -= g * 0.5; +#endif +} + +void PM_walk(float buttons_prev, float maxspd_mod) +{ + if (!WAS_ONGROUND(self)) + { +#ifdef SVQC + if (autocvar_speedmeter) + dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n")); +#endif + if (self.lastground < time - 0.3) + self.velocity *= (1 - PHYS_FRICTION_ONLAND); +#ifdef SVQC + if (self.jumppadcount > 1) + dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n")); + self.jumppadcount = 0; +#endif + } + + // walking + makevectors(self.v_angle.y * '0 1 0'); + vector wishvel = v_forward * self.movement.x + + v_right * self.movement.y; + // acceleration + vector wishdir = normalize(wishvel); + float wishspeed = vlen(wishvel); + + wishspeed = min(wishspeed, PHYS_MAXSPEED(self) * maxspd_mod); + if (IS_DUCKED(self)) + wishspeed *= 0.5; + + // apply edge friction + float f = vlen(vec2(self.velocity)); + if (f > 0) + { + float realfriction; + trace_dphitq3surfaceflags = 0; + tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 1', MOVE_NOMONSTERS, self); + // TODO: apply edge friction + // apply ground friction + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK) + realfriction = PHYS_FRICTION_SLICK; + else + realfriction = PHYS_FRICTION; + + f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED) ? (PHYS_STOPSPEED / f) : 1); + f = max(0, f); + self.velocity *= f; + /* + Mathematical analysis time! + + Our goal is to invert this mess. + + For the two cases we get: + v = v0 * (1 - PHYS_INPUT_TIMELENGTH * (PHYS_STOPSPEED / v0) * PHYS_FRICTION) + = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED * PHYS_FRICTION + v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED * PHYS_FRICTION + and + v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) + v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) + + These cases would be chosen ONLY if: + v0 < PHYS_STOPSPEED + v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED * PHYS_FRICTION < PHYS_STOPSPEED + v < PHYS_STOPSPEED * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) + and, respectively: + v0 >= PHYS_STOPSPEED + v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) >= PHYS_STOPSPEED + v >= PHYS_STOPSPEED * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION) + */ + } + float addspeed = wishspeed - self.velocity * wishdir; + if (addspeed > 0) + { + float accelspeed = min(PHYS_ACCELERATE * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed); + self.velocity += accelspeed * wishdir; + } + float g = PHYS_GRAVITY * PHYS_ENTGRAVITY(self) * PHYS_INPUT_TIMELENGTH; + if (!(GAMEPLAYFIX_NOGRAVITYONGROUND)) + self.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1); + if (self.velocity * self.velocity) + PM_ClientMovement_Move(); + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + if (!IS_ONGROUND(self) || !GAMEPLAYFIX_NOGRAVITYONGROUND) + self.velocity_z -= g * 0.5; +} + +void PM_air(float buttons_prev, float maxspd_mod) +{ + makevectors(self.v_angle.y * '0 1 0'); + vector wishvel = v_forward * self.movement.x + + v_right * self.movement.y; + // acceleration + vector wishdir = normalize(wishvel); + float wishspeed = vlen(wishvel); + +#ifdef SVQC + if (time >= self.teleport_time) +#else + if (pmove_waterjumptime <= 0) +#endif + { + float maxairspd = PHYS_MAXAIRSPEED * min(maxspd_mod, 1); + + // apply air speed limit + float airaccelqw = PHYS_AIRACCEL_QW(self); + float wishspeed0 = wishspeed; + wishspeed = min(wishspeed, maxairspd); + if (IS_DUCKED(self)) + wishspeed *= 0.5; + float airaccel = PHYS_AIRACCELERATE * min(maxspd_mod, 1); + + float accelerating = (self.velocity * wishdir > 0); + float wishspeed2 = wishspeed; + + // CPM: air control + if (PHYS_AIRSTOPACCELERATE) + { + vector curdir = normalize(vec2(self.velocity)); + airaccel += (PHYS_AIRSTOPACCELERATE*maxspd_mod - airaccel) * max(0, -(curdir * wishdir)); + } + // note that for straight forward jumping: + // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0; + // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw); + // --> + // dv/dt = accel * maxspeed (when slow) + // dv/dt = accel * maxspeed * (1 - accelqw) (when fast) + // log dv/dt = logaccel + logmaxspeed (when slow) + // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast) + float strafity = IsMoveInDirection(self.movement, -90) + IsMoveInDirection(self.movement, +90); // if one is nonzero, other is always zero + if (PHYS_MAXAIRSTRAFESPEED) + wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED*maxspd_mod)); + if (PHYS_AIRSTRAFEACCELERATE) + airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE*maxspd_mod); + if (PHYS_AIRSTRAFEACCEL_QW(self)) + airaccelqw = + (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(self) : PHYS_AIRACCEL_QW(self)) >= 0) ? +1 : -1) + * + (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(self)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(self)))); + // !CPM + + if (PHYS_WARSOWBUNNY_TURNACCEL && accelerating && self.movement.y == 0 && self.movement.x != 0) + PM_AirAccelerate(wishdir, wishspeed2); + else + PM_Accelerate(wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(self), PHYS_AIRACCEL_SIDEWAYS_FRICTION / maxairspd, PHYS_AIRSPEEDLIMIT_NONQW(self)); + + if (PHYS_AIRCONTROL) + CPM_PM_Aircontrol(wishdir, wishspeed2); + } + float g = PHYS_GRAVITY * PHYS_ENTGRAVITY(self) * PHYS_INPUT_TIMELENGTH; + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + self.velocity_z -= g * 0.5; + else + self.velocity_z -= g; + PM_ClientMovement_Move(); + if (!IS_ONGROUND(self) || !(GAMEPLAYFIX_NOGRAVITYONGROUND)) + if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) + self.velocity_z -= g * 0.5; +} + +// used for calculating airshots +bool IsFlying(entity a) +{ + if(IS_ONGROUND(a)) + return false; + if(a.waterlevel >= WATERLEVEL_SWIMMING) + return false; + traceline(a.origin, a.origin - '0 0 48', MOVE_NORMAL, a); + if(trace_fraction < 1) + return false; + return true; +} + +void PM_Main() +{ + int buttons = PHYS_INPUT_BUTTON_MASK(self); +#ifdef CSQC + self.items = getstati(STAT_ITEMS, 0, 24); + + self.movement = PHYS_INPUT_MOVEVALUES(self); + + vector oldv_angle = self.v_angle; + vector oldangles = self.angles; // we need to save these, as they're abused by other code + self.v_angle = PHYS_INPUT_ANGLES(self); + self.angles = PHYS_WORLD_ANGLES(self); + + self.team = myteam + 1; // is this correct? + if (!(PHYS_INPUT_BUTTON_JUMP(self))) // !jump + UNSET_JUMP_HELD(self); // canjump = true + pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH; + + PM_ClientMovement_UpdateStatus(true); +#endif + + +#ifdef SVQC + WarpZone_PlayerPhysics_FixVAngle(); +#endif + float maxspeed_mod = 1; + maxspeed_mod *= PM_check_keepaway(); + maxspeed_mod *= PHYS_HIGHSPEED; + +#ifdef SVQC + Physics_UpdateStats(maxspeed_mod); + + if (self.PlayerPhysplug) + if (self.PlayerPhysplug()) + return; +#endif + + PM_check_race_movetime(); +#ifdef SVQC + anticheat_physics(); +#endif + + if (PM_check_specialcommand(buttons)) + return; +#ifdef SVQC + if (sv_maxidle > 0) + { + if (buttons != self.buttons_old || self.movement != self.movement_old || self.v_angle != self.v_angle_old) + self.parm_idlesince = time; + } +#endif + int buttons_prev = self.buttons_old; + self.buttons_old = buttons; + self.movement_old = self.movement; + self.v_angle_old = self.v_angle; + + PM_check_nickspam(); + + PM_check_punch(); +#ifdef SVQC + if (IS_BOT_CLIENT(self)) + { + if (playerdemo_read()) + return; + bot_think(); + } + + if (IS_PLAYER(self)) +#endif + { +#ifdef SVQC + if (self.race_penalty) + if (time > self.race_penalty) + self.race_penalty = 0; +#endif + + bool not_allowed_to_move = false; +#ifdef SVQC + if (self.race_penalty) + not_allowed_to_move = true; +#endif +#ifdef SVQC + if (time < game_starttime) + not_allowed_to_move = true; +#endif + + if (not_allowed_to_move) + { + self.velocity = '0 0 0'; + self.movetype = MOVETYPE_NONE; +#ifdef SVQC + self.disableclientprediction = 2; +#endif + } +#ifdef SVQC + else if (self.disableclientprediction == 2) + { + if (self.movetype == MOVETYPE_NONE) + self.movetype = MOVETYPE_WALK; + self.disableclientprediction = 0; + } +#endif + } + +#ifdef SVQC + if (self.movetype == MOVETYPE_NONE) + return; + + // when we get here, disableclientprediction cannot be 2 + self.disableclientprediction = 0; +#endif + + PM_check_spider(); + + PM_check_frozen(); + + PM_check_blocked(); + + maxspeed_mod = 1; + + if (self.in_swamp) + maxspeed_mod *= self.swamp_slowdown; //cvar("g_balance_swamp_moverate"); + + // conveyors: first fix velocity + if (self.conveyor.state) + self.velocity -= self.conveyor.movedir; + +#ifdef SVQC + MUTATOR_CALLHOOK(PlayerPhysics); +#endif +#ifdef CSQC + PM_multijump(); +#endif + +// float forcedodge = 1; +// if(forcedodge) { +//#ifdef CSQC +// PM_dodging_checkpressedkeys(); +//#endif +// PM_dodging(); +// PM_ClientMovement_Move(); +// return; +// } + +#ifdef SVQC + if (!IS_PLAYER(self)) + { + maxspeed_mod = autocvar_sv_spectator_speed_multiplier; + if (!self.spectatorspeed) + self.spectatorspeed = maxspeed_mod; + if (self.impulse && self.impulse <= 19 || (self.impulse >= 200 && self.impulse <= 209) || (self.impulse >= 220 && self.impulse <= 229)) + { + if (self.lastclassname != "player") + { + if (self.impulse == 10 || self.impulse == 15 || self.impulse == 18 || (self.impulse >= 200 && self.impulse <= 209)) + self.spectatorspeed = bound(1, self.spectatorspeed + 0.5, 5); + else if (self.impulse == 11) + self.spectatorspeed = maxspeed_mod; + else if (self.impulse == 12 || self.impulse == 16 || self.impulse == 19 || (self.impulse >= 220 && self.impulse <= 229)) + self.spectatorspeed = bound(1, self.spectatorspeed - 0.5, 5); + else if (self.impulse >= 1 && self.impulse <= 9) + self.spectatorspeed = 1 + 0.5 * (self.impulse - 1); + } // otherwise just clear + self.impulse = 0; + } + maxspeed_mod = self.spectatorspeed; + } + + float spd = max(PHYS_MAXSPEED(self), PHYS_MAXAIRSPEED) * maxspeed_mod; + if(self.speed != spd) + { + self.speed = spd; + string temps = ftos(spd); + stuffcmd(self, strcat("cl_forwardspeed ", temps, "\n")); + stuffcmd(self, strcat("cl_backspeed ", temps, "\n")); + stuffcmd(self, strcat("cl_sidespeed ", temps, "\n")); + stuffcmd(self, strcat("cl_upspeed ", temps, "\n")); + } +#endif + + if(PHYS_DEAD(self)) + goto end; + +#ifdef SVQC + if (!self.fixangle && !g_bugrigs) + self.angles = '0 1 0' * self.v_angle.y; +#endif + + PM_check_hitground(); + + if(IsFlying(self)) + self.wasFlying = 1; + + if (IS_PLAYER(self)) + CheckPlayerJump(); + + if (self.flags & FL_WATERJUMP) + { + self.velocity_x = self.movedir_x; + self.velocity_y = self.movedir_y; + if (time > self.teleport_time || self.waterlevel == WATERLEVEL_NONE) + { + self.flags &= ~FL_WATERJUMP; + self.teleport_time = 0; + } + } + +#ifdef SVQC + else if (g_bugrigs && IS_PLAYER(self)) + RaceCarPhysics(); +#endif + + else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY || self.movetype == MOVETYPE_FLY_WORLDONLY || (BUFFS(self) & BUFF_FLIGHT)) + PM_fly(maxspeed_mod); + + else if (self.waterlevel >= WATERLEVEL_SWIMMING) + PM_swim(maxspeed_mod); + + else if (time < self.ladder_time) + PM_ladder(maxspeed_mod); + - else if (ITEMS(self) & IT_USING_JETPACK) ++ else if (ITEMS_STAT(self) & IT_USING_JETPACK) + PM_jetpack(maxspeed_mod); + + else if (IS_ONGROUND(self)) + PM_walk(buttons_prev, maxspeed_mod); + + else + PM_air(buttons_prev, maxspeed_mod); + +#ifdef SVQC + if (!IS_OBSERVER(self)) + PM_check_race(); +#endif + PM_check_vortex(); + +:end + if (IS_ONGROUND(self)) + self.lastground = time; + + // conveyors: then break velocity again + if(self.conveyor.state) + self.velocity += self.conveyor.movedir; + + self.lastflags = self.flags; + + self.lastclassname = self.classname; + +#ifdef CSQC + self.v_angle = oldv_angle; + self.angles = oldangles; +#endif +} + +#ifdef SVQC +void SV_PlayerPhysics(void) +#elif defined(CSQC) +void CSQC_ClientMovement_PlayerMove_Frame(void) +#endif +{ + PM_Main(); + +#ifdef CSQC + self.pmove_flags = + ((self.flags & FL_DUCKED) ? PMF_DUCKED : 0) | + (!(self.flags & FL_JUMPRELEASED) ? 0 : PMF_JUMP_HELD) | + ((self.flags & FL_ONGROUND) ? PMF_ONGROUND : 0); +#endif +} diff --cc qcsrc/common/physics.qh index c704bc917c,0000000000..599eee5d16 mode 100644,000000..100644 --- a/qcsrc/common/physics.qh +++ b/qcsrc/common/physics.qh @@@ -1,349 -1,0 +1,349 @@@ +#ifndef COMMON_PHYSICS_H +#define COMMON_PHYSICS_H + +// Client/server mappings + +.entity conveyor; + +.float race_penalty; + +.float gravity; +.float swamp_slowdown; +.float lastflags; +.float lastground; +.float wasFlying; +.float spectatorspeed; + +.vector movement_old; +.float buttons_old; +.vector v_angle_old; +.string lastclassname; + +.float() PlayerPhysplug; +float AdjustAirAccelQW(float accelqw, float factor); + +bool IsFlying(entity a); + +#ifdef CSQC + + const int FL_WATERJUMP = 2048; // player jumping out of water + const int FL_JUMPRELEASED = 4096; // for jump debouncing + + float PM_multijump_checkjump(); + void PM_multijump(); + + .float watertype; + .int items; + + .vector movement; + .vector v_angle; + +// TODO + #define IS_CLIENT(s) (s).isplayermodel + #define IS_PLAYER(s) (s).isplayermodel + #define isPushable(s) (s).isplayermodel + + float player_multijump; + float player_jumpheight; + + #define PHYS_INPUT_ANGLES(s) input_angles +// TODO + #define PHYS_WORLD_ANGLES(s) input_angles + + #define PHYS_INPUT_TIMELENGTH input_timelength + #define PHYS_INPUT_FRAMETIME serverdeltatime + + #define PHYS_INPUT_MOVEVALUES(s) input_movevalues + + #define PHYS_INPUT_BUTTON_MASK(s) (input_buttons | 128 * (input_movevalues_x < 0) | 256 * (input_movevalues_x > 0) | 512 * (input_movevalues_y < 0) | 1024 * (input_movevalues_y > 0)) + #define PHYS_INPUT_BUTTON_ATCK(s) !!(input_buttons & 1) + #define PHYS_INPUT_BUTTON_JUMP(s) !!(input_buttons & 2) + #define PHYS_INPUT_BUTTON_ATCK2(s) !!(input_buttons & 4) + #define PHYS_INPUT_BUTTON_ZOOM(s) !!(input_buttons & 8) + #define PHYS_INPUT_BUTTON_CROUCH(s) !!(input_buttons & 16) + #define PHYS_INPUT_BUTTON_HOOK(s) !!(input_buttons & 32) + #define PHYS_INPUT_BUTTON_USE(s) !!(input_buttons & 64) + #define PHYS_INPUT_BUTTON_BACKWARD(s) !!(input_buttons & 128) + #define PHYS_INPUT_BUTTON_FORWARD(s) !!(input_buttons & 256) + #define PHYS_INPUT_BUTTON_LEFT(s) !!(input_buttons & 512) + #define PHYS_INPUT_BUTTON_RIGHT(s) !!(input_buttons & 1024) + #define PHYS_INPUT_BUTTON_JETPACK(s) !!(input_buttons & 4096) + + #define PHYS_DEAD(s) s.csqcmodel_isdead + + #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE !!(moveflags & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE) + #define GAMEPLAYFIX_NOGRAVITYONGROUND cvar("sv_gameplayfix_nogravityonground") + #define GAMEPLAYFIX_Q2AIRACCELERATE cvar("sv_gameplayfix_q2airaccelerate") + #define GAMEPLAYFIX_EASIERWATERJUMP getstati(STAT_GAMEPLAYFIX_EASIERWATERJUMP) + #define GAMEPLAYFIX_DOWNTRACEONGROUND getstati(STAT_GAMEPLAYFIX_DOWNTRACEONGROUND) + #define GAMEPLAYFIX_STEPMULTIPLETIMES getstati(STAT_GAMEPLAYFIX_STEPMULTIPLETIMES) + #define GAMEPLAYFIX_UNSTICKPLAYERS getstati(STAT_GAMEPLAYFIX_UNSTICKPLAYERS) + #define GAMEPLAYFIX_STEPDOWN getstati(STAT_GAMEPLAYFIX_STEPDOWN) + + #define IS_DUCKED(s) !!(s.flags & FL_DUCKED) + #define SET_DUCKED(s) s.flags |= FL_DUCKED + #define UNSET_DUCKED(s) s.flags &= ~FL_DUCKED + + #define IS_JUMP_HELD(s) !(s.flags & FL_JUMPRELEASED) + #define SET_JUMP_HELD(s) s.flags &= ~FL_JUMPRELEASED + #define UNSET_JUMP_HELD(s) s.flags |= FL_JUMPRELEASED + + #define IS_ONGROUND(s) !!(s.flags & FL_ONGROUND) + #define SET_ONGROUND(s) s.flags |= FL_ONGROUND + #define UNSET_ONGROUND(s) s.flags &= ~FL_ONGROUND + + #define WAS_ONGROUND(s) !!(s.lastflags & FL_ONGROUND) + - #define ITEMS(s) (s).items ++ #define ITEMS_STAT(s) (s).items + #define BUFFS(s) getstati(STAT_BUFFS) + + #define PHYS_AMMO_FUEL(s) getstati(STAT_FUEL) + + #define PHYS_FROZEN(s) getstati(STAT_FROZEN) + + #define PHYS_DOUBLEJUMP getstati(STAT_DOUBLEJUMP) + + #define PHYS_BUGRIGS getstati(STAT_BUGRIGS) + #define PHYS_BUGRIGS_ANGLE_SMOOTHING getstati(STAT_BUGRIGS_ANGLE_SMOOTHING) + #define PHYS_BUGRIGS_PLANAR_MOVEMENT getstati(STAT_BUGRIGS_PLANAR_MOVEMENT) + #define PHYS_BUGRIGS_REVERSE_SPEEDING getstati(STAT_BUGRIGS_REVERSE_SPEEDING) + #define PHYS_BUGRIGS_FRICTION_FLOOR getstatf(STAT_BUGRIGS_FRICTION_FLOOR) + #define PHYS_BUGRIGS_AIR_STEERING getstati(STAT_BUGRIGS_AIR_STEERING) + #define PHYS_BUGRIGS_FRICTION_BRAKE getstatf(STAT_BUGRIGS_FRICTION_BRAKE) + #define PHYS_BUGRIGS_ACCEL getstatf(STAT_BUGRIGS_ACCEL) + #define PHYS_BUGRIGS_SPEED_REF getstatf(STAT_BUGRIGS_SPEED_REF) + #define PHYS_BUGRIGS_SPEED_POW getstatf(STAT_BUGRIGS_SPEED_POW) + #define PHYS_BUGRIGS_STEER getstatf(STAT_BUGRIGS_STEER) + #define PHYS_BUGRIGS_FRICTION_AIR getstatf(STAT_BUGRIGS_FRICTION_AIR) + #define PHYS_BUGRIGS_CAR_JUMPING getstatf(STAT_BUGRIGS_CAR_JUMPING) + #define PHYS_BUGRIGS_REVERSE_SPINNING getstatf(STAT_BUGRIGS_REVERSE_SPINNING) + #define PHYS_BUGRIGS_REVERSE_STOPPING getstatf(STAT_BUGRIGS_REVERSE_STOPPING) + + #define PHYS_JUMPSPEEDCAP_MIN getstatf(STAT_MOVEVARS_JUMPSPEEDCAP_MIN) + #define PHYS_JUMPSPEEDCAP_MAX getstatf(STAT_MOVEVARS_JUMPSPEEDCAP_MAX) + #define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS getstati(STAT_MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS) + + #define PHYS_TRACK_CANJUMP(s) getstati(STAT_MOVEVARS_TRACK_CANJUMP) + #define PHYS_ACCELERATE getstatf(STAT_MOVEVARS_ACCELERATE) + #define PHYS_AIRACCEL_QW(s) getstatf(STAT_MOVEVARS_AIRACCEL_QW) + #define PHYS_AIRACCEL_QW_STRETCHFACTOR(s) getstatf(STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR) + #define PHYS_AIRACCEL_SIDEWAYS_FRICTION getstatf(STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION) + #define PHYS_AIRACCELERATE getstatf(STAT_MOVEVARS_AIRACCELERATE) + #define PHYS_AIRCONTROL getstatf(STAT_MOVEVARS_AIRCONTROL) + #define PHYS_AIRCONTROL_PENALTY getstatf(STAT_MOVEVARS_AIRCONTROL_PENALTY) + #define PHYS_AIRCONTROL_POWER getstatf(STAT_MOVEVARS_AIRCONTROL_POWER) + #define PHYS_AIRSPEEDLIMIT_NONQW(s) getstatf(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW) + #define PHYS_AIRSTOPACCELERATE getstatf(STAT_MOVEVARS_AIRSTOPACCELERATE) + #define PHYS_AIRSTRAFEACCEL_QW(s) getstatf(STAT_MOVEVARS_AIRSTRAFEACCEL_QW) + #define PHYS_AIRSTRAFEACCELERATE getstatf(STAT_MOVEVARS_AIRSTRAFEACCELERATE) + #define PHYS_ENTGRAVITY(s) getstatf(STAT_MOVEVARS_ENTGRAVITY) + #define PHYS_FRICTION getstatf(STAT_MOVEVARS_FRICTION) + #define PHYS_FRICTION_SLICK getstatf(STAT_MOVEVARS_FRICTION_SLICK) + #define PHYS_FRICTION_ONLAND getstatf(STAT_MOVEVARS_FRICTION_ONLAND) + #define PHYS_GRAVITY getstatf(STAT_MOVEVARS_GRAVITY) + #define PHYS_HIGHSPEED getstatf(STAT_MOVEVARS_HIGHSPEED) + #define PHYS_JUMPVELOCITY getstatf(STAT_MOVEVARS_JUMPVELOCITY) + #define PHYS_MAXAIRSPEED getstatf(STAT_MOVEVARS_MAXAIRSPEED) + #define PHYS_MAXAIRSTRAFESPEED getstatf(STAT_MOVEVARS_MAXAIRSTRAFESPEED) + #define PHYS_MAXSPEED(s) getstatf(STAT_MOVEVARS_MAXSPEED) + #define PHYS_STEPHEIGHT getstatf(STAT_MOVEVARS_STEPHEIGHT) + #define PHYS_STOPSPEED getstatf(STAT_MOVEVARS_STOPSPEED) + #define PHYS_WARSOWBUNNY_ACCEL getstatf(STAT_MOVEVARS_WARSOWBUNNY_ACCEL) + #define PHYS_WARSOWBUNNY_BACKTOSIDERATIO getstatf(STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO) + #define PHYS_WARSOWBUNNY_AIRFORWARDACCEL getstatf(STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL) + #define PHYS_WARSOWBUNNY_TOPSPEED getstatf(STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED) + #define PHYS_WARSOWBUNNY_TURNACCEL getstatf(STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL) + + #define PHYS_WALLFRICTION getstati(STAT_MOVEVARS_WALLFRICTION) + + #define PHYS_JETPACK_ACCEL_UP getstatf(STAT_JETPACK_ACCEL_UP) + #define PHYS_JETPACK_ACCEL_SIDE getstatf(STAT_JETPACK_ACCEL_SIDE) + #define PHYS_JETPACK_ANTIGRAVITY getstatf(STAT_JETPACK_ANTIGRAVITY) + #define PHYS_JETPACK_FUEL getstatf(STAT_JETPACK_FUEL) + #define PHYS_JETPACK_MAXSPEED_UP getstatf(STAT_JETPACK_MAXSPEED_UP) + #define PHYS_JETPACK_MAXSPEED_SIDE getstatf(STAT_JETPACK_MAXSPEED_SIDE) + + #define PHYS_DODGING_FROZEN getstati(STAT_DODGING_FROZEN) + + #define PHYS_NOSTEP getstati(STAT_NOSTEP) + #define PHYS_JUMPSTEP getstati(STAT_MOVEVARS_JUMPSTEP) + +#elif defined(SVQC) + + .vector stat_pl_view_ofs; + .vector stat_pl_crouch_view_ofs; + + .vector stat_pl_min; + .vector stat_pl_max; + .vector stat_pl_crouch_min; + .vector stat_pl_crouch_max; + + .float stat_sv_airaccel_qw; + .float stat_sv_airstrafeaccel_qw; + .float stat_sv_airspeedlimit_nonqw; + .float stat_sv_maxspeed; + .float stat_movement_highspeed; + + .float stat_sv_friction_on_land; + .float stat_sv_friction_slick; + + .float stat_doublejump; + + .float stat_jumpspeedcap_min; + .float stat_jumpspeedcap_max; + .float stat_jumpspeedcap_disable_onramps; + + .float stat_jetpack_accel_side; + .float stat_jetpack_accel_up; + .float stat_jetpack_antigravity; + .float stat_jetpack_fuel; + .float stat_jetpack_maxspeed_up; + .float stat_jetpack_maxspeed_side; + .float stat_gameplayfix_easierwaterjump; + .float stat_gameplayfix_downtracesupportsongroundflag; + .float stat_gameplayfix_stepmultipletimes; + .float stat_gameplayfix_unstickplayers; + .float stat_gameplayfix_stepdown; + + .float stat_bugrigs; + .float stat_bugrigs_angle_smoothing; + .float stat_bugrigs_planar_movement; + .float stat_bugrigs_reverse_speeding; + .float stat_bugrigs_friction_floor; + .float stat_bugrigs_air_steering; + .float stat_bugrigs_friction_brake; + .float stat_bugrigs_accel; + .float stat_bugrigs_speed_ref; + .float stat_bugrigs_speed_pow; + .float stat_bugrigs_steer; + .float stat_bugrigs_friction_air; + .float stat_bugrigs_car_jumping; + .float stat_bugrigs_reverse_spinning; + .float stat_bugrigs_reverse_stopping; + + .float stat_nostep; + .float stat_jumpstep; + + #define PHYS_INPUT_ANGLES(s) s.v_angle + #define PHYS_WORLD_ANGLES(s) s.angles + + #define PHYS_INPUT_TIMELENGTH frametime + #define PHYS_INPUT_FRAMETIME sys_frametime + + #define PHYS_INPUT_MOVEVALUES(s) s.movement + // TODO: cache + #define PHYS_INPUT_BUTTON_MASK(s) (s.BUTTON_ATCK | 2 * s.BUTTON_JUMP | 4 * s.BUTTON_ATCK2 | 8 * s.BUTTON_ZOOM | 16 * s.BUTTON_CROUCH | 32 * s.BUTTON_HOOK | 64 * s.BUTTON_USE | 128 * (s.movement_x < 0) | 256 * (s.movement_x > 0) | 512 * (s.movement_y < 0) | 1024 * (s.movement_y > 0)) + #define PHYS_INPUT_BUTTON_ATCK(s) s.BUTTON_ATCK + #define PHYS_INPUT_BUTTON_JUMP(s) s.BUTTON_JUMP + #define PHYS_INPUT_BUTTON_ATCK2(s) s.BUTTON_ATCK2 + #define PHYS_INPUT_BUTTON_ZOOM(s) s.BUTTON_ZOOM + #define PHYS_INPUT_BUTTON_CROUCH(s) s.BUTTON_CROUCH + #define PHYS_INPUT_BUTTON_HOOK(s) s.BUTTON_HOOK + #define PHYS_INPUT_BUTTON_USE(s) s.BUTTON_USE + #define PHYS_INPUT_BUTTON_BACKWARD(s) (s.movement_x < 0) + #define PHYS_INPUT_BUTTON_FORWARD(s) (s.movement_x > 0) + #define PHYS_INPUT_BUTTON_LEFT(s) (s.movement_y < 0) + #define PHYS_INPUT_BUTTON_RIGHT(s) (s.movement_y > 0) + #define PHYS_INPUT_BUTTON_JETPACK(s) s.BUTTON_JETPACK + + #define PHYS_DEAD(s) s.deadflag != DEAD_NO + + #define GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE autocvar_sv_gameplayfix_gravityunaffectedbyticrate + #define GAMEPLAYFIX_NOGRAVITYONGROUND cvar("sv_gameplayfix_nogravityonground") + #define GAMEPLAYFIX_Q2AIRACCELERATE autocvar_sv_gameplayfix_q2airaccelerate + #define GAMEPLAYFIX_EASIERWATERJUMP cvar("sv_gameplayfix_easierwaterjump") + #define GAMEPLAYFIX_DOWNTRACEONGROUND cvar("sv_gameplayfix_downtracesupportsongroundflag") + #define GAMEPLAYFIX_STEPMULTIPLETIMES cvar("sv_gameplayfix_stepmultipletimes") + #define GAMEPLAYFIX_UNSTICKPLAYERS cvar("sv_gameplayfix_unstickplayers") + #define GAMEPLAYFIX_STEPDOWN cvar("sv_gameplayfix_stepdown") + + #define IS_DUCKED(s) s.crouch + #define SET_DUCKED(s) s.crouch = true + #define UNSET_DUCKED(s) s.crouch = false + + #define IS_JUMP_HELD(s) !(s.flags & FL_JUMPRELEASED) + #define SET_JUMP_HELD(s) s.flags &= ~FL_JUMPRELEASED + #define UNSET_JUMP_HELD(s) s.flags |= FL_JUMPRELEASED + + #define IS_ONGROUND(s) !!(s.flags & FL_ONGROUND) + #define SET_ONGROUND(s) s.flags |= FL_ONGROUND + #define UNSET_ONGROUND(s) s.flags &= ~FL_ONGROUND + + #define WAS_ONGROUND(s) !!((s).lastflags & FL_ONGROUND) + - #define ITEMS(s) s.items ++ #define ITEMS_STAT(s) s.items + #define BUFFS(s) (s).buffs + + #define PHYS_AMMO_FUEL(s) s.ammo_fuel + + #define PHYS_FROZEN(s) s.frozen + + #define PHYS_DOUBLEJUMP autocvar_sv_doublejump + + #define PHYS_BUGRIGS g_bugrigs + #define PHYS_BUGRIGS_ANGLE_SMOOTHING g_bugrigs_angle_smoothing + #define PHYS_BUGRIGS_PLANAR_MOVEMENT g_bugrigs_planar_movement + #define PHYS_BUGRIGS_REVERSE_SPEEDING g_bugrigs_reverse_speeding + #define PHYS_BUGRIGS_FRICTION_FLOOR g_bugrigs_friction_floor + #define PHYS_BUGRIGS_AIR_STEERING g_bugrigs_air_steering + #define PHYS_BUGRIGS_FRICTION_BRAKE g_bugrigs_friction_brake + #define PHYS_BUGRIGS_ACCEL g_bugrigs_accel + #define PHYS_BUGRIGS_SPEED_REF g_bugrigs_speed_ref + #define PHYS_BUGRIGS_SPEED_POW g_bugrigs_speed_pow + #define PHYS_BUGRIGS_STEER g_bugrigs_steer + #define PHYS_BUGRIGS_FRICTION_AIR g_bugrigs_friction_air + #define PHYS_BUGRIGS_CAR_JUMPING g_bugrigs_planar_movement_car_jumping + #define PHYS_BUGRIGS_REVERSE_SPINNING g_bugrigs_reverse_spinning + #define PHYS_BUGRIGS_REVERSE_STOPPING g_bugrigs_reverse_stopping + + #define PHYS_JUMPSPEEDCAP_MIN autocvar_sv_jumpspeedcap_min + #define PHYS_JUMPSPEEDCAP_MAX autocvar_sv_jumpspeedcap_max + #define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS autocvar_sv_jumpspeedcap_max_disable_on_ramps + + #define PHYS_TRACK_CANJUMP(s) s.cvar_cl_movement_track_canjump + #define PHYS_ACCELERATE autocvar_sv_accelerate + #define PHYS_AIRACCEL_QW(s) s.stat_sv_airaccel_qw + #define PHYS_AIRACCEL_QW_STRETCHFACTOR(s) autocvar_sv_airaccel_qw_stretchfactor + #define PHYS_AIRACCEL_SIDEWAYS_FRICTION autocvar_sv_airaccel_sideways_friction + #define PHYS_AIRACCELERATE autocvar_sv_airaccelerate + #define PHYS_AIRCONTROL autocvar_sv_aircontrol + #define PHYS_AIRCONTROL_PENALTY autocvar_sv_aircontrol_penalty + #define PHYS_AIRCONTROL_POWER autocvar_sv_aircontrol_power + #define PHYS_AIRSPEEDLIMIT_NONQW(s) s.stat_sv_airspeedlimit_nonqw + #define PHYS_AIRSTOPACCELERATE autocvar_sv_airstopaccelerate + #define PHYS_AIRSTRAFEACCEL_QW(s) s.stat_sv_airstrafeaccel_qw + #define PHYS_AIRSTRAFEACCELERATE autocvar_sv_airstrafeaccelerate + #define PHYS_ENTGRAVITY(s) s.gravity + #define PHYS_FRICTION autocvar_sv_friction + #define PHYS_FRICTION_SLICK autocvar_sv_friction_slick + #define PHYS_FRICTION_ONLAND autocvar_sv_friction_on_land + #define PHYS_GRAVITY autocvar_sv_gravity + #define PHYS_HIGHSPEED autocvar_g_movement_highspeed + #define PHYS_JUMPVELOCITY autocvar_sv_jumpvelocity + #define PHYS_MAXAIRSPEED autocvar_sv_maxairspeed + #define PHYS_MAXAIRSTRAFESPEED autocvar_sv_maxairstrafespeed + #define PHYS_MAXSPEED(s) s.stat_sv_maxspeed + #define PHYS_STEPHEIGHT autocvar_sv_stepheight + #define PHYS_STOPSPEED autocvar_sv_stopspeed + #define PHYS_WARSOWBUNNY_ACCEL autocvar_sv_warsowbunny_accel + #define PHYS_WARSOWBUNNY_BACKTOSIDERATIO autocvar_sv_warsowbunny_backtosideratio + #define PHYS_WARSOWBUNNY_AIRFORWARDACCEL autocvar_sv_warsowbunny_airforwardaccel + #define PHYS_WARSOWBUNNY_TOPSPEED autocvar_sv_warsowbunny_topspeed + #define PHYS_WARSOWBUNNY_TURNACCEL autocvar_sv_warsowbunny_turnaccel + + #define PHYS_WALLFRICTION cvar("sv_wallfriction") + + #define PHYS_JETPACK_ACCEL_UP autocvar_g_jetpack_acceleration_up + #define PHYS_JETPACK_ACCEL_SIDE autocvar_g_jetpack_acceleration_side + #define PHYS_JETPACK_ANTIGRAVITY autocvar_g_jetpack_antigravity + #define PHYS_JETPACK_FUEL autocvar_g_jetpack_fuel + #define PHYS_JETPACK_MAXSPEED_UP autocvar_g_jetpack_maxspeed_up + #define PHYS_JETPACK_MAXSPEED_SIDE autocvar_g_jetpack_maxspeed_side + + #define PHYS_DODGING_FROZEN autocvar_sv_dodging_frozen + + #define PHYS_NOSTEP cvar("sv_nostep") + #define PHYS_JUMPSTEP cvar("sv_jumpstep") + +#endif +#endif diff --cc qcsrc/common/triggers/func/breakable.qc index 9c371b5d42,0000000000..16bfcd57d2 mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/breakable.qc +++ b/qcsrc/common/triggers/func/breakable.qc @@@ -1,315 -1,0 +1,316 @@@ +#ifdef SVQC +#include "../../../server/weapons/common.qh" ++#include "../../../server/_all.qh" + +.entity sprite; + +.float dmg; +.float dmg_edge; +.float dmg_radius; +.float dmg_force; +.float debrismovetype; +.float debrissolid; +.vector debrisvelocity; +.vector debrisvelocityjitter; +.vector debrisavelocityjitter; +.float debristime; +.float debristimejitter; +.float debrisfadetime; +.float debrisdamageforcescale; +.float debrisskin; + +.string mdl_dead; // or "" to hide when broken +.string debris; // space separated list of debris models +// other fields: +// mdl = particle effect name +// count = particle effect multiplier +// targetname = target to trigger to unbreak the model +// target = targets to trigger when broken +// health = amount of damage it can take +// spawnflags: +// 1 = start disabled (needs to be triggered to activate) +// 2 = indicate damage +// notes: +// for mdl_dead to work, origin must be set (using a common/origin brush). +// Otherwise mdl_dead will be displayed at the map origin, and nobody would +// want that! + +void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); + +// +// func_breakable +// - basically func_assault_destructible for general gameplay use +// +void LaunchDebris (string debrisname, vector force) +{ + entity dbr = spawn(); + setorigin(dbr, self.absmin + + '1 0 0' * random() * (self.absmax.x - self.absmin.x) + + '0 1 0' * random() * (self.absmax.y - self.absmin.y) + + '0 0 1' * random() * (self.absmax.z - self.absmin.z)); + setmodel (dbr, debrisname ); + dbr.skin = self.debrisskin; + dbr.colormap = self.colormap; // inherit team colors + dbr.owner = self; // do not be affected by our own explosion + dbr.movetype = self.debrismovetype; + dbr.solid = self.debrissolid; + if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out + setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it + dbr.velocity_x = self.debrisvelocity.x + self.debrisvelocityjitter.x * crandom(); + dbr.velocity_y = self.debrisvelocity.y + self.debrisvelocityjitter.y * crandom(); + dbr.velocity_z = self.debrisvelocity.z + self.debrisvelocityjitter.z * crandom(); + self.velocity = self.velocity + force * self.debrisdamageforcescale; + dbr.avelocity_x = random()*self.debrisavelocityjitter.x; + dbr.avelocity_y = random()*self.debrisavelocityjitter.y; + dbr.avelocity_z = random()*self.debrisavelocityjitter.z; + dbr.damageforcescale = self.debrisdamageforcescale; + if(dbr.damageforcescale) + dbr.takedamage = DAMAGE_YES; + SUB_SetFade(dbr, time + self.debristime + crandom() * self.debristimejitter, self.debrisfadetime); +} + +void func_breakable_colormod() +{ + float h; + if (!(self.spawnflags & 2)) + return; + h = self.health / self.max_health; + if(h < 0.25) + self.colormod = '1 0 0'; + else if(h <= 0.75) + self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5); + else + self.colormod = '1 1 1'; + + CSQCMODEL_AUTOUPDATE(); +} + +void func_breakable_look_destroyed() +{ + float floorZ; + + if(self.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first + self.dropped_origin = self.origin; + + if(self.mdl_dead == "") + self.effects |= EF_NODRAW; + else { + if (self.origin == '0 0 0') { // probably no origin brush, so don't spawn in the middle of the map.. + floorZ = self.absmin.z; + setorigin(self,((self.absmax+self.absmin)*.5)); + self.origin_z = floorZ; + } + setmodel(self, self.mdl_dead); + self.effects &= ~EF_NODRAW; + } + + CSQCMODEL_AUTOUPDATE(); + + self.solid = SOLID_NOT; +} + +void func_breakable_look_restore() +{ + setmodel(self, self.mdl); + self.effects &= ~EF_NODRAW; + + if(self.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow + setorigin(self, self.dropped_origin); + + CSQCMODEL_AUTOUPDATE(); + + self.solid = SOLID_BSP; +} + +void func_breakable_behave_destroyed() +{ + self.health = self.max_health; + self.takedamage = DAMAGE_NO; + self.bot_attack = false; + self.event_damage = func_null; + self.state = 1; + func_breakable_colormod(); + if (self.noise1) + stopsound (self, CH_TRIGGER_SINGLE); +} + +void func_breakable_behave_restore() +{ + self.health = self.max_health; + if(self.sprite) + { + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + } + self.takedamage = DAMAGE_AIM; + self.bot_attack = true; + self.event_damage = func_breakable_damage; + self.state = 0; + self.nextthink = 0; // cancel auto respawn + func_breakable_colormod(); + if (self.noise1) + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); +} + +void func_breakable_init_for_player(entity player) +{ + if (self.noise1 && self.state == 0 && clienttype(player) == CLIENTTYPE_REAL) + { + msg_entity = player; + soundto (MSG_ONE, self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + } +} + +void func_breakable_destroyed() +{ + func_breakable_look_destroyed(); + func_breakable_behave_destroyed(); + + CSQCMODEL_AUTOUPDATE(); +} + +void func_breakable_restore() +{ + func_breakable_look_restore(); + func_breakable_behave_restore(); + + CSQCMODEL_AUTOUPDATE(); +} + +vector debrisforce; // global, set before calling this +void func_breakable_destroy() { + float n, i; + string oldmsg; + + activator = self.owner; + self.owner = world; // set by W_PrepareExplosionByDamage + + // now throw around the debris + n = tokenize_console(self.debris); + for(i = 0; i < n; ++i) + LaunchDebris(argv(i), debrisforce); + + func_breakable_destroyed(); + + if(self.noise) + sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM); + + if(self.dmg) + RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world); + + if(self.cnt) + pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count); + + if(self.respawntime) + { + self.think = func_breakable_restore; + self.nextthink = time + self.respawntime + crandom() * self.respawntimejitter; + } + + oldmsg = self.message; + self.message = ""; + SUB_UseTargets(); + self.message = oldmsg; +} + +void func_breakable_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + if(self.state == 1) + return; + if(self.spawnflags & DOOR_NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + if(self.team) + if(attacker.team == self.team) + return; + self.health = self.health - damage; + if(self.sprite) + { + WaypointSprite_Ping(self.sprite); + WaypointSprite_UpdateHealth(self.sprite, self.health); + } + func_breakable_colormod(); + + if(self.health <= 0) + { + debrisforce = force; + W_PrepareExplosionByDamage(attacker, func_breakable_destroy); + } +} + +void func_breakable_reset() +{ + self.team = self.team_saved; + func_breakable_look_restore(); + if(self.spawnflags & 1) + func_breakable_behave_destroyed(); + else + func_breakable_behave_restore(); + + CSQCMODEL_AUTOUPDATE(); +} + +// destructible walls that can be used to trigger target_objective_decrease +void spawnfunc_func_breakable() +{ + float n, i; + if(!self.health) + self.health = 100; + self.max_health = self.health; + + // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway + if(!self.debrismovetype) self.debrismovetype = MOVETYPE_BOUNCE; + if(!self.debrissolid) self.debrissolid = SOLID_NOT; + if(self.debrisvelocity == '0 0 0') self.debrisvelocity = '0 0 140'; + if(self.debrisvelocityjitter == '0 0 0') self.debrisvelocityjitter = '70 70 70'; + if(self.debrisavelocityjitter == '0 0 0') self.debrisavelocityjitter = '600 600 600'; + if(!self.debristime) self.debristime = 3.5; + if(!self.debristimejitter) self.debristime = 2.5; + + if(self.mdl != "") + self.cnt = particleeffectnum(self.mdl); + if(self.count == 0) + self.count = 1; + + if(self.message == "") + self.message = "got too close to an explosion"; + if(self.message2 == "") + self.message2 = "was pushed into an explosion by"; + if(!self.dmg_radius) + self.dmg_radius = 150; + if(!self.dmg_force) + self.dmg_force = 200; + + self.mdl = self.model; + SetBrushEntityModel(); + + self.use = func_breakable_restore; + + // precache all the models + if (self.mdl_dead) + precache_model(self.mdl_dead); + n = tokenize_console(self.debris); + for(i = 0; i < n; ++i) + precache_model(argv(i)); + if(self.noise) + precache_sound(self.noise); + if(self.noise1) + precache_sound(self.noise1); + + self.team_saved = self.team; + self.dropped_origin = self.origin; + + self.reset = func_breakable_reset; + func_breakable_reset(); + + self.init_for_player_needed = 1; + self.init_for_player = func_breakable_init_for_player; + + CSQCMODEL_AUTOINIT(); +} + +// for use in maps with a "model" key set +void spawnfunc_misc_breakablemodel() { + spawnfunc_func_breakable(); +} +#endif diff --cc qcsrc/common/triggers/func/pointparticles.qc index 773ce6a51c,0000000000..9b32371dbe mode 100644,000000..100644 --- a/qcsrc/common/triggers/func/pointparticles.qc +++ b/qcsrc/common/triggers/func/pointparticles.qc @@@ -1,362 -1,0 +1,362 @@@ +#ifdef CSQC + #include "../../../client/particles.qh" +#endif + +#ifdef SVQC +// NOTE: also contains func_sparks + +float pointparticles_SendEntity(entity to, float fl) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES); + + // optional features to save space + fl = fl & 0x0F; + if(self.spawnflags & 2) + fl |= 0x10; // absolute count on toggle-on + if(self.movedir != '0 0 0' || self.velocity != '0 0 0') + fl |= 0x20; // 4 bytes - saves CPU + if(self.waterlevel || self.count != 1) + fl |= 0x40; // 4 bytes - obscure features almost never used + if(self.mins != '0 0 0' || self.maxs != '0 0 0') + fl |= 0x80; // 14 bytes - saves lots of space + + WriteByte(MSG_ENTITY, fl); + if(fl & 2) + { + if(self.state) + WriteCoord(MSG_ENTITY, self.impulse); + else + WriteCoord(MSG_ENTITY, 0); // off + } + if(fl & 4) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + } + if(fl & 1) + { + if(self.model != "null") + { + WriteShort(MSG_ENTITY, self.modelindex); + if(fl & 0x80) + { + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + } + } + else + { + WriteShort(MSG_ENTITY, 0); + if(fl & 0x80) + { + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + } + } + WriteShort(MSG_ENTITY, self.cnt); + if(fl & 0x20) + { + WriteShort(MSG_ENTITY, compressShortVector(self.velocity)); + WriteShort(MSG_ENTITY, compressShortVector(self.movedir)); + } + if(fl & 0x40) + { + WriteShort(MSG_ENTITY, self.waterlevel * 16.0); + WriteByte(MSG_ENTITY, self.count * 16.0); + } + WriteString(MSG_ENTITY, self.noise); + if(self.noise != "") + { + WriteByte(MSG_ENTITY, floor(self.atten * 64)); + WriteByte(MSG_ENTITY, floor(self.volume * 255)); + } + WriteString(MSG_ENTITY, self.bgmscript); + if(self.bgmscript != "") + { + WriteByte(MSG_ENTITY, floor(self.bgmscriptattack * 64)); + WriteByte(MSG_ENTITY, floor(self.bgmscriptdecay * 64)); + WriteByte(MSG_ENTITY, floor(self.bgmscriptsustain * 255)); + WriteByte(MSG_ENTITY, floor(self.bgmscriptrelease * 64)); + } + } + return 1; +} + +void pointparticles_use() +{ + self.state = !self.state; + self.SendFlags |= 2; +} + +void pointparticles_think() +{ + if(self.origin != self.oldorigin) + { + self.SendFlags |= 4; + self.oldorigin = self.origin; + } + self.nextthink = time; +} + +void pointparticles_reset() +{ + if(self.spawnflags & 1) + self.state = 1; + else + self.state = 0; +} + +void spawnfunc_func_pointparticles() +{ + if(self.model != "") + setmodel(self, self.model); + if(self.noise != "") + precache_sound (self.noise); + + if(!self.bgmscriptsustain) + self.bgmscriptsustain = 1; + else if(self.bgmscriptsustain < 0) + self.bgmscriptsustain = 0; + + if(!self.atten) + self.atten = ATTEN_NORM; + else if(self.atten < 0) + self.atten = 0; + if(!self.volume) + self.volume = 1; + if(!self.count) + self.count = 1; + if(!self.impulse) + self.impulse = 1; + + if(!self.modelindex) + { + setorigin(self, self.origin + self.mins); + setsize(self, '0 0 0', self.maxs - self.mins); + } + if(!self.cnt) + self.cnt = particleeffectnum(self.mdl); + + Net_LinkEntity(self, (self.spawnflags & 4), 0, pointparticles_SendEntity); + + IFTARGETED + { + self.use = pointparticles_use; + self.reset = pointparticles_reset; + self.reset(); + } + else + self.state = 1; + self.think = pointparticles_think; + self.nextthink = time; +} + +void spawnfunc_func_sparks() +{ + // self.cnt is the amount of sparks that one burst will spawn + if(self.cnt < 1) { + self.cnt = 25.0; // nice default value + } + + // self.wait is the probability that a sparkthink will spawn a spark shower + // range: 0 - 1, but 0 makes little sense, so... + if(self.wait < 0.05) { + self.wait = 0.25; // nice default value + } + + self.count = self.cnt; + self.mins = '0 0 0'; + self.maxs = '0 0 0'; + self.velocity = '0 0 -1'; + self.mdl = "TE_SPARK"; + self.impulse = 10 * self.wait; // by default 2.5/sec + self.wait = 0; + self.cnt = 0; // use mdl + + spawnfunc_func_pointparticles(); +} +#elif defined(CSQC) + +void Draw_PointParticles() +{ + float n, i, fail; + vector p; + vector sz; + vector o; + o = self.origin; + sz = self.maxs - self.mins; - n = BGMScript(self); ++ n = doBGMScript(self); + if(self.absolute == 2) + { + if(n >= 0) + n = self.just_toggled ? self.impulse : 0; + else + n = self.impulse * drawframetime; + } + else + { + n *= self.impulse * drawframetime; + if(self.just_toggled) + if(n < 1) + n = 1; + } + if(n == 0) + return; + fail = 0; + for(i = random(); i <= n && fail <= 64*n; ++i) + { + p = o + self.mins; + p.x += random() * sz.x; + p.y += random() * sz.y; + p.z += random() * sz.z; + if(WarpZoneLib_BoxTouchesBrush(p, p, self, world)) + { + if(self.movedir != '0 0 0') + { + traceline(p, p + normalize(self.movedir) * 4096, 0, world); + p = trace_endpos; + pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count); + } + else + { + pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count); + } + if(self.noise != "") + { + setorigin(self, p); + sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten); + } + self.just_toggled = 0; + } + else if(self.absolute) + { + ++fail; + --i; + } + } + setorigin(self, o); +} + +void Ent_PointParticles_Remove() +{ + if(self.noise) + strunzone(self.noise); + self.noise = string_null; + if(self.bgmscript) + strunzone(self.bgmscript); + self.bgmscript = string_null; +} + +void Ent_PointParticles() +{ + float i; + vector v; + int f = ReadByte(); + if(f & 2) + { + i = ReadCoord(); // density (<0: point, >0: volume) + if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed + self.just_toggled = 1; + self.impulse = i; + } + if(f & 4) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + } + if(f & 1) + { + self.modelindex = ReadShort(); + if(f & 0x80) + { + if(self.modelindex) + { + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + } + else + { + self.mins = '0 0 0'; + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + } + } + else + { + self.mins = self.maxs = '0 0 0'; + } + + self.cnt = ReadShort(); // effect number + + if(f & 0x20) + { + self.velocity = decompressShortVector(ReadShort()); + self.movedir = decompressShortVector(ReadShort()); + } + else + { + self.velocity = self.movedir = '0 0 0'; + } + if(f & 0x40) + { + self.waterlevel = ReadShort() / 16.0; + self.count = ReadByte() / 16.0; + } + else + { + self.waterlevel = 0; + self.count = 1; + } + if(self.noise) + strunzone(self.noise); + if(self.bgmscript) + strunzone(self.bgmscript); + self.noise = strzone(ReadString()); + if(self.noise != "") + { + self.atten = ReadByte() / 64.0; + self.volume = ReadByte() / 255.0; + } + self.bgmscript = strzone(ReadString()); + if(self.bgmscript != "") + { + self.bgmscriptattack = ReadByte() / 64.0; + self.bgmscriptdecay = ReadByte() / 64.0; + self.bgmscriptsustain = ReadByte() / 255.0; + self.bgmscriptrelease = ReadByte() / 64.0; + } + BGMScript_InitEntity(self); + } + + if(f & 2) + { + self.absolute = (self.impulse >= 0); + if(!self.absolute) + { + v = self.maxs - self.mins; + self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube + } + } + + if(f & 0x10) + self.absolute = 2; + + setorigin(self, self.origin); + setsize(self, self.mins, self.maxs); + self.solid = SOLID_NOT; + self.draw = Draw_PointParticles; + self.entremove = Ent_PointParticles_Remove; +} +#endif diff --cc qcsrc/common/triggers/target/music.qc index 136288bdc5,0000000000..a0763390be mode 100644,000000..100644 --- a/qcsrc/common/triggers/target/music.qc +++ b/qcsrc/common/triggers/target/music.qc @@@ -1,330 -1,0 +1,330 @@@ +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include "../../../dpdefs/progsdefs.qh" + #include "../../../dpdefs/dpextensions.qh" - #include "../../constants.qh" ++ #include "../../../server/_all.qh" + #include "../../../server/constants.qh" + #include "../../../server/defs.qh" +#endif + +#ifdef SVQC + +// values: +// volume +// noise +// targetname +// lifetime +// fade_time +// fade_rate +// when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0) +// when targetname is not set, THIS ONE is default +void target_music_sendto(float to, float is) +{ + WriteByte(to, SVC_TEMPENTITY); + WriteByte(to, TE_CSQC_TARGET_MUSIC); + WriteShort(to, num_for_edict(self)); + WriteByte(to, self.volume * 255.0 * is); + WriteByte(to, self.fade_time * 16.0); + WriteByte(to, self.fade_rate * 16.0); + WriteByte(to, self.lifetime); + WriteString(to, self.noise); +} +void target_music_reset() +{ + if(self.targetname == "") + target_music_sendto(MSG_ALL, 1); +} +void target_music_use() +{ + if(!activator) + return; + if(IS_REAL_CLIENT(activator)) + { + msg_entity = activator; + target_music_sendto(MSG_ONE, 1); + } + entity head; + FOR_EACH_SPEC(head) if(head.enemy == activator) { msg_entity = head; target_music_sendto(MSG_ONE, 1); } +} +void spawnfunc_target_music() +{ + self.use = target_music_use; + self.reset = target_music_reset; + if(!self.volume) + self.volume = 1; + if(self.targetname == "") + target_music_sendto(MSG_INIT, 1); + else + target_music_sendto(MSG_INIT, 0); +} +void TargetMusic_RestoreGame() +{ + for(self = world; (self = find(self, classname, "target_music")); ) + { + if(self.targetname == "") + target_music_sendto(MSG_INIT, 1); + else + target_music_sendto(MSG_INIT, 0); + } +} +// values: +// volume +// noise +// targetname +// fade_time +// spawnflags: +// 1 = START_OFF +// when triggered, it is disabled/enabled for everyone +float trigger_music_SendEntity(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC); + sf &= ~0x80; + if(self.cnt) + sf |= 0x80; + WriteByte(MSG_ENTITY, sf); + if(sf & 4) + { + WriteCoord(MSG_ENTITY, self.origin.x); + WriteCoord(MSG_ENTITY, self.origin.y); + WriteCoord(MSG_ENTITY, self.origin.z); + } + if(sf & 1) + { + if(self.model != "null") + { + WriteShort(MSG_ENTITY, self.modelindex); + WriteCoord(MSG_ENTITY, self.mins.x); + WriteCoord(MSG_ENTITY, self.mins.y); + WriteCoord(MSG_ENTITY, self.mins.z); + WriteCoord(MSG_ENTITY, self.maxs.x); + WriteCoord(MSG_ENTITY, self.maxs.y); + WriteCoord(MSG_ENTITY, self.maxs.z); + } + else + { + WriteShort(MSG_ENTITY, 0); + WriteCoord(MSG_ENTITY, self.maxs.x); + WriteCoord(MSG_ENTITY, self.maxs.y); + WriteCoord(MSG_ENTITY, self.maxs.z); + } + WriteByte(MSG_ENTITY, self.volume * 255.0); + WriteByte(MSG_ENTITY, self.fade_time * 16.0); + WriteByte(MSG_ENTITY, self.fade_rate * 16.0); + WriteString(MSG_ENTITY, self.noise); + } + return 1; +} +void trigger_music_reset() +{ + self.cnt = !(self.spawnflags & 1); + self.SendFlags |= 0x80; +} +void trigger_music_use() +{ + self.cnt = !self.cnt; + self.SendFlags |= 0x80; +} +void spawnfunc_trigger_music() +{ + if(self.model != "") + setmodel(self, self.model); + if(!self.volume) + self.volume = 1; + if(!self.modelindex) + { + setorigin(self, self.origin + self.mins); + setsize(self, '0 0 0', self.maxs - self.mins); + } + trigger_music_reset(); + + self.use = trigger_music_use; + self.reset = trigger_music_reset; + + Net_LinkEntity(self, false, 0, trigger_music_SendEntity); +} +#elif defined(CSQC) + +void TargetMusic_Advance() +{ + // run AFTER all the thinks! + entity best, e; + float vol, vol0; + best = music_default; + if(music_target && time < music_target.lifetime) + best = music_target; + if(music_trigger) + best = music_trigger; + for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); ) if(e.noise) + { + vol0 = e.lastvol; + if(getsoundtime(e, CH_BGM_SINGLE) < 0) + { + vol0 = -1; + } + if(e == best) + { + // increase volume + if(e.fade_time > 0) + e.state = bound(0, e.state + frametime / e.fade_time, 1); + else + e.state = 1; + } + else + { + // decrease volume + if(e.fade_rate > 0) + e.state = bound(0, e.state - frametime / e.fade_rate, 1); + else + e.state = 0; + } + vol = e.state * e.volume * autocvar_bgmvolume; + if(vol != vol0) + { + if(vol0 < 0) + sound(e, CH_BGM_SINGLE, e.noise, vol, ATTEN_NONE); // restart + else + sound(e, CH_BGM_SINGLE, "", vol, ATTEN_NONE); + e.lastvol = vol; + } + } + music_trigger = world; + + if(best) + bgmtime = getsoundtime(best, CH_BGM_SINGLE); + else + bgmtime = gettime(GETTIME_CDTRACK); +} + +void Net_TargetMusic() +{ + int id = ReadShort(); + float vol = ReadByte() / 255.0; + float fai = ReadByte() / 16.0; + float fao = ReadByte() / 16.0; + float tim = ReadByte(); + string noi = ReadString(); + + entity e; + for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); ) + { + if(e.count == id) + break; + } + if(!e) + { + e = spawn(); + e.enttype = ENT_CLIENT_TRIGGER_MUSIC; + e.count = id; + } + if(e.noise != noi) + { + if(e.noise) + strunzone(e.noise); + e.noise = strzone(noi); + precache_sound(e.noise); + sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE); + if(getsoundtime(e, CH_BGM_SINGLE) < 0) + { + dprintf("Cannot initialize sound %s\n", e.noise); + strunzone(e.noise); + e.noise = string_null; + } + } + 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() +{ + if(WarpZoneLib_BoxTouchesBrush(view_origin, view_origin, self, world)) + { + music_trigger = self; + } + self.nextthink = time; +} + +void Ent_TriggerMusic_Remove() +{ + if(self.noise) + strunzone(self.noise); + self.noise = string_null; +} + +void Ent_ReadTriggerMusic() +{ + int f = ReadByte(); + if(f & 4) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + } + if(f & 1) + { + self.modelindex = ReadShort(); + if(self.modelindex) + { + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + } + else + { + self.mins = '0 0 0'; + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + } + + self.volume = ReadByte() / 255.0; + self.fade_time = ReadByte() / 16.0; + self.fade_rate = ReadByte() / 16.0; + string s = self.noise; + if(self.noise) + strunzone(self.noise); + self.noise = strzone(ReadString()); + if(self.noise != s) + { + precache_sound(self.noise); + sound(self, CH_BGM_SINGLE, self.noise, 0, ATTEN_NONE); + if(getsoundtime(self, CH_BGM_SINGLE) < 0) + { + dprintf("Cannot initialize sound %s\n", self.noise); + strunzone(self.noise); + self.noise = string_null; + } + } + } + + setorigin(self, self.origin); + setsize(self, self.mins, self.maxs); + self.cnt = 1; + self.think = Ent_TriggerMusic_Think; + self.nextthink = time; +} + +#endif diff --cc qcsrc/common/triggers/target/music.qh index 8d014c819c,0000000000..712d412f6a mode 100644,000000..100644 --- a/qcsrc/common/triggers/target/music.qh +++ b/qcsrc/common/triggers/target/music.qh @@@ -1,27 -1,0 +1,28 @@@ +#ifndef TARGET_MUSIC_H +#define TARGET_MUSIC_H + +.float lifetime; + +#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. + - .int state; - .float lastvol; ++entityclass(TargetMusic); ++class(TargetMusic) .int state; ++class(TargetMusic) .float lastvol; + +void TargetMusic_Advance(); + +void Net_TargetMusic(); + +void Ent_TriggerMusic_Think(); + +void Ent_TriggerMusic_Remove(); + +void Ent_ReadTriggerMusic(); +#endif + +#endif diff --cc qcsrc/common/triggers/target/spawn.qc index 7b183553da,0000000000..365a9e79d8 mode 100644,000000..100644 --- a/qcsrc/common/triggers/target/spawn.qc +++ b/qcsrc/common/triggers/target/spawn.qc @@@ -1,350 -1,0 +1,350 @@@ +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include "../../../dpdefs/progsdefs.qh" + #include "../../../dpdefs/dpextensions.qh" + #include "../../util.qh" - #include "../../../server/defs.qh" ++ #include "../../../server/_all.qh" +#endif + +#ifdef SVQC + +// spawner entity +// "classname" "target_spawn" +// "message" "fieldname value fieldname value ..." +// "spawnflags" +// 1 = call the spawn function +// 2 = trigger on map load + +float target_spawn_initialized; +.void() target_spawn_spawnfunc; +float target_spawn_spawnfunc_field; +.entity target_spawn_activator; +.float target_spawn_id; +float target_spawn_count; + +void target_spawn_helper_setmodel() +{ + setmodel(self, self.model); +} + +void target_spawn_helper_setsize() +{ + setsize(self, self.mins, self.maxs); +} + +void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act) +{ + float i, n, valuefieldpos; + string key, value, valuefield, valueoffset, valueoffsetrandom; + entity valueent; + vector data, data2; + entity oldself; + entity oldactivator; + + n = tokenize_console(msg); + + for(i = 0; i < n-1; i += 2) + { + key = argv(i); + value = argv(i+1); + if(key == "$") + { + data.x = -1; + data.y = FIELD_STRING; + } + else + { + data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key))); + if(data.y == 0) // undefined field, i.e., invalid type + { + print("target_spawn: invalid/unknown entity key ", key, " specified, ignored!\n"); + continue; + } + } + if(substring(value, 0, 1) == "$") + { + value = substring(value, 1, strlen(value) - 1); + if(substring(value, 0, 1) == "$") + { + // deferred replacement + // do nothing + // useful for creating target_spawns with this! + } + else + { + // replace me! + valuefieldpos = strstrofs(value, "+", 0); + valueoffset = ""; + if(valuefieldpos != -1) + { + valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1); + value = substring(value, 0, valuefieldpos); + } + + valuefieldpos = strstrofs(valueoffset, "+", 0); + valueoffsetrandom = ""; + if(valuefieldpos != -1) + { + valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1); + valueoffset = substring(valueoffset, 0, valuefieldpos); + } + + valuefieldpos = strstrofs(value, ".", 0); + valuefield = ""; + if(valuefieldpos != -1) + { + valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1); + value = substring(value, 0, valuefieldpos); + } + + if(value == "self") + { + valueent = self; + value = ""; + } + else if(value == "activator") + { + valueent = act; + value = ""; + } + else if(value == "other") + { + valueent = other; + value = ""; + } + else if(value == "pusher") + { + if(time < act.pushltime) + valueent = act.pusher; + else + valueent = world; + value = ""; + } + else if(value == "target") + { + valueent = e; + value = ""; + } + else if(value == "killtarget") + { + valueent = kt; + value = ""; + } + else if(value == "target2") + { + valueent = t2; + value = ""; + } + else if(value == "target3") + { + valueent = t3; + value = ""; + } + else if(value == "target4") + { + valueent = t4; + value = ""; + } + else if(value == "time") + { + valueent = world; + value = ftos(time); + } + else + { + print("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!\n"); + continue; + } + + if(valuefield == "") + { + if(value == "") + value = ftos(num_for_edict(valueent)); + } + else + { + if(value != "") + { + print("target_spawn: try to get a field of a non-entity, ignored!\n"); + continue; + } + data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield))); + if(data2_y == 0) // undefined field, i.e., invalid type + { + print("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!\n"); + continue; + } + value = getentityfieldstring(data2_x, valueent); + } + + if(valueoffset != "") + { + switch(data.y) + { + case FIELD_STRING: + value = strcat(value, valueoffset); + break; + case FIELD_FLOAT: + value = ftos(stof(value) + stof(valueoffset)); + break; + case FIELD_VECTOR: + value = vtos(stov(value) + stov(valueoffset)); + break; + default: + print("target_spawn: only string, float and vector fields can do calculations, calculation ignored!\n"); + break; + } + } + + if(valueoffsetrandom != "") + { + switch(data.y) + { + case FIELD_FLOAT: + value = ftos(stof(value) + random() * stof(valueoffsetrandom)); + break; + case FIELD_VECTOR: + data2 = stov(valueoffsetrandom); + value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1'); + break; + default: + print("target_spawn: only float and vector fields can do random calculations, calculation ignored!\n"); + break; + } + } + } + } + if(key == "$") + { + if(substring(value, 0, 1) == "_") + value = strcat("target_spawn_helper", value); + putentityfieldstring(target_spawn_spawnfunc_field, e, value); + + oldself = self; + oldactivator = activator; + + self = e; + activator = act; + + self.target_spawn_spawnfunc(); + + self = oldself; + activator = oldactivator; + + // 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) +{ + self.target_spawn_activator = activator; + target_spawn_edit_entity( + e, + self.message, + find(world, targetname, self.killtarget), + find(world, targetname, self.target2), + find(world, targetname, self.target3), + find(world, targetname, self.target4), + activator + ); +} + +float target_spawn_cancreate() +{ + float c; + entity e; + + c = self.count; + if(c == 0) // no limit? + return 1; + + ++c; // increase count to not include MYSELF + for(e = world; (e = findfloat(e, target_spawn_id, self.target_spawn_id)); --c) + ; + + // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more + if(c == 0) + return 0; + return 1; +} + +void target_spawn_use() +{ + entity e; + + if(self.target == "") + { + // spawn new entity + if(!target_spawn_cancreate()) + return; + e = spawn(); + target_spawn_useon(e); + e.target_spawn_id = self.target_spawn_id; + } + else if(self.target == "*activator") + { + // edit entity + if(activator) + target_spawn_useon(activator); + } + else + { + // edit entity + for(e = world; (e = find(e, targetname, self.target)); ) + target_spawn_useon(e); + } +} + +void target_spawn_spawnfirst() +{ + activator = self.target_spawn_activator; + if(self.spawnflags & 2) + target_spawn_use(); +} + +void initialize_field_db() +{ + if(!target_spawn_initialized) + { + float n, i; + string fn; - vector prev, new; ++ vector prev, next; + float ft; + + n = numentityfields(); + for(i = 0; i < n; ++i) + { + fn = entityfieldname(i); + ft = entityfieldtype(i); - new = i * '1 0 0' + ft * '0 1 0' + '0 0 1'; ++ 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(new)); ++ db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(next)); + if(fn == "target_spawn_spawnfunc") + target_spawn_spawnfunc_field = i; + } + } + + target_spawn_initialized = 1; + } +} + +void spawnfunc_target_spawn() +{ + initialize_field_db(); + self.use = target_spawn_use; + self.message = strzone(strreplace("'", "\"", self.message)); + self.target_spawn_id = ++target_spawn_count; + InitializeEntity(self, target_spawn_spawnfirst, INITPRIO_LAST); +} +#endif diff --cc qcsrc/common/triggers/teleporters.qc index 5e91d76073,0000000000..ca887c63bb mode 100644,000000..100644 --- a/qcsrc/common/triggers/teleporters.qc +++ b/qcsrc/common/triggers/teleporters.qc @@@ -1,252 -1,0 +1,253 @@@ +#include "teleporters.qh" + +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include "../../warpzonelib/common.qh" + #include "../../warpzonelib/util_server.qh" + #include "../../warpzonelib/server.qh" + #include "../constants.qh" + #include "../triggers/subs.qh" + #include "../util.qh" ++ #include "../../server/_all.qh" + #include "../../server/weapons/csqcprojectile.qh" + #include "../../server/autocvars.qh" + #include "../../server/constants.qh" + #include "../../server/defs.qh" + #include "../deathtypes.qh" + #include "../../server/tturrets/include/turrets_early.qh" - #include "../../server/vehicles/vehicles_def.qh" ++ #include "../../server/vehicles/all.qh" + #include "../mapinfo.qh" + #include "../../server/anticheat.qh" +#endif + +#ifdef SVQC + +float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax) +{ + if (IS_PLAYER(player) && player.health >= 1) + { + TDEATHLOOP(org) + { + if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) + if(IS_PLAYER(head)) + if(head.health >= 1) + return 1; + } + } + return 0; +} + +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, head.origin, '0 0 0'); + } + } + else // dead bodies and monsters gib themselves instead of telefragging + Damage (telefragger, teleporter, telefragger, 10000, DEATH_TELEFRAG, telefragger.origin, '0 0 0'); + } +} + +void spawn_tdeath(vector v0, entity e, vector v) +{ + tdeath(e, e, e, '0 0 0', '0 0 0'); +} + +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); + + 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(self.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps + { + if(tflags & TELEPORT_FLAG_SOUND) + sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM); + if(tflags & TELEPORT_FLAG_PARTICLES) + { + pointparticles(particleeffectnum("teleport"), player.origin, '0 0 0', 1); + pointparticles(particleeffectnum("teleport"), to + v_forward * 32, '0 0 0', 1); + } + self.pushltime = time + 0.2; + } + } + + // Relocate the player + // assuming to allows PL_MIN to PL_MAX box and some more + 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); + + if(IS_PLAYER(player)) + { + if(tflags & TELEPORT_FLAG_TDEATH) + if(player.takedamage && player.deadflag == DEAD_NO && !g_race && !g_cts && (autocvar_g_telefrags || (tflags & TELEPORT_FLAG_FORCE_TDEATH))) + tdeath(player, teleporter, telefragger, telefragmin, telefragmax); + + // player no longer is on ground + player.flags &= ~FL_ONGROUND; + + // 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 = player.BUTTON_CHAT; + } + else + { + player.pushltime = 0; + player.istypefrag = 0; + } + + player.lastteleporttime = time; + } +} + +entity Simple_TeleportPlayer(entity teleporter, entity player) +{ + vector locout; + entity e; + float p; + + // Find the output teleporter + if(teleporter.enemy) + { + e = teleporter.enemy; + } + else + { + RandomSelection_Init(); + for(e = world; (e = find(e, targetname, teleporter.target)); ) + { + p = 1; + if(autocvar_g_telefrags_avoid) + { + locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); + if(check_tdeath(player, locout, '0 0 0', '0 0 0')) + p = 0; + } + RandomSelection_Add(e, 0, string_null, (e.cnt ? e.cnt : 1), p); + } + e = RandomSelection_chosen_ent; + } + + if(!e) { sprint(player, "Teleport destination vanished. Sorry... please complain to the mapper.\n"); } + + makevectors(e.mangle); + + if(e.speed) + if(vlen(player.velocity) > e.speed) + player.velocity = normalize(player.velocity) * max(0, e.speed); + + if(autocvar_g_teleport_maxspeed) + if(vlen(player.velocity) > autocvar_g_teleport_maxspeed) + player.velocity = normalize(player.velocity) * max(0, autocvar_g_teleport_maxspeed); + + 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 (void) +{ + entity e; + float n; + + n = 0; + for(e = world; (e = find(e, targetname, self.target)); ) + { + ++n; + if(e.movetype == MOVETYPE_NONE) + waypoint_spawnforteleporter(self, e.origin, 0); + if(e.classname != "info_teleport_destination") + print("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work.\n"); + } + + if(n == 0) + { + // no dest! + objerror ("Teleporter with nonexistant target"); + return; + } + else if(n == 1) + { + // exactly one dest - bots love that + self.enemy = find(e, targetname, self.target); + } + else + { + // have to use random selection every single time + self.enemy = world; + } + + // now enable touch + self.touch = Teleport_Touch; +} + +entity Teleport_Find(vector mi, vector ma) +{ + entity e; + for(e = world; (e = find(e, classname, "trigger_teleport")); ) + if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, world)) + return e; + return world; +} + +void WarpZone_PostTeleportPlayer_Callback(entity pl) +{ + makevectors(pl.angles); + Reset_ArcBeam(pl, v_forward); + UpdateCSQCProjectileAfterTeleport(pl); + { + entity oldself = self; + self = pl; + anticheat_fixangle(); + self = oldself; + } + // "disown" projectiles after teleport + if(pl.owner) + if(pl.owner == pl.realowner) + { + if(!(pl.flags & FL_PROJECTILE)) + print("A non-projectile got through a warpzone and its owner cleared. It's a ", pl.classname, ".\n"); + pl.owner = world; + } + if(IS_PLAYER(pl)) + { + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + pl.oldvelocity = pl.velocity; + // reset teleport time tracking too (or multijump can cause insane speeds) + pl.lastteleporttime = time; + } +} +#endif diff --cc qcsrc/common/triggers/trigger/jumppads.qc index 9ad0e720a6,0000000000..e56f3c256e mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/jumppads.qc +++ b/qcsrc/common/triggers/trigger/jumppads.qc @@@ -1,474 -1,0 +1,475 @@@ +// TODO: split target_push and put it in the target folder +#ifdef SVQC +#include "jumppads.qh" +#include "../../movetypes/movetypes.qh" ++#include "../../../server/_all.qh" + +void trigger_push_use() +{ + if(teamplay) + { + self.team = activator.team; + self.SendFlags |= 2; + } +} +#endif + +/* + trigger_push_calculatevelocity + + Arguments: + org - origin of the object which is to be pushed + tgt - target entity (can be either a point or a model entity; if it is + the latter, its midpoint is used) + ht - jump height, measured from the higher one of org and tgt's midpoint + + Returns: velocity for the jump + the global trigger_push_calculatevelocity_flighttime is set to the total + jump time + */ + +vector trigger_push_calculatevelocity(vector org, entity tgt, float ht) +{ + float grav, sdist, zdist, vs, vz, jumpheight; + vector sdir, torg; + + torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5; + + grav = PHYS_GRAVITY; + if(PHYS_ENTGRAVITY(other)) + grav *= PHYS_ENTGRAVITY(other); + + zdist = torg.z - org.z; + sdist = vlen(torg - org - zdist * '0 0 1'); + sdir = normalize(torg - org - zdist * '0 0 1'); + + // how high do we need to push the player? + jumpheight = fabs(ht); + if(zdist > 0) + jumpheight = jumpheight + zdist; + + /* + STOP. + + You will not understand the following equations anyway... + But here is what I did to get them. + + I used the functions + + s(t) = t * vs + z(t) = t * vz - 1/2 grav t^2 + + and solved for: + + s(ti) = sdist + z(ti) = zdist + max(z, ti) = jumpheight + + From these three equations, you will find the three parameters vs, vz + and ti. + */ + + // push him so high... + vz = sqrt(fabs(2 * grav * jumpheight)); // NOTE: sqrt(positive)! + + // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump! + if(ht < 0) + if(zdist < 0) + vz = -vz; + + vector solution; + solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist" + // ALWAYS solvable because jumpheight >= zdist + if(!solution.z) + solution_y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0) + if(zdist == 0) + solution_x = solution.y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually) + + if(zdist < 0) + { + // down-jump + if(ht < 0) + { + // almost straight line type + // jump apex is before the jump + // we must take the larger one + trigger_push_calculatevelocity_flighttime = solution.y; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one too + trigger_push_calculatevelocity_flighttime = solution.y; + } + } + else + { + // up-jump + if(ht < 0) + { + // almost straight line type + // jump apex is after the jump + // we must take the smaller one + trigger_push_calculatevelocity_flighttime = solution.x; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one + trigger_push_calculatevelocity_flighttime = solution.y; + } + } + vs = sdist / trigger_push_calculatevelocity_flighttime; + + // finally calculate the velocity + return sdir * vs + '0 0 1' * vz; +} + +void trigger_push_touch() +{ + if (self.active == ACTIVE_NOT) + return; + +#ifdef SVQC + if (!isPushable(other)) + return; +#endif + + if(self.team) + if(((self.spawnflags & 4) == 0) == (DIFF_TEAM(self, other))) + return; + + EXACTTRIGGER_TOUCH; + + if(self.enemy) + { + other.velocity = trigger_push_calculatevelocity(other.origin, self.enemy, self.height); + other.move_velocity = other.velocity; + } + else if(self.target) + { + entity e; + RandomSelection_Init(); + for(e = world; (e = find(e, targetname, self.target)); ) + { + if(e.cnt) + RandomSelection_Add(e, 0, string_null, e.cnt, 1); + else + RandomSelection_Add(e, 0, string_null, 1, 1); + } + other.velocity = trigger_push_calculatevelocity(other.origin, RandomSelection_chosen_ent, self.height); + other.move_velocity = other.velocity; + } + else + { + other.velocity = self.movedir; + other.move_velocity = other.velocity; + } + + UNSET_ONGROUND(other); + + other.move_flags &= ~FL_ONGROUND; + +#ifdef SVQC + if (IS_PLAYER(other)) + { + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + other.oldvelocity = other.velocity; + + if(self.pushltime < time) // prevent "snorring" sound when a player hits the jumppad more than once + { + // flash when activated + pointparticles(particleeffectnum("jumppad_activate"), other.origin, other.velocity, 1); + sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM); + self.pushltime = time + 0.2; + } + if(IS_REAL_CLIENT(other) || IS_BOT_CLIENT(other)) + { + bool found = false; + for(int i = 0; i < other.jumppadcount && i < NUM_JUMPPADSUSED; ++i) + if(other.(jumppadsused[i]) == self) + found = true; + if(!found) + { + other.(jumppadsused[other.jumppadcount % NUM_JUMPPADSUSED]) = self; + other.jumppadcount = other.jumppadcount + 1; + } + + if(IS_REAL_CLIENT(other)) + { + if(self.message) + centerprint(other, self.message); + } + else + other.lastteleporttime = time; + + if (other.deadflag == DEAD_NO) + animdecide_setaction(other, ANIMACTION_JUMP, true); + } + else + other.jumppadcount = true; + + // reset tracking of who pushed you into a hazard (for kill credit) + other.pushltime = 0; + other.istypefrag = 0; + } + + if(self.enemy.target) + { + entity oldself; + oldself = self; + activator = other; + self = self.enemy; + SUB_UseTargets(); + self = oldself; + } + + if (other.flags & FL_PROJECTILE) + { + other.angles = vectoangles (other.velocity); + switch(other.movetype) + { + case MOVETYPE_FLY: + other.movetype = MOVETYPE_TOSS; + other.gravity = 1; + break; + case MOVETYPE_BOUNCEMISSILE: + other.movetype = MOVETYPE_BOUNCE; + other.gravity = 1; + break; + } + UpdateCSQCProjectile(other); + } + + if (self.spawnflags & PUSH_ONCE) + { + self.touch = func_null; + self.think = SUB_Remove; + self.nextthink = time; + } +#endif +} + +#ifdef SVQC +void trigger_push_link(); +void trigger_push_updatelink(); +#endif +void trigger_push_findtarget() +{ + entity t; + vector org; + + // first calculate a typical start point for the jump + org = (self.absmin + self.absmax) * 0.5; + org_z = self.absmax.z - PL_MIN.z; + + if (self.target) + { + float n = 0; + for(t = world; (t = find(t, targetname, self.target)); ) + { + ++n; +#ifdef SVQC + entity e = spawn(); + setorigin(e, org); + setsize(e, PL_MIN, PL_MAX); + e.velocity = trigger_push_calculatevelocity(org, t, self.height); + tracetoss(e, e); + if(e.movetype == MOVETYPE_NONE) + waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity)); + remove(e); +#endif + } + + if(!n) + { + // no dest! +#ifdef SVQC + objerror ("Jumppad with nonexistant target"); +#endif + return; + } + else if(n == 1) + { + // exactly one dest - bots love that + self.enemy = find(world, targetname, self.target); + } + else + { + // have to use random selection every single time + self.enemy = world; + } + } +#ifdef SVQC + else + { + entity e = spawn(); + setorigin(e, org); + setsize(e, PL_MIN, PL_MAX); + e.velocity = self.movedir; + tracetoss(e, e); + waypoint_spawnforteleporter(self, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity)); + remove(e); + } + + trigger_push_link(); + defer(0.1, trigger_push_updatelink); +#endif +} + +#ifdef SVQC +float trigger_push_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH); + WriteByte(MSG_ENTITY, sf); + + if(sf & 1) + { + WriteByte(MSG_ENTITY, self.team); + WriteInt24_t(MSG_ENTITY, self.spawnflags); + WriteByte(MSG_ENTITY, self.active); + WriteByte(MSG_ENTITY, self.height); + + trigger_common_write(true); + } + + if(sf & 2) + { + WriteByte(MSG_ENTITY, self.team); + WriteByte(MSG_ENTITY, self.active); + } + + return true; +} + +void trigger_push_updatelink() +{ + self.SendFlags |= 1; +} + +void trigger_push_link() +{ + //Net_LinkEntity(self, false, 0, trigger_push_send); +} +#endif +#ifdef SVQC +/* + * ENTITY PARAMETERS: + * + * target: target of jump + * height: the absolute value is the height of the highest point of the jump + * trajectory above the higher one of the player and the target. + * the sign indicates whether the highest point is INSIDE (positive) + * or OUTSIDE (negative) of the jump trajectory. General rule: use + * positive values for targets mounted on the floor, and use negative + * values to target a point on the ceiling. + * movedir: if target is not set, this * speed * 10 is the velocity to be reached. + */ +void spawnfunc_trigger_push() +{ + SetMovedir (); + + EXACTTRIGGER_INIT; + + self.active = ACTIVE_ACTIVE; + self.use = trigger_push_use; + self.touch = trigger_push_touch; + + // normal push setup + if (!self.speed) + self.speed = 1000; + self.movedir = self.movedir * self.speed * 10; + + if (!self.noise) + self.noise = "misc/jumppad.wav"; + precache_sound (self.noise); + + // this must be called to spawn the teleport waypoints for bots + InitializeEntity(self, trigger_push_findtarget, INITPRIO_FINDTARGET); +} + + +float target_push_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH); + + WriteByte(MSG_ENTITY, self.cnt); + WriteString(MSG_ENTITY, self.targetname); + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + return true; +} + +void target_push_link() +{ + Net_LinkEntity(self, false, 0, target_push_send); + self.SendFlags |= 1; // update +} + +void spawnfunc_target_push() { target_push_link(); } +void spawnfunc_info_notnull() { target_push_link(); } +void spawnfunc_target_position() { target_push_link(); } + +#endif + +#ifdef CSQC + +void ent_trigger_push() +{ + float sf = ReadByte(); + + if(sf & 1) + { + self.classname = "jumppad"; + int mytm = ReadByte(); if(mytm) { self.team = mytm - 1; } + self.spawnflags = ReadInt24_t(); + self.active = ReadByte(); + self.height = ReadByte(); + + trigger_common_read(true); + + self.entremove = trigger_remove_generic; + self.solid = SOLID_TRIGGER; + self.draw = trigger_draw_generic; + self.trigger_touch = trigger_push_touch; + self.drawmask = MASK_NORMAL; + self.move_time = time; + trigger_push_findtarget(); + } + + if(sf & 2) + { + self.team = ReadByte(); + self.active = ReadByte(); + } +} + +void target_push_remove() +{ + if(self.classname) + strunzone(self.classname); + self.classname = string_null; + + if(self.targetname) + strunzone(self.targetname); + self.targetname = string_null; +} + +void ent_target_push() +{ + self.classname = "push_target"; + self.cnt = ReadByte(); + self.targetname = strzone(ReadString()); + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.drawmask = MASK_NORMAL; + self.entremove = target_push_remove; +} +#endif diff --cc qcsrc/common/triggers/trigger/secret.qc index 1902b7e7d3,0000000000..c04e1f4b8b mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/secret.qc +++ b/qcsrc/common/triggers/trigger/secret.qc @@@ -1,85 -1,0 +1,85 @@@ +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include "../../../dpdefs/progsdefs.qh" + #include "../../util.qh" - #include "../../../server/defs.qh" ++ #include "../../../server/_all.qh" + #include "secret.qh" +#endif + +#ifdef SVQC + +void secrets_setstatus() { + self.stat_secrets_total = secrets_total; + self.stat_secrets_found = secrets_found; +} + +/** + * A secret has been found (maybe :P) + */ +void trigger_secret_touch() { + // only a player can trigger this + if (!IS_PLAYER(other)) + return; + + // update secrets found counter + secrets_found += 1; + //print("Secret found: ", ftos(secret_counter.cnt), "/"); + //print(ftos(secret_counter.count), "\n"); + + // centerprint message (multi_touch() doesn't always call centerprint()) + centerprint(other, self.message); + self.message = ""; + + // handle normal trigger features + multi_touch(); + remove(self); +} + +/*QUAKED trigger_secret (.5 .5 .5) ? +Variable sized secret trigger. Can be targeted at one or more entities. +Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found. +-------- KEYS -------- +sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1) +noise: path to sound file, if you want to play something else +target: trigger all entities with this targetname when triggered +message: print this message to the player who activated the trigger instead of the standard 'You found a secret!' +killtarget: remove all entities with this targetname when triggered +-------- NOTES -------- +You should create a common/trigger textured brush covering the entrance to a secret room/area. +Trigger secret can only be trigger by a player's touch and can not be a target itself. +*/ +void spawnfunc_trigger_secret() { + // FIXME: should it be disabled in most modes? + + // update secrets count + secrets_total += 1; + + // add default message + if (self.message == "") + self.message = "You found a secret!"; + + // set default sound + if (self.noise == "") + if (!self.sounds) + self.sounds = 1; // misc/secret.wav + + // this entity can't be a target itself!!!! + self.targetname = ""; + + // you can't just shoot a room to find it, can you? + self.health = 0; + + // a secret can not be delayed + self.delay = 0; + + // convert this trigger to trigger_once + self.classname = "trigger_once"; + spawnfunc_trigger_once(); + + // take over the touch() function, so we can mark secret as found + self.touch = trigger_secret_touch; + // ignore triggering; + self.use = func_null; +} +#endif diff --cc qcsrc/common/triggers/trigger/swamp.qc index 8013f3b17e,0000000000..7f1bb892c3 mode 100644,000000..100644 --- a/qcsrc/common/triggers/trigger/swamp.qc +++ b/qcsrc/common/triggers/trigger/swamp.qc @@@ -1,154 -1,0 +1,155 @@@ +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) ++ #include "../../../server/_all.qh" + #include "../../../dpdefs/progsdefs.qh" + #include "../../../warpzonelib/util_server.qh" - #include "../../weapons/weapons.qh" ++ #include "../../weapons/all.qh" + #include "../../../server/defs.qh" + #include "../../deathtypes.qh" +#endif + +/* +* t_swamp.c +* Adds spawnfunc_trigger_swamp and suppoart routines for xonotic 1.2.1+ +* Author tZork (Jakob MG) +* jakob@games43.se +* 2005 11 29 +*/ + +.float swamp_interval; //Hurt players in swamp with this interval +.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) +.entity swampslug; + +#ifdef SVQC +void spawnfunc_trigger_swamp(void); +#endif +void swamp_touch(void); +void swampslug_think(); + + +/* +* Uses a entity calld swampslug to handle players in the swamp +* It works like this: When the plyer enters teh swamp the spawnfunc_trigger_swamp +* attaches a new "swampslug" to the player. As long as the plyer is inside +* the swamp the swamp gives the slug new health. But the slug slowly kills itself +* so when the player goes outside the swamp, it dies and releases the player from the +* swamps curses (dmg/slowdown) +* +* I do it this way becuz there is no "untouch" event. +*/ +void swampslug_think(void) +{ + //Slowly kill the slug + self.health = self.health - 1; + + //Slug dead? then remove curses. + if(self.health <= 0) + { + self.owner.in_swamp = 0; + remove(self); + //centerprint(self.owner,"Killing slug...\n"); + return; + } + + // Slug still alive, so we are still in the swamp + // Or we have exited it very recently. + // Do the damage and renew the timer. +#ifdef SVQC + Damage (self.owner, self, self, self.dmg, DEATH_SWAMP, other.origin, '0 0 0'); +#endif + + self.nextthink = time + self.swamp_interval; +} + +void swamp_touch(void) +{ + // If whatever thats touching the swamp is not a player + // or if its a dead player, just dont care abt it. + if(!IS_PLAYER(other) || PHYS_DEAD(other)) + return; + + EXACTTRIGGER_TOUCH; + + // Chech if player alredy got a swampslug. + if(other.in_swamp != 1) + { + // If not attach one. + //centerprint(other,"Entering swamp!\n"); + other.swampslug = spawn(); + other.swampslug.health = 2; + other.swampslug.think = swampslug_think; + other.swampslug.nextthink = time; + other.swampslug.owner = other; + other.swampslug.dmg = self.dmg; + other.swampslug.swamp_interval = self.swamp_interval; + other.swamp_slowdown = self.swamp_slowdown; + other.in_swamp = 1; + return; + } + + //other.in_swamp = 1; + + //Revitalize players swampslug + other.swampslug.health = 2; +} + +#ifdef SVQC +float swamp_send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER); + + WriteByte(MSG_ENTITY, self.dmg); // can probably get away with using a single byte here + WriteByte(MSG_ENTITY, self.swamp_slowdown); + WriteByte(MSG_ENTITY, self.swamp_interval); + + trigger_common_write(false); + + return true; +} + +void swamp_link() +{ + Net_LinkEntity(self, false, 0, func_ladder_send); +} + +/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ? +Players gettin into the swamp will +get slowd down and damaged +*/ +void spawnfunc_trigger_swamp(void) +{ + // Init stuff + EXACTTRIGGER_INIT; + self.touch = swamp_touch; + + // Setup default keys, if missing + if(self.dmg <= 0) + self.dmg = 5; + if(self.swamp_interval <= 0) + self.swamp_interval = 1; + if(self.swamp_slowdown <= 0) + self.swamp_slowdown = 0.5; + + swamp_link(); +} + +#elif defined(CSQC) + +void ent_swamp() +{ + self.dmg = ReadByte(); + self.swamp_slowdown = ReadByte(); + self.swamp_interval = ReadByte(); + + trigger_common_read(false); + + self.classname = "trigger_swamp"; + self.solid = SOLID_TRIGGER; + self.draw = trigger_draw_generic; + self.trigger_touch = swamp_touch; + self.drawmask = MASK_NORMAL; + self.move_time = time; + self.entremove = trigger_remove_generic; +} +#endif diff --cc qcsrc/common/triggers/triggers.qh index 8be1b3cd99,0000000000..3baff3095a mode 100644,000000..100644 --- a/qcsrc/common/triggers/triggers.qh +++ b/qcsrc/common/triggers/triggers.qh @@@ -1,55 -1,0 +1,49 @@@ +#ifndef TRIGGERS_H +#define TRIGGERS_H + +const float SF_TRIGGER_INIT = 1; +const float SF_TRIGGER_UPDATE = 2; +const float SF_TRIGGER_RESET = 4; + +const float SPAWNFLAG_NOMESSAGE = 1; +const float SPAWNFLAG_NOTOUCH = 1; + +.void() trigger_touch; + +.float height; + +.float nottargeted; +#define IFTARGETED if(!self.nottargeted && self.targetname != "") + - .string bgmscript; - .float bgmscriptattack; - .float bgmscriptdecay; - .float bgmscriptsustain; - .float bgmscriptrelease; - +.float lip; + +// used elsewhere (will fix) +#ifdef SVQC +void trigger_common_write(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); +#endif + +.float volume, atten; + +.vector dest; + +#ifdef CSQC +void trigger_common_read(bool withtarget); +void trigger_remove_generic(); + +.float active; +.string target; +.string targetname; + +const int ACTIVE_NOT = 0; +const int ACTIVE_ACTIVE = 1; +const int ACTIVE_IDLE = 2; +const int ACTIVE_BUSY = 2; +const int ACTIVE_TOGGLE = 3; +#endif + +#endif diff --cc qcsrc/common/weapons/all.qc index 0000000000,847c8371d6..36f08eff90 mode 000000,100644..100644 --- a/qcsrc/common/weapons/all.qc +++ b/qcsrc/common/weapons/all.qc @@@ -1,0 -1,375 +1,375 @@@ + #ifndef WEAPONS_ALL_C + #define WEAPONS_ALL_C + + #include "all.qh" + + #if defined(CSQC) + #include "../../dpdefs/csprogsdefs.qh" + #include "../../client/defs.qh" + #include "../constants.qh" + #include "../stats.qh" + #include "../../warpzonelib/anglestransform.qh" + #include "../../warpzonelib/mathlib.qh" + #include "../../warpzonelib/common.qh" + #include "../../warpzonelib/client.qh" + #include "../util.qh" + #include "../buffs.qh" + #include "../../client/autocvars.qh" + #include "../deathtypes.qh" + #include "../../csqcmodellib/interpolate.qh" - #include "../../client/movetypes.qh" ++ #include "../movetypes/movetypes.qh" + #include "../../client/main.qh" + #include "../../csqcmodellib/cl_model.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/anglestransform.qh" + #include "../../warpzonelib/mathlib.qh" + #include "../../warpzonelib/common.qh" + #include "../../warpzonelib/util_server.qh" + #include "../../warpzonelib/server.qh" + #include "../constants.qh" + #include "../stats.qh" + #include "../teams.qh" + #include "../util.qh" + #include "../buffs.qh" + #include "../monsters/all.qh" + #include "config.qh" + #include "../../server/weapons/csqcprojectile.qh" + #include "../../server/weapons/tracing.qh" + #include "../../server/t_items.qh" + #include "../../server/autocvars.qh" + #include "../../server/constants.qh" + #include "../../server/defs.qh" + #include "../notifications.qh" + #include "../deathtypes.qh" + #include "../../server/mutators/mutators_include.qh" + #include "../mapinfo.qh" + #include "../../server/command/common.qh" + #include "../../csqcmodellib/sv_model.qh" + #include "../../server/portals.qh" + #include "../../server/g_hook.qh" + #endif + #ifndef MENUQC + #include "calculations.qc" + #endif + #include "all.inc" + + // WEAPON PLUGIN SYSTEM + entity weapon_info[WEP_MAXCOUNT]; + entity dummy_weapon_info; + + #if WEP_MAXCOUNT > 72 + # error Kein Weltraum links auf dem Gerät + #endif + + WepSet WepSet_FromWeapon(int a) { + a -= WEP_FIRST; + #if WEP_MAXCOUNT > 24 + if(a >= 24) { + a -= 24; + #if WEP_MAXCOUNT > 48 + if(a >= 24) { + a -= 24; + return '0 0 1' * power2of(a); + } + #endif + return '0 1 0' * power2of(a); + } + #endif + return '1 0 0' * power2of(a); + } + #ifdef SVQC + void WepSet_AddStat() + { + addstat(STAT_WEAPONS, AS_INT, weapons_x); + #if WEP_MAXCOUNT > 24 + addstat(STAT_WEAPONS2, AS_INT, weapons_y); + #if WEP_MAXCOUNT > 48 + addstat(STAT_WEAPONS3, AS_INT, weapons_z); + #endif + #endif + } + void WriteWepSet(float dst, WepSet w) + { + #if WEP_MAXCOUNT > 48 + WriteInt72_t(dst, w); + #elif WEP_MAXCOUNT > 24 + WriteInt48_t(dst, w); + #else + WriteInt24_t(dst, w.x); + #endif + } + #endif + #ifdef CSQC + WepSet WepSet_GetFromStat() + { + WepSet w = '0 0 0'; + w.x = getstati(STAT_WEAPONS); + #if WEP_MAXCOUNT > 24 + w.y = getstati(STAT_WEAPONS2); + #if WEP_MAXCOUNT > 48 + w.z = getstati(STAT_WEAPONS3); + #endif + #endif + return w; + } + WepSet ReadWepSet() + { + #if WEP_MAXCOUNT > 48 + return ReadInt72_t(); + #elif WEP_MAXCOUNT > 24 + return ReadInt48_t(); + #else + return ReadInt24_t() * '1 0 0'; + #endif + } + #endif + + void register_weapon( + int id, + WepSet bit, + bool(int) func, + .int ammotype, + int i, + int weapontype, + float pickupbasevalue, + vector clr, + string modelname, + string simplemdl, + string crosshair, + string wepimg, + string refname, + string wepname) + { + entity e; + weapon_info[id - 1] = e = spawn(); + e.classname = "weapon_info"; + e.weapon = id; + e.weapons = bit; + e.weapon_func = func; + e.ammo_field = ammotype; + e.impulse = i; + e.spawnflags = weapontype; + e.bot_pickupbasevalue = pickupbasevalue; + e.wpcolor = clr; + e.wpmodel = strzone(strcat("wpn-", ftos(id))); + e.mdl = modelname; + e.model = strzone(strcat("models/weapons/g_", modelname, ".md3")); + e.w_simplemdl = strzone(simplemdl); // simpleitems weapon model/image + e.w_crosshair = strzone(car(crosshair)); + string s = cdr(crosshair); + e.w_crosshair_size = ((s != "") ? stof(s) : 1); // so that we can scale the crosshair from code (for compat) + e.model2 = strzone(wepimg); + e.netname = refname; + e.message = wepname; + + #ifdef CSQC + func(WR_INIT); + #endif + } + bool w_null(int dummy) + { + return 0; + } + void register_weapons_done() + { + dummy_weapon_info = spawn(); + dummy_weapon_info.classname = "weapon_info"; + dummy_weapon_info.weapon = 0; // you can recognize dummies by this + dummy_weapon_info.weapons = '0 0 0'; + dummy_weapon_info.netname = ""; + dummy_weapon_info.message = "AOL CD Thrower"; + dummy_weapon_info.weapon_func = w_null; + dummy_weapon_info.wpmodel = ""; + dummy_weapon_info.mdl = ""; + dummy_weapon_info.model = ""; + dummy_weapon_info.spawnflags = 0; + dummy_weapon_info.impulse = -1; + dummy_weapon_info.bot_pickupbasevalue = 0; + dummy_weapon_info.ammo_field = ammo_none; + + dummy_weapon_info.w_crosshair = "gfx/crosshair1"; + dummy_weapon_info.w_crosshair_size = 1; + dummy_weapon_info.model2 = ""; + + int i; + weaponorder_byid = ""; + for(i = WEP_MAXCOUNT; i >= 1; --i) + if(weapon_info[i-1]) + weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i)); + weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1)); + } + entity get_weaponinfo(int id) + { + entity w; + if(id < WEP_FIRST || id > WEP_LAST) + return dummy_weapon_info; + w = weapon_info[id - 1]; + if(w) + return w; + return dummy_weapon_info; + } + string W_FixWeaponOrder(string order, float complete) + { + return fixPriorityList(order, WEP_FIRST, WEP_LAST, 230 - WEP_FIRST, complete); + } + string W_NameWeaponOrder_MapFunc(string s) + { + entity wi; + if(s == "0" || stof(s)) + { + wi = get_weaponinfo(stof(s)); + if(wi != dummy_weapon_info) + return wi.netname; + } + return s; + } + + string W_UndeprecateName(string s) + { + switch ( s ) + { + case "nex" : return "vortex"; + case "rocketlauncher" : return "devastator"; + case "laser" : return "blaster"; + case "minstanex" : return "vaporizer"; + case "grenadelauncher": return "mortar"; + case "uzi" : return "machinegun"; + default : return s; + } + } + string W_NameWeaponOrder(string order) + { + return mapPriorityList(order, W_NameWeaponOrder_MapFunc); + } + string W_NumberWeaponOrder_MapFunc(string s) + { + int i; + if(s == "0" || stof(s)) + return s; + s = W_UndeprecateName(s); + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + if(s == get_weaponinfo(i).netname) + return ftos(i); + return s; + } + string W_NumberWeaponOrder(string order) + { + return mapPriorityList(order, W_NumberWeaponOrder_MapFunc); + } + + float W_FixWeaponOrder_BuildImpulseList_buf[WEP_MAXCOUNT]; + string W_FixWeaponOrder_BuildImpulseList_order; + void W_FixWeaponOrder_BuildImpulseList_swap(int i, int j, entity pass) + { + float h; + h = W_FixWeaponOrder_BuildImpulseList_buf[i]; + W_FixWeaponOrder_BuildImpulseList_buf[i] = W_FixWeaponOrder_BuildImpulseList_buf[j]; + W_FixWeaponOrder_BuildImpulseList_buf[j] = h; + } + float W_FixWeaponOrder_BuildImpulseList_cmp(int i, int j, entity pass) + { + entity e1, e2; + float d; + e1 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[i]); + e2 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[j]); + d = (e1.impulse + 9) % 10 - (e2.impulse + 9) % 10; + if(d != 0) + return -d; // high impulse first! + return + strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[i]), 0) + - + strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[j]), 0) + ; // low char index first! + } + string W_FixWeaponOrder_BuildImpulseList(string o) + { + int i; + W_FixWeaponOrder_BuildImpulseList_order = o; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST] = i; + heapsort(WEP_LAST - WEP_FIRST + 1, W_FixWeaponOrder_BuildImpulseList_swap, W_FixWeaponOrder_BuildImpulseList_cmp, world); + o = ""; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + o = strcat(o, " ", ftos(W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST])); + W_FixWeaponOrder_BuildImpulseList_order = string_null; + return substring(o, 1, -1); + } + + string W_FixWeaponOrder_AllowIncomplete(string order) + { + return W_FixWeaponOrder(order, 0); + } + + string W_FixWeaponOrder_ForceComplete(string order) + { + if(order == "") + order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority")); + return W_FixWeaponOrder(order, 1); + } + + void W_RandomWeapons(entity e, float n) + { + int i, j; + WepSet remaining; + WepSet result; + remaining = e.weapons; + result = '0 0 0'; + for(i = 0; i < n; ++i) + { + RandomSelection_Init(); + for(j = WEP_FIRST; j <= WEP_LAST; ++j) + if(remaining & WepSet_FromWeapon(j)) + RandomSelection_Add(world, j, string_null, 1, 1); + result |= WepSet_FromWeapon(RandomSelection_chosen_float); + remaining &= ~WepSet_FromWeapon(RandomSelection_chosen_float); + } + e.weapons = result; + } + + string GetAmmoPicture(.int ammotype) + { + switch(ammotype) + { + case ammo_shells: return "ammo_shells"; + case ammo_nails: return "ammo_bullets"; + case ammo_rockets: return "ammo_rockets"; + case ammo_cells: return "ammo_cells"; + case ammo_plasma: return "ammo_cells"; + case ammo_fuel: return "ammo_fuel"; + default: return ""; // wtf, no ammo type? + } + } + + #ifdef CSQC + .int GetAmmoFieldFromNum(int i) + { + switch(i) + { + case 0: return ammo_shells; + case 1: return ammo_nails; + case 2: return ammo_rockets; + case 3: return ammo_cells; + case 4: return ammo_plasma; + case 5: return ammo_fuel; + default: return ammo_none; + } + } + + int GetAmmoStat(.int ammotype) + { + switch(ammotype) + { + case ammo_shells: return STAT_SHELLS; + case ammo_nails: return STAT_NAILS; + case ammo_rockets: return STAT_ROCKETS; + case ammo_cells: return STAT_CELLS; + case ammo_plasma: return STAT_PLASMA; + case ammo_fuel: return STAT_FUEL; + default: return -1; + } + } + #endif + #endif diff --cc qcsrc/dpdefs/upstream/csprogsdefs.qc index 0000000000,202b840e07..21b7c5433b mode 000000,100644..100644 --- a/qcsrc/dpdefs/upstream/csprogsdefs.qc +++ b/qcsrc/dpdefs/upstream/csprogsdefs.qc @@@ -1,0 -1,1466 +1,1467 @@@ + /* + ============================================================================== + + SOURCE FOR GLOBALVARS_T C STRUCTURE + MUST NOT BE MODIFIED, OR CRC ERRORS WILL APPEAR + + ============================================================================== + */ + + // + // system globals + // + entity self; + entity other; + entity world; + float time; + float frametime; + + float player_localentnum; //the entnum + float player_localnum; //the playernum + float maxclients; //a constant filled in by the engine. gah, portability eh? + + float clientcommandframe; //player movement + float servercommandframe; //clientframe echoed off the server + + string mapname; + + // + // global variables set by built in functions + // + vector v_forward, v_up, v_right; // set by makevectors() + + // set by traceline / tracebox + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vector trace_endpos; + vector trace_plane_normal; + float trace_plane_dist; + entity trace_ent; + float trace_inopen; + float trace_inwater; + + // + // required prog functions + // + void() CSQC_Init; + void() CSQC_Shutdown; + float(float f, float t, float n) CSQC_InputEvent; + void(float w, float h) CSQC_UpdateView; + float(string s) CSQC_ConsoleCommand; + + //these fields are read and set by the default player physics + vector pmove_org; + vector pmove_vel; + vector pmove_mins; + vector pmove_maxs; + //retrieved from the current movement commands (read by player physics) + float input_timelength; + vector input_angles; + vector input_movevalues; //forwards, right, up. + float input_buttons; //attack, use, jump (default physics only uses jump) + + float movevar_gravity; + float movevar_stopspeed; + float movevar_maxspeed; + float movevar_spectatormaxspeed; //used by NOCLIP movetypes. + float movevar_accelerate; + float movevar_airaccelerate; + float movevar_wateraccelerate; + float movevar_friction; + float movevar_waterfriction; + float movevar_entgravity; //the local player's gravity field. Is a multiple (1 is the normal value) + + //================================================ + void end_sys_globals; // flag for structure dumping + //================================================ + + /* + ============================================================================== + + SOURCE FOR ENTVARS_T C STRUCTURE + MUST NOT BE MODIFIED, OR CRC ERRORS WILL APPEAR + + ============================================================================== + */ + + // + // system fields (*** = do not set in prog code, maintained by C code) + // + .float modelindex; // *** model index in the precached list + .vector absmin, absmax; // *** origin + mins / maxs + + .float entnum; // *** the ent number as on the server + .float drawmask; + .void() predraw; + + .float movetype; + .float solid; + + .vector origin; // *** + .vector oldorigin; // *** + .vector velocity; + .vector angles; + .vector avelocity; + + .string classname; // spawn function + .string model; + .float frame; + .float skin; + .float effects; + + .vector mins, maxs; // bounding box extents reletive to origin + .vector size; // maxs - mins + + .void() touch; + .void() use; + .void() think; + .void() blocked; // for doors or plats, called when can't push other + + .float nextthink; + + .entity chain; + + .string netname; + + .entity enemy; + + .float flags; + + .float colormap; + + .entity owner; // who launched a missile + + //================================================ + void end_sys_fields; // flag for structure dumping + //================================================ + + /* + ============================================================================== + + OPTIONAL FIELDS AND GLOBALS + + ============================================================================== + */ + + // Additional OPTIONAL Fields and Globals + float intermission; // indicates intermission state (0 = normal, 1 = scores, 2 = finale text) + + vector view_angles; // same as input_angles + vector view_punchangle; // from server + vector view_punchvector; // from server + + /* + ============================================================================== + + CONSTANT DEFINITIONS + + ============================================================================== + */ + + const float MASK_ENGINE = 1; + const float MASK_ENGINEVIEWMODELS = 2; + const float MASK_NORMAL = 4; + + const float RF_VIEWMODEL = 1; + const float RF_EXTERNALMODEL = 2; + const float RF_DEPTHHACK = 4; + const float RF_ADDITIVE = 8; + const float RF_USEAXIS = 16; + + const float VF_MIN = 1; //(vector) + const float VF_MIN_X = 2; //(float) + const float VF_MIN_Y = 3; //(float) + const float VF_SIZE = 4; //(vector) (viewport size) + const float VF_SIZE_Y = 5; //(float) + const float VF_SIZE_X = 6; //(float) + const float VF_VIEWPORT = 7; //(vector, vector) + const float VF_FOV = 8; //(vector) + const float VF_FOVX = 9; //(float) + const float VF_FOVY = 10; //(float) + const float VF_ORIGIN = 11; //(vector) + const float VF_ORIGIN_X = 12; //(float) + const float VF_ORIGIN_Y = 13; //(float) + const float VF_ORIGIN_Z = 14; //(float) + const float VF_ANGLES = 15; //(vector) + const float VF_ANGLES_X = 16; //(float) + const float VF_ANGLES_Y = 17; //(float) + const float VF_ANGLES_Z = 18; //(float) + const float VF_DRAWWORLD = 19; //(float) + const float VF_DRAWENGINESBAR = 20; //(float) + const float VF_DRAWCROSSHAIR = 21; //(float) + + const float VF_CL_VIEWANGLES = 33; //(vector) + const float VF_CL_VIEWANGLES_X = 34; //(float) + const float VF_CL_VIEWANGLES_Y = 35; //(float) + const float VF_CL_VIEWANGLES_Z = 36; //(float) + + const float VF_PERSPECTIVE = 200; + + const float STAT_HEALTH = 0; + const float STAT_WEAPONMODEL = 2; + const float STAT_AMMO = 3; + const float STAT_ARMOR = 4; + const float STAT_WEAPONFRAME = 5; + const float STAT_SHELLS = 6; + const float STAT_NAILS = 7; + const float STAT_ROCKETS = 8; + const float STAT_CELLS = 9; + const float STAT_ACTIVEWEAPON = 10; + const float STAT_TOTALSECRETS = 11; + const float STAT_TOTALMONSTERS = 12; + const float STAT_SECRETS = 13; + const float STAT_MONSTERS = 14; + const float STAT_ITEMS = 15; + const float STAT_VIEWHEIGHT = 16; + + // Quake Sound Constants + const float CHAN_AUTO = 0; + const float CHAN_WEAPON = 1; + const float CHAN_VOICE = 2; + const float CHAN_ITEM = 3; + const float CHAN_BODY = 4; + + const float ATTN_NONE = 0; + const float ATTN_NORM = 1; + const float ATTN_IDLE = 2; + const float ATTN_STATIC = 3; + + // Frik File Constants + const float FILE_READ = 0; + const float FILE_APPEND = 1; + const float FILE_WRITE = 2; + + // Quake Point Contents + const float CONTENT_EMPTY = -1; + const float CONTENT_SOLID = -2; + const float CONTENT_WATER = -3; + const float CONTENT_SLIME = -4; + const float CONTENT_LAVA = -5; + const float CONTENT_SKY = -6; + + // Quake Solid Constants + const float SOLID_NOT = 0; + const float SOLID_TRIGGER = 1; + const float SOLID_BBOX = 2; + const float SOLID_SLIDEBOX = 3; + const float SOLID_BSP = 4; + const float SOLID_CORPSE = 5; + + // Quake Move Constants + const float MOVE_NORMAL = 0; + const float MOVE_NOMONSTERS = 1; + const float MOVE_MISSILE = 2; + + // Boolean Constants + const float true = 1; + const float false = 0; + const float TRUE = 1; + const float FALSE = 0; + + const float EXTRA_LOW = -99999999; + const float EXTRA_HIGH = 99999999; + + const vector VEC_1 = '1 1 1'; + const vector VEC_0 = '0 0 0'; + const vector VEC_M1 = '-1 -1 -1'; + + const float M_PI = 3.14159265358979323846; + + vector VEC_HULL_MIN = '-16 -16 -24'; + vector VEC_HULL_MAX = '16 16 32'; + + // Quake Temporary Entity Constants + const float TE_SPIKE = 0; + const float TE_SUPERSPIKE = 1; + const float TE_GUNSHOT = 2; + const float TE_EXPLOSION = 3; + const float TE_TAREXPLOSION = 4; + const float TE_LIGHTNING1 = 5; + const float TE_LIGHTNING2 = 6; + const float TE_WIZSPIKE = 7; + const float TE_KNIGHTSPIKE = 8; + const float TE_LIGHTNING3 = 9; + const float TE_LAVASPLASH = 10; + const float TE_TELEPORT = 11; + const float TE_EXPLOSION2 = 12; + // Darkplaces Additions + const float TE_EXPLOSIONRGB = 53; + const float TE_GUNSHOTQUAD = 57; + const float TE_EXPLOSIONQUAD = 70; + const float TE_SPIKEQUAD = 58; + const float TE_SUPERSPIKEQUAD = 59; + + // PFlags for Dynamic Lights + const float PFLAGS_NOSHADOW = 1; + const float PFLAGS_CORONA = 2; + const float PFLAGS_FULLDYNAMIC = 128; + + const float EF_ADDITIVE = 32; + const float EF_BLUE = 64; + const float EF_FLAME = 1024; + const float EF_FULLBRIGHT = 512; + const float EF_NODEPTHTEST = 8192; + const float EF_NODRAW = 16; + const float EF_NOSHADOW = 4096; + const float EF_RED = 128; + const float EF_STARDUST = 2048; + const float EF_SELECTABLE = 16384; + + const float PFL_ONGROUND = 1; + const float PFL_CROUCH = 2; + const float PFL_DEAD = 4; + const float PFL_GIBBED = 8; + + // draw flags + const float DRAWFLAG_NORMAL = 0; + const float DRAWFLAG_ADDITIVE = 1; + const float DRAWFLAG_MODULATE = 2; + const float DRAWFLAG_2XMODULATE = 3; + const float DRAWFLAG_SCREEN = 4; + const float DRAWFLAG_MIPMAP = 0x100; // only for R_BeginPolygon + + /* + ============================================================================== + + BUILTIN DEFINITIONS + EXTENSIONS ARE NOT ADDED HERE, BUT BELOW! + + ============================================================================== + */ + + void(vector ang) makevectors = #1; + void(entity e, vector o) setorigin = #2; + void(entity e, string m) setmodel = #3; + void(entity e, vector min, vector max) setsize = #4; + + void() break_to_debugger = #6; + float() random = #7; + void(entity e, float chan, string samp) sound = #8; + vector(vector v) normalize = #9; + void(string e) error = #10; + void(string e) objerror = #11; + float(vector v) vlen = #12; + float(vector v) vectoyaw = #13; + entity() spawn = #14; + void(entity e) remove = #15; + float(vector v1, vector v2, float tryents, entity ignoreentity) traceline = #16; + + entity(entity start, .string fld, string match) find = #18; + void(string s) precache_sound = #19; + void(string s) precache_model = #20; + + entity(vector org, float rad) findradius = #22; + + void(string s, ...) dprint = #25; + string(float f) ftos = #26; + string(vector v) vtos = #27; + void() coredump = #28; + void() traceon = #29; + void() traceoff = #30; + void(entity e) eprint = #31; + // settrace optional + float(float yaw, float dist, float settrace) walkmove = #32; + + float() droptofloor = #34; + void(float style, string value) lightstyle = #35; + float(float v) rint = #36; + float(float v) floor = #37; + float(float v) ceil = #38; + + float(entity e) checkbottom = #40; + float(vector v) pointcontents = #41; + + float(float f) fabs = #43; + + float(string s) cvar = #45; + void(string s, ...) localcmd = #46; + entity(entity e) nextent = #47; + void(vector o, vector d, float color, float count) particle = #48; + void() ChangeYaw = #49; + + vector(vector v) vectoangles = #51; + vector(vector v, vector w) vectoangles2 = #51; + + float(float f) sin = #60; + float(float f) cos = #61; + float(float f) sqrt = #62; + void(entity ent) changepitch = #63; + void(entity e, entity ignore) tracetoss = #64; + string(entity ent) etos = #65; + + string(string s) precache_file = #68; + void(entity e) makestatic = #69; + + void(string var, string val) cvar_set = #72; + + void(vector pos, string samp, float vol, float atten) ambientsound = #74; + string(string s) precache_model2 = #75; + string(string s) precache_sound2 = #76; + string(string s) precache_file2 = #77; + + float(string s) stof = #81; + + + void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox = #90; + vector() randomvec = #91; + vector(vector org) getlight = #92; + vector(vector org, float lpflags) getlight2 = #92; + vector getlight_dir; + vector getlight_ambient; + vector getlight_diffuse; + const float LP_LIGHTMAP = 1; + const float LP_RTWORLD = 2; + const float LP_DYNLIGHT = 4; + const float LP_COMPLETE = 7; + + float(string name, string value) registercvar = #93; + float( float a, ... ) min = #94; + float( float b, ... ) max = #95; + float(float minimum, float val, float maximum) bound = #96; + float(float f, float f) pow = #97; + entity(entity start, .float fld, float match) findfloat = #98; ++entity(entity start, .entity fld, entity match) findentity = #98; + float(string s) checkextension = #99; + // FrikaC and Telejano range #100-#199 + + float(string filename, float mode) fopen = #110; + void(float fhandle) fclose = #111; + string(float fhandle) fgets = #112; + void(float fhandle, string s) fputs = #113; + float(string s) strlen = #114; + string(...) strcat = #115; + string(string s, float start, float length) substring = #116; + vector(string) stov = #117; + string(string s) strzone = #118; + void(string s) strunzone = #119; + + // FTEQW range #200-#299 + + float(float number, float quantity) bitshift = #218; + + //float(string str, string sub[, float startpos]) strstrofs = #221; + float(string str, string sub, float startpos) strstrofs = #221; + float(string str, float ofs) str2chr = #222; + string(float c, ...) chr2str = #223; + string(float ccase, float calpha, float cnum, string s, ...) strconv = #224; + string(float chars, string s, ...) strpad = #225; + string(string info, string key, string value, ...) infoadd = #226; + string(string info, string key) infoget = #227; + float(string s1, string s2) strcmp = #228; + float(string s1, string s2, float len) strncmp = #228; + float(string s1, string s2) strcasecmp = #229; + float(string s1, string s2, float len) strncasecmp = #230; + + // CSQC range #300-#399 + void() clearscene = #300; + void(float mask) addentities = #301; + void(entity ent) addentity = #302; + float(float property, ...) setproperty = #303; + float(float property) getproperty = #309; + vector(float property) getpropertyvec = #309; + void() renderscene = #304; + void(vector org, float radius, vector lightcolours) adddynamiclight = #305; + void(vector org, float radius, vector lightcolours, float style, string cubemapname, float pflags) adddynamiclight2 = #305; + //void(string texturename, float flag[, float is2d, float lines]) R_BeginPolygon = #306; + void(string texturename, float flag, ...) R_BeginPolygon = #306; + void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #307; + void() R_EndPolygon = #308; + vector (vector v) cs_unproject = #310; + vector (vector v) cs_project = #311; + + void(float width, vector pos1, vector pos2, float flag) drawline = #315; + float(string name) iscachedpic = #316; + string(string name, ...) precache_pic = #317; + string(string name) precache_cubemap = #317; + vector(string picname) draw_getimagesize = #318; + void(string name) freepic = #319; + float(vector position, float character, vector scale, vector rgb, float alpha, float flag) drawcharacter = #320; + float(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring = #321; + float(vector position, string pic, vector size, vector rgb, float alpha, float flag) drawpic = #322; + float(vector position, vector size, vector rgb, float alpha, float flag) drawfill = #323; + void(float x, float y, float width, float height) drawsetcliparea = #324; + void(void) drawresetcliparea = #325; + float(vector position, string text, vector scale, float alpha, float flag) drawcolorcodedstring = #326; + vector(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawcolorcodedstring2 = #326; + + float(float stnum) getstatf = #330; + float(float stnum, ...) getstati = #331; // can optionally take first bit and count + string(float firststnum) getstats = #332; + void(entity e, float mdlindex) setmodelindex = #333; + string(float mdlindex) modelnameforindex = #334; + float(string effectname) particleeffectnum = #335; + void(entity ent, float effectnum, vector start, vector end) trailparticles = #336; + //void(float effectnum, vector origin [, vector dir, float count]) pointparticles = #337; + void(float effectnum, vector origin , vector dir, float count) pointparticles = #337; + void(string s, ...) centerprint = #338; + void(string s, ...) print = #339; + string(float keynum) keynumtostring = #340; + float(string keyname) stringtokeynum = #341; + string(float keynum) getkeybind = #342; + void(float usecursor) setcursormode = #343; + vector() getmousepos = #344; + float(float framenum) getinputstate = #345; + void(float sens) setsensitivityscale = #346; + void(...) runstandardplayerphysics = #347; // this may or may not take a player ent + string(float playernum, string keyname) getplayerkeyvalue = #348; + float() isdemo = #349; + float() isserver = #350; + void(vector origin, vector forward, vector right, vector up) SetListener = #351; + void(string cmdname) registercommand = #352; + float(entity ent) wasfreed = #353; + string(string key) serverkey = #354; + + // Use proper case; refer to the id1 Write* functions! + float() ReadByte = #360; + float() ReadChar = #361; + float() ReadShort = #362; + float() ReadLong = #363; + float() ReadCoord = #364; + float() ReadAngle = #365; + string() ReadString = #366; + float() ReadFloat = #367; + + // LordHavoc's range #400-#499 + void(entity from, entity to) copyentity = #400; + + entity(.string fld, string match) findchain = #402; + entity(.float fld, float match) findchainfloat = #403; + void(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; + void(vector org, vector velocity, float howmany) te_blood = #405; + void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; + void(vector org, vector color) te_explosionrgb = #407; + void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; + void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; + void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; + void(vector org, vector vel, float howmany) te_spark = #411; + void(vector org) te_gunshotquad = #412; + void(vector org) te_spikequad = #413; + void(vector org) te_superspikequad = #414; + void(vector org) te_explosionquad = #415; + void(vector org) te_smallflash = #416; + void(vector org, float radius, float lifetime, vector color) te_customflash = #417; + void(vector org) te_gunshot = #418; + void(vector org) te_spike = #419; + void(vector org) te_superspike = #420; + void(vector org) te_explosion = #421; + void(vector org) te_tarexplosion = #422; + void(vector org) te_wizspike = #423; + void(vector org) te_knightspike = #424; + void(vector org) te_lavasplash = #425; + void(vector org) te_teleport = #426; + void(vector org, float colorstart, float colorlength) te_explosion2 = #427; + void(entity own, vector start, vector end) te_lightning1 = #428; + void(entity own, vector start, vector end) te_lightning2 = #429; + void(entity own, vector start, vector end) te_lightning3 = #430; + void(entity own, vector start, vector end) te_beam = #431; + void(vector dir) vectorvectors = #432; + void(vector org) te_plasmaburn = #433; + float(entity e, float s) getsurfacenumpoints = #434; + vector(entity e, float s, float n) getsurfacepoint = #435; + vector(entity e, float s) getsurfacenormal = #436; + string(entity e, float s) getsurfacetexture = #437; + float(entity e, vector p) getsurfacenearpoint = #438; + vector(entity e, float s, vector p) getsurfaceclippedpoint = #439; + + float(string s) tokenize = #441; + string(float n) argv = #442; + void(entity e, entity tagentity, string tagname) setattachment = #443; + float(string pattern, float caseinsensitive, float quiet) search_begin = #444; + void(float handle) search_end = #445; + float(float handle) search_getsize = #446; + string(float handle, float num) search_getfilename = #447; + string(string s) cvar_string = #448; + entity(entity start, .float fld, float match) findflags = #449; + entity(.float fld, float match) findchainflags = #450; + float(entity ent, string tagname) gettagindex = #451; + vector(entity ent, float tagindex) gettaginfo = #452; + + void(vector org, vector vel, float howmany) te_flamejet = #457; + + entity(float num) entitybyindex = #459; + float() buf_create = #460; + void(float bufhandle) buf_del = #461; + float(float bufhandle) buf_getsize = #462; + void(float bufhandle_from, float bufhandle_to) buf_copy = #463; + void(float bufhandle, float sortpower, float backward) buf_sort = #464; + string(float bufhandle, string glue) buf_implode = #465; + string(float bufhandle, float string_index) bufstr_get = #466; + void(float bufhandle, float string_index, string str) bufstr_set = #467; + float(float bufhandle, string str, float order) bufstr_add = #468; + void(float bufhandle, float string_index) bufstr_free = #469; + + float(float s) asin = #471; + float(float c) acos = #472; + float(float t) atan = #473; + float(float c, float s) atan2 = #474; + float(float a) tan = #475; + float(string s) strippedstringlen = #476; + float(string s) strlennocol = #476; // This is the correct name for the function, but not removing the decolorizedstring mapping. + string(string s) decolorizedstring = #477; + string(string s) strdecolorize = #477; // This is the correct name for the function, but not removing the decolorizedstring mapping. + string(float uselocaltime, string format, ...) strftime = #478; + string(string s) strtolower = #480; + string(string s) strtoupper = #481; + string(string s) cvar_defstring = #482; + void(vector origin, string sample, float volume, float attenuation) pointsound = #483; + string(string search, string replace, string subject) strreplace = #484; + string(string search, string replace, string subject) strireplace = #485; + vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; + #ifdef SUPPORT_GECKO + float gecko_create( string name ) = #487; + void gecko_destroy( string name ) = #488; + void gecko_navigate( string name, string URI ) = #489; + float gecko_keyevent( string name, float key, float eventtype ) = #490; + void gecko_mousemove( string name, float x, float y ) = #491; + void gecko_resize( string name, float w, float h ) = #492; + vector gecko_get_texture_extent( string name ) = #493; + #else + + #endif + + /* + ============================================================================== + + EXTENSION DEFINITIONS + + ============================================================================== + */ + + // DP_CSQC_SPAWNPARTICLE + // idea: VorteX + // darkplaces implementation: VorteX + // constant definitions: + // particle base behavior: + float PT_ALPHASTATIC = 1; + float PT_STATIC = 2; + float PT_SPARK = 3; + float PT_BEAM = 4; + float PT_RAIN = 5; + float PT_RAINDECAL = 6; + float PT_SNOW = 7; + float PT_BUBBLE = 8; + float PT_BLOOD = 9; + float PT_SMOKE = 10; + float PT_DECAL = 11; + float PT_ENTITYPARTICLE = 12; + // particle blendtypes: + float PBLEND_ALPHA = 0; + float PBLEND_ADD = 1; + float PBLEND_INVMOD = 2; + // particle orientation: + float PARTICLE_BILLBOARD = 0; + float PARTICLE_SPARK = 1; + float PARTICLE_ORIENTED_DOUBLESIDED = 2; + float PARTICLE_BEAM = 3; + // global definitions: + float particle_type; // one of PT_ + float particle_blendmode; // one of PBLEND_ values + float particle_orientation; // one of PARTICLE_ values + vector particle_color1; + vector particle_color2; + float particle_tex; // number of chunk in particlefont + float particle_size; + float particle_sizeincrease; + float particle_alpha; + float particle_alphafade; + float particle_time; + float particle_gravity; + float particle_bounce; + float particle_airfriction; + float particle_liquidfriction; + float particle_originjitter; + float particle_velocityjitter; + float particle_qualityreduction; // enable culling of this particle when FPS is low + float particle_stretch; + vector particle_staincolor1; + vector particle_staincolor2; + float particle_staintex; + float particle_stainalpha; + float particle_stainsize; + float particle_delayspawn; + float particle_delaycollision; + float particle_angle; + float particle_spin; + // builtin definitions: + float(float max_themes) initparticlespawner = #522; // check fields/globals for integration and enable particle spawner, return 1 is succeded, otherwise returns 0 + void() resetparticle = #523; // reset p_ globals to default theme #0 + void(float theme) particletheme = #524; // restore p_ globals from saved theme + float() particlethemesave = #525; // save p_ globals to new particletheme and return it's index + void(float theme) particlethemeupdate = #525; // save p_ globals to new particletheme and return it's index + void(float theme) particlethemefree = #526; // delete a particle theme + float(vector org, vector vel) spawnparticle = #527; // returns 0 when failed, 1 when spawned + float(vector org, vector vel, float theme) quickparticle = #527; // not reading globals, just theme, returns 0 when failed, 1 when spawned + float(vector org, vector vel, float delay, float collisiondelay) delayedparticle = #528; + float(vector org, vector vel, float delay, float collisiondelay, float theme) quickdelayedparticle = #528; + // description: this builtin provides an easy and flexible way to spawn particles, + // it is not created as replace for DP_SV_POINTPARTICLES but as an addition to it. + // With this extension you can create a specific particles like rain particles, or entity particles + // notes: + // 1) 0 is default particle template, it could be changed + // 2) color vectors could have value 0-255 of each component + // restrictions: max themes could be between 4 and 2048 + // warning: you should call initparticlespawner() at very beginning BEFORE all other particle spawner functions + // function to query particle info + // don't remove this function as it protects all particle_ globals from FTEQCC/FRIKQCC non-referenced removal optimisation + void() printparticle = + { + // vortex: this also protects from 'non-referenced' optimisation on some compilers + print("PARTICLE:\n"); + print(strcat(" type: ", ftos(particle_type), "\n")); + print(strcat(" blendmode: ", ftos(particle_blendmode), "\n")); + print(strcat(" orientation: ", ftos(particle_orientation), "\n")); + print(strcat(" color1: ", vtos(particle_color1), "\n")); + print(strcat(" color2: ", vtos(particle_color2), "\n")); + print(strcat(" tex: ", ftos(particle_tex), "\n")); + print(strcat(" size: ", ftos(particle_size), "\n")); + print(strcat(" sizeincrease: ", ftos(particle_sizeincrease), "\n")); + print(strcat(" alpha: ", ftos(particle_alpha), "\n")); + print(strcat(" alphafade: ", ftos(particle_alphafade), "\n")); + print(strcat(" time: ", ftos(particle_time), "\n")); + print(strcat(" gravity: ", ftos(particle_gravity), "\n")); + print(strcat(" bounce: ", ftos(particle_bounce), "\n")); + print(strcat(" airfriction: ", ftos(particle_airfriction), "\n")); + print(strcat(" liquidfriction: ", ftos(particle_liquidfriction), "\n")); + print(strcat(" originjitter: ", ftos(particle_originjitter), "\n")); + print(strcat(" velocityjitter: ", ftos(particle_velocityjitter), "\n")); + print(strcat(" qualityreduction: ", ftos(particle_qualityreduction), "\n")); + print(strcat(" stretch: ", ftos(particle_stretch), "\n")); + print(strcat(" staincolor1: ", vtos(particle_staincolor1), "\n")); + print(strcat(" staincolor2: ", vtos(particle_staincolor2), "\n")); + print(strcat(" staintex: ", ftos(particle_staintex), "\n")); + print(strcat(" stainalpha: ", ftos(particle_stainalpha), "\n")); + print(strcat(" stainsize: ", ftos(particle_stainsize), "\n")); + print(strcat(" delayspawn: ", ftos(particle_delayspawn), "\n")); + print(strcat(" delaycollision: ", ftos(particle_delaycollision), "\n")); + print(strcat(" angle: ", ftos(particle_angle), "\n")); + print(strcat(" spin: ", ftos(particle_spin), "\n")); + } + + // DP_CSQC_ENTITYTRANSPARENTSORTING_OFFSET + // idea: VorteX + // darkplaces implementation: VorteX + float RF_USETRANSPARENTOFFSET = 64; // enables transparent origin offsetting + // global definitions + float transparent_offset; // should be set before entity is added + // description: offset a model's meshes origin used for transparent sorting. Could be used to tweak sorting bugs on very large transparent entities or hacking transparent sorting order for certain objects + // example: transparent_offset = 1000000; // entity always appear on background of other transparents + // note: offset is done in view forward axis + + // DP_CSQC_ENTITYWORLDOBJECT + // idea: VorteX + // darkplaces implementation: VorteX + const float RF_WORLDOBJECT = 128; + // description: when renderflag is set, engine will not use culling methods for this entity, e.g. it will always be drawn + // useful for large outdoor objects (like asteroids on sky horizon or sky models) + + // DP_CSQC_ENTITYMODELLIGHT + // idea: VorteX + // darkplaces implementation: VorteX + const float RF_MODELLIGHT = 4096; + .vector modellight_ambient; + .vector modellight_diffuse; + .vector modellight_dir; + // description: allows CSQC to override directional model lightning on entity + + // DP_CSQC_SETPAUSE + // idea: VorteX + // darkplaces implementation: VorteX + // builtin definitions: + void(float ispaused) setpause = #531; + // description: provides ability to set pause in local games (similar to one set once console is activated) + // not stopping sound/cd track, useful for inventory screens, ingame menus with input etc. + + // DP_CSQC_QUERYRENDERENTITY + // idea: VorteX + // darkplaces implementation: VorteX + // constant definitions: + // render entity fields: + float E_ACTIVE = 0; // float 0/1 + float E_ORIGIN = 1; // vector + float E_FORWARD = 2; // vector + float E_RIGHT = 3; // vector + float E_UP = 4; // vector + float E_SCALE = 5; // float + float E_ORIGINANDVECTORS = 6; // returns origin, + sets v_* vectors to orientation + float E_ALPHA = 7; // float + float E_COLORMOD = 8; // vector + float E_PANTSCOLOR = 9; // vector + float E_SHIRTCOLOR = 10; // vector + float E_SKIN = 11; // float + float E_MINS = 12; // vector + float E_MAXS = 13; // vector + float E_ABSMIN = 14; // vector + float E_ABSMAX = 15; // vector + float E_LIGHT = 16; // vector - modellight + // builtin definitions: + float(float entitynum, float fldnum) getentity = #504; + vector(float entitynum, float fldnum) getentityvec = #504; + // description: allows to query parms from render entities, especially useful with attaching CSQC ents to + // server entities networked and interpolated by engine (monsters, players), number of entity is it's SVQC number + // you can send it via tempentity/CSQC entity message. Note that this builtin doesnt know about entity removing/reallocating + // so it's meaning to work for short period of time, dont use it on missiles/grenades whatever will be removed next five seconds + + //DP_GFX_FONTS + //idea: Blub\0, divVerent + //darkplaces implementation: Blub\0 + //console commands: + // loadfont fontname fontmaps size1 size2 ... + // A font can simply be gfx/tgafile (freetype fonts doent need extension), + // or alternatively you can specify multiple fonts and faces + // Like this: gfx/vera-sans:2,gfx/fallback:1 + // to load face 2 of the font gfx/vera-sans and use face 1 + // of gfx/fallback as fallback font + // You can also specify a list of font sizes to load, like this: + // loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32 + // In many cases, 8 12 16 24 32 should be a good choice. + // for slots see: + //constant definitions: + float drawfont; // set it before drawstring()/drawchar() calls + float FONT_DEFAULT = 0; // 'default' + float FONT_CONSOLE = 1; // 'console', REALLY should be fixed width (ls!) + float FONT_SBAR = 2; // 'sbar', used on hud, must be fixed width + float FONT_NOTIFY = 3; // 'notify', used on sprint/bprint + float FONT_CHAT = 4; // 'chat' + float FONT_CENTERPRINT = 5;// 'centerprint' + float FONT_INFOBAR = 6; // 'infobar' + float FONT_MENU = 7; // 'menu', should be fixed width + float FONT_USER0 = 8; // 'user0', userdefined fonts + float FONT_USER1 = 9; // 'user1', userdefined fonts + float FONT_USER2 = 10; // 'user2', userdefined fonts + float FONT_USER3 = 11; // 'user3', userdefined fonts + float FONT_USER4 = 12; // 'user4', userdefined fonts + float FONT_USER5 = 13; // 'user5', userdefined fonts + float FONT_USER6 = 14; // 'user6', userdefined fonts + float FONT_USER7 = 15; // 'user7' slot, userdefined fonts + //builtin definitions: + float findfont(string s) = #356; // find font by fontname and return it's index + float loadfont(string fontname, string fontmaps, string sizes, float slot, float fix_scale, float fix_voffset) = #357; + // loads font immediately so stringwidth() function can be used just after builtin call + // returns a font slotnum (which is used to set drawfont to) + // first 3 parms are identical to "loadfont" console command ones + // slot could be one of FONT_ constants or result of findfont() or -1 to not use it + // if slot is given, font will be loaded to this slotnum and fontname become new title for it + // this way you can rename user* fonts to something more usable + // fix_* parms let you fix badly made fonts by applying some transformations to them + // fix_scale : per-character center-oriented scale (doesn't change line height at all) + // fix_voffset : vertical offset for each character, it's a multiplier to character height + float stringwidth(string text, float allowColorCodes, vector size) = #327; // get a width of string with given font and char size + float stringwidth_menu(string text, float allowColorCodes, vector size) = #468; // in menu.dat it has different builtin # + //description: engine support for custom fonts in console, hud, qc etc. + // limits: + // max 128 chars for font name + // max 3 font fallbacks + // max 8 sizes per font + + //DP_GFX_FONTS_FREETYPE + //idea: Blub\0, divVerent + //darkplaces implementation: Blub\0 + //cvar definitions: + // r_font_disable_freetype 0/1 : disable freetype fonts loading (uttetly disables freetype library initialization) + // r_font_antialias 0/1 : antialiasing when loading font + // r_font_hint 0/1/2/3 : hinting when loading font, 0 is no hinting, 1 light autohinting , 2 full autohinting, 3 full hinting + // r_font_postprocess_blur X : font outline blur amount + // r_font_postprocess_outline X : font outline width + // r_font_postprocess_shadow_x X : font outline shadow x shift amount, applied during outlining + // r_font_postprocess_shadow_y X : font outline shadow y shift amount, applied during outlining + // r_font_postprocess_shadow_z X : font outline shadow z shift amount, applied during blurring + //description: engine support for truetype/freetype fonts + //so .AFM+.PFB/.OTF/.TTF files could be stuffed as fontmaps in loadfont() + //(console command version will support them as well) + + //DP_CSQC_BINDMAPS + //idea: daemon, motorsep + //darkplaces implementation: divVerent + //builtin definitions: + string(float key, float bindmap) getkeybind_bindmap = #342; + float(float key, string bind, float bindmap) setkeybind_bindmap = #630; + vector(void) getbindmaps = #631; + float(vector bm) setbindmaps = #632; + string(string command, float bindmap) findkeysforcommand = #610; + // float(string key) stringtokeynum = #341; + // string(float keynum) keynumtostring = #340; + //description: key bind setting/getting including support for switchable + //bindmaps. + + //DP_CRYPTO + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: (CSQC) + float(string url, float id, string content_type, string delim, float buf, float keyid) crypto_uri_postbuf = #513; + //description: + //use -1 as buffer handle to justs end delim as postdata + + //DP_CSQC_MAINVIEW + //idea: divVerent + //darkplaces implementation: divVerent + //constant definitions: + const float VF_MAINVIEW = 400; + //use setproperty(VF_MAINVIEW, 1); before calling R_RenderView for the render + //that shall become the "main" view, which is e.g. used by PRYDON_CLIENTCURSOR + //this flag is set for the first scene, and not cleared by R_ClearScene + //this flag is automatically cleared by R_RenderView + //so when not using this extension, the first view rendered is the main view + + //DP_CSQC_MINFPS_QUALITY + //idea: divVerent + //darkplaces implementation: divVerent + //constant definitions: + const float VF_MINFPS_QUALITY = 401; + //use getproperty(VF_MINFPS_QUALITY); to do CSQC based LOD based on cl_minfps + //1 should lead to an unmodified view + + //DP_CSQC_V_CALCREFDEF_WIP1 + //DP_CSQC_V_CALCREFDEF_WIP2 + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: + void(entity e, float refdefflags) V_CalcRefdef = #640; + //constant definitions: + float PMF_DUCKED = 4; + float PMF_ONGROUND = 8; + float REFDEFFLAG_TELEPORTED = 1; + float REFDEFFLAG_JUMPING = 2; + float REFDEFFLAG_DEAD = 4; + float REFDEFFLAG_INTERMISSION = 8; + //- use this on the player entity after performing prediction + //- pass REFDEFFLAG_TELEPORTED if the player teleported since last frame + //- pass REFDEFFLAG_JUMPING if jump button is pressed + //- pass REFDEFFLAG_DEAD if dead (DP_CSQC_V_CALCREFDEF_WIP2) + //- pass REFDEFFLAG_INTERMISSION if in intermission (DP_CSQC_V_CALCREFDEF_WIP2) + //- the player entity needs to have origin, velocity, pmove_flags set according + // to prediction (the above two PMF_ flags are used in the player's pmove_flags) + //- NOTE: to check for this, ALSO OR a check with DP_CSQC_V_CALCREFDEF to also support + // the finished extension once done + + // assorted builtins + float drawsubpic(vector position, vector size, string pic, vector srcPosition, vector srcSize, vector rgb, float alpha, float flag) = #328; + vector drawgetimagesize(string pic) = #318; + #define SPA_POSITION 0 + #define SPA_S_AXIS 1 + #define SPA_T_AXIS 2 + #define SPA_R_AXIS 3 + #define SPA_TEXCOORDS0 4 + #define SPA_LIGHTMAP0_TEXCOORDS 5 + #define SPA_LIGHTMAP_COLOR 6 + // float (entity e, float s) getsurfacenumpoints = #434; + // vector (entity e, float s, float n) getsurfacepoint = #435; + // vector (entity e, float s) getsurfacenormal = #436; + // string (entity e, float s) getsurfacetexture = #437; + // float (entity e, vector p) getsurfacenearpoint = #438; + // vector (entity e, float s, vector p) getsurfaceclippedpoint = #439; + // vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; + float(entity e, float s) getsurfacenumtriangles = #628; + vector(entity e, float s, float n) getsurfacetriangle = #629; + + //DP_QC_ASINACOSATANATAN2TAN + //idea: Urre + //darkplaces implementation: LordHavoc + //constant definitions: + float DEG2RAD = 0.0174532925199432957692369076848861271344287188854172545609719144; + float RAD2DEG = 57.2957795130823208767981548141051703324054724665643215491602438612; + float PI = 3.1415926535897932384626433832795028841971693993751058209749445923; + //builtin definitions: + /* -Wdouble-declaration + float(float s) asin = #471; // returns angle in radians for a given sin() value, the result is in the range -PI*0.5 to PI*0.5 + float(float c) acos = #472; // returns angle in radians for a given cos() value, the result is in the range 0 to PI + float(float t) atan = #473; // returns angle in radians for a given tan() value, the result is in the range -PI*0.5 to PI*0.5 + float(float c, float s) atan2 = #474; // returns angle in radians for a given cos() and sin() value pair, the result is in the range -PI to PI (this is identical to vectoyaw except it returns radians rather than degrees) + float(float a) tan = #475; // returns tangent value (which is simply sin(a)/cos(a)) for the given angle in radians, the result is in the range -infinity to +infinity + */ + //description: + //useful math functions for analyzing vectors, note that these all use angles in radians (just like the cos/sin functions) not degrees unlike makevectors/vectoyaw/vectoangles, so be sure to do the appropriate conversions (multiply by DEG2RAD or RAD2DEG as needed). + //note: atan2 can take unnormalized vectors (just like vectoyaw), and the function was included only for completeness (more often you want vectoyaw or vectoangles), atan2(v_x,v_y) * RAD2DEG gives the same result as vectoyaw(v) + + //DP_QC_SPRINTF + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: + string(string format, ...) sprintf = #627; + //description: + //you know sprintf :P + //supported stuff: + // % + // optional: $ for the argument to format (the arg counter then is not increased) + // flags: #0- + + // optional: , *, or *$ for the field width (width is read before value and precision) + // optional: ., .*, or .*$ for the precision (precision is read before value) + // length modifiers: h for forcing a float, l for forcing an int/entity (by default, %d etc. cast a float to int) + // conversions: + // d takes a float if no length is specified or h is, and an int/entity if l is specified as length, and cast it to an int + // i takes an int/entity if no length is specified or i is, and a float if h is specified as length, and cast it to an int + // ouxXc take a float if no length is specified or h is, and an int/entity if l is specified as length, and cast it to an unsigned int + // eEfFgG take a float if no length is specified or h is, and an int/entity if l is specified as length, and cast it to a double + // s takes a string + // vV takes a vector, and processes the three components as if it were a gG for all three components, separated by space + // For conversions s and c, the flag # makes precision and width interpreted + // as byte count, by default it is interpreted as character count in UTF-8 + // enabled engines. No other conversions can create wide characters, and # + // has another meaning in these. + + //DP_QC_GETTIME + //idea: tZork + //darkplaces implementation: tZork, divVerent + //constant definitions: + float GETTIME_FRAMESTART = 0; // time of start of frame + float GETTIME_REALTIME = 1; // current time (may be OS specific) + float GETTIME_HIRES = 2; // like REALTIME, but may reset between QC invocations and thus can be higher precision + float GETTIME_UPTIME = 3; // time since start of the engine + //builtin definitions: + float(float tmr) gettime = #519; + //description: + //some timers to query... + + //DP_QC_GETTIME_CDTRACK + //idea: divVerent + //darkplaces implementation: divVerent + //constant definitions: + float GETTIME_CDTRACK = 4; + //description: + //returns the playing time of the current cdtrack when passed to gettime() + //see DP_END_GETSOUNDTIME for similar functionality but for entity sound channels + + //DP_QC_TOKENIZEBYSEPARATOR + //idea: Electro, SavageX, LordHavoc + //darkplaces implementation: LordHavoc + //builtin definitions: + float(string s, string separator1, ...) tokenizebyseparator = #479; + //description: + //this function returns tokens separated by any of the supplied separator strings, example: + //numnumbers = tokenizebyseparator("10.2.3.4", "."); + //returns 4 and the tokens are "10" "2" "3" "4" + //possibly useful for parsing IPv4 addresses (such as "1.2.3.4") and IPv6 addresses (such as "[1234:5678:9abc:def0:1234:5678:9abc:def0]:26000") + + //DP_QC_TOKENIZE_CONSOLE + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: + float(string s) tokenize_console = #514; + float(float i) argv_start_index = #515; + float(float i) argv_end_index = #516; + //description: + //this function returns tokens separated just like the console does + //also, functions are provided to get the index of the first and last character of each token in the original string + //Passing negative values to them, or to argv, will be treated as indexes from the LAST token (like lists work in Perl). So argv(-1) will return the LAST token. + + //DP_SND_SOUND7_WIP1 + //DP_SND_SOUND7_WIP2 + //idea: divVerent + //darkplaces implementation: divVerent + //builtin definitions: + void(entity e, float chan, string samp, float vol, float atten, float speed, float flags) sound7 = #8; + float SOUNDFLAG_RELIABLE = 1; + //description: + //plays a sound, with some more flags + //extensions to sound(): + //- channel may be in the range from -128 to 127; channels -128 to 0 are "auto", + // i.e. support multiple sounds at once, but cannot be stopped/restarted + //- a value 0 in the speed parameter means no change; otherwise, it is a + // percentage of playback speed ("pitch shifting"). 100 is normal pitch, 50 is + // half speed, 200 is double speed, etc. (DP_SND_SOUND7_WIP2) + //- the flag SOUNDFLAG_RELIABLE can be specified, which makes the sound send + // to MSG_ALL (reliable) instead of MSG_BROADCAST (unreliable, default); + // similarily, SOUNDFLAG_RELIABLE_TO_ONE sends to MSG_ONE + //- channel 0 is controlled by snd_channel0volume; channel 1 and -1 by + // snd_channel1volume, etc. (so, a channel shares the cvar with its respective + // auto-channel); however, the mod MUST define snd_channel8volume and upwards + // in default.cfg if they are to be used, as the engine does not create them + // to not litter the cvar list + //- this extension applies to CSQC as well; CSQC_Event_Sound will get speed and + // flags as extra 7th and 8th argument + //- WIP2 ideas: SOUNDFLAG_RELIABLE_TO_ONE, SOUNDFLAG_NOPHS, SOUNDFLAG_FORCELOOP + //- NOTE: to check for this, ALSO OR a check with DP_SND_SOUND7 to also support + // the finished extension once done + + //DP_PRECACHE_PIC_FLAGS + //idea: divVerent + //darkplaces implementation: divVerent + //constant definitions: + float PRECACHE_PIC_FROMWAD = 1; // this one actually is part of EXT_CSQC + float PRECACHE_PIC_NOTPERSISTENT = 2; // picture may get deallocated when unused + float PRECACHE_PIC_MIPMAP = 8; // mipmap the texture for possibly better downscaling at memory expense + //notes: these constants are given as optional second argument to precache_pic() + + //DP_QC_TRACE_MOVETYPE_WORLDONLY + //idea: LordHavoc + //darkplaces implementation: LordHavoc + //constant definitions: + float MOVE_WORLDONLY = 3; + //description: + //allows traces to hit only world (ignoring all entities, unlike MOVE_NOMONSTERS which hits all bmodels), use as the nomonsters parameter to trace functions + + //DP_SND_GETSOUNDTIME + //idea: VorteX + //darkplaces implementation: VorteX + //constant definitions: + float(entity e, float channel) getsoundtime = #533; // get currently sound playing position on entity channel, -1 if not playing or error + float(string sample) soundlength = #534; // returns length of sound sample in seconds, -1 on error (sound not precached, sound system not initialized etc.) + //description: provides opportunity to query length of sound samples and realtime tracking of sound playing on entities (similar to DP_GETTIME_CDTRACK) + //note: beware dedicated server not running sound engine at all, so in dedicated mode this builtins will not work in server progs + //note also: menu progs not supporting getsoundtime() (will give a warning) since it has no sound playing on entities + //examples of use: + // - QC-driven looped sounds + // - QC events when sound playing is finished + // - toggleable ambientsounds + // - subtitles + + //DP_QC_NUM_FOR_EDICT + //idea: Blub\0 + //darkplaces implementation: Blub\0 + //Function to get the number of an entity - a clean way. + float(entity num) num_for_edict = #512; + + //DP_TRACE_HITCONTENTSMASK_SURFACEINFO + //idea: LordHavoc + //darkplaces implementation: LordHavoc + //globals: + .float dphitcontentsmask; // if non-zero on the entity passed to traceline/tracebox/tracetoss this will override the normal collidable contents rules and instead hit these contents values (for example AI can use tracelines that hit DONOTENTER if it wants to, by simply changing this field on the entity passed to traceline), this affects normal movement as well as trace calls + float trace_dpstartcontents; // DPCONTENTS_ value at start position of trace + float trace_dphitcontents; // DPCONTENTS_ value of impacted surface (not contents at impact point, just contents of the surface that was hit) + float trace_dphitq3surfaceflags; // Q3SURFACEFLAG_ value of impacted surface + string trace_dphittexturename; // texture name of impacted surface + //constants: + float DPCONTENTS_SOLID = 1; // hit a bmodel, not a bounding box + float DPCONTENTS_WATER = 2; + float DPCONTENTS_SLIME = 4; + float DPCONTENTS_LAVA = 8; + float DPCONTENTS_SKY = 16; + float DPCONTENTS_BODY = 32; // hit a bounding box, not a bmodel + float DPCONTENTS_CORPSE = 64; // hit a SOLID_CORPSE entity + float DPCONTENTS_NODROP = 128; // an area where backpacks should not spawn + float DPCONTENTS_PLAYERCLIP = 256; // blocks player movement + float DPCONTENTS_MONSTERCLIP = 512; // blocks monster movement + float DPCONTENTS_DONOTENTER = 1024; // AI hint brush + float DPCONTENTS_LIQUIDSMASK = 14; // WATER | SLIME | LAVA + float DPCONTENTS_BOTCLIP = 2048; // AI hint brush + float DPCONTENTS_OPAQUE = 4096; // only fully opaque brushes get this (may be useful for line of sight checks) + float Q3SURFACEFLAG_NODAMAGE = 1; + float Q3SURFACEFLAG_SLICK = 2; // low friction surface + float Q3SURFACEFLAG_SKY = 4; // sky surface (also has NOIMPACT and NOMARKS set) + float Q3SURFACEFLAG_LADDER = 8; // climbable surface + float Q3SURFACEFLAG_NOIMPACT = 16; // projectiles should remove themselves on impact (this is set on sky) + float Q3SURFACEFLAG_NOMARKS = 32; // projectiles should not leave marks, such as decals (this is set on sky) + float Q3SURFACEFLAG_FLESH = 64; // projectiles should do a fleshy effect (blood?) on impact + float Q3SURFACEFLAG_NODRAW = 128; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_HINT = 256; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_SKIP = 512; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_NOLIGHTMAP = 1024; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_POINTLIGHT = 2048; // compiler hint (not important to qc) + float Q3SURFACEFLAG_METALSTEPS = 4096; // walking on this surface should make metal step sounds + float Q3SURFACEFLAG_NOSTEPS = 8192; // walking on this surface should not make footstep sounds + float Q3SURFACEFLAG_NONSOLID = 16384; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_LIGHTFILTER = 32768; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_ALPHASHADOW = 65536; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_NODLIGHT = 131072; // compiler hint (not important to qc) + //float Q3SURFACEFLAG_DUST = 262144; // translucent 'light beam' effect (not important to qc) + //description: + //adds additional information after a traceline/tracebox/tracetoss call. + //also (very important) sets trace_* globals before calling .touch functions, + //this allows them to inspect the nature of the collision (for example + //determining if a projectile hit sky), clears trace_* variables for the other + //object in a touch event (that is to say, a projectile moving will see the + //trace results in its .touch function, but the player it hit will see very + //little information in the trace_ variables as it was not moving at the time) + + //DP_QC_CVAR_TYPE + //idea: divVerent + //DarkPlaces implementation: divVerent + //builtin definitions: + float(string name) cvar_type = #495; + float CVAR_TYPEFLAG_EXISTS = 1; + float CVAR_TYPEFLAG_SAVED = 2; + float CVAR_TYPEFLAG_PRIVATE = 4; + float CVAR_TYPEFLAG_ENGINE = 8; + float CVAR_TYPEFLAG_HASDESCRIPTION = 16; + float CVAR_TYPEFLAG_READONLY = 32; + + //DP_QC_CRC16 + //idea: divVerent + //darkplaces implementation: divVerent + //Some hash function to build hash tables with. This has to be be the CRC-16-CCITT that is also required for the QuakeWorld download protocol. + //When caseinsensitive is set, the CRC is calculated of the lower cased string. + float(float caseinsensitive, string s, ...) crc16 = #494; + + //DP_QC_URI_ESCAPE + //idea: divVerent + //darkplaces implementation: divVerent + //URI::Escape's functionality + string(string in) uri_escape = #510; + string(string in) uri_unescape = #511; + + //DP_QC_DIGEST + //idea: motorsep, Spike + //DarkPlaces implementation: divVerent + //builtin definitions: + string(string digest, string data, ...) digest_hex = #639; + //description: + //returns a given hex digest of given data + //the returned digest is always encoded in hexadecimal + //only the "MD4" digest is always supported! + //if the given digest is not supported, string_null is returned + //the digest string is matched case sensitively, use "MD4", not "md4"! + + //DP_QC_DIGEST_SHA256 + //idea: motorsep, Spike + //DarkPlaces implementation: divVerent + //description: + //"SHA256" is also an allowed digest type + + //DP_QC_LOG + //darkplaces implementation: divVerent + //builtin definitions: + float log(float f) = #532; + //description: + //logarithm + + //FTE_CSQC_SKELETONOBJECTS + //idea: Spike, LordHavoc + //darkplaces implementation: LordHavoc + //builtin definitions: + // all skeleton numbers are 1-based (0 being no skeleton) + // all bone numbers are 1-based (0 being invalid) + float(float modlindex) skel_create = #263; // create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex, as the skeleton uses the hierarchy from the model. + float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure + float(float skel) skel_get_numbones = #265; // returns how many bones exist in the created skeleton, 0 if skeleton does not exist + string(float skel, float bonenum) skel_get_bonename = #266; // returns name of bone (as a tempstring), "" if invalid bonenum (< 1 for example) or skeleton does not exist + float(float skel, float bonenum) skel_get_boneparent = #267; // returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this) + float(float skel, string tagname) skel_find_bone = #268; // get number of bone with specified name, 0 on failure, bonenum (1-based) on success, same as using gettagindex but takes modelindex instead of entity + vector(float skel, float bonenum) skel_get_bonerel = #269; // get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone) + vector(float skel, float bonenum) skel_get_boneabs = #270; // get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity) + void(float skel, float bonenum, vector org) skel_set_bone = #271; // set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone) + void(float skel, float bonenum, vector org) skel_mul_bone = #272; // transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone) + void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones) + void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse + void(float skel) skel_delete = #275; // deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work) + float(float modlindex, string framename) frameforname = #276; // finds number of a specified frame in the animation, returns -1 if no match found + float(float modlindex, float framenum) frameduration = #277; // returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0. + //fields: + .float skeletonindex; // active skeleton overriding standard animation on model + .float frame; // primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4) + .float frame2; // secondary framegroup animation (strength = lerpfrac) + .float frame3; // tertiary framegroup animation (strength = lerpfrac3) + .float frame4; // quaternary framegroup animation (strength = lerpfrac4) + .float lerpfrac; // strength of framegroup blend + .float lerpfrac3; // strength of framegroup blend + .float lerpfrac4; // strength of framegroup blend + .float frame1time; // start time of framegroup animation + .float frame2time; // start time of framegroup animation + .float frame3time; // start time of framegroup animation + .float frame4time; // start time of framegroup animation + //description: + //this extension provides a way to do complex skeletal animation on an entity. + // + //see also DP_SKELETONOBJECTS (this extension implemented on server as well as client) + // + //notes: + //each model contains its own skeleton, reusing a skeleton with incompatible models will yield garbage (or not render). + //each model contains its own animation data, you can use animations from other model files (for example saving out all character animations as separate model files). + //if an engine supports loading an animation-only file format such as .md5anim in FTEQW, it can be used to animate any model with a compatible skeleton. + //proper use of this extension may require understanding matrix transforms (v_forward, v_right, v_up, origin), and you must keep in mind that v_right is negative for this purpose. + // + //features include: + //multiple animations blended together. + //animating a model with animations from another model with a compatible skeleton. + //restricting animation blends to certain bones of a model - for example independent animation of legs, torso, head. + //custom bone controllers - for example making eyes track a target location. + // + // + // + //example code follows... + // + //this helper function lets you identify (by parentage) what group a bone + //belongs to - for example "torso", "leftarm", would return 1 ("torso") for + //all children of the bone named "torso", unless they are children of + //"leftarm" (which is a child of "torso") which would return 2 instead... + float(float skel, float bonenum, string g1, string g2, string g3, string g4, string g5, string g6) example_skel_findbonegroup = + { + local string bonename; + while (bonenum >= 0) + { + bonename = skel_get_bonename(skel, bonenum); + if (bonename == g1) return 1; + if (bonename == g2) return 2; + if (bonename == g3) return 3; + if (bonename == g4) return 4; + if (bonename == g5) return 5; + if (bonename == g6) return 6; + bonenum = skel_get_boneparent(skel, bonenum); + } + return 0; + }; + // create a skeletonindex for our player using current modelindex + void() example_skel_player_setup = + { + self.skeletonindex = skel_create(self.modelindex); + }; + // setup bones of skeleton based on an animation + // note: animmodelindex can be a different model than self.modelindex + void(float animmodelindex, float framegroup, float framegroupstarttime) example_skel_player_update_begin = + { + // start with our standard animation + self.frame = framegroup; + self.frame2 = 0; + self.frame3 = 0; + self.frame4 = 0; + self.frame1time = framegroupstarttime; + self.frame2time = 0; + self.frame3time = 0; + self.frame4time = 0; + self.lerpfrac = 0; + self.lerpfrac3 = 0; + self.lerpfrac4 = 0; + skel_build(self.skeletonindex, self, animmodelindex, 0, 0, 100000); + }; + // apply a different framegroup animation to bones with a specified parent + void(float animmodelindex, float framegroup, float framegroupstarttime, float blendalpha, string groupbonename, string excludegroupname1, string excludegroupname2) example_skel_player_update_applyoverride = + { + local float bonenum; + local float numbones; + self.frame = framegroup; + self.frame2 = 0; + self.frame3 = 0; + self.frame4 = 0; + self.frame1time = framegroupstarttime; + self.frame2time = 0; + self.frame3time = 0; + self.frame4time = 0; + self.lerpfrac = 0; + self.lerpfrac3 = 0; + self.lerpfrac4 = 0; + bonenum = 0; + numbones = skel_get_numbones(self.skeletonindex); + while (bonenum < numbones) + { + if (example_skel_findbonegroup(self.skeletonindex, bonenum, groupbonename, excludegroupname1, excludegroupname2, "", "", "") == 1) + skel_build(self.skeletonindex, self, animmodelindex, 1 - blendalpha, bonenum, bonenum + 1); + bonenum = bonenum + 1; + } + }; + // make eyes point at a target location, be sure v_forward, v_right, v_up are set correctly before calling + void(vector eyetarget, string bonename) example_skel_player_update_eyetarget = + { + local float bonenum; + local vector ang; + local vector oldforward, oldright, oldup; + local vector relforward, relright, relup, relorg; + local vector boneforward, boneright, boneup, boneorg; + local vector parentforward, parentright, parentup, parentorg; + local vector u, v; + local vector modeleyetarget; + bonenum = skel_find_bone(self.skeletonindex, bonename) - 1; + if (bonenum < 0) + return; + oldforward = v_forward; + oldright = v_right; + oldup = v_up; + v = eyetarget - self.origin; + modeleyetarget_x = v * v_forward; + modeleyetarget_y = 0-v * v_right; + modeleyetarget_z = v * v_up; + // this is an eyeball, make it point at the target location + // first get all the data we can... + relorg = skel_get_bonerel(self.skeletonindex, bonenum); + relforward = v_forward; + relright = v_right; + relup = v_up; + boneorg = skel_get_boneabs(self.skeletonindex, bonenum); + boneforward = v_forward; + boneright = v_right; + boneup = v_up; + parentorg = skel_get_boneabs(self.skeletonindex, skel_get_boneparent(self.skeletonindex, bonenum)); + parentforward = v_forward; + parentright = v_right; + parentup = v_up; + // get the vector from the eyeball to the target + u = modeleyetarget - boneorg; + // now transform it inversely by the parent matrix to produce new rel vectors + v_x = u * parentforward; + v_y = u * parentright; + v_z = u * parentup; + ang = vectoangles2(v, relup); + ang_x = 0 - ang_x; + makevectors(ang); + // set the relative bone matrix + skel_set_bone(self.skeletonindex, bonenum, relorg); + // restore caller's v_ vectors + v_forward = oldforward; + v_right = oldright; + v_up = oldup; + }; + // delete skeleton when we're done with it + // note: skeleton remains valid until next frame when it is really deleted + void() example_skel_player_delete = + { + skel_delete(self.skeletonindex); + self.skeletonindex = 0; + }; + // + // END OF EXAMPLES FOR FTE_CSQC_SKELETONOBJECTS + // + + //DP_QC_ENTITYDATA + //idea: KrimZon + //darkplaces implementation: KrimZon + //builtin definitions: + float() numentityfields = #496; + string(float fieldnum) entityfieldname = #497; + float(float fieldnum) entityfieldtype = #498; + string(float fieldnum, entity ent) getentityfieldstring = #499; + float(float fieldnum, entity ent, string s) putentityfieldstring = #500; + //constants: + //Returned by entityfieldtype + float FIELD_STRING = 1; + float FIELD_FLOAT = 2; + float FIELD_VECTOR = 3; + float FIELD_ENTITY = 4; + float FIELD_FUNCTION = 6; + //description: + //Versatile functions intended for storing data from specific entities between level changes, but can be customized for some kind of partial savegame. + //WARNING: .entity fields cannot be saved and restored between map loads as they will leave dangling pointers. + //numentityfields returns the number of entity fields. NOT offsets. Vectors comprise 4 fields: v, v_x, v_y and v_z. + //entityfieldname returns the name as a string, eg. "origin" or "classname" or whatever. + //entityfieldtype returns a value that the constants represent, but the field may be of another type in more exotic progs.dat formats or compilers. + //getentityfieldstring returns data as would be written to a savegame, eg... "0.05" (float), "0 0 1" (vector), or "Hello World!" (string). Function names can also be returned. + //putentityfieldstring puts the data returned by getentityfieldstring back into the entity. + + //DP_QC_ENTITYSTRING + void(string s) loadfromdata = #529; + void(string s) loadfromfile = #530; + void(string s) callfunction = #605; + void(float fh, entity e) writetofile = #606; + float(string s) isfunction = #607; + void(entity e, string s) parseentitydata = #608; + + //DP_COVERAGE + //idea: divVerent + //darkplaces implementation: divVerent + //function definitions: + void coverage() = #642; // Reports a coverage event. The engine counts for each of the calls to this builtin whether it has been called. + + // assorted builtins + const float STAT_MOVEVARS_TICRATE = 240; + const float STAT_MOVEVARS_TIMESCALE = 241; + const float STAT_FRAGLIMIT = 235; + const float STAT_TIMELIMIT = 236; + const float STAT_MOVEVARS_GRAVITY = 242; + string(void) ReadPicture = #501; + float PARTICLES_USEALPHA = 1; + float particles_alphamin, particles_alphamax; + float PARTICLES_USECOLOR = 2; + vector particles_colormin, particles_colormax; + float PARTICLES_USEFADE = 4; // fades the COUNT (fade alpha using alphamin/alphamax) + float particles_fade; + float PARTICLES_DRAWASTRAIL = 128; + void(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, float flags) boxparticles = #502; + float trace_networkentity; + const float RF_FULLBRIGHT = 256; + const float RF_NOSHADOW = 512; + float RF_DYNAMICMODELLIGHT = 8192; + + float gettaginfo_parent; + string gettaginfo_name; + vector gettaginfo_offset; + vector gettaginfo_forward; + vector gettaginfo_right; + vector gettaginfo_up; + float checkpvs(vector viewpos, entity viewee) = #240; diff --cc qcsrc/server/bot/havocbot/havocbot.qc index 139926b7e4,dfb615d037..9fa1667099 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ b/qcsrc/server/bot/havocbot/havocbot.qc @@@ -1,9 -1,16 +1,18 @@@ #include "havocbot.qh" - #include "role_onslaught.qc" - #include "role_keyhunt.qc" - #include "roles.qc" + #include "../../_all.qh" + + #include "../aim.qh" + #include "../bot.qh" + #include "../navigation.qh" + #include "../scripting.qh" + #include "../waypoints.qh" + + #include "../../../common/constants.qh" + +#include "../../../common/triggers/trigger/jumppads.qh" + + #include "../../../warpzonelib/common.qh" + void havocbot_ai() { if(self.draggedby) diff --cc qcsrc/server/bot/navigation.qc index 67353587b1,3c3f135cc4..ad0a8d761d --- a/qcsrc/server/bot/navigation.qc +++ b/qcsrc/server/bot/navigation.qc @@@ -1,3 -1,13 +1,14 @@@ + #include "navigation.qh" + #include "../_all.qh" + + #include "bot.qh" + #include "waypoints.qh" + + #include "../t_items.qh" + + #include "../../common/constants.qh" ++#include "../../common/triggers/trigger/jumppads.qh" + void bot_debug(string input) { switch(autocvar_bot_debug) diff --cc qcsrc/server/cheats.qc index b8eaf6e425,d9eb759150..0c6fc7f39d --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@@ -1,29 -1,26 +1,30 @@@ #include "cheats.qh" + #include "_all.qh" + #include "g_damage.qh" #include "race.qh" -#include "t_teleporters.qh" +#include "../common/triggers/teleporters.qh" - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "../warpzonelib/anglestransform.qh" - #include "../warpzonelib/util_server.qh" - #include "../common/constants.qh" - #include "../common/util.qh" - #include "../common/triggers/func/breakable.qh" - #include "../common/monsters/monsters.qh" - #include "../common/weapons/weapons.qh" - #include "weapons/tracing.qh" - #include "autocvars.qh" - #include "defs.qh" - #include "../common/deathtypes.qh" - #include "../common/triggers/subs.qh" - #include "../common/triggers/func/breakable.qh" - #include "mutators/mutators_include.qh" - #include "../csqcmodellib/sv_model.qh" - #endif + #include "mutators/mutators_include.qh" + + #include "weapons/tracing.qh" + + #include "../common/constants.qh" + #include "../common/deathtypes.qh" + #include "../common/util.qh" + + #include "../common/monsters/all.qh" + + #include "../common/weapons/all.qh" + ++#include "../common/triggers/subs.qh" ++ ++#include "../common/triggers/func/breakable.qh" ++ + #include "../csqcmodellib/sv_model.qh" + + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/util_server.qh" void CopyBody(float keepvelocity); diff --cc qcsrc/server/cl_client.qc index d3bb7362e9,b73eebbf4a..33491ff0e7 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@@ -3,12 -6,24 +6,21 @@@ #include "cl_impulse.qh" #include "cl_player.qh" #include "ent_cs.qh" --#include "g_subs.qh" #include "ipban.qh" #include "miscfunctions.qh" #include "portals.qh" #include "teamplay.qh" #include "playerdemo.qh" -#include "secret.qh" + #include "spawnpoints.qh" + #include "g_damage.qh" + #include "g_hook.qh" -#include "t_teleporters.qh" + #include "command/common.qh" + #include "cheats.qh" + #include "g_world.qh" + #include "race.qh" + #include "antilag.qh" + #include "campaign.qh" + #include "command/common.qh" #include "bot/bot.qh" #include "bot/navigation.qh" @@@ -17,12 -34,9 +31,14 @@@ #include "weapons/weaponsystem.qh" #include "../common/net_notice.qh" +#include "../common/physics.qh" + +#include "../common/triggers/subs.qh" +#include "../common/triggers/triggers.qh" +#include "../common/triggers/trigger/secret.qh" + #include "../common/items/inventory.qh" + #include "../common/monsters/sv_monsters.qh" #include "../warpzonelib/server.qh" diff --cc qcsrc/server/cl_player.qc index 58968018b5,059d754bb7..aae39ea66e --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@@ -1,6 -1,23 +1,23 @@@ #include "cl_player.qh" + #include "_all.qh" + + #include "bot/bot.qh" + #include "cheats.qh" + #include "g_damage.qh" + #include "g_subs.qh" -#include "g_triggers.qh" #include "g_violence.qh" #include "miscfunctions.qh" + #include "portals.qh" + #include "teamplay.qh" + #include "waypointsprites.qh" + #include "weapons/throwing.qh" + #include "command/common.qh" + #include "../common/animdecide.qh" + #include "../common/csqcmodel_settings.qh" + #include "../common/deathtypes.qh" ++#include "../common/triggers/subs.qh" + #include "../common/playerstats.qh" + #include "../csqcmodellib/sv_model.qh" #include "weapons/weaponstats.qh" diff --cc qcsrc/server/command/cmd.qc index 14e0f201c0,4548bbf9bb..7471438ec9 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@@ -1,32 -1,40 +1,39 @@@ - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../../dpdefs/progsdefs.qh" - #include "../../dpdefs/dpextensions.qh" - #include "../../warpzonelib/common.qh" - #include "../../common/constants.qh" - #include "../../common/teams.qh" - #include "../../common/util.qh" - #include "../../common/command/shared_defs.qh" - #include "../../common/monsters/monsters.qh" - #include "../../common/monsters/sv_monsters.qh" - #include "../../common/monsters/spawn.qh" - #include "../autocvars.qh" - #include "../defs.qh" - #include "../../common/notifications.qh" - #include "../../common/deathtypes.qh" - #include "../mutators/mutators_include.qh" - #include "../vehicles/vehicles_def.qh" - #include "../campaign.qh" - #include "../../common/mapinfo.qh" - #include "common.qh" - #include "vote.qh" - #include "cmd.qh" - #include "../cheats.qh" - #include "../scores.qh" - #include "../ipban.qh" + #include "../../common/command/command.qh" + #include "cmd.qh" + #include "../_all.qh" + + #include "common.qh" + #include "vote.qh" + + #include "../campaign.qh" + #include "../cheats.qh" + #include "../cl_player.qh" -#include "../g_triggers.qh" + #include "../ipban.qh" + #include "../mapvoting.qh" + #include "../scores.qh" + #include "../teamplay.qh" + + #include "../mutators/mutators_include.qh" + + #ifdef SVQC + #include "../vehicles/vehicle.qh" #endif + #include "../../common/constants.qh" + #include "../../common/deathtypes.qh" + #include "../../common/mapinfo.qh" + #include "../../common/notifications.qh" + #include "../../common/teams.qh" + #include "../../common/util.qh" + + #include "../../common/monsters/all.qh" + #include "../../common/monsters/spawn.qh" + #include "../../common/monsters/sv_monsters.qh" + + #include "../../warpzonelib/common.qh" + + void ClientKill_TeamChange (float targetteam); // 0 = don't change, -1 = auto, -2 = spec + // ========================================================= // Server side networked commands code, reworked by Samual // Last updated: December 28th, 2011 diff --cc qcsrc/server/command/radarmap.qc index 73e5108795,45a4cc7f27..53cab57472 --- a/qcsrc/server/command/radarmap.qc +++ b/qcsrc/server/command/radarmap.qc @@@ -1,13 -1,13 +1,12 @@@ - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../../dpdefs/progsdefs.qh" - #include "../../dpdefs/dpextensions.qh" - #include "../../common/util.qh" - #include "../defs.qh" - #include "radarmap.qh" - #include "../../csqcmodellib/sv_model.qh" - #endif + #include "../../common/command/command.qh" + #include "radarmap.qh" + #include "../_all.qh" + -#include "../g_subs.qh" + #include "../g_world.qh" + + #include "../../common/util.qh" + + #include "../../csqcmodellib/sv_model.qh" // =============================================== // Generates radar map images for use in the HUD diff --cc qcsrc/server/g_damage.qc index 24e1e534f9,24bce642d8..68e8f4f732 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@@ -1,32 -1,26 +1,27 @@@ #include "g_damage.qh" + #include "_all.qh" - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../warpzonelib/common.qh" - #include "../common/constants.qh" - #include "../common/teams.qh" - #include "../common/util.qh" - #include "../common/weapons/weapons.qh" - #include "weapons/accuracy.qh" - #include "weapons/csqcprojectile.qh" - #include "weapons/selection.qh" - #include "t_items.qh" - #include "autocvars.qh" - #include "constants.qh" - #include "defs.qh" - #include "../common/notifications.qh" - #include "../common/deathtypes.qh" - #include "mutators/mutators_include.qh" - #include "tturrets/include/turrets_early.qh" - #include "vehicles/vehicles_def.qh" - #include "../csqcmodellib/sv_model.qh" - #include "../common/playerstats.qh" - #include "g_hook.qh" - #include "scores.qh" - #include "spawnpoints.qh" - #include "../common/movetypes/movetypes.qh" - #endif + #include "g_hook.qh" + #include "mutators/mutators_include.qh" + #include "scores.qh" + #include "waypointsprites.qh" + #include "spawnpoints.qh" + #include "tturrets/include/turrets_early.qh" + #include "t_items.qh" + #include "vehicles/vehicle.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "../common/constants.qh" + #include "../common/deathtypes.qh" + #include "../common/notifications.qh" ++#include "../common/movetypes/movetypes.qh" + #include "../common/playerstats.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/weapons/all.qh" + #include "../csqcmodellib/sv_model.qh" + #include "../warpzonelib/common.qh" float Damage_DamageInfo_SendEntity(entity to, int sf) { diff --cc qcsrc/server/g_hook.qc index 83451ef960,4a3f3101ac..7a127eddab --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@@ -1,22 -1,20 +1,19 @@@ - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "../warpzonelib/common.qh" - #include "../warpzonelib/server.qh" - #include "../common/constants.qh" - #include "../common/util.qh" - #include "../common/weapons/weapons.qh" - #include "autocvars.qh" - #include "constants.qh" - #include "defs.qh" - #include "vehicles/vehicles_def.qh" - #include "command/common.qh" - #include "g_hook.qh" - #include "round_handler.qh" - #include "weapons/common.qh" - #endif + #include "g_hook.qh" + #include "_all.qh" + + #include "weapons/common.qh" + #include "weapons/weaponsystem.qh" + #include "weapons/selection.qh" + #include "weapons/tracing.qh" + #include "cl_player.qh" -#include "t_teleporters.qh" + #include "command/common.qh" + #include "round_handler.qh" + #include "vehicles/vehicle.qh" + #include "../common/constants.qh" + #include "../common/util.qh" + #include "../common/weapons/all.qh" + #include "../warpzonelib/common.qh" + #include "../warpzonelib/server.qh" /*============================================ diff --cc qcsrc/server/g_models.qc index 2693abb388,e25ce66f32..11cf642985 --- a/qcsrc/server/g_models.qc +++ b/qcsrc/server/g_models.qc @@@ -1,15 -1,10 +1,11 @@@ - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "../common/constants.qh" - #include "../common/triggers/subs.qh" - #include "autocvars.qh" - #include "constants.qh" - #include "defs.qh" - #include "../csqcmodellib/sv_model.qh" - #endif + #include "_all.qh" + -#include "g_subs.qh" -#include "g_triggers.qh" ++#include "../common/triggers/subs.qh" ++ ++#include "../client/bgmscript.qh" + + #include "../common/constants.qh" + #include "../csqcmodellib/sv_model.qh" .float modelscale; diff --cc qcsrc/server/g_subs.qc index d42cc6750f,e850b61b3e..eacba64528 --- a/qcsrc/server/g_subs.qc +++ b/qcsrc/server/g_subs.qc @@@ -1,5 -1,12 +1,10 @@@ #include "g_subs.qh" + #include "_all.qh" + + #include "antilag.qh" + #include "command/common.qh" + #include "../warpzonelib/common.qh" -void SUB_NullThink(void) { } - void spawnfunc_info_null (void) { remove(self); diff --cc qcsrc/server/g_world.qc index 2be246bd36,a31670a08a..948367f693 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@@ -1,38 -1,39 +1,38 @@@ #include "g_world.qh" - + #include "_all.qh" + + #include "anticheat.qh" + #include "antilag.qh" + #include "bot/bot.qh" + #include "campaign.qh" + #include "cheats.qh" + #include "cl_client.qh" + #include "command/common.qh" + #include "command/getreplies.qh" + #include "command/sv_cmd.qh" + #include "command/vote.qh" + #include "g_hook.qh" + #include "ipban.qh" + #include "mapvoting.qh" + #include "mutators/mutators_include.qh" + #include "race.qh" + #include "scores.qh" -#include "secret.qh" + #include "teamplay.qh" + #include "waypointsprites.qh" + #include "weapons/weaponstats.qh" #include "../common/buffs.qh" - - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../common/constants.qh" - #include "../common/stats.qh" - #include "../common/teams.qh" - #include "../common/util.qh" - #include "../common/monsters/sv_monsters.qh" - #include "../common/weapons/weapons.qh" - #include "weapons/weaponstats.qh" - #include "autocvars.qh" - #include "constants.qh" - #include "defs.qh" - #include "../common/notifications.qh" - #include "mutators/mutators_include.qh" - #include "campaign.qh" - #include "../common/mapinfo.qh" - #include "command/common.qh" - #include "command/vote.qh" - #include "command/getreplies.qh" - #include "command/sv_cmd.qh" - #include "anticheat.qh" - #include "cheats.qh" - #include "../common/playerstats.qh" - #include "g_hook.qh" - #include "scores.qh" - #include "mapvoting.qh" - #include "ipban.qh" - #include "race.qh" - #include "antilag.qh" - #endif + #include "../common/constants.qh" + #include "../common/deathtypes.qh" + #include "../common/mapinfo.qh" + #include "../common/monsters/all.qh" + #include "../common/monsters/sv_monsters.qh" + #include "../common/notifications.qh" + #include "../common/playerstats.qh" + #include "../common/stats.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/items/all.qh" + #include "../common/weapons/all.qh" const float LATENCY_THINKRATE = 10; .float latency_sum; @@@ -573,10 -575,9 +574,10 @@@ void Nagger_Init() void ClientInit_Spawn(); void WeaponStats_Init(); void WeaponStats_Shutdown(); +void Physics_AddStats(); void spawnfunc_worldspawn (void) { - float fd, l, i, j, n; + float fd, l, j, n; string s; cvar = cvar_normal; diff --cc qcsrc/server/item_key.qc index a130b826c2,1aa054d07e..1518151f2c --- a/qcsrc/server/item_key.qc +++ b/qcsrc/server/item_key.qc @@@ -1,16 -1,10 +1,11 @@@ - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "../warpzonelib/util_server.qh" - #include "../common/util.qh" - #include "../common/monsters/monsters.qh" - #include "../common/triggers/subs.qh" - #include "defs.qh" - #include "../common/notifications.qh" - #include "item_key.qh" - #endif + #include "item_key.qh" + #include "_all.qh" + ++#include "../common/triggers/subs.qh" + #include "../common/monsters/all.qh" + #include "../common/notifications.qh" + #include "../common/util.qh" + #include "../warpzonelib/util_server.qh" /* TODO: diff --cc qcsrc/server/miscfunctions.qc index 63435ab998,e8a1529e4e..e0ea62a1f4 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@@ -1,35 -1,30 +1,30 @@@ - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "miscfunctions.qh" - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "../common/playerstats.qh" - #include "../warpzonelib/anglestransform.qh" - #include "../warpzonelib/server.qh" - #include "../common/constants.qh" - #include "../common/teams.qh" - #include "../common/util.qh" - #include "../common/urllib.qh" - #include "../common/command/generic.qh" - #include "../common/weapons/weapons.qh" - #include "weapons/accuracy.qh" - #include "weapons/csqcprojectile.qh" - #include "weapons/selection.qh" - #include "t_items.qh" - #include "autocvars.qh" - #include "constants.qh" - #include "defs.qh" - #include "../common/notifications.qh" - #include "../common/deathtypes.qh" - #include "../common/triggers/subs.qh" - #include "mutators/mutators_include.qh" - #include "tturrets/include/turrets_early.qh" - #include "../common/mapinfo.qh" - #include "command/common.qh" - #include "../csqcmodellib/sv_model.qh" - #include "ipban.qh" - #endif + #include "miscfunctions.qh" + #include "_all.qh" - + #include "antilag.qh" + #include "command/common.qh" + #include "constants.qh" + #include "g_hook.qh" + #include "ipban.qh" + #include "mutators/mutators_include.qh" + #include "tturrets/include/turrets_early.qh" + #include "t_items.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "../common/command/generic.qh" + #include "../common/constants.qh" + #include "../common/deathtypes.qh" + #include "../common/mapinfo.qh" + #include "../common/notifications.qh" + #include "../common/playerstats.qh" + #include "../common/teams.qh" ++#include "../common/triggers/subs.qh" + #include "../common/urllib.qh" + #include "../common/util.qh" + #include "../common/weapons/all.qh" + #include "../csqcmodellib/sv_model.qh" + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/server.qh" void crosshair_trace(entity pl) { @@@ -1262,7 -1269,8 +1256,7 @@@ void SetCustomizer(entity e, float(void e.uncustomizeentityforclient_set = !!uncustomizer; } - void Net_LinkEntity(entity e, float docull, float dt, bool(entity, int) sendfunc) - + void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc) { vector mi, ma; diff --cc qcsrc/server/mutators/gamemode.qh index 0000000000,101bdece81..32e48f3701 mode 000000,100644..100644 --- a/qcsrc/server/mutators/gamemode.qh +++ b/qcsrc/server/mutators/gamemode.qh @@@ -1,0 -1,49 +1,49 @@@ + #ifndef GAMEMODE_H + #define GAMEMODE_H + + #include "mutator_nades.qh" + + #include "../cl_client.qh" + #include "../cl_player.qh" + #include "../cl_impulse.qh" + #include "../cheats.qh" + #include "../g_damage.qh" -#include "../g_subs.qh" -#include "../t_teleporters.qh" + #include "../round_handler.qh" + #include "../scores.qh" + #include "../scores_rules.qh" + #include "../waypointsprites.qh" + + #include "../bot/bot.qh" + #include "../bot/navigation.qh" + #include "../bot/waypoints.qh" + #include "../bot/havocbot/roles.qh" + #include "../bot/havocbot/role_keyhunt.qh" + + #include "../bot/havocbot/havocbot.qh" + + #include "../command/vote.qh" + + #include "../../common/monsters/all.qh" + + #include "../command/common.qh" + + #include "../weapons/tracing.qh" + #include "../weapons/weaponsystem.qh" + + #include "../../common/deathtypes.qh" + #include "../../common/notifications.qh" ++#include "../../common/triggers/teleporters.qh" ++#include "../../common/triggers/subs.qh" + #include "../../common/stats.qh" + #include "../../common/teams.qh" + + #include "../../warpzonelib/mathlib.qh" + #include "../../warpzonelib/server.qh" + #include "../../warpzonelib/util_server.qh" + + .float lastground; + float total_players; + float redalive, bluealive, yellowalive, pinkalive; + .float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat; + + #endif diff --cc qcsrc/server/mutators/gamemode_ctf.qc index c97d2a2610,d8a4e357bb..c2189155c4 --- a/qcsrc/server/mutators/gamemode_ctf.qc +++ b/qcsrc/server/mutators/gamemode_ctf.qc @@@ -1,5 -1,20 +1,15 @@@ - #include "../../common/movetypes/movetypes.qh" + #include "gamemode_ctf.qh" + #include "../_all.qh" + + #include "gamemode.qh" + + #ifdef SVQC + #include "../vehicles/vehicle.qh" + #endif + + #include "../../warpzonelib/common.qh" + #include "../../warpzonelib/mathlib.qh" -// ================================================================ -// Official capture the flag game mode coding, reworked by Samual -// Last updated: September, 2012 -// ================================================================ - void ctf_FakeTimeLimit(entity e, float t) { msg_entity = e; diff --cc qcsrc/server/mutators/gamemode_ctf.qh index faf12a4fe7,302f6ce104..b183a2ae58 --- a/qcsrc/server/mutators/gamemode_ctf.qh +++ b/qcsrc/server/mutators/gamemode_ctf.qh @@@ -71,28 -71,30 +71,30 @@@ float wpforenemy_announced float wpforenemy_nextthink; // statuses -const float FLAG_BASE = 1; -const float FLAG_DROPPED = 2; -const float FLAG_CARRY = 3; -const float FLAG_PASSING = 4; +const int FLAG_BASE = 1; +const int FLAG_DROPPED = 2; +const int FLAG_CARRY = 3; +const int FLAG_PASSING = 4; -const float DROP_NORMAL = 1; -const float DROP_THROW = 2; -const float DROP_PASS = 3; -const float DROP_RESET = 4; +const int DROP_NORMAL = 1; +const int DROP_THROW = 2; +const int DROP_PASS = 3; +const int DROP_RESET = 4; -const float PICKUP_BASE = 1; -const float PICKUP_DROPPED = 2; +const int PICKUP_BASE = 1; +const int PICKUP_DROPPED = 2; -const float CAPTURE_NORMAL = 1; -const float CAPTURE_DROPPED = 2; +const int CAPTURE_NORMAL = 1; +const int CAPTURE_DROPPED = 2; -const float RETURN_TIMEOUT = 1; -const float RETURN_DROPPED = 2; -const float RETURN_DAMAGE = 3; -const float RETURN_SPEEDRUN = 4; -const float RETURN_NEEDKILL = 5; +const int RETURN_TIMEOUT = 1; +const int RETURN_DROPPED = 2; +const int RETURN_DAMAGE = 3; +const int RETURN_SPEEDRUN = 4; +const int RETURN_NEEDKILL = 5; + void ctf_Handle_Throw(entity player, entity receiver, float droptype); + // flag properties #define ctf_spawnorigin dropped_origin float ctf_stalemate; // indicates that a stalemate is active diff --cc qcsrc/server/mutators/gamemode_nexball.qc index afdb769cf6,4c081ab445..9844875b3d --- a/qcsrc/server/mutators/gamemode_nexball.qc +++ b/qcsrc/server/mutators/gamemode_nexball.qc @@@ -1,3 -1,10 +1,8 @@@ + #include "gamemode_nexball.qh" + #include "../_all.qh" + + #include "gamemode.qh" + -#include "../t_jumppads.qh" - float autocvar_g_nexball_safepass_turnrate; float autocvar_g_nexball_safepass_maxdist; float autocvar_g_nexball_safepass_holdtime; diff --cc qcsrc/server/mutators/mutator.qh index 0000000000,d7474fab45..36bf631bbd mode 000000,100644..100644 --- a/qcsrc/server/mutators/mutator.qh +++ b/qcsrc/server/mutators/mutator.qh @@@ -1,0 -1,47 +1,47 @@@ + #ifndef MUTATOR_H + #define MUTATOR_H + + #include "base.qh" + #include "mutator_nades.qh" + + #include "../cl_client.qh" + #include "../cl_player.qh" + #include "../cl_impulse.qh" + #include "../cheats.qh" + #include "../g_damage.qh" -#include "../g_subs.qh" -#include "../t_teleporters.qh" + #include "../round_handler.qh" + #include "../scores.qh" + #include "../scores_rules.qh" + #include "../waypointsprites.qh" + + #include "../bot/bot.qh" + #include "../bot/navigation.qh" + #include "../bot/waypoints.qh" + + #include "../bot/havocbot/havocbot.qh" + #include "../bot/havocbot/roles.qh" + #include "../bot/havocbot/role_keyhunt.qh" + + #include "../command/vote.qh" + #include "../command/common.qh" + + #include "../weapons/common.qh" + #include "../weapons/tracing.qh" + #include "../weapons/throwing.qh" + #include "../weapons/weaponsystem.qh" + + #include "../../common/deathtypes.qh" + #include "../../common/notifications.qh" ++#include "../../common/triggers/teleporters.qh" ++#include "../../common/triggers/subs.qh" + #include "../../common/stats.qh" + #include "../../common/teams.qh" + + #include "../../common/monsters/all.qh" + + #include "../../warpzonelib/anglestransform.qh" + #include "../../warpzonelib/mathlib.qh" + #include "../../warpzonelib/server.qh" + #include "../../warpzonelib/util_server.qh" + + #endif diff --cc qcsrc/server/mutators/mutator_dodging.qc index 130a864089,4537d04761..ab70fb217d --- a/qcsrc/server/mutators/mutator_dodging.qc +++ b/qcsrc/server/mutators/mutator_dodging.qc @@@ -1,34 -1,9 +1,40 @@@ +#ifdef CSQC + #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime)) + #define PHYS_DODGING getstati(STAT_DODGING) + #define PHYS_DODGING_DELAY getstatf(STAT_DODGING_DELAY) + #define PHYS_DODGING_TIMEOUT(s) getstatf(STAT_DODGING_TIMEOUT) + #define PHYS_DODGING_HORIZ_SPEED_FROZEN getstatf(STAT_DODGING_HORIZ_SPEED_FROZEN) + #define PHYS_DODGING_FROZEN_NODOUBLETAP getstati(STAT_DODGING_FROZEN_NO_DOUBLETAP) + #define PHYS_DODGING_HORIZ_SPEED getstatf(STAT_DODGING_HORIZ_SPEED) + #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys + #define PHYS_DODGING_HEIGHT_THRESHOLD getstatf(STAT_DODGING_HEIGHT_THRESHOLD) + #define PHYS_DODGING_DISTANCE_THRESHOLD getstatf(STAT_DODGING_DISTANCE_THRESHOLD) + #define PHYS_DODGING_RAMP_TIME getstatf(STAT_DODGING_RAMP_TIME) + #define PHYS_DODGING_UP_SPEED getstatf(STAT_DODGING_UP_SPEED) + #define PHYS_DODGING_WALL getstatf(STAT_DODGING_WALL) +#elif defined(SVQC) + #define PHYS_DODGING_FRAMETIME sys_frametime + #define PHYS_DODGING g_dodging + #define PHYS_DODGING_DELAY autocvar_sv_dodging_delay + #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout + #define PHYS_DODGING_HORIZ_SPEED_FROZEN autocvar_sv_dodging_horiz_speed_frozen + #define PHYS_DODGING_FROZEN_NODOUBLETAP autocvar_sv_dodging_frozen_doubletap + #define PHYS_DODGING_HORIZ_SPEED autocvar_sv_dodging_horiz_speed + #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys + #define PHYS_DODGING_HEIGHT_THRESHOLD autocvar_sv_dodging_height_threshold + #define PHYS_DODGING_DISTANCE_THRESHOLD autocvar_sv_dodging_wall_distance_threshold + #define PHYS_DODGING_RAMP_TIME autocvar_sv_dodging_ramp_time + #define PHYS_DODGING_UP_SPEED autocvar_sv_dodging_up_speed + #define PHYS_DODGING_WALL autocvar_sv_dodging_wall_dodging +#endif + +#ifdef SVQC + #include "mutator_dodging.qh" + #include "../_all.qh" + + #include "mutator.qh" + + #include "../../common/animdecide.qh" .float cvar_cl_dodging_timeout; diff --cc qcsrc/server/mutators/mutator_multijump.qc index c2847c625f,235845184c..dd879e5de4 --- a/qcsrc/server/mutators/mutator_multijump.qc +++ b/qcsrc/server/mutators/mutator_multijump.qc @@@ -1,3 -1,9 +1,9 @@@ -#include "../_all.qh" - -#include "mutator.qh" - -#include "../antilag.qh" ++#ifdef SVQC ++ #include "../_all.qh" ++ #include "mutator.qh" ++ #include "../antilag.qh" ++#endif + .float multijump_count; .float multijump_ready; diff --cc qcsrc/server/mutators/mutators_include.qc index c3bdec50c9,4debaa5a88..e139865ab5 --- a/qcsrc/server/mutators/mutators_include.qc +++ b/qcsrc/server/mutators/mutators_include.qc @@@ -74,9 -74,10 +74,9 @@@ #include "../playerdemo.qh" #include "../round_handler.qh" #include "../item_key.qh" - #include "../secret.qh" #include "../pathlib/pathlib.qh" #include "../tturrets/include/turrets.qh" - #include "../vehicles/vehicles.qh" + #include "../vehicles/all.qh" #endif #include "base.qc" diff --cc qcsrc/server/pathlib.qc index 2f66441f7a,cbc43240de..bfd9c0b35f --- a/qcsrc/server/pathlib.qc +++ b/qcsrc/server/pathlib.qc @@@ -1,12 -1,8 +1,6 @@@ - //#define PATHLIB_RDFIELDS - #ifdef PATHLIB_RDFIELDS - #define path_next swampslug - #define path_prev lasertarget - #else - .entity path_next; - .entity path_prev; - #endif + #include "pathlib.qh" + #include "_all.qh" -#include "g_subs.qh" - #define medium spawnshieldtime //#define DEBUGPATHING diff --cc qcsrc/server/portals.qc index 8ea9dd2bc6,cb671ddc70..ad8aea50cd --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@@ -1,22 -1,18 +1,18 @@@ - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "../warpzonelib/anglestransform.qh" - #include "../warpzonelib/util_server.qh" - #include "../common/constants.qh" - #include "../common/util.qh" - #include "../common/weapons/weapons.qh" - #include "autocvars.qh" - #include "defs.qh" - #include "../common/notifications.qh" - #include "../common/deathtypes.qh" - #include "mutators/mutators_include.qh" - #include "../csqcmodellib/sv_model.qh" - #include "portals.qh" - #include "g_hook.qh" - #endif + #include "portals.qh" + #include "_all.qh" + + #include "g_hook.qh" -#include "g_subs.qh" + #include "mutators/mutators_include.qh" -#include "t_teleporters.qh" + #include "../common/constants.qh" + #include "../common/deathtypes.qh" + #include "../common/notifications.qh" ++#include "../common/triggers/teleporters.qh" ++#include "../common/triggers/subs.qh" + #include "../common/util.qh" + #include "../common/weapons/all.qh" + #include "../csqcmodellib/sv_model.qh" + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/util_server.qh" #define PORTALS_ARE_NOT_SOLID diff --cc qcsrc/server/progs.src index 9cab150c33,75301d5929..ab87642aaa --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@@ -13,11 -12,11 +12,9 @@@ campaign.q cheats.qc cl_client.qc cl_impulse.qc -cl_physics.qc cl_player.qc csqceffects.qc - // ctf.qc - // domination.qc ent_cs.qc -func_breakable.qc g_casings.qc g_damage.qc g_hook.qc @@@ -37,28 -37,37 +34,30 @@@ playerdemo.q portals.qc race.qc round_handler.qc - // runematch.qc scores.qc scores_rules.qc -secret.qc spawnpoints.qc steerlib.qc sv_main.qc teamplay.qc t_halflife.qc t_items.qc -t_jumppads.qc -t_plats.qc -t_quake.qc t_quake3.qc -t_swamp.qc -t_teleporters.qc +t_quake.qc waypointsprites.qc + bot/aim.qc bot/bot.qc + bot/navigation.qc + bot/scripting.qc + bot/waypoints.qc - command/banning.qc - command/cmd.qc - command/common.qc - command/getreplies.qc - command/radarmap.qc - command/sv_cmd.qc - command/vote.qc + bot/havocbot/havocbot.qc + bot/havocbot/role_keyhunt.qc + bot/havocbot/role_onslaught.qc + bot/havocbot/roles.qc + + command/all.qc mutators/mutators_include.qc mutators/mutators.qc @@@ -78,25 -96,22 +86,25 @@@ weapons/weaponsystem.q ../common/buffs.qc ../common/campaign_file.qc ../common/campaign_setup.qc - ../common/command/generic.qc - ../common/command/markup.qc - ../common/command/rpn.qc ../common/mapinfo.qc - ../common/monsters/monsters.qc + ../common/monsters/all.qc ../common/monsters/spawn.qc ../common/monsters/sv_monsters.qc +../common/movetypes/include.qc ../common/nades.qc ../common/net_notice.qc ../common/notifications.qc +../common/physics.qc ../common/playerstats.qc ../common/test.qc +../common/triggers/include.qc ../common/urllib.qc ../common/util.qc + + ../common/items/all.qc + ../common/weapons/config.qc - ../common/weapons/weapons.qc // TODO + ../common/weapons/all.qc // TODO ../csqcmodellib/sv_model.qc diff --cc qcsrc/server/t_items.qc index 75ba1881b2,f817957c82..4e3e376e3b --- a/qcsrc/server/t_items.qc +++ b/qcsrc/server/t_items.qc @@@ -1,32 -1,32 +1,32 @@@ - #if defined(CSQC) - #include "../dpdefs/csprogsdefs.qh" - #include "../common/util.qh" - #include "../common/buffs.qh" - #include "../common/weapons/weapons.qh" - #include "../client/autocvars.qh" - #include "../common/movetypes/movetypes.qh" - #include "../client/main.qh" - #include "../csqcmodellib/common.qh" - #include "../csqcmodellib/cl_model.qh" - #include "t_items.qh" - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "../warpzonelib/util_server.qh" - #include "../common/constants.qh" - #include "../common/util.qh" - #include "../common/monsters/monsters.qh" - #include "../common/triggers/subs.qh" - #include "../common/weapons/weapons.qh" + #include "t_items.qh" + + #include "../common/items/all.qc" + + #if defined(SVQC) + #include "_all.qh" + - #include "g_subs.qh" + #include "waypointsprites.qh" + + #include "bot/bot.qh" + #include "bot/waypoints.qh" + + #include "mutators/mutators_include.qh" + + #include "weapons/common.qh" + #include "weapons/selection.qh" #include "weapons/weaponsystem.qh" - #include "t_items.qh" - #include "autocvars.qh" - #include "constants.qh" - #include "defs.qh" - #include "../common/notifications.qh" + + #include "../common/constants.qh" #include "../common/deathtypes.qh" - #include "mutators/mutators_include.qh" + #include "../common/notifications.qh" ++ #include "../common/triggers/subs.qh" + #include "../common/util.qh" + + #include "../common/monsters/all.qh" + + #include "../common/weapons/all.qh" + + #include "../warpzonelib/util_server.qh" #endif #ifdef CSQC diff --cc qcsrc/server/t_plats.qc index 0000000000,40d6a4d436..b93b1d5777 mode 000000,100644..100644 --- a/qcsrc/server/t_plats.qc +++ b/qcsrc/server/t_plats.qc @@@ -1,0 -1,2277 +1,2276 @@@ + #include "_all.qh" + + #include "bot/bot.qh" + + #include "command/common.qh" + + #include "g_damage.qh" -#include "g_subs.qh" + #include "item_key.qh" + + #include "../common/constants.qh" + #include "../common/deathtypes.qh" + #include "../common/notifications.qh" + #include "../common/util.qh" + + #include "../common/weapons/all.qh" + + #include "../csqcmodellib/sv_model.qh" + + #include "../warpzonelib/common.qh" + #include "../warpzonelib/mathlib.qh" + #include "../warpzonelib/util_server.qh" + + .float height; + + .float dmgtime2; + void generic_plat_blocked() + { + if(self.dmg && other.takedamage != DAMAGE_NO) { + if(self.dmgtime2 < time) { + Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + self.dmgtime2 = time + self.dmgtime; + } + + // Gib dead/dying stuff + if(other.deadflag != DEAD_NO) + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } + } + + + .entity trigger_field; + + void() plat_center_touch; + void() plat_outside_touch; + void() plat_trigger_use; + void() plat_go_up; + void() plat_go_down; + void() plat_crush; + const float PLAT_LOW_TRIGGER = 1; + + void plat_spawn_inside_trigger() + { + entity trigger; + vector tmin, tmax; + + trigger = spawn(); + trigger.touch = plat_center_touch; + trigger.movetype = MOVETYPE_NONE; + trigger.solid = SOLID_TRIGGER; + trigger.enemy = self; + + tmin = self.absmin + '25 25 0'; + tmax = self.absmax - '25 25 -8'; + tmin.z = tmax.z - (self.pos1_z - self.pos2_z + 8); + if (self.spawnflags & PLAT_LOW_TRIGGER) + tmax.z = tmin.z + 8; + + if (self.size.x <= 50) + { + tmin.x = (self.mins.x + self.maxs.x) / 2; + tmax.x = tmin.x + 1; + } + if (self.size.y <= 50) + { + tmin.y = (self.mins.y + self.maxs.y) / 2; + tmax.y = tmin.y + 1; + } + + if(tmin.x < tmax.x) + if(tmin.y < tmax.y) + if(tmin.z < tmax.z) + { + setsize (trigger, tmin, tmax); + return; + } + + // otherwise, something is fishy... + remove(trigger); + objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn"); + } + + void plat_hit_top() + { + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = 1; + self.think = plat_go_down; + self.nextthink = self.ltime + 3; + } + + void plat_hit_bottom() + { + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = 2; + } + + void plat_go_down() + { + sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM); + self.state = 3; + SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, plat_hit_bottom); + } + + void plat_go_up() + { + sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_NORM); + self.state = 4; + SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, plat_hit_top); + } + + void plat_center_touch() + { + if (!other.iscreature) + return; + + if (other.health <= 0) + return; + + self = self.enemy; + if (self.state == 2) + plat_go_up (); + else if (self.state == 1) + self.nextthink = self.ltime + 1; // delay going down + } + + void plat_outside_touch() + { + if (!other.iscreature) + return; + + if (other.health <= 0) + return; + + self = self.enemy; + if (self.state == 1) + plat_go_down (); + } + + void plat_trigger_use() + { + if (self.think) + return; // already activated + plat_go_down(); + } + + + void plat_crush() + { + if((self.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!! + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } else { + if((self.dmg) && (other.takedamage != DAMAGE_NO)) { // Shall we bite? + Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + // Gib dead/dying stuff + if(other.deadflag != DEAD_NO) + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } + + if (self.state == 4) + plat_go_down (); + else if (self.state == 3) + plat_go_up (); + // when in other states, then the plat_crush event came delayed after + // plat state already had changed + // this isn't a bug per se! + } + } + + void plat_use() + { + self.use = func_null; + if (self.state != 4) + objerror ("plat_use: not in up state"); + plat_go_down(); + } + + .string sound1, sound2; + + void plat_reset() + { + IFTARGETED + { + setorigin (self, self.pos1); + self.state = 4; + self.use = plat_use; + } + else + { + setorigin (self, self.pos2); + self.state = 2; + self.use = plat_trigger_use; + } + } + + .float platmovetype_start_default, platmovetype_end_default; + float set_platmovetype(entity e, string s) + { + // sets platmovetype_start and platmovetype_end based on a string consisting of two values + + float n; + n = tokenize_console(s); + if(n > 0) + e.platmovetype_start = stof(argv(0)); + else + e.platmovetype_start = 0; + + if(n > 1) + e.platmovetype_end = stof(argv(1)); + else + e.platmovetype_end = e.platmovetype_start; + + if(n > 2) + if(argv(2) == "force") + return true; // no checking, return immediately + + if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end)) + { + objerror("Invalid platform move type; platform would go in reverse, which is not allowed."); + return false; + } + + return true; + } + + void spawnfunc_path_corner() + { + // setup values for overriding train movement + // if a second value does not exist, both start and end speeds are the single value specified + if(!set_platmovetype(self, self.platmovetype)) + return; + } + void spawnfunc_func_plat() + { + if (self.sounds == 0) + self.sounds = 2; + + if(self.spawnflags & 4) + self.dmg = 10000; + + if(self.dmg && (self.message == "")) + self.message = "was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + + if (self.sounds == 1) + { + precache_sound ("plats/plat1.wav"); + precache_sound ("plats/plat2.wav"); + self.noise = "plats/plat1.wav"; + self.noise1 = "plats/plat2.wav"; + } + + if (self.sounds == 2) + { + precache_sound ("plats/medplat1.wav"); + precache_sound ("plats/medplat2.wav"); + self.noise = "plats/medplat1.wav"; + self.noise1 = "plats/medplat2.wav"; + } + + if (self.sound1) + { + precache_sound (self.sound1); + self.noise = self.sound1; + } + if (self.sound2) + { + precache_sound (self.sound2); + self.noise1 = self.sound2; + } + + self.mangle = self.angles; + self.angles = '0 0 0'; + + self.classname = "plat"; + if (!InitMovingBrushTrigger()) + return; + self.effects |= EF_LOWPRECISION; + setsize (self, self.mins , self.maxs); + + self.blocked = plat_crush; + + if (!self.speed) + self.speed = 150; + if (!self.lip) + self.lip = 16; + if (!self.height) + self.height = self.size.z - self.lip; + + self.pos1 = self.origin; + self.pos2 = self.origin; + self.pos2_z = self.origin.z - self.height; + + self.reset = plat_reset; + plat_reset(); + + plat_spawn_inside_trigger (); // the "start moving" trigger + } + + .float train_wait_turning; + void() train_next; + void train_wait() + { + entity oldself; + oldself = self; + self = self.enemy; + SUB_UseTargets(); + self = oldself; + self.enemy = world; + + // if turning is enabled, the train will turn toward the next point while waiting + if(self.platmovetype_turn && !self.train_wait_turning) + { + entity targ, cp; + vector ang; + targ = find(world, targetname, self.target); + if((self.spawnflags & 1) && targ.curvetarget) + cp = find(world, targetname, targ.curvetarget); + else + cp = world; + + if(cp) // bezier curves movement + ang = cp.origin - (self.origin - self.view_ofs); // use the origin of the control point of the next path_corner + else // linear movement + ang = targ.origin - (self.origin - self.view_ofs); // use the origin of the next path_corner + ang = vectoangles(ang); + ang.x = -ang.x; // flip up / down orientation + + if(self.wait > 0) // slow turning + SUB_CalcAngleMove(ang, TSPEED_TIME, self.ltime - time + self.wait, train_wait); + else // instant turning + SUB_CalcAngleMove(ang, TSPEED_TIME, 0.0000001, train_wait); + self.train_wait_turning = true; + return; + } + + if(self.noise != "") + stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway + + if(self.wait < 0 || self.train_wait_turning) // no waiting or we already waited while turning + { + self.train_wait_turning = false; + train_next(); + } + else + { + self.think = train_next; + self.nextthink = self.ltime + self.wait; + } + } + + void train_next() + { + entity targ, cp = world; + vector cp_org = '0 0 0'; + + targ = find(world, targetname, self.target); + self.target = targ.target; + if (self.spawnflags & 1) + { + if(targ.curvetarget) + { + cp = find(world, targetname, targ.curvetarget); // get its second target (the control point) + cp_org = cp.origin - self.view_ofs; // no control point found, assume a straight line to the destination + } + } + if (self.target == "") + objerror("train_next: no next target"); + self.wait = targ.wait; + if (!self.wait) + self.wait = 0.1; + + if(targ.platmovetype) + { + // this path_corner contains a movetype overrider, apply it + self.platmovetype_start = targ.platmovetype_start; + self.platmovetype_end = targ.platmovetype_end; + } + else + { + // this path_corner doesn't contain a movetype overrider, use the train's defaults + self.platmovetype_start = self.platmovetype_start_default; + self.platmovetype_end = self.platmovetype_end_default; + } + + if (targ.speed) + { + if (cp) + SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); + else + SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); + } + else + { + if (cp) + SUB_CalcMove_Bezier(cp_org, targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait); + else + SUB_CalcMove(targ.origin - self.view_ofs, TSPEED_LINEAR, self.speed, train_wait); + } + + if(self.noise != "") + sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE); + } + + void func_train_find() + { + entity targ; + targ = find(world, targetname, self.target); + self.target = targ.target; + if (self.target == "") + objerror("func_train_find: no next target"); + setorigin(self, targ.origin - self.view_ofs); + self.nextthink = self.ltime + 1; + self.think = train_next; + } + + /*QUAKED spawnfunc_func_train (0 .5 .8) ? + Ridable platform, targets spawnfunc_path_corner path to follow. + speed : speed the train moves (can be overridden by each spawnfunc_path_corner) + target : targetname of first spawnfunc_path_corner (starts here) + */ + void spawnfunc_func_train() + { + if (self.noise != "") + precache_sound(self.noise); + + if (self.target == "") + objerror("func_train without a target"); + if (!self.speed) + self.speed = 100; + + if (!InitMovingBrushTrigger()) + return; + self.effects |= EF_LOWPRECISION; + + if (self.spawnflags & 2) + { + self.platmovetype_turn = true; + self.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now + } + else + self.view_ofs = self.mins; + + // wait for targets to spawn + InitializeEntity(self, func_train_find, INITPRIO_SETLOCATION); + + self.blocked = generic_plat_blocked; + if(self.dmg && (self.message == "")) + self.message = " was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + if(self.dmg && (!self.dmgtime)) + self.dmgtime = 0.25; + self.dmgtime2 = time; + + if(!set_platmovetype(self, self.platmovetype)) + return; + self.platmovetype_start_default = self.platmovetype_start; + self.platmovetype_end_default = self.platmovetype_end; + + // TODO make a reset function for this one + } + + void func_rotating_setactive(float astate) + { + + if (astate == ACTIVE_TOGGLE) + { + if(self.active == ACTIVE_ACTIVE) + self.active = ACTIVE_NOT; + else + self.active = ACTIVE_ACTIVE; + } + else + self.active = astate; + + if(self.active == ACTIVE_NOT) + self.avelocity = '0 0 0'; + else + self.avelocity = self.pos1; + } + + /*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS + Brush model that spins in place on one axis (default Z). + speed : speed to rotate (in degrees per second) + noise : path/name of looping .wav file to play. + dmg : Do this mutch dmg every .dmgtime intervall when blocked + dmgtime : See above. + */ + + void spawnfunc_func_rotating() + { + if (self.noise != "") + { + precache_sound(self.noise); + ambientsound(self.origin, self.noise, VOL_BASE, ATTEN_IDLE); + } + + self.active = ACTIVE_ACTIVE; + self.setactive = func_rotating_setactive; + + if (!self.speed) + self.speed = 100; + // FIXME: test if this turns the right way, then remove this comment (negate as needed) + if (self.spawnflags & 4) // X (untested) + self.avelocity = '0 0 1' * self.speed; + // FIXME: test if this turns the right way, then remove this comment (negate as needed) + else if (self.spawnflags & 8) // Y (untested) + self.avelocity = '1 0 0' * self.speed; + // FIXME: test if this turns the right way, then remove this comment (negate as needed) + else // Z + self.avelocity = '0 1 0' * self.speed; + + self.pos1 = self.avelocity; + + if(self.dmg && (self.message == "")) + self.message = " was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + + + if(self.dmg && (!self.dmgtime)) + self.dmgtime = 0.25; + + self.dmgtime2 = time; + + if (!InitMovingBrushTrigger()) + return; + // no EF_LOWPRECISION here, as rounding angles is bad + + self.blocked = generic_plat_blocked; + + // wait for targets to spawn + self.nextthink = self.ltime + 999999999; + self.think = SUB_NullThink; // for PushMove + + // TODO make a reset function for this one + } + + .float height; + void func_bobbing_controller_think() + { + vector v; + self.nextthink = time + 0.1; + + if(self.owner.active != ACTIVE_ACTIVE) + { + self.owner.velocity = '0 0 0'; + return; + } + + // calculate sinewave using makevectors + makevectors((self.nextthink * self.owner.cnt + self.owner.phase * 360) * '0 1 0'); + v = self.owner.destvec + self.owner.movedir * v_forward.y; + if(self.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed + // * 10 so it will arrive in 0.1 sec + self.owner.velocity = (v - self.owner.origin) * 10; + } + + /*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS + Brush model that moves back and forth on one axis (default Z). + speed : how long one cycle takes in seconds (default 4) + height : how far the cycle moves (default 32) + phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0) + noise : path/name of looping .wav file to play. + dmg : Do this mutch dmg every .dmgtime intervall when blocked + dmgtime : See above. + */ + void spawnfunc_func_bobbing() + { + entity controller; + if (self.noise != "") + { + precache_sound(self.noise); + soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE); + } + if (!self.speed) + self.speed = 4; + if (!self.height) + self.height = 32; + // center of bobbing motion + self.destvec = self.origin; + // time scale to get degrees + self.cnt = 360 / self.speed; + + self.active = ACTIVE_ACTIVE; + + // damage when blocked + self.blocked = generic_plat_blocked; + if(self.dmg && (self.message == "")) + self.message = " was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + if(self.dmg && (!self.dmgtime)) + self.dmgtime = 0.25; + self.dmgtime2 = time; + + // how far to bob + if (self.spawnflags & 1) // X + self.movedir = '1 0 0' * self.height; + else if (self.spawnflags & 2) // Y + self.movedir = '0 1 0' * self.height; + else // Z + self.movedir = '0 0 1' * self.height; + + if (!InitMovingBrushTrigger()) + return; + + // wait for targets to spawn + controller = spawn(); + controller.classname = "func_bobbing_controller"; + controller.owner = self; + controller.nextthink = time + 1; + controller.think = func_bobbing_controller_think; + self.nextthink = self.ltime + 999999999; + self.think = SUB_NullThink; // for PushMove + + // Savage: Reduce bandwith, critical on e.g. nexdm02 + self.effects |= EF_LOWPRECISION; + + // TODO make a reset function for this one + } + + .float freq; + void func_pendulum_controller_think() + { + float v; + self.nextthink = time + 0.1; + + if (!(self.owner.active == ACTIVE_ACTIVE)) + { + self.owner.avelocity_x = 0; + return; + } + + // calculate sinewave using makevectors + makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0'); + v = self.owner.speed * v_forward.y + self.cnt; + if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed + { + // * 10 so it will arrive in 0.1 sec + self.owner.avelocity_z = (remainder(v - self.owner.angles.z, 360)) * 10; + } + } + + void spawnfunc_func_pendulum() + { + entity controller; + if (self.noise != "") + { + precache_sound(self.noise); + soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE); + } + + self.active = ACTIVE_ACTIVE; + + // keys: angle, speed, phase, noise, freq + + if(!self.speed) + self.speed = 30; + // not initializing self.dmg to 2, to allow damageless pendulum + + if(self.dmg && (self.message == "")) + self.message = " was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + if(self.dmg && (!self.dmgtime)) + self.dmgtime = 0.25; + self.dmgtime2 = time; + + self.blocked = generic_plat_blocked; + + self.avelocity_z = 0.0000001; + if (!InitMovingBrushTrigger()) + return; + + if(!self.freq) + { + // find pendulum length (same formula as Q3A) + self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins.z)))); + } + + // copy initial angle + self.cnt = self.angles.z; + + // wait for targets to spawn + controller = spawn(); + controller.classname = "func_pendulum_controller"; + controller.owner = self; + controller.nextthink = time + 1; + controller.think = func_pendulum_controller_think; + self.nextthink = self.ltime + 999999999; + self.think = SUB_NullThink; // for PushMove + + //self.effects |= EF_LOWPRECISION; + + // TODO make a reset function for this one + } + + // button and multiple button + + void() button_wait; + void() button_return; + + void button_wait() + { + self.state = STATE_TOP; + self.nextthink = self.ltime + self.wait; + self.think = button_return; + activator = self.enemy; + SUB_UseTargets(); + self.frame = 1; // use alternate textures + } + + void button_done() + { + self.state = STATE_BOTTOM; + } + + void button_return() + { + self.state = STATE_DOWN; + SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, button_done); + self.frame = 0; // use normal textures + if (self.health) + self.takedamage = DAMAGE_YES; // can be shot again + } + + + void button_blocked() + { + // do nothing, just don't come all the way back out + } + + + void button_fire() + { + self.health = self.max_health; + self.takedamage = DAMAGE_NO; // will be reset upon return + + if (self.state == STATE_UP || self.state == STATE_TOP) + return; + + if (self.noise != "") + sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM); + + self.state = STATE_UP; + SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, button_wait); + } + + void button_reset() + { + self.health = self.max_health; + setorigin(self, self.pos1); + self.frame = 0; // use normal textures + self.state = STATE_BOTTOM; + if (self.health) + self.takedamage = DAMAGE_YES; // can be shot again + } + + void button_use() + { + if(self.active != ACTIVE_ACTIVE) + return; + + self.enemy = activator; + button_fire (); + } + + void button_touch() + { + if (!other) + return; + if (!other.iscreature) + return; + if(other.velocity * self.movedir < 0) + return; + self.enemy = other; + if (other.owner) + self.enemy = other.owner; + button_fire (); + } + + void button_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) + { + if(self.spawnflags & DOOR_NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + self.health = self.health - damage; + if (self.health <= 0) + { + self.enemy = damage_attacker; + button_fire (); + } + } + + + /*QUAKED spawnfunc_func_button (0 .5 .8) ? + When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again. + + "angle" determines the opening direction + "target" all entities with a matching targetname will be used + "speed" override the default 40 speed + "wait" override the default 1 second wait (-1 = never return) + "lip" override the default 4 pixel lip remaining at end of move + "health" if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the InstaGib laser + "sounds" + 0) steam metal + 1) wooden clunk + 2) metallic click + 3) in-out + */ + void spawnfunc_func_button() + { + SetMovedir (); + + if (!InitMovingBrushTrigger()) + return; + self.effects |= EF_LOWPRECISION; + + self.blocked = button_blocked; + self.use = button_use; + + // if (self.health == 0) // all buttons are now shootable + // self.health = 10; + if (self.health) + { + self.max_health = self.health; + self.event_damage = button_damage; + self.takedamage = DAMAGE_YES; + } + else + self.touch = button_touch; + + if (!self.speed) + self.speed = 40; + if (!self.wait) + self.wait = 1; + if (!self.lip) + self.lip = 4; + + if(self.noise != "") + precache_sound(self.noise); + + self.active = ACTIVE_ACTIVE; + + self.pos1 = self.origin; + self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); + self.flags |= FL_NOTARGET; + + button_reset(); + } + + + const float DOOR_START_OPEN = 1; + const float DOOR_DONT_LINK = 4; + const float DOOR_TOGGLE = 32; + + /* + + Doors are similar to buttons, but can spawn a fat trigger field around them + to open without a touch, and they link together to form simultanious + double/quad doors. + + Door.owner is the master door. If there is only one door, it points to itself. + If multiple doors, all will point to a single one. + + Door.enemy chains from the master door through all doors linked in the chain. + + */ + + /* + ============================================================================= + + THINK FUNCTIONS + + ============================================================================= + */ + + void() door_go_down; + void() door_go_up; + void() door_rotating_go_down; + void() door_rotating_go_up; + + void door_blocked() + { + + if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!! + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } else { + + if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + + //Dont chamge direction for dead or dying stuff + if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) { + if (self.wait >= 0) + { + if (self.state == STATE_DOWN) + if (self.classname == "door") + { + door_go_up (); + } else + { + door_rotating_go_up (); + } + else + if (self.classname == "door") + { + door_go_down (); + } else + { + door_rotating_go_down (); + } + } + } else { + //gib dying stuff just to make sure + if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } + } + + //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic); + // if a door has a negative wait, it would never come back if blocked, + // so let it just squash the object to death real fast + /* if (self.wait >= 0) + { + if (self.state == STATE_DOWN) + door_go_up (); + else + door_go_down (); + } + */ + } + + + void door_hit_top() + { + if (self.noise1 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = STATE_TOP; + if (self.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + if (self.classname == "door") + { + self.think = door_go_down; + } else + { + self.think = door_rotating_go_down; + } + self.nextthink = self.ltime + self.wait; + } + + void door_hit_bottom() + { + if (self.noise1 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = STATE_BOTTOM; + } + + void door_go_down() + { + if (self.noise2 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + if (self.max_health) + { + self.takedamage = DAMAGE_YES; + self.health = self.max_health; + } + + self.state = STATE_DOWN; + SUB_CalcMove (self.pos1, TSPEED_LINEAR, self.speed, door_hit_bottom); + } + + void door_go_up() + { + if (self.state == STATE_UP) + return; // already going up + + if (self.state == STATE_TOP) + { // reset top wait time + self.nextthink = self.ltime + self.wait; + return; + } + + if (self.noise2 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + self.state = STATE_UP; + SUB_CalcMove (self.pos2, TSPEED_LINEAR, self.speed, door_hit_top); + + string oldmessage; + oldmessage = self.message; + self.message = ""; + SUB_UseTargets(); + self.message = oldmessage; + } + + + + /* + ============================================================================= + + ACTIVATION FUNCTIONS + + ============================================================================= + */ + + float door_check_keys(void) { + entity door = self.owner ? self.owner : self; + + // no key needed + if (!door.itemkeys) + return true; + + // this door require a key + // only a player can have a key + if (!IS_PLAYER(other)) + return false; + + if (item_keys_usekey(door, other)) { + // some keys were used + if (other.key_door_messagetime <= time) { + play2(other, "misc/talk.wav"); + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys)); + other.key_door_messagetime = time + 2; + } + } else { + // no keys were used + if (other.key_door_messagetime <= time) { + play2(other, "misc/talk.wav"); + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys)); + other.key_door_messagetime = time + 2; + } + } + + if (door.itemkeys) { + // door is now unlocked + play2(other, "misc/talk.wav"); + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_UNLOCKED); + return true; + } else + return false; + } + + + void door_fire() + { + entity oself; + entity starte; + + if (self.owner != self) + objerror ("door_fire: self.owner != self"); + + oself = self; + + if (self.spawnflags & DOOR_TOGGLE) + { + if (self.state == STATE_UP || self.state == STATE_TOP) + { + starte = self; + do + { + if (self.classname == "door") + { + door_go_down (); + } + else + { + door_rotating_go_down (); + } + self = self.enemy; + } while ( (self != starte) && (self != world) ); + self = oself; + return; + } + } + + // trigger all paired doors + starte = self; + do + { + if (self.classname == "door") + { + door_go_up (); + } else + { + // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction + if ((self.spawnflags & 2) && other.trigger_reverse!=0 && self.lip!=666 && self.state == STATE_BOTTOM) + { + self.lip = 666; // self.lip is used to remember reverse opening direction for door_rotating + self.pos2 = '0 0 0' - self.pos2; + } + // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side + if (!((self.spawnflags & 2) && (self.spawnflags & 8) && self.state == STATE_DOWN + && (((self.lip==666) && (other.trigger_reverse==0)) || ((self.lip!=666) && (other.trigger_reverse!=0))))) + { + door_rotating_go_up (); + } + } + self = self.enemy; + } while ( (self != starte) && (self != world) ); + self = oself; + } + + + void door_use() + { + entity oself; + + //dprint("door_use (model: ");dprint(self.model);dprint(")\n"); + + if (self.owner) + { + oself = self; + self = self.owner; + door_fire (); + self = oself; + } + } + + + void door_trigger_touch() + { + if (other.health < 1) + if (!(other.iscreature && other.deadflag == DEAD_NO)) + return; + + if (time < self.attack_finished_single) + return; + + // check if door is locked + if (!door_check_keys()) + return; + + self.attack_finished_single = time + 1; + + activator = other; + + self = self.owner; + door_use (); + } + + + void door_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) + { + entity oself; + if(self.spawnflags & DOOR_NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + self.health = self.health - damage; + + if (self.itemkeys) { + // don't allow opening doors through damage if keys are required + return; + } + + if (self.health <= 0) + { + oself = self; + self = self.owner; + self.health = self.max_health; + self.takedamage = DAMAGE_NO; // wil be reset upon return + door_use (); + self = oself; + } + } + + + /* + ================ + door_touch + + Prints messages + ================ + */ + void door_touch() + { + if (!IS_PLAYER(other)) + return; + if (self.owner.attack_finished_single > time) + return; + + self.owner.attack_finished_single = time + 2; + + if (!(self.owner.dmg) && (self.owner.message != "")) + { + if (IS_CLIENT(other)) + centerprint(other, self.owner.message); + play2(other, "misc/talk.wav"); + } + } + + + void door_generic_plat_blocked() + { + + if((self.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!! + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } else { + + if((self.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + + //Dont chamge direction for dead or dying stuff + if(other.deadflag != DEAD_NO && (other.takedamage == DAMAGE_NO)) { + if (self.wait >= 0) + { + if (self.state == STATE_DOWN) + door_rotating_go_up (); + else + door_rotating_go_down (); + } + } else { + //gib dying stuff just to make sure + if((self.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (other, self, self, 10000, DEATH_HURTTRIGGER, other.origin, '0 0 0'); + } + } + + //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic); + // if a door has a negative wait, it would never come back if blocked, + // so let it just squash the object to death real fast + /* if (self.wait >= 0) + { + if (self.state == STATE_DOWN) + door_rotating_go_up (); + else + door_rotating_go_down (); + } + */ + } + + + void door_rotating_hit_top() + { + if (self.noise1 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.state = STATE_TOP; + if (self.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + self.think = door_rotating_go_down; + self.nextthink = self.ltime + self.wait; + } + + void door_rotating_hit_bottom() + { + if (self.noise1 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating + { + self.pos2 = '0 0 0' - self.pos2; + self.lip = 0; + } + self.state = STATE_BOTTOM; + } + + void door_rotating_go_down() + { + if (self.noise2 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + if (self.max_health) + { + self.takedamage = DAMAGE_YES; + self.health = self.max_health; + } + + self.state = STATE_DOWN; + SUB_CalcAngleMove (self.pos1, TSPEED_LINEAR, self.speed, door_rotating_hit_bottom); + } + + void door_rotating_go_up() + { + if (self.state == STATE_UP) + return; // already going up + + if (self.state == STATE_TOP) + { // reset top wait time + self.nextthink = self.ltime + self.wait; + return; + } + if (self.noise2 != "") + sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + self.state = STATE_UP; + SUB_CalcAngleMove (self.pos2, TSPEED_LINEAR, self.speed, door_rotating_hit_top); + + string oldmessage; + oldmessage = self.message; + self.message = ""; + SUB_UseTargets(); + self.message = oldmessage; + } + + + + + /* + ============================================================================= + + SPAWNING FUNCTIONS + + ============================================================================= + */ + + + entity spawn_field(vector fmins, vector fmaxs) + { + entity trigger; + vector t1, t2; + + trigger = spawn(); + trigger.classname = "doortriggerfield"; + trigger.movetype = MOVETYPE_NONE; + trigger.solid = SOLID_TRIGGER; + trigger.owner = self; + trigger.touch = door_trigger_touch; + + t1 = fmins; + t2 = fmaxs; + setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); + return (trigger); + } + + + entity LinkDoors_nextent(entity cur, entity near, entity pass) + { + while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy)) + { + } + return cur; + } + + float LinkDoors_isconnected(entity e1, entity e2, entity pass) + { + float DELTA = 4; + if (e1.absmin.x > e2.absmax.x + DELTA) + return false; + if (e1.absmin.y > e2.absmax.y + DELTA) + return false; + if (e1.absmin.z > e2.absmax.z + DELTA) + return false; + if (e2.absmin.x > e1.absmax.x + DELTA) + return false; + if (e2.absmin.y > e1.absmax.y + DELTA) + return false; + if (e2.absmin.z > e1.absmax.z + DELTA) + return false; + return true; + } + + /* + ============= + LinkDoors + + + ============= + */ + void LinkDoors() + { + entity t; + vector cmins, cmaxs; + + if (self.enemy) + return; // already linked by another door + if (self.spawnflags & 4) + { + self.owner = self.enemy = self; + + if (self.health) + return; + IFTARGETED + return; + if (self.items) + return; + self.trigger_field = spawn_field(self.absmin, self.absmax); + + return; // don't want to link this door + } + + FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world); + + // set owner, and make a loop of the chain + dprint("LinkDoors: linking doors:"); + for(t = self; ; t = t.enemy) + { + dprint(" ", etos(t)); + t.owner = self; + if(t.enemy == world) + { + t.enemy = self; + break; + } + } + dprint("\n"); + + // collect health, targetname, message, size + cmins = self.absmin; + cmaxs = self.absmax; + for(t = self; ; t = t.enemy) + { + if(t.health && !self.health) + self.health = t.health; + if((t.targetname != "") && (self.targetname == "")) + self.targetname = t.targetname; + if((t.message != "") && (self.message == "")) + self.message = t.message; + if (t.absmin.x < cmins.x) + cmins.x = t.absmin.x; + if (t.absmin.y < cmins.y) + cmins.y = t.absmin.y; + if (t.absmin.z < cmins.z) + cmins.z = t.absmin.z; + if (t.absmax.x > cmaxs.x) + cmaxs.x = t.absmax.x; + if (t.absmax.y > cmaxs.y) + cmaxs.y = t.absmax.y; + if (t.absmax.z > cmaxs.z) + cmaxs.z = t.absmax.z; + if(t.enemy == self) + break; + } + + // distribute health, targetname, message + for(t = self; t; t = t.enemy) + { + t.health = self.health; + t.targetname = self.targetname; + t.message = self.message; + if(t.enemy == self) + break; + } + + // shootable, or triggered doors just needed the owner/enemy links, + // they don't spawn a field + + if (self.health) + return; + IFTARGETED + return; + if (self.items) + return; + + self.trigger_field = spawn_field(cmins, cmaxs); + } + + + /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE + if two doors touch, they are assumed to be connected and operate as a unit. + + TOGGLE causes the door to wait in both the start and end states for a trigger event. + + START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors). + + GOLD_KEY causes the door to open only if the activator holds a gold key. + + SILVER_KEY causes the door to open only if the activator holds a silver key. + + "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet + "angle" determines the opening direction + "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. + "health" if set, door must be shot open + "speed" movement speed (100 default) + "wait" wait before returning (3 default, -1 = never return) + "lip" lip remaining at end of move (8 default) + "dmg" damage to inflict when blocked (2 default) + "sounds" + 0) no sound + 1) stone + 2) base + 3) stone chain + 4) screechy metal + FIXME: only one sound set available at the time being + + */ + + void door_init_startopen() + { + setorigin (self, self.pos2); + self.pos2 = self.pos1; + self.pos1 = self.origin; + } + + void door_reset() + { + setorigin(self, self.pos1); + self.velocity = '0 0 0'; + self.state = STATE_BOTTOM; + self.think = func_null; + self.nextthink = 0; + } + + // spawnflags require key (for now only func_door) + const float SPAWNFLAGS_GOLD_KEY = 8; + const float SPAWNFLAGS_SILVER_KEY = 16; + void spawnfunc_func_door() + { + // Quake 1 keys compatibility + if (self.spawnflags & SPAWNFLAGS_GOLD_KEY) + self.itemkeys |= ITEM_KEY_BIT(0); + if (self.spawnflags & SPAWNFLAGS_SILVER_KEY) + self.itemkeys |= ITEM_KEY_BIT(1); + + //if (!self.deathtype) // map makers can override this + // self.deathtype = " got in the way"; + SetMovedir (); + + self.max_health = self.health; + if (!InitMovingBrushTrigger()) + return; + self.effects |= EF_LOWPRECISION; + self.classname = "door"; + + self.blocked = door_blocked; + self.use = door_use; + + // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY + // if(self.spawnflags & 8) + // self.dmg = 10000; + + if(self.dmg && (self.message == "")) + self.message = "was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + + if (self.sounds > 0) + { + precache_sound ("plats/medplat1.wav"); + precache_sound ("plats/medplat2.wav"); + self.noise2 = "plats/medplat1.wav"; + self.noise1 = "plats/medplat2.wav"; + } + + if (!self.speed) + self.speed = 100; + if (!self.wait) + self.wait = 3; + if (!self.lip) + self.lip = 8; + + self.pos1 = self.origin; + self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); + + // DOOR_START_OPEN is to allow an entity to be lighted in the closed position + // but spawn in the open position + if (self.spawnflags & DOOR_START_OPEN) + InitializeEntity(self, door_init_startopen, INITPRIO_SETLOCATION); + + self.state = STATE_BOTTOM; + + if (self.health) + { + self.takedamage = DAMAGE_YES; + self.event_damage = door_damage; + } + + if (self.items) + self.wait = -1; + + self.touch = door_touch; + + // LinkDoors can't be done until all of the doors have been spawned, so + // the sizes can be detected properly. + InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS); + + self.reset = door_reset; + } + + /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS + if two doors touch, they are assumed to be connected and operate as a unit. + + TOGGLE causes the door to wait in both the start and end states for a trigger event. + + BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor. + The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction + must have set trigger_reverse to 1. + BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side. + + START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors). + + "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet + "angle" determines the destination angle for opening. negative values reverse the direction. + "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. + "health" if set, door must be shot open + "speed" movement speed (100 default) + "wait" wait before returning (3 default, -1 = never return) + "dmg" damage to inflict when blocked (2 default) + "sounds" + 0) no sound + 1) stone + 2) base + 3) stone chain + 4) screechy metal + FIXME: only one sound set available at the time being + */ + + void door_rotating_reset() + { + self.angles = self.pos1; + self.avelocity = '0 0 0'; + self.state = STATE_BOTTOM; + self.think = func_null; + self.nextthink = 0; + } + + void door_rotating_init_startopen() + { + self.angles = self.movedir; + self.pos2 = '0 0 0'; + self.pos1 = self.movedir; + } + + + void spawnfunc_func_door_rotating() + { + + //if (!self.deathtype) // map makers can override this + // self.deathtype = " got in the way"; + + // I abuse "movedir" for denoting the axis for now + if (self.spawnflags & 64) // X (untested) + self.movedir = '0 0 1'; + else if (self.spawnflags & 128) // Y (untested) + self.movedir = '1 0 0'; + else // Z + self.movedir = '0 1 0'; + + if (self.angles.y ==0) self.angles_y = 90; + + self.movedir = self.movedir * self.angles.y; + self.angles = '0 0 0'; + + self.max_health = self.health; + self.avelocity = self.movedir; + if (!InitMovingBrushTrigger()) + return; + self.velocity = '0 0 0'; + //self.effects |= EF_LOWPRECISION; + self.classname = "door_rotating"; + + self.blocked = door_blocked; + self.use = door_use; + + if(self.spawnflags & 8) + self.dmg = 10000; + + if(self.dmg && (self.message == "")) + self.message = "was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + + if (self.sounds > 0) + { + precache_sound ("plats/medplat1.wav"); + precache_sound ("plats/medplat2.wav"); + self.noise2 = "plats/medplat1.wav"; + self.noise1 = "plats/medplat2.wav"; + } + + if (!self.speed) + self.speed = 50; + if (!self.wait) + self.wait = 1; + self.lip = 0; // self.lip is used to remember reverse opening direction for door_rotating + + self.pos1 = '0 0 0'; + self.pos2 = self.movedir; + + // DOOR_START_OPEN is to allow an entity to be lighted in the closed position + // but spawn in the open position + if (self.spawnflags & DOOR_START_OPEN) + InitializeEntity(self, door_rotating_init_startopen, INITPRIO_SETLOCATION); + + self.state = STATE_BOTTOM; + + if (self.health) + { + self.takedamage = DAMAGE_YES; + self.event_damage = door_damage; + } + + if (self.items) + self.wait = -1; + + self.touch = door_touch; + + // LinkDoors can't be done until all of the doors have been spawned, so + // the sizes can be detected properly. + InitializeEntity(self, LinkDoors, INITPRIO_LINKDOORS); + + self.reset = door_rotating_reset; + } + + /* + ============================================================================= + + SECRET DOORS + + ============================================================================= + */ + + void() fd_secret_move1; + void() fd_secret_move2; + void() fd_secret_move3; + void() fd_secret_move4; + void() fd_secret_move5; + void() fd_secret_move6; + void() fd_secret_done; + + const float SECRET_OPEN_ONCE = 1; // stays open + const float SECRET_1ST_LEFT = 2; // 1st move is left of arrow + const float SECRET_1ST_DOWN = 4; // 1st move is down from arrow + const float SECRET_NO_SHOOT = 8; // only opened by trigger + const float SECRET_YES_SHOOT = 16; // shootable even if targeted + + void fd_secret_use() + { + float temp; + string message_save; + + self.health = 10000; + self.bot_attack = true; + + // exit if still moving around... + if (self.origin != self.oldorigin) + return; + + message_save = self.message; + self.message = ""; // no more message + SUB_UseTargets(); // fire all targets / killtargets + self.message = message_save; + + self.velocity = '0 0 0'; + + // Make a sound, wait a little... + + if (self.noise1 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTEN_NORM); + self.nextthink = self.ltime + 0.1; + + temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1 + makevectors(self.mangle); + + if (!self.t_width) + { + if (self.spawnflags & SECRET_1ST_DOWN) + self.t_width = fabs(v_up * self.size); + else + self.t_width = fabs(v_right * self.size); + } + + if (!self.t_length) + self.t_length = fabs(v_forward * self.size); + + if (self.spawnflags & SECRET_1ST_DOWN) + self.dest1 = self.origin - v_up * self.t_width; + else + self.dest1 = self.origin + v_right * (self.t_width * temp); + + self.dest2 = self.dest1 + v_forward * self.t_length; + SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move1); + if (self.noise2 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + } + + void fd_secret_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) + { + fd_secret_use(); + } + + // Wait after first movement... + void fd_secret_move1() + { + self.nextthink = self.ltime + 1.0; + self.think = fd_secret_move2; + if (self.noise3 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM); + } + + // Start moving sideways w/sound... + void fd_secret_move2() + { + if (self.noise2 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(self.dest2, TSPEED_LINEAR, self.speed, fd_secret_move3); + } + + // Wait here until time to go back... + void fd_secret_move3() + { + if (self.noise3 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM); + if (!(self.spawnflags & SECRET_OPEN_ONCE)) + { + self.nextthink = self.ltime + self.wait; + self.think = fd_secret_move4; + } + } + + // Move backward... + void fd_secret_move4() + { + if (self.noise2 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(self.dest1, TSPEED_LINEAR, self.speed, fd_secret_move5); + } + + // Wait 1 second... + void fd_secret_move5() + { + self.nextthink = self.ltime + 1.0; + self.think = fd_secret_move6; + if (self.noise3 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM); + } + + void fd_secret_move6() + { + if (self.noise2 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(self.oldorigin, TSPEED_LINEAR, self.speed, fd_secret_done); + } + + void fd_secret_done() + { + if (self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + //self.th_pain = fd_secret_use; + } + if (self.noise3 != "") + sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM); + } + + void secret_blocked() + { + if (time < self.attack_finished_single) + return; + self.attack_finished_single = time + 0.5; + //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic); + } + + /* + ============== + secret_touch + + Prints messages + ================ + */ + void secret_touch() + { + if (!other.iscreature) + return; + if (self.attack_finished_single > time) + return; + + self.attack_finished_single = time + 2; + + if (self.message) + { + if (IS_CLIENT(other)) + centerprint(other, self.message); + play2(other, "misc/talk.wav"); + } + } + + void secret_reset() + { + if (self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + } + setorigin(self, self.oldorigin); + self.think = func_null; + self.nextthink = 0; + } + + /*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot + Basic secret door. Slides back, then to the side. Angle determines direction. + wait = # of seconds before coming back + 1st_left = 1st move is left of arrow + 1st_down = 1st move is down from arrow + always_shoot = even if targeted, keep shootable + t_width = override WIDTH to move back (or height if going down) + t_length = override LENGTH to move sideways + "dmg" damage to inflict when blocked (2 default) + + If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage. + "sounds" + 1) medieval + 2) metal + 3) base + */ + + void spawnfunc_func_door_secret() + { + /*if (!self.deathtype) // map makers can override this + self.deathtype = " got in the way";*/ + + if (!self.dmg) + self.dmg = 2; + + // Magic formula... + self.mangle = self.angles; + self.angles = '0 0 0'; + self.classname = "door"; + if (!InitMovingBrushTrigger()) + return; + self.effects |= EF_LOWPRECISION; + + self.touch = secret_touch; + self.blocked = secret_blocked; + self.speed = 50; + self.use = fd_secret_use; + IFTARGETED + { + } + else + self.spawnflags |= SECRET_YES_SHOOT; + + if(self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + self.event_damage = fd_secret_damage; + } + self.oldorigin = self.origin; + if (!self.wait) + self.wait = 5; // 5 seconds before closing + + self.reset = secret_reset; + secret_reset(); + } + + /*QUAKED spawnfunc_func_fourier (0 .5 .8) ? + Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions. + netname: list of quadruples, separated by spaces; note that phase 0 represents a sine wave, and phase 0.25 a cosine wave (by default, it uses 1 0 0 0 1, to match func_bobbing's defaults + speed: how long one cycle of frequency multiplier 1 in seconds (default 4) + height: amplitude modifier (default 32) + phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0) + noise: path/name of looping .wav file to play. + dmg: Do this mutch dmg every .dmgtime intervall when blocked + dmgtime: See above. + */ + + void func_fourier_controller_think() + { + vector v; + float n, i, t; + + self.nextthink = time + 0.1; + if(self.owner.active != ACTIVE_ACTIVE) + { + self.owner.velocity = '0 0 0'; + return; + } + + + n = floor((tokenize_console(self.owner.netname)) / 5); + t = self.nextthink * self.owner.cnt + self.owner.phase * 360; + + v = self.owner.destvec; + + for(i = 0; i < n; ++i) + { + makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0'); + v = v + ('1 0 0' * stof(argv(i*5+2)) + '0 1 0' * stof(argv(i*5+3)) + '0 0 1' * stof(argv(i*5+4))) * self.owner.height * v_forward.y; + } + + if(self.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed + // * 10 so it will arrive in 0.1 sec + self.owner.velocity = (v - self.owner.origin) * 10; + } + + void spawnfunc_func_fourier() + { + entity controller; + if (self.noise != "") + { + precache_sound(self.noise); + soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE); + } + + if (!self.speed) + self.speed = 4; + if (!self.height) + self.height = 32; + self.destvec = self.origin; + self.cnt = 360 / self.speed; + + self.blocked = generic_plat_blocked; + if(self.dmg && (self.message == "")) + self.message = " was squished"; + if(self.dmg && (self.message2 == "")) + self.message2 = "was squished by"; + if(self.dmg && (!self.dmgtime)) + self.dmgtime = 0.25; + self.dmgtime2 = time; + + if(self.netname == "") + self.netname = "1 0 0 0 1"; + + if (!InitMovingBrushTrigger()) + return; + + self.active = ACTIVE_ACTIVE; + + // wait for targets to spawn + controller = spawn(); + controller.classname = "func_fourier_controller"; + controller.owner = self; + controller.nextthink = time + 1; + controller.think = func_fourier_controller_think; + self.nextthink = self.ltime + 999999999; + self.think = SUB_NullThink; // for PushMove + + // Savage: Reduce bandwith, critical on e.g. nexdm02 + self.effects |= EF_LOWPRECISION; + + // TODO make a reset function for this one + } + + // reusing some fields havocbots declared + .entity wp00, wp01, wp02, wp03; + + .float targetfactor, target2factor, target3factor, target4factor; + .vector targetnormal, target2normal, target3normal, target4normal; + + vector func_vectormamamam_origin(entity o, float t) + { + vector v, p; + float f; + entity e; + + f = o.spawnflags; + v = '0 0 0'; + + e = o.wp00; + if(e) + { + p = e.origin + t * e.velocity; + if(f & 1) + v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor; + else + v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor; + } + + e = o.wp01; + if(e) + { + p = e.origin + t * e.velocity; + if(f & 2) + v = v + (p * o.target2normal) * o.target2normal * o.target2factor; + else + v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor; + } + + e = o.wp02; + if(e) + { + p = e.origin + t * e.velocity; + if(f & 4) + v = v + (p * o.target3normal) * o.target3normal * o.target3factor; + else + v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor; + } + + e = o.wp03; + if(e) + { + p = e.origin + t * e.velocity; + if(f & 8) + v = v + (p * o.target4normal) * o.target4normal * o.target4factor; + else + v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor; + } + + return v; + } + + void func_vectormamamam_controller_think() + { + self.nextthink = time + 0.1; + + if(self.owner.active != ACTIVE_ACTIVE) + { + self.owner.velocity = '0 0 0'; + return; + } + + if(self.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed + self.owner.velocity = (self.owner.destvec + func_vectormamamam_origin(self.owner, 0.1) - self.owner.origin) * 10; + } + + void func_vectormamamam_findtarget() + { + if(self.target != "") + self.wp00 = find(world, targetname, self.target); + + if(self.target2 != "") + self.wp01 = find(world, targetname, self.target2); + + if(self.target3 != "") + self.wp02 = find(world, targetname, self.target3); + + if(self.target4 != "") + self.wp03 = find(world, targetname, self.target4); + + if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03) + objerror("No reference entity found, so there is nothing to move. Aborting."); + + self.destvec = self.origin - func_vectormamamam_origin(self, 0); + + entity controller; + controller = spawn(); + controller.classname = "func_vectormamamam_controller"; + controller.owner = self; + controller.nextthink = time + 1; + controller.think = func_vectormamamam_controller_think; + } + + void spawnfunc_func_vectormamamam() + { + if (self.noise != "") + { + precache_sound(self.noise); + soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE); + } + + if(!self.targetfactor) + self.targetfactor = 1; + + if(!self.target2factor) + self.target2factor = 1; + + if(!self.target3factor) + self.target3factor = 1; + + if(!self.target4factor) + self.target4factor = 1; + + if(vlen(self.targetnormal)) + self.targetnormal = normalize(self.targetnormal); + + if(vlen(self.target2normal)) + self.target2normal = normalize(self.target2normal); + + if(vlen(self.target3normal)) + self.target3normal = normalize(self.target3normal); + + if(vlen(self.target4normal)) + self.target4normal = normalize(self.target4normal); + + self.blocked = generic_plat_blocked; + if(self.dmg && (self.message == "")) + self.message = " was squished"; + if(self.dmg && (self.message == "")) + self.message2 = "was squished by"; + if(self.dmg && (!self.dmgtime)) + self.dmgtime = 0.25; + self.dmgtime2 = time; + + if(self.netname == "") + self.netname = "1 0 0 0 1"; + + if (!InitMovingBrushTrigger()) + return; + + // wait for targets to spawn + self.nextthink = self.ltime + 999999999; + self.think = SUB_NullThink; // for PushMove + + // Savage: Reduce bandwith, critical on e.g. nexdm02 + self.effects |= EF_LOWPRECISION; + + self.active = ACTIVE_ACTIVE; + + InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET); + } + + void conveyor_think() + { + entity e; + + // set myself as current conveyor where possible + for(e = world; (e = findentity(e, conveyor, self)); ) + e.conveyor = world; + + if(self.state) + { + for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain) + if(!e.conveyor.state) + if(isPushable(e)) + { + vector emin = e.absmin; + vector emax = e.absmax; + if(self.solid == SOLID_BSP) + { + emin -= '1 1 1'; + emax += '1 1 1'; + } + if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick + if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate + e.conveyor = self; + } + + for(e = world; (e = findentity(e, conveyor, self)); ) + { + if(IS_CLIENT(e)) // doing it via velocity has quite some advantages + continue; // done in SV_PlayerPhysics + + setorigin(e, e.origin + self.movedir * sys_frametime); + move_out_of_solid(e); + UpdateCSQCProjectile(e); + /* + // stupid conveyor code + tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e); + if(trace_fraction > 0) + setorigin(e, trace_endpos); + */ + } + } + + self.nextthink = time; + } + + void conveyor_use() + { + self.state = !self.state; + } + + void conveyor_reset() + { + self.state = (self.spawnflags & 1); + } + + void conveyor_init() + { + if (!self.speed) + self.speed = 200; + self.movedir = self.movedir * self.speed; + self.think = conveyor_think; + self.nextthink = time; + IFTARGETED + { + self.use = conveyor_use; + self.reset = conveyor_reset; + conveyor_reset(); + } + else + self.state = 1; + } + + void spawnfunc_trigger_conveyor() + { + SetMovedir(); + EXACTTRIGGER_INIT; + conveyor_init(); + } + + void spawnfunc_func_conveyor() + { + SetMovedir(); + InitMovingBrushTrigger(); + self.movetype = MOVETYPE_NONE; + conveyor_init(); + } diff --cc qcsrc/server/tturrets/units/unit_walker.qc index f76d119230,812fb22456..f70f7ab267 --- a/qcsrc/server/tturrets/units/unit_walker.qc +++ b/qcsrc/server/tturrets/units/unit_walker.qc @@@ -1,3 -1,9 +1,8 @@@ + #include "../../_all.qh" + + #include "../../movelib.qh" + #include "../../steerlib.qh" -#include "../../t_teleporters.qh" + const float ANIM_NO = 0; const float ANIM_TURN = 1; const float ANIM_WALK = 2; diff --cc qcsrc/server/vehicles/racer.qc index dd0e629bee,545e309e4f..8cecdfa840 --- a/qcsrc/server/vehicles/racer.qc +++ b/qcsrc/server/vehicles/racer.qc @@@ -1,9 -1,7 +1,9 @@@ - const vector RACER_MIN = '-120 -120 -40'; - const vector RACER_MAX = '120 120 40'; + #include "vehicle.qh" + #include "racer.qh" #ifdef SVQC +#include "../../common/triggers/trigger/impulse.qh" + void racer_exit(float eject); void racer_enter(); diff --cc qcsrc/server/vehicles/vehicle.qh index 0000000000,696ea1a59f..59d9ef76d3 mode 000000,100644..100644 --- a/qcsrc/server/vehicles/vehicle.qh +++ b/qcsrc/server/vehicles/vehicle.qh @@@ -1,0 -1,197 +1,196 @@@ + #ifndef VEHICLE_H + #define VEHICLE_H + + #include "../_all.qh" + + #include "../antilag.qh" + #include "../cl_player.qh" + #include "../g_damage.qh" + #include "../g_hook.qh" -#include "../g_subs.qh" + #include "../movelib.qh" + + #include "../bot/bot.qh" + + #include "../command/common.qh" + + #include "../tturrets/include/turrets_early.qh" + + #include "../weapons/tracing.qh" + + #include "../../common/deathtypes.qh" + #include "../../common/stats.qh" + + #include "../../warpzonelib/anglestransform.qh" + #include "../../warpzonelib/server.qh" + + entity vehicles_projectile(string _mzlfx, string _mzlsound, + vector _org, vector _vel, + float _dmg, float _radi, float _force, float _size, + float _deahtype, float _projtype, float _health, + float _cull, float _clianim, entity _owner); + + vector vehicles_findgoodexit(vector prefer_spot); + + + + /** vehicles_locktarget + + Generic target locking. + + Figure out if what target is "locked" (if any), for missile tracking as such. + + after calling, "if(self.lock_target != world && self.lock_strength == 1)" mean + you have a locked in target. + + Exspects a crosshair_trace() or equivalent to be + dont before calling. + + **/ + .entity lock_target; + .float lock_strength; + .float lock_time; + .float lock_soundtime; + + void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, int axh_id); + + vector vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string _tagname, + float _pichlimit_min, float _pichlimit_max, + float _rotlimit_min, float _rotlimit_max, float _aimspeed); + + #define VEHICLE_UPDATE_PLAYER(ply,fld,vhname) \ + ply.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100 + + void CSQCVehicleSetup(entity own, float vehicle_id); + + .float() PlayerPhysplug; + + float autocvar_g_vehicles_allow_bots = 0; + + void vehicles_touch(); + + void vehicles_regen(float timer, .float regen_field, float field_max, float rpause, float regen, float delta_time, float _healthscale); + + float shortangle_f(float ang1, float ang2); + float anglemods(float v); + + entity vehicle_tossgib(entity _template, vector _vel, string _tag, float _burn, float _explode, float _maxtime, vector _rot); + + void vehicles_impact(float _minspeed, float _speedfac, float _maxpain); + + void shieldhit_think(); + + float vehicle_addplayerslot( entity _owner, + entity _slot, + float _hud, + string _hud_model, + float() _framefunc, + void(float) _exitfunc); + + .void() vehicle_impact; + + float vehicle_initialize(string net_name, + string bodymodel, + string topmodel, + string hudmodel, + string toptag, + string hudtag, + string viewtag, + float vhud, + vector min_s, + vector max_s, + float nodrop, + void(float _spawnflag) spawnproc, + float _respawntime, + float() physproc, + void() enterproc, + void(float extflag) exitfunc, + void() dieproc, + void() thinkproc, + float use_csqc, + float _max_health, + float _max_shield); + + float force_fromtag_normpower; + + void vehicles_painframe(); + + void vehicles_locktarget(float incr, float decr, float _lock_time); + + vector vehicles_force_fromtag_maglev(string tag_name, float spring_length, float max_power); + + vector vehicles_force_fromtag_hover(string tag_name, float spring_length, float max_power); + + void vehicles_projectile_explode(); + + #if VEHICLES_ENABLED + + .int vehicle_flags; + const int VHF_ISVEHICLE = 2; /// Indicates vehicle + const int VHF_HASSHIELD = 4; /// Vehicle has shileding + const int VHF_SHIELDREGEN = 8; /// Vehicles shield regenerates + const int VHF_HEALTHREGEN = 16; /// Vehicles health regenerates + const int VHF_ENERGYREGEN = 32; /// Vehicles energy regenerates + const int VHF_DEATHEJECT = 64; /// Vehicle ejects pilot upon fatal damage + const int VHF_MOVE_GROUND = 128; /// Vehicle moves on gound + const int VHF_MOVE_HOVER = 256; /// Vehicle hover close to gound + const int VHF_MOVE_FLY = 512; /// Vehicle is airborn + const int VHF_DMGSHAKE = 1024; /// Add random velocity each frame if health < 50% + const int VHF_DMGROLL = 2048; /// Add random angles each frame if health < 50% + const int VHF_DMGHEADROLL = 4096; /// Add random head angles each frame if health < 50% + const int VHF_MULTISLOT = 8192; /// Vehicle has multiple player slots + const int VHF_PLAYERSLOT = 16384; /// This ent is a player slot on a multi-person vehicle + + .entity gun1; + .entity gun2; + .entity gun3; + .entity vehicle_shieldent; /// Entity to disply the shild effect on damage + .entity vehicle; + .entity vehicle_viewport; + .entity vehicle_hudmodel; + .entity vehicle_controller; + + .entity gunner1; + .entity gunner2; + + .float vehicle_health; /// If self is player this is 0..100 indicating precentage of health left on vehicle. If self is vehile, this is the real health value. + .float vehicle_energy; /// If self is player this is 0..100 indicating precentage of energy left on vehicle. If self is vehile, this is the real energy value. + .float vehicle_shield; /// If self is player this is 0..100 indicating precentage of shield left on vehicle. If self is vehile, this is the real shield value. + + .float vehicle_ammo1; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real ammo1 value. + .float vehicle_reload1; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real reload1 value. + .float vehicle_ammo2; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real ammo2 value. + .float vehicle_reload2; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real reload2 value. + + .float sound_nexttime; + const float VOL_VEHICLEENGINE = 1; + + .float hud; + .float dmg_time; + .float vehicle_respawntime; + //.void() vehicle_spawn; + + void vehicles_exit(float eject); + .void(float exit_flags) vehicle_exit; + const float VHEF_NORMAL = 0; /// User pressed exit key + const float VHEF_EJECT = 1; /// User pressed exit key 3 times fast (not implemented) or vehile is dying + const float VHEF_RELESE = 2; /// Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (not implemented) + + const float SVC_SETVIEWPORT = 5; // Net.Protocol 0x05 + const float SVC_SETVIEWANGLES = 10; // Net.Protocol 0x0A + const float SVC_UPDATEENTITY = 128; // Net.Protocol 0x80 + + .void() vehicle_enter; /// Vehicles custom funciton to be executed when owner exit it + .void() vehicle_die; /// Vehicles custom function to be executed when vehile die + const float VHSF_NORMAL = 0; + const float VHSF_FACTORY = 2; + .void(float _spawnflag) vehicle_spawn; /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns + .float(float _imp) vehicles_impulse; + .float vehicle_weapon2mode; + + #if VEHICLES_USE_ODE + void(entity e, float physics_enabled) physics_enable = #540; // enable or disable physics on object + void(entity e, vector force, vector force_pos) physics_addforce = #541; // apply a force from certain origin, length of force vector is power of force + void(entity e, vector torque) physics_addtorque = #542; // add relative torque + #endif // VEHICLES_USE_ODE + #endif // VEHICLES_ENABLED + #endif diff --cc qcsrc/server/weapons/hitplot.qc index ec1004cd1d,76e159738f..0564bdc762 --- a/qcsrc/server/weapons/hitplot.qc +++ b/qcsrc/server/weapons/hitplot.qc @@@ -1,14 -1,9 +1,8 @@@ - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../../dpdefs/progsdefs.qh" - #include "../../dpdefs/dpextensions.qh" - #include "../../common/weapons/weapons.qh" - #include "hitplot.qh" - #include "../autocvars.qh" - #include "../defs.qh" - #include "../antilag.qh" - #endif + #include "hitplot.qh" + #include "../_all.qh" + + #include "../antilag.qh" -#include "../g_subs.qh" + #include "../../common/weapons/all.qh" vector W_HitPlotUnnormalizedUntransform(vector screenforward, vector screenright, vector screenup, vector v) { diff --cc qcsrc/server/weapons/throwing.qc index 6b47a88227,1fea5fb393..d14bcfd1cf --- a/qcsrc/server/weapons/throwing.qc +++ b/qcsrc/server/weapons/throwing.qc @@@ -1,20 -1,15 +1,14 @@@ - #if defined(CSQC) - #elif defined(MENUQC) - #elif defined(SVQC) - #include "../../dpdefs/progsdefs.qh" - #include "../../dpdefs/dpextensions.qh" - #include "../../common/util.qh" - #include "../../common/weapons/weapons.qh" - #include "throwing.qh" - #include "weaponsystem.qh" - #include "../t_items.qh" - #include "../autocvars.qh" - #include "../constants.qh" - #include "../defs.qh" - #include "../../common/notifications.qh" - #include "../mutators/mutators_include.qh" - #include "../../common/mapinfo.qh" - #endif + #include "throwing.qh" + #include "../_all.qh" + + #include "weaponsystem.qh" + #include "../mutators/mutators_include.qh" + #include "../t_items.qh" + #include "../g_damage.qh" -#include "../g_subs.qh" + #include "../../common/mapinfo.qh" + #include "../../common/notifications.qh" + #include "../../common/util.qh" + #include "../../common/weapons/all.qh" void thrown_wep_think() { diff --cc qcsrc/warpzonelib/server.qc index 0ecc65fd45,bd6266ea0c..53d9039025 --- a/qcsrc/warpzonelib/server.qc +++ b/qcsrc/warpzonelib/server.qc @@@ -1,16 -4,13 +4,14 @@@ #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "common.qh" - #include "server.qh" #include "../common/constants.qh" + #include "../common/triggers/subs.qh" #include "../common/util.qh" + #include "../dpdefs/dpextensions.qh" + #include "../dpdefs/progsdefs.qh" + #include "../server/command/common.qh" #include "../server/constants.qh" #include "../server/defs.qh" - #include "../server/command/common.qh" #endif #ifdef WARPZONELIB_KEEPDEBUG diff --cc qcsrc/warpzonelib/util_server.qc index 06ca72a688,e82c4ba6e2..0af6d3c0dc --- a/qcsrc/warpzonelib/util_server.qc +++ b/qcsrc/warpzonelib/util_server.qc @@@ -1,12 -5,70 +5,11 @@@ #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "util_server.qh" #include "../csqcmodellib/sv_model.qh" + #include "../dpdefs/dpextensions.qh" + #include "../dpdefs/progsdefs.qh" #endif - -void WarpZoneLib_MoveOutOfSolid_Expand(entity e, vector by) -{ - float eps = 0.0625; - tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e); - if (trace_startsolid) - return; - if (trace_fraction < 1) - { - // hit something - // adjust origin in the other direction... - setorigin(e,e.origin - by * (1 - trace_fraction)); - } -} - -float WarpZoneLib_MoveOutOfSolid(entity e) -{ - vector o, m0, m1; - - o = e.origin; - traceline(o, o, MOVE_WORLDONLY, e); - if (trace_startsolid) - return false; - - tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e); - if (!trace_startsolid) - return true; - - m0 = e.mins; - m1 = e.maxs; - e.mins = '0 0 0'; - e.maxs = '0 0 0'; - WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m0_x); - e.mins_x = m0_x; - WarpZoneLib_MoveOutOfSolid_Expand(e, '1 0 0' * m1_x); - e.maxs_x = m1_x; - WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m0_y); - e.mins_y = m0_y; - WarpZoneLib_MoveOutOfSolid_Expand(e, '0 1 0' * m1_y); - e.maxs_y = m1_y; - WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m0_z); - e.mins_z = m0_z; - WarpZoneLib_MoveOutOfSolid_Expand(e, '0 0 1' * m1_z); - e.maxs_z = m1_z; - setorigin(e, e.origin); - - tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e); - if (trace_startsolid) - { - setorigin(e, o); - return false; - } - - return true; -} - -float WarpZoneLib_ExactTrigger_Touch() -{ - return !WarpZoneLib_BoxTouchesBrush(other.absmin, other.absmax, self, other); -} +#include "common.qh" void WarpZoneLib_ExactTrigger_Init() {