if [ x"$mode" = x"txt" ]; then
{
- echo "en English \"English\""
+ item=`grep "^en " languages.txt`
+ echo "$item"
for X in common.*.po; do
[ -f "$X" ] || continue
if [ -n "$language" ]; then
if [ "$p" -lt 50 ]; then
continue
fi
- item="$l $l \"$l (0%)\""
+ item="$l $l \"$l\" 0%"
fi
- printf "%s\n" "$item" | sed -e "s/([0-9][0-9]*%)/($p%)/"
+ printf "%s\n" "$item" | sed -e "s/[0-9][0-9]*%/$p%/"
done
} | tr '"' '\t' | sort -k3 | tr '\t' '"'
fi
-ast Asturian "Asturianu (60%)"
-de German "Deutsch (90%)"
-de_CH German "Deutsch (Schweiz) (90%)"
-en_AU en_AU "en_AU (77%)"
-en English "English"
-es Spanish "Español (68%)"
-fr French "Français (98%)"
-it Italian "Italiano (97%)"
-hu Hungarian "Magyar (50%)"
-nl Dutch "Nederlands (45%)"
-pl Polish "Polski (60%)"
-pt Portuguese "Português (42%)"
-ro Romanian "Romana (90%)"
-fi Finnish "Suomi (35%)"
-el Greek "Ελληνική (25%)"
-be Belarusian "Беларуская (65%)"
-bg Bulgarian "Български (65%)"
-ru Russian "Русский (93%)"
-uk Ukrainian "Українська (60%)"
+ast Asturian "Asturianu" 60%
+de German "Deutsch" 90%
+de_CH German "Deutsch (Schweiz)" 90%
+en English "English"
+en_AU English "English (Australia)" 77%
+es Spanish "Español" 68%
+fr French "Français" 98%
+it Italian "Italiano" 97%
+hu Hungarian "Magyar" 50%
+nl Dutch "Nederlands" 45%
+pl Polish "Polski" 60%
+pt Portuguese "Português" 42%
+ro Romanian "Romana" 90%
+fi Finnish "Suomi" 35%
+el Greek "Ελληνική" 25%
+be Belarusian "Беларуская" 65%
+bg Bulgarian "Български" 65%
+ru Russian "Русский" 93%
+uk Ukrainian "Українська" 60%
\ No newline at end of file
#include "announcer.qh"
+#include "mutators/events.qh"
+
#include "../common/notifications.qh"
#include "../common/stats.qh"
bool announcer_1min;
bool announcer_5min;
+string AnnouncerOption()
+{
+ string ret = autocvar_cl_announcer;
+ MUTATOR_CALLHOOK(AnnouncerOption, ret);
+ ret = ret_string;
+ return ret;
+}
+
void Announcer_Countdown()
{
SELFPARAM();
- float starttime = getstatf(STAT_GAMESTARTTIME);
+ float starttime = STAT(GAMESTARTTIME);
float roundstarttime = getstatf(STAT_ROUNDSTARTTIME);
if(roundstarttime == -1)
{
float previous_game_starttime;
void Announcer_Gamestart()
{
- float startTime = getstatf(STAT_GAMESTARTTIME);
+ float startTime = STAT(GAMESTARTTIME);
float roundstarttime = getstatf(STAT_ROUNDSTARTTIME);
if(roundstarttime > startTime)
startTime = roundstarttime;
{
if(time < startTime)
{
- entity e = find(world, classname, "announcer_countdown");
- if (!e)
+ static entity announcer_countdown;
+ if (!announcer_countdown)
{
- e = spawn();
- e.classname = "announcer_countdown";
- e.think = Announcer_Countdown;
+ announcer_countdown = new(announcer_countdown);
+ announcer_countdown.think = Announcer_Countdown;
}
if(time + 5.0 < startTime) // if connecting to server while restart was active don't always play prepareforbattle
- if(time > e.nextthink) // don't play it again if countdown was already going
+ if(time > announcer_countdown.nextthink) // don't play it again if countdown was already going
Local_Notification(MSG_ANNCE, ANNCE_PREPARE);
- e.nextthink = startTime - floor(startTime - time); //synchronize nextthink to startTime
+ announcer_countdown.nextthink = startTime - floor(startTime - time); //synchronize nextthink to startTime
}
}
void Announcer_Time()
{
float timelimit = getstatf(STAT_TIMELIMIT);
- float timeleft = max(0, timelimit * 60 + getstatf(STAT_GAMESTARTTIME) - time);
+ float timeleft = max(0, timelimit * 60 + STAT(GAMESTARTTIME) - time);
float warmup_timeleft = 0;
if(warmup_stage)
if(autocvar_g_warmup_limit > 0)
- warmup_timeleft = max(0, autocvar_g_warmup_limit + getstatf(STAT_GAMESTARTTIME) - time);
+ warmup_timeleft = max(0, autocvar_g_warmup_limit + STAT(GAMESTARTTIME) - time);
// 5 minute check
if(autocvar_cl_announcer_maptime >= 2)
void Announcer();
+string AnnouncerOption();
+
#endif
#include "../autocvars.qh"
#include "../defs.qh"
-#include "../hud.qh"
-#include "../hud_config.qh"
+#include "../hud/all.qh"
#include "../main.qh"
#include "../mapvoting.qh"
#include "../miscfunctions.qh"
case CMD_REQUEST_COMMAND:
{
string modelname = argv(1);
- entity debugmodel_entity;
- debugmodel_entity = spawn();
+ entity debugmodel_entity = new(debugmodel);
precache_model(modelname);
_setmodel(debugmodel_entity, modelname);
setorigin(debugmodel_entity, view_origin);
debugmodel_entity.angles = view_angles;
debugmodel_entity.draw = DrawDebugModel;
- debugmodel_entity.classname = "debugmodel";
return;
}
+++ /dev/null
-#include "controlpoint.qh"
-
-#include "teamradar.qh"
-#include "../common/movetypes/movetypes.qh"
-
-.vector colormod;
-.float alpha;
-.int count;
-.float pain_finished;
-
-.bool iscaptured;
-
-.vector cp_origin, cp_bob_origin;
-.float cp_bob_spd;
-
-.vector cp_bob_dmg;
-
-.vector punchangle;
-
-.float max_health;
-
-.entity icon_realmodel;
-
-void cpicon_draw(entity this)
-{
- if(time < this.move_time) { return; }
-
- if(this.cp_bob_dmg_z > 0)
- this.cp_bob_dmg_z = this.cp_bob_dmg_z - 3 * frametime;
- else
- this.cp_bob_dmg_z = 0;
- this.cp_bob_origin_z = 4 * PI * (1 - cos(this.cp_bob_spd));
- this.cp_bob_spd = this.cp_bob_spd + 1.875 * frametime;
- this.colormod = '1 1 1' * (2 - bound(0, (this.pain_finished - time) / 10, 1));
-
- if(!this.iscaptured) this.alpha = this.health / this.max_health;
-
- if(this.iscaptured)
- {
- if (this.punchangle_x > 0)
- {
- this.punchangle_x = this.punchangle_x - 60 * frametime;
- if (this.punchangle_x < 0)
- this.punchangle_x = 0;
- }
- else if (this.punchangle_x < 0)
- {
- this.punchangle_x = this.punchangle_x + 60 * frametime;
- if (this.punchangle_x > 0)
- this.punchangle_x = 0;
- }
-
- if (this.punchangle_y > 0)
- {
- this.punchangle_y = this.punchangle_y - 60 * frametime;
- if (this.punchangle_y < 0)
- this.punchangle_y = 0;
- }
- else if (this.punchangle_y < 0)
- {
- this.punchangle_y = this.punchangle_y + 60 * frametime;
- if (this.punchangle_y > 0)
- this.punchangle_y = 0;
- }
-
- if (this.punchangle_z > 0)
- {
- this.punchangle_z = this.punchangle_z - 60 * frametime;
- if (this.punchangle_z < 0)
- this.punchangle_z = 0;
- }
- else if (this.punchangle_z < 0)
- {
- this.punchangle_z = this.punchangle_z + 60 * frametime;
- if (this.punchangle_z > 0)
- this.punchangle_z = 0;
- }
-
- this.angles_x = this.punchangle_x;
- this.angles_y = this.punchangle_y + this.move_angles_y;
- this.angles_z = this.punchangle_z;
- this.move_angles_y = this.move_angles_y + 45 * frametime;
- }
-
- setorigin(this, this.cp_origin + this.cp_bob_origin + this.cp_bob_dmg);
-}
-
-void cpicon_damage(entity this, float hp)
-{
- if(!this.iscaptured) { return; }
-
- if(hp < this.max_health * 0.25)
- setmodel(this, MDL_ONS_CP3);
- else if(hp < this.max_health * 0.50)
- setmodel(this, MDL_ONS_CP2);
- else if(hp < this.max_health * 0.75)
- setmodel(this, MDL_ONS_CP1);
- else if(hp <= this.max_health || hp >= this.max_health)
- setmodel(this, MDL_ONS_CP);
-
- this.punchangle = (2 * randomvec() - '1 1 1') * 45;
-
- this.cp_bob_dmg_z = (2 * random() - 1) * 15;
- this.pain_finished = time + 1;
- this.colormod = '2 2 2';
-
- setsize(this, CPICON_MIN, CPICON_MAX);
-}
-
-void cpicon_construct(entity this)
-{
- this.netname = "Control Point Icon";
-
- setmodel(this, MDL_ONS_CP);
- setsize(this, CPICON_MIN, CPICON_MAX);
-
- if(this.icon_realmodel == world)
- {
- this.icon_realmodel = spawn();
- setmodel(this.icon_realmodel, MDL_Null);
- setorigin(this.icon_realmodel, this.origin);
- setsize(this.icon_realmodel, CPICON_MIN, CPICON_MAX);
- this.icon_realmodel.movetype = MOVETYPE_NOCLIP;
- this.icon_realmodel.solid = SOLID_NOT;
- this.icon_realmodel.move_origin = this.icon_realmodel.origin;
- }
-
- if(this.iscaptured) { this.icon_realmodel.solid = SOLID_BBOX; }
-
- this.move_movetype = MOVETYPE_NOCLIP;
- this.solid = SOLID_NOT;
- this.movetype = MOVETYPE_NOCLIP;
- this.move_origin = this.origin;
- this.move_time = time;
- this.drawmask = MASK_NORMAL;
- this.alpha = 1;
- this.draw = cpicon_draw;
- this.cp_origin = this.origin;
- this.cp_bob_origin = '0 0 0.1';
- this.cp_bob_spd = 0;
-}
-
-.vector glowmod;
-void cpicon_changeteam(entity this)
-{
- if(this.team)
- {
- this.glowmod = Team_ColorRGB(this.team - 1);
- this.teamradar_color = Team_ColorRGB(this.team - 1);
- this.colormap = 1024 + (this.team - 1) * 17;
- }
- else
- {
- this.colormap = 1024;
- this.glowmod = '1 1 0';
- this.teamradar_color = '1 1 0';
- }
-}
-
-void ent_cpicon(entity this)
-{
- int sf = ReadByte();
-
- if(sf & CPSF_SETUP)
- {
- this.origin_x = ReadCoord();
- this.origin_y = ReadCoord();
- this.origin_z = ReadCoord();
- setorigin(this, this.origin);
-
- this.health = ReadByte();
- this.max_health = ReadByte();
- this.count = ReadByte();
- this.team = ReadByte();
- this.iscaptured = ReadByte();
-
- if(!this.count)
- this.count = (this.health - this.max_health) * frametime;
-
- cpicon_changeteam(this);
- cpicon_construct(this);
- }
-
- if(sf & CPSF_STATUS)
- {
- int _tmp = ReadByte();
- if(_tmp != this.team)
- {
- this.team = _tmp;
- cpicon_changeteam(this);
- }
-
- _tmp = ReadByte();
-
- if(_tmp != this.health)
- cpicon_damage(this, _tmp);
-
- this.health = _tmp;
- }
-}
+++ /dev/null
-#ifndef CLIENT_CONTROLPOINT_H
-#define CLIENT_CONTROLPOINT_H
-
-const vector CPICON_MIN = '-32 -32 -9';
-const vector CPICON_MAX = '32 32 25';
-
-const int CPSF_STATUS = 4;
-const int CPSF_SETUP = 8;
-
-void ent_cpicon(entity this);
-
-#endif
.int lodmodelindex0;
.int lodmodelindex1;
.int lodmodelindex2;
-void CSQCPlayer_LOD_Apply(void)
+void CSQCPlayer_LOD_Apply()
{SELFPARAM();
// LOD model loading
if(self.lodmodelindex0 != self.modelindex)
.vector glowmod;
.vector old_glowmod;
-void CSQCPlayer_ModelAppearance_PreUpdate(void)
+void CSQCPlayer_ModelAppearance_PreUpdate()
{SELFPARAM();
self.model = self.forceplayermodels_savemodel;
self.modelindex = self.forceplayermodels_savemodelindex;
self.skin = self.forceplayermodels_saveskin;
self.colormap = self.forceplayermodels_savecolormap;
}
-void CSQCPlayer_ModelAppearance_PostUpdate(void)
+void CSQCPlayer_ModelAppearance_PostUpdate()
{SELFPARAM();
self.forceplayermodels_savemodel = self.model;
self.forceplayermodels_savemodelindex = self.modelindex;
// which one is ALWAYS good?
if (!forceplayermodels_goodmodel)
{
- entity e;
- e = spawn();
+ entity e = spawn();
precache_model(cvar_defstring("_cl_playermodel"));
_setmodel(e, cvar_defstring("_cl_playermodel"));
forceplayermodels_goodmodel = e.model;
forceplayermodels_attempted = 1;
// only if this failed, find it out on our own
- entity e;
- e = spawn();
+ entity e = spawn();
_setmodel(e, autocvar__cl_playermodel); // this is harmless, see below
forceplayermodels_modelisgoodmodel = fexists(e.model);
forceplayermodels_model = e.model;
if(autocvar_cl_forcemyplayermodel != "" && autocvar_cl_forcemyplayermodel != forceplayermodels_mymodel)
{
- entity e;
- e = spawn();
+ entity e = spawn();
_setmodel(e, autocvar_cl_forcemyplayermodel); // this is harmless, see below
forceplayermodels_myisgoodmodel = fexists(e.model);
forceplayermodels_mymodel = e.model;
.int csqcmodel_framecount;
#define IS_DEAD_FRAME(f) ((f) == 0 || (f) == 1)
-void CSQCPlayer_FallbackFrame_PreUpdate(void)
+void CSQCPlayer_FallbackFrame_PreUpdate()
{SELFPARAM();
self.frame = self.csqcmodel_saveframe;
self.frame2 = self.csqcmodel_saveframe2;
LOG_INFOF("Frame %d missing in model %s, and we have no fallback - FAIL!\n", f, self.model);
return f;
}
-void CSQCPlayer_FallbackFrame_Apply(void)
+void CSQCPlayer_FallbackFrame_Apply()
{SELFPARAM();
self.frame = CSQCPlayer_FallbackFrame(self.frame);
self.frame2 = CSQCPlayer_FallbackFrame(self.frame2);
.entity tag_entity;
.int tag_entity_lastmodelindex;
.int tag_index;
-void CSQCModel_AutoTagIndex_Apply(void)
+void CSQCModel_AutoTagIndex_Apply()
{SELFPARAM();
if(self.tag_entity && wasfreed(self.tag_entity))
self.tag_entity = world;
.int csqcmodel_effects;
.int csqcmodel_modelflags;
.int csqcmodel_traileffect;
-void CSQCModel_Effects_PreUpdate(void)
+void CSQCModel_Effects_PreUpdate()
{SELFPARAM();
self.effects = self.csqcmodel_effects;
self.modelflags = self.csqcmodel_modelflags;
self.traileffect = self.csqcmodel_traileffect;
}
-void Reset_ArcBeam(void);
-void CSQCModel_Effects_PostUpdate(void)
+void Reset_ArcBeam();
+void CSQCModel_Effects_PostUpdate()
{SELFPARAM();
if (self == csqcplayer) {
if (self.csqcmodel_teleported) {
Projectile_ResetTrail(self, self.origin);
}
.int snd_looping;
-void CSQCModel_Effects_Apply(void)
+void CSQCModel_Effects_Apply()
{SELFPARAM();
int eff = self.csqcmodel_effects & ~CSQCMODEL_EF_RESPAWNGHOST;
int tref = self.csqcmodel_traileffect;
if(eff & EF_FULLBRIGHT)
self.renderflags |= RF_FULLBRIGHT;
if(eff & EF_FLAME)
- pointparticles(particleeffectnum(EFFECT_EF_FLAME), self.origin, '0 0 0', bound(0, frametime, 0.1));
+ pointparticles(EFFECT_EF_FLAME, self.origin, '0 0 0', bound(0, frametime, 0.1));
if(eff & EF_STARDUST)
- pointparticles(particleeffectnum(EFFECT_EF_STARDUST), self.origin, '0 0 0', bound(0, frametime, 0.1));
+ pointparticles(EFFECT_EF_STARDUST, self.origin, '0 0 0', bound(0, frametime, 0.1));
if(eff & EF_NOSHADOW)
self.renderflags |= RF_NOSHADOW;
if(eff & EF_NODEPTHTEST)
+++ /dev/null
-#include "damage.qh"
-
-#include "gibs.qh"
-#include "../common/deathtypes/all.qh"
-#include "../common/movetypes/movetypes.qh"
-#include "../common/vehicles/all.qh"
-#include "../common/weapons/all.qh"
-
-.entity tag_entity;
-
-.float cnt;
-.int state;
-.bool isplayermodel;
-
-void DamageEffect_Think()
-{SELFPARAM();
- // if particle distribution is enabled, slow ticrate by total number of damages
- if(autocvar_cl_damageeffect_distribute)
- self.nextthink = time + autocvar_cl_damageeffect_ticrate * self.owner.total_damages;
- else
- self.nextthink = time + autocvar_cl_damageeffect_ticrate;
-
- if(time >= self.cnt || !self.owner || !self.owner.modelindex || !self.owner.drawmask)
- {
- // time is up or the player got gibbed / disconnected
- self.owner.total_damages = max(0, self.owner.total_damages - 1);
- remove(self);
- return;
- }
- if(self.state && !self.owner.csqcmodel_isdead)
- {
- // if the player was dead but is now alive, it means he respawned
- // if so, clear his damage effects, or damages from his dead body will be copied back
- self.owner.total_damages = max(0, self.owner.total_damages - 1);
- remove(self);
- return;
- }
- self.state = self.owner.csqcmodel_isdead;
- if(self.owner.isplayermodel && (self.owner.entnum == player_localentnum) && !autocvar_chase_active)
- return; // if we aren't using a third person camera, hide our own effects
-
- // now generate the particles
- vector org;
- org = gettaginfo(self, 0); // origin at attached location
- pointparticles(self.team, org, '0 0 0', 1);
-}
-
-void DamageEffect(vector hitorg, float thedamage, int type, int specnum)
-{SELFPARAM();
- // particle effects for players and objects damaged by weapons (eg: flames coming out of victims shot with rockets)
-
- int nearestbone = 0;
- float life;
- string specstr, effectname;
- entity e;
-
- if(!autocvar_cl_damageeffect || autocvar_cl_gentle || autocvar_cl_gentle_damage)
- return;
- if(!self || !self.modelindex || !self.drawmask)
- return;
-
- // if this is a rigged mesh, the effect will show on the bone where damage was dealt
- // we do this by choosing the skeletal bone closest to the impact, and attaching our entity to it
- // if there's no skeleton, object origin will automatically be selected
- FOR_EACH_TAG(self)
- {
- if(!tagnum)
- continue; // skip empty bones
- // blacklist bones positioned outside the mesh, or the effect will be floating
- // TODO: Do we have to do it this way? Why do these bones exist at all?
- if(gettaginfo_name == "master" || gettaginfo_name == "knee_L" || gettaginfo_name == "knee_R" || gettaginfo_name == "leg_L" || gettaginfo_name == "leg_R")
- continue; // player model bone blacklist
-
- // now choose the bone closest to impact origin
- if(nearestbone == 0 || vlen(hitorg - gettaginfo(self, tagnum)) <= vlen(hitorg - gettaginfo(self, nearestbone)))
- nearestbone = tagnum;
- }
- gettaginfo(self, nearestbone); // set gettaginfo_name
-
- // return if we reached our damage effect limit or damages are disabled
- // TODO: When the limit is reached, it would be better if the oldest damage was removed instead of not adding a new one
- if(nearestbone)
- {
- if(self.total_damages >= autocvar_cl_damageeffect_bones)
- return; // allow multiple damages on skeletal models
- }
- else
- {
- if(autocvar_cl_damageeffect < 2 || self.total_damages)
- return; // allow a single damage on non-skeletal models
- }
-
- life = bound(autocvar_cl_damageeffect_lifetime_min, thedamage * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max);
-
- effectname = DEATH_WEAPONOF(type).netname;
-
- if(substring(effectname, strlen(effectname) - 5, 5) == "BLOOD")
- {
- if(self.isplayermodel)
- {
- specstr = species_prefix(specnum);
- specstr = substring(specstr, 0, strlen(specstr) - 1);
- effectname = strreplace("BLOOD", specstr, effectname);
- }
- else { return; } // objects don't bleed
- }
-
- e = spawn();
- setmodel(e, MDL_Null); // necessary to attach and read origin
- setattachment(e, self, gettaginfo_name); // attach to the given bone
- e.classname = "damage";
- e.owner = self;
- e.cnt = time + life;
- e.team = _particleeffectnum(effectname);
- e.think = DamageEffect_Think;
- e.nextthink = time;
- self.total_damages += 1;
-}
-
-void Ent_DamageInfo(float isNew)
-{SELFPARAM();
- float thedamage, rad, edge, thisdmg;
- bool hitplayer = false;
- int species, forcemul;
- vector force, thisforce;
-
- w_deathtype = ReadShort();
- w_issilent = (w_deathtype & 0x8000);
- w_deathtype = (w_deathtype & 0x7FFF);
-
- w_org.x = ReadCoord();
- w_org.y = ReadCoord();
- w_org.z = ReadCoord();
-
- thedamage = ReadByte();
- rad = ReadByte();
- edge = ReadByte();
- force = decompressShortVector(ReadShort());
- species = ReadByte();
-
- if (!isNew)
- return;
-
- if(rad < 0)
- {
- rad = -rad;
- forcemul = -1;
- }
- else
- forcemul = 1;
-
- for(entity e = findradius(w_org, rad + MAX_DAMAGEEXTRARADIUS); e; e = e.chain)
- {
- setself(e);
- // attached ents suck
- if(self.tag_entity)
- continue;
-
- vector nearest = NearestPointOnBox(self, w_org);
- if(rad)
- {
- thisdmg = ((vlen (nearest - w_org) - bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
- if(thisdmg >= 1)
- continue;
- if(thisdmg < 0)
- thisdmg = 0;
- if(thedamage)
- {
- thisdmg = thedamage + (edge - thedamage) * thisdmg;
- thisforce = forcemul * vlen(force) * (thisdmg / thedamage) * normalize(self.origin - w_org);
- }
- else
- {
- thisdmg = 0;
- thisforce = forcemul * vlen(force) * normalize(self.origin - w_org);
- }
- }
- else
- {
- if(vlen(nearest - w_org) > bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS))
- continue;
-
- thisdmg = thedamage;
- thisforce = forcemul * force;
- }
-
- if(self.damageforcescale)
- if(vlen(thisforce))
- {
- self.move_velocity = self.move_velocity + damage_explosion_calcpush(self.damageforcescale * thisforce, self.move_velocity, autocvar_g_balance_damagepush_speedfactor);
- self.move_flags &= ~FL_ONGROUND;
- }
-
- if(w_issilent)
- self.silent = 1;
-
- if(self.event_damage)
- self.event_damage(thisdmg, w_deathtype, w_org, thisforce);
-
- DamageEffect(w_org, thisdmg, w_deathtype, species);
-
- if(self.isplayermodel)
- hitplayer = true; // this impact damaged a player
- }
- setself(this);
-
- if(DEATH_ISVEHICLE(w_deathtype))
- {
- traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
- if(trace_plane_normal != '0 0 0')
- w_backoff = trace_plane_normal;
- else
- w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16));
-
- setorigin(self, w_org + w_backoff * 2); // for sound() calls
-
- switch(DEATH_ENT(w_deathtype))
- {
- case DEATH_VH_CRUSH:
- break;
-
- // spiderbot
- case DEATH_VH_SPID_MINIGUN:
- sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_SPIDERBOT_MINIGUN_IMPACT), self.origin, w_backoff * 1000, 1);
- break;
- case DEATH_VH_SPID_ROCKET:
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_SPIDERBOT_ROCKET_EXPLODE), self.origin, w_backoff * 1000, 1);
- break;
- case DEATH_VH_SPID_DEATH:
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN);
- pointparticles(particleeffectnum(EFFECT_EXPLOSION_BIG), self.origin, w_backoff * 1000, 1);
- break;
-
- case DEATH_VH_WAKI_GUN:
- sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_RACER_IMPACT), self.origin, w_backoff * 1000, 1);
- break;
- case DEATH_VH_WAKI_ROCKET:
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_RACER_ROCKET_EXPLODE), self.origin, w_backoff * 1000, 1);
- break;
- case DEATH_VH_WAKI_DEATH:
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN);
- pointparticles(particleeffectnum(EFFECT_EXPLOSION_BIG), self.origin, w_backoff * 1000, 1);
- break;
-
- case DEATH_VH_RAPT_CANNON:
- sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_RAPTOR_CANNON_IMPACT), self.origin, w_backoff * 1000, 1);
- break;
- case DEATH_VH_RAPT_FRAGMENT:
- float i;
- vector ang, vel;
- for(i = 1; i < 4; ++i)
- {
- vel = normalize(w_org - (w_org + normalize(force) * 16)) + randomvec() * 128;
- ang = vectoangles(vel);
- RaptorCBShellfragToss(w_org, vel, ang + '0 0 1' * (120 * i));
- }
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_RAPTOR_BOMB_SPREAD), self.origin, w_backoff * 1000, 1);
- break;
- case DEATH_VH_RAPT_BOMB:
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_RAPTOR_BOMB_IMPACT), self.origin, w_backoff * 1000, 1);
- break;
- case DEATH_VH_RAPT_DEATH:
- sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_MIN);
- pointparticles(particleeffectnum(EFFECT_EXPLOSION_BIG), self.origin, w_backoff * 1000, 1);
- break;
- case DEATH_VH_BUMB_GUN:
- sound(self, CH_SHOTS, SND_FIREBALL_IMPACT2, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_BIGPLASMA_IMPACT), self.origin, w_backoff * 1000, 1);
- break;
- }
- }
-
-
- if(DEATH_ISTURRET(w_deathtype))
- {
- traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
- if(trace_plane_normal != '0 0 0')
- w_backoff = trace_plane_normal;
- else
- w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16));
-
- setorigin(self, w_org + w_backoff * 2); // for sound() calls
-
- switch(DEATH_ENT(w_deathtype))
- {
- case DEATH_TURRET_EWHEEL:
- sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_MIN);
- pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), self.origin, w_backoff * 1000, 1);
- break;
-
- case DEATH_TURRET_FLAC:
- pointparticles(particleeffectnum(EFFECT_HAGAR_EXPLODE), w_org, '0 0 0', 1);
- sound(self, CH_SHOTS, SND_HAGEXP_RANDOM(), VOL_BASE, ATTEN_NORM);
- break;
-
- case DEATH_TURRET_MLRS:
- case DEATH_TURRET_HK:
- case DEATH_TURRET_WALK_ROCKET:
- case DEATH_TURRET_HELLION:
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN);
- pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), self.origin, w_backoff * 1000, 1);
- break;
-
- case DEATH_TURRET_MACHINEGUN:
- case DEATH_TURRET_WALK_GUN:
- sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_MACHINEGUN_IMPACT), self.origin, w_backoff * 1000, 1);
- break;
-
- case DEATH_TURRET_PLASMA:
- sound(self, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_MIN);
- pointparticles(particleeffectnum(EFFECT_ELECTRO_IMPACT), self.origin, w_backoff * 1000, 1);
- break;
-
- case DEATH_TURRET_WALK_MELEE:
- sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTEN_MIN);
- pointparticles(particleeffectnum(EFFECT_TE_SPARK), self.origin, w_backoff * 1000, 1);
- break;
-
- case DEATH_TURRET_PHASER:
- break;
-
- case DEATH_TURRET_TESLA:
- te_smallflash(self.origin);
- break;
-
- }
- }
-
- // TODO spawn particle effects and sounds based on w_deathtype
- if(!DEATH_ISSPECIAL(w_deathtype))
- if(!hitplayer || rad) // don't show ground impacts for hitscan weapons if a player was hit
- {
- Weapon hitwep = DEATH_WEAPONOF(w_deathtype);
- w_random = prandom();
-
- traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
- if(trace_fraction < 1 && hitwep != WEP_VORTEX && hitwep != WEP_VAPORIZER)
- w_backoff = trace_plane_normal;
- else
- w_backoff = -1 * normalize(force);
- setorigin(self, w_org + w_backoff * 2); // for sound() calls
-
- if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) {
- hitwep.wr_impacteffect(hitwep);
- }
- }
-}
+++ /dev/null
-#ifndef CLIENT_DAMAGE_H
-#define CLIENT_DAMAGE_H
-
-.float total_damages; // number of effects which currently are attached to a player
-
-void Ent_DamageInfo(float isNew);
-
-#endif
#endif
// Basic variables
-.float enttype; // entity type sent from server
-.int sv_entnum; // entity number sent from server
+.int enttype; // entity type sent from server
+.int sv_entnum; // entity number sent from server
.int team;
.int team_size;
+++ /dev/null
-#include "effects.qh"
-
-/*
-.vector fx_start;
-.vector fx_end;
-.float fx_with;
-.string fx_texture;
-.float fx_lifetime;
-
-void b_draw()
-{
- //Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, 0, time * 3, '1 1 1', 0.7, DRAWFLAG_ADDITIVE, view_origin);
- Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, (self.fx_with/256), 0, '1 1 1', 1, DRAWFLAG_ADDITIVE, view_origin);
-
-}
-void b_make(vector s,vector e, string t,float l,float z)
-{
- entity b;
- b = spawn();
- b.fx_texture = t;
- b.fx_start = s;
- b.fx_end = e;
- b.fx_with = z;
- b.think = SUB_Remove;
- b.nextthink = time + l;
- b.draw = b_draw;
-
- //b.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
-}
-*/
-
-void cl_effects_lightningarc(vector from, vector to,float seglength,float drifts,float drifte,float branchfactor,float branchfactor_add)
-{
- vector direction,dirnew, pos, pos_l;
- float length, steps, steplength, i,drift;
-
- length = vlen(from - to);
- if(length < 1)
- return;
-
- // Use at most 16 te_lightning1 segments, as these eat up beam list segments.
- // TODO: Change this to R_BeginPolygon code, then we no longer have this limit.
- steps = min(16, floor(length / seglength));
- if(steps < 1)
- {
- te_lightning1(world,from,to);
- return;
- }
-
- steplength = length / steps;
- direction = normalize(to - from);
- pos_l = from;
- if(length > seglength)
- {
- for(i = 1; i < steps; i += 1)
- {
- drift = drifts * (1 - (i / steps)) + drifte * (i / steps);
- dirnew = normalize(direction * (1 - drift) + randomvec() * drift);
- pos = pos_l + dirnew * steplength;
- te_lightning1(world,pos_l,pos);
- // WTF endless recursion if branchfactor is 1.0 (possibly due to adding branchfactor_add). FIXME
- // if(random() < branchfactor)
- // cl_effects_lightningarc(pos, pos + (dirnew * length * 0.25),seglength,drifts,drifte,min(branchfactor + branchfactor_add,1),branchfactor_add);
-
- pos_l = pos;
- }
- te_lightning1(world,pos_l,to);
-
- }
- else
- te_lightning1(world,from,to);
-
-}
-
-void Net_ReadLightningarc()
-{
- vector from, to;
-
- from.x = ReadCoord(); from.y = ReadCoord(); from.z = ReadCoord();
- to.x = ReadCoord(); to.y = ReadCoord(); to.z = ReadCoord();
-
- if(autocvar_cl_effects_lightningarc_simple)
- {
- te_lightning1(world,from,to);
- }
- else
- {
- float seglength, drifts, drifte, branchfactor, branchfactor_add;
-
- seglength = autocvar_cl_effects_lightningarc_segmentlength;
- drifts = autocvar_cl_effects_lightningarc_drift_start;
- drifte = autocvar_cl_effects_lightningarc_drift_end;
- branchfactor = autocvar_cl_effects_lightningarc_branchfactor_start;
- branchfactor_add = autocvar_cl_effects_lightningarc_branchfactor_add;
-
- cl_effects_lightningarc(from,to,seglength,drifts,drifte,branchfactor,branchfactor_add);
- }
-
-}
-void Net_ReadArc() { Net_ReadLightningarc(); }
+++ /dev/null
-#ifndef CLIENT_EFFECTS_H
-#define CLIENT_EFFECTS_H
-
-void Net_ReadArc();
-
-#endif
+++ /dev/null
-#include "generator.qh"
-
-#include "teamradar.qh"
-#include "../common/movetypes/movetypes.qh"
-
-.float alpha;
-.float scale;
-.int count;
-.float max_health;
-
-void ons_generator_ray_draw(entity this)
-{
- if(time < self.move_time)
- return;
-
- self.move_time = time + 0.05;
-
- if(self.count > 10)
- {
- remove(self);
- return;
- }
-
- if(self.count > 5)
- self.alpha -= 0.1;
- else
- self.alpha += 0.1;
-
- self.scale += 0.2;
- self.count +=1;
-}
-
-void ons_generator_ray_spawn(vector org)
-{
- entity e;
- e = spawn();
- e.classname = "ons_ray";
- setmodel(e, MDL_ONS_RAY);
- setorigin(e, org);
- e.angles = randomvec() * 360;
- e.move_origin = org;
- e.movetype = MOVETYPE_NONE;
- e.alpha = 0;
- e.scale = random() * 5 + 8;
- e.move_time = time + 0.05;
- e.drawmask = MASK_NORMAL;
- e.draw = ons_generator_ray_draw;
-}
-
-void generator_draw(entity this)
-{
- if(time < self.move_time)
- return;
-
- if(self.health > 0)
- {
- // damaged fx (less probable the more damaged is the generator)
- if(random() < 0.9 - self.health / self.max_health)
- if(random() < 0.01)
- {
- pointparticles(particleeffectnum(EFFECT_ELECTRO_BALLEXPLODE), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);
- sound(self, CH_TRIGGER, SND_ONS_ELECTRICITY_EXPLODE, VOL_BASE, ATTEN_NORM);
- }
- else
- pointparticles(particleeffectnum(EFFECT_ONS_GENERATOR_DAMAGED), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);
-
- self.move_time = time + 0.1;
-
- return;
- }
-
- if(self.count <= 0)
- return;
-
- vector org;
- int i;
-
- // White shockwave
- if(self.count==40||self.count==20)
- {
- sound(self, CH_TRIGGER, SND_ONS_SHOCKWAVE, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_ELECTRO_COMBO), self.origin, '0 0 0', 6);
- }
-
- // rays
- if(random() > 0.25)
- {
- ons_generator_ray_spawn(self.origin);
- }
-
- // Spawn fire balls
- for(i=0;i < 10;++i)
- {
- org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');
- pointparticles(particleeffectnum(EFFECT_ONS_GENERATOR_GIB), org, '0 0 0', 1);
- }
-
- // Short explosion sound + small explosion
- if(random() < 0.25)
- {
- te_explosion(self.origin);
- sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
- }
-
- // Particles
- org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');
- pointparticles(particleeffectnum(EFFECT_ONS_GENERATOR_EXPLODE), org, '0 0 0', 1);
-
- // Final explosion
- if(self.count==1)
- {
- org = self.origin;
- te_explosion(org);
- pointparticles(particleeffectnum(EFFECT_ONS_GENERATOR_EXPLODE2), org, '0 0 0', 1);
- sound(self, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
- }
-
- self.move_time = time + 0.05;
-
- self.count -= 1;
-}
-
-void generator_damage(float hp)
-{SELFPARAM();
- if(hp <= 0)
- setmodel(self, MDL_ONS_GEN_DEAD);
- else if(hp < self.max_health * 0.10)
- setmodel(self, MDL_ONS_GEN9);
- else if(hp < self.max_health * 0.20)
- setmodel(self, MDL_ONS_GEN8);
- else if(hp < self.max_health * 0.30)
- setmodel(self, MDL_ONS_GEN7);
- else if(hp < self.max_health * 0.40)
- setmodel(self, MDL_ONS_GEN6);
- else if(hp < self.max_health * 0.50)
- setmodel(self, MDL_ONS_GEN5);
- else if(hp < self.max_health * 0.60)
- setmodel(self, MDL_ONS_GEN4);
- else if(hp < self.max_health * 0.70)
- setmodel(self, MDL_ONS_GEN3);
- else if(hp < self.max_health * 0.80)
- setmodel(self, MDL_ONS_GEN2);
- else if(hp < self.max_health * 0.90)
- setmodel(self, MDL_ONS_GEN1);
- else if(hp <= self.max_health || hp >= self.max_health)
- setmodel(self, MDL_ONS_GEN);
-
- setsize(self, GENERATOR_MIN, GENERATOR_MAX);
-}
-
-void generator_construct()
-{SELFPARAM();
- self.netname = "Generator";
- self.classname = "onslaught_generator";
-
- setorigin(self, self.origin);
- setmodel(self, MDL_ONS_GEN);
- setsize(self, GENERATOR_MIN, GENERATOR_MAX);
-
- self.move_movetype = MOVETYPE_NOCLIP;
- self.solid = SOLID_BBOX;
- self.movetype = MOVETYPE_NOCLIP;
- self.move_origin = self.origin;
- self.move_time = time;
- self.drawmask = MASK_NORMAL;
- self.alpha = 1;
- self.draw = generator_draw;
-}
-
-.vector glowmod;
-void generator_changeteam()
-{SELFPARAM();
- if(self.team)
- {
- self.glowmod = Team_ColorRGB(self.team - 1);
- self.teamradar_color = Team_ColorRGB(self.team - 1);
- self.colormap = 1024 + (self.team - 1) * 17;
- }
- else
- {
- self.colormap = 1024;
- self.glowmod = '1 1 0';
- self.teamradar_color = '1 1 0';
- }
-}
-
-void ent_generator()
-{SELFPARAM();
- int sf = ReadByte();
-
- if(sf & GSF_SETUP)
- {
- self.origin_x = ReadCoord();
- self.origin_y = ReadCoord();
- self.origin_z = ReadCoord();
- setorigin(self, self.origin);
-
- self.health = ReadByte();
- self.max_health = ReadByte();
- self.count = ReadByte();
- self.team = ReadByte();
-
- if(!self.count)
- self.count = 40;
-
- generator_changeteam();
- generator_construct();
- }
-
- if(sf & GSF_STATUS)
- {
- int _tmp;
- _tmp = ReadByte();
- if(_tmp != self.team)
- {
- self.team = _tmp;
- generator_changeteam();
- }
-
- _tmp = ReadByte();
-
- if(_tmp != self.health)
- generator_damage(_tmp);
-
- self.health = _tmp;
- }
-}
+++ /dev/null
-#ifndef CLIENT_GENERATOR_H
-#define CLIENT_GENERATOR_H
-const vector GENERATOR_MIN = '-52 -52 -14';
-const vector GENERATOR_MAX = '52 52 75';
-
-const int GSF_STATUS = 4;
-const int GSF_SETUP = 8;
-
-void ent_generator();
-#endif
+++ /dev/null
-#include "gibs.qh"
-
-#include "rubble.qh"
-#include "../common/movetypes/movetypes.qh"
-
-.float scale;
-.float alpha;
-.float cnt;
-.float gravity;
-
-void Gib_Delete()
-{SELFPARAM();
- remove(self);
-}
-
-string species_prefix(int specnum)
-{
- switch(specnum)
- {
- case SPECIES_HUMAN: return "";
- case SPECIES_ALIEN: return "alien_";
- case SPECIES_ROBOT_SHINY: return "robot_";
- case SPECIES_ROBOT_RUSTY: return "robot_"; // use the same effects, only different gibs
- case SPECIES_ROBOT_SOLID: return "robot_"; // use the same effects, only different gibs
- case SPECIES_ANIMAL: return "animal_";
- case SPECIES_RESERVED: return "reserved_";
- default: return "";
- }
-}
-
-void Gib_setmodel(entity gib, string mdlname, int specnum)
-{
- switch(specnum)
- {
- case SPECIES_ROBOT_RUSTY:
- case SPECIES_ROBOT_SHINY:
- case SPECIES_ROBOT_SOLID:
- if(specnum != SPECIES_ROBOT_SOLID || mdlname == "models/gibs/chunk.mdl")
- {
- if(mdlname == "models/gibs/bloodyskull.md3")
- setmodel(gib, MDL_GIB_ROBO);
- else
- setmodel(gib, MDL_GIB_ROBO_RANDOM());
- if(specnum == SPECIES_ROBOT_SHINY)
- {
- gib.skin = 1;
- gib.colormod = '2 2 2';
- }
- gib.scale = 1;
- break;
- }
- default:
- _setmodel(gib, mdlname);
- gib.skin = specnum;
- break;
- }
-}
-
-void new_te_bloodshower (int ef, vector org, float explosionspeed, int howmany)
-{
- float i, pmod;
- pmod = autocvar_cl_particles_quality;
- for (i = 0; i < 50 * pmod; ++i)
- pointparticles(ef, org, randomvec() * explosionspeed, howmany / 50);
-}
-
-void SUB_RemoveOnNoImpact()
-{
- if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
- Gib_Delete();
-}
-
-void Gib_Touch()
-{SELFPARAM();
- // TODO maybe bounce of walls, make more gibs, etc.
-
- if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
- {
- Gib_Delete();
- return;
- }
-
- if(!self.silent)
- sound(self, CH_PAIN, SND_GIB_SPLAT_RANDOM(), VOL_BASE, ATTEN_NORM);
- pointparticles(_particleeffectnum(strcat(species_prefix(self.cnt), "blood")), self.origin + '0 0 1', '0 0 30', 10);
-
- Gib_Delete();
-}
-
-void Gib_Draw(entity this)
-{
- vector oldorg;
- oldorg = self.origin;
-
- Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
- if(wasfreed(self))
- return;
-
- if(self.touch == Gib_Touch) // don't do this for the "chunk" thingie...
- // TODO somehow make it spray in a direction dependent on self.angles
- trailparticles(self, _particleeffectnum(strcat(species_prefix(self.cnt), EFFECT_TR_SLIGHTBLOOD.eent_eff_name)), oldorg, self.origin);
- else
- trailparticles(self, _particleeffectnum(strcat(species_prefix(self.cnt), EFFECT_TR_BLOOD.eent_eff_name)), oldorg, self.origin);
-
- self.renderflags = 0;
-
- // make gibs die faster at low view quality
- // if view_quality is 0.5, we want to have them die twice as fast
- self.nextthink -= frametime * (1 / bound(0.01, view_quality, 1.00) - 1);
-
- self.alpha = bound(0, self.nextthink - time, 1);
-
- if(self.alpha < ALPHA_MIN_VISIBLE)
- {
- self.drawmask = 0;
- Gib_Delete();
- }
-}
-
-void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector vrand, int specnum, bool destroyontouch, bool issilent)
-{
- entity gib;
-
- // TODO remove some gibs according to cl_nogibs
- gib = RubbleNew("gib");
- gib.classname = "gib";
- gib.move_movetype = MOVETYPE_BOUNCE;
- gib.gravity = 1;
- gib.solid = SOLID_CORPSE;
- gib.cnt = specnum;
- gib.silent = issilent;
- Gib_setmodel(gib, mdlname, specnum);
-
- setsize (gib, '-8 -8 -8', '8 8 8');
-
- gib.draw = Gib_Draw;
- if(destroyontouch)
- gib.move_touch = Gib_Touch;
- else
- gib.move_touch = SUB_RemoveOnNoImpact;
-
- // don't spawn gibs inside solid - just don't
- if(org != safeorg)
- {
- tracebox(safeorg, gib.mins, gib.maxs, org, MOVE_NOMONSTERS, gib);
- org = trace_endpos;
- }
-
- gib.move_origin = org;
- setorigin(gib, org);
- gib.move_velocity = vconst * autocvar_cl_gibs_velocity_scale + vrand * autocvar_cl_gibs_velocity_random + '0 0 1' * autocvar_cl_gibs_velocity_up;
- gib.move_avelocity = prandomvec() * vlen(gib.move_velocity) * autocvar_cl_gibs_avelocity_scale;
- gib.move_time = time;
- gib.damageforcescale = autocvar_cl_gibs_damageforcescale;
-
- gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15);
- gib.drawmask = MASK_NORMAL;
-
- RubbleLimit("gib", autocvar_cl_gibs_maxcount, Gib_Delete);
-}
-
-void Ent_GibSplash(bool isNew)
-{SELFPARAM();
- int amount, type, specnum;
- vector org, vel;
- string specstr;
- bool issilent;
- string gentle_prefix = "morphed_";
-
- float randomvalue;
- int c;
-
- type = ReadByte(); // gibbage type
- amount = ReadByte() / 16.0; // gibbage amount
- org.x = ReadShort() * 4 + 2;
- org.y = ReadShort() * 4 + 2;
- org.z = ReadShort() * 4 + 2;
- vel = decompressShortVector(ReadShort());
-
- float cl_gentle_gibs = autocvar_cl_gentle_gibs;
- if(cl_gentle_gibs || autocvar_cl_gentle)
- type |= 0x80; // set gentle bit
-
- if(type & 0x80)
- {
- if(cl_gentle_gibs == 2)
- gentle_prefix = "";
- else if(cl_gentle_gibs == 3)
- gentle_prefix = "happy_";
- }
- else if(autocvar_cl_particlegibs)
- {
- type |= 0x80;
- gentle_prefix = "particlegibs_";
- }
-
- if (!(cl_gentle_gibs || autocvar_cl_gentle))
- amount *= 1 - autocvar_cl_nogibs;
-
- if(autocvar_ekg)
- amount *= 5;
-
- if(amount <= 0 || !isNew)
- return;
-
- setorigin(self, org); // for the sounds
-
- specnum = (type & 0x78) / 8; // blood/gibmodel type: using four bits (0..7, bit indexes 3,4,5)
- issilent = (type & 0x40);
- type = type & 0x87; // remove the species bits: bit 7 = gentle, bit 0,1,2 = kind of gib
- specstr = species_prefix(specnum);
-
- switch(type)
- {
- case 0x01:
- if(!issilent)
- sound (self, CH_PAIN, SND_GIB, VOL_BASE, ATTEN_NORM);
-
- if(prandom() < amount)
- TossGib ("models/gibs/eye.md3", org, org, vel, prandomvec() * 150, specnum, 0, issilent);
- new_te_bloodshower(_particleeffectnum(strcat(specstr, "bloodshower")), org, 1200, amount);
- if(prandom() < amount)
- TossGib ("models/gibs/bloodyskull.md3", org, org + 16 * prandomvec(), vel, prandomvec() * 100, specnum, 0, issilent);
-
- for(c = 0; c < amount; ++c)
- {
- randomvalue = amount - c;
-
- if(prandom() < randomvalue)
- TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent);
- if(prandom() < randomvalue)
- TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent);
- if(prandom() < randomvalue)
- TossGib ("models/gibs/chest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent);
- if(prandom() < randomvalue)
- TossGib ("models/gibs/smallchest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent);
- if(prandom() < randomvalue)
- TossGib ("models/gibs/leg1.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent);
- if(prandom() < randomvalue)
- TossGib ("models/gibs/leg2.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent);
-
- // these splat on impact
- if(prandom() < randomvalue)
- TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
- if(prandom() < randomvalue)
- TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
- if(prandom() < randomvalue)
- TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
- if(prandom() < randomvalue)
- TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
- }
- break;
- case 0x02:
- pointparticles(_particleeffectnum(strcat(specstr, "blood")), org, vel, amount * 16);
- break;
- case 0x03:
- if(prandom() < amount)
- TossGib ("models/gibs/chunk.mdl", org, org, vel, prandomvec() * (prandom() * 30 + 20), specnum, 1, issilent); // TODO maybe adjust to more randomization?
- break;
- case 0x81:
- pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_dissolve")), org, vel, amount);
- break;
- case 0x82:
- pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_hit")), org, vel, amount * 16);
- break;
- case 0x83:
- // no gibs in gentle mode, sorry
- break;
- }
-}
+++ /dev/null
-#ifndef CLIENT_GIBS_H
-#define CLIENT_GIBS_H
-
-.vector colormod;
-
-.bool silent;
-
-void Gib_Delete();
-
-string species_prefix(int specnum);
-
-void Gib_setmodel(entity gib, string mdlname, int specnum);
-
-void new_te_bloodshower (int ef, vector org, float explosionspeed, int howmany);
-
-void SUB_RemoveOnNoImpact();
-
-void Gib_Touch();
-
-void Gib_Draw(entity this);
-
-void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector vrand, int specnum, bool destroyontouch, bool issilent);
-
-void Ent_GibSplash(bool isNew);
-
-#endif
#include "../lib/warpzone/common.qh"
entityclass(Hook);
-class(Hook) .float HookType; // ENT_CLIENT_*
+class(Hook) .entity HookType; // ENT_CLIENT_*
class(Hook) .vector origin;
class(Hook) .vector velocity;
class(Hook) .float HookSilent;
switch(self.HookType)
{
default:
- case ENT_CLIENT_HOOK:
+ case NET_ENT_CLIENT_HOOK:
vs = hook_shotorigin[s];
break;
- case ENT_CLIENT_ARC_BEAM:
+ case NET_ENT_CLIENT_ARC_BEAM:
vs = lightning_shotorigin[s];
break;
}
switch(self.HookType)
{
default:
- case ENT_CLIENT_HOOK:
+ case NET_ENT_CLIENT_HOOK:
a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z;
b = self.origin;
break;
- case ENT_CLIENT_ARC_BEAM:
+ case NET_ENT_CLIENT_ARC_BEAM:
if(self.HookRange)
b = view_origin + view_forward * self.HookRange;
else
switch(self.HookType)
{
default:
- case ENT_CLIENT_HOOK:
+ case NET_ENT_CLIENT_HOOK:
a = self.velocity;
b = self.origin;
break;
- case ENT_CLIENT_ARC_BEAM:
+ case NET_ENT_CLIENT_ARC_BEAM:
a = self.origin;
b = self.velocity;
break;
switch(self.HookType)
{
default:
- case ENT_CLIENT_HOOK:
+ case NET_ENT_CLIENT_HOOK:
intensity = 1;
offset = 0;
switch(t)
default: tex = "particles/hook_white"; rgb = getcsqcplayercolor(self.sv_entnum); break;
}
break;
- case ENT_CLIENT_ARC_BEAM: // todo
+ case NET_ENT_CLIENT_ARC_BEAM: // todo
intensity = bound(0.2, 1 + Noise_Pink(self, frametime) * 1 + Noise_Burst(self, frametime, 0.03) * 0.3, 2);
offset = Noise_Brown(self, frametime) * 10;
tex = "particles/lgbeam";
Draw_GrapplingHook_trace_callback_rnd = offset;
Draw_GrapplingHook_trace_callback_rgb = rgb;
Draw_GrapplingHook_trace_callback_a = intensity;
- WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, ((self.HookType == ENT_CLIENT_HOOK) ? MOVE_NOTHING : MOVE_NORMAL), world, world, Draw_GrapplingHook_trace_callback);
+ WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, ((self.HookType == NET_ENT_CLIENT_HOOK) ? MOVE_NOTHING : MOVE_NORMAL), world, world, Draw_GrapplingHook_trace_callback);
Draw_GrapplingHook_trace_callback_tex = string_null;
atrans = WarpZone_TransformOrigin(WarpZone_trace_transform, a);
switch(self.HookType)
{
default:
- case ENT_CLIENT_HOOK:
+ case NET_ENT_CLIENT_HOOK:
if(vlen(trace_endpos - atrans) > 0.5)
{
setorigin(self, trace_endpos); // hook endpoint!
self.drawmask = 0;
}
break;
- case ENT_CLIENT_ARC_BEAM:
+ case NET_ENT_CLIENT_ARC_BEAM:
setorigin(self, a); // beam origin!
break;
}
switch(self.HookType)
{
default:
- case ENT_CLIENT_HOOK:
+ case NET_ENT_CLIENT_HOOK:
break;
- case ENT_CLIENT_ARC_BEAM:
- pointparticles(particleeffectnum(EFFECT_ARC_LIGHTNING2), trace_endpos, normalize(atrans - trace_endpos), frametime * intensity); // todo: new effect
+ case NET_ENT_CLIENT_ARC_BEAM:
+ pointparticles(EFFECT_ARC_LIGHTNING2, trace_endpos, normalize(atrans - trace_endpos), frametime * intensity); // todo: new effect
break;
}
}
sound (self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
}
-void Ent_ReadHook(float bIsNew, float type)
-{SELFPARAM();
- self.HookType = type;
+NET_HANDLE(ENT_CLIENT_HOOK, bool bIsNew)
+{
+ self.HookType = NET_ENT_CLIENT_HOOK;
int sf = ReadByte();
switch(self.HookType)
{
default:
- case ENT_CLIENT_HOOK:
+ case NET_ENT_CLIENT_HOOK:
self.HookRange = 0;
break;
- case ENT_CLIENT_ARC_BEAM:
+ case NET_ENT_CLIENT_ARC_BEAM:
self.HookRange = ReadCoord();
break;
}
switch(self.HookType)
{
default:
- case ENT_CLIENT_HOOK:
+ case NET_ENT_CLIENT_HOOK:
// for the model
setmodel(self, MDL_HOOK);
self.drawmask = MASK_NORMAL;
break;
- case ENT_CLIENT_ARC_BEAM:
+ case NET_ENT_CLIENT_ARC_BEAM:
sound (self, CH_SHOTS_SINGLE, SND_LGBEAM_FLY, VOL_BASE, ATTEN_NORM);
break;
}
}
self.teleport_time = time + 10;
+ return true;
}
// TODO: hook: temporarily transform self.origin for drawing the model along warpzones!
void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg);
-void Ent_ReadHook(float bIsNew, float type);
-
#endif
+++ /dev/null
-#include "hud.qh"
-
-#include "hud_config.qh"
-#include "mapvoting.qh"
-#include "scoreboard.qh"
-#include "teamradar.qh"
-#include "t_items.qh"
-#include "../common/buffs/all.qh"
-#include "../common/deathtypes/all.qh"
-#include "../common/items/all.qc"
-#include "../common/mapinfo.qh"
-#include "../common/mutators/mutator/waypoints/all.qh"
-#include "../common/nades/all.qh"
-#include "../common/stats.qh"
-#include "../lib/csqcmodel/cl_player.qh"
-// TODO: remove
-#include "../server/mutators/mutator/gamemode_ctf.qc"
-
-
-/*
-==================
-Misc HUD functions
-==================
-*/
-
-vector HUD_Get_Num_Color (float x, float maxvalue)
-{
- float blinkingamt;
- vector color;
- if(x >= maxvalue) {
- color.x = sin(2*M_PI*time);
- color.y = 1;
- color.z = sin(2*M_PI*time);
- }
- else if(x > maxvalue * 0.75) {
- color.x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0
- color.y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1
- color.z = 0;
- }
- else if(x > maxvalue * 0.5) {
- color.x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4
- color.y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9
- color.z = 1 - (x-100)*0.02; // blue value between 1 -> 0
- }
- else if(x > maxvalue * 0.25) {
- color.x = 1;
- color.y = 1;
- color.z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1
- }
- else if(x > maxvalue * 0.1) {
- color.x = 1;
- color.y = (x-20)*90/27/100; // green value between 0 -> 1
- color.z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2
- }
- else {
- color.x = 1;
- color.y = 0;
- color.z = 0;
- }
-
- blinkingamt = (1 - x/maxvalue/0.25);
- if(blinkingamt > 0)
- {
- color.x = color.x - color.x * blinkingamt * sin(2*M_PI*time);
- color.y = color.y - color.y * blinkingamt * sin(2*M_PI*time);
- color.z = color.z - color.z * blinkingamt * sin(2*M_PI*time);
- }
- return color;
-}
-
-float HUD_GetRowCount(int item_count, vector size, float item_aspect)
-{
- float aspect = size_y / size_x;
- return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count);
-}
-
-vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect)
-{
- float columns, rows;
- float ratio, best_ratio = 0;
- float best_columns = 1, best_rows = 1;
- bool vertical = (psize.x / psize.y >= item_aspect);
- if(vertical)
- {
- psize = eX * psize.y + eY * psize.x;
- item_aspect = 1 / item_aspect;
- }
-
- rows = ceil(sqrt(item_count));
- columns = ceil(item_count/rows);
- while(columns >= 1)
- {
- ratio = (psize.x/columns) / (psize.y/rows);
- if(ratio > item_aspect)
- ratio = item_aspect * item_aspect / ratio;
-
- if(ratio <= best_ratio)
- break; // ratio starts decreasing by now, skip next configurations
-
- best_columns = columns;
- best_rows = rows;
- best_ratio = ratio;
-
- if(columns == 1)
- break;
-
- --columns;
- rows = ceil(item_count/columns);
- }
-
- if(vertical)
- return eX * best_rows + eY * best_columns;
- else
- return eX * best_columns + eY * best_rows;
-}
-
-// return the string of the onscreen race timer
-string MakeRaceString(int cp, float mytime, float theirtime, float lapdelta, string theirname)
-{
- string col;
- string timestr;
- string cpname;
- string lapstr;
- lapstr = "";
-
- if(theirtime == 0) // goal hit
- {
- if(mytime > 0)
- {
- timestr = strcat("+", ftos_decimals(+mytime, TIME_DECIMALS));
- col = "^1";
- }
- else if(mytime == 0)
- {
- timestr = "+0.0";
- col = "^3";
- }
- else
- {
- timestr = strcat("-", ftos_decimals(-mytime, TIME_DECIMALS));
- col = "^2";
- }
-
- if(lapdelta > 0)
- {
- lapstr = sprintf(_(" (-%dL)"), lapdelta);
- col = "^2";
- }
- else if(lapdelta < 0)
- {
- lapstr = sprintf(_(" (+%dL)"), -lapdelta);
- col = "^1";
- }
- }
- else if(theirtime > 0) // anticipation
- {
- if(mytime >= theirtime)
- timestr = strcat("+", ftos_decimals(mytime - theirtime, TIME_DECIMALS));
- else
- timestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(theirtime));
- col = "^3";
- }
- else
- {
- col = "^7";
- timestr = "";
- }
-
- if(cp == 254)
- cpname = _("Start line");
- else if(cp == 255)
- cpname = _("Finish line");
- else if(cp)
- cpname = sprintf(_("Intermediate %d"), cp);
- else
- cpname = _("Finish line");
-
- if(theirtime < 0)
- return strcat(col, cpname);
- else if(theirname == "")
- return strcat(col, sprintf("%s (%s)", cpname, timestr));
- else
- return strcat(col, sprintf("%s (%s %s)", cpname, timestr, strcat(theirname, col, lapstr)));
-}
-
-// Check if the given name already exist in race rankings? In that case, where? (otherwise return 0)
-int race_CheckName(string net_name)
-{
- int i;
- for (i=RANKINGS_CNT-1;i>=0;--i)
- if(grecordholder[i] == net_name)
- return i+1;
- return 0;
-}
-
-/*
-==================
-HUD panels
-==================
-*/
-
-//basically the same code of draw_ButtonPicture and draw_VertButtonPicture for the menu
-void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag)
-{
- if(!length_ratio || !theAlpha)
- return;
- if(length_ratio > 1)
- length_ratio = 1;
- if (baralign == 3)
- {
- if(length_ratio < -1)
- length_ratio = -1;
- }
- else if(length_ratio < 0)
- return;
-
- vector square;
- vector width, height;
- if(vertical) {
- pic = strcat(hud_skin_path, "/", pic, "_vertical");
- if(precache_pic(pic) == "") {
- pic = "gfx/hud/default/progressbar_vertical";
- }
-
- if (baralign == 1) // bottom align
- theOrigin.y += (1 - length_ratio) * theSize.y;
- else if (baralign == 2) // center align
- theOrigin.y += 0.5 * (1 - length_ratio) * theSize.y;
- else if (baralign == 3) // center align, positive values down, negative up
- {
- theSize.y *= 0.5;
- if (length_ratio > 0)
- theOrigin.y += theSize.y;
- else
- {
- theOrigin.y += (1 + length_ratio) * theSize.y;
- length_ratio = -length_ratio;
- }
- }
- theSize.y *= length_ratio;
-
- vector bH;
- width = eX * theSize.x;
- height = eY * theSize.y;
- if(theSize.y <= theSize.x * 2)
- {
- // button not high enough
- // draw just upper and lower part then
- square = eY * theSize.y * 0.5;
- bH = eY * (0.25 * theSize.y / (theSize.x * 2));
- drawsubpic(theOrigin, square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, drawflag);
- drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, drawflag);
- }
- else
- {
- square = eY * theSize.x;
- drawsubpic(theOrigin, width + square, pic, '0 0 0', '1 0.25 0', theColor, theAlpha, drawflag);
- drawsubpic(theOrigin + square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5 0', theColor, theAlpha, drawflag);
- drawsubpic(theOrigin + height - square, width + square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, drawflag);
- }
- } else {
- pic = strcat(hud_skin_path, "/", pic);
- if(precache_pic(pic) == "") {
- pic = "gfx/hud/default/progressbar";
- }
-
- if (baralign == 1) // right align
- theOrigin.x += (1 - length_ratio) * theSize.x;
- else if (baralign == 2) // center align
- theOrigin.x += 0.5 * (1 - length_ratio) * theSize.x;
- else if (baralign == 3) // center align, positive values on the right, negative on the left
- {
- theSize.x *= 0.5;
- if (length_ratio > 0)
- theOrigin.x += theSize.x;
- else
- {
- theOrigin.x += (1 + length_ratio) * theSize.x;
- length_ratio = -length_ratio;
- }
- }
- theSize.x *= length_ratio;
-
- vector bW;
- width = eX * theSize.x;
- height = eY * theSize.y;
- if(theSize.x <= theSize.y * 2)
- {
- // button not wide enough
- // draw just left and right part then
- square = eX * theSize.x * 0.5;
- bW = eX * (0.25 * theSize.x / (theSize.y * 2));
- drawsubpic(theOrigin, square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, drawflag);
- drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, drawflag);
- }
- else
- {
- square = eX * theSize.y;
- drawsubpic(theOrigin, height + square, pic, '0 0 0', '0.25 1 0', theColor, theAlpha, drawflag);
- drawsubpic(theOrigin + square, theSize - 2 * square, pic, '0.25 0 0', '0.5 1 0', theColor, theAlpha, drawflag);
- drawsubpic(theOrigin + width - square, height + square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, drawflag);
- }
- }
-}
-
-void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, int drawflag)
-{
- if(!theAlpha)
- return;
-
- string pic;
- pic = strcat(hud_skin_path, "/num_leading");
- if(precache_pic(pic) == "") {
- pic = "gfx/hud/default/num_leading";
- }
-
- drawsubpic(pos, eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0 0 0', '0.25 1 0', color, theAlpha, drawflag);
- if(mySize.x/mySize.y > 2)
- drawsubpic(pos + eX * mySize.y, eX * (mySize.x - 2 * mySize.y) + eY * mySize.y, pic, '0.25 0 0', '0.5 1 0', color, theAlpha, drawflag);
- drawsubpic(pos + eX * mySize.x - eX * min(mySize.x * 0.5, mySize.y), eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0.75 0 0', '0.25 1 0', color, theAlpha, drawflag);
-}
-
-// Weapon icons (#0)
-//
-entity weaponorder[Weapons_MAX];
-void weaponorder_swap(int i, int j, entity pass)
-{
- entity h = weaponorder[i];
- weaponorder[i] = weaponorder[j];
- weaponorder[j] = h;
-}
-
-string weaponorder_cmp_str;
-int weaponorder_cmp(int i, int j, entity pass)
-{
- int ai, aj;
- ai = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[i].weapon), 0);
- aj = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[j].weapon), 0);
- return aj - ai; // the string is in REVERSE order (higher prio at the right is what we want, but higher prio first is the string)
-}
-
-void HUD_Weapons()
-{
- SELFPARAM();
- // declarations
- WepSet weapons_stat = WepSet_GetFromStat();
- int i;
- float f, a;
- float screen_ar;
- vector center = '0 0 0';
- int weapon_count, weapon_id;
- int row, column, rows = 0, columns = 0;
- bool vertical_order = true;
- float aspect = autocvar_hud_panel_weapons_aspect;
-
- float timeout = autocvar_hud_panel_weapons_timeout;
- float timein_effect_length = autocvar_hud_panel_weapons_timeout_speed_in; //? 0.375 : 0);
- float timeout_effect_length = autocvar_hud_panel_weapons_timeout_speed_out; //? 0.75 : 0);
-
- vector barsize = '0 0 0', baroffset = '0 0 0';
- vector ammo_color = '1 0 1';
- float ammo_alpha = 1;
-
- float when = max(1, autocvar_hud_panel_weapons_complainbubble_time);
- float fadetime = max(0, autocvar_hud_panel_weapons_complainbubble_fadetime);
-
- vector weapon_pos, weapon_size = '0 0 0';
- vector color;
-
- // check to see if we want to continue
- if(hud != HUD_NORMAL) return;
-
- if(!autocvar__hud_configure)
- {
- if((!autocvar_hud_panel_weapons) || (spectatee_status == -1))
- return;
- if(timeout && time >= weapontime + timeout + timeout_effect_length)
- if(autocvar_hud_panel_weapons_timeout_effect == 3 || (autocvar_hud_panel_weapons_timeout_effect == 1 && !(autocvar_hud_panel_weapons_timeout_fadebgmin + autocvar_hud_panel_weapons_timeout_fadefgmin)))
- {
- weaponprevtime = time;
- return;
- }
- }
-
- // update generic hud functions
- HUD_Panel_UpdateCvars();
-
- // figure out weapon order (how the weapons are sorted) // TODO make this configurable
- if(weaponorder_bypriority != autocvar_cl_weaponpriority || !weaponorder[0])
- {
- int weapon_cnt;
- if(weaponorder_bypriority)
- strunzone(weaponorder_bypriority);
- if(weaponorder_byimpulse)
- strunzone(weaponorder_byimpulse);
-
- weaponorder_bypriority = strzone(autocvar_cl_weaponpriority);
- weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(W_FixWeaponOrder_ForceComplete(W_NumberWeaponOrder(weaponorder_bypriority))));
- weaponorder_cmp_str = strcat(" ", weaponorder_byimpulse, " ");
-
- weapon_cnt = 0;
- for(i = WEP_FIRST; i <= WEP_LAST; ++i)
- {
- setself(get_weaponinfo(i));
- if(self.impulse >= 0)
- {
- weaponorder[weapon_cnt] = self;
- ++weapon_cnt;
- }
- }
- for(i = weapon_cnt; i < Weapons_MAX; ++i)
- weaponorder[i] = world;
- heapsort(weapon_cnt, weaponorder_swap, weaponorder_cmp, world);
-
- weaponorder_cmp_str = string_null;
- }
-
- if(!autocvar_hud_panel_weapons_complainbubble || autocvar__hud_configure || time - complain_weapon_time >= when + fadetime)
- complain_weapon = 0;
-
- if(autocvar__hud_configure)
- {
- if(!weapons_stat)
- for(i = WEP_FIRST; i <= WEP_LAST; i += floor((WEP_LAST-WEP_FIRST)/5))
- weapons_stat |= WepSet_FromWeapon(i);
-
- #if 0
- /// debug code
- if(cvar("wep_add"))
- {
- weapons_stat = '0 0 0';
- float countw = 1 + floor((floor(time * cvar("wep_add"))) % (Weapons_COUNT - 1));
- for(i = WEP_FIRST; i <= countw; ++i)
- weapons_stat |= WepSet_FromWeapon(i);
- }
- #endif
- }
-
- // determine which weapons are going to be shown
- if (autocvar_hud_panel_weapons_onlyowned)
- {
- if(autocvar__hud_configure)
- {
- if(menu_enabled != 2)
- HUD_Panel_DrawBg(1); // also draw the bg of the entire panel
- }
-
- // do we own this weapon?
- weapon_count = 0;
- for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i)
- if((weapons_stat & WepSet_FromWeapon(weaponorder[i].weapon)) || (weaponorder[i].weapon == complain_weapon))
- ++weapon_count;
-
-
- // might as well commit suicide now, no reason to live ;)
- if (weapon_count == 0)
- return;
-
- vector old_panel_size = panel_size;
- vector padded_panel_size = panel_size - '2 2 0' * panel_bg_padding;
-
- // get the all-weapons layout
- int nHidden = 0;
- WepSet weapons_stat = WepSet_GetFromStat();
- for (int i = WEP_FIRST; i <= WEP_LAST; ++i) {
- WepSet weapons_wep = WepSet_FromWeapon(i);
- if (weapons_stat & weapons_wep) continue;
- Weapon w = get_weaponinfo(i);
- if (w.spawnflags & WEP_FLAG_MUTATORBLOCKED) nHidden += 1;
- }
- vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1) - nHidden, padded_panel_size, aspect);
- columns = table_size.x;
- rows = table_size.y;
- weapon_size.x = padded_panel_size.x / columns;
- weapon_size.y = padded_panel_size.y / rows;
-
- // NOTE: although weapons should aways look the same even if onlyowned is enabled,
- // we enlarge them a bit when possible to better match the desired aspect ratio
- if(padded_panel_size.x / padded_panel_size.y < aspect)
- {
- // maximum number of rows that allows to display items with the desired aspect ratio
- int max_rows = floor(padded_panel_size.y / (weapon_size.x / aspect));
- columns = min(columns, ceil(weapon_count / max_rows));
- rows = ceil(weapon_count / columns);
- weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect);
- weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y);
- vertical_order = false;
- }
- else
- {
- int max_columns = floor(padded_panel_size.x / (weapon_size.y * aspect));
- rows = min(rows, ceil(weapon_count / max_columns));
- columns = ceil(weapon_count / rows);
- weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y);
- weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect);
- vertical_order = true;
- }
-
- // reduce size of the panel
- panel_size.x = columns * weapon_size.x;
- panel_size.y = rows * weapon_size.y;
- panel_size += '2 2 0' * panel_bg_padding;
-
- // center the resized panel, or snap it to the screen edge when close enough
- if(panel_pos.x > vid_conwidth * 0.001)
- {
- if(panel_pos.x + old_panel_size.x > vid_conwidth * 0.999)
- panel_pos.x += old_panel_size.x - panel_size.x;
- else
- panel_pos.x += (old_panel_size.x - panel_size.x) / 2;
- }
- else if(old_panel_size.x > vid_conwidth * 0.999)
- panel_pos.x += (old_panel_size.x - panel_size.x) / 2;
-
- if(panel_pos.y > vid_conheight * 0.001)
- {
- if(panel_pos.y + old_panel_size.y > vid_conheight * 0.999)
- panel_pos.y += old_panel_size.y - panel_size.y;
- else
- panel_pos.y += (old_panel_size.y - panel_size.y) / 2;
- }
- else if(old_panel_size.y > vid_conheight * 0.999)
- panel_pos.y += (old_panel_size.y - panel_size.y) / 2;
- }
- else
- weapon_count = (Weapons_COUNT - 1);
-
- // animation for fading in/out the panel respectively when not in use
- if(!autocvar__hud_configure)
- {
- if (timeout && time >= weapontime + timeout) // apply timeout effect if needed
- {
- f = bound(0, (time - (weapontime + timeout)) / timeout_effect_length, 1);
-
- // fade the panel alpha
- if(autocvar_hud_panel_weapons_timeout_effect == 1)
- {
- panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * f + (1 - f));
- panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * f + (1 - f));
- }
- else if(autocvar_hud_panel_weapons_timeout_effect == 3)
- {
- panel_bg_alpha *= (1 - f);
- panel_fg_alpha *= (1 - f);
- }
-
- // move the panel off the screen
- if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3)
- {
- f *= f; // for a cooler movement
- center.x = panel_pos.x + panel_size.x/2;
- center.y = panel_pos.y + panel_size.y/2;
- screen_ar = vid_conwidth/vid_conheight;
- if (center.x/center.y < screen_ar) //bottom left
- {
- if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom
- panel_pos.y += f * (vid_conheight - panel_pos.y);
- else //left
- panel_pos.x -= f * (panel_pos.x + panel_size.x);
- }
- else //top right
- {
- if ((vid_conwidth - center.x)/center.y < screen_ar) //right
- panel_pos.x += f * (vid_conwidth - panel_pos.x);
- else //top
- panel_pos.y -= f * (panel_pos.y + panel_size.y);
- }
- if(f == 1)
- center.x = -1; // mark the panel as off screen
- }
- weaponprevtime = time - (1 - f) * timein_effect_length;
- }
- else if (timeout && time < weaponprevtime + timein_effect_length) // apply timein effect if needed
- {
- f = bound(0, (time - weaponprevtime) / timein_effect_length, 1);
-
- // fade the panel alpha
- if(autocvar_hud_panel_weapons_timeout_effect == 1)
- {
- panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * (1 - f) + f);
- panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * (1 - f) + f);
- }
- else if(autocvar_hud_panel_weapons_timeout_effect == 3)
- {
- panel_bg_alpha *= (f);
- panel_fg_alpha *= (f);
- }
-
- // move the panel back on screen
- if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3)
- {
- f *= f; // for a cooler movement
- f = 1 - f;
- center.x = panel_pos.x + panel_size.x/2;
- center.y = panel_pos.y + panel_size.y/2;
- screen_ar = vid_conwidth/vid_conheight;
- if (center.x/center.y < screen_ar) //bottom left
- {
- if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom
- panel_pos.y += f * (vid_conheight - panel_pos.y);
- else //left
- panel_pos.x -= f * (panel_pos.x + panel_size.x);
- }
- else //top right
- {
- if ((vid_conwidth - center.x)/center.y < screen_ar) //right
- panel_pos.x += f * (vid_conwidth - panel_pos.x);
- else //top
- panel_pos.y -= f * (panel_pos.y + panel_size.y);
- }
- }
- }
- }
-
- // draw the background, then change the virtual size of it to better fit other items inside
- HUD_Panel_DrawBg(1);
-
- if(center.x == -1)
- return;
-
- if(panel_bg_padding)
- {
- panel_pos += '1 1 0' * panel_bg_padding;
- panel_size -= '2 2 0' * panel_bg_padding;
- }
-
- // after the sizing and animations are done, update the other values
-
- if(!rows) // if rows is > 0 onlyowned code has already updated these vars
- {
- vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1), panel_size, aspect);
- columns = table_size.x;
- rows = table_size.y;
- weapon_size.x = panel_size.x / columns;
- weapon_size.y = panel_size.y / rows;
- vertical_order = (panel_size.x / panel_size.y >= aspect);
- }
-
- // calculate position/size for visual bar displaying ammount of ammo status
- if (autocvar_hud_panel_weapons_ammo)
- {
- ammo_color = stov(autocvar_hud_panel_weapons_ammo_color);
- ammo_alpha = panel_fg_alpha * autocvar_hud_panel_weapons_ammo_alpha;
-
- if(weapon_size.x/weapon_size.y > aspect)
- {
- barsize.x = aspect * weapon_size.y;
- barsize.y = weapon_size.y;
- baroffset.x = (weapon_size.x - barsize.x) / 2;
- }
- else
- {
- barsize.y = 1/aspect * weapon_size.x;
- barsize.x = weapon_size.x;
- baroffset.y = (weapon_size.y - barsize.y) / 2;
- }
- }
- if(autocvar_hud_panel_weapons_accuracy)
- Accuracy_LoadColors();
-
- // draw items
- row = column = 0;
- vector label_size = '1 1 0' * min(weapon_size.x, weapon_size.y) * bound(0, autocvar_hud_panel_weapons_label_scale, 1);
- vector noncurrent_pos = '0 0 0';
- vector noncurrent_size = weapon_size * bound(0, autocvar_hud_panel_weapons_noncurrent_scale, 1);
- float noncurrent_alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_weapons_noncurrent_alpha, 1);
- bool isCurrent;
-
- for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i)
- {
- // retrieve information about the current weapon to be drawn
- setself(weaponorder[i]);
- weapon_id = self.impulse;
- isCurrent = (self.weapon == switchweapon);
-
- // skip if this weapon doesn't exist
- if(!self || weapon_id < 0) { continue; }
-
- // skip this weapon if we don't own it (and onlyowned is enabled)-- or if weapons_complainbubble is showing for this weapon
- if(autocvar_hud_panel_weapons_onlyowned)
- if (!((weapons_stat & WepSet_FromWeapon(self.weapon)) || (self.weapon == complain_weapon)))
- continue;
-
- // figure out the drawing position of weapon
- weapon_pos = (panel_pos + eX * column * weapon_size.x + eY * row * weapon_size.y);
- noncurrent_pos.x = weapon_pos.x + (weapon_size.x - noncurrent_size.x) / 2;
- noncurrent_pos.y = weapon_pos.y + (weapon_size.y - noncurrent_size.y) / 2;
-
- // draw background behind currently selected weapon
- if(isCurrent)
- drawpic_aspect_skin(weapon_pos, "weapon_current_bg", weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
-
- // draw the weapon accuracy
- if(autocvar_hud_panel_weapons_accuracy)
- {
- float panel_weapon_accuracy = weapon_accuracy[self.weapon-WEP_FIRST];
- if(panel_weapon_accuracy >= 0)
- {
- color = Accuracy_GetColor(panel_weapon_accuracy);
- drawpic_aspect_skin(weapon_pos, "weapon_accuracy", weapon_size, color, panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- }
-
- // drawing all the weapon items
- if(weapons_stat & WepSet_FromWeapon(self.weapon))
- {
- // draw the weapon image
- if(isCurrent)
- drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- else
- drawpic_aspect_skin(noncurrent_pos, self.model2, noncurrent_size, '1 1 1', noncurrent_alpha, DRAWFLAG_NORMAL);
-
- // draw weapon label string
- switch(autocvar_hud_panel_weapons_label)
- {
- case 1: // weapon number
- drawstring(weapon_pos, ftos(weapon_id), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- break;
-
- case 2: // bind
- drawstring(weapon_pos, getcommandkey(ftos(weapon_id), strcat("weapon_group_", ftos(weapon_id))), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- break;
-
- case 3: // weapon name
- drawstring(weapon_pos, strtolower(self.message), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- break;
-
- default: // nothing
- break;
- }
-
- // draw ammo status bar
- if(autocvar_hud_panel_weapons_ammo && (self.ammo_field != ammo_none))
- {
- float ammo_full;
- a = getstati(GetAmmoStat(self.ammo_field)); // how much ammo do we have?
-
- if(a > 0)
- {
- switch(self.ammo_field)
- {
- case ammo_shells: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break;
- case ammo_nails: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break;
- case ammo_rockets: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break;
- case ammo_cells: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break;
- case ammo_plasma: ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma; break;
- case ammo_fuel: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break;
- default: ammo_full = 60;
- }
-
- drawsetcliparea(
- weapon_pos.x + baroffset.x,
- weapon_pos.y + baroffset.y,
- barsize.x * bound(0, a/ammo_full, 1),
- barsize.y
- );
-
- drawpic_aspect_skin(
- weapon_pos,
- "weapon_ammo",
- weapon_size,
- ammo_color,
- ammo_alpha,
- DRAWFLAG_NORMAL
- );
-
- drawresetcliparea();
- }
- }
- }
- else // draw a "ghost weapon icon" if you don't have the weapon
- {
- drawpic_aspect_skin(noncurrent_pos, self.model2, noncurrent_size, '0.2 0.2 0.2', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
- }
-
- // draw the complain message
- if(self.weapon == complain_weapon)
- {
- if(fadetime)
- a = ((complain_weapon_time + when > time) ? 1 : bound(0, (complain_weapon_time + when + fadetime - time) / fadetime, 1));
- else
- a = ((complain_weapon_time + when > time) ? 1 : 0);
-
- string s;
- if(complain_weapon_type == 0) {
- s = _("Out of ammo");
- color = stov(autocvar_hud_panel_weapons_complainbubble_color_outofammo);
- }
- else if(complain_weapon_type == 1) {
- s = _("Don't have");
- color = stov(autocvar_hud_panel_weapons_complainbubble_color_donthave);
- }
- else {
- s = _("Unavailable");
- color = stov(autocvar_hud_panel_weapons_complainbubble_color_unavailable);
- }
- float padding = autocvar_hud_panel_weapons_complainbubble_padding;
- drawpic_aspect_skin(weapon_pos + '1 1 0' * padding, "weapon_complainbubble", weapon_size - '2 2 0' * padding, color, a * panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(weapon_pos + '1 1 0' * padding, s, weapon_size - '2 2 0' * padding, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- }
-
- #if 0
- /// debug code
- if(!autocvar_hud_panel_weapons_onlyowned)
- {
- drawfill(weapon_pos + '1 1 0', weapon_size - '2 2 0', '1 1 1', panel_fg_alpha * 0.2, DRAWFLAG_NORMAL);
- drawstring(weapon_pos, ftos(i + 1), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- #endif
-
- // continue with new position for the next weapon
- if(vertical_order)
- {
- ++column;
- if(column >= columns)
- {
- column = 0;
- ++row;
- }
- }
- else
- {
- ++row;
- if(row >= rows)
- {
- row = 0;
- ++column;
- }
- }
- }
-}
-
-// Ammo (#1)
-void DrawNadeProgressBar(vector myPos, vector mySize, float progress, vector color)
-{
- HUD_Panel_DrawProgressBar(
- myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x,
- mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x,
- autocvar_hud_panel_ammo_progressbar_name,
- progress, 0, 0, color,
- autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
-}
-
-void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time)
-{
- float bonusNades = getstatf(STAT_NADE_BONUS);
- float bonusProgress = getstatf(STAT_NADE_BONUS_SCORE);
- float bonusType = getstati(STAT_NADE_BONUS_TYPE);
- vector nadeColor = Nades[bonusType].m_color;
- string nadeIcon = Nades[bonusType].m_icon;
-
- vector iconPos, textPos;
-
- if(autocvar_hud_panel_ammo_iconalign)
- {
- iconPos = myPos + eX * 2 * mySize.y;
- textPos = myPos;
- }
- else
- {
- iconPos = myPos;
- textPos = myPos + eX * mySize.y;
- }
-
- if(bonusNades > 0 || bonusProgress > 0)
- {
- DrawNadeProgressBar(myPos, mySize, bonusProgress, nadeColor);
-
- if(autocvar_hud_panel_ammo_text)
- drawstring_aspect(textPos, ftos(bonusNades), eX * (2/3) * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
-
- if(draw_expanding)
- drawpic_aspect_skin_expanding(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, expand_time);
-
- drawpic_aspect_skin(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- }
-}
-
-void DrawAmmoItem(vector myPos, vector mySize, .int ammoType, bool isCurrent, bool isInfinite)
-{
- if(ammoType == ammo_none)
- return;
-
- // Initialize variables
-
- int ammo;
- if(autocvar__hud_configure)
- {
- isCurrent = (ammoType == ammo_rockets); // Rockets always current
- ammo = 60;
- }
- else
- ammo = getstati(GetAmmoStat(ammoType));
-
- if(!isCurrent)
- {
- float scale = bound(0, autocvar_hud_panel_ammo_noncurrent_scale, 1);
- myPos = myPos + (mySize - mySize * scale) * 0.5;
- mySize = mySize * scale;
- }
-
- vector iconPos, textPos;
- if(autocvar_hud_panel_ammo_iconalign)
- {
- iconPos = myPos + eX * 2 * mySize.y;
- textPos = myPos;
- }
- else
- {
- iconPos = myPos;
- textPos = myPos + eX * mySize.y;
- }
-
- bool isShadowed = (ammo <= 0 && !isCurrent && !isInfinite);
-
- vector iconColor = isShadowed ? '0 0 0' : '1 1 1';
- vector textColor;
- if(isInfinite)
- textColor = '0.2 0.95 0';
- else if(isShadowed)
- textColor = '0 0 0';
- else if(ammo < 10)
- textColor = '0.8 0.04 0';
- else
- textColor = '1 1 1';
-
- float alpha;
- if(isCurrent)
- alpha = panel_fg_alpha;
- else if(isShadowed)
- alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1) * 0.5;
- else
- alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1);
-
- string text = isInfinite ? "\xE2\x88\x9E" : ftos(ammo); // Use infinity symbol (U+221E)
-
- // Draw item
-
- if(isCurrent)
- drawpic_aspect_skin(myPos, "ammo_current_bg", mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
-
- if(ammo > 0 && autocvar_hud_panel_ammo_progressbar)
- HUD_Panel_DrawProgressBar(myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, autocvar_hud_panel_ammo_progressbar_name, ammo/autocvar_hud_panel_ammo_maxammo, 0, 0, textColor, autocvar_hud_progressbar_alpha * alpha, DRAWFLAG_NORMAL);
-
- if(autocvar_hud_panel_ammo_text)
- drawstring_aspect(textPos, text, eX * (2/3) * mySize.x + eY * mySize.y, textColor, alpha, DRAWFLAG_NORMAL);
-
- drawpic_aspect_skin(iconPos, GetAmmoPicture(ammoType), '1 1 0' * mySize.y, iconColor, alpha, DRAWFLAG_NORMAL);
-}
-
-int nade_prevstatus;
-int nade_prevframe;
-float nade_statuschange_time;
-void HUD_Ammo(void)
-{
- if(hud != HUD_NORMAL) return;
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_ammo) return;
- if(spectatee_status == -1) return;
- }
-
- HUD_Panel_UpdateCvars();
-
- draw_beginBoldFont();
-
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- int rows = 0, columns, row, column;
- float nade_cnt = getstatf(STAT_NADE_BONUS), nade_score = getstatf(STAT_NADE_BONUS_SCORE);
- bool draw_nades = (nade_cnt > 0 || nade_score > 0);
- float nade_statuschange_elapsedtime;
- int total_ammo_count;
-
- vector ammo_size;
- if (autocvar_hud_panel_ammo_onlycurrent)
- total_ammo_count = 1;
- else
- total_ammo_count = AMMO_COUNT;
-
- if(draw_nades)
- {
- ++total_ammo_count;
- if (nade_cnt != nade_prevframe)
- {
- nade_statuschange_time = time;
- nade_prevstatus = nade_prevframe;
- nade_prevframe = nade_cnt;
- }
- }
- else
- nade_prevstatus = nade_prevframe = nade_statuschange_time = 0;
-
- rows = HUD_GetRowCount(total_ammo_count, mySize, 3);
- columns = ceil((total_ammo_count)/rows);
- ammo_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
-
- vector offset = '0 0 0'; // fteqcc sucks
- float newSize;
- if(ammo_size.x/ammo_size.y > 3)
- {
- newSize = 3 * ammo_size.y;
- offset.x = ammo_size.x - newSize;
- pos.x += offset.x/2;
- ammo_size.x = newSize;
- }
- else
- {
- newSize = 1/3 * ammo_size.x;
- offset.y = ammo_size.y - newSize;
- pos.y += offset.y/2;
- ammo_size.y = newSize;
- }
-
- int i;
- bool infinite_ammo = (getstati(STAT_ITEMS, 0, 24) & IT_UNLIMITED_WEAPON_AMMO);
- row = column = 0;
- if(autocvar_hud_panel_ammo_onlycurrent)
- {
- if(autocvar__hud_configure)
- {
- DrawAmmoItem(pos, ammo_size, ammo_rockets, true, false);
- }
- else
- {
- DrawAmmoItem(
- pos,
- ammo_size,
- (get_weaponinfo(switchweapon)).ammo_field,
- true,
- infinite_ammo
- );
- }
-
- ++row;
- if(row >= rows)
- {
- row = 0;
- column = column + 1;
- }
- }
- else
- {
- .int ammotype;
- row = column = 0;
- for(i = 0; i < AMMO_COUNT; ++i)
- {
- ammotype = GetAmmoFieldFromNum(i);
- DrawAmmoItem(
- pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y),
- ammo_size,
- ammotype,
- ((get_weaponinfo(switchweapon)).ammo_field == ammotype),
- infinite_ammo
- );
-
- ++row;
- if(row >= rows)
- {
- row = 0;
- column = column + 1;
- }
- }
- }
-
- if (draw_nades)
- {
- nade_statuschange_elapsedtime = time - nade_statuschange_time;
-
- float f = bound(0, nade_statuschange_elapsedtime*2, 1);
-
- DrawAmmoNades(pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y), ammo_size, nade_prevstatus < nade_cnt && nade_cnt != 0 && f < 1, f);
- }
-
- draw_endBoldFont();
-}
-
-void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha, float fadelerp)
-{
- vector newPos = '0 0 0', newSize = '0 0 0';
- vector picpos, numpos;
-
- if (vertical)
- {
- if(mySize.y/mySize.x > 2)
- {
- newSize.y = 2 * mySize.x;
- newSize.x = mySize.x;
-
- newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
- newPos.x = myPos.x;
- }
- else
- {
- newSize.x = 1/2 * mySize.y;
- newSize.y = mySize.y;
-
- newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
- newPos.y = myPos.y;
- }
-
- if(icon_right_align)
- {
- numpos = newPos;
- picpos = newPos + eY * newSize.x;
- }
- else
- {
- picpos = newPos;
- numpos = newPos + eY * newSize.x;
- }
-
- newSize.y /= 2;
- drawpic_aspect_skin(picpos, icon, newSize, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
- // make number smaller than icon, it looks better
- // reduce only y to draw numbers with different number of digits with the same y size
- numpos.y += newSize.y * ((1 - 0.7) / 2);
- newSize.y *= 0.7;
- drawstring_aspect(numpos, ftos(x), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
- return;
- }
-
- if(mySize.x/mySize.y > 3)
- {
- newSize.x = 3 * mySize.y;
- newSize.y = mySize.y;
-
- newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
- newPos.y = myPos.y;
- }
- else
- {
- newSize.y = 1/3 * mySize.x;
- newSize.x = mySize.x;
-
- newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
- newPos.x = myPos.x;
- }
-
- if(icon_right_align) // right align
- {
- numpos = newPos;
- picpos = newPos + eX * 2 * newSize.y;
- }
- else // left align
- {
- numpos = newPos + eX * newSize.y;
- picpos = newPos;
- }
-
- // NOTE: newSize_x is always equal to 3 * mySize_y so we can use
- // '2 1 0' * newSize_y instead of eX * (2/3) * newSize_x + eY * newSize_y
- drawstring_aspect_expanding(numpos, ftos(x), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
- drawpic_aspect_skin_expanding(picpos, icon, '1 1 0' * newSize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
-}
-
-void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha)
-{
- DrawNumIcon_expanding(myPos, mySize, x, icon, vertical, icon_right_align, color, theAlpha, 0);
-}
-
-// Powerups (#2)
-//
-
-// Powerup item fields (reusing existing fields)
-.string message; // Human readable name
-.string netname; // Icon name
-.vector colormod; // Color
-.float count; // Time left
-.float lifetime; // Maximum time
-
-entity powerupItems;
-int powerupItemsCount;
-
-void resetPowerupItems()
-{
- entity item;
- for(item = powerupItems; item; item = item.chain)
- item.count = 0;
-
- powerupItemsCount = 0;
-}
-
-void addPowerupItem(string name, string icon, vector color, float currentTime, float lifeTime)
-{
- if(!powerupItems)
- powerupItems = spawn();
-
- entity item;
- for(item = powerupItems; item.count; item = item.chain)
- if(!item.chain)
- item.chain = spawn();
-
- item.message = name;
- item.netname = icon;
- item.colormod = color;
- item.count = currentTime;
- item.lifetime = lifeTime;
-
- ++powerupItemsCount;
-}
-
-int getPowerupItemAlign(int align, int column, int row, int columns, int rows, bool isVertical)
-{
- if(align < 2)
- return align;
-
- bool isTop = isVertical && rows > 1 && row == 0;
- bool isBottom = isVertical && rows > 1 && row == rows-1;
- bool isLeft = !isVertical && columns > 1 && column == 0;
- bool isRight = !isVertical && columns > 1 && column == columns-1;
-
- if(isTop || isLeft) return (align == 2) ? 1 : 0;
- if(isBottom || isRight) return (align == 2) ? 0 : 1;
-
- return 2;
-}
-
-void HUD_Powerups()
-{
- int allItems = getstati(STAT_ITEMS, 0, 24);
- int allBuffs = getstati(STAT_BUFFS, 0, 24);
- int strengthTime, shieldTime, superTime;
-
- // Initialize items
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_powerups) return;
- if(spectatee_status == -1) return;
- if(getstati(STAT_HEALTH) <= 0) return;
- if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return;
-
- strengthTime = bound(0, getstatf(STAT_STRENGTH_FINISHED) - time, 99);
- shieldTime = bound(0, getstatf(STAT_INVINCIBLE_FINISHED) - time, 99);
- superTime = bound(0, getstatf(STAT_SUPERWEAPONS_FINISHED) - time, 99);
-
- if(allItems & IT_UNLIMITED_SUPERWEAPONS)
- superTime = 99;
-
- // Prevent stuff to show up on mismatch that will be fixed next frame
- if(!(allItems & IT_SUPERWEAPON))
- superTime = 0;
- }
- else
- {
- strengthTime = 15;
- shieldTime = 27;
- superTime = 13;
- allBuffs = 0;
- }
-
- // Add items to linked list
- resetPowerupItems();
-
- if(strengthTime)
- addPowerupItem("Strength", "strength", autocvar_hud_progressbar_strength_color, strengthTime, 30);
- if(shieldTime)
- addPowerupItem("Shield", "shield", autocvar_hud_progressbar_shield_color, shieldTime, 30);
- if(superTime)
- addPowerupItem("Superweapons", "superweapons", autocvar_hud_progressbar_superweapons_color, superTime, 30);
-
- FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA(
- addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, getstatf(STAT_BUFF_TIME) - time, 99), 60);
- ));
-
- if(!powerupItemsCount)
- return;
-
- // Draw panel background
- HUD_Panel_UpdateCvars();
- HUD_Panel_DrawBg(1);
-
- // Set drawing area
- vector pos = panel_pos;
- vector size = panel_size;
- bool isVertical = size.y > size.x;
-
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- size -= '2 2 0' * panel_bg_padding;
- }
-
- // Find best partitioning of the drawing area
- const float DESIRED_ASPECT = 6;
- float aspect = 0, a;
- int columns = 0, c;
- int rows = 0, r;
- int i = 1;
-
- do
- {
- c = floor(powerupItemsCount / i);
- r = ceil(powerupItemsCount / c);
- a = isVertical ? (size.y/r) / (size.x/c) : (size.x/c) / (size.y/r);
-
- if(i == 1 || fabs(DESIRED_ASPECT - a) < fabs(DESIRED_ASPECT - aspect))
- {
- aspect = a;
- columns = c;
- rows = r;
- }
- }
- while(++i <= powerupItemsCount);
-
- // Prevent single items from getting too wide
- if(powerupItemsCount == 1 && aspect > DESIRED_ASPECT)
- {
- if(isVertical)
- {
- size.y *= 0.5;
- pos.y += size.y * 0.5;
- }
- else
- {
- size.x *= 0.5;
- pos.x += size.x * 0.5;
- }
- }
-
- // Draw items from linked list
- vector itemPos = pos;
- vector itemSize = eX * (size.x / columns) + eY * (size.y / rows);
- vector textColor = '1 1 1';
-
- int fullSeconds = 0;
- int align = 0;
- int column = 0;
- int row = 0;
-
- draw_beginBoldFont();
- for(entity item = powerupItems; item.count; item = item.chain)
- {
- itemPos = eX * (pos.x + column * itemSize.x) + eY * (pos.y + row * itemSize.y);
-
- // Draw progressbar
- if(autocvar_hud_panel_powerups_progressbar)
- {
- align = getPowerupItemAlign(autocvar_hud_panel_powerups_baralign, column, row, columns, rows, isVertical);
- HUD_Panel_DrawProgressBar(itemPos, itemSize, "progressbar", item.count / item.lifetime, isVertical, align, item.colormod, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- }
-
- // Draw icon and text
- if(autocvar_hud_panel_powerups_text)
- {
- align = getPowerupItemAlign(autocvar_hud_panel_powerups_iconalign, column, row, columns, rows, isVertical);
- fullSeconds = ceil(item.count);
- textColor = '0.6 0.6 0.6' + (item.colormod * 0.4);
-
- if(item.count > 1)
- DrawNumIcon(itemPos, itemSize, fullSeconds, item.netname, isVertical, align, textColor, panel_fg_alpha);
- if(item.count <= 5)
- DrawNumIcon_expanding(itemPos, itemSize, fullSeconds, item.netname, isVertical, align, textColor, panel_fg_alpha, bound(0, (fullSeconds - item.count) / 0.5, 1));
- }
-
- // Determine next section
- if(isVertical)
- {
- if(++column >= columns)
- {
- column = 0;
- ++row;
- }
- }
- else
- {
- if(++row >= rows)
- {
- row = 0;
- ++column;
- }
- }
- }
- draw_endBoldFont();
-}
-
-// Health/armor (#3)
-//
-
-
-void HUD_HealthArmor(void)
-{
- int armor, health, fuel;
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_healtharmor) return;
- if(hud != HUD_NORMAL) return;
- if(spectatee_status == -1) return;
-
- health = getstati(STAT_HEALTH);
- if(health <= 0)
- {
- prev_health = -1;
- return;
- }
- armor = getstati(STAT_ARMOR);
-
- // code to check for spectatee_status changes is in Ent_ClientData()
- // prev_p_health and prev_health can be set to -1 there
-
- if (prev_p_health == -1)
- {
- // no effect
- health_beforedamage = 0;
- armor_beforedamage = 0;
- health_damagetime = 0;
- armor_damagetime = 0;
- prev_health = health;
- prev_armor = armor;
- old_p_health = health;
- old_p_armor = armor;
- prev_p_health = health;
- prev_p_armor = armor;
- }
- else if (prev_health == -1)
- {
- //start the load effect
- health_damagetime = 0;
- armor_damagetime = 0;
- prev_health = 0;
- prev_armor = 0;
- }
- fuel = getstati(STAT_FUEL);
- }
- else
- {
- health = 150;
- armor = 75;
- fuel = 20;
- }
-
- HUD_Panel_UpdateCvars();
-
- draw_beginBoldFont();
-
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- int baralign = autocvar_hud_panel_healtharmor_baralign;
- int iconalign = autocvar_hud_panel_healtharmor_iconalign;
-
- int maxhealth = autocvar_hud_panel_healtharmor_maxhealth;
- int maxarmor = autocvar_hud_panel_healtharmor_maxarmor;
- if(autocvar_hud_panel_healtharmor == 2) // combined health and armor display
- {
- vector v;
- v = healtharmor_maxdamage(health, armor, armorblockpercent, DEATH_WEAPON.m_id);
-
- float x;
- x = floor(v.x + 1);
-
- float maxtotal = maxhealth + maxarmor;
- string biggercount;
- if(v.z) // NOT fully armored
- {
- biggercount = "health";
- if(autocvar_hud_panel_healtharmor_progressbar)
- HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_health, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- if(armor)
- if(autocvar_hud_panel_healtharmor_text)
- drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "armor", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha * armor / health, DRAWFLAG_NORMAL);
- }
- else
- {
- biggercount = "armor";
- if(autocvar_hud_panel_healtharmor_progressbar)
- HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- if(health)
- if(autocvar_hud_panel_healtharmor_text)
- drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "health", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- if(autocvar_hud_panel_healtharmor_text)
- DrawNumIcon(pos, mySize, x, biggercount, 0, iconalign, HUD_Get_Num_Color(x, maxtotal), 1);
-
- if(fuel)
- HUD_Panel_DrawProgressBar(pos, eX * mySize.x + eY * 0.2 * mySize.y, "progressbar", fuel/100, 0, (baralign == 1 || baralign == 3), autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL);
- }
- else
- {
- float panel_ar = mySize.x/mySize.y;
- bool is_vertical = (panel_ar < 1);
- vector health_offset = '0 0 0', armor_offset = '0 0 0';
- if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1))
- {
- mySize.x *= 0.5;
- if (autocvar_hud_panel_healtharmor_flip)
- health_offset.x = mySize.x;
- else
- armor_offset.x = mySize.x;
- }
- else
- {
- mySize.y *= 0.5;
- if (autocvar_hud_panel_healtharmor_flip)
- health_offset.y = mySize.y;
- else
- armor_offset.y = mySize.y;
- }
-
- bool health_baralign, armor_baralign, fuel_baralign;
- bool health_iconalign, armor_iconalign;
- if (autocvar_hud_panel_healtharmor_flip)
- {
- armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1);
- health_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1);
- fuel_baralign = health_baralign;
- armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1);
- health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1);
- }
- else
- {
- health_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1);
- armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1);
- fuel_baralign = armor_baralign;
- health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1);
- armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1);
- }
-
- //if(health)
- {
- if(autocvar_hud_panel_healtharmor_progressbar)
- {
- float p_health, pain_health_alpha;
- p_health = health;
- pain_health_alpha = 1;
- if (autocvar_hud_panel_healtharmor_progressbar_gfx)
- {
- if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0)
- {
- if (fabs(prev_health - health) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth)
- {
- if (time - old_p_healthtime < 1)
- old_p_health = prev_p_health;
- else
- old_p_health = prev_health;
- old_p_healthtime = time;
- }
- if (time - old_p_healthtime < 1)
- {
- p_health += (old_p_health - health) * (1 - (time - old_p_healthtime));
- prev_p_health = p_health;
- }
- }
- if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0)
- {
- if (prev_health - health >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage)
- {
- if (time - health_damagetime >= 1)
- health_beforedamage = prev_health;
- health_damagetime = time;
- }
- if (time - health_damagetime < 1)
- {
- float health_damagealpha = 1 - (time - health_damagetime)*(time - health_damagetime);
- HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, health_beforedamage/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * health_damagealpha, DRAWFLAG_NORMAL);
- }
- }
- prev_health = health;
-
- if (health <= autocvar_hud_panel_healtharmor_progressbar_gfx_lowhealth)
- {
- float BLINK_FACTOR = 0.15;
- float BLINK_BASE = 0.85;
- float BLINK_FREQ = 9;
- pain_health_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
- }
- }
- HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, p_health/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * pain_health_alpha, DRAWFLAG_NORMAL);
- }
- if(autocvar_hud_panel_healtharmor_text)
- DrawNumIcon(pos + health_offset, mySize, health, "health", is_vertical, health_iconalign, HUD_Get_Num_Color(health, maxhealth), 1);
- }
-
- if(armor)
- {
- if(autocvar_hud_panel_healtharmor_progressbar)
- {
- float p_armor;
- p_armor = armor;
- if (autocvar_hud_panel_healtharmor_progressbar_gfx)
- {
- if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0)
- {
- if (fabs(prev_armor - armor) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth)
- {
- if (time - old_p_armortime < 1)
- old_p_armor = prev_p_armor;
- else
- old_p_armor = prev_armor;
- old_p_armortime = time;
- }
- if (time - old_p_armortime < 1)
- {
- p_armor += (old_p_armor - armor) * (1 - (time - old_p_armortime));
- prev_p_armor = p_armor;
- }
- }
- if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0)
- {
- if (prev_armor - armor >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage)
- {
- if (time - armor_damagetime >= 1)
- armor_beforedamage = prev_armor;
- armor_damagetime = time;
- }
- if (time - armor_damagetime < 1)
- {
- float armor_damagealpha = 1 - (time - armor_damagetime)*(time - armor_damagetime);
- HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, armor_beforedamage/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * armor_damagealpha, DRAWFLAG_NORMAL);
- }
- }
- prev_armor = armor;
- }
- HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, p_armor/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- if(autocvar_hud_panel_healtharmor_text)
- DrawNumIcon(pos + armor_offset, mySize, armor, "armor", is_vertical, armor_iconalign, HUD_Get_Num_Color(armor, maxarmor), 1);
- }
-
- if(fuel)
- {
- if (is_vertical)
- mySize.x *= 0.2 / 2; //if vertical always halve x to not cover too much numbers with 3 digits
- else
- mySize.y *= 0.2;
- if (panel_ar >= 4)
- mySize.x *= 2; //restore full panel size
- else if (panel_ar < 1/4)
- mySize.y *= 2; //restore full panel size
- HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", fuel/100, is_vertical, fuel_baralign, autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL);
- }
- }
-
- draw_endBoldFont();
-}
-
-// Notification area (#4)
-//
-
-void HUD_Notify_Push(string icon, string attacker, string victim)
-{
- if (icon == "")
- return;
-
- ++notify_count;
- --notify_index;
-
- if (notify_index == -1)
- notify_index = NOTIFY_MAX_ENTRIES-1;
-
- // Free old strings
- if (notify_attackers[notify_index])
- strunzone(notify_attackers[notify_index]);
-
- if (notify_victims[notify_index])
- strunzone(notify_victims[notify_index]);
-
- if (notify_icons[notify_index])
- strunzone(notify_icons[notify_index]);
-
- // Allocate new strings
- if (victim != "")
- {
- notify_attackers[notify_index] = strzone(attacker);
- notify_victims[notify_index] = strzone(victim);
- }
- else
- {
- // In case of a notification without a victim, the attacker
- // is displayed on the victim's side. Instead of special
- // treatment later on, we can simply switch them here.
- notify_attackers[notify_index] = string_null;
- notify_victims[notify_index] = strzone(attacker);
- }
-
- notify_icons[notify_index] = strzone(icon);
- notify_times[notify_index] = time;
-}
-
-void HUD_Notify(void)
-{
- if (!autocvar__hud_configure)
- if (!autocvar_hud_panel_notify)
- return;
-
- HUD_Panel_UpdateCvars();
- HUD_Panel_DrawBg(1);
-
- if (!autocvar__hud_configure)
- if (notify_count == 0)
- return;
-
- vector pos, size;
- pos = panel_pos;
- size = panel_size;
-
- if (panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- size -= '2 2 0' * panel_bg_padding;
- }
-
- float fade_start = max(0, autocvar_hud_panel_notify_time);
- float fade_time = max(0, autocvar_hud_panel_notify_fadetime);
- float icon_aspect = max(1, autocvar_hud_panel_notify_icon_aspect);
-
- int entry_count = bound(1, floor(NOTIFY_MAX_ENTRIES * size.y / size.x), NOTIFY_MAX_ENTRIES);
- float entry_height = size.y / entry_count;
-
- float panel_width_half = size.x * 0.5;
- float icon_width_half = entry_height * icon_aspect / 2;
- float name_maxwidth = panel_width_half - icon_width_half - size.x * NOTIFY_ICON_MARGIN;
-
- vector font_size = '0.5 0.5 0' * entry_height * autocvar_hud_panel_notify_fontsize;
- vector icon_size = (eX * icon_aspect + eY) * entry_height;
- vector icon_left = eX * (panel_width_half - icon_width_half);
- vector attacker_right = eX * name_maxwidth;
- vector victim_left = eX * (size.x - name_maxwidth);
-
- vector attacker_pos, victim_pos, icon_pos;
- string attacker, victim, icon;
- int i, j, count, step, limit;
- float alpha;
-
- if (autocvar_hud_panel_notify_flip)
- {
- // Order items from the top down
- i = 0;
- step = +1;
- limit = entry_count;
- }
- else
- {
- // Order items from the bottom up
- i = entry_count - 1;
- step = -1;
- limit = -1;
- }
-
- for (j = notify_index, count = 0; i != limit; i += step, ++j, ++count)
- {
- if(autocvar__hud_configure)
- {
- attacker = sprintf(_("Player %d"), count + 1);
- victim = sprintf(_("Player %d"), count + 2);
- icon = get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).model2;
- alpha = bound(0, 1.2 - count / entry_count, 1);
- }
- else
- {
- if (j == NOTIFY_MAX_ENTRIES)
- j = 0;
-
- if (notify_times[j] + fade_start > time)
- alpha = 1;
- else if (fade_time != 0)
- {
- alpha = bound(0, (notify_times[j] + fade_start + fade_time - time) / fade_time, 1);
- if (alpha == 0)
- break;
- }
- else
- break;
-
- attacker = notify_attackers[j];
- victim = notify_victims[j];
- icon = notify_icons[j];
- }
-
- if (icon != "" && victim != "")
- {
- vector name_top = eY * (i * entry_height + 0.5 * (entry_height - font_size.y));
-
- icon_pos = pos + icon_left + eY * i * entry_height;
- drawpic_aspect_skin(icon_pos, icon, icon_size, '1 1 1', panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
-
- victim = textShortenToWidth(victim, name_maxwidth, font_size, stringwidth_colors);
- victim_pos = pos + victim_left + name_top;
- drawcolorcodedstring(victim_pos, victim, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
-
- if (attacker != "")
- {
- attacker = textShortenToWidth(attacker, name_maxwidth, font_size, stringwidth_colors);
- attacker_pos = pos + attacker_right - eX * stringwidth(attacker, true, font_size) + name_top;
- drawcolorcodedstring(attacker_pos, attacker, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
- }
- }
- }
-
- notify_count = count;
-}
-
-void HUD_Timer(void)
-{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_timer) return;
- }
-
- HUD_Panel_UpdateCvars();
-
- draw_beginBoldFont();
-
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- string timer;
- float timelimit, elapsedTime, timeleft, minutesLeft;
-
- timelimit = getstatf(STAT_TIMELIMIT);
-
- timeleft = max(0, timelimit * 60 + getstatf(STAT_GAMESTARTTIME) - time);
- timeleft = ceil(timeleft);
-
- minutesLeft = floor(timeleft / 60);
-
- vector timer_color;
- if(minutesLeft >= 5 || warmup_stage || timelimit == 0) //don't use red or yellow in warmup or when there is no timelimit
- timer_color = '1 1 1'; //white
- else if(minutesLeft >= 1)
- timer_color = '1 1 0'; //yellow
- else
- timer_color = '1 0 0'; //red
-
- if (autocvar_hud_panel_timer_increment || timelimit == 0 || warmup_stage) {
- if (time < getstatf(STAT_GAMESTARTTIME)) {
- //while restart is still active, show 00:00
- timer = seconds_tostring(0);
- } else {
- elapsedTime = floor(time - getstatf(STAT_GAMESTARTTIME)); //127
- timer = seconds_tostring(elapsedTime);
- }
- } else {
- timer = seconds_tostring(timeleft);
- }
-
- drawstring_aspect(pos, timer, mySize, timer_color, panel_fg_alpha, DRAWFLAG_NORMAL);
-
- draw_endBoldFont();
-}
-
-// Radar (#6)
-//
-
-float HUD_Radar_Clickable()
-{
- return hud_panel_radar_mouse && !hud_panel_radar_temp_hidden;
-}
-
-void HUD_Radar_Show_Maximized(bool doshow,float clickable)
-{
- hud_panel_radar_maximized = doshow;
- hud_panel_radar_temp_hidden = 0;
-
- if ( doshow )
- {
- if (clickable)
- {
- if(autocvar_hud_cursormode)
- setcursormode(1);
- hud_panel_radar_mouse = 1;
- }
- }
- else if ( hud_panel_radar_mouse )
- {
- hud_panel_radar_mouse = 0;
- mouseClicked = 0;
- if(autocvar_hud_cursormode)
- if(!mv_active)
- setcursormode(0);
- }
-}
-void HUD_Radar_Hide_Maximized()
-{
- HUD_Radar_Show_Maximized(false,false);
-}
-
-
-float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary)
-{
- if(!hud_panel_radar_maximized || !hud_panel_radar_mouse ||
- autocvar__hud_configure || mv_active)
- return false;
-
- if(bInputType == 3)
- {
- mousepos_x = nPrimary;
- mousepos_y = nSecondary;
- return true;
- }
-
- if(nPrimary == K_MOUSE1)
- {
- if(bInputType == 0) // key pressed
- mouseClicked |= S_MOUSE1;
- else if(bInputType == 1) // key released
- mouseClicked -= (mouseClicked & S_MOUSE1);
- }
- else if(nPrimary == K_MOUSE2)
- {
- if(bInputType == 0) // key pressed
- mouseClicked |= S_MOUSE2;
- else if(bInputType == 1) // key released
- mouseClicked -= (mouseClicked & S_MOUSE2);
- }
- else if ( nPrimary == K_ESCAPE && bInputType == 0 )
- {
- HUD_Radar_Hide_Maximized();
- }
- else
- {
- // allow console/use binds to work without hiding the map
- string con_keys;
- float keys;
- float i;
- con_keys = strcat(findkeysforcommand("toggleconsole", 0)," ",findkeysforcommand("+use", 0)) ;
- keys = tokenize(con_keys); // findkeysforcommand returns data for this
- for (i = 0; i < keys; ++i)
- {
- if(nPrimary == stof(argv(i)))
- return false;
- }
-
- if ( getstati(STAT_HEALTH) <= 0 )
- {
- // Show scoreboard
- if ( bInputType < 2 )
- {
- con_keys = findkeysforcommand("+showscores", 0);
- keys = tokenize(con_keys);
- for (i = 0; i < keys; ++i)
- {
- if ( nPrimary == stof(argv(i)) )
- {
- hud_panel_radar_temp_hidden = bInputType == 0;
- return false;
- }
- }
- }
- }
- else if ( bInputType == 0 )
- HUD_Radar_Hide_Maximized();
-
- return false;
- }
-
- return true;
-}
-
-void HUD_Radar_Mouse()
-{
- if ( !hud_panel_radar_mouse ) return;
- if(mv_active) return;
-
- if ( intermission )
- {
- HUD_Radar_Hide_Maximized();
- return;
- }
-
- if(mouseClicked & S_MOUSE2)
- {
- HUD_Radar_Hide_Maximized();
- return;
- }
-
- if(!autocvar_hud_cursormode)
- {
- mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed;
-
- mousepos_x = bound(0, mousepos_x, vid_conwidth);
- mousepos_y = bound(0, mousepos_y, vid_conheight);
- }
-
- HUD_Panel_UpdateCvars();
-
-
- panel_size = autocvar_hud_panel_radar_maximized_size;
- panel_size_x = bound(0.2, panel_size_x, 1) * vid_conwidth;
- panel_size_y = bound(0.2, panel_size_y, 1) * vid_conheight;
- panel_pos_x = (vid_conwidth - panel_size_x) / 2;
- panel_pos_y = (vid_conheight - panel_size_y) / 2;
-
- if(mouseClicked & S_MOUSE1)
- {
- // click outside
- if ( mousepos_x < panel_pos_x || mousepos_x > panel_pos_x + panel_size_x ||
- mousepos_y < panel_pos_y || mousepos_y > panel_pos_y + panel_size_y )
- {
- HUD_Radar_Hide_Maximized();
- return;
- }
- vector pos = teamradar_texcoord_to_3dcoord(teamradar_2dcoord_to_texcoord(mousepos),view_origin_z);
- localcmd(sprintf("cmd ons_spawn %f %f %f",pos_x,pos_y,pos_z));
-
- HUD_Radar_Hide_Maximized();
- return;
- }
-
-
- const vector cursor_size = '32 32 0';
- drawpic(mousepos-'8 4 0', strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursor_size, '1 1 1', 0.8, DRAWFLAG_NORMAL);
-}
-
-void HUD_Radar(void)
-{
- if (!autocvar__hud_configure)
- {
- if (hud_panel_radar_maximized)
- {
- if (!hud_draw_maximized) return;
- }
- else
- {
- if (autocvar_hud_panel_radar == 0) return;
- if (autocvar_hud_panel_radar != 2 && !teamplay) return;
- if(radar_panel_modified)
- {
- panel.update_time = time; // forces reload of panel attributes
- radar_panel_modified = false;
- }
- }
- }
-
- if ( hud_panel_radar_temp_hidden )
- return;
-
- HUD_Panel_UpdateCvars();
-
- float f = 0;
-
- if (hud_panel_radar_maximized && !autocvar__hud_configure)
- {
- panel_size = autocvar_hud_panel_radar_maximized_size;
- panel_size.x = bound(0.2, panel_size.x, 1) * vid_conwidth;
- panel_size.y = bound(0.2, panel_size.y, 1) * vid_conheight;
- panel_pos.x = (vid_conwidth - panel_size.x) / 2;
- panel_pos.y = (vid_conheight - panel_size.y) / 2;
-
- string panel_bg;
- panel_bg = strcat(hud_skin_path, "/border_default"); // always use the default border when maximized
- if(precache_pic(panel_bg) == "")
- panel_bg = "gfx/hud/default/border_default"; // fallback
- if(!radar_panel_modified && panel_bg != panel.current_panel_bg)
- radar_panel_modified = true;
- if(panel.current_panel_bg)
- strunzone(panel.current_panel_bg);
- panel.current_panel_bg = strzone(panel_bg);
-
- switch(hud_panel_radar_maximized_zoommode)
- {
- default:
- case 0:
- f = current_zoomfraction;
- break;
- case 1:
- f = 1 - current_zoomfraction;
- break;
- case 2:
- f = 0;
- break;
- case 3:
- f = 1;
- break;
- }
-
- switch(hud_panel_radar_maximized_rotation)
- {
- case 0:
- teamradar_angle = view_angles.y - 90;
- break;
- default:
- teamradar_angle = 90 * hud_panel_radar_maximized_rotation;
- break;
- }
- }
- if (!hud_panel_radar_maximized && !autocvar__hud_configure)
- {
- switch(hud_panel_radar_zoommode)
- {
- default:
- case 0:
- f = current_zoomfraction;
- break;
- case 1:
- f = 1 - current_zoomfraction;
- break;
- case 2:
- f = 0;
- break;
- case 3:
- f = 1;
- break;
- }
-
- switch(hud_panel_radar_rotation)
- {
- case 0:
- teamradar_angle = view_angles.y - 90;
- break;
- default:
- teamradar_angle = 90 * hud_panel_radar_rotation;
- break;
- }
- }
-
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- int color2;
- entity tm;
- float scale2d, normalsize, bigsize;
-
- teamradar_origin2d = pos + 0.5 * mySize;
- teamradar_size2d = mySize;
-
- if(minimapname == "")
- return;
-
- teamradar_loadcvars();
-
- scale2d = vlen_maxnorm2d(mi_picmax - mi_picmin);
- teamradar_size2d = mySize;
-
- teamradar_extraclip_mins = teamradar_extraclip_maxs = '0 0 0'; // we always center
-
- // pixels per world qu to match the teamradar_size2d_x range in the longest dimension
- if((hud_panel_radar_rotation == 0 && !hud_panel_radar_maximized) || (hud_panel_radar_maximized_rotation == 0 && hud_panel_radar_maximized))
- {
- // max-min distance must fit the radar in any rotation
- bigsize = vlen_minnorm2d(teamradar_size2d) * scale2d / (1.05 * vlen2d(mi_scale));
- }
- else
- {
- vector c0, c1, c2, c3, span;
- c0 = rotate(mi_min, teamradar_angle * DEG2RAD);
- c1 = rotate(mi_max, teamradar_angle * DEG2RAD);
- c2 = rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD);
- c3 = rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD);
- span = '0 0 0';
- span.x = max(c0_x, c1_x, c2_x, c3_x) - min(c0_x, c1_x, c2_x, c3_x);
- span.y = max(c0_y, c1_y, c2_y, c3_y) - min(c0_y, c1_y, c2_y, c3_y);
-
- // max-min distance must fit the radar in x=x, y=y
- bigsize = min(
- teamradar_size2d.x * scale2d / (1.05 * span.x),
- teamradar_size2d.y * scale2d / (1.05 * span.y)
- );
- }
-
- normalsize = vlen_maxnorm2d(teamradar_size2d) * scale2d / hud_panel_radar_scale;
- if(bigsize > normalsize)
- normalsize = bigsize;
-
- teamradar_size =
- f * bigsize
- + (1 - f) * normalsize;
- teamradar_origin3d_in_texcoord = teamradar_3dcoord_to_texcoord(
- f * mi_center
- + (1 - f) * view_origin);
-
- drawsetcliparea(
- pos.x,
- pos.y,
- mySize.x,
- mySize.y
- );
-
- draw_teamradar_background(hud_panel_radar_foreground_alpha);
-
- for(tm = world; (tm = find(tm, classname, "radarlink")); )
- draw_teamradar_link(tm.origin, tm.velocity, tm.team);
-
- vector coord;
- vector brightcolor;
- for(tm = world; (tm = findflags(tm, teamradar_icon, 0xFFFFFF)); )
- {
- if ( hud_panel_radar_mouse )
- if ( tm.health > 0 )
- if ( tm.team == myteam+1 )
- {
- coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(tm.origin));
- if ( vlen(mousepos-coord) < 8 )
- {
- brightcolor_x = min(1,tm.teamradar_color_x*1.5);
- brightcolor_y = min(1,tm.teamradar_color_y*1.5);
- brightcolor_z = min(1,tm.teamradar_color_z*1.5);
- drawpic(coord - '8 8 0', "gfx/teamradar_icon_glow", '16 16 0', brightcolor, panel_fg_alpha, 0);
- }
- }
- entity icon = RadarIcons[tm.teamradar_icon];
- draw_teamradar_icon(tm.origin, icon, tm, spritelookupcolor(tm, icon.netname, tm.teamradar_color), panel_fg_alpha);
- }
- for(tm = world; (tm = find(tm, classname, "entcs_receiver")); )
- {
- color2 = GetPlayerColor(tm.sv_entnum);
- //if(color == NUM_SPECTATOR || color == color2)
- draw_teamradar_player(tm.origin, tm.angles, Team_ColorRGB(color2));
- }
- draw_teamradar_player(view_origin, view_angles, '1 1 1');
-
- drawresetcliparea();
-
- if ( hud_panel_radar_mouse )
- {
- string message = "Click to select teleport destination";
-
- if ( getstati(STAT_HEALTH) <= 0 )
- {
- message = "Click to select spawn location";
- }
-
- drawcolorcodedstring(pos + '0.5 0 0' * (mySize_x - stringwidth(message, true, hud_fontsize)) - '0 1 0' * hud_fontsize_y * 2,
- message, hud_fontsize, hud_panel_radar_foreground_alpha, DRAWFLAG_NORMAL);
-
- hud_panel_radar_bottom = pos_y + mySize_y + hud_fontsize_y;
- }
-}
-
-// Score (#7)
-//
-void HUD_UpdatePlayerTeams();
-void HUD_Score_Rankings(vector pos, vector mySize, entity me)
-{
- float score;
- entity tm = world, pl;
- int SCOREPANEL_MAX_ENTRIES = 6;
- float SCOREPANEL_ASPECTRATIO = 2;
- int entries = bound(1, floor(SCOREPANEL_MAX_ENTRIES * mySize.y/mySize.x * SCOREPANEL_ASPECTRATIO), SCOREPANEL_MAX_ENTRIES);
- vector fontsize = '1 1 0' * (mySize.y/entries);
-
- vector rgb, score_color;
- rgb = '1 1 1';
- score_color = '1 1 1';
-
- float name_size = mySize.x*0.75;
- float spacing_size = mySize.x*0.04;
- const float highlight_alpha = 0.2;
- int i = 0, first_pl = 0;
- bool me_printed = false;
- string s;
- if (autocvar__hud_configure)
- {
- float players_per_team = 0;
- if (team_count)
- {
- // show team scores in the first line
- float score_size = mySize.x / team_count;
- players_per_team = max(2, ceil((entries - 1) / team_count));
- for(i=0; i<team_count; ++i) {
- if (i == floor((entries - 2) / players_per_team) || (entries == 1 && i == 0))
- HUD_Panel_DrawHighlight(pos + eX * score_size * i, eX * score_size + eY * fontsize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(pos + eX * score_size * i, ftos(175 - 23*i), eX * score_size + eY * fontsize.y, Team_ColorRGB(ColorByTeam(i)) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- first_pl = 1;
- pos.y += fontsize.y;
- }
- score = 10 + SCOREPANEL_MAX_ENTRIES * 3;
- for (i=first_pl; i<entries; ++i)
- {
- //simulate my score is lower than all displayed players,
- //so that I don't appear at all showing pure rankings.
- //This is to better show the difference between the 2 ranking views
- if (i == entries-1 && autocvar_hud_panel_score_rankings == 1)
- {
- rgb = '1 1 0';
- drawfill(pos, eX * mySize.x + eY * fontsize.y, rgb, highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- s = GetPlayerName(player_localnum);
- score = 7;
- }
- else
- {
- s = sprintf(_("Player %d"), i + 1 - first_pl);
- score -= 3;
- }
-
- if (team_count)
- score_color = Team_ColorRGB(ColorByTeam(floor((i - first_pl) / players_per_team))) * 0.8;
- s = textShortenToWidth(s, name_size, fontsize, stringwidth_colors);
- drawcolorcodedstring(pos + eX * (name_size - stringwidth(s, true, fontsize)), s, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring(pos + eX * (name_size + spacing_size), ftos(score), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL);
- pos.y += fontsize.y;
- }
- return;
- }
-
- if (!scoreboard_fade_alpha) // the scoreboard too calls HUD_UpdatePlayerTeams
- HUD_UpdatePlayerTeams();
- if (team_count)
- {
- // show team scores in the first line
- float score_size = mySize.x / team_count;
- for(tm = teams.sort_next; tm; tm = tm.sort_next) {
- if(tm.team == NUM_SPECTATOR)
- continue;
- if (tm.team == myteam)
- drawfill(pos + eX * score_size * i, eX * score_size + eY * fontsize.y, '1 1 1', highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(pos + eX * score_size * i, ftos(tm.(teamscores[ts_primary])), eX * score_size + eY * fontsize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
- ++i;
- }
- first_pl = 1;
- pos.y += fontsize.y;
- tm = teams.sort_next;
- }
- i = first_pl;
-
- do
- for (pl = players.sort_next; pl && i<entries; pl = pl.sort_next)
- {
- if ((team_count && pl.team != tm.team) || pl.team == NUM_SPECTATOR)
- continue;
-
- if (i == entries-1 && !me_printed && pl != me)
- if (autocvar_hud_panel_score_rankings == 1 && spectatee_status != -1)
- {
- for (pl = me.sort_next; pl; pl = pl.sort_next)
- if (pl.team != NUM_SPECTATOR)
- break;
-
- if (pl)
- rgb = '1 1 0'; //not last but not among the leading players: yellow
- else
- rgb = '1 0 0'; //last: red
- pl = me;
- }
-
- if (pl == me)
- {
- if (i == first_pl)
- rgb = '0 1 0'; //first: green
- me_printed = true;
- drawfill(pos, eX * mySize.x + eY * fontsize.y, rgb, highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- if (team_count)
- score_color = Team_ColorRGB(pl.team) * 0.8;
- s = textShortenToWidth(GetPlayerName(pl.sv_entnum), name_size, fontsize, stringwidth_colors);
- drawcolorcodedstring(pos + eX * (name_size - stringwidth(s, true, fontsize)), s, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring(pos + eX * (name_size + spacing_size), ftos(pl.(scores[ps_primary])), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL);
- pos.y += fontsize.y;
- ++i;
- }
- while (i<entries && team_count && (tm = tm.sort_next) && (tm.team != NUM_SPECTATOR || (tm = tm.sort_next)));
-}
-
-void HUD_Score(void)
-{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_score) return;
- if(spectatee_status == -1 && (gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
- }
-
- HUD_Panel_UpdateCvars();
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- float score, distribution = 0;
- string sign;
- vector distribution_color;
- entity tm, pl, me;
-
- me = playerslots[current_player];
-
- if((scores_flags[ps_primary] & SFL_TIME) && !teamplay) { // race/cts record display on HUD
- string timer, distrtimer;
-
- pl = players.sort_next;
- if(pl == me)
- pl = pl.sort_next;
- if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
- if(pl.scores[ps_primary] == 0)
- pl = world;
-
- score = me.(scores[ps_primary]);
- timer = TIME_ENCODED_TOSTRING(score);
-
- draw_beginBoldFont();
- if (pl && ((!(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)) || score)) {
- // distribution display
- distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
-
- distrtimer = ftos_decimals(fabs(distribution/pow(10, TIME_DECIMALS)), TIME_DECIMALS);
-
- if (distribution <= 0) {
- distribution_color = '0 1 0';
- sign = "-";
- }
- else {
- distribution_color = '1 0 0';
- sign = "+";
- }
- drawstring_aspect(pos + eX * 0.75 * mySize.x, strcat(sign, distrtimer), eX * 0.25 * mySize.x + eY * (1/3) * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- // race record display
- if (distribution <= 0)
- HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(pos, timer, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- draw_endBoldFont();
- } else if (!teamplay) { // non-teamgames
- if ((spectatee_status == -1 && !autocvar__hud_configure) || autocvar_hud_panel_score_rankings)
- {
- HUD_Score_Rankings(pos, mySize, me);
- return;
- }
- // me vector := [team/connected frags id]
- pl = players.sort_next;
- if(pl == me)
- pl = pl.sort_next;
-
- if(autocvar__hud_configure)
- distribution = 42;
- else if(pl)
- distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
- else
- distribution = 0;
-
- score = me.(scores[ps_primary]);
- if(autocvar__hud_configure)
- score = 123;
-
- if(distribution >= 5)
- distribution_color = eY;
- else if(distribution >= 0)
- distribution_color = '1 1 1';
- else if(distribution >= -5)
- distribution_color = '1 1 0';
- else
- distribution_color = eX;
-
- string distribution_str;
- distribution_str = ftos(distribution);
- draw_beginBoldFont();
- if (distribution >= 0)
- {
- if (distribution > 0)
- distribution_str = strcat("+", distribution_str);
- HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(pos + eX * 0.75 * mySize.x, distribution_str, eX * 0.25 * mySize.x + eY * (1/3) * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
- draw_endBoldFont();
- } else { // teamgames
- float row, column, rows = 0, columns = 0;
- vector offset = '0 0 0';
- vector score_pos, score_size; //for scores other than myteam
- if(autocvar_hud_panel_score_rankings)
- {
- HUD_Score_Rankings(pos, mySize, me);
- return;
- }
- if(spectatee_status == -1)
- {
- rows = HUD_GetRowCount(team_count, mySize, 3);
- columns = ceil(team_count/rows);
- score_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
-
- float newSize;
- if(score_size.x/score_size.y > 3)
- {
- newSize = 3 * score_size.y;
- offset.x = score_size.x - newSize;
- pos.x += offset.x/2;
- score_size.x = newSize;
- }
- else
- {
- newSize = 1/3 * score_size.x;
- offset.y = score_size.y - newSize;
- pos.y += offset.y/2;
- score_size.y = newSize;
- }
- }
- else
- score_size = eX * mySize.x*(1/4) + eY * mySize.y*(1/3);
-
- float max_fragcount;
- max_fragcount = -99;
- draw_beginBoldFont();
- row = column = 0;
- for(tm = teams.sort_next; tm; tm = tm.sort_next) {
- if(tm.team == NUM_SPECTATOR)
- continue;
- score = tm.(teamscores[ts_primary]);
- if(autocvar__hud_configure)
- score = 123;
-
- if (score > max_fragcount)
- max_fragcount = score;
-
- if (spectatee_status == -1)
- {
- score_pos = pos + eX * column * (score_size.x + offset.x) + eY * row * (score_size.y + offset.y);
- if (max_fragcount == score)
- HUD_Panel_DrawHighlight(score_pos, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(score_pos, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
- ++row;
- if(row >= rows)
- {
- row = 0;
- ++column;
- }
- }
- else if(tm.team == myteam) {
- if (max_fragcount == score)
- HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
- } else {
- if (max_fragcount == score)
- HUD_Panel_DrawHighlight(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
- ++rows;
- }
- }
- draw_endBoldFont();
- }
-}
-
-// Race timer (#8)
-//
-void HUD_RaceTimer (void)
-{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_racetimer) return;
- if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
- if(spectatee_status == -1) return;
- }
-
- HUD_Panel_UpdateCvars();
-
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- // always force 4:1 aspect
- vector newSize = '0 0 0';
- if(mySize.x/mySize.y > 4)
- {
- newSize.x = 4 * mySize.y;
- newSize.y = mySize.y;
-
- pos.x = pos.x + (mySize.x - newSize.x) / 2;
- }
- else
- {
- newSize.y = 1/4 * mySize.x;
- newSize.x = mySize.x;
-
- pos.y = pos.y + (mySize.y - newSize.y) / 2;
- }
- mySize = newSize;
-
- float a, t;
- string s, forcetime;
-
- if(autocvar__hud_configure)
- {
- s = "0:13:37";
- draw_beginBoldFont();
- drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.60 0.60 0' * mySize.y), s, '0.60 0.60 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- draw_endBoldFont();
- s = _("^1Intermediate 1 (+15.42)");
- drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.60 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
- s = sprintf(_("^1PENALTY: %.1f (%s)"), 2, "missing a checkpoint");
- drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.80 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- else if(race_checkpointtime)
- {
- a = bound(0, 2 - (time - race_checkpointtime), 1);
- s = "";
- forcetime = "";
- if(a > 0) // just hit a checkpoint?
- {
- if(race_checkpoint != 254)
- {
- if(race_time && race_previousbesttime)
- s = MakeRaceString(race_checkpoint, TIME_DECODE(race_time) - TIME_DECODE(race_previousbesttime), 0, 0, race_previousbestname);
- else
- s = MakeRaceString(race_checkpoint, 0, -1, 0, race_previousbestname);
- if(race_time)
- forcetime = TIME_ENCODED_TOSTRING(race_time);
- }
- }
- else
- {
- if(race_laptime && race_nextbesttime && race_nextcheckpoint != 254)
- {
- a = bound(0, 2 - ((race_laptime + TIME_DECODE(race_nextbesttime)) - (time + TIME_DECODE(race_penaltyaccumulator))), 1);
- if(a > 0) // next one?
- {
- s = MakeRaceString(race_nextcheckpoint, (time + TIME_DECODE(race_penaltyaccumulator)) - race_laptime, TIME_DECODE(race_nextbesttime), 0, race_nextbestname);
- }
- }
- }
-
- if(s != "" && a > 0)
- {
- drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- }
-
- if(race_penaltytime)
- {
- a = bound(0, 2 - (time - race_penaltyeventtime), 1);
- if(a > 0)
- {
- s = sprintf(_("^1PENALTY: %.1f (%s)"), race_penaltytime * 0.1, race_penaltyreason);
- drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.8 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- }
- }
-
- draw_beginBoldFont();
-
- if(forcetime != "")
- {
- a = bound(0, (time - race_checkpointtime) / 0.5, 1);
- drawstring_expanding(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(forcetime, false, '1 1 0' * 0.6 * mySize.y), forcetime, '1 1 0' * 0.6 * mySize.y, '1 1 1', panel_fg_alpha, 0, a);
- }
- else
- a = 1;
-
- if(race_laptime && race_checkpoint != 255)
- {
- s = TIME_ENCODED_TOSTRING(TIME_ENCODE(time + TIME_DECODE(race_penaltyaccumulator) - race_laptime));
- drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.6 0.6 0' * mySize.y), s, '0.6 0.6 0' * mySize.y, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- }
-
- draw_endBoldFont();
- }
- else
- {
- if(race_mycheckpointtime)
- {
- a = bound(0, 2 - (time - race_mycheckpointtime), 1);
- s = MakeRaceString(race_mycheckpoint, TIME_DECODE(race_mycheckpointdelta), -(race_mycheckpointenemy == ""), race_mycheckpointlapsdelta, race_mycheckpointenemy);
- drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- }
- if(race_othercheckpointtime && race_othercheckpointenemy != "")
- {
- a = bound(0, 2 - (time - race_othercheckpointtime), 1);
- s = MakeRaceString(race_othercheckpoint, -TIME_DECODE(race_othercheckpointdelta), -(race_othercheckpointenemy == ""), race_othercheckpointlapsdelta, race_othercheckpointenemy);
- drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- }
-
- if(race_penaltytime && !race_penaltyaccumulator)
- {
- t = race_penaltytime * 0.1 + race_penaltyeventtime;
- a = bound(0, (1 + t - time), 1);
- if(a > 0)
- {
- if(time < t)
- s = sprintf(_("^1PENALTY: %.1f (%s)"), (t - time) * 0.1, race_penaltyreason);
- else
- s = sprintf(_("^2PENALTY: %.1f (%s)"), 0, race_penaltyreason);
- drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- }
- }
- }
-}
-
-// Vote window (#9)
-//
-
-void HUD_Vote(void)
-{
- if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS)))
- {
- vote_active = 1;
- if (autocvar__hud_configure)
- {
- vote_yescount = 0;
- vote_nocount = 0;
- LOG_INFO(_("^1You must answer before entering hud configure mode\n"));
- cvar_set("_hud_configure", "0");
- }
- if(vote_called_vote)
- strunzone(vote_called_vote);
- vote_called_vote = strzone(_("^2Name ^7instead of \"^1Anonymous player^7\" in stats"));
- uid2name_dialog = 1;
- }
-
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_vote) return;
-
- panel_fg_alpha = autocvar_hud_panel_fg_alpha;
- panel_bg_alpha_str = autocvar_hud_panel_vote_bg_alpha;
-
- if(panel_bg_alpha_str == "") {
- panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha);
- }
- panel_bg_alpha = stof(panel_bg_alpha_str);
- }
- else
- {
- vote_yescount = 3;
- vote_nocount = 2;
- vote_needed = 4;
- }
-
- string s;
- float a;
- if(vote_active != vote_prev) {
- vote_change = time;
- vote_prev = vote_active;
- }
-
- if(vote_active || autocvar__hud_configure)
- vote_alpha = bound(0, (time - vote_change) * 2, 1);
- else
- vote_alpha = bound(0, 1 - (time - vote_change) * 2, 1);
-
- if(!vote_alpha)
- return;
-
- HUD_Panel_UpdateCvars();
-
- if(uid2name_dialog)
- {
- panel_pos = eX * 0.3 * vid_conwidth + eY * 0.1 * vid_conheight;
- panel_size = eX * 0.4 * vid_conwidth + eY * 0.3 * vid_conheight;
- }
-
- // these must be below above block
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- a = vote_alpha * (vote_highlighted ? autocvar_hud_panel_vote_alreadyvoted_alpha : 1);
- HUD_Panel_DrawBg(a);
- a = panel_fg_alpha * a;
-
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- // always force 3:1 aspect
- vector newSize = '0 0 0';
- if(mySize.x/mySize.y > 3)
- {
- newSize.x = 3 * mySize.y;
- newSize.y = mySize.y;
-
- pos.x = pos.x + (mySize.x - newSize.x) / 2;
- }
- else
- {
- newSize.y = 1/3 * mySize.x;
- newSize.x = mySize.x;
-
- pos.y = pos.y + (mySize.y - newSize.y) / 2;
- }
- mySize = newSize;
-
- s = _("A vote has been called for:");
- if(uid2name_dialog)
- s = _("Allow servers to store and display your name?");
- drawstring_aspect(pos, s, eX * mySize.x + eY * (2/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
- s = textShortenToWidth(vote_called_vote, mySize.x, '1 1 0' * mySize.y * (1/8), stringwidth_colors);
- if(autocvar__hud_configure)
- s = _("^1Configure the HUD");
- drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, a, DRAWFLAG_NORMAL);
-
- // print the yes/no counts
- s = sprintf(_("Yes (%s): %d"), getcommandkey("vyes", "vyes"), vote_yescount);
- drawstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '0 1 0', a, DRAWFLAG_NORMAL);
- s = sprintf(_("No (%s): %d"), getcommandkey("vno", "vno"), vote_nocount);
- drawstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '1 0 0', a, DRAWFLAG_NORMAL);
-
- // draw the progress bar backgrounds
- drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_back", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
-
- // draw the highlights
- if(vote_highlighted == 1) {
- drawsetcliparea(pos.x, pos.y, mySize.x * 0.5, mySize.y);
- drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
- }
- else if(vote_highlighted == -1) {
- drawsetcliparea(pos.x + 0.5 * mySize.x, pos.y, mySize.x * 0.5, mySize.y);
- drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
- }
-
- // draw the progress bars
- if(vote_yescount && vote_needed)
- {
- drawsetcliparea(pos.x, pos.y, mySize.x * 0.5 * (vote_yescount/vote_needed), mySize.y);
- drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
- }
-
- if(vote_nocount && vote_needed)
- {
- drawsetcliparea(pos.x + mySize.x - mySize.x * 0.5 * (vote_nocount/vote_needed), pos.y, mySize.x * 0.5, mySize.y);
- drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
- }
-
- drawresetcliparea();
-}
-
-// Mod icons panel (#10)
-//
-
-bool mod_active; // is there any active mod icon?
-
-void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i)
-{
- int stat = -1;
- string pic = "";
- vector color = '0 0 0';
- switch(i)
- {
- case 0:
- stat = getstati(STAT_REDALIVE);
- pic = "player_red.tga";
- color = '1 0 0';
- break;
- case 1:
- stat = getstati(STAT_BLUEALIVE);
- pic = "player_blue.tga";
- color = '0 0 1';
- break;
- case 2:
- stat = getstati(STAT_YELLOWALIVE);
- pic = "player_yellow.tga";
- color = '1 1 0';
- break;
- default:
- case 3:
- stat = getstati(STAT_PINKALIVE);
- pic = "player_pink.tga";
- color = '1 0 1';
- break;
- }
-
- if(mySize.x/mySize.y > aspect_ratio)
- {
- i = aspect_ratio * mySize.y;
- myPos.x = myPos.x + (mySize.x - i) / 2;
- mySize.x = i;
- }
- else
- {
- i = 1/aspect_ratio * mySize.x;
- myPos.y = myPos.y + (mySize.y - i) / 2;
- mySize.y = i;
- }
-
- if(layout)
- {
- drawpic_aspect_skin(myPos, pic, eX * 0.7 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(myPos + eX * 0.7 * mySize.x, ftos(stat), eX * 0.3 * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- else
- drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
-}
-
-// Clan Arena and Freeze Tag HUD modicons
-void HUD_Mod_CA(vector myPos, vector mySize)
-{
- mod_active = 1; // required in each mod function that always shows something
-
- int layout;
- if(gametype == MAPINFO_TYPE_CA)
- layout = autocvar_hud_panel_modicons_ca_layout;
- else //if(gametype == MAPINFO_TYPE_FREEZETAG)
- layout = autocvar_hud_panel_modicons_freezetag_layout;
- int rows, columns;
- float aspect_ratio;
- aspect_ratio = (layout) ? 2 : 1;
- rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
- columns = ceil(team_count/rows);
-
- int i;
- float row = 0, column = 0;
- vector pos, itemSize;
- itemSize = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
- for(i=0; i<team_count; ++i)
- {
- pos = myPos + eX * column * itemSize.x + eY * row * itemSize.y;
-
- DrawCAItem(pos, itemSize, aspect_ratio, layout, i);
-
- ++row;
- if(row >= rows)
- {
- row = 0;
- ++column;
- }
- }
-}
-
-// CTF HUD modicon section
-int redflag_prevframe, blueflag_prevframe, yellowflag_prevframe, pinkflag_prevframe, neutralflag_prevframe; // status during previous frame
-int redflag_prevstatus, blueflag_prevstatus, yellowflag_prevstatus, pinkflag_prevstatus, neutralflag_prevstatus; // last remembered status
-float redflag_statuschange_time, blueflag_statuschange_time, yellowflag_statuschange_time, pinkflag_statuschange_time, neutralflag_statuschange_time; // time when the status changed
-
-void HUD_Mod_CTF_Reset(void)
-{
- redflag_prevstatus = blueflag_prevstatus = yellowflag_prevstatus = pinkflag_prevstatus = neutralflag_prevstatus = 0;
- redflag_prevframe = blueflag_prevframe = yellowflag_prevframe = pinkflag_prevframe = neutralflag_prevframe = 0;
- redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_statuschange_time = neutralflag_statuschange_time = 0;
-}
-
-void HUD_Mod_CTF(vector pos, vector mySize)
-{
- vector redflag_pos, blueflag_pos, yellowflag_pos, pinkflag_pos, neutralflag_pos;
- vector flag_size;
- float f; // every function should have that
-
- int redflag, blueflag, yellowflag, pinkflag, neutralflag; // current status
- float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime, yellowflag_statuschange_elapsedtime, pinkflag_statuschange_elapsedtime, neutralflag_statuschange_elapsedtime; // time since the status changed
- bool ctf_oneflag; // one-flag CTF mode enabled/disabled
- int stat_items = getstati(STAT_CTF_FLAGSTATUS, 0, 24);
- float fs, fs2, fs3, size1, size2;
- vector e1, e2;
-
- redflag = (stat_items/CTF_RED_FLAG_TAKEN) & 3;
- blueflag = (stat_items/CTF_BLUE_FLAG_TAKEN) & 3;
- yellowflag = (stat_items/CTF_YELLOW_FLAG_TAKEN) & 3;
- pinkflag = (stat_items/CTF_PINK_FLAG_TAKEN) & 3;
- neutralflag = (stat_items/CTF_NEUTRAL_FLAG_TAKEN) & 3;
-
- ctf_oneflag = (stat_items & CTF_FLAG_NEUTRAL);
-
- mod_active = (redflag || blueflag || yellowflag || pinkflag || neutralflag);
-
- if (autocvar__hud_configure) {
- redflag = 1;
- blueflag = 2;
- if (team_count >= 3)
- yellowflag = 2;
- if (team_count >= 4)
- pinkflag = 3;
- ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor?
- }
-
- // when status CHANGES, set old status into prevstatus and current status into status
- #define X(team) do { \
- if (team##flag != team##flag_prevframe) { \
- team##flag_statuschange_time = time; \
- team##flag_prevstatus = team##flag_prevframe; \
- team##flag_prevframe = team##flag; \
- } \
- team##flag_statuschange_elapsedtime = time - team##flag_statuschange_time; \
- } while (0)
- X(red);
- X(blue);
- X(yellow);
- X(pink);
- X(neutral);
- #undef X
-
- const float BLINK_FACTOR = 0.15;
- const float BLINK_BASE = 0.85;
- // note:
- // RMS = sqrt(BLINK_BASE^2 + 0.5 * BLINK_FACTOR^2)
- // thus
- // BLINK_BASE = sqrt(RMS^2 - 0.5 * BLINK_FACTOR^2)
- // ensure RMS == 1
- const float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz
-
- #define X(team, cond) \
- string team##_icon, team##_icon_prevstatus; \
- int team##_alpha, team##_alpha_prevstatus; \
- team##_alpha = team##_alpha_prevstatus = 1; \
- do { \
- switch (team##flag) { \
- case 1: team##_icon = "flag_" #team "_taken"; break; \
- case 2: team##_icon = "flag_" #team "_lost"; break; \
- case 3: team##_icon = "flag_" #team "_carrying"; team##_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \
- default: \
- if ((stat_items & CTF_SHIELDED) && (cond)) { \
- team##_icon = "flag_" #team "_shielded"; \
- } else { \
- team##_icon = string_null; \
- } \
- break; \
- } \
- switch (team##flag_prevstatus) { \
- case 1: team##_icon_prevstatus = "flag_" #team "_taken"; break; \
- case 2: team##_icon_prevstatus = "flag_" #team "_lost"; break; \
- case 3: team##_icon_prevstatus = "flag_" #team "_carrying"; team##_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \
- default: \
- if (team##flag == 3) { \
- team##_icon_prevstatus = "flag_" #team "_carrying"; /* make it more visible */\
- } else if((stat_items & CTF_SHIELDED) && (cond)) { \
- team##_icon_prevstatus = "flag_" #team "_shielded"; \
- } else { \
- team##_icon_prevstatus = string_null; \
- } \
- break; \
- } \
- } while (0)
- X(red, myteam != NUM_TEAM_1);
- X(blue, myteam != NUM_TEAM_2);
- X(yellow, myteam != NUM_TEAM_3);
- X(pink, myteam != NUM_TEAM_4);
- X(neutral, true);
- #undef X
-
- if (ctf_oneflag) {
- // hacky, but these aren't needed
- red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null;
- fs = fs2 = fs3 = 1;
- } else switch (team_count) {
- default:
- case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break;
- case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break;
- case 4: fs = 0.75; fs2 = 0.25; fs3 = 0.5; break;
- }
-
- if (mySize_x > mySize_y) {
- size1 = mySize_x;
- size2 = mySize_y;
- e1 = eX;
- e2 = eY;
- } else {
- size1 = mySize_y;
- size2 = mySize_x;
- e1 = eY;
- e2 = eX;
- }
-
- switch (myteam) {
- default:
- case NUM_TEAM_1: {
- redflag_pos = pos;
- blueflag_pos = pos + eX * fs2 * size1;
- yellowflag_pos = pos - eX * fs2 * size1;
- pinkflag_pos = pos + eX * fs3 * size1;
- break;
- }
- case NUM_TEAM_2: {
- redflag_pos = pos + eX * fs2 * size1;
- blueflag_pos = pos;
- yellowflag_pos = pos - eX * fs2 * size1;
- pinkflag_pos = pos + eX * fs3 * size1;
- break;
- }
- case NUM_TEAM_3: {
- redflag_pos = pos + eX * fs3 * size1;
- blueflag_pos = pos - eX * fs2 * size1;
- yellowflag_pos = pos;
- pinkflag_pos = pos + eX * fs2 * size1;
- break;
- }
- case NUM_TEAM_4: {
- redflag_pos = pos - eX * fs2 * size1;
- blueflag_pos = pos + eX * fs3 * size1;
- yellowflag_pos = pos + eX * fs2 * size1;
- pinkflag_pos = pos;
- break;
- }
- }
- neutralflag_pos = pos;
- flag_size = e1 * fs * size1 + e2 * size2;
-
- #define X(team) do { \
- f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \
- if (team##_icon_prevstatus && f < 1) \
- drawpic_aspect_skin_expanding(team##flag_pos, team##_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * team##_alpha_prevstatus, DRAWFLAG_NORMAL, f); \
- if (team##_icon) \
- drawpic_aspect_skin(team##flag_pos, team##_icon, flag_size, '1 1 1', panel_fg_alpha * team##_alpha * f, DRAWFLAG_NORMAL); \
- } while (0)
- X(red);
- X(blue);
- X(yellow);
- X(pink);
- X(neutral);
- #undef X
-}
-
-// Keyhunt HUD modicon section
-vector KH_SLOTS[4];
-
-void HUD_Mod_KH(vector pos, vector mySize)
-{
- mod_active = 1; // keyhunt should never hide the mod icons panel
-
- // Read current state
-
- int state = getstati(STAT_KH_KEYS);
- int i, key_state;
- int all_keys, team1_keys, team2_keys, team3_keys, team4_keys, dropped_keys, carrying_keys;
- all_keys = team1_keys = team2_keys = team3_keys = team4_keys = dropped_keys = carrying_keys = 0;
-
- for(i = 0; i < 4; ++i)
- {
- key_state = (bitshift(state, i * -5) & 31) - 1;
-
- if(key_state == -1)
- continue;
-
- if(key_state == 30)
- {
- ++carrying_keys;
- key_state = myteam;
- }
-
- switch(key_state)
- {
- case NUM_TEAM_1: ++team1_keys; break;
- case NUM_TEAM_2: ++team2_keys; break;
- case NUM_TEAM_3: ++team3_keys; break;
- case NUM_TEAM_4: ++team4_keys; break;
- case 29: ++dropped_keys; break;
- }
-
- ++all_keys;
- }
-
- // Calculate slot measurements
-
- vector slot_size;
-
- if(all_keys == 4 && mySize.x * 0.5 < mySize.y && mySize.y * 0.5 < mySize.x)
- {
- // Quadratic arrangement
- slot_size = eX * mySize.x * 0.5 + eY * mySize.y * 0.5;
- KH_SLOTS[0] = pos;
- KH_SLOTS[1] = pos + eX * slot_size.x;
- KH_SLOTS[2] = pos + eY * slot_size.y;
- KH_SLOTS[3] = pos + eX * slot_size.x + eY * slot_size.y;
- }
- else
- {
- if(mySize.x > mySize.y)
- {
- // Horizontal arrangement
- slot_size = eX * mySize.x / all_keys + eY * mySize.y;
- for(i = 0; i < all_keys; ++i)
- KH_SLOTS[i] = pos + eX * slot_size.x * i;
- }
- else
- {
- // Vertical arrangement
- slot_size = eX * mySize.x + eY * mySize.y / all_keys;
- for(i = 0; i < all_keys; ++i)
- KH_SLOTS[i] = pos + eY * slot_size.y * i;
- }
- }
-
- // Make icons blink in case of RUN HERE
-
- float blink = 0.6 + sin(2*M_PI*time) / 2.5; // Oscillate between 0.2 and 1
- float alpha;
- alpha = 1;
-
- if(carrying_keys)
- switch(myteam)
- {
- case NUM_TEAM_1: if(team1_keys == all_keys) alpha = blink; break;
- case NUM_TEAM_2: if(team2_keys == all_keys) alpha = blink; break;
- case NUM_TEAM_3: if(team3_keys == all_keys) alpha = blink; break;
- case NUM_TEAM_4: if(team4_keys == all_keys) alpha = blink; break;
- }
-
- // Draw icons
-
- i = 0;
-
- while(team1_keys--)
- if(myteam == NUM_TEAM_1 && carrying_keys)
- {
- drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
- --carrying_keys;
- }
- else
- drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
-
- while(team2_keys--)
- if(myteam == NUM_TEAM_2 && carrying_keys)
- {
- drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
- --carrying_keys;
- }
- else
- drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
-
- while(team3_keys--)
- if(myteam == NUM_TEAM_3 && carrying_keys)
- {
- drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
- --carrying_keys;
- }
- else
- drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
-
- while(team4_keys--)
- if(myteam == NUM_TEAM_4 && carrying_keys)
- {
- drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
- --carrying_keys;
- }
- else
- drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
-
- while(dropped_keys--)
- drawpic_aspect_skin(KH_SLOTS[i++], "kh_dropped", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
-}
-
-// Keepaway HUD mod icon
-int kaball_prevstatus; // last remembered status
-float kaball_statuschange_time; // time when the status changed
-
-// we don't need to reset for keepaway since it immediately
-// autocorrects prevstatus as to if the player has the ball or not
-
-void HUD_Mod_Keepaway(vector pos, vector mySize)
-{
- mod_active = 1; // keepaway should always show the mod HUD
-
- float BLINK_FACTOR = 0.15;
- float BLINK_BASE = 0.85;
- float BLINK_FREQ = 5;
- float kaball_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
-
- int stat_items = getstati(STAT_ITEMS, 0, 24);
- int kaball = (stat_items/IT_KEY1) & 1;
-
- if(kaball != kaball_prevstatus)
- {
- kaball_statuschange_time = time;
- kaball_prevstatus = kaball;
- }
-
- vector kaball_pos, kaball_size;
-
- if(mySize.x > mySize.y) {
- kaball_pos = pos + eX * 0.25 * mySize.x;
- kaball_size = eX * 0.5 * mySize.x + eY * mySize.y;
- } else {
- kaball_pos = pos + eY * 0.25 * mySize.y;
- kaball_size = eY * 0.5 * mySize.y + eX * mySize.x;
- }
-
- float kaball_statuschange_elapsedtime = time - kaball_statuschange_time;
- float f = bound(0, kaball_statuschange_elapsedtime*2, 1);
-
- if(kaball_prevstatus && f < 1)
- drawpic_aspect_skin_expanding(kaball_pos, "keepawayball_carrying", kaball_size, '1 1 1', panel_fg_alpha * kaball_alpha, DRAWFLAG_NORMAL, f);
-
- if(kaball)
- drawpic_aspect_skin(pos, "keepawayball_carrying", eX * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha * kaball_alpha * f, DRAWFLAG_NORMAL);
-}
-
-
-// Nexball HUD mod icon
-void HUD_Mod_NexBall(vector pos, vector mySize)
-{
- float nb_pb_starttime, dt, p;
- int stat_items;
-
- stat_items = getstati(STAT_ITEMS, 0, 24);
- nb_pb_starttime = getstatf(STAT_NB_METERSTART);
-
- if (stat_items & IT_KEY1)
- mod_active = 1;
- else
- mod_active = 0;
-
- //Manage the progress bar if any
- if (nb_pb_starttime > 0)
- {
- dt = (time - nb_pb_starttime) % nb_pb_period;
- // one period of positive triangle
- p = 2 * dt / nb_pb_period;
- if (p > 1)
- p = 2 - p;
-
- HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", p, (mySize.x <= mySize.y), 0, autocvar_hud_progressbar_nexball_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- }
-
- if (stat_items & IT_KEY1)
- drawpic_aspect_skin(pos, "nexball_carrying", eX * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
-}
-
-// Race/CTS HUD mod icons
-float crecordtime_prev; // last remembered crecordtime
-float crecordtime_change_time; // time when crecordtime last changed
-float srecordtime_prev; // last remembered srecordtime
-float srecordtime_change_time; // time when srecordtime last changed
-
-float race_status_time;
-int race_status_prev;
-string race_status_name_prev;
-void HUD_Mod_Race(vector pos, vector mySize)
-{
- mod_active = 1; // race should never hide the mod icons panel
- entity me;
- me = playerslots[player_localnum];
- float t, score;
- float f; // yet another function has this
- score = me.(scores[ps_primary]);
-
- if(!(scores_flags[ps_primary] & SFL_TIME) || teamplay) // race/cts record display on HUD
- return; // no records in the actual race
-
- // clientside personal record
- string rr;
- if(gametype == MAPINFO_TYPE_CTS)
- rr = CTS_RECORD;
- else
- rr = RACE_RECORD;
- t = stof(db_get(ClientProgsDB, strcat(shortmapname, rr, "time")));
-
- if(score && (score < t || !t)) {
- db_put(ClientProgsDB, strcat(shortmapname, rr, "time"), ftos(score));
- if(autocvar_cl_autodemo_delete_keeprecords)
- {
- f = autocvar_cl_autodemo_delete;
- f &= ~1;
- cvar_set("cl_autodemo_delete", ftos(f)); // don't delete demo with new record!
- }
- }
-
- if(t != crecordtime_prev) {
- crecordtime_prev = t;
- crecordtime_change_time = time;
- }
-
- vector textPos, medalPos;
- float squareSize;
- if(mySize.x > mySize.y) {
- // text on left side
- squareSize = min(mySize.y, mySize.x/2);
- textPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eY * 0.5 * (mySize.y - squareSize);
- medalPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eX * 0.5 * mySize.x + eY * 0.5 * (mySize.y - squareSize);
- } else {
- // text on top
- squareSize = min(mySize.x, mySize.y/2);
- textPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eX * 0.5 * (mySize.x - squareSize);
- medalPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eY * 0.5 * mySize.y + eX * 0.5 * (mySize.x - squareSize);
- }
-
- f = time - crecordtime_change_time;
-
- if (f > 1) {
- drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- } else {
- drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect_expanding(pos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
- drawstring_aspect_expanding(pos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
- }
-
- // server record
- t = race_server_record;
- if(t != srecordtime_prev) {
- srecordtime_prev = t;
- srecordtime_change_time = time;
- }
- f = time - srecordtime_change_time;
-
- if (f > 1) {
- drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- } else {
- drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect_expanding(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
- drawstring_aspect_expanding(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
- }
-
- if (race_status != race_status_prev || race_status_name != race_status_name_prev) {
- race_status_time = time + 5;
- race_status_prev = race_status;
- if (race_status_name_prev)
- strunzone(race_status_name_prev);
- race_status_name_prev = strzone(race_status_name);
- }
-
- // race "awards"
- float a;
- a = bound(0, race_status_time - time, 1);
-
- string s;
- s = textShortenToWidth(race_status_name, squareSize, '1 1 0' * 0.1 * squareSize, stringwidth_colors);
-
- float rank;
- if(race_status > 0)
- rank = race_CheckName(race_status_name);
- else
- rank = 0;
- string rankname;
- rankname = count_ordinal(rank);
-
- vector namepos;
- namepos = medalPos + '0 0.8 0' * squareSize;
- vector rankpos;
- rankpos = medalPos + '0 0.15 0' * squareSize;
-
- if(race_status == 0)
- drawpic_aspect_skin(medalPos, "race_newfail", '1 1 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- else if(race_status == 1) {
- drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newtime", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- } else if(race_status == 2) {
- if(race_status_name == GetPlayerName(player_localnum) || !race_myrank || race_myrank < rank)
- drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankgreen", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- else
- drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankyellow", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- } else if(race_status == 3) {
- drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrecordserver", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- }
-
- if (race_status_time - time <= 0) {
- race_status_prev = -1;
- race_status = -1;
- if(race_status_name)
- strunzone(race_status_name);
- race_status_name = string_null;
- if(race_status_name_prev)
- strunzone(race_status_name_prev);
- race_status_name_prev = string_null;
- }
-}
-
-void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i)
-{
- float stat = -1;
- string pic = "";
- vector color = '0 0 0';
- switch(i)
- {
- case 0:
- stat = getstatf(STAT_DOM_PPS_RED);
- pic = "dom_icon_red";
- color = '1 0 0';
- break;
- case 1:
- stat = getstatf(STAT_DOM_PPS_BLUE);
- pic = "dom_icon_blue";
- color = '0 0 1';
- break;
- case 2:
- stat = getstatf(STAT_DOM_PPS_YELLOW);
- pic = "dom_icon_yellow";
- color = '1 1 0';
- break;
- default:
- case 3:
- stat = getstatf(STAT_DOM_PPS_PINK);
- pic = "dom_icon_pink";
- color = '1 0 1';
- break;
- }
- float pps_ratio = stat / getstatf(STAT_DOM_TOTAL_PPS);
-
- if(mySize.x/mySize.y > aspect_ratio)
- {
- i = aspect_ratio * mySize.y;
- myPos.x = myPos.x + (mySize.x - i) / 2;
- mySize.x = i;
- }
- else
- {
- i = 1/aspect_ratio * mySize.x;
- myPos.y = myPos.y + (mySize.y - i) / 2;
- mySize.y = i;
- }
-
- if (layout) // show text too
- {
- //draw the text
- color *= 0.5 + pps_ratio * (1 - 0.5); // half saturated color at min, full saturated at max
- if (layout == 2) // average pps
- drawstring_aspect(myPos + eX * mySize.y, ftos_decimals(stat, 2), eX * (2/3) * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
- else // percentage of average pps
- drawstring_aspect(myPos + eX * mySize.y, strcat( ftos(floor(pps_ratio*100 + 0.5)), "%" ), eX * (2/3) * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
- }
-
- //draw the icon
- drawpic_aspect_skin(myPos, pic, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- if (stat > 0)
- {
- drawsetcliparea(myPos.x, myPos.y + mySize.y * (1 - pps_ratio), mySize.y, mySize.y * pps_ratio);
- drawpic_aspect_skin(myPos, strcat(pic, "-highlighted"), '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawresetcliparea();
- }
-}
-
-void HUD_Mod_Dom(vector myPos, vector mySize)
-{
- mod_active = 1; // required in each mod function that always shows something
-
- int layout = autocvar_hud_panel_modicons_dom_layout;
- int rows, columns;
- float aspect_ratio;
- aspect_ratio = (layout) ? 3 : 1;
- rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
- columns = ceil(team_count/rows);
-
- int i;
- float row = 0, column = 0;
- vector pos, itemSize;
- itemSize = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
- for(i=0; i<team_count; ++i)
- {
- pos = myPos + eX * column * itemSize.x + eY * row * itemSize.y;
-
- DrawDomItem(pos, itemSize, aspect_ratio, layout, i);
-
- ++row;
- if(row >= rows)
- {
- row = 0;
- ++column;
- }
- }
-}
-
-void HUD_ModIcons_SetFunc()
-{
- switch(gametype)
- {
- case MAPINFO_TYPE_KEYHUNT: HUD_ModIcons_GameType = HUD_Mod_KH; break;
- case MAPINFO_TYPE_CTF: HUD_ModIcons_GameType = HUD_Mod_CTF; break;
- case MAPINFO_TYPE_NEXBALL: HUD_ModIcons_GameType = HUD_Mod_NexBall; break;
- case MAPINFO_TYPE_CTS:
- case MAPINFO_TYPE_RACE: HUD_ModIcons_GameType = HUD_Mod_Race; break;
- case MAPINFO_TYPE_CA:
- case MAPINFO_TYPE_FREEZETAG: HUD_ModIcons_GameType = HUD_Mod_CA; break;
- case MAPINFO_TYPE_DOMINATION: HUD_ModIcons_GameType = HUD_Mod_Dom; break;
- case MAPINFO_TYPE_KEEPAWAY: HUD_ModIcons_GameType = HUD_Mod_Keepaway; break;
- }
-}
-
-int mod_prev; // previous state of mod_active to check for a change
-float mod_alpha;
-float mod_change; // "time" when mod_active changed
-
-void HUD_ModIcons(void)
-{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_modicons) return;
- if(!HUD_ModIcons_GameType) return;
- }
-
- HUD_Panel_UpdateCvars();
-
- draw_beginBoldFont();
-
- if(mod_active != mod_prev) {
- mod_change = time;
- mod_prev = mod_active;
- }
-
- if(mod_active || autocvar__hud_configure)
- mod_alpha = bound(0, (time - mod_change) * 2, 1);
- else
- mod_alpha = bound(0, 1 - (time - mod_change) * 2, 1);
-
- if(mod_alpha)
- HUD_Panel_DrawBg(mod_alpha);
-
- if(panel_bg_padding)
- {
- panel_pos += '1 1 0' * panel_bg_padding;
- panel_size -= '2 2 0' * panel_bg_padding;
- }
-
- if(autocvar__hud_configure)
- HUD_Mod_CTF(panel_pos, panel_size);
- else
- HUD_ModIcons_GameType(panel_pos, panel_size);
-
- draw_endBoldFont();
-}
-
-// Draw pressed keys (#11)
-//
-void HUD_PressedKeys(void)
-{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_pressedkeys) return;
- if(spectatee_status <= 0 && autocvar_hud_panel_pressedkeys < 2) return;
- }
-
- HUD_Panel_UpdateCvars();
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- // force custom aspect
- float aspect = autocvar_hud_panel_pressedkeys_aspect;
- if(aspect)
- {
- vector newSize = '0 0 0';
- if(mySize.x/mySize.y > aspect)
- {
- newSize.x = aspect * mySize.y;
- newSize.y = mySize.y;
-
- pos.x = pos.x + (mySize.x - newSize.x) / 2;
- }
- else
- {
- newSize.y = 1/aspect * mySize.x;
- newSize.x = mySize.x;
-
- pos.y = pos.y + (mySize.y - newSize.y) / 2;
- }
- mySize = newSize;
- }
-
- vector keysize;
- keysize = eX * mySize.x * (1/3.0) + eY * mySize.y * (1/(3.0 - !autocvar_hud_panel_pressedkeys_attack));
- float pressedkeys;
- pressedkeys = getstatf(STAT_PRESSED_KEYS);
-
- if(autocvar_hud_panel_pressedkeys_attack)
- {
- drawpic_aspect_skin(pos + eX * keysize.x * 0.5, ((pressedkeys & KEY_ATCK) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawpic_aspect_skin(pos + eX * keysize.x * 1.5, ((pressedkeys & KEY_ATCK2) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- pos.y += keysize.y;
- }
-
- drawpic_aspect_skin(pos, ((pressedkeys & KEY_CROUCH) ? "key_crouch_inv.tga" : "key_crouch.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_FORWARD) ? "key_forward_inv.tga" : "key_forward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_JUMP) ? "key_jump_inv.tga" : "key_jump.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- pos.y += keysize.y;
- drawpic_aspect_skin(pos, ((pressedkeys & KEY_LEFT) ? "key_left_inv.tga" : "key_left.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_BACKWARD) ? "key_backward_inv.tga" : "key_backward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_RIGHT) ? "key_right_inv.tga" : "key_right.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
-}
-
-// Handle chat as a panel (#12)
-//
-void HUD_Chat(void)
-{
- if(!autocvar__hud_configure)
- {
- if (!autocvar_hud_panel_chat)
- {
- if (!autocvar_con_chatrect)
- cvar_set("con_chatrect", "0");
- return;
- }
- if(autocvar__con_chat_maximized)
- {
- if(!hud_draw_maximized) return;
- }
- else if(chat_panel_modified)
- {
- panel.update_time = time; // forces reload of panel attributes
- chat_panel_modified = false;
- }
- }
-
- HUD_Panel_UpdateCvars();
-
- if(intermission == 2)
- {
- // reserve some more space to the mapvote panel
- // by resizing and moving chat panel to the bottom
- panel_size.y = min(panel_size.y, vid_conheight * 0.2);
- panel_pos.y = vid_conheight - panel_size.y - panel_bg_border * 2;
- chat_posy = panel_pos.y;
- chat_sizey = panel_size.y;
- }
- if(autocvar__con_chat_maximized && !autocvar__hud_configure) // draw at full screen height if maximized
- {
- panel_pos.y = panel_bg_border;
- panel_size.y = vid_conheight - panel_bg_border * 2;
- if(panel.current_panel_bg == "0") // force a border when maximized
- {
- string panel_bg;
- panel_bg = strcat(hud_skin_path, "/border_default");
- if(precache_pic(panel_bg) == "")
- panel_bg = "gfx/hud/default/border_default";
- if(panel.current_panel_bg)
- strunzone(panel.current_panel_bg);
- panel.current_panel_bg = strzone(panel_bg);
- chat_panel_modified = true;
- }
- panel_bg_alpha = max(0.75, panel_bg_alpha); // force an theAlpha of at least 0.75
- }
-
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
-
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- if (!autocvar_con_chatrect)
- cvar_set("con_chatrect", "1");
-
- cvar_set("con_chatrect_x", ftos(pos.x/vid_conwidth));
- cvar_set("con_chatrect_y", ftos(pos.y/vid_conheight));
-
- cvar_set("con_chatwidth", ftos(mySize.x/vid_conwidth));
- cvar_set("con_chat", ftos(floor(mySize.y/autocvar_con_chatsize - 0.5)));
-
- if(autocvar__hud_configure)
- {
- vector chatsize;
- chatsize = '1 1 0' * autocvar_con_chatsize;
- cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over theAlpha and such
- float i, a;
- for(i = 0; i < autocvar_con_chat; ++i)
- {
- if(i == autocvar_con_chat - 1)
- a = panel_fg_alpha;
- else
- a = panel_fg_alpha * floor(((i + 1) * 7 + autocvar_con_chattime)/45);
- drawcolorcodedstring(pos, textShortenToWidth(_("^3Player^7: This is the chat area."), mySize.x, chatsize, stringwidth_colors), chatsize, a, DRAWFLAG_NORMAL);
- pos.y += chatsize.y;
- }
- }
-}
-
-// Engine info panel (#13)
-//
-float prevfps;
-float prevfps_time;
-int framecounter;
-
-float frametimeavg;
-float frametimeavg1; // 1 frame ago
-float frametimeavg2; // 2 frames ago
-void HUD_EngineInfo(void)
-{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_engineinfo) return;
- }
-
- HUD_Panel_UpdateCvars();
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- float currentTime = gettime(GETTIME_REALTIME);
- if(cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage"))
- {
- float currentframetime = currentTime - prevfps_time;
- frametimeavg = (frametimeavg + frametimeavg1 + frametimeavg2 + currentframetime)/4; // average three frametimes into framecounter for slightly more stable fps readings :P
- frametimeavg2 = frametimeavg1;
- frametimeavg1 = frametimeavg;
-
- float weight;
- weight = cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_new_weight");
- if(currentframetime > 0.0001) // filter out insane values which sometimes seem to occur and throw off the average? If you are getting 10,000 fps or more, then you don't need a framerate counter.
- {
- if(fabs(prevfps - (1/frametimeavg)) > prevfps * cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_instantupdate_change_threshold")) // if there was a big jump in fps, just force prevfps at current (1/currentframetime) to make big updates instant
- prevfps = (1/currentframetime);
- prevfps = (1 - weight) * prevfps + weight * (1/frametimeavg); // framecounter just used so there's no need for a new variable, think of it as "frametime average"
- }
- prevfps_time = currentTime;
- }
- else
- {
- framecounter += 1;
- if(currentTime - prevfps_time > autocvar_hud_panel_engineinfo_framecounter_time)
- {
- prevfps = framecounter/(currentTime - prevfps_time);
- framecounter = 0;
- prevfps_time = currentTime;
- }
- }
-
- vector color;
- color = HUD_Get_Num_Color (prevfps, 100);
- drawstring_aspect(pos, sprintf(_("FPS: %.*f"), autocvar_hud_panel_engineinfo_framecounter_decimals, prevfps), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
-}
-
-// Info messages panel (#14)
-//
-#define drawInfoMessage(s) do { \
- if(autocvar_hud_panel_infomessages_flip) \
- o.x = pos.x + mySize.x - stringwidth(s, true, fontsize); \
- drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL); \
- o.y += fontsize.y; \
-} while(0)
-void HUD_InfoMessages(void)
-{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_infomessages) return;
- }
-
- HUD_Panel_UpdateCvars();
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
- }
-
- // always force 5:1 aspect
- vector newSize = '0 0 0';
- if(mySize.x/mySize.y > 5)
- {
- newSize.x = 5 * mySize.y;
- newSize.y = mySize.y;
-
- pos.x = pos.x + (mySize.x - newSize.x) / 2;
- }
- else
- {
- newSize.y = 1/5 * mySize.x;
- newSize.x = mySize.x;
-
- pos.y = pos.y + (mySize.y - newSize.y) / 2;
- }
-
- mySize = newSize;
- entity tm;
- vector o;
- o = pos;
-
- vector fontsize;
- fontsize = '0.20 0.20 0' * mySize.y;
-
- float a;
- a = panel_fg_alpha;
-
- string s;
- if(!autocvar__hud_configure)
- {
- if(spectatee_status && !intermission)
- {
- a = 1;
- if(spectatee_status == -1)
- s = _("^1Observing");
- else
- s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(current_player));
- drawInfoMessage(s);
-
- if(spectatee_status == -1)
- s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire"));
- else
- s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
- drawInfoMessage(s);
-
- if(spectatee_status == -1)
- s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
- else
- s = sprintf(_("^1Press ^3%s^1 to observe"), getcommandkey("secondary fire", "+fire2"));
- drawInfoMessage(s);
-
- s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info"));
- drawInfoMessage(s);
-
- if(gametype == MAPINFO_TYPE_LMS)
- {
- entity sk;
- sk = playerslots[player_localnum];
- if(sk.(scores[ps_primary]) >= 666)
- s = _("^1Match has already begun");
- else if(sk.(scores[ps_primary]) > 0)
- s = _("^1You have no more lives left");
- else
- s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
- }
- else
- s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
- drawInfoMessage(s);
-
- //show restart countdown:
- if (time < getstatf(STAT_GAMESTARTTIME)) {
- float countdown;
- //we need to ceil, otherwise the countdown would be off by .5 when using round()
- countdown = ceil(getstatf(STAT_GAMESTARTTIME) - time);
- s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown);
- drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL);
- o.y += fontsize.y;
- }
- }
- if(warmup_stage && !intermission)
- {
- s = _("^2Currently in ^1warmup^2 stage!");
- drawInfoMessage(s);
- }
-
- string blinkcolor;
- if(time % 1 >= 0.5)
- blinkcolor = "^1";
- else
- blinkcolor = "^3";
-
- if(ready_waiting && !intermission && !spectatee_status)
- {
- if(ready_waiting_for_me)
- {
- if(warmup_stage)
- s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
- else
- s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
- }
- else
- {
- if(warmup_stage)
- s = _("^2Waiting for others to ready up to end warmup...");
- else
- s = _("^2Waiting for others to ready up...");
- }
- drawInfoMessage(s);
- }
- else if(warmup_stage && !intermission && !spectatee_status)
- {
- s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready"));
- drawInfoMessage(s);
- }
-
- if(teamplay && !intermission && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger)
- {
- float ts_min = 0, ts_max = 0;
- tm = teams.sort_next;
- if (tm)
- {
- for (; tm.sort_next; tm = tm.sort_next)
- {
- if(!tm.team_size || tm.team == NUM_SPECTATOR)
- continue;
- if(!ts_min) ts_min = tm.team_size;
- else ts_min = min(ts_min, tm.team_size);
- if(!ts_max) ts_max = tm.team_size;
- else ts_max = max(ts_max, tm.team_size);
- }
- if ((ts_max - ts_min) > 1)
- {
- s = strcat(blinkcolor, _("Teamnumbers are unbalanced!"));
- tm = GetTeam(myteam, false);
- if (tm)
- if (tm.team != NUM_SPECTATOR)
- if (tm.team_size == ts_max)
- s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor));
- drawInfoMessage(s);
- }
- }
- }
- }
- else
- {
- s = _("^7Press ^3ESC ^7to show HUD options.");
- drawInfoMessage(s);
- s = _("^3Doubleclick ^7a panel for panel-specific options.");
- drawInfoMessage(s);
- s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and");
- drawInfoMessage(s);
- s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments.");
- drawInfoMessage(s);
- }
-}
-
-// Physics panel (#15)
-//
-vector acc_prevspeed;
-float acc_prevtime, acc_avg, top_speed, top_speed_time;
-float physics_update_time, discrete_speed, discrete_acceleration;
-void HUD_Physics(void)
-{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_physics) return;
- if(spectatee_status == -1 && (autocvar_hud_panel_physics == 1 || autocvar_hud_panel_physics == 3)) return;
- if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
- }
-
- HUD_Panel_UpdateCvars();
-
- draw_beginBoldFont();
-
- HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
- {
- panel_pos += '1 1 0' * panel_bg_padding;
- panel_size -= '2 2 0' * panel_bg_padding;
- }
-
- float acceleration_progressbar_scale = 0;
- if(autocvar_hud_panel_physics_progressbar && autocvar_hud_panel_physics_acceleration_progressbar_scale > 1)
- acceleration_progressbar_scale = autocvar_hud_panel_physics_acceleration_progressbar_scale;
-
- float text_scale;
- if (autocvar_hud_panel_physics_text_scale <= 0)
- text_scale = 1;
- else
- text_scale = min(autocvar_hud_panel_physics_text_scale, 1);
-
- //compute speed
- float speed, conversion_factor;
- string unit;
-
- switch(autocvar_hud_panel_physics_speed_unit)
- {
- default:
- case 1:
- unit = _(" qu/s");
- conversion_factor = 1.0;
- break;
- case 2:
- unit = _(" m/s");
- conversion_factor = 0.0254;
- break;
- case 3:
- unit = _(" km/h");
- conversion_factor = 0.0254 * 3.6;
- break;
- case 4:
- unit = _(" mph");
- conversion_factor = 0.0254 * 3.6 * 0.6213711922;
- break;
- case 5:
- unit = _(" knots");
- conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
- break;
- }
-
- vector vel = (csqcplayer ? csqcplayer.velocity : pmove_vel);
-
- float max_speed = floor( autocvar_hud_panel_physics_speed_max * conversion_factor + 0.5 );
- if (autocvar__hud_configure)
- speed = floor( max_speed * 0.65 + 0.5 );
- else if(autocvar_hud_panel_physics_speed_vertical)
- speed = floor( vlen(vel) * conversion_factor + 0.5 );
- else
- speed = floor( vlen(vel - vel.z * '0 0 1') * conversion_factor + 0.5 );
-
- //compute acceleration
- float acceleration, f;
- if (autocvar__hud_configure)
- acceleration = autocvar_hud_panel_physics_acceleration_max * 0.3;
- else
- {
- // 1 m/s = 0.0254 qu/s; 1 g = 9.80665 m/s^2
- f = time - acc_prevtime;
- if(autocvar_hud_panel_physics_acceleration_vertical)
- acceleration = (vlen(vel) - vlen(acc_prevspeed));
- else
- acceleration = (vlen(vel - '0 0 1' * vel.z) - vlen(acc_prevspeed - '0 0 1' * acc_prevspeed.z));
-
- acceleration = acceleration * (1 / max(0.0001, f)) * (0.0254 / 9.80665);
-
- acc_prevspeed = vel;
- acc_prevtime = time;
-
- if(autocvar_hud_panel_physics_acceleration_movingaverage)
- {
- f = bound(0, f * 10, 1);
- acc_avg = acc_avg * (1 - f) + acceleration * f;
- acceleration = acc_avg;
- }
- }
-
- int acc_decimals = 2;
- if(time > physics_update_time)
- {
- // workaround for ftos_decimals returning a negative 0
- if(discrete_acceleration > -1 / pow(10, acc_decimals) && discrete_acceleration < 0)
- discrete_acceleration = 0;
- discrete_acceleration = acceleration;
- discrete_speed = speed;
- physics_update_time += autocvar_hud_panel_physics_update_interval;
- }
-
- //compute layout
- float panel_ar = panel_size.x/panel_size.y;
- vector speed_offset = '0 0 0', acceleration_offset = '0 0 0';
- if (panel_ar >= 5 && !acceleration_progressbar_scale)
- {
- panel_size.x *= 0.5;
- if (autocvar_hud_panel_physics_flip)
- speed_offset.x = panel_size.x;
- else
- acceleration_offset.x = panel_size.x;
- }
- else
- {
- panel_size.y *= 0.5;
- if (autocvar_hud_panel_physics_flip)
- speed_offset.y = panel_size.y;
- else
- acceleration_offset.y = panel_size.y;
- }
- int speed_baralign, acceleration_baralign;
- if (autocvar_hud_panel_physics_baralign == 1)
- acceleration_baralign = speed_baralign = 1;
- else if(autocvar_hud_panel_physics_baralign == 4)
- acceleration_baralign = speed_baralign = 2;
- else if (autocvar_hud_panel_physics_flip)
- {
- acceleration_baralign = (autocvar_hud_panel_physics_baralign == 2);
- speed_baralign = (autocvar_hud_panel_physics_baralign == 3);
- }
- else
- {
- speed_baralign = (autocvar_hud_panel_physics_baralign == 2);
- acceleration_baralign = (autocvar_hud_panel_physics_baralign == 3);
- }
- if (autocvar_hud_panel_physics_acceleration_progressbar_mode == 0)
- acceleration_baralign = 3; //override hud_panel_physics_baralign value for acceleration
-
- //draw speed
- if(speed)
- if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2)
- HUD_Panel_DrawProgressBar(panel_pos + speed_offset, panel_size, "progressbar", speed/max_speed, 0, speed_baralign, autocvar_hud_progressbar_speed_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- vector tmp_offset = '0 0 0', tmp_size = '0 0 0';
- if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2)
- {
- tmp_size.x = panel_size.x * 0.75;
- tmp_size.y = panel_size.y * text_scale;
- if (speed_baralign)
- tmp_offset.x = panel_size.x - tmp_size.x;
- //else
- //tmp_offset_x = 0;
- tmp_offset.y = (panel_size.y - tmp_size.y) / 2;
- drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(discrete_speed), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
-
- //draw speed unit
- if (speed_baralign)
- tmp_offset.x = 0;
- else
- tmp_offset.x = tmp_size.x;
- if (autocvar_hud_panel_physics_speed_unit_show)
- {
- //tmp_offset_y = 0;
- tmp_size.x = panel_size.x * (1 - 0.75);
- tmp_size.y = panel_size.y * 0.4 * text_scale;
- tmp_offset.y = (panel_size.y * 0.4 - tmp_size.y) / 2;
- drawstring_aspect(panel_pos + speed_offset + tmp_offset, unit, tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- }
-
- //compute and draw top speed
- if (autocvar_hud_panel_physics_topspeed)
- if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2)
- {
- if (autocvar__hud_configure)
- {
- top_speed = floor( max_speed * 0.75 + 0.5 );
- f = 1;
- }
- else
- {
- if (speed >= top_speed)
- {
- top_speed = speed;
- top_speed_time = time;
- }
- if (top_speed != 0)
- {
- f = max(1, autocvar_hud_panel_physics_topspeed_time);
- // divide by f to make it start from 1
- f = cos( ((time - top_speed_time) / f) * PI/2 );
- }
- else //hide top speed 0, it would be stupid
- f = 0;
- }
- if (f > 0)
- {
- //top speed progressbar peak
- if(speed < top_speed)
- if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2)
- {
- float peak_offsetX;
- vector peak_size = '0 0 0';
- if (speed_baralign == 0)
- peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x;
- else if (speed_baralign == 1)
- peak_offsetX = (1 - min(top_speed, max_speed)/max_speed) * panel_size.x;
- else // if (speed_baralign == 2)
- peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x * 0.5;
- peak_size.x = floor(panel_size.x * 0.01 + 1.5);
- peak_size.y = panel_size.y;
- if (speed_baralign == 2) // draw two peaks, on both sides
- {
- drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x + peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x - peak_offsetX + peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- else
- drawfill(panel_pos + speed_offset + eX * (peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- }
-
- //top speed
- tmp_offset.y = panel_size.y * 0.4;
- tmp_size.x = panel_size.x * (1 - 0.75);
- tmp_size.y = (panel_size.y - tmp_offset.y) * text_scale;
- tmp_offset.y += (panel_size.y - tmp_offset.y - tmp_size.y) / 2;
- drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(top_speed), tmp_size, '1 0 0', f * panel_fg_alpha, DRAWFLAG_NORMAL);
- }
- else
- top_speed = 0;
- }
-
- //draw acceleration
- if(acceleration)
- if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 3)
- {
- vector progressbar_color;
- if(acceleration < 0)
- progressbar_color = autocvar_hud_progressbar_acceleration_neg_color;
- else
- progressbar_color = autocvar_hud_progressbar_acceleration_color;
-
- f = acceleration/autocvar_hud_panel_physics_acceleration_max;
- if (autocvar_hud_panel_physics_acceleration_progressbar_nonlinear)
- f = (f >= 0 ? sqrt(f) : -sqrt(-f));
-
- if (acceleration_progressbar_scale) // allow progressbar to go out of panel bounds
- {
- tmp_size = acceleration_progressbar_scale * panel_size.x * eX + panel_size.y * eY;
-
- if (acceleration_baralign == 1)
- tmp_offset.x = panel_size.x - tmp_size.x;
- else if (acceleration_baralign == 2 || acceleration_baralign == 3)
- tmp_offset.x = (panel_size.x - tmp_size.x) / 2;
- else
- tmp_offset.x = 0;
- tmp_offset.y = 0;
- }
- else
- {
- tmp_size = panel_size;
- tmp_offset = '0 0 0';
- }
-
- HUD_Panel_DrawProgressBar(panel_pos + acceleration_offset + tmp_offset, tmp_size, "accelbar", f, 0, acceleration_baralign, progressbar_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
- }
-
- if(autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 3)
- {
- tmp_size.x = panel_size.x;
- tmp_size.y = panel_size.y * text_scale;
- tmp_offset.x = 0;
- tmp_offset.y = (panel_size.y - tmp_size.y) / 2;
-
- drawstring_aspect(panel_pos + acceleration_offset + tmp_offset, strcat(ftos_decimals(discrete_acceleration, acc_decimals), "g"), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- }
-
- draw_endBoldFont();
-}
-
-// CenterPrint (#16)
-//
-const int CENTERPRINT_MAX_MSGS = 10;
-const int CENTERPRINT_MAX_ENTRIES = 50;
-const float CENTERPRINT_SPACING = 0.7;
-int cpm_index;
-string centerprint_messages[CENTERPRINT_MAX_MSGS];
-int centerprint_msgID[CENTERPRINT_MAX_MSGS];
-float centerprint_time[CENTERPRINT_MAX_MSGS];
-float centerprint_expire_time[CENTERPRINT_MAX_MSGS];
-int centerprint_countdown_num[CENTERPRINT_MAX_MSGS];
-bool centerprint_showing;
-
-void centerprint_generic(int new_id, string strMessage, float duration, int countdown_num)
-{
- //printf("centerprint_generic(%d, '%s^7', %d, %d);\n", new_id, strMessage, duration, countdown_num);
- int i, j;
-
- if(strMessage == "" && new_id == 0)
- return;
-
- // strip trailing newlines
- j = strlen(strMessage) - 1;
- while(substring(strMessage, j, 1) == "\n" && j >= 0)
- --j;
- if (j < strlen(strMessage) - 1)
- strMessage = substring(strMessage, 0, j + 1);
-
- if(strMessage == "" && new_id == 0)
- return;
-
- // strip leading newlines
- j = 0;
- while(substring(strMessage, j, 1) == "\n" && j < strlen(strMessage))
- ++j;
- if (j > 0)
- strMessage = substring(strMessage, j, strlen(strMessage) - j);
-
- if(strMessage == "" && new_id == 0)
- return;
-
- if (!centerprint_showing)
- centerprint_showing = true;
-
- for (i=0, j=cpm_index; i<CENTERPRINT_MAX_MSGS; ++i, ++j)
- {
- if (j == CENTERPRINT_MAX_MSGS)
- j = 0;
- if (new_id && new_id == centerprint_msgID[j])
- {
- if (strMessage == "" && centerprint_messages[j] != "" && centerprint_countdown_num[j] == 0)
- {
- // fade out the current msg (duration and countdown_num are ignored)
- centerprint_time[j] = min(5, autocvar_hud_panel_centerprint_fade_out);
- if (centerprint_expire_time[j] > time + min(5, autocvar_hud_panel_centerprint_fade_out) || centerprint_expire_time[j] < time)
- centerprint_expire_time[j] = time + min(5, autocvar_hud_panel_centerprint_fade_out);
- return;
- }
- break; // found a msg with the same id, at position j
- }
- }
-
- if (i == CENTERPRINT_MAX_MSGS)
- {
- // a msg with the same id was not found, add the msg at the next position
- --cpm_index;
- if (cpm_index == -1)
- cpm_index = CENTERPRINT_MAX_MSGS - 1;
- j = cpm_index;
- }
- if(centerprint_messages[j])
- strunzone(centerprint_messages[j]);
- centerprint_messages[j] = strzone(strMessage);
- centerprint_msgID[j] = new_id;
- if (duration < 0)
- {
- centerprint_time[j] = -1;
- centerprint_expire_time[j] = time;
- }
- else
- {
- if(duration == 0)
- duration = max(1, autocvar_hud_panel_centerprint_time);
- centerprint_time[j] = duration;
- centerprint_expire_time[j] = time + duration;
- }
- centerprint_countdown_num[j] = countdown_num;
-}
-
-void centerprint_hud(string strMessage)
-{
- centerprint_generic(0, strMessage, autocvar_hud_panel_centerprint_time, 0);
-}
-
-void reset_centerprint_messages(void)
-{
- int i;
- for (i=0; i<CENTERPRINT_MAX_MSGS; ++i)
- {
- centerprint_expire_time[i] = 0;
- centerprint_time[i] = 1;
- centerprint_msgID[i] = 0;
- if(centerprint_messages[i])
- strunzone(centerprint_messages[i]);
- centerprint_messages[i] = string_null;
- }
-}
-float hud_configure_cp_generation_time;
-void HUD_CenterPrint (void)
-{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_centerprint) return;
-
- if(hud_configure_prev)
- reset_centerprint_messages();
- }
- else
- {
- if(!hud_configure_prev)
- reset_centerprint_messages();
- if (time > hud_configure_cp_generation_time)
- {
- if(highlightedPanel == HUD_PANEL(CENTERPRINT))
- {
- float r;
- r = random();
- if (r > 0.8)
- centerprint_generic(floor(r*1000), strcat(sprintf("^3Countdown message at time %s", seconds_tostring(time)), ", seconds left: ^COUNT"), 1, 10);
- else if (r > 0.55)
- centerprint_generic(0, sprintf("^1Multiline message at time %s that\n^1lasts longer than normal", seconds_tostring(time)), 20, 0);
- else
- centerprint_hud(sprintf("Message at time %s", seconds_tostring(time)));
- hud_configure_cp_generation_time = time + 1 + random()*4;
- }
- else
- {
- centerprint_generic(0, sprintf("Centerprint message", seconds_tostring(time)), 10, 0);
- hud_configure_cp_generation_time = time + 10 - random()*3;
- }
- }
- }
-
- // this panel fades only when the menu does
- float hud_fade_alpha_save = 0;
- if(scoreboard_fade_alpha)
- {
- hud_fade_alpha_save = hud_fade_alpha;
- hud_fade_alpha = 1 - autocvar__menu_alpha;
- }
- HUD_Panel_UpdateCvars();
-
- if ( HUD_Radar_Clickable() )
- {
- if (hud_panel_radar_bottom >= 0.96 * vid_conheight)
- return;
-
- panel_pos = eY * hud_panel_radar_bottom + eX * 0.5 * (vid_conwidth - panel_size_x);
- panel_size_y = min(panel_size_y, vid_conheight - hud_panel_radar_bottom);
- }
- else if(scoreboard_fade_alpha)
- {
- hud_fade_alpha = hud_fade_alpha_save;
-
- // move the panel below the scoreboard
- if (scoreboard_bottom >= 0.96 * vid_conheight)
- return;
- vector target_pos;
-
- target_pos = eY * scoreboard_bottom + eX * 0.5 * (vid_conwidth - panel_size.x);
-
- if(target_pos.y > panel_pos.y)
- {
- panel_pos = panel_pos + (target_pos - panel_pos) * sqrt(scoreboard_fade_alpha);
- panel_size.y = min(panel_size.y, vid_conheight - scoreboard_bottom);
- }
- }
-
- HUD_Panel_DrawBg(1);
-
- if (!centerprint_showing)
- return;
-
- if(panel_bg_padding)
- {
- panel_pos += '1 1 0' * panel_bg_padding;
- panel_size -= '2 2 0' * panel_bg_padding;
- }
-
- int entries;
- float height;
- vector fontsize;
- // entries = bound(1, floor(CENTERPRINT_MAX_ENTRIES * 4 * panel_size_y/panel_size_x), CENTERPRINT_MAX_ENTRIES);
- // height = panel_size_y/entries;
- // fontsize = '1 1 0' * height;
- height = vid_conheight/50 * autocvar_hud_panel_centerprint_fontscale;
- fontsize = '1 1 0' * height;
- entries = bound(1, floor(panel_size.y/height), CENTERPRINT_MAX_ENTRIES);
-
- int i, j, k, n, g;
- float a, sz, align, current_msg_posY = 0, msg_size;
- vector pos;
- string ts;
- bool all_messages_expired = true;
-
- pos = panel_pos;
- if (autocvar_hud_panel_centerprint_flip)
- pos.y += panel_size.y;
- align = bound(0, autocvar_hud_panel_centerprint_align, 1);
- for (g=0, i=0, j=cpm_index; i<CENTERPRINT_MAX_MSGS; ++i, ++j)
- {
- if (j == CENTERPRINT_MAX_MSGS)
- j = 0;
- if (centerprint_expire_time[j] <= time)
- {
- if (centerprint_countdown_num[j] && centerprint_time[j] > 0)
- {
- centerprint_countdown_num[j] = centerprint_countdown_num[j] - 1;
- if (centerprint_countdown_num[j] == 0)
- continue;
- centerprint_expire_time[j] = centerprint_expire_time[j] + centerprint_time[j];
- }
- else if(centerprint_time[j] != -1)
- continue;
- }
-
- all_messages_expired = false;
-
- // fade the centerprint_hud in/out
- if(centerprint_time[j] < 0) // Expired but forced. Expire time is the fade-in time.
- a = (time - centerprint_expire_time[j]) / max(0.0001, autocvar_hud_panel_centerprint_fade_in);
- else if(centerprint_expire_time[j] - autocvar_hud_panel_centerprint_fade_out > time) // Regularily printed. Not fading out yet.
- a = (time - (centerprint_expire_time[j] - centerprint_time[j])) / max(0.0001, autocvar_hud_panel_centerprint_fade_in);
- else // Expiring soon, so fade it out.
- a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out);
-
- // while counting down show it anyway in order to hold the current message position
- if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0) // Guaranteed invisible - don't show.
- continue;
- if (a > 1)
- a = 1;
-
- // set the size from fading in/out before subsequent fading
- sz = autocvar_hud_panel_centerprint_fade_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_minfontsize);
-
- // also fade it based on positioning
- if(autocvar_hud_panel_centerprint_fade_subsequent)
- {
- a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1); // pass one: all messages after the first have half theAlpha
- a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1); // pass two: after that, gradually lower theAlpha even more for each message
- }
- a *= panel_fg_alpha;
-
- // finally set the size based on the new theAlpha from subsequent fading
- sz = sz * (autocvar_hud_panel_centerprint_fade_subsequent_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_subsequent_minfontsize));
- drawfontscale = sz * '1 1 0';
-
- if (centerprint_countdown_num[j])
- n = tokenizebyseparator(strreplace("^COUNT", count_seconds(centerprint_countdown_num[j]), centerprint_messages[j]), "\n");
- else
- n = tokenizebyseparator(centerprint_messages[j], "\n");
-
- if (autocvar_hud_panel_centerprint_flip)
- {
- // check if the message can be entirely shown
- for(k = 0; k < n; ++k)
- {
- getWrappedLine_remaining = argv(k);
- while(getWrappedLine_remaining)
- {
- ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors);
- if (ts != "")
- pos.y -= fontsize.y;
- else
- pos.y -= fontsize.y * CENTERPRINT_SPACING/2;
- }
- }
- current_msg_posY = pos.y; // save starting pos (first line) of the current message
- }
-
- msg_size = pos.y;
- for(k = 0; k < n; ++k)
- {
- getWrappedLine_remaining = argv(k);
- while(getWrappedLine_remaining)
- {
- ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors);
- if (ts != "")
- {
- if (align)
- pos.x = panel_pos.x + (panel_size.x - stringwidth(ts, true, fontsize)) * align;
- if (a > 0.5/255.0) // Otherwise guaranteed invisible - don't show. This is checked a second time after some multiplications with other factors were done so temporary changes of these cannot cause flicker.
- drawcolorcodedstring(pos + eY * 0.5 * (1 - sz) * fontsize.y, ts, fontsize, a, DRAWFLAG_NORMAL);
- pos.y += fontsize.y;
- }
- else
- pos.y += fontsize.y * CENTERPRINT_SPACING/2;
- }
- }
-
- ++g; // move next position number up
-
- msg_size = pos.y - msg_size;
- if (autocvar_hud_panel_centerprint_flip)
- {
- pos.y = current_msg_posY - CENTERPRINT_SPACING * fontsize.y;
- if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
- pos.y += (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz));
-
- if (pos.y < panel_pos.y) // check if the next message can be shown
- {
- drawfontscale = '1 1 0';
- return;
- }
- }
- else
- {
- pos.y += CENTERPRINT_SPACING * fontsize.y;
- if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
- pos.y -= (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz));
-
- if(pos.y > panel_pos.y + panel_size.y - fontsize.y) // check if the next message can be shown
- {
- drawfontscale = '1 1 0';
- return;
- }
- }
- }
- drawfontscale = '1 1 0';
- if (all_messages_expired)
- {
- centerprint_showing = false;
- reset_centerprint_messages();
- }
-}
-
-
-// Minigame
-//
-#include "../common/minigames/cl_minigames_hud.qc"
-
-
-// QuickMenu (#23)
-//
-#include "quickmenu.qc"
-
-
-/*
-==================
-Main HUD system
-==================
-*/
-
-void HUD_Vehicle()
-{
- if(autocvar__hud_configure) return;
- if(intermission == 2) return;
-
- if(hud == HUD_BUMBLEBEE_GUN)
- CSQC_BUMBLE_GUN_HUD();
- else {
- Vehicle info = get_vehicleinfo(hud);
- info.vr_hud(info);
- }
-}
-
-bool HUD_Panel_CheckFlags(int showflags)
-{
- if ( HUD_Minigame_Showpanels() )
- return showflags & PANEL_SHOW_MINIGAME;
- if(intermission == 2)
- return showflags & PANEL_SHOW_MAPVOTE;
- return showflags & PANEL_SHOW_MAINGAME;
-}
-
-void HUD_Panel_Draw(entity panent)
-{
- panel = panent;
- if(autocvar__hud_configure)
- {
- if(panel.panel_configflags & PANEL_CONFIG_MAIN)
- panel.panel_draw();
- }
- else if(HUD_Panel_CheckFlags(panel.panel_showflags))
- panel.panel_draw();
-}
-
-void HUD_Reset(void)
-{
- // reset gametype specific icons
- if(gametype == MAPINFO_TYPE_CTF)
- HUD_Mod_CTF_Reset();
-}
-
-void HUD_Main(void)
-{
- int i;
- // global hud theAlpha fade
- if(menu_enabled == 1)
- hud_fade_alpha = 1;
- else
- hud_fade_alpha = (1 - autocvar__menu_alpha);
-
- if(scoreboard_fade_alpha)
- hud_fade_alpha = (1 - scoreboard_fade_alpha);
-
- HUD_Configure_Frame();
-
- // panels that we want to be active together with the scoreboard
- // they must fade only when the menu does
- if(scoreboard_fade_alpha == 1)
- {
- HUD_Panel_Draw(HUD_PANEL(CENTERPRINT));
- return;
- }
-
- if(!autocvar__hud_configure && !hud_fade_alpha)
- {
- hud_fade_alpha = 1;
- HUD_Panel_Draw(HUD_PANEL(VOTE));
- hud_fade_alpha = 0;
- return;
- }
-
- // Drawing stuff
- if (hud_skin_prev != autocvar_hud_skin)
- {
- if (hud_skin_path)
- strunzone(hud_skin_path);
- hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
- if (hud_skin_prev)
- strunzone(hud_skin_prev);
- hud_skin_prev = strzone(autocvar_hud_skin);
- }
-
- // draw the dock
- if(autocvar_hud_dock != "" && autocvar_hud_dock != "0")
- {
- int f;
- vector color;
- float hud_dock_color_team = autocvar_hud_dock_color_team;
- if((teamplay) && hud_dock_color_team) {
- if(autocvar__hud_configure && myteam == NUM_SPECTATOR)
- color = '1 0 0' * hud_dock_color_team;
- else
- color = myteamcolors * hud_dock_color_team;
- }
- else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) {
- color = '1 0 0' * hud_dock_color_team;
- }
- else
- {
- string hud_dock_color = autocvar_hud_dock_color;
- if(hud_dock_color == "shirt") {
- f = stof(getplayerkeyvalue(current_player, "colors"));
- color = colormapPaletteColor(floor(f / 16), 0);
- }
- else if(hud_dock_color == "pants") {
- f = stof(getplayerkeyvalue(current_player, "colors"));
- color = colormapPaletteColor(f % 16, 1);
- }
- else
- color = stov(hud_dock_color);
- }
-
- string pic;
- pic = strcat(hud_skin_path, "/", autocvar_hud_dock);
- if(precache_pic(pic) == "") {
- pic = strcat(hud_skin_path, "/dock_medium");
- if(precache_pic(pic) == "") {
- pic = "gfx/hud/default/dock_medium";
- }
- }
- drawpic('0 0 0', pic, eX * vid_conwidth + eY * vid_conheight, color, autocvar_hud_dock_alpha * hud_fade_alpha, DRAWFLAG_NORMAL); // no aspect ratio forcing on dock...
- }
-
- // cache the panel order into the panel_order array
- if(autocvar__hud_panelorder != hud_panelorder_prev) {
- for(i = 0; i < hud_panels_COUNT; ++i)
- panel_order[i] = -1;
- string s = "";
- int p_num;
- bool warning = false;
- int argc = tokenize_console(autocvar__hud_panelorder);
- if (argc > hud_panels_COUNT)
- warning = true;
- //first detect wrong/missing panel numbers
- for(i = 0; i < hud_panels_COUNT; ++i) {
- p_num = stoi(argv(i));
- if (p_num >= 0 && p_num < hud_panels_COUNT) { //correct panel number?
- if (panel_order[p_num] == -1) //found for the first time?
- s = strcat(s, ftos(p_num), " ");
- panel_order[p_num] = 1; //mark as found
- }
- else
- warning = true;
- }
- for(i = 0; i < hud_panels_COUNT; ++i) {
- if (panel_order[i] == -1) {
- warning = true;
- s = strcat(s, ftos(i), " "); //add missing panel number
- }
- }
- if (warning)
- LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n");
-
- cvar_set("_hud_panelorder", s);
- if(hud_panelorder_prev)
- strunzone(hud_panelorder_prev);
- hud_panelorder_prev = strzone(s);
-
- //now properly set panel_order
- tokenize_console(s);
- for(i = 0; i < hud_panels_COUNT; ++i) {
- panel_order[i] = stof(argv(i));
- }
- }
-
- hud_draw_maximized = 0;
- // draw panels in the order specified by panel_order array
- for(i = hud_panels_COUNT - 1; i >= 0; --i)
- HUD_Panel_Draw(hud_panels[panel_order[i]]);
-
- HUD_Vehicle();
-
- hud_draw_maximized = 1; // panels that may be maximized must check this var
- // draw maximized panels on top
- if(hud_panel_radar_maximized)
- HUD_Panel_Draw(HUD_PANEL(RADAR));
- if(autocvar__con_chat_maximized)
- HUD_Panel_Draw(HUD_PANEL(CHAT));
- if(hud_panel_quickmenu)
- HUD_Panel_Draw(HUD_PANEL(QUICKMENU));
-
- if (scoreboard_active || intermission == 2)
- HUD_Reset();
-
- HUD_Configure_PostDraw();
-
- hud_configure_prev = autocvar__hud_configure;
-}
+++ /dev/null
-#ifndef CLIENT_HUD_H
-#define CLIENT_HUD_H
-
-#include "../common/weapons/all.qh"
-
-bool HUD_Radar_Clickable();
-void HUD_Radar_Mouse();
-
-REGISTRY(hud_panels, 24)
-REGISTER_REGISTRY(Registerhud_panels)
-
-#define REGISTER_HUD_PANEL(id, draw_func, name, configflags, showflags) \
- void draw_func(); \
- REGISTER(Registerhud_panels, HUD_PANEL, hud_panels, id, m_id, new(hud_panel)) { \
- this.panel_id = this.m_id; \
- this.panel_draw = draw_func; \
- this.panel_name = #name; \
- this.panel_configflags = configflags; \
- this.panel_showflags = showflags; \
- }
-
-#define HUD_PANEL(NAME) HUD_PANEL_##NAME
-
-// draw the background/borders
-#define HUD_Panel_DrawBg(theAlpha) do { \
- if(panel.current_panel_bg != "0" && panel.current_panel_bg != "") \
- draw_BorderPicture(panel_pos - '1 1 0' * panel_bg_border, panel.current_panel_bg, panel_size + '1 1 0' * 2 * panel_bg_border, panel_bg_color, panel_bg_alpha * theAlpha, '1 1 0' * (panel_bg_border/BORDER_MULTIPLIER));\
-} while(0)
-
-int panel_order[hud_panels_MAX];
-string hud_panelorder_prev;
-
-bool hud_draw_maximized;
-bool hud_panel_radar_maximized;
-bool hud_panel_radar_mouse;
-float hud_panel_radar_bottom;
-bool hud_panel_radar_temp_hidden;
-bool chat_panel_modified;
-bool radar_panel_modified;
-
-float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary);
-void HUD_Radar_Hide_Maximized();
-
-void HUD_Reset (void);
-void HUD_Main (void);
-
-int vote_yescount;
-int vote_nocount;
-int vote_needed;
-int vote_highlighted; // currently selected vote
-
-int vote_active; // is there an active vote?
-int vote_prev; // previous state of vote_active to check for a change
-float vote_alpha;
-float vote_change; // "time" when vote_active changed
-
-float hud_panel_quickmenu;
-
-vector mousepos;
-vector panel_click_distance; // mouse cursor distance from the top left corner of the panel (saved only upon a click)
-vector panel_click_resizeorigin; // coordinates for opposite point when resizing
-float resizeCorner; // 1 = topleft, 2 = topright, 3 = bottomleft, 4 = bottomright
-entity highlightedPanel;
-float highlightedAction; // 0 = nothing, 1 = move, 2 = resize
-
-const float BORDER_MULTIPLIER = 0.25;
-float scoreboard_bottom;
-int weapon_accuracy[Weapons_MAX];
-
-int complain_weapon;
-string complain_weapon_name;
-float complain_weapon_type;
-float complain_weapon_time;
-
-int ps_primary, ps_secondary;
-int ts_primary, ts_secondary;
-
-int last_switchweapon;
-int last_activeweapon;
-float weapontime;
-float weaponprevtime;
-
-float teamnagger;
-
-float hud_configure_checkcollisions;
-float hud_configure_prev;
-vector hud_configure_gridSize;
-vector hud_configure_realGridSize;
-
-int hudShiftState;
-const int S_SHIFT = 1;
-const int S_CTRL = 2;
-const int S_ALT = 4;
-
-float menu_enabled; // 1 showing the entire HUD, 2 showing only the clicked panel
-
-float hud_fade_alpha;
-
-string hud_skin_path;
-string hud_skin_prev;
-
-vector myteamcolors;
-
-entity highlightedPanel_backup;
-vector panel_pos_backup;
-vector panel_size_backup;
-
-vector panel_size_copied;
-
-entity panel;
-entityclass(HUDPanel);
-class(HUDPanel) .string panel_name;
-class(HUDPanel) .int panel_id;
-class(HUDPanel) .vector current_panel_pos;
-class(HUDPanel) .vector current_panel_size;
-class(HUDPanel) .string current_panel_bg;
-class(HUDPanel) .float current_panel_bg_alpha;
-class(HUDPanel) .float current_panel_bg_border;
-class(HUDPanel) .vector current_panel_bg_color;
-class(HUDPanel) .float current_panel_bg_color_team;
-class(HUDPanel) .float current_panel_bg_padding;
-class(HUDPanel) .float current_panel_fg_alpha;
-class(HUDPanel) .float update_time;
-float panel_enabled;
-vector panel_pos;
-vector panel_size;
-string panel_bg_str; // "_str" vars contain the raw value of the cvar, non-"_str" contains what hud.qc code should use
-vector panel_bg_color;
-string panel_bg_color_str;
-float panel_bg_color_team;
-string panel_bg_color_team_str;
-float panel_fg_alpha;
-float panel_bg_alpha;
-string panel_bg_alpha_str;
-float panel_bg_border;
-string panel_bg_border_str;
-float panel_bg_padding;
-string panel_bg_padding_str;
-
-class(HUDPanel) .void() panel_draw;
-
-// chat panel can be reduced / moved while the mapvote is active
-// let know the mapvote panel about chat pos and size
-float chat_posy;
-float chat_sizey;
-
-float current_player;
-
-float stringwidth_colors(string s, vector theSize);
-float stringwidth_nocolors(string s, vector theSize);
-float GetPlayerColorForce(int i);
-int GetPlayerColor(int i);
-string GetPlayerName(int i);
-void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag);
-
-.int panel_showflags;
-const int PANEL_SHOW_NEVER = 0x00;
-const int PANEL_SHOW_MAINGAME = 0x01;
-const int PANEL_SHOW_MINIGAME = 0x02;
-const int PANEL_SHOW_MAPVOTE = 0x04;
-const int PANEL_SHOW_ALWAYS = 0xff;
-bool HUD_Panel_CheckFlags(int showflags);
-
-.int panel_configflags;
-const int PANEL_CONFIG_NO = 0x00;
-const int PANEL_CONFIG_MAIN = 0x01;
-
-
-// prev_* vars contain the health/armor at the previous FRAME
-// set to -1 when player is dead or was not playing
-int prev_health, prev_armor;
-float health_damagetime, armor_damagetime;
-int health_beforedamage, armor_beforedamage;
-// old_p_* vars keep track of previous values when smoothing value changes of the progressbar
-int old_p_health, old_p_armor;
-float old_p_healthtime, old_p_armortime;
-// prev_p_* vars contain the health/armor progressbar value at the previous FRAME
-// set to -1 to forcedly stop effects when we switch spectated player (e.g. from playerX: 70h to playerY: 50h)
-int prev_p_health, prev_p_armor;
-
-void HUD_ItemsTime();
-
-REGISTER_HUD_PANEL(WEAPONS, HUD_Weapons, weapons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(AMMO, HUD_Ammo, ammo, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(POWERUPS, HUD_Powerups, powerups, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(HEALTHARMOR, HUD_HealthArmor, healtharmor, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(NOTIFY, HUD_Notify, notify, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE)
-REGISTER_HUD_PANEL(TIMER, HUD_Timer, timer, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE)
-REGISTER_HUD_PANEL(RADAR, HUD_Radar, radar, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(SCORE, HUD_Score, score, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE)
-REGISTER_HUD_PANEL(RACETIMER, HUD_RaceTimer, racetimer, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(VOTE, HUD_Vote, vote, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS )
-REGISTER_HUD_PANEL(MODICONS, HUD_ModIcons, modicons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(PRESSEDKEYS, HUD_PressedKeys, pressedkeys, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(CHAT, HUD_Chat, chat, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS )
-REGISTER_HUD_PANEL(ENGINEINFO, HUD_EngineInfo, engineinfo, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS )
-REGISTER_HUD_PANEL(INFOMESSAGES, HUD_InfoMessages, infomessages, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(PHYSICS, HUD_Physics, physics, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(CENTERPRINT, HUD_CenterPrint, centerprint, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(MINIGAME_BOARD, HUD_MinigameBoard, minigameboard, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME)
-REGISTER_HUD_PANEL(MINIGAME_STATUS, HUD_MinigameStatus, minigamestatus, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME)
-REGISTER_HUD_PANEL(MINIGAME_HELP, HUD_MinigameHelp, minigamehelp, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME)
-REGISTER_HUD_PANEL(MINIGAME_MENU, HUD_MinigameMenu, minigamemenu, PANEL_CONFIG_NO , PANEL_SHOW_ALWAYS )
-REGISTER_HUD_PANEL(MAPVOTE, MapVote_Draw, mapvote, PANEL_CONFIG_NO , PANEL_SHOW_MAPVOTE )
-REGISTER_HUD_PANEL(ITEMSTIME, HUD_ItemsTime, itemstime, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
-// always add new panels to the end of list
-
-// Because calling lots of functions in QC apparently cuts fps in half on many machines:
-// ----------------------
-// MACRO HELL STARTS HERE
-// ----------------------
-// Little help for the poor people who have to make sense of this: Start from the bottom ;)
-
-// Get value for panel.current_panel_bg: if "" fetch default, else use panel_bg_str
-// comment on last line of macro: // we probably want to see a background in config mode at all times...
-#define HUD_Panel_GetBg() do { \
- string panel_bg; \
- if (!autocvar__hud_configure && panel_bg_str == "0") { \
- panel_bg = "0"; \
- } else { \
- if (panel_bg_str == "") { \
- panel_bg_str = autocvar_hud_panel_bg; \
- } \
- if (panel_bg_str == "0" && !autocvar__hud_configure) { \
- panel_bg = "0"; \
- } else { \
- if (panel_bg_str == "0" && autocvar__hud_configure) \
- panel_bg_alpha_str = "0"; \
- panel_bg = strcat(hud_skin_path, "/", panel_bg_str); \
- if (precache_pic(panel_bg) == "") { \
- panel_bg = strcat(hud_skin_path, "/", "border_default"); \
- if (precache_pic(panel_bg) == "") { \
- panel_bg = strcat("gfx/hud/default/", "border_default"); \
- } \
- } \
- } \
- } \
- if (panel.current_panel_bg) \
- strunzone(panel.current_panel_bg); \
- panel.current_panel_bg = strzone(panel_bg); \
-} while(0)
-
-// Get value for panel_bg_color: if "" fetch default, else use panel_bg_color. Convert pants, shirt or teamcolor into a vector.
-#define HUD_Panel_GetColor() do { \
- if ((teamplay) && panel_bg_color_team) { \
- if (autocvar__hud_configure && myteam == NUM_SPECTATOR) \
- panel_bg_color = '1 0 0' * panel_bg_color_team; \
- else \
- panel_bg_color = myteamcolors * panel_bg_color_team; \
- } else if (autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && panel_bg_color_team) { \
- panel_bg_color = '1 0 0' * panel_bg_color_team; \
- } else { \
- if (panel_bg_color_str == "") { \
- panel_bg_color = autocvar_hud_panel_bg_color; \
- } else { \
- if (panel_bg_color_str == "shirt") { \
- panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player, "colors")) / 16), 0); \
- } else if (panel_bg_color_str == "pants") { \
- panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player, "colors")) % 16, 1); \
- } else { \
- panel_bg_color = stov(panel_bg_color_str); \
- } \
- } \
- } \
-} while(0)
-
-// Get value for panel_bg_color_team: if "" fetch default, else use panel_bg_color_team_str
-#define HUD_Panel_GetColorTeam() do { \
- if (panel_bg_color_team_str == "") { \
- panel_bg_color_team = autocvar_hud_panel_bg_color_team; \
- } else { \
- panel_bg_color_team = stof(panel_bg_color_team_str); \
- } \
-} while(0)
-
-// Get value for panel_bg_alpha: if "" fetch default, else use panel_bg_alpha. Also do various menu dialog fadeout/in checks, and minalpha checks
-// comment on line 3 of macro: // do not set a minalpha cap when showing the config dialog for this panel
-#define HUD_Panel_GetBgAlpha() do { \
- if (panel_bg_alpha_str == "") { \
- panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha); \
- } \
- panel_bg_alpha = stof(panel_bg_alpha_str); \
- if (autocvar__hud_configure) { \
- if (!panel_enabled) \
- panel_bg_alpha = 0.25; \
- else if (menu_enabled == 2 && panel == highlightedPanel) \
- panel_bg_alpha = (1 - autocvar__menu_alpha) * max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha) + autocvar__menu_alpha * panel_bg_alpha;\
- else \
- panel_bg_alpha = max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha); \
- } \
-} while(0)
-
-// Get value for panel_fg_alpha. Also do various minalpha checks
-// comment on line 2 of macro: // ALWAYS show disabled panels at 0.25 alpha when in config mode
-#define HUD_Panel_GetFgAlpha() do { \
- panel_fg_alpha = autocvar_hud_panel_fg_alpha; \
- if (autocvar__hud_configure && !panel_enabled) \
- panel_fg_alpha = 0.25; \
-} while(0)
-
-// Get border. See comments above, it's similar.
-#define HUD_Panel_GetBorder() do { \
- if (panel_bg_border_str == "") { \
- panel_bg_border = autocvar_hud_panel_bg_border; \
- } else { \
- panel_bg_border = stof(panel_bg_border_str); \
- } \
-} while(0)
-
-// Get padding. See comments above, it's similar.
-// last line is a port of the old function, basically always make sure the panel contents are at least 5 pixels tall/wide, to disallow extreme padding values
-#define HUD_Panel_GetPadding() do { \
- if (panel_bg_padding_str == "") { \
- panel_bg_padding = autocvar_hud_panel_bg_padding; \
- } else { \
- panel_bg_padding = stof(panel_bg_padding_str); \
- } \
- panel_bg_padding = min(min(panel_size.x, panel_size.y)/2 - 5, panel_bg_padding); \
-} while(0)
-
-// return smoothly faded pos and size of given panel when a dialog is active
-// don't center too wide panels, it doesn't work with different resolutions
-#define HUD_Panel_UpdatePosSize_ForMenu() do { \
- vector menu_enable_size = panel_size; \
- float max_panel_width = 0.52 * vid_conwidth; \
- if(panel_size.x > max_panel_width) \
- { \
- menu_enable_size.x = max_panel_width; \
- menu_enable_size.y = panel_size.y * (menu_enable_size.x / panel_size.x); \
- } \
- vector menu_enable_pos = eX * (panel_bg_border + 0.5 * max_panel_width) + eY * 0.5 * vid_conheight - 0.5 * menu_enable_size; \
- panel_pos = (1 - autocvar__menu_alpha) * panel_pos + (autocvar__menu_alpha) * menu_enable_pos; \
- panel_size = (1 - autocvar__menu_alpha) * panel_size + (autocvar__menu_alpha) * menu_enable_size; \
-} while(0)
-
-// Scale the pos and size vectors to absolute coordinates
-#define HUD_Panel_ScalePosSize() do { \
- panel_pos.x *= vid_conwidth; panel_pos.y *= vid_conheight; \
- panel_size.x *= vid_conwidth; panel_size.y *= vid_conheight; \
-} while(0)
-
-// NOTE: in hud_configure mode cvars must be reloaded every frame
-#define HUD_Panel_UpdateCvars() do { \
- if (panel.update_time <= time) { \
- if (autocvar__hud_configure) panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \
- panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \
- panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \
- HUD_Panel_ScalePosSize(); \
- panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg")); \
- panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color")); \
- panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team")); \
- panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha")); \
- panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \
- panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding")); \
- HUD_Panel_GetBg(); \
- if (panel.current_panel_bg != "0") { \
- HUD_Panel_GetColorTeam(); \
- HUD_Panel_GetColor(); \
- HUD_Panel_GetBgAlpha(); \
- HUD_Panel_GetBorder(); \
- } \
- HUD_Panel_GetFgAlpha(); \
- HUD_Panel_GetPadding(); \
- panel.current_panel_bg_alpha = panel_bg_alpha; \
- panel.current_panel_fg_alpha = panel_fg_alpha; \
- if (menu_enabled == 2 && panel == highlightedPanel) { \
- HUD_Panel_UpdatePosSize_ForMenu(); \
- } else { \
- panel_bg_alpha *= hud_fade_alpha; \
- panel_fg_alpha *= hud_fade_alpha; \
- } \
- panel.current_panel_pos = panel_pos; \
- panel.current_panel_size = panel_size; \
- panel.current_panel_bg_border = panel_bg_border; \
- panel.current_panel_bg_color = panel_bg_color; \
- panel.current_panel_bg_color_team = panel_bg_color_team; \
- panel.current_panel_bg_padding = panel_bg_padding; \
- panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval; \
- } else { \
- panel_pos = panel.current_panel_pos; \
- panel_size = panel.current_panel_size; \
- panel_bg_alpha = panel.current_panel_bg_alpha * hud_fade_alpha; \
- panel_bg_border = panel.current_panel_bg_border; \
- panel_bg_color = panel.current_panel_bg_color; \
- panel_bg_color_team = panel.current_panel_bg_color_team; \
- panel_bg_padding = panel.current_panel_bg_padding; \
- panel_fg_alpha = panel.current_panel_fg_alpha * hud_fade_alpha; \
- } \
-} while(0)
-
-#define HUD_Panel_UpdatePosSize() do { \
- panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \
- panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \
- panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \
- HUD_Panel_ScalePosSize(); \
- if (menu_enabled == 2 && panel == highlightedPanel) { \
- HUD_Panel_UpdatePosSize_ForMenu(); \
- } \
- panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \
- HUD_Panel_GetBorder(); \
-} while(0)
-
-const int NOTIFY_MAX_ENTRIES = 10;
-const float NOTIFY_ICON_MARGIN = 0.02;
-
-int notify_index;
-int notify_count;
-float notify_times[NOTIFY_MAX_ENTRIES];
-string notify_attackers[NOTIFY_MAX_ENTRIES];
-string notify_victims[NOTIFY_MAX_ENTRIES];
-string notify_icons[NOTIFY_MAX_ENTRIES];
-
-void HUD_Notify_Push(string icon, string attacker, string victim);
-
-var void HUD_ModIcons_GameType(vector pos, vector size);
-void HUD_ModIcons_SetFunc();
-#endif
--- /dev/null
+#include "panel/weapons.qc"
+#include "panel/ammo.qc"
+#include "panel/powerups.qc"
+#include "panel/healtharmor.qc"
+#include "panel/notify.qc"
+#include "panel/timer.qc"
+#include "panel/radar.qc"
+#include "panel/score.qc"
+#include "panel/racetimer.qc"
+#include "panel/vote.qc"
+#include "panel/modicons.qc"
+#include "panel/pressedkeys.qc"
+#include "panel/chat.qc"
+#include "panel/engineinfo.qc"
+#include "panel/infomessages.qc"
+#include "panel/physics.qc"
+#include "panel/centerprint.qc"
+#include "panel/minigame.qc"
+// #include "panel/mapvote.qc"
+// #include "panel/itemstime.qc"
+#include "panel/quickmenu.qc"
--- /dev/null
+#include "hud.qc"
+#include "hud_config.qc"
--- /dev/null
+#include "hud.qh"
+#include "hud_config.qh"
--- /dev/null
+#include "hud.qh"
+
+#include "hud_config.qh"
+#include "mapvoting.qh"
+#include "scoreboard.qh"
+#include "teamradar.qh"
+#include "t_items.qh"
+#include "../common/buffs/all.qh"
+#include "../common/deathtypes/all.qh"
+#include "../common/items/all.qc"
+#include "../common/mapinfo.qh"
+#include "../common/mutators/mutator/waypoints/all.qh"
+#include "../common/nades/all.qh"
+#include "../common/stats.qh"
+#include "../lib/csqcmodel/cl_player.qh"
+// TODO: remove
+#include "../server/mutators/mutator/gamemode_ctf.qc"
+
+
+/*
+==================
+Misc HUD functions
+==================
+*/
+
+vector HUD_Get_Num_Color (float x, float maxvalue)
+{
+ float blinkingamt;
+ vector color;
+ if(x >= maxvalue) {
+ color.x = sin(2*M_PI*time);
+ color.y = 1;
+ color.z = sin(2*M_PI*time);
+ }
+ else if(x > maxvalue * 0.75) {
+ color.x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0
+ color.y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1
+ color.z = 0;
+ }
+ else if(x > maxvalue * 0.5) {
+ color.x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4
+ color.y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9
+ color.z = 1 - (x-100)*0.02; // blue value between 1 -> 0
+ }
+ else if(x > maxvalue * 0.25) {
+ color.x = 1;
+ color.y = 1;
+ color.z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1
+ }
+ else if(x > maxvalue * 0.1) {
+ color.x = 1;
+ color.y = (x-20)*90/27/100; // green value between 0 -> 1
+ color.z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2
+ }
+ else {
+ color.x = 1;
+ color.y = 0;
+ color.z = 0;
+ }
+
+ blinkingamt = (1 - x/maxvalue/0.25);
+ if(blinkingamt > 0)
+ {
+ color.x = color.x - color.x * blinkingamt * sin(2*M_PI*time);
+ color.y = color.y - color.y * blinkingamt * sin(2*M_PI*time);
+ color.z = color.z - color.z * blinkingamt * sin(2*M_PI*time);
+ }
+ return color;
+}
+
+float HUD_GetRowCount(int item_count, vector size, float item_aspect)
+{
+ float aspect = size_y / size_x;
+ return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count);
+}
+
+vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect)
+{
+ float columns, rows;
+ float ratio, best_ratio = 0;
+ float best_columns = 1, best_rows = 1;
+ bool vertical = (psize.x / psize.y >= item_aspect);
+ if(vertical)
+ {
+ psize = eX * psize.y + eY * psize.x;
+ item_aspect = 1 / item_aspect;
+ }
+
+ rows = ceil(sqrt(item_count));
+ columns = ceil(item_count/rows);
+ while(columns >= 1)
+ {
+ ratio = (psize.x/columns) / (psize.y/rows);
+ if(ratio > item_aspect)
+ ratio = item_aspect * item_aspect / ratio;
+
+ if(ratio <= best_ratio)
+ break; // ratio starts decreasing by now, skip next configurations
+
+ best_columns = columns;
+ best_rows = rows;
+ best_ratio = ratio;
+
+ if(columns == 1)
+ break;
+
+ --columns;
+ rows = ceil(item_count/columns);
+ }
+
+ if(vertical)
+ return eX * best_rows + eY * best_columns;
+ else
+ return eX * best_columns + eY * best_rows;
+}
+
+// return the string of the onscreen race timer
+string MakeRaceString(int cp, float mytime, float theirtime, float lapdelta, string theirname)
+{
+ string col;
+ string timestr;
+ string cpname;
+ string lapstr;
+ lapstr = "";
+
+ if(theirtime == 0) // goal hit
+ {
+ if(mytime > 0)
+ {
+ timestr = strcat("+", ftos_decimals(+mytime, TIME_DECIMALS));
+ col = "^1";
+ }
+ else if(mytime == 0)
+ {
+ timestr = "+0.0";
+ col = "^3";
+ }
+ else
+ {
+ timestr = strcat("-", ftos_decimals(-mytime, TIME_DECIMALS));
+ col = "^2";
+ }
+
+ if(lapdelta > 0)
+ {
+ lapstr = sprintf(_(" (-%dL)"), lapdelta);
+ col = "^2";
+ }
+ else if(lapdelta < 0)
+ {
+ lapstr = sprintf(_(" (+%dL)"), -lapdelta);
+ col = "^1";
+ }
+ }
+ else if(theirtime > 0) // anticipation
+ {
+ if(mytime >= theirtime)
+ timestr = strcat("+", ftos_decimals(mytime - theirtime, TIME_DECIMALS));
+ else
+ timestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(theirtime));
+ col = "^3";
+ }
+ else
+ {
+ col = "^7";
+ timestr = "";
+ }
+
+ if(cp == 254)
+ cpname = _("Start line");
+ else if(cp == 255)
+ cpname = _("Finish line");
+ else if(cp)
+ cpname = sprintf(_("Intermediate %d"), cp);
+ else
+ cpname = _("Finish line");
+
+ if(theirtime < 0)
+ return strcat(col, cpname);
+ else if(theirname == "")
+ return strcat(col, sprintf("%s (%s)", cpname, timestr));
+ else
+ return strcat(col, sprintf("%s (%s %s)", cpname, timestr, strcat(theirname, col, lapstr)));
+}
+
+// Check if the given name already exist in race rankings? In that case, where? (otherwise return 0)
+int race_CheckName(string net_name)
+{
+ int i;
+ for (i=RANKINGS_CNT-1;i>=0;--i)
+ if(grecordholder[i] == net_name)
+ return i+1;
+ return 0;
+}
+
+/*
+==================
+HUD panels
+==================
+*/
+
+//basically the same code of draw_ButtonPicture and draw_VertButtonPicture for the menu
+void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag)
+{
+ if(!length_ratio || !theAlpha)
+ return;
+ if(length_ratio > 1)
+ length_ratio = 1;
+ if (baralign == 3)
+ {
+ if(length_ratio < -1)
+ length_ratio = -1;
+ }
+ else if(length_ratio < 0)
+ return;
+
+ vector square;
+ vector width, height;
+ if(vertical) {
+ pic = strcat(hud_skin_path, "/", pic, "_vertical");
+ if(precache_pic(pic) == "") {
+ pic = "gfx/hud/default/progressbar_vertical";
+ }
+
+ if (baralign == 1) // bottom align
+ theOrigin.y += (1 - length_ratio) * theSize.y;
+ else if (baralign == 2) // center align
+ theOrigin.y += 0.5 * (1 - length_ratio) * theSize.y;
+ else if (baralign == 3) // center align, positive values down, negative up
+ {
+ theSize.y *= 0.5;
+ if (length_ratio > 0)
+ theOrigin.y += theSize.y;
+ else
+ {
+ theOrigin.y += (1 + length_ratio) * theSize.y;
+ length_ratio = -length_ratio;
+ }
+ }
+ theSize.y *= length_ratio;
+
+ vector bH;
+ width = eX * theSize.x;
+ height = eY * theSize.y;
+ if(theSize.y <= theSize.x * 2)
+ {
+ // button not high enough
+ // draw just upper and lower part then
+ square = eY * theSize.y * 0.5;
+ bH = eY * (0.25 * theSize.y / (theSize.x * 2));
+ drawsubpic(theOrigin, square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, drawflag);
+ drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, drawflag);
+ }
+ else
+ {
+ square = eY * theSize.x;
+ drawsubpic(theOrigin, width + square, pic, '0 0 0', '1 0.25 0', theColor, theAlpha, drawflag);
+ drawsubpic(theOrigin + square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5 0', theColor, theAlpha, drawflag);
+ drawsubpic(theOrigin + height - square, width + square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, drawflag);
+ }
+ } else {
+ pic = strcat(hud_skin_path, "/", pic);
+ if(precache_pic(pic) == "") {
+ pic = "gfx/hud/default/progressbar";
+ }
+
+ if (baralign == 1) // right align
+ theOrigin.x += (1 - length_ratio) * theSize.x;
+ else if (baralign == 2) // center align
+ theOrigin.x += 0.5 * (1 - length_ratio) * theSize.x;
+ else if (baralign == 3) // center align, positive values on the right, negative on the left
+ {
+ theSize.x *= 0.5;
+ if (length_ratio > 0)
+ theOrigin.x += theSize.x;
+ else
+ {
+ theOrigin.x += (1 + length_ratio) * theSize.x;
+ length_ratio = -length_ratio;
+ }
+ }
+ theSize.x *= length_ratio;
+
+ vector bW;
+ width = eX * theSize.x;
+ height = eY * theSize.y;
+ if(theSize.x <= theSize.y * 2)
+ {
+ // button not wide enough
+ // draw just left and right part then
+ square = eX * theSize.x * 0.5;
+ bW = eX * (0.25 * theSize.x / (theSize.y * 2));
+ drawsubpic(theOrigin, square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, drawflag);
+ drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, drawflag);
+ }
+ else
+ {
+ square = eX * theSize.y;
+ drawsubpic(theOrigin, height + square, pic, '0 0 0', '0.25 1 0', theColor, theAlpha, drawflag);
+ drawsubpic(theOrigin + square, theSize - 2 * square, pic, '0.25 0 0', '0.5 1 0', theColor, theAlpha, drawflag);
+ drawsubpic(theOrigin + width - square, height + square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, drawflag);
+ }
+ }
+}
+
+void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, int drawflag)
+{
+ if(!theAlpha)
+ return;
+
+ string pic;
+ pic = strcat(hud_skin_path, "/num_leading");
+ if(precache_pic(pic) == "") {
+ pic = "gfx/hud/default/num_leading";
+ }
+
+ drawsubpic(pos, eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0 0 0', '0.25 1 0', color, theAlpha, drawflag);
+ if(mySize.x/mySize.y > 2)
+ drawsubpic(pos + eX * mySize.y, eX * (mySize.x - 2 * mySize.y) + eY * mySize.y, pic, '0.25 0 0', '0.5 1 0', color, theAlpha, drawflag);
+ drawsubpic(pos + eX * mySize.x - eX * min(mySize.x * 0.5, mySize.y), eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0.75 0 0', '0.25 1 0', color, theAlpha, drawflag);
+}
+
+void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha, float fadelerp)
+{
+ vector newPos = '0 0 0', newSize = '0 0 0';
+ vector picpos, numpos;
+
+ if (vertical)
+ {
+ if(mySize.y/mySize.x > 2)
+ {
+ newSize.y = 2 * mySize.x;
+ newSize.x = mySize.x;
+
+ newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
+ newPos.x = myPos.x;
+ }
+ else
+ {
+ newSize.x = 1/2 * mySize.y;
+ newSize.y = mySize.y;
+
+ newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
+ newPos.y = myPos.y;
+ }
+
+ if(icon_right_align)
+ {
+ numpos = newPos;
+ picpos = newPos + eY * newSize.x;
+ }
+ else
+ {
+ picpos = newPos;
+ numpos = newPos + eY * newSize.x;
+ }
+
+ newSize.y /= 2;
+ drawpic_aspect_skin(picpos, icon, newSize, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+ // make number smaller than icon, it looks better
+ // reduce only y to draw numbers with different number of digits with the same y size
+ numpos.y += newSize.y * ((1 - 0.7) / 2);
+ newSize.y *= 0.7;
+ drawstring_aspect(numpos, ftos(x), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+ return;
+ }
+
+ if(mySize.x/mySize.y > 3)
+ {
+ newSize.x = 3 * mySize.y;
+ newSize.y = mySize.y;
+
+ newPos.x = myPos.x + (mySize.x - newSize.x) / 2;
+ newPos.y = myPos.y;
+ }
+ else
+ {
+ newSize.y = 1/3 * mySize.x;
+ newSize.x = mySize.x;
+
+ newPos.y = myPos.y + (mySize.y - newSize.y) / 2;
+ newPos.x = myPos.x;
+ }
+
+ if(icon_right_align) // right align
+ {
+ numpos = newPos;
+ picpos = newPos + eX * 2 * newSize.y;
+ }
+ else // left align
+ {
+ numpos = newPos + eX * newSize.y;
+ picpos = newPos;
+ }
+
+ // NOTE: newSize_x is always equal to 3 * mySize_y so we can use
+ // '2 1 0' * newSize_y instead of eX * (2/3) * newSize_x + eY * newSize_y
+ drawstring_aspect_expanding(numpos, ftos(x), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
+ drawpic_aspect_skin_expanding(picpos, icon, '1 1 0' * newSize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp);
+}
+
+void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha)
+{
+ DrawNumIcon_expanding(myPos, mySize, x, icon, vertical, icon_right_align, color, theAlpha, 0);
+}
+
+#include "all.inc"
+
+/*
+==================
+Main HUD system
+==================
+*/
+
+void HUD_Vehicle()
+{
+ if(autocvar__hud_configure) return;
+ if(intermission == 2) return;
+
+ if(hud == HUD_BUMBLEBEE_GUN)
+ CSQC_BUMBLE_GUN_HUD();
+ else {
+ Vehicle info = get_vehicleinfo(hud);
+ info.vr_hud(info);
+ }
+}
+
+bool HUD_Panel_CheckFlags(int showflags)
+{
+ if ( HUD_Minigame_Showpanels() )
+ return showflags & PANEL_SHOW_MINIGAME;
+ if(intermission == 2)
+ return showflags & PANEL_SHOW_MAPVOTE;
+ return showflags & PANEL_SHOW_MAINGAME;
+}
+
+void HUD_Panel_Draw(entity panent)
+{
+ panel = panent;
+ if(autocvar__hud_configure)
+ {
+ if(panel.panel_configflags & PANEL_CONFIG_MAIN)
+ panel.panel_draw();
+ }
+ else if(HUD_Panel_CheckFlags(panel.panel_showflags))
+ panel.panel_draw();
+}
+
+void HUD_Reset()
+{
+ // reset gametype specific icons
+ if(gametype == MAPINFO_TYPE_CTF)
+ HUD_Mod_CTF_Reset();
+}
+
+void HUD_Main()
+{
+ int i;
+ // global hud theAlpha fade
+ if(menu_enabled == 1)
+ hud_fade_alpha = 1;
+ else
+ hud_fade_alpha = (1 - autocvar__menu_alpha);
+
+ if(scoreboard_fade_alpha)
+ hud_fade_alpha = (1 - scoreboard_fade_alpha);
+
+ HUD_Configure_Frame();
+
+ // panels that we want to be active together with the scoreboard
+ // they must fade only when the menu does
+ if(scoreboard_fade_alpha == 1)
+ {
+ HUD_Panel_Draw(HUD_PANEL(CENTERPRINT));
+ return;
+ }
+
+ if(!autocvar__hud_configure && !hud_fade_alpha)
+ {
+ hud_fade_alpha = 1;
+ HUD_Panel_Draw(HUD_PANEL(VOTE));
+ hud_fade_alpha = 0;
+ return;
+ }
+
+ // Drawing stuff
+ if (hud_skin_prev != autocvar_hud_skin)
+ {
+ if (hud_skin_path)
+ strunzone(hud_skin_path);
+ hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
+ if (hud_skin_prev)
+ strunzone(hud_skin_prev);
+ hud_skin_prev = strzone(autocvar_hud_skin);
+ }
+
+ // draw the dock
+ if(autocvar_hud_dock != "" && autocvar_hud_dock != "0")
+ {
+ int f;
+ vector color;
+ float hud_dock_color_team = autocvar_hud_dock_color_team;
+ if((teamplay) && hud_dock_color_team) {
+ if(autocvar__hud_configure && myteam == NUM_SPECTATOR)
+ color = '1 0 0' * hud_dock_color_team;
+ else
+ color = myteamcolors * hud_dock_color_team;
+ }
+ else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) {
+ color = '1 0 0' * hud_dock_color_team;
+ }
+ else
+ {
+ string hud_dock_color = autocvar_hud_dock_color;
+ if(hud_dock_color == "shirt") {
+ f = stof(getplayerkeyvalue(current_player, "colors"));
+ color = colormapPaletteColor(floor(f / 16), 0);
+ }
+ else if(hud_dock_color == "pants") {
+ f = stof(getplayerkeyvalue(current_player, "colors"));
+ color = colormapPaletteColor(f % 16, 1);
+ }
+ else
+ color = stov(hud_dock_color);
+ }
+
+ string pic;
+ pic = strcat(hud_skin_path, "/", autocvar_hud_dock);
+ if(precache_pic(pic) == "") {
+ pic = strcat(hud_skin_path, "/dock_medium");
+ if(precache_pic(pic) == "") {
+ pic = "gfx/hud/default/dock_medium";
+ }
+ }
+ drawpic('0 0 0', pic, eX * vid_conwidth + eY * vid_conheight, color, autocvar_hud_dock_alpha * hud_fade_alpha, DRAWFLAG_NORMAL); // no aspect ratio forcing on dock...
+ }
+
+ // cache the panel order into the panel_order array
+ if(autocvar__hud_panelorder != hud_panelorder_prev) {
+ for(i = 0; i < hud_panels_COUNT; ++i)
+ panel_order[i] = -1;
+ string s = "";
+ int p_num;
+ bool warning = false;
+ int argc = tokenize_console(autocvar__hud_panelorder);
+ if (argc > hud_panels_COUNT)
+ warning = true;
+ //first detect wrong/missing panel numbers
+ for(i = 0; i < hud_panels_COUNT; ++i) {
+ p_num = stoi(argv(i));
+ if (p_num >= 0 && p_num < hud_panels_COUNT) { //correct panel number?
+ if (panel_order[p_num] == -1) //found for the first time?
+ s = strcat(s, ftos(p_num), " ");
+ panel_order[p_num] = 1; //mark as found
+ }
+ else
+ warning = true;
+ }
+ for(i = 0; i < hud_panels_COUNT; ++i) {
+ if (panel_order[i] == -1) {
+ warning = true;
+ s = strcat(s, ftos(i), " "); //add missing panel number
+ }
+ }
+ if (warning)
+ LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n");
+
+ cvar_set("_hud_panelorder", s);
+ if(hud_panelorder_prev)
+ strunzone(hud_panelorder_prev);
+ hud_panelorder_prev = strzone(s);
+
+ //now properly set panel_order
+ tokenize_console(s);
+ for(i = 0; i < hud_panels_COUNT; ++i) {
+ panel_order[i] = stof(argv(i));
+ }
+ }
+
+ hud_draw_maximized = 0;
+ // draw panels in the order specified by panel_order array
+ for(i = hud_panels_COUNT - 1; i >= 0; --i)
+ HUD_Panel_Draw(hud_panels_from(panel_order[i]));
+
+ HUD_Vehicle();
+
+ hud_draw_maximized = 1; // panels that may be maximized must check this var
+ // draw maximized panels on top
+ if(hud_panel_radar_maximized)
+ HUD_Panel_Draw(HUD_PANEL(RADAR));
+ if(autocvar__con_chat_maximized)
+ HUD_Panel_Draw(HUD_PANEL(CHAT));
+ if(hud_panel_quickmenu)
+ HUD_Panel_Draw(HUD_PANEL(QUICKMENU));
+
+ if (scoreboard_active || intermission == 2)
+ HUD_Reset();
+
+ HUD_Configure_PostDraw();
+
+ hud_configure_prev = autocvar__hud_configure;
+}
--- /dev/null
+#ifndef CLIENT_HUD_H
+#define CLIENT_HUD_H
+
+#include "../common/weapons/all.qh"
+
+bool HUD_Radar_Clickable();
+void HUD_Radar_Mouse();
+
+REGISTRY(hud_panels, BITS(6))
+#define hud_panels_from(i) _hud_panels_from(i, NULL)
+REGISTER_REGISTRY(Registerhud_panels)
+
+#define REGISTER_HUD_PANEL(id, draw_func, name, configflags, showflags) \
+ void draw_func(); \
+ REGISTER(Registerhud_panels, HUD_PANEL, hud_panels, id, m_id, new(hud_panel)) { \
+ make_pure(this); \
+ this.panel_id = this.m_id; \
+ this.panel_draw = draw_func; \
+ this.panel_name = #name; \
+ this.panel_configflags = configflags; \
+ this.panel_showflags = showflags; \
+ }
+
+#define HUD_PANEL(NAME) HUD_PANEL_##NAME
+
+// draw the background/borders
+#define HUD_Panel_DrawBg(theAlpha) do { \
+ if(panel.current_panel_bg != "0" && panel.current_panel_bg != "") \
+ draw_BorderPicture(panel_pos - '1 1 0' * panel_bg_border, panel.current_panel_bg, panel_size + '1 1 0' * 2 * panel_bg_border, panel_bg_color, panel_bg_alpha * theAlpha, '1 1 0' * (panel_bg_border/BORDER_MULTIPLIER));\
+} while(0)
+
+int panel_order[hud_panels_MAX];
+string hud_panelorder_prev;
+
+bool hud_draw_maximized;
+bool hud_panel_radar_maximized;
+bool hud_panel_radar_mouse;
+float hud_panel_radar_bottom;
+bool hud_panel_radar_temp_hidden;
+bool chat_panel_modified;
+bool radar_panel_modified;
+
+float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary);
+void HUD_Radar_Hide_Maximized();
+
+void HUD_Reset ();
+void HUD_Main ();
+
+int vote_yescount;
+int vote_nocount;
+int vote_needed;
+int vote_highlighted; // currently selected vote
+
+int vote_active; // is there an active vote?
+int vote_prev; // previous state of vote_active to check for a change
+float vote_alpha;
+float vote_change; // "time" when vote_active changed
+
+float hud_panel_quickmenu;
+
+vector mousepos;
+vector panel_click_distance; // mouse cursor distance from the top left corner of the panel (saved only upon a click)
+vector panel_click_resizeorigin; // coordinates for opposite point when resizing
+float resizeCorner; // 1 = topleft, 2 = topright, 3 = bottomleft, 4 = bottomright
+entity highlightedPanel;
+float highlightedAction; // 0 = nothing, 1 = move, 2 = resize
+
+const float BORDER_MULTIPLIER = 0.25;
+float scoreboard_bottom;
+int weapon_accuracy[Weapons_MAX];
+
+int complain_weapon;
+string complain_weapon_name;
+float complain_weapon_type;
+float complain_weapon_time;
+
+int ps_primary, ps_secondary;
+int ts_primary, ts_secondary;
+
+int last_switchweapon;
+int last_activeweapon;
+float weapontime;
+float weaponprevtime;
+
+float teamnagger;
+
+float hud_configure_checkcollisions;
+float hud_configure_prev;
+vector hud_configure_gridSize;
+vector hud_configure_realGridSize;
+
+int hudShiftState;
+const int S_SHIFT = 1;
+const int S_CTRL = 2;
+const int S_ALT = 4;
+
+float menu_enabled; // 1 showing the entire HUD, 2 showing only the clicked panel
+
+float hud_fade_alpha;
+
+string hud_skin_path;
+string hud_skin_prev;
+
+vector myteamcolors;
+
+entity highlightedPanel_backup;
+vector panel_pos_backup;
+vector panel_size_backup;
+
+vector panel_size_copied;
+
+entity panel;
+entityclass(HUDPanel);
+class(HUDPanel) .string panel_name;
+class(HUDPanel) .int panel_id;
+class(HUDPanel) .vector current_panel_pos;
+class(HUDPanel) .vector current_panel_size;
+class(HUDPanel) .string current_panel_bg;
+class(HUDPanel) .float current_panel_bg_alpha;
+class(HUDPanel) .float current_panel_bg_border;
+class(HUDPanel) .vector current_panel_bg_color;
+class(HUDPanel) .float current_panel_bg_color_team;
+class(HUDPanel) .float current_panel_bg_padding;
+class(HUDPanel) .float current_panel_fg_alpha;
+class(HUDPanel) .float update_time;
+float panel_enabled;
+vector panel_pos;
+vector panel_size;
+string panel_bg_str; // "_str" vars contain the raw value of the cvar, non-"_str" contains what hud.qc code should use
+vector panel_bg_color;
+string panel_bg_color_str;
+float panel_bg_color_team;
+string panel_bg_color_team_str;
+float panel_fg_alpha;
+float panel_bg_alpha;
+string panel_bg_alpha_str;
+float panel_bg_border;
+string panel_bg_border_str;
+float panel_bg_padding;
+string panel_bg_padding_str;
+
+class(HUDPanel) .void() panel_draw;
+
+// chat panel can be reduced / moved while the mapvote is active
+// let know the mapvote panel about chat pos and size
+float chat_posy;
+float chat_sizey;
+
+float current_player;
+
+float stringwidth_colors(string s, vector theSize);
+float stringwidth_nocolors(string s, vector theSize);
+float GetPlayerColorForce(int i);
+int GetPlayerColor(int i);
+string GetPlayerName(int i);
+void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag);
+
+.int panel_showflags;
+const int PANEL_SHOW_NEVER = 0x00;
+const int PANEL_SHOW_MAINGAME = 0x01;
+const int PANEL_SHOW_MINIGAME = 0x02;
+const int PANEL_SHOW_MAPVOTE = 0x04;
+const int PANEL_SHOW_ALWAYS = 0xff;
+bool HUD_Panel_CheckFlags(int showflags);
+
+.int panel_configflags;
+const int PANEL_CONFIG_NO = 0x00;
+const int PANEL_CONFIG_MAIN = 0x01;
+
+
+// prev_* vars contain the health/armor at the previous FRAME
+// set to -1 when player is dead or was not playing
+int prev_health, prev_armor;
+float health_damagetime, armor_damagetime;
+int health_beforedamage, armor_beforedamage;
+// old_p_* vars keep track of previous values when smoothing value changes of the progressbar
+int old_p_health, old_p_armor;
+float old_p_healthtime, old_p_armortime;
+// prev_p_* vars contain the health/armor progressbar value at the previous FRAME
+// set to -1 to forcedly stop effects when we switch spectated player (e.g. from playerX: 70h to playerY: 50h)
+int prev_p_health, prev_p_armor;
+
+void HUD_ItemsTime();
+
+REGISTER_HUD_PANEL(WEAPONS, HUD_Weapons, weapons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(AMMO, HUD_Ammo, ammo, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(POWERUPS, HUD_Powerups, powerups, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(HEALTHARMOR, HUD_HealthArmor, healtharmor, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(NOTIFY, HUD_Notify, notify, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE)
+REGISTER_HUD_PANEL(TIMER, HUD_Timer, timer, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE)
+REGISTER_HUD_PANEL(RADAR, HUD_Radar, radar, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(SCORE, HUD_Score, score, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE)
+REGISTER_HUD_PANEL(RACETIMER, HUD_RaceTimer, racetimer, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(VOTE, HUD_Vote, vote, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS )
+REGISTER_HUD_PANEL(MODICONS, HUD_ModIcons, modicons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(PRESSEDKEYS, HUD_PressedKeys, pressedkeys, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(CHAT, HUD_Chat, chat, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS )
+REGISTER_HUD_PANEL(ENGINEINFO, HUD_EngineInfo, engineinfo, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS )
+REGISTER_HUD_PANEL(INFOMESSAGES, HUD_InfoMessages, infomessages, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(PHYSICS, HUD_Physics, physics, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(CENTERPRINT, HUD_CenterPrint, centerprint, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(MINIGAME_BOARD, HUD_MinigameBoard, minigameboard, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME)
+REGISTER_HUD_PANEL(MINIGAME_STATUS, HUD_MinigameStatus, minigamestatus, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME)
+REGISTER_HUD_PANEL(MINIGAME_HELP, HUD_MinigameHelp, minigamehelp, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME)
+REGISTER_HUD_PANEL(MINIGAME_MENU, HUD_MinigameMenu, minigamemenu, PANEL_CONFIG_NO , PANEL_SHOW_ALWAYS )
+REGISTER_HUD_PANEL(MAPVOTE, MapVote_Draw, mapvote, PANEL_CONFIG_NO , PANEL_SHOW_MAPVOTE )
+REGISTER_HUD_PANEL(ITEMSTIME, HUD_ItemsTime, itemstime, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME)
+// always add new panels to the end of list
+
+// Because calling lots of functions in QC apparently cuts fps in half on many machines:
+// ----------------------
+// MACRO HELL STARTS HERE
+// ----------------------
+// Little help for the poor people who have to make sense of this: Start from the bottom ;)
+
+// Get value for panel.current_panel_bg: if "" fetch default, else use panel_bg_str
+// comment on last line of macro: // we probably want to see a background in config mode at all times...
+#define HUD_Panel_GetBg() do { \
+ string panel_bg; \
+ if (!autocvar__hud_configure && panel_bg_str == "0") { \
+ panel_bg = "0"; \
+ } else { \
+ if (panel_bg_str == "") { \
+ panel_bg_str = autocvar_hud_panel_bg; \
+ } \
+ if (panel_bg_str == "0" && !autocvar__hud_configure) { \
+ panel_bg = "0"; \
+ } else { \
+ if (panel_bg_str == "0" && autocvar__hud_configure) \
+ panel_bg_alpha_str = "0"; \
+ panel_bg = strcat(hud_skin_path, "/", panel_bg_str); \
+ if (precache_pic(panel_bg) == "") { \
+ panel_bg = strcat(hud_skin_path, "/", "border_default"); \
+ if (precache_pic(panel_bg) == "") { \
+ panel_bg = strcat("gfx/hud/default/", "border_default"); \
+ } \
+ } \
+ } \
+ } \
+ if (panel.current_panel_bg) \
+ strunzone(panel.current_panel_bg); \
+ panel.current_panel_bg = strzone(panel_bg); \
+} while(0)
+
+// Get value for panel_bg_color: if "" fetch default, else use panel_bg_color. Convert pants, shirt or teamcolor into a vector.
+#define HUD_Panel_GetColor() do { \
+ if ((teamplay) && panel_bg_color_team) { \
+ if (autocvar__hud_configure && myteam == NUM_SPECTATOR) \
+ panel_bg_color = '1 0 0' * panel_bg_color_team; \
+ else \
+ panel_bg_color = myteamcolors * panel_bg_color_team; \
+ } else if (autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && panel_bg_color_team) { \
+ panel_bg_color = '1 0 0' * panel_bg_color_team; \
+ } else { \
+ if (panel_bg_color_str == "") { \
+ panel_bg_color = autocvar_hud_panel_bg_color; \
+ } else { \
+ if (panel_bg_color_str == "shirt") { \
+ panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player, "colors")) / 16), 0); \
+ } else if (panel_bg_color_str == "pants") { \
+ panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player, "colors")) % 16, 1); \
+ } else { \
+ panel_bg_color = stov(panel_bg_color_str); \
+ } \
+ } \
+ } \
+} while(0)
+
+// Get value for panel_bg_color_team: if "" fetch default, else use panel_bg_color_team_str
+#define HUD_Panel_GetColorTeam() do { \
+ if (panel_bg_color_team_str == "") { \
+ panel_bg_color_team = autocvar_hud_panel_bg_color_team; \
+ } else { \
+ panel_bg_color_team = stof(panel_bg_color_team_str); \
+ } \
+} while(0)
+
+// Get value for panel_bg_alpha: if "" fetch default, else use panel_bg_alpha. Also do various menu dialog fadeout/in checks, and minalpha checks
+// comment on line 3 of macro: // do not set a minalpha cap when showing the config dialog for this panel
+#define HUD_Panel_GetBgAlpha() do { \
+ if (panel_bg_alpha_str == "") { \
+ panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha); \
+ } \
+ panel_bg_alpha = stof(panel_bg_alpha_str); \
+ if (autocvar__hud_configure) { \
+ if (!panel_enabled) \
+ panel_bg_alpha = 0.25; \
+ else if (menu_enabled == 2 && panel == highlightedPanel) \
+ panel_bg_alpha = (1 - autocvar__menu_alpha) * max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha) + autocvar__menu_alpha * panel_bg_alpha;\
+ else \
+ panel_bg_alpha = max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha); \
+ } \
+} while(0)
+
+// Get value for panel_fg_alpha. Also do various minalpha checks
+// comment on line 2 of macro: // ALWAYS show disabled panels at 0.25 alpha when in config mode
+#define HUD_Panel_GetFgAlpha() do { \
+ panel_fg_alpha = autocvar_hud_panel_fg_alpha; \
+ if (autocvar__hud_configure && !panel_enabled) \
+ panel_fg_alpha = 0.25; \
+} while(0)
+
+// Get border. See comments above, it's similar.
+#define HUD_Panel_GetBorder() do { \
+ if (panel_bg_border_str == "") { \
+ panel_bg_border = autocvar_hud_panel_bg_border; \
+ } else { \
+ panel_bg_border = stof(panel_bg_border_str); \
+ } \
+} while(0)
+
+// Get padding. See comments above, it's similar.
+// last line is a port of the old function, basically always make sure the panel contents are at least 5 pixels tall/wide, to disallow extreme padding values
+#define HUD_Panel_GetPadding() do { \
+ if (panel_bg_padding_str == "") { \
+ panel_bg_padding = autocvar_hud_panel_bg_padding; \
+ } else { \
+ panel_bg_padding = stof(panel_bg_padding_str); \
+ } \
+ panel_bg_padding = min(min(panel_size.x, panel_size.y)/2 - 5, panel_bg_padding); \
+} while(0)
+
+// return smoothly faded pos and size of given panel when a dialog is active
+// don't center too wide panels, it doesn't work with different resolutions
+#define HUD_Panel_UpdatePosSize_ForMenu() do { \
+ vector menu_enable_size = panel_size; \
+ float max_panel_width = 0.52 * vid_conwidth; \
+ if(panel_size.x > max_panel_width) \
+ { \
+ menu_enable_size.x = max_panel_width; \
+ menu_enable_size.y = panel_size.y * (menu_enable_size.x / panel_size.x); \
+ } \
+ vector menu_enable_pos = eX * (panel_bg_border + 0.5 * max_panel_width) + eY * 0.5 * vid_conheight - 0.5 * menu_enable_size; \
+ panel_pos = (1 - autocvar__menu_alpha) * panel_pos + (autocvar__menu_alpha) * menu_enable_pos; \
+ panel_size = (1 - autocvar__menu_alpha) * panel_size + (autocvar__menu_alpha) * menu_enable_size; \
+} while(0)
+
+// Scale the pos and size vectors to absolute coordinates
+#define HUD_Panel_ScalePosSize() do { \
+ panel_pos.x *= vid_conwidth; panel_pos.y *= vid_conheight; \
+ panel_size.x *= vid_conwidth; panel_size.y *= vid_conheight; \
+} while(0)
+
+// NOTE: in hud_configure mode cvars must be reloaded every frame
+#define HUD_Panel_UpdateCvars() do { \
+ if (panel.update_time <= time) { \
+ if (autocvar__hud_configure) panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \
+ panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \
+ panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \
+ HUD_Panel_ScalePosSize(); \
+ panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg")); \
+ panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color")); \
+ panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team")); \
+ panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha")); \
+ panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \
+ panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding")); \
+ HUD_Panel_GetBg(); \
+ if (panel.current_panel_bg != "0") { \
+ HUD_Panel_GetColorTeam(); \
+ HUD_Panel_GetColor(); \
+ HUD_Panel_GetBgAlpha(); \
+ HUD_Panel_GetBorder(); \
+ } \
+ HUD_Panel_GetFgAlpha(); \
+ HUD_Panel_GetPadding(); \
+ panel.current_panel_bg_alpha = panel_bg_alpha; \
+ panel.current_panel_fg_alpha = panel_fg_alpha; \
+ if (menu_enabled == 2 && panel == highlightedPanel) { \
+ HUD_Panel_UpdatePosSize_ForMenu(); \
+ } else { \
+ panel_bg_alpha *= hud_fade_alpha; \
+ panel_fg_alpha *= hud_fade_alpha; \
+ } \
+ panel.current_panel_pos = panel_pos; \
+ panel.current_panel_size = panel_size; \
+ panel.current_panel_bg_border = panel_bg_border; \
+ panel.current_panel_bg_color = panel_bg_color; \
+ panel.current_panel_bg_color_team = panel_bg_color_team; \
+ panel.current_panel_bg_padding = panel_bg_padding; \
+ panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval; \
+ } else { \
+ panel_pos = panel.current_panel_pos; \
+ panel_size = panel.current_panel_size; \
+ panel_bg_alpha = panel.current_panel_bg_alpha * hud_fade_alpha; \
+ panel_bg_border = panel.current_panel_bg_border; \
+ panel_bg_color = panel.current_panel_bg_color; \
+ panel_bg_color_team = panel.current_panel_bg_color_team; \
+ panel_bg_padding = panel.current_panel_bg_padding; \
+ panel_fg_alpha = panel.current_panel_fg_alpha * hud_fade_alpha; \
+ } \
+} while(0)
+
+#define HUD_Panel_UpdatePosSize() do { \
+ panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \
+ panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \
+ panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \
+ HUD_Panel_ScalePosSize(); \
+ if (menu_enabled == 2 && panel == highlightedPanel) { \
+ HUD_Panel_UpdatePosSize_ForMenu(); \
+ } \
+ panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \
+ HUD_Panel_GetBorder(); \
+} while(0)
+
+const int NOTIFY_MAX_ENTRIES = 10;
+const float NOTIFY_ICON_MARGIN = 0.02;
+
+int notify_index;
+int notify_count;
+float notify_times[NOTIFY_MAX_ENTRIES];
+string notify_attackers[NOTIFY_MAX_ENTRIES];
+string notify_victims[NOTIFY_MAX_ENTRIES];
+string notify_icons[NOTIFY_MAX_ENTRIES];
+
+void HUD_Notify_Push(string icon, string attacker, string victim);
+
+var void HUD_ModIcons_GameType(vector pos, vector size);
+void HUD_ModIcons_SetFunc();
+#endif
--- /dev/null
+#include "hud_config.qh"
+
+#include "hud.qh"
+
+#define HUD_Write(s) fputs(fh, s)
+// q: quoted, n: not quoted
+#define HUD_Write_Cvar_n(cvar) HUD_Write(strcat("seta ", cvar, " ", cvar_string(cvar), "\n"))
+#define HUD_Write_Cvar_q(cvar) HUD_Write(strcat("seta ", cvar, " \"", cvar_string(cvar), "\"\n"))
+#define HUD_Write_PanelCvar_n(cvar_suf) HUD_Write_Cvar_n(strcat("hud_panel_", panel.panel_name, cvar_suf))
+#define HUD_Write_PanelCvar_q(cvar_suf) HUD_Write_Cvar_q(strcat("hud_panel_", panel.panel_name, cvar_suf))
+// Save the config
+void HUD_Panel_ExportCfg(string cfgname)
+{
+ float fh;
+ string filename = strcat("hud_", autocvar_hud_skin, "_", cfgname, ".cfg");
+ fh = fopen(filename, FILE_WRITE);
+ if(fh >= 0)
+ {
+ HUD_Write_Cvar_q("hud_skin");
+ HUD_Write_Cvar_q("hud_panel_bg");
+ HUD_Write_Cvar_q("hud_panel_bg_color");
+ HUD_Write_Cvar_q("hud_panel_bg_color_team");
+ HUD_Write_Cvar_q("hud_panel_bg_alpha");
+ HUD_Write_Cvar_q("hud_panel_bg_border");
+ HUD_Write_Cvar_q("hud_panel_bg_padding");
+ HUD_Write_Cvar_q("hud_panel_fg_alpha");
+ HUD_Write("\n");
+
+ HUD_Write_Cvar_q("hud_dock");
+ HUD_Write_Cvar_q("hud_dock_color");
+ HUD_Write_Cvar_q("hud_dock_color_team");
+ HUD_Write_Cvar_q("hud_dock_alpha");
+ HUD_Write("\n");
+
+ HUD_Write_Cvar_q("hud_progressbar_alpha");
+ HUD_Write_Cvar_q("hud_progressbar_strength_color");
+ HUD_Write_Cvar_q("hud_progressbar_shield_color");
+ HUD_Write_Cvar_q("hud_progressbar_health_color");
+ HUD_Write_Cvar_q("hud_progressbar_armor_color");
+ HUD_Write_Cvar_q("hud_progressbar_fuel_color");
+ HUD_Write_Cvar_q("hud_progressbar_nexball_color");
+ HUD_Write_Cvar_q("hud_progressbar_speed_color");
+ HUD_Write_Cvar_q("hud_progressbar_acceleration_color");
+ HUD_Write_Cvar_q("hud_progressbar_acceleration_neg_color");
+ HUD_Write("\n");
+
+ HUD_Write_Cvar_q("_hud_panelorder");
+ HUD_Write("\n");
+
+ HUD_Write_Cvar_q("hud_configure_grid");
+ HUD_Write_Cvar_q("hud_configure_grid_xsize");
+ HUD_Write_Cvar_q("hud_configure_grid_ysize");
+ HUD_Write("\n");
+
+ // common cvars for all panels
+ for (int i = 0; i < hud_panels_COUNT; ++i)
+ {
+ panel = hud_panels_from(i);
+
+ HUD_Write_PanelCvar_n("");
+ HUD_Write_PanelCvar_q("_pos");
+ HUD_Write_PanelCvar_q("_size");
+ HUD_Write_PanelCvar_q("_bg");
+ HUD_Write_PanelCvar_q("_bg_color");
+ HUD_Write_PanelCvar_q("_bg_color_team");
+ HUD_Write_PanelCvar_q("_bg_alpha");
+ HUD_Write_PanelCvar_q("_bg_border");
+ HUD_Write_PanelCvar_q("_bg_padding");
+ switch(panel) {
+ case HUD_PANEL_WEAPONS:
+ HUD_Write_PanelCvar_q("_accuracy");
+ HUD_Write_PanelCvar_q("_label");
+ HUD_Write_PanelCvar_q("_label_scale");
+ HUD_Write_PanelCvar_q("_complainbubble");
+ HUD_Write_PanelCvar_q("_complainbubble_padding");
+ HUD_Write_PanelCvar_q("_complainbubble_time");
+ HUD_Write_PanelCvar_q("_complainbubble_fadetime");
+ HUD_Write_PanelCvar_q("_complainbubble_color_outofammo");
+ HUD_Write_PanelCvar_q("_complainbubble_color_donthave");
+ HUD_Write_PanelCvar_q("_complainbubble_color_unavailable");
+ HUD_Write_PanelCvar_q("_ammo");
+ HUD_Write_PanelCvar_q("_ammo_color");
+ HUD_Write_PanelCvar_q("_ammo_alpha");
+ HUD_Write_PanelCvar_q("_aspect");
+ HUD_Write_PanelCvar_q("_timeout");
+ HUD_Write_PanelCvar_q("_timeout_effect");
+ HUD_Write_PanelCvar_q("_timeout_fadebgmin");
+ HUD_Write_PanelCvar_q("_timeout_fadefgmin");
+ HUD_Write_PanelCvar_q("_timeout_speed_in");
+ HUD_Write_PanelCvar_q("_timeout_speed_out");
+ HUD_Write_PanelCvar_q("_onlyowned");
+ HUD_Write_PanelCvar_q("_noncurrent_alpha");
+ HUD_Write_PanelCvar_q("_noncurrent_scale");
+ break;
+ case HUD_PANEL_AMMO:
+ HUD_Write_PanelCvar_q("_onlycurrent");
+ HUD_Write_PanelCvar_q("_noncurrent_alpha");
+ HUD_Write_PanelCvar_q("_noncurrent_scale");
+ HUD_Write_PanelCvar_q("_iconalign");
+ HUD_Write_PanelCvar_q("_progressbar");
+ HUD_Write_PanelCvar_q("_progressbar_name");
+ HUD_Write_PanelCvar_q("_progressbar_xoffset");
+ HUD_Write_PanelCvar_q("_text");
+ break;
+ case HUD_PANEL_POWERUPS:
+ HUD_Write_PanelCvar_q("_iconalign");
+ HUD_Write_PanelCvar_q("_baralign");
+ HUD_Write_PanelCvar_q("_progressbar");
+ HUD_Write_PanelCvar_q("_text");
+ break;
+ case HUD_PANEL_HEALTHARMOR:
+ HUD_Write_PanelCvar_q("_flip");
+ HUD_Write_PanelCvar_q("_iconalign");
+ HUD_Write_PanelCvar_q("_baralign");
+ HUD_Write_PanelCvar_q("_progressbar");
+ HUD_Write_PanelCvar_q("_progressbar_health");
+ HUD_Write_PanelCvar_q("_progressbar_armor");
+ HUD_Write_PanelCvar_q("_progressbar_gfx");
+ HUD_Write_PanelCvar_q("_progressbar_gfx_smooth");
+ HUD_Write_PanelCvar_q("_text");
+ break;
+ case HUD_PANEL_NOTIFY:
+ HUD_Write_PanelCvar_q("_flip");
+ HUD_Write_PanelCvar_q("_fontsize");
+ HUD_Write_PanelCvar_q("_time");
+ HUD_Write_PanelCvar_q("_fadetime");
+ HUD_Write_PanelCvar_q("_icon_aspect");
+ break;
+ case HUD_PANEL_TIMER:
+ HUD_Write_PanelCvar_q("_increment");
+ break;
+ case HUD_PANEL_RADAR:
+ HUD_Write_PanelCvar_q("_foreground_alpha");
+ HUD_Write_PanelCvar_q("_rotation");
+ HUD_Write_PanelCvar_q("_zoommode");
+ HUD_Write_PanelCvar_q("_scale");
+ HUD_Write_PanelCvar_q("_maximized_scale");
+ HUD_Write_PanelCvar_q("_maximized_size");
+ HUD_Write_PanelCvar_q("_maximized_rotation");
+ HUD_Write_PanelCvar_q("_maximized_zoommode");
+ break;
+ case HUD_PANEL_SCORE:
+ HUD_Write_PanelCvar_q("_rankings");
+ break;
+ case HUD_PANEL_VOTE:
+ HUD_Write_PanelCvar_q("_alreadyvoted_alpha");
+ break;
+ case HUD_PANEL_MODICONS:
+ HUD_Write_PanelCvar_q("_ca_layout");
+ HUD_Write_PanelCvar_q("_dom_layout");
+ HUD_Write_PanelCvar_q("_freezetag_layout");
+ break;
+ case HUD_PANEL_PRESSEDKEYS:
+ HUD_Write_PanelCvar_q("_aspect");
+ HUD_Write_PanelCvar_q("_attack");
+ break;
+ case HUD_PANEL_ENGINEINFO:
+ HUD_Write_PanelCvar_q("_framecounter_time");
+ HUD_Write_PanelCvar_q("_framecounter_decimals");
+ break;
+ case HUD_PANEL_INFOMESSAGES:
+ HUD_Write_PanelCvar_q("_flip");
+ break;
+ case HUD_PANEL_PHYSICS:
+ HUD_Write_PanelCvar_q("_speed_unit");
+ HUD_Write_PanelCvar_q("_speed_unit_show");
+ HUD_Write_PanelCvar_q("_speed_max");
+ HUD_Write_PanelCvar_q("_speed_vertical");
+ HUD_Write_PanelCvar_q("_topspeed");
+ HUD_Write_PanelCvar_q("_topspeed_time");
+ HUD_Write_PanelCvar_q("_acceleration_max");
+ HUD_Write_PanelCvar_q("_acceleration_vertical");
+ HUD_Write_PanelCvar_q("_flip");
+ HUD_Write_PanelCvar_q("_baralign");
+ HUD_Write_PanelCvar_q("_progressbar");
+ HUD_Write_PanelCvar_q("_progressbar_acceleration_mode");
+ HUD_Write_PanelCvar_q("_progressbar_acceleration_scale");
+ HUD_Write_PanelCvar_q("_progressbar_acceleration_nonlinear");
+ HUD_Write_PanelCvar_q("_text");
+ HUD_Write_PanelCvar_q("_text_scale");
+ break;
+ case HUD_PANEL_CENTERPRINT:
+ HUD_Write_PanelCvar_q("_align");
+ HUD_Write_PanelCvar_q("_flip");
+ HUD_Write_PanelCvar_q("_fontscale");
+ HUD_Write_PanelCvar_q("_time");
+ HUD_Write_PanelCvar_q("_fade_in");
+ HUD_Write_PanelCvar_q("_fade_out");
+ HUD_Write_PanelCvar_q("_fade_subsequent");
+ HUD_Write_PanelCvar_q("_fade_subsequent_passone");
+ HUD_Write_PanelCvar_q("_fade_subsequent_passone_minalpha");
+ HUD_Write_PanelCvar_q("_fade_subsequent_passtwo");
+ HUD_Write_PanelCvar_q("_fade_subsequent_passtwo_minalpha");
+ HUD_Write_PanelCvar_q("_fade_subsequent_minfontsize");
+ HUD_Write_PanelCvar_q("_fade_minfontsize");
+ break;
+ case HUD_PANEL_ITEMSTIME:
+ HUD_Write_PanelCvar_q("_iconalign");
+ HUD_Write_PanelCvar_q("_progressbar");
+ HUD_Write_PanelCvar_q("_progressbar_name");
+ HUD_Write_PanelCvar_q("_progressbar_reduced");
+ HUD_Write_PanelCvar_q("_text");
+ HUD_Write_PanelCvar_q("_ratio");
+ HUD_Write_PanelCvar_q("_dynamicsize");
+ case HUD_PANEL_QUICKMENU:
+ HUD_Write_PanelCvar_q("_align");
+ break;
+ }
+ HUD_Write("\n");
+ }
+ HUD_Write("menu_sync\n"); // force the menu to reread the cvars, so that the dialogs are updated
+
+ LOG_INFOF(_("^2Successfully exported to %s! (Note: It's saved in data/data/)\n"), filename);
+ fclose(fh);
+ }
+ else
+ LOG_INFOF(_("^1Couldn't write to %s\n"), filename);
+}
+
+void HUD_Configure_Exit_Force()
+{
+ if (menu_enabled)
+ {
+ menu_enabled = 0;
+ localcmd("togglemenu\n");
+ }
+ cvar_set("_hud_configure", "0");
+}
+
+// check if move will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector
+vector HUD_Panel_CheckMove(vector myPos, vector mySize)
+{
+ vector myCenter, targCenter;
+ vector myTarget = myPos;
+ int i;
+ for (i = 0; i < hud_panels_COUNT; ++i) {
+ panel = hud_panels_from(i);
+ if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue;
+ if(panel == highlightedPanel) continue;
+ HUD_Panel_UpdatePosSize();
+ if(!panel_enabled) continue;
+
+ panel_pos -= '1 1 0' * panel_bg_border;
+ panel_size += '2 2 0' * panel_bg_border;
+
+ if(myPos.y + mySize.y < panel_pos.y)
+ continue;
+ if(myPos.y > panel_pos.y + panel_size.y)
+ continue;
+
+ if(myPos.x + mySize.x < panel_pos.x)
+ continue;
+ if(myPos.x > panel_pos.x + panel_size.x)
+ continue;
+
+ // OK, there IS a collision.
+
+ myCenter.x = myPos.x + 0.5 * mySize.x;
+ myCenter.y = myPos.y + 0.5 * mySize.y;
+
+ targCenter.x = panel_pos.x + 0.5 * panel_size.x;
+ targCenter.y = panel_pos.y + 0.5 * panel_size.y;
+
+ if(myCenter.x < targCenter.x && myCenter.y < targCenter.y) // top left (of the target panel)
+ {
+ if(myPos.x + mySize.x - panel_pos.x < myPos.y + mySize.y - panel_pos.y) // push it to the side
+ myTarget.x = panel_pos.x - mySize.x;
+ else // push it upwards
+ myTarget.y = panel_pos.y - mySize.y;
+ }
+ else if(myCenter.x > targCenter.x && myCenter.y < targCenter.y) // top right
+ {
+ if(panel_pos.x + panel_size.x - myPos.x < myPos.y + mySize.y - panel_pos.y) // push it to the side
+ myTarget.x = panel_pos.x + panel_size.x;
+ else // push it upwards
+ myTarget.y = panel_pos.y - mySize.y;
+ }
+ else if(myCenter.x < targCenter.x && myCenter.y > targCenter.y) // bottom left
+ {
+ if(myPos.x + mySize.x - panel_pos.x < panel_pos.y + panel_size.y - myPos.y) // push it to the side
+ myTarget.x = panel_pos.x - mySize.x;
+ else // push it downwards
+ myTarget.y = panel_pos.y + panel_size.y;
+ }
+ else if(myCenter.x > targCenter.x && myCenter.y > targCenter.y) // bottom right
+ {
+ if(panel_pos.x + panel_size.x - myPos.x < panel_pos.y + panel_size.y - myPos.y) // push it to the side
+ myTarget.x = panel_pos.x + panel_size.x;
+ else // push it downwards
+ myTarget.y = panel_pos.y + panel_size.y;
+ }
+ //if(cvar("hud_configure_checkcollisions_debug"))
+ //drawfill(panel_pos, panel_size, '1 1 0', .3, DRAWFLAG_NORMAL);
+ }
+
+ return myTarget;
+}
+
+void HUD_Panel_SetPos(vector pos)
+{
+ panel = highlightedPanel;
+ HUD_Panel_UpdatePosSize();
+ vector mySize;
+ mySize = panel_size;
+
+ //if(cvar("hud_configure_checkcollisions_debug"))
+ //drawfill(pos, mySize, '1 1 1', .2, DRAWFLAG_NORMAL);
+
+ if(autocvar_hud_configure_grid)
+ {
+ pos.x = floor((pos.x/vid_conwidth)/hud_configure_gridSize.x + 0.5) * hud_configure_realGridSize.x;
+ pos.y = floor((pos.y/vid_conheight)/hud_configure_gridSize.y + 0.5) * hud_configure_realGridSize.y;
+ }
+
+ if(hud_configure_checkcollisions)
+ pos = HUD_Panel_CheckMove(pos, mySize);
+
+ pos.x = bound(0, pos.x, vid_conwidth - mySize.x);
+ pos.y = bound(0, pos.y, vid_conheight - mySize.y);
+
+ string s;
+ s = strcat(ftos(pos.x/vid_conwidth), " ", ftos(pos.y/vid_conheight));
+
+ cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s);
+}
+
+// check if resize will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector
+vector HUD_Panel_CheckResize(vector mySize, vector resizeorigin) {
+ vector targEndPos;
+ vector dist;
+ float ratio = mySize.x/mySize.y;
+ int i;
+ for (i = 0; i < hud_panels_COUNT; ++i) {
+ panel = hud_panels_from(i);
+ if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue;
+ if(panel == highlightedPanel) continue;
+ HUD_Panel_UpdatePosSize();
+ if(!panel_enabled) continue;
+
+ panel_pos -= '1 1 0' * panel_bg_border;
+ panel_size += '2 2 0' * panel_bg_border;
+
+ targEndPos = panel_pos + panel_size;
+
+ // resizeorigin is WITHIN target panel, just abort any collision testing against that particular panel to produce expected behaviour!
+ if(resizeorigin.x > panel_pos.x && resizeorigin.x < targEndPos.x && resizeorigin.y > panel_pos.y && resizeorigin.y < targEndPos.y)
+ continue;
+
+ if (resizeCorner == 1)
+ {
+ // check if this panel is on our way
+ if (resizeorigin.x <= panel_pos.x)
+ continue;
+ if (resizeorigin.y <= panel_pos.y)
+ continue;
+ if (targEndPos.x <= resizeorigin.x - mySize.x)
+ continue;
+ if (targEndPos.y <= resizeorigin.y - mySize.y)
+ continue;
+
+ // there is a collision:
+ // detect which side of the panel we are facing is actually limiting the resizing
+ // (which side the resize direction finds for first) and reduce the size up to there
+ //
+ // dist is the distance between resizeorigin and the "analogous" point of the panel
+ // in this case between resizeorigin (bottom-right point) and the bottom-right point of the panel
+ dist.x = resizeorigin.x - targEndPos.x;
+ dist.y = resizeorigin.y - targEndPos.y;
+ if (dist.y <= 0 || dist.x / dist.y > ratio)
+ mySize.x = min(mySize.x, dist.x);
+ else
+ mySize.y = min(mySize.y, dist.y);
+ }
+ else if (resizeCorner == 2)
+ {
+ if (resizeorigin.x >= targEndPos.x)
+ continue;
+ if (resizeorigin.y <= panel_pos.y)
+ continue;
+ if (panel_pos.x >= resizeorigin.x + mySize.x)
+ continue;
+ if (targEndPos.y <= resizeorigin.y - mySize.y)
+ continue;
+
+ dist.x = panel_pos.x - resizeorigin.x;
+ dist.y = resizeorigin.y - targEndPos.y;
+ if (dist.y <= 0 || dist.x / dist.y > ratio)
+ mySize.x = min(mySize.x, dist.x);
+ else
+ mySize.y = min(mySize.y, dist.y);
+ }
+ else if (resizeCorner == 3)
+ {
+ if (resizeorigin.x <= panel_pos.x)
+ continue;
+ if (resizeorigin.y >= targEndPos.y)
+ continue;
+ if (targEndPos.x <= resizeorigin.x - mySize.x)
+ continue;
+ if (panel_pos.y >= resizeorigin.y + mySize.y)
+ continue;
+
+ dist.x = resizeorigin.x - targEndPos.x;
+ dist.y = panel_pos.y - resizeorigin.y;
+ if (dist.y <= 0 || dist.x / dist.y > ratio)
+ mySize.x = min(mySize.x, dist.x);
+ else
+ mySize.y = min(mySize.y, dist.y);
+ }
+ else if (resizeCorner == 4)
+ {
+ if (resizeorigin.x >= targEndPos.x)
+ continue;
+ if (resizeorigin.y >= targEndPos.y)
+ continue;
+ if (panel_pos.x >= resizeorigin.x + mySize.x)
+ continue;
+ if (panel_pos.y >= resizeorigin.y + mySize.y)
+ continue;
+
+ dist.x = panel_pos.x - resizeorigin.x;
+ dist.y = panel_pos.y - resizeorigin.y;
+ if (dist.y <= 0 || dist.x / dist.y > ratio)
+ mySize.x = min(mySize.x, dist.x);
+ else
+ mySize.y = min(mySize.y, dist.y);
+ }
+ //if(cvar("hud_configure_checkcollisions_debug"))
+ //drawfill(panel_pos, panel_size, '1 1 0', .3, DRAWFLAG_NORMAL);
+ }
+
+ return mySize;
+}
+
+void HUD_Panel_SetPosSize(vector mySize)
+{
+ panel = highlightedPanel;
+ HUD_Panel_UpdatePosSize();
+ vector resizeorigin = panel_click_resizeorigin;
+ vector myPos;
+
+ // minimum panel size cap
+ mySize.x = max(0.025 * vid_conwidth, mySize.x);
+ mySize.y = max(0.025 * vid_conheight, mySize.y);
+
+ if(highlightedPanel == HUD_PANEL(CHAT)) // some panels have their own restrictions, like the chat panel (which actually only moves the engine chat print around). Looks bad if it's too small.
+ {
+ mySize.x = max(17 * autocvar_con_chatsize, mySize.x);
+ mySize.y = max(2 * autocvar_con_chatsize + 2 * panel_bg_padding, mySize.y);
+ }
+
+ // collision testing|
+ // -----------------+
+
+ // we need to know pos at this stage, but it might still change later if we hit a screen edge/other panel (?)
+ if(resizeCorner == 1) {
+ myPos.x = resizeorigin.x - mySize.x;
+ myPos.y = resizeorigin.y - mySize.y;
+ } else if(resizeCorner == 2) {
+ myPos.x = resizeorigin.x;
+ myPos.y = resizeorigin.y - mySize.y;
+ } else if(resizeCorner == 3) {
+ myPos.x = resizeorigin.x - mySize.x;
+ myPos.y = resizeorigin.y;
+ } else { // resizeCorner == 4
+ myPos.x = resizeorigin.x;
+ myPos.y = resizeorigin.y;
+ }
+
+ // left/top screen edges
+ if(myPos.x < 0)
+ mySize.x = mySize.x + myPos.x;
+ if(myPos.y < 0)
+ mySize.y = mySize.y + myPos.y;
+
+ // bottom/right screen edges
+ if(myPos.x + mySize.x > vid_conwidth)
+ mySize.x = vid_conwidth - myPos.x;
+ if(myPos.y + mySize.y > vid_conheight)
+ mySize.y = vid_conheight - myPos.y;
+
+ //if(cvar("hud_configure_checkcollisions_debug"))
+ //drawfill(myPos, mySize, '1 1 1', .2, DRAWFLAG_NORMAL);
+
+ // before checkresize, otherwise panel can be snapped partially inside another panel or panel aspect ratio can be broken
+ if(autocvar_hud_configure_grid)
+ {
+ mySize.x = floor((mySize.x/vid_conwidth)/hud_configure_gridSize.x + 0.5) * hud_configure_realGridSize.x;
+ mySize.y = floor((mySize.y/vid_conheight)/hud_configure_gridSize.y + 0.5) * hud_configure_realGridSize.y;
+ }
+
+ if(hud_configure_checkcollisions)
+ mySize = HUD_Panel_CheckResize(mySize, resizeorigin);
+
+ // minimum panel size cap, do this once more so we NEVER EVER EVER have a panel smaller than this, JUST IN CASE above code still makes the panel eg negative (impossible to resize back without changing cvars manually then)
+ mySize.x = max(0.025 * vid_conwidth, mySize.x);
+ mySize.y = max(0.025 * vid_conheight, mySize.y);
+
+ // do another pos check, as size might have changed by now
+ if(resizeCorner == 1) {
+ myPos.x = resizeorigin.x - mySize.x;
+ myPos.y = resizeorigin.y - mySize.y;
+ } else if(resizeCorner == 2) {
+ myPos.x = resizeorigin.x;
+ myPos.y = resizeorigin.y - mySize.y;
+ } else if(resizeCorner == 3) {
+ myPos.x = resizeorigin.x - mySize.x;
+ myPos.y = resizeorigin.y;
+ } else { // resizeCorner == 4
+ myPos.x = resizeorigin.x;
+ myPos.y = resizeorigin.y;
+ }
+
+ //if(cvar("hud_configure_checkcollisions_debug"))
+ //drawfill(myPos, mySize, '0 1 0', .3, DRAWFLAG_NORMAL);
+
+ string s;
+ s = strcat(ftos(mySize.x/vid_conwidth), " ", ftos(mySize.y/vid_conheight));
+ cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s);
+
+ s = strcat(ftos(myPos.x/vid_conwidth), " ", ftos(myPos.y/vid_conheight));
+ cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s);
+}
+
+float pressed_key_time;
+vector highlightedPanel_initial_pos, highlightedPanel_initial_size;
+void HUD_Panel_Arrow_Action(float nPrimary)
+{
+ if(!highlightedPanel)
+ return;
+
+ hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions);
+
+ float step;
+ if(autocvar_hud_configure_grid)
+ {
+ if (nPrimary == K_UPARROW || nPrimary == K_DOWNARROW)
+ {
+ if (hudShiftState & S_SHIFT)
+ step = hud_configure_realGridSize.y;
+ else
+ step = 2 * hud_configure_realGridSize.y;
+ }
+ else
+ {
+ if (hudShiftState & S_SHIFT)
+ step = hud_configure_realGridSize.x;
+ else
+ step = 2 * hud_configure_realGridSize.x;
+ }
+ }
+ else
+ {
+ if (nPrimary == K_UPARROW || nPrimary == K_DOWNARROW)
+ step = vid_conheight;
+ else
+ step = vid_conwidth;
+ if (hudShiftState & S_SHIFT)
+ step = (step / 256); // more precision
+ else
+ step = (step / 64) * (1 + 2 * (time - pressed_key_time));
+ }
+
+ panel = highlightedPanel;
+ HUD_Panel_UpdatePosSize();
+
+ highlightedPanel_initial_pos = panel_pos;
+ highlightedPanel_initial_size = panel_size;
+
+ if (hudShiftState & S_ALT) // resize
+ {
+ if(nPrimary == K_UPARROW)
+ resizeCorner = 1;
+ else if(nPrimary == K_RIGHTARROW)
+ resizeCorner = 2;
+ else if(nPrimary == K_LEFTARROW)
+ resizeCorner = 3;
+ else // if(nPrimary == K_DOWNARROW)
+ resizeCorner = 4;
+
+ // ctrl+arrow reduces the size, instead of increasing it
+ // Note that ctrl disables collisions check too, but it's fine
+ // since we don't collide with anything reducing the size
+ if (hudShiftState & S_CTRL) {
+ step = -step;
+ resizeCorner = 5 - resizeCorner;
+ }
+
+ vector mySize;
+ mySize = panel_size;
+ panel_click_resizeorigin = panel_pos;
+ if(resizeCorner == 1) {
+ panel_click_resizeorigin += mySize;
+ mySize.y += step;
+ } else if(resizeCorner == 2) {
+ panel_click_resizeorigin.y += mySize.y;
+ mySize.x += step;
+ } else if(resizeCorner == 3) {
+ panel_click_resizeorigin.x += mySize.x;
+ mySize.x += step;
+ } else { // resizeCorner == 4
+ mySize.y += step;
+ }
+ HUD_Panel_SetPosSize(mySize);
+ }
+ else // move
+ {
+ vector pos;
+ pos = panel_pos;
+ if(nPrimary == K_UPARROW)
+ pos.y -= step;
+ else if(nPrimary == K_DOWNARROW)
+ pos.y += step;
+ else if(nPrimary == K_LEFTARROW)
+ pos.x -= step;
+ else // if(nPrimary == K_RIGHTARROW)
+ pos.x += step;
+
+ HUD_Panel_SetPos(pos);
+ }
+
+ panel = highlightedPanel;
+ HUD_Panel_UpdatePosSize();
+
+ if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size)
+ {
+ // backup!
+ panel_pos_backup = highlightedPanel_initial_pos;
+ panel_size_backup = highlightedPanel_initial_size;
+ highlightedPanel_backup = highlightedPanel;
+ }
+}
+
+void HUD_Panel_EnableMenu();
+entity tab_panels[hud_panels_MAX];
+entity tab_panel;
+vector tab_panel_pos;
+float tab_backward;
+void HUD_Panel_FirstInDrawQ(float id);
+void reset_tab_panels()
+{
+ int i;
+ for(i = 0; i < hud_panels_COUNT; ++i)
+ tab_panels[i] = world;
+}
+float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
+{
+ string s;
+
+ if(bInputType == 2)
+ return false;
+
+ if(!autocvar__hud_configure)
+ return false;
+
+ if(bInputType == 3)
+ {
+ mousepos.x = nPrimary;
+ mousepos.y = nSecondary;
+ return true;
+ }
+
+ // block any input while a menu dialog is fading
+ // don't block mousepos read as it leads to cursor jumps in the interaction with the menu
+ if(autocvar__menu_alpha)
+ {
+ hudShiftState = 0;
+ mouseClicked = 0;
+ return true;
+ }
+
+ // allow console bind to work
+ string con_keys;
+ float keys;
+ con_keys = findkeysforcommand("toggleconsole", 0);
+ keys = tokenize(con_keys); // findkeysforcommand returns data for this
+
+ bool hit_con_bind = false;
+ int i;
+ for (i = 0; i < keys; ++i)
+ {
+ if(nPrimary == stof(argv(i)))
+ hit_con_bind = true;
+ }
+
+ if(bInputType == 0) {
+ if(nPrimary == K_ALT) hudShiftState |= S_ALT;
+ if(nPrimary == K_CTRL) hudShiftState |= S_CTRL;
+ if(nPrimary == K_SHIFT) hudShiftState |= S_SHIFT;
+ }
+ else if(bInputType == 1) {
+ if(nPrimary == K_ALT) hudShiftState -= (hudShiftState & S_ALT);
+ if(nPrimary == K_CTRL) hudShiftState -= (hudShiftState & S_CTRL);
+ if(nPrimary == K_SHIFT) hudShiftState -= (hudShiftState & S_SHIFT);
+ }
+
+ if(nPrimary == K_CTRL)
+ {
+ if (bInputType == 1) //ctrl has been released
+ {
+ if (tab_panel)
+ {
+ //switch to selected panel
+ highlightedPanel = tab_panel;
+ highlightedAction = 0;
+ HUD_Panel_FirstInDrawQ(highlightedPanel.panel_id);
+ }
+ tab_panel = world;
+ reset_tab_panels();
+ }
+ }
+
+ if(nPrimary == K_MOUSE1)
+ {
+ if(bInputType == 0) // key pressed
+ mouseClicked |= S_MOUSE1;
+ else if(bInputType == 1) // key released
+ mouseClicked -= (mouseClicked & S_MOUSE1);
+ }
+ else if(nPrimary == K_MOUSE2)
+ {
+ if(bInputType == 0) // key pressed
+ mouseClicked |= S_MOUSE2;
+ else if(bInputType == 1) // key released
+ mouseClicked -= (mouseClicked & S_MOUSE2);
+ }
+ else if(nPrimary == K_ESCAPE)
+ {
+ if (bInputType == 1)
+ return true;
+ menu_enabled = 1;
+ localcmd("menu_showhudexit\n");
+ }
+ else if(nPrimary == K_BACKSPACE && hudShiftState & S_CTRL)
+ {
+ if (bInputType == 1)
+ return true;
+ if (!menu_enabled)
+ cvar_set("_hud_configure", "0");
+ }
+ else if(nPrimary == K_TAB && hudShiftState & S_CTRL) // switch panel
+ {
+ if (bInputType == 1 || mouseClicked)
+ return true;
+
+ // FIXME minor bug: if a panel is highlighted, has the same pos_x and
+ // lays in the same level of another panel then the next consecutive
+ // CTRL TAB presses will reselect once more the highlighted panel
+
+ entity starting_panel;
+ entity old_tab_panel = tab_panel;
+ if (!tab_panel) //first press of TAB
+ {
+ if (highlightedPanel)
+ {
+ panel = highlightedPanel;
+ HUD_Panel_UpdatePosSize();
+ }
+ else
+ panel_pos = '0 0 0';
+ starting_panel = highlightedPanel;
+ tab_panel_pos = panel_pos; //to compute level
+ }
+ else
+ {
+ if ( ((!tab_backward) && (hudShiftState & S_SHIFT)) || (tab_backward && !(hudShiftState & S_SHIFT)) ) //tab direction changed?
+ reset_tab_panels();
+ starting_panel = tab_panel;
+ }
+ tab_backward = (hudShiftState & S_SHIFT);
+
+ float k, level = 0, start_posX;
+ vector candidate_pos = '0 0 0';
+ const float LEVELS_NUM = 4;
+ float level_height = vid_conheight / LEVELS_NUM;
+:find_tab_panel
+ level = floor(tab_panel_pos.y / level_height) * level_height; //starting level
+ candidate_pos.x = (!tab_backward) ? vid_conwidth : 0;
+ start_posX = tab_panel_pos.x;
+ tab_panel = world;
+ k=0;
+ while(++k)
+ {
+ for(i = 0; i < hud_panels_COUNT; ++i)
+ {
+ panel = hud_panels_from(i);
+ if(!(panel.panel_configflags & PANEL_CONFIG_MAIN))
+ continue;
+ if (panel == tab_panels[i] || panel == starting_panel)
+ continue;
+ HUD_Panel_UpdatePosSize();
+ if (panel_pos.y >= level && (panel_pos.y - level) < level_height)
+ if ( ( !tab_backward && panel_pos.x >= start_posX && (panel_pos.x < candidate_pos.x || (panel_pos.x == candidate_pos.x && panel_pos.y <= candidate_pos.y)) )
+ || ( tab_backward && panel_pos.x <= start_posX && (panel_pos.x > candidate_pos.x || (panel_pos.x == candidate_pos.x && panel_pos.y >= candidate_pos.y)) ) )
+ {
+ tab_panel = panel;
+ tab_panel_pos = candidate_pos = panel_pos;
+ }
+ }
+ if (tab_panel)
+ break;
+ if (k == LEVELS_NUM) //tab_panel not found
+ {
+ reset_tab_panels();
+ if (!old_tab_panel)
+ {
+ tab_panel = world;
+ return true;
+ }
+ starting_panel = old_tab_panel;
+ old_tab_panel = world;
+ goto find_tab_panel; //u must find tab_panel!
+ }
+ if (!tab_backward)
+ {
+ level = (level + level_height) % vid_conheight;
+ start_posX = 0;
+ candidate_pos.x = vid_conwidth;
+ }
+ else
+ {
+ level = (level - level_height) % vid_conheight;
+ start_posX = vid_conwidth;
+ candidate_pos.x = 0;
+ }
+ }
+
+ tab_panels[tab_panel.panel_id] = tab_panel;
+ }
+ else if(nPrimary == K_SPACE && hudShiftState & S_CTRL) // enable/disable highlighted panel or dock
+ {
+ if (bInputType == 1 || mouseClicked)
+ return true;
+
+ if (highlightedPanel)
+ cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name))));
+ else
+ cvar_set(strcat("hud_dock"), (autocvar_hud_dock == "") ? "dock" : "");
+ }
+ else if(nPrimary == 'c' && hudShiftState & S_CTRL) // copy highlighted panel size
+ {
+ if (bInputType == 1 || mouseClicked)
+ return true;
+
+ if (highlightedPanel)
+ {
+ panel = highlightedPanel;
+ HUD_Panel_UpdatePosSize();
+ panel_size_copied = panel_size;
+ }
+ }
+ else if(nPrimary == 'v' && hudShiftState & S_CTRL) // past copied size on the highlighted panel
+ {
+ if (bInputType == 1 || mouseClicked)
+ return true;
+
+ if (panel_size_copied == '0 0 0' || !highlightedPanel)
+ return true;
+
+ panel = highlightedPanel;
+ HUD_Panel_UpdatePosSize();
+
+ // reduce size if it'd go beyond screen boundaries
+ vector tmp_size = panel_size_copied;
+ if (panel_pos.x + panel_size_copied.x > vid_conwidth)
+ tmp_size.x = vid_conwidth - panel_pos.x;
+ if (panel_pos.y + panel_size_copied.y > vid_conheight)
+ tmp_size.y = vid_conheight - panel_pos.y;
+
+ if (panel_size == tmp_size)
+ return true;
+
+ // backup first!
+ panel_pos_backup = panel_pos;
+ panel_size_backup = panel_size;
+ highlightedPanel_backup = highlightedPanel;
+
+ s = strcat(ftos(tmp_size.x/vid_conwidth), " ", ftos(tmp_size.y/vid_conheight));
+ cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s);
+ }
+ else if(nPrimary == 'z' && hudShiftState & S_CTRL) // undo last action
+ {
+ if (bInputType == 1 || mouseClicked)
+ return true;
+ //restore previous values
+ if (highlightedPanel_backup)
+ {
+ s = strcat(ftos(panel_pos_backup.x/vid_conwidth), " ", ftos(panel_pos_backup.y/vid_conheight));
+ cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_pos"), s);
+ s = strcat(ftos(panel_size_backup.x/vid_conwidth), " ", ftos(panel_size_backup.y/vid_conheight));
+ cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_size"), s);
+ highlightedPanel_backup = world;
+ }
+ }
+ else if(nPrimary == 's' && hudShiftState & S_CTRL) // save config
+ {
+ if (bInputType == 1 || mouseClicked)
+ return true;
+ localcmd("hud save myconfig\n");
+ }
+ else if(nPrimary == K_UPARROW || nPrimary == K_DOWNARROW || nPrimary == K_LEFTARROW || nPrimary == K_RIGHTARROW)
+ {
+ if (bInputType == 1)
+ {
+ pressed_key_time = 0;
+ return true;
+ }
+ else if (pressed_key_time == 0)
+ pressed_key_time = time;
+
+ if (!mouseClicked)
+ HUD_Panel_Arrow_Action(nPrimary); //move or resize panel
+ }
+ else if(nPrimary == K_ENTER || nPrimary == K_SPACE || nPrimary == K_KP_ENTER)
+ {
+ if (bInputType == 1)
+ return true;
+ if (highlightedPanel)
+ HUD_Panel_EnableMenu();
+ }
+ else if(hit_con_bind || nPrimary == K_PAUSE)
+ return false;
+
+ return true;
+}
+
+float HUD_Panel_Check_Mouse_Pos(float allow_move)
+{
+ int i, j = 0;
+ while(j < hud_panels_COUNT)
+ {
+ i = panel_order[j];
+ j += 1;
+
+ panel = hud_panels_from(i);
+ if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue;
+ HUD_Panel_UpdatePosSize();
+
+ float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
+
+ // move
+ if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y)
+ {
+ return 1;
+ }
+ // resize from topleft border
+ else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + 0.5 * panel_size.y)
+ {
+ return 2;
+ }
+ // resize from topright border
+ else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + 0.5 * panel_size.y)
+ {
+ return 3;
+ }
+ // resize from bottomleft border
+ else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + panel_size.y + border)
+ {
+ return 3;
+ }
+ // resize from bottomright border
+ else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + panel_size.y + border)
+ {
+ return 2;
+ }
+ }
+ return 0;
+}
+
+// move a panel to the beginning of the panel order array (which means it gets drawn last, on top of everything else)
+void HUD_Panel_FirstInDrawQ(float id)
+{
+ int i;
+ int place = -1;
+ // find out where in the array our current id is, save into place
+ for(i = 0; i < hud_panels_COUNT; ++i)
+ {
+ if(panel_order[i] == id)
+ {
+ place = i;
+ break;
+ }
+ }
+ // place last if we didn't find a place for it yet (probably new panel, or screwed up cvar)
+ if(place == -1)
+ place = hud_panels_COUNT - 1;
+
+ // move all ids up by one step in the array until "place"
+ for(i = place; i > 0; --i)
+ {
+ panel_order[i] = panel_order[i-1];
+ }
+ // now save the new top id
+ panel_order[0] = id;
+
+ // let's save them into the cvar by some strcat trickery
+ string s = "";
+ for(i = 0; i < hud_panels_COUNT; ++i)
+ {
+ s = strcat(s, ftos(panel_order[i]), " ");
+ }
+ cvar_set("_hud_panelorder", s);
+ if(hud_panelorder_prev)
+ strunzone(hud_panelorder_prev);
+ hud_panelorder_prev = strzone(autocvar__hud_panelorder); // prevent HUD_Main from doing useless update, we already updated here
+}
+
+void HUD_Panel_Highlight(float allow_move)
+{
+ int i, j = 0;
+
+ while(j < hud_panels_COUNT)
+ {
+ i = panel_order[j];
+ j += 1;
+
+ panel = hud_panels_from(i);
+ if(!(panel.panel_configflags & PANEL_CONFIG_MAIN))
+ continue;
+ HUD_Panel_UpdatePosSize();
+
+ float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
+
+ // move
+ if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y)
+ {
+ highlightedPanel = hud_panels_from(i);
+ HUD_Panel_FirstInDrawQ(i);
+ highlightedAction = 1;
+ panel_click_distance = mousepos - panel_pos;
+ return;
+ }
+ // resize from topleft border
+ else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + 0.5 * panel_size.y)
+ {
+ highlightedPanel = hud_panels_from(i);
+ HUD_Panel_FirstInDrawQ(i);
+ highlightedAction = 2;
+ resizeCorner = 1;
+ panel_click_distance = mousepos - panel_pos;
+ panel_click_resizeorigin = panel_pos + panel_size;
+ return;
+ }
+ // resize from topright border
+ else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + 0.5 * panel_size.y)
+ {
+ highlightedPanel = hud_panels_from(i);
+ HUD_Panel_FirstInDrawQ(i);
+ highlightedAction = 2;
+ resizeCorner = 2;
+ panel_click_distance.x = panel_size.x - mousepos.x + panel_pos.x;
+ panel_click_distance.y = mousepos.y - panel_pos.y;
+ panel_click_resizeorigin = panel_pos + eY * panel_size.y;
+ return;
+ }
+ // resize from bottomleft border
+ else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + panel_size.y + border)
+ {
+ highlightedPanel = hud_panels_from(i);
+ HUD_Panel_FirstInDrawQ(i);
+ highlightedAction = 2;
+ resizeCorner = 3;
+ panel_click_distance.x = mousepos.x - panel_pos.x;
+ panel_click_distance.y = panel_size.y - mousepos.y + panel_pos.y;
+ panel_click_resizeorigin = panel_pos + eX * panel_size.x;
+ return;
+ }
+ // resize from bottomright border
+ else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + panel_size.y + border)
+ {
+ highlightedPanel = hud_panels_from(i);
+ HUD_Panel_FirstInDrawQ(i);
+ highlightedAction = 2;
+ resizeCorner = 4;
+ panel_click_distance = panel_size - mousepos + panel_pos;
+ panel_click_resizeorigin = panel_pos;
+ return;
+ }
+ }
+ highlightedPanel = world;
+ highlightedAction = 0;
+}
+
+void HUD_Panel_EnableMenu()
+{
+ menu_enabled = 2;
+ localcmd("menu_showhudoptions ", highlightedPanel.panel_name, "\n");
+}
+float mouse_over_panel;
+void HUD_Panel_Mouse()
+{
+ if(autocvar__menu_alpha == 1)
+ return;
+
+ if (!autocvar_hud_cursormode)
+ {
+ mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed;
+
+ mousepos.x = bound(0, mousepos.x, vid_conwidth);
+ mousepos.y = bound(0, mousepos.y, vid_conheight);
+ }
+
+ if(mouseClicked)
+ {
+ if(prevMouseClicked == 0)
+ {
+ if (tab_panel)
+ {
+ //stop ctrl-tab selection
+ tab_panel = world;
+ reset_tab_panels();
+ }
+ HUD_Panel_Highlight(mouseClicked & S_MOUSE1); // sets highlightedPanel, highlightedAction, panel_click_distance, panel_click_resizeorigin
+ // and calls HUD_Panel_UpdatePosSize() for the highlighted panel
+ if (highlightedPanel)
+ {
+ highlightedPanel_initial_pos = panel_pos;
+ highlightedPanel_initial_size = panel_size;
+ }
+ // doubleclick check
+ if ((mouseClicked & S_MOUSE1) && time - prevMouseClickedTime < 0.4 && highlightedPanel && prevMouseClickedPos == mousepos)
+ {
+ mouseClicked = 0; // to prevent spam, I guess.
+ HUD_Panel_EnableMenu();
+ }
+ else
+ {
+ if (mouseClicked & S_MOUSE1)
+ {
+ prevMouseClickedTime = time;
+ prevMouseClickedPos = mousepos;
+ }
+ mouse_over_panel = HUD_Panel_Check_Mouse_Pos(mouseClicked & S_MOUSE1);
+ }
+ }
+ else
+ {
+ panel = highlightedPanel;
+ HUD_Panel_UpdatePosSize();
+ }
+
+ if (highlightedPanel)
+ {
+ drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL);
+ if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size)
+ {
+ hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions);
+ // backup!
+ panel_pos_backup = highlightedPanel_initial_pos;
+ panel_size_backup = highlightedPanel_initial_size;
+ highlightedPanel_backup = highlightedPanel;
+ }
+ else
+ // in case the clicked panel is inside another panel and we aren't
+ // moving it, avoid the immediate "fix" of its position/size
+ // (often unwanted and hateful) by disabling collisions check
+ hud_configure_checkcollisions = false;
+ }
+
+ if(highlightedAction == 1)
+ HUD_Panel_SetPos(mousepos - panel_click_distance);
+ else if(highlightedAction == 2)
+ {
+ vector mySize = '0 0 0';
+ if(resizeCorner == 1) {
+ mySize.x = panel_click_resizeorigin.x - (mousepos.x - panel_click_distance.x);
+ mySize.y = panel_click_resizeorigin.y - (mousepos.y - panel_click_distance.y);
+ } else if(resizeCorner == 2) {
+ mySize.x = mousepos.x + panel_click_distance.x - panel_click_resizeorigin.x;
+ mySize.y = panel_click_distance.y + panel_click_resizeorigin.y - mousepos.y;
+ } else if(resizeCorner == 3) {
+ mySize.x = panel_click_resizeorigin.x + panel_click_distance.x - mousepos.x;
+ mySize.y = mousepos.y + panel_click_distance.y - panel_click_resizeorigin.y;
+ } else { // resizeCorner == 4
+ mySize.x = mousepos.x - (panel_click_resizeorigin.x - panel_click_distance.x);
+ mySize.y = mousepos.y - (panel_click_resizeorigin.y - panel_click_distance.y);
+ }
+ HUD_Panel_SetPosSize(mySize);
+ }
+ }
+ else
+ {
+ if(prevMouseClicked)
+ highlightedAction = 0;
+ if(menu_enabled == 2)
+ mouse_over_panel = 0;
+ else
+ mouse_over_panel = HUD_Panel_Check_Mouse_Pos(true);
+ if (mouse_over_panel && !tab_panel)
+ drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL);
+ }
+ // draw cursor after performing move/resize to have the panel pos/size updated before mouse_over_panel
+ const vector cursorsize = '32 32 0';
+ float cursor_alpha = 1 - autocvar__menu_alpha;
+
+ if(!mouse_over_panel)
+ drawpic(mousepos, strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL);
+ else if(mouse_over_panel == 1)
+ drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_move.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL);
+ else if(mouse_over_panel == 2)
+ drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_resize.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL);
+ else
+ drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_resize2.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL);
+
+ prevMouseClicked = mouseClicked;
+}
+void HUD_Configure_DrawGrid()
+{
+ float i;
+ if(autocvar_hud_configure_grid && autocvar_hud_configure_grid_alpha)
+ {
+ hud_configure_gridSize.x = bound(0.005, cvar("hud_configure_grid_xsize"), 0.2);
+ hud_configure_gridSize.y = bound(0.005, cvar("hud_configure_grid_ysize"), 0.2);
+ hud_configure_realGridSize.x = hud_configure_gridSize.x * vid_conwidth;
+ hud_configure_realGridSize.y = hud_configure_gridSize.y * vid_conheight;
+ vector s;
+ // x-axis
+ s = eX + eY * vid_conheight;
+ for(i = 1; i < 1/hud_configure_gridSize.x; ++i)
+ drawfill(eX * i * hud_configure_realGridSize.x, s, '0.5 0.5 0.5', autocvar_hud_configure_grid_alpha, DRAWFLAG_NORMAL);
+ // y-axis
+ s = eY + eX * vid_conwidth;
+ for(i = 1; i < 1/hud_configure_gridSize.y; ++i)
+ drawfill(eY * i * hud_configure_realGridSize.y, s, '0.5 0.5 0.5', autocvar_hud_configure_grid_alpha, DRAWFLAG_NORMAL);
+ }
+}
+
+float _menu_alpha_prev;
+void HUD_Configure_Frame()
+{
+ int i;
+ if(autocvar__hud_configure)
+ {
+ if(isdemo() || intermission == 2)
+ {
+ HUD_Configure_Exit_Force();
+ return;
+ }
+
+ if(!hud_configure_prev)
+ {
+ if(autocvar_hud_cursormode)
+ setcursormode(1);
+ hudShiftState = 0;
+ for(i = hud_panels_COUNT - 1; i >= 0; --i)
+ hud_panels_from(panel_order[i]).update_time = time;
+ }
+
+ // NOTE this check is necessary because _menu_alpha isn't updated the frame the menu gets enabled
+ if(autocvar__menu_alpha != _menu_alpha_prev)
+ {
+ if(autocvar__menu_alpha == 0)
+ menu_enabled = 0;
+ _menu_alpha_prev = autocvar__menu_alpha;
+ }
+
+ HUD_Configure_DrawGrid();
+ }
+ else if(hud_configure_prev)
+ {
+ if(menu_enabled)
+ menu_enabled = 0;
+ if(autocvar_hud_cursormode)
+ setcursormode(0);
+ }
+}
+
+const float hlBorderSize = 2;
+const string hlBorder = "gfx/hud/default/border_highlighted";
+const string hlBorder2 = "gfx/hud/default/border_highlighted2";
+void HUD_Panel_HlBorder(float myBorder, vector color, float theAlpha)
+{
+ drawfill(panel_pos - '1 1 0' * myBorder, panel_size + '2 2 0' * myBorder, '0 0.5 1', .5 * theAlpha, DRAWFLAG_NORMAL);
+ drawpic_tiled(panel_pos - '1 1 0' * myBorder, hlBorder, '8 1 0' * hlBorderSize, eX * (panel_size.x + 2 * myBorder) + eY * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL);
+ drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * (panel_size.y + 2 * myBorder - hlBorderSize), hlBorder, '8 1 0' * hlBorderSize, eX * (panel_size.x + 2 * myBorder) + eY * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL);
+ drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * hlBorderSize, hlBorder2, '1 8 0' * hlBorderSize, eY * (panel_size.y + 2 * myBorder - 2 * hlBorderSize) + eX * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL);
+ drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * hlBorderSize + eX * (panel_size.x + 2 * myBorder - hlBorderSize), hlBorder2, '1 8 0' * hlBorderSize, eY * (panel_size.y + 2 * myBorder - 2 * hlBorderSize) + eX * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL);
+}
+
+void HUD_Configure_PostDraw()
+{
+ if(autocvar__hud_configure)
+ {
+ if(tab_panel)
+ {
+ panel = tab_panel;
+ HUD_Panel_UpdatePosSize();
+ drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .2, DRAWFLAG_NORMAL);
+ }
+ if(highlightedPanel)
+ {
+ panel = highlightedPanel;
+ HUD_Panel_UpdatePosSize();
+ HUD_Panel_HlBorder(panel_bg_border * hlBorderSize, '0 0.5 1', 0.4 * (1 - autocvar__menu_alpha));
+ }
+ }
+}
--- /dev/null
+#ifndef CLIENT_HUD_CONFIG_H
+#define CLIENT_HUD_CONFIG_H
+
+const int S_MOUSE1 = 1;
+const int S_MOUSE2 = 2;
+const int S_MOUSE3 = 4;
+int mouseClicked;
+int prevMouseClicked; // previous state
+float prevMouseClickedTime; // time during previous left mouse click, to check for doubleclicks
+vector prevMouseClickedPos; // pos during previous left mouse click, to check for doubleclicks
+
+void HUD_Panel_ExportCfg(string cfgname);
+
+void HUD_Panel_Mouse();
+
+void HUD_Configure_Frame();
+
+void HUD_Configure_PostDraw();
+
+float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary);
+
+#endif
--- /dev/null
+// Ammo (#1)
+
+void DrawNadeProgressBar(vector myPos, vector mySize, float progress, vector color)
+{
+ HUD_Panel_DrawProgressBar(
+ myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x,
+ mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x,
+ autocvar_hud_panel_ammo_progressbar_name,
+ progress, 0, 0, color,
+ autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+}
+
+void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time)
+{
+ float bonusNades = getstatf(STAT_NADE_BONUS);
+ float bonusProgress = getstatf(STAT_NADE_BONUS_SCORE);
+ float bonusType = getstati(STAT_NADE_BONUS_TYPE);
+ Nade def = Nades_from(bonusType);
+ vector nadeColor = def.m_color;
+ string nadeIcon = def.m_icon;
+
+ vector iconPos, textPos;
+
+ if(autocvar_hud_panel_ammo_iconalign)
+ {
+ iconPos = myPos + eX * 2 * mySize.y;
+ textPos = myPos;
+ }
+ else
+ {
+ iconPos = myPos;
+ textPos = myPos + eX * mySize.y;
+ }
+
+ if(bonusNades > 0 || bonusProgress > 0)
+ {
+ DrawNadeProgressBar(myPos, mySize, bonusProgress, nadeColor);
+
+ if(autocvar_hud_panel_ammo_text)
+ drawstring_aspect(textPos, ftos(bonusNades), eX * (2/3) * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+
+ if(draw_expanding)
+ drawpic_aspect_skin_expanding(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, expand_time);
+
+ drawpic_aspect_skin(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+}
+
+void DrawAmmoItem(vector myPos, vector mySize, .int ammoType, bool isCurrent, bool isInfinite)
+{
+ if(ammoType == ammo_none)
+ return;
+
+ // Initialize variables
+
+ int ammo;
+ if(autocvar__hud_configure)
+ {
+ isCurrent = (ammoType == ammo_rockets); // Rockets always current
+ ammo = 60;
+ }
+ else
+ ammo = getstati(GetAmmoStat(ammoType));
+
+ if(!isCurrent)
+ {
+ float scale = bound(0, autocvar_hud_panel_ammo_noncurrent_scale, 1);
+ myPos = myPos + (mySize - mySize * scale) * 0.5;
+ mySize = mySize * scale;
+ }
+
+ vector iconPos, textPos;
+ if(autocvar_hud_panel_ammo_iconalign)
+ {
+ iconPos = myPos + eX * 2 * mySize.y;
+ textPos = myPos;
+ }
+ else
+ {
+ iconPos = myPos;
+ textPos = myPos + eX * mySize.y;
+ }
+
+ bool isShadowed = (ammo <= 0 && !isCurrent && !isInfinite);
+
+ vector iconColor = isShadowed ? '0 0 0' : '1 1 1';
+ vector textColor;
+ if(isInfinite)
+ textColor = '0.2 0.95 0';
+ else if(isShadowed)
+ textColor = '0 0 0';
+ else if(ammo < 10)
+ textColor = '0.8 0.04 0';
+ else
+ textColor = '1 1 1';
+
+ float alpha;
+ if(isCurrent)
+ alpha = panel_fg_alpha;
+ else if(isShadowed)
+ alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1) * 0.5;
+ else
+ alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1);
+
+ string text = isInfinite ? "\xE2\x88\x9E" : ftos(ammo); // Use infinity symbol (U+221E)
+
+ // Draw item
+
+ if(isCurrent)
+ drawpic_aspect_skin(myPos, "ammo_current_bg", mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+
+ if(ammo > 0 && autocvar_hud_panel_ammo_progressbar)
+ HUD_Panel_DrawProgressBar(myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, autocvar_hud_panel_ammo_progressbar_name, ammo/autocvar_hud_panel_ammo_maxammo, 0, 0, textColor, autocvar_hud_progressbar_alpha * alpha, DRAWFLAG_NORMAL);
+
+ if(autocvar_hud_panel_ammo_text)
+ drawstring_aspect(textPos, text, eX * (2/3) * mySize.x + eY * mySize.y, textColor, alpha, DRAWFLAG_NORMAL);
+
+ drawpic_aspect_skin(iconPos, GetAmmoPicture(ammoType), '1 1 0' * mySize.y, iconColor, alpha, DRAWFLAG_NORMAL);
+}
+
+int nade_prevstatus;
+int nade_prevframe;
+float nade_statuschange_time;
+
+void HUD_Ammo()
+{
+ if(hud != HUD_NORMAL) return;
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_ammo) return;
+ if(spectatee_status == -1) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+
+ draw_beginBoldFont();
+
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ int rows = 0, columns, row, column;
+ float nade_cnt = getstatf(STAT_NADE_BONUS), nade_score = getstatf(STAT_NADE_BONUS_SCORE);
+ bool draw_nades = (nade_cnt > 0 || nade_score > 0);
+ float nade_statuschange_elapsedtime;
+ int total_ammo_count;
+
+ vector ammo_size;
+ if (autocvar_hud_panel_ammo_onlycurrent)
+ total_ammo_count = 1;
+ else
+ total_ammo_count = AMMO_COUNT;
+
+ if(draw_nades)
+ {
+ ++total_ammo_count;
+ if (nade_cnt != nade_prevframe)
+ {
+ nade_statuschange_time = time;
+ nade_prevstatus = nade_prevframe;
+ nade_prevframe = nade_cnt;
+ }
+ }
+ else
+ nade_prevstatus = nade_prevframe = nade_statuschange_time = 0;
+
+ rows = HUD_GetRowCount(total_ammo_count, mySize, 3);
+ columns = ceil((total_ammo_count)/rows);
+ ammo_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
+
+ vector offset = '0 0 0'; // fteqcc sucks
+ float newSize;
+ if(ammo_size.x/ammo_size.y > 3)
+ {
+ newSize = 3 * ammo_size.y;
+ offset.x = ammo_size.x - newSize;
+ pos.x += offset.x/2;
+ ammo_size.x = newSize;
+ }
+ else
+ {
+ newSize = 1/3 * ammo_size.x;
+ offset.y = ammo_size.y - newSize;
+ pos.y += offset.y/2;
+ ammo_size.y = newSize;
+ }
+
+ int i;
+ bool infinite_ammo = (getstati(STAT_ITEMS, 0, 24) & IT_UNLIMITED_WEAPON_AMMO);
+ row = column = 0;
+ if(autocvar_hud_panel_ammo_onlycurrent)
+ {
+ if(autocvar__hud_configure)
+ {
+ DrawAmmoItem(pos, ammo_size, ammo_rockets, true, false);
+ }
+ else
+ {
+ DrawAmmoItem(
+ pos,
+ ammo_size,
+ (get_weaponinfo(switchweapon)).ammo_field,
+ true,
+ infinite_ammo
+ );
+ }
+
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ column = column + 1;
+ }
+ }
+ else
+ {
+ .int ammotype;
+ row = column = 0;
+ for(i = 0; i < AMMO_COUNT; ++i)
+ {
+ ammotype = GetAmmoFieldFromNum(i);
+ DrawAmmoItem(
+ pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y),
+ ammo_size,
+ ammotype,
+ ((get_weaponinfo(switchweapon)).ammo_field == ammotype),
+ infinite_ammo
+ );
+
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ column = column + 1;
+ }
+ }
+ }
+
+ if (draw_nades)
+ {
+ nade_statuschange_elapsedtime = time - nade_statuschange_time;
+
+ float f = bound(0, nade_statuschange_elapsedtime*2, 1);
+
+ DrawAmmoNades(pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y), ammo_size, nade_prevstatus < nade_cnt && nade_cnt != 0 && f < 1, f);
+ }
+
+ draw_endBoldFont();
+}
--- /dev/null
+// CenterPrint (#16)
+
+const int CENTERPRINT_MAX_MSGS = 10;
+const int CENTERPRINT_MAX_ENTRIES = 50;
+const float CENTERPRINT_SPACING = 0.7;
+int cpm_index;
+string centerprint_messages[CENTERPRINT_MAX_MSGS];
+int centerprint_msgID[CENTERPRINT_MAX_MSGS];
+float centerprint_time[CENTERPRINT_MAX_MSGS];
+float centerprint_expire_time[CENTERPRINT_MAX_MSGS];
+int centerprint_countdown_num[CENTERPRINT_MAX_MSGS];
+bool centerprint_showing;
+
+void centerprint_generic(int new_id, string strMessage, float duration, int countdown_num)
+{
+ //printf("centerprint_generic(%d, '%s^7', %d, %d);\n", new_id, strMessage, duration, countdown_num);
+ int i, j;
+
+ if(strMessage == "" && new_id == 0)
+ return;
+
+ // strip trailing newlines
+ j = strlen(strMessage) - 1;
+ while(substring(strMessage, j, 1) == "\n" && j >= 0)
+ --j;
+ if (j < strlen(strMessage) - 1)
+ strMessage = substring(strMessage, 0, j + 1);
+
+ if(strMessage == "" && new_id == 0)
+ return;
+
+ // strip leading newlines
+ j = 0;
+ while(substring(strMessage, j, 1) == "\n" && j < strlen(strMessage))
+ ++j;
+ if (j > 0)
+ strMessage = substring(strMessage, j, strlen(strMessage) - j);
+
+ if(strMessage == "" && new_id == 0)
+ return;
+
+ if (!centerprint_showing)
+ centerprint_showing = true;
+
+ for (i=0, j=cpm_index; i<CENTERPRINT_MAX_MSGS; ++i, ++j)
+ {
+ if (j == CENTERPRINT_MAX_MSGS)
+ j = 0;
+ if (new_id && new_id == centerprint_msgID[j])
+ {
+ if (strMessage == "" && centerprint_messages[j] != "" && centerprint_countdown_num[j] == 0)
+ {
+ // fade out the current msg (duration and countdown_num are ignored)
+ centerprint_time[j] = min(5, autocvar_hud_panel_centerprint_fade_out);
+ if (centerprint_expire_time[j] > time + min(5, autocvar_hud_panel_centerprint_fade_out) || centerprint_expire_time[j] < time)
+ centerprint_expire_time[j] = time + min(5, autocvar_hud_panel_centerprint_fade_out);
+ return;
+ }
+ break; // found a msg with the same id, at position j
+ }
+ }
+
+ if (i == CENTERPRINT_MAX_MSGS)
+ {
+ // a msg with the same id was not found, add the msg at the next position
+ --cpm_index;
+ if (cpm_index == -1)
+ cpm_index = CENTERPRINT_MAX_MSGS - 1;
+ j = cpm_index;
+ }
+ if(centerprint_messages[j])
+ strunzone(centerprint_messages[j]);
+ centerprint_messages[j] = strzone(strMessage);
+ centerprint_msgID[j] = new_id;
+ if (duration < 0)
+ {
+ centerprint_time[j] = -1;
+ centerprint_expire_time[j] = time;
+ }
+ else
+ {
+ if(duration == 0)
+ duration = max(1, autocvar_hud_panel_centerprint_time);
+ centerprint_time[j] = duration;
+ centerprint_expire_time[j] = time + duration;
+ }
+ centerprint_countdown_num[j] = countdown_num;
+}
+
+void centerprint_hud(string strMessage)
+{
+ centerprint_generic(0, strMessage, autocvar_hud_panel_centerprint_time, 0);
+}
+
+void reset_centerprint_messages()
+{
+ int i;
+ for (i=0; i<CENTERPRINT_MAX_MSGS; ++i)
+ {
+ centerprint_expire_time[i] = 0;
+ centerprint_time[i] = 1;
+ centerprint_msgID[i] = 0;
+ if(centerprint_messages[i])
+ strunzone(centerprint_messages[i]);
+ centerprint_messages[i] = string_null;
+ }
+}
+float hud_configure_cp_generation_time;
+void HUD_CenterPrint ()
+{
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_centerprint) return;
+
+ if(hud_configure_prev)
+ reset_centerprint_messages();
+ }
+ else
+ {
+ if(!hud_configure_prev)
+ reset_centerprint_messages();
+ if (time > hud_configure_cp_generation_time)
+ {
+ if(highlightedPanel == HUD_PANEL(CENTERPRINT))
+ {
+ float r;
+ r = random();
+ if (r > 0.8)
+ centerprint_generic(floor(r*1000), strcat(sprintf("^3Countdown message at time %s", seconds_tostring(time)), ", seconds left: ^COUNT"), 1, 10);
+ else if (r > 0.55)
+ centerprint_generic(0, sprintf("^1Multiline message at time %s that\n^1lasts longer than normal", seconds_tostring(time)), 20, 0);
+ else
+ centerprint_hud(sprintf("Message at time %s", seconds_tostring(time)));
+ hud_configure_cp_generation_time = time + 1 + random()*4;
+ }
+ else
+ {
+ centerprint_generic(0, sprintf("Centerprint message", seconds_tostring(time)), 10, 0);
+ hud_configure_cp_generation_time = time + 10 - random()*3;
+ }
+ }
+ }
+
+ // this panel fades only when the menu does
+ float hud_fade_alpha_save = 0;
+ if(scoreboard_fade_alpha)
+ {
+ hud_fade_alpha_save = hud_fade_alpha;
+ hud_fade_alpha = 1 - autocvar__menu_alpha;
+ }
+ HUD_Panel_UpdateCvars();
+
+ if ( HUD_Radar_Clickable() )
+ {
+ if (hud_panel_radar_bottom >= 0.96 * vid_conheight)
+ return;
+
+ panel_pos = eY * hud_panel_radar_bottom + eX * 0.5 * (vid_conwidth - panel_size_x);
+ panel_size_y = min(panel_size_y, vid_conheight - hud_panel_radar_bottom);
+ }
+ else if(scoreboard_fade_alpha)
+ {
+ hud_fade_alpha = hud_fade_alpha_save;
+
+ // move the panel below the scoreboard
+ if (scoreboard_bottom >= 0.96 * vid_conheight)
+ return;
+ vector target_pos;
+
+ target_pos = eY * scoreboard_bottom + eX * 0.5 * (vid_conwidth - panel_size.x);
+
+ if(target_pos.y > panel_pos.y)
+ {
+ panel_pos = panel_pos + (target_pos - panel_pos) * sqrt(scoreboard_fade_alpha);
+ panel_size.y = min(panel_size.y, vid_conheight - scoreboard_bottom);
+ }
+ }
+
+ HUD_Panel_DrawBg(1);
+
+ if (!centerprint_showing)
+ return;
+
+ if(panel_bg_padding)
+ {
+ panel_pos += '1 1 0' * panel_bg_padding;
+ panel_size -= '2 2 0' * panel_bg_padding;
+ }
+
+ int entries;
+ float height;
+ vector fontsize;
+ // entries = bound(1, floor(CENTERPRINT_MAX_ENTRIES * 4 * panel_size_y/panel_size_x), CENTERPRINT_MAX_ENTRIES);
+ // height = panel_size_y/entries;
+ // fontsize = '1 1 0' * height;
+ height = vid_conheight/50 * autocvar_hud_panel_centerprint_fontscale;
+ fontsize = '1 1 0' * height;
+ entries = bound(1, floor(panel_size.y/height), CENTERPRINT_MAX_ENTRIES);
+
+ int i, j, k, n, g;
+ float a, sz, align, current_msg_posY = 0, msg_size;
+ vector pos;
+ string ts;
+ bool all_messages_expired = true;
+
+ pos = panel_pos;
+ if (autocvar_hud_panel_centerprint_flip)
+ pos.y += panel_size.y;
+ align = bound(0, autocvar_hud_panel_centerprint_align, 1);
+ for (g=0, i=0, j=cpm_index; i<CENTERPRINT_MAX_MSGS; ++i, ++j)
+ {
+ if (j == CENTERPRINT_MAX_MSGS)
+ j = 0;
+ if (centerprint_expire_time[j] <= time)
+ {
+ if (centerprint_countdown_num[j] && centerprint_time[j] > 0)
+ {
+ centerprint_countdown_num[j] = centerprint_countdown_num[j] - 1;
+ if (centerprint_countdown_num[j] == 0)
+ continue;
+ centerprint_expire_time[j] = centerprint_expire_time[j] + centerprint_time[j];
+ }
+ else if(centerprint_time[j] != -1)
+ continue;
+ }
+
+ all_messages_expired = false;
+
+ // fade the centerprint_hud in/out
+ if(centerprint_time[j] < 0) // Expired but forced. Expire time is the fade-in time.
+ a = (time - centerprint_expire_time[j]) / max(0.0001, autocvar_hud_panel_centerprint_fade_in);
+ else if(centerprint_expire_time[j] - autocvar_hud_panel_centerprint_fade_out > time) // Regularily printed. Not fading out yet.
+ a = (time - (centerprint_expire_time[j] - centerprint_time[j])) / max(0.0001, autocvar_hud_panel_centerprint_fade_in);
+ else // Expiring soon, so fade it out.
+ a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out);
+
+ // while counting down show it anyway in order to hold the current message position
+ if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0) // Guaranteed invisible - don't show.
+ continue;
+ if (a > 1)
+ a = 1;
+
+ // set the size from fading in/out before subsequent fading
+ sz = autocvar_hud_panel_centerprint_fade_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_minfontsize);
+
+ // also fade it based on positioning
+ if(autocvar_hud_panel_centerprint_fade_subsequent)
+ {
+ a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1); // pass one: all messages after the first have half theAlpha
+ a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1); // pass two: after that, gradually lower theAlpha even more for each message
+ }
+ a *= panel_fg_alpha;
+
+ // finally set the size based on the new theAlpha from subsequent fading
+ sz = sz * (autocvar_hud_panel_centerprint_fade_subsequent_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_subsequent_minfontsize));
+ drawfontscale = sz * '1 1 0';
+
+ if (centerprint_countdown_num[j])
+ n = tokenizebyseparator(strreplace("^COUNT", count_seconds(centerprint_countdown_num[j]), centerprint_messages[j]), "\n");
+ else
+ n = tokenizebyseparator(centerprint_messages[j], "\n");
+
+ if (autocvar_hud_panel_centerprint_flip)
+ {
+ // check if the message can be entirely shown
+ for(k = 0; k < n; ++k)
+ {
+ getWrappedLine_remaining = argv(k);
+ while(getWrappedLine_remaining)
+ {
+ ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors);
+ if (ts != "")
+ pos.y -= fontsize.y;
+ else
+ pos.y -= fontsize.y * CENTERPRINT_SPACING/2;
+ }
+ }
+ current_msg_posY = pos.y; // save starting pos (first line) of the current message
+ }
+
+ msg_size = pos.y;
+ for(k = 0; k < n; ++k)
+ {
+ getWrappedLine_remaining = argv(k);
+ while(getWrappedLine_remaining)
+ {
+ ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors);
+ if (ts != "")
+ {
+ if (align)
+ pos.x = panel_pos.x + (panel_size.x - stringwidth(ts, true, fontsize)) * align;
+ if (a > 0.5/255.0) // Otherwise guaranteed invisible - don't show. This is checked a second time after some multiplications with other factors were done so temporary changes of these cannot cause flicker.
+ drawcolorcodedstring(pos + eY * 0.5 * (1 - sz) * fontsize.y, ts, fontsize, a, DRAWFLAG_NORMAL);
+ pos.y += fontsize.y;
+ }
+ else
+ pos.y += fontsize.y * CENTERPRINT_SPACING/2;
+ }
+ }
+
+ ++g; // move next position number up
+
+ msg_size = pos.y - msg_size;
+ if (autocvar_hud_panel_centerprint_flip)
+ {
+ pos.y = current_msg_posY - CENTERPRINT_SPACING * fontsize.y;
+ if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
+ pos.y += (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz));
+
+ if (pos.y < panel_pos.y) // check if the next message can be shown
+ {
+ drawfontscale = '1 1 0';
+ return;
+ }
+ }
+ else
+ {
+ pos.y += CENTERPRINT_SPACING * fontsize.y;
+ if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages
+ pos.y -= (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz));
+
+ if(pos.y > panel_pos.y + panel_size.y - fontsize.y) // check if the next message can be shown
+ {
+ drawfontscale = '1 1 0';
+ return;
+ }
+ }
+ }
+ drawfontscale = '1 1 0';
+ if (all_messages_expired)
+ {
+ centerprint_showing = false;
+ reset_centerprint_messages();
+ }
+}
--- /dev/null
+/** Handle chat as a panel (#12) */
+void HUD_Chat()
+{
+ if(!autocvar__hud_configure)
+ {
+ if (!autocvar_hud_panel_chat)
+ {
+ if (!autocvar_con_chatrect)
+ cvar_set("con_chatrect", "0");
+ return;
+ }
+ if(autocvar__con_chat_maximized)
+ {
+ if(!hud_draw_maximized) return;
+ }
+ else if(chat_panel_modified)
+ {
+ panel.update_time = time; // forces reload of panel attributes
+ chat_panel_modified = false;
+ }
+ }
+
+ HUD_Panel_UpdateCvars();
+
+ if(intermission == 2)
+ {
+ // reserve some more space to the mapvote panel
+ // by resizing and moving chat panel to the bottom
+ panel_size.y = min(panel_size.y, vid_conheight * 0.2);
+ panel_pos.y = vid_conheight - panel_size.y - panel_bg_border * 2;
+ chat_posy = panel_pos.y;
+ chat_sizey = panel_size.y;
+ }
+ if(autocvar__con_chat_maximized && !autocvar__hud_configure) // draw at full screen height if maximized
+ {
+ panel_pos.y = panel_bg_border;
+ panel_size.y = vid_conheight - panel_bg_border * 2;
+ if(panel.current_panel_bg == "0") // force a border when maximized
+ {
+ string panel_bg;
+ panel_bg = strcat(hud_skin_path, "/border_default");
+ if(precache_pic(panel_bg) == "")
+ panel_bg = "gfx/hud/default/border_default";
+ if(panel.current_panel_bg)
+ strunzone(panel.current_panel_bg);
+ panel.current_panel_bg = strzone(panel_bg);
+ chat_panel_modified = true;
+ }
+ panel_bg_alpha = max(0.75, panel_bg_alpha); // force an theAlpha of at least 0.75
+ }
+
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ if (!autocvar_con_chatrect)
+ cvar_set("con_chatrect", "1");
+
+ cvar_set("con_chatrect_x", ftos(pos.x/vid_conwidth));
+ cvar_set("con_chatrect_y", ftos(pos.y/vid_conheight));
+
+ cvar_set("con_chatwidth", ftos(mySize.x/vid_conwidth));
+ cvar_set("con_chat", ftos(floor(mySize.y/autocvar_con_chatsize - 0.5)));
+
+ if(autocvar__hud_configure)
+ {
+ vector chatsize;
+ chatsize = '1 1 0' * autocvar_con_chatsize;
+ cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over theAlpha and such
+ float i, a;
+ for(i = 0; i < autocvar_con_chat; ++i)
+ {
+ if(i == autocvar_con_chat - 1)
+ a = panel_fg_alpha;
+ else
+ a = panel_fg_alpha * floor(((i + 1) * 7 + autocvar_con_chattime)/45);
+ drawcolorcodedstring(pos, textShortenToWidth(_("^3Player^7: This is the chat area."), mySize.x, chatsize, stringwidth_colors), chatsize, a, DRAWFLAG_NORMAL);
+ pos.y += chatsize.y;
+ }
+ }
+}
--- /dev/null
+// Engine info panel (#13)
+
+float prevfps;
+float prevfps_time;
+int framecounter;
+
+float frametimeavg;
+float frametimeavg1; // 1 frame ago
+float frametimeavg2; // 2 frames ago
+void HUD_EngineInfo()
+{
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_engineinfo) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ float currentTime = gettime(GETTIME_REALTIME);
+ if(cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage"))
+ {
+ float currentframetime = currentTime - prevfps_time;
+ frametimeavg = (frametimeavg + frametimeavg1 + frametimeavg2 + currentframetime)/4; // average three frametimes into framecounter for slightly more stable fps readings :P
+ frametimeavg2 = frametimeavg1;
+ frametimeavg1 = frametimeavg;
+
+ float weight;
+ weight = cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_new_weight");
+ if(currentframetime > 0.0001) // filter out insane values which sometimes seem to occur and throw off the average? If you are getting 10,000 fps or more, then you don't need a framerate counter.
+ {
+ if(fabs(prevfps - (1/frametimeavg)) > prevfps * cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_instantupdate_change_threshold")) // if there was a big jump in fps, just force prevfps at current (1/currentframetime) to make big updates instant
+ prevfps = (1/currentframetime);
+ prevfps = (1 - weight) * prevfps + weight * (1/frametimeavg); // framecounter just used so there's no need for a new variable, think of it as "frametime average"
+ }
+ prevfps_time = currentTime;
+ }
+ else
+ {
+ framecounter += 1;
+ if(currentTime - prevfps_time > autocvar_hud_panel_engineinfo_framecounter_time)
+ {
+ prevfps = framecounter/(currentTime - prevfps_time);
+ framecounter = 0;
+ prevfps_time = currentTime;
+ }
+ }
+
+ vector color;
+ color = HUD_Get_Num_Color (prevfps, 100);
+ drawstring_aspect(pos, sprintf(_("FPS: %.*f"), autocvar_hud_panel_engineinfo_framecounter_decimals, prevfps), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+}
--- /dev/null
+/** Health/armor (#3) */
+void HUD_HealthArmor()
+{
+ int armor, health, fuel;
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_healtharmor) return;
+ if(hud != HUD_NORMAL) return;
+ if(spectatee_status == -1) return;
+
+ health = getstati(STAT_HEALTH);
+ if(health <= 0)
+ {
+ prev_health = -1;
+ return;
+ }
+ armor = getstati(STAT_ARMOR);
+
+ // code to check for spectatee_status changes is in Ent_ClientData()
+ // prev_p_health and prev_health can be set to -1 there
+
+ if (prev_p_health == -1)
+ {
+ // no effect
+ health_beforedamage = 0;
+ armor_beforedamage = 0;
+ health_damagetime = 0;
+ armor_damagetime = 0;
+ prev_health = health;
+ prev_armor = armor;
+ old_p_health = health;
+ old_p_armor = armor;
+ prev_p_health = health;
+ prev_p_armor = armor;
+ }
+ else if (prev_health == -1)
+ {
+ //start the load effect
+ health_damagetime = 0;
+ armor_damagetime = 0;
+ prev_health = 0;
+ prev_armor = 0;
+ }
+ fuel = getstati(STAT_FUEL);
+ }
+ else
+ {
+ health = 150;
+ armor = 75;
+ fuel = 20;
+ }
+
+ HUD_Panel_UpdateCvars();
+
+ draw_beginBoldFont();
+
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ int baralign = autocvar_hud_panel_healtharmor_baralign;
+ int iconalign = autocvar_hud_panel_healtharmor_iconalign;
+
+ int maxhealth = autocvar_hud_panel_healtharmor_maxhealth;
+ int maxarmor = autocvar_hud_panel_healtharmor_maxarmor;
+ if(autocvar_hud_panel_healtharmor == 2) // combined health and armor display
+ {
+ vector v;
+ v = healtharmor_maxdamage(health, armor, armorblockpercent, DEATH_WEAPON.m_id);
+
+ float x;
+ x = floor(v.x + 1);
+
+ float maxtotal = maxhealth + maxarmor;
+ string biggercount;
+ if(v.z) // NOT fully armored
+ {
+ biggercount = "health";
+ if(autocvar_hud_panel_healtharmor_progressbar)
+ HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_health, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ if(armor)
+ if(autocvar_hud_panel_healtharmor_text)
+ drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "armor", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha * armor / health, DRAWFLAG_NORMAL);
+ }
+ else
+ {
+ biggercount = "armor";
+ if(autocvar_hud_panel_healtharmor_progressbar)
+ HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ if(health)
+ if(autocvar_hud_panel_healtharmor_text)
+ drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "health", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ if(autocvar_hud_panel_healtharmor_text)
+ DrawNumIcon(pos, mySize, x, biggercount, 0, iconalign, HUD_Get_Num_Color(x, maxtotal), 1);
+
+ if(fuel)
+ HUD_Panel_DrawProgressBar(pos, eX * mySize.x + eY * 0.2 * mySize.y, "progressbar", fuel/100, 0, (baralign == 1 || baralign == 3), autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL);
+ }
+ else
+ {
+ float panel_ar = mySize.x/mySize.y;
+ bool is_vertical = (panel_ar < 1);
+ vector health_offset = '0 0 0', armor_offset = '0 0 0';
+ if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1))
+ {
+ mySize.x *= 0.5;
+ if (autocvar_hud_panel_healtharmor_flip)
+ health_offset.x = mySize.x;
+ else
+ armor_offset.x = mySize.x;
+ }
+ else
+ {
+ mySize.y *= 0.5;
+ if (autocvar_hud_panel_healtharmor_flip)
+ health_offset.y = mySize.y;
+ else
+ armor_offset.y = mySize.y;
+ }
+
+ bool health_baralign, armor_baralign, fuel_baralign;
+ bool health_iconalign, armor_iconalign;
+ if (autocvar_hud_panel_healtharmor_flip)
+ {
+ armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1);
+ health_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1);
+ fuel_baralign = health_baralign;
+ armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1);
+ health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1);
+ }
+ else
+ {
+ health_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1);
+ armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1);
+ fuel_baralign = armor_baralign;
+ health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1);
+ armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1);
+ }
+
+ //if(health)
+ {
+ if(autocvar_hud_panel_healtharmor_progressbar)
+ {
+ float p_health, pain_health_alpha;
+ p_health = health;
+ pain_health_alpha = 1;
+ if (autocvar_hud_panel_healtharmor_progressbar_gfx)
+ {
+ if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0)
+ {
+ if (fabs(prev_health - health) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth)
+ {
+ if (time - old_p_healthtime < 1)
+ old_p_health = prev_p_health;
+ else
+ old_p_health = prev_health;
+ old_p_healthtime = time;
+ }
+ if (time - old_p_healthtime < 1)
+ {
+ p_health += (old_p_health - health) * (1 - (time - old_p_healthtime));
+ prev_p_health = p_health;
+ }
+ }
+ if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0)
+ {
+ if (prev_health - health >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage)
+ {
+ if (time - health_damagetime >= 1)
+ health_beforedamage = prev_health;
+ health_damagetime = time;
+ }
+ if (time - health_damagetime < 1)
+ {
+ float health_damagealpha = 1 - (time - health_damagetime)*(time - health_damagetime);
+ HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, health_beforedamage/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * health_damagealpha, DRAWFLAG_NORMAL);
+ }
+ }
+ prev_health = health;
+
+ if (health <= autocvar_hud_panel_healtharmor_progressbar_gfx_lowhealth)
+ {
+ float BLINK_FACTOR = 0.15;
+ float BLINK_BASE = 0.85;
+ float BLINK_FREQ = 9;
+ pain_health_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
+ }
+ }
+ HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, p_health/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * pain_health_alpha, DRAWFLAG_NORMAL);
+ }
+ if(autocvar_hud_panel_healtharmor_text)
+ DrawNumIcon(pos + health_offset, mySize, health, "health", is_vertical, health_iconalign, HUD_Get_Num_Color(health, maxhealth), 1);
+ }
+
+ if(armor)
+ {
+ if(autocvar_hud_panel_healtharmor_progressbar)
+ {
+ float p_armor;
+ p_armor = armor;
+ if (autocvar_hud_panel_healtharmor_progressbar_gfx)
+ {
+ if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0)
+ {
+ if (fabs(prev_armor - armor) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth)
+ {
+ if (time - old_p_armortime < 1)
+ old_p_armor = prev_p_armor;
+ else
+ old_p_armor = prev_armor;
+ old_p_armortime = time;
+ }
+ if (time - old_p_armortime < 1)
+ {
+ p_armor += (old_p_armor - armor) * (1 - (time - old_p_armortime));
+ prev_p_armor = p_armor;
+ }
+ }
+ if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0)
+ {
+ if (prev_armor - armor >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage)
+ {
+ if (time - armor_damagetime >= 1)
+ armor_beforedamage = prev_armor;
+ armor_damagetime = time;
+ }
+ if (time - armor_damagetime < 1)
+ {
+ float armor_damagealpha = 1 - (time - armor_damagetime)*(time - armor_damagetime);
+ HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, armor_beforedamage/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * armor_damagealpha, DRAWFLAG_NORMAL);
+ }
+ }
+ prev_armor = armor;
+ }
+ HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, p_armor/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ if(autocvar_hud_panel_healtharmor_text)
+ DrawNumIcon(pos + armor_offset, mySize, armor, "armor", is_vertical, armor_iconalign, HUD_Get_Num_Color(armor, maxarmor), 1);
+ }
+
+ if(fuel)
+ {
+ if (is_vertical)
+ mySize.x *= 0.2 / 2; //if vertical always halve x to not cover too much numbers with 3 digits
+ else
+ mySize.y *= 0.2;
+ if (panel_ar >= 4)
+ mySize.x *= 2; //restore full panel size
+ else if (panel_ar < 1/4)
+ mySize.y *= 2; //restore full panel size
+ HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", fuel/100, is_vertical, fuel_baralign, autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL);
+ }
+ }
+
+ draw_endBoldFont();
+}
--- /dev/null
+// Info messages panel (#14)
+
+#define drawInfoMessage(s) do { \
+ if(autocvar_hud_panel_infomessages_flip) \
+ o.x = pos.x + mySize.x - stringwidth(s, true, fontsize); \
+ drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL); \
+ o.y += fontsize.y; \
+} while(0)
+void HUD_InfoMessages()
+{
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_infomessages) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ // always force 5:1 aspect
+ vector newSize = '0 0 0';
+ if(mySize.x/mySize.y > 5)
+ {
+ newSize.x = 5 * mySize.y;
+ newSize.y = mySize.y;
+
+ pos.x = pos.x + (mySize.x - newSize.x) / 2;
+ }
+ else
+ {
+ newSize.y = 1/5 * mySize.x;
+ newSize.x = mySize.x;
+
+ pos.y = pos.y + (mySize.y - newSize.y) / 2;
+ }
+
+ mySize = newSize;
+ entity tm;
+ vector o;
+ o = pos;
+
+ vector fontsize;
+ fontsize = '0.20 0.20 0' * mySize.y;
+
+ float a;
+ a = panel_fg_alpha;
+
+ string s;
+ if(!autocvar__hud_configure)
+ {
+ if(spectatee_status && !intermission)
+ {
+ a = 1;
+ if(spectatee_status == -1)
+ s = _("^1Observing");
+ else
+ s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(current_player));
+ drawInfoMessage(s);
+
+ if(spectatee_status == -1)
+ s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire"));
+ else
+ s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
+ drawInfoMessage(s);
+
+ if(spectatee_status == -1)
+ s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev"));
+ else
+ s = sprintf(_("^1Press ^3%s^1 to observe"), getcommandkey("secondary fire", "+fire2"));
+ drawInfoMessage(s);
+
+ s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info"));
+ drawInfoMessage(s);
+
+ if(gametype == MAPINFO_TYPE_LMS)
+ {
+ entity sk;
+ sk = playerslots[player_localnum];
+ if(sk.(scores[ps_primary]) >= 666)
+ s = _("^1Match has already begun");
+ else if(sk.(scores[ps_primary]) > 0)
+ s = _("^1You have no more lives left");
+ else
+ s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
+ }
+ else
+ s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump"));
+ drawInfoMessage(s);
+
+ //show restart countdown:
+ if (time < STAT(GAMESTARTTIME)) {
+ float countdown;
+ //we need to ceil, otherwise the countdown would be off by .5 when using round()
+ countdown = ceil(STAT(GAMESTARTTIME) - time);
+ s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown);
+ drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL);
+ o.y += fontsize.y;
+ }
+ }
+ if(warmup_stage && !intermission)
+ {
+ s = _("^2Currently in ^1warmup^2 stage!");
+ drawInfoMessage(s);
+ }
+
+ string blinkcolor;
+ if(time % 1 >= 0.5)
+ blinkcolor = "^1";
+ else
+ blinkcolor = "^3";
+
+ if(ready_waiting && !intermission && !spectatee_status)
+ {
+ if(ready_waiting_for_me)
+ {
+ if(warmup_stage)
+ s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+ else
+ s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor);
+ }
+ else
+ {
+ if(warmup_stage)
+ s = _("^2Waiting for others to ready up to end warmup...");
+ else
+ s = _("^2Waiting for others to ready up...");
+ }
+ drawInfoMessage(s);
+ }
+ else if(warmup_stage && !intermission && !spectatee_status)
+ {
+ s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready"));
+ drawInfoMessage(s);
+ }
+
+ if(teamplay && !intermission && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger)
+ {
+ float ts_min = 0, ts_max = 0;
+ tm = teams.sort_next;
+ if (tm)
+ {
+ for (; tm.sort_next; tm = tm.sort_next)
+ {
+ if(!tm.team_size || tm.team == NUM_SPECTATOR)
+ continue;
+ if(!ts_min) ts_min = tm.team_size;
+ else ts_min = min(ts_min, tm.team_size);
+ if(!ts_max) ts_max = tm.team_size;
+ else ts_max = max(ts_max, tm.team_size);
+ }
+ if ((ts_max - ts_min) > 1)
+ {
+ s = strcat(blinkcolor, _("Teamnumbers are unbalanced!"));
+ tm = GetTeam(myteam, false);
+ if (tm)
+ if (tm.team != NUM_SPECTATOR)
+ if (tm.team_size == ts_max)
+ s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor));
+ drawInfoMessage(s);
+ }
+ }
+ }
+ }
+ else
+ {
+ s = _("^7Press ^3ESC ^7to show HUD options.");
+ drawInfoMessage(s);
+ s = _("^3Doubleclick ^7a panel for panel-specific options.");
+ drawInfoMessage(s);
+ s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and");
+ drawInfoMessage(s);
+ s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments.");
+ drawInfoMessage(s);
+ }
+}
--- /dev/null
+// Minigame
+
+#include "../../../common/minigames/cl_minigames_hud.qc"
--- /dev/null
+// Mod icons panel (#10)
+
+bool mod_active; // is there any active mod icon?
+
+void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i)
+{
+ int stat = -1;
+ string pic = "";
+ vector color = '0 0 0';
+ switch(i)
+ {
+ case 0:
+ stat = getstati(STAT_REDALIVE);
+ pic = "player_red.tga";
+ color = '1 0 0';
+ break;
+ case 1:
+ stat = getstati(STAT_BLUEALIVE);
+ pic = "player_blue.tga";
+ color = '0 0 1';
+ break;
+ case 2:
+ stat = getstati(STAT_YELLOWALIVE);
+ pic = "player_yellow.tga";
+ color = '1 1 0';
+ break;
+ default:
+ case 3:
+ stat = getstati(STAT_PINKALIVE);
+ pic = "player_pink.tga";
+ color = '1 0 1';
+ break;
+ }
+
+ if(mySize.x/mySize.y > aspect_ratio)
+ {
+ i = aspect_ratio * mySize.y;
+ myPos.x = myPos.x + (mySize.x - i) / 2;
+ mySize.x = i;
+ }
+ else
+ {
+ i = 1/aspect_ratio * mySize.x;
+ myPos.y = myPos.y + (mySize.y - i) / 2;
+ mySize.y = i;
+ }
+
+ if(layout)
+ {
+ drawpic_aspect_skin(myPos, pic, eX * 0.7 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(myPos + eX * 0.7 * mySize.x, ftos(stat), eX * 0.3 * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ else
+ drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+}
+
+// Clan Arena and Freeze Tag HUD modicons
+void HUD_Mod_CA(vector myPos, vector mySize)
+{
+ mod_active = 1; // required in each mod function that always shows something
+
+ int layout;
+ if(gametype == MAPINFO_TYPE_CA)
+ layout = autocvar_hud_panel_modicons_ca_layout;
+ else //if(gametype == MAPINFO_TYPE_FREEZETAG)
+ layout = autocvar_hud_panel_modicons_freezetag_layout;
+ int rows, columns;
+ float aspect_ratio;
+ aspect_ratio = (layout) ? 2 : 1;
+ rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
+ columns = ceil(team_count/rows);
+
+ int i;
+ float row = 0, column = 0;
+ vector pos, itemSize;
+ itemSize = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
+ for(i=0; i<team_count; ++i)
+ {
+ pos = myPos + eX * column * itemSize.x + eY * row * itemSize.y;
+
+ DrawCAItem(pos, itemSize, aspect_ratio, layout, i);
+
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ ++column;
+ }
+ }
+}
+
+// CTF HUD modicon section
+int redflag_prevframe, blueflag_prevframe, yellowflag_prevframe, pinkflag_prevframe, neutralflag_prevframe; // status during previous frame
+int redflag_prevstatus, blueflag_prevstatus, yellowflag_prevstatus, pinkflag_prevstatus, neutralflag_prevstatus; // last remembered status
+float redflag_statuschange_time, blueflag_statuschange_time, yellowflag_statuschange_time, pinkflag_statuschange_time, neutralflag_statuschange_time; // time when the status changed
+
+void HUD_Mod_CTF_Reset()
+{
+ redflag_prevstatus = blueflag_prevstatus = yellowflag_prevstatus = pinkflag_prevstatus = neutralflag_prevstatus = 0;
+ redflag_prevframe = blueflag_prevframe = yellowflag_prevframe = pinkflag_prevframe = neutralflag_prevframe = 0;
+ redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_statuschange_time = neutralflag_statuschange_time = 0;
+}
+
+void HUD_Mod_CTF(vector pos, vector mySize)
+{
+ vector redflag_pos, blueflag_pos, yellowflag_pos, pinkflag_pos, neutralflag_pos;
+ vector flag_size;
+ float f; // every function should have that
+
+ int redflag, blueflag, yellowflag, pinkflag, neutralflag; // current status
+ float redflag_statuschange_elapsedtime, blueflag_statuschange_elapsedtime, yellowflag_statuschange_elapsedtime, pinkflag_statuschange_elapsedtime, neutralflag_statuschange_elapsedtime; // time since the status changed
+ bool ctf_oneflag; // one-flag CTF mode enabled/disabled
+ int stat_items = getstati(STAT_CTF_FLAGSTATUS, 0, 24);
+ float fs, fs2, fs3, size1, size2;
+ vector e1, e2;
+
+ redflag = (stat_items/CTF_RED_FLAG_TAKEN) & 3;
+ blueflag = (stat_items/CTF_BLUE_FLAG_TAKEN) & 3;
+ yellowflag = (stat_items/CTF_YELLOW_FLAG_TAKEN) & 3;
+ pinkflag = (stat_items/CTF_PINK_FLAG_TAKEN) & 3;
+ neutralflag = (stat_items/CTF_NEUTRAL_FLAG_TAKEN) & 3;
+
+ ctf_oneflag = (stat_items & CTF_FLAG_NEUTRAL);
+
+ mod_active = (redflag || blueflag || yellowflag || pinkflag || neutralflag);
+
+ if (autocvar__hud_configure) {
+ redflag = 1;
+ blueflag = 2;
+ if (team_count >= 3)
+ yellowflag = 2;
+ if (team_count >= 4)
+ pinkflag = 3;
+ ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor?
+ }
+
+ // when status CHANGES, set old status into prevstatus and current status into status
+ #define X(team) do { \
+ if (team##flag != team##flag_prevframe) { \
+ team##flag_statuschange_time = time; \
+ team##flag_prevstatus = team##flag_prevframe; \
+ team##flag_prevframe = team##flag; \
+ } \
+ team##flag_statuschange_elapsedtime = time - team##flag_statuschange_time; \
+ } while (0)
+ X(red);
+ X(blue);
+ X(yellow);
+ X(pink);
+ X(neutral);
+ #undef X
+
+ const float BLINK_FACTOR = 0.15;
+ const float BLINK_BASE = 0.85;
+ // note:
+ // RMS = sqrt(BLINK_BASE^2 + 0.5 * BLINK_FACTOR^2)
+ // thus
+ // BLINK_BASE = sqrt(RMS^2 - 0.5 * BLINK_FACTOR^2)
+ // ensure RMS == 1
+ const float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz
+
+ #define X(team, cond) \
+ string team##_icon, team##_icon_prevstatus; \
+ int team##_alpha, team##_alpha_prevstatus; \
+ team##_alpha = team##_alpha_prevstatus = 1; \
+ do { \
+ switch (team##flag) { \
+ case 1: team##_icon = "flag_" #team "_taken"; break; \
+ case 2: team##_icon = "flag_" #team "_lost"; break; \
+ case 3: team##_icon = "flag_" #team "_carrying"; team##_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \
+ default: \
+ if ((stat_items & CTF_SHIELDED) && (cond)) { \
+ team##_icon = "flag_" #team "_shielded"; \
+ } else { \
+ team##_icon = string_null; \
+ } \
+ break; \
+ } \
+ switch (team##flag_prevstatus) { \
+ case 1: team##_icon_prevstatus = "flag_" #team "_taken"; break; \
+ case 2: team##_icon_prevstatus = "flag_" #team "_lost"; break; \
+ case 3: team##_icon_prevstatus = "flag_" #team "_carrying"; team##_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \
+ default: \
+ if (team##flag == 3) { \
+ team##_icon_prevstatus = "flag_" #team "_carrying"; /* make it more visible */\
+ } else if((stat_items & CTF_SHIELDED) && (cond)) { \
+ team##_icon_prevstatus = "flag_" #team "_shielded"; \
+ } else { \
+ team##_icon_prevstatus = string_null; \
+ } \
+ break; \
+ } \
+ } while (0)
+ X(red, myteam != NUM_TEAM_1);
+ X(blue, myteam != NUM_TEAM_2);
+ X(yellow, myteam != NUM_TEAM_3);
+ X(pink, myteam != NUM_TEAM_4);
+ X(neutral, true);
+ #undef X
+
+ if (ctf_oneflag) {
+ // hacky, but these aren't needed
+ red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null;
+ fs = fs2 = fs3 = 1;
+ } else switch (team_count) {
+ default:
+ case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break;
+ case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break;
+ case 4: fs = 0.75; fs2 = 0.25; fs3 = 0.5; break;
+ }
+
+ if (mySize_x > mySize_y) {
+ size1 = mySize_x;
+ size2 = mySize_y;
+ e1 = eX;
+ e2 = eY;
+ } else {
+ size1 = mySize_y;
+ size2 = mySize_x;
+ e1 = eY;
+ e2 = eX;
+ }
+
+ switch (myteam) {
+ default:
+ case NUM_TEAM_1: {
+ redflag_pos = pos;
+ blueflag_pos = pos + eX * fs2 * size1;
+ yellowflag_pos = pos - eX * fs2 * size1;
+ pinkflag_pos = pos + eX * fs3 * size1;
+ break;
+ }
+ case NUM_TEAM_2: {
+ redflag_pos = pos + eX * fs2 * size1;
+ blueflag_pos = pos;
+ yellowflag_pos = pos - eX * fs2 * size1;
+ pinkflag_pos = pos + eX * fs3 * size1;
+ break;
+ }
+ case NUM_TEAM_3: {
+ redflag_pos = pos + eX * fs3 * size1;
+ blueflag_pos = pos - eX * fs2 * size1;
+ yellowflag_pos = pos;
+ pinkflag_pos = pos + eX * fs2 * size1;
+ break;
+ }
+ case NUM_TEAM_4: {
+ redflag_pos = pos - eX * fs2 * size1;
+ blueflag_pos = pos + eX * fs3 * size1;
+ yellowflag_pos = pos + eX * fs2 * size1;
+ pinkflag_pos = pos;
+ break;
+ }
+ }
+ neutralflag_pos = pos;
+ flag_size = e1 * fs * size1 + e2 * size2;
+
+ #define X(team) do { \
+ f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \
+ if (team##_icon_prevstatus && f < 1) \
+ drawpic_aspect_skin_expanding(team##flag_pos, team##_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * team##_alpha_prevstatus, DRAWFLAG_NORMAL, f); \
+ if (team##_icon) \
+ drawpic_aspect_skin(team##flag_pos, team##_icon, flag_size, '1 1 1', panel_fg_alpha * team##_alpha * f, DRAWFLAG_NORMAL); \
+ } while (0)
+ X(red);
+ X(blue);
+ X(yellow);
+ X(pink);
+ X(neutral);
+ #undef X
+}
+
+// Keyhunt HUD modicon section
+vector KH_SLOTS[4];
+
+void HUD_Mod_KH(vector pos, vector mySize)
+{
+ mod_active = 1; // keyhunt should never hide the mod icons panel
+
+ // Read current state
+
+ int state = STAT(KH_KEYS);
+ int i, key_state;
+ int all_keys, team1_keys, team2_keys, team3_keys, team4_keys, dropped_keys, carrying_keys;
+ all_keys = team1_keys = team2_keys = team3_keys = team4_keys = dropped_keys = carrying_keys = 0;
+
+ for(i = 0; i < 4; ++i)
+ {
+ key_state = (bitshift(state, i * -5) & 31) - 1;
+
+ if(key_state == -1)
+ continue;
+
+ if(key_state == 30)
+ {
+ ++carrying_keys;
+ key_state = myteam;
+ }
+
+ switch(key_state)
+ {
+ case NUM_TEAM_1: ++team1_keys; break;
+ case NUM_TEAM_2: ++team2_keys; break;
+ case NUM_TEAM_3: ++team3_keys; break;
+ case NUM_TEAM_4: ++team4_keys; break;
+ case 29: ++dropped_keys; break;
+ }
+
+ ++all_keys;
+ }
+
+ // Calculate slot measurements
+
+ vector slot_size;
+
+ if(all_keys == 4 && mySize.x * 0.5 < mySize.y && mySize.y * 0.5 < mySize.x)
+ {
+ // Quadratic arrangement
+ slot_size = eX * mySize.x * 0.5 + eY * mySize.y * 0.5;
+ KH_SLOTS[0] = pos;
+ KH_SLOTS[1] = pos + eX * slot_size.x;
+ KH_SLOTS[2] = pos + eY * slot_size.y;
+ KH_SLOTS[3] = pos + eX * slot_size.x + eY * slot_size.y;
+ }
+ else
+ {
+ if(mySize.x > mySize.y)
+ {
+ // Horizontal arrangement
+ slot_size = eX * mySize.x / all_keys + eY * mySize.y;
+ for(i = 0; i < all_keys; ++i)
+ KH_SLOTS[i] = pos + eX * slot_size.x * i;
+ }
+ else
+ {
+ // Vertical arrangement
+ slot_size = eX * mySize.x + eY * mySize.y / all_keys;
+ for(i = 0; i < all_keys; ++i)
+ KH_SLOTS[i] = pos + eY * slot_size.y * i;
+ }
+ }
+
+ // Make icons blink in case of RUN HERE
+
+ float blink = 0.6 + sin(2*M_PI*time) / 2.5; // Oscillate between 0.2 and 1
+ float alpha;
+ alpha = 1;
+
+ if(carrying_keys)
+ switch(myteam)
+ {
+ case NUM_TEAM_1: if(team1_keys == all_keys) alpha = blink; break;
+ case NUM_TEAM_2: if(team2_keys == all_keys) alpha = blink; break;
+ case NUM_TEAM_3: if(team3_keys == all_keys) alpha = blink; break;
+ case NUM_TEAM_4: if(team4_keys == all_keys) alpha = blink; break;
+ }
+
+ // Draw icons
+
+ i = 0;
+
+ while(team1_keys--)
+ if(myteam == NUM_TEAM_1 && carrying_keys)
+ {
+ drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
+ --carrying_keys;
+ }
+ else
+ drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
+
+ while(team2_keys--)
+ if(myteam == NUM_TEAM_2 && carrying_keys)
+ {
+ drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
+ --carrying_keys;
+ }
+ else
+ drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
+
+ while(team3_keys--)
+ if(myteam == NUM_TEAM_3 && carrying_keys)
+ {
+ drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
+ --carrying_keys;
+ }
+ else
+ drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
+
+ while(team4_keys--)
+ if(myteam == NUM_TEAM_4 && carrying_keys)
+ {
+ drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
+ --carrying_keys;
+ }
+ else
+ drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
+
+ while(dropped_keys--)
+ drawpic_aspect_skin(KH_SLOTS[i++], "kh_dropped", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
+}
+
+// Keepaway HUD mod icon
+int kaball_prevstatus; // last remembered status
+float kaball_statuschange_time; // time when the status changed
+
+// we don't need to reset for keepaway since it immediately
+// autocorrects prevstatus as to if the player has the ball or not
+
+void HUD_Mod_Keepaway(vector pos, vector mySize)
+{
+ mod_active = 1; // keepaway should always show the mod HUD
+
+ float BLINK_FACTOR = 0.15;
+ float BLINK_BASE = 0.85;
+ float BLINK_FREQ = 5;
+ float kaball_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
+
+ int stat_items = getstati(STAT_ITEMS, 0, 24);
+ int kaball = (stat_items/IT_KEY1) & 1;
+
+ if(kaball != kaball_prevstatus)
+ {
+ kaball_statuschange_time = time;
+ kaball_prevstatus = kaball;
+ }
+
+ vector kaball_pos, kaball_size;
+
+ if(mySize.x > mySize.y) {
+ kaball_pos = pos + eX * 0.25 * mySize.x;
+ kaball_size = eX * 0.5 * mySize.x + eY * mySize.y;
+ } else {
+ kaball_pos = pos + eY * 0.25 * mySize.y;
+ kaball_size = eY * 0.5 * mySize.y + eX * mySize.x;
+ }
+
+ float kaball_statuschange_elapsedtime = time - kaball_statuschange_time;
+ float f = bound(0, kaball_statuschange_elapsedtime*2, 1);
+
+ if(kaball_prevstatus && f < 1)
+ drawpic_aspect_skin_expanding(kaball_pos, "keepawayball_carrying", kaball_size, '1 1 1', panel_fg_alpha * kaball_alpha, DRAWFLAG_NORMAL, f);
+
+ if(kaball)
+ drawpic_aspect_skin(pos, "keepawayball_carrying", eX * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha * kaball_alpha * f, DRAWFLAG_NORMAL);
+}
+
+
+// Nexball HUD mod icon
+void HUD_Mod_NexBall(vector pos, vector mySize)
+{
+ float nb_pb_starttime, dt, p;
+ int stat_items;
+
+ stat_items = getstati(STAT_ITEMS, 0, 24);
+ nb_pb_starttime = getstatf(STAT_NB_METERSTART);
+
+ if (stat_items & IT_KEY1)
+ mod_active = 1;
+ else
+ mod_active = 0;
+
+ //Manage the progress bar if any
+ if (nb_pb_starttime > 0)
+ {
+ dt = (time - nb_pb_starttime) % nb_pb_period;
+ // one period of positive triangle
+ p = 2 * dt / nb_pb_period;
+ if (p > 1)
+ p = 2 - p;
+
+ HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", p, (mySize.x <= mySize.y), 0, autocvar_hud_progressbar_nexball_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+
+ if (stat_items & IT_KEY1)
+ drawpic_aspect_skin(pos, "nexball_carrying", eX * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+}
+
+// Race/CTS HUD mod icons
+float crecordtime_prev; // last remembered crecordtime
+float crecordtime_change_time; // time when crecordtime last changed
+float srecordtime_prev; // last remembered srecordtime
+float srecordtime_change_time; // time when srecordtime last changed
+
+float race_status_time;
+int race_status_prev;
+string race_status_name_prev;
+void HUD_Mod_Race(vector pos, vector mySize)
+{
+ mod_active = 1; // race should never hide the mod icons panel
+ entity me;
+ me = playerslots[player_localnum];
+ float t, score;
+ float f; // yet another function has this
+ score = me.(scores[ps_primary]);
+
+ if(!(scores_flags[ps_primary] & SFL_TIME) || teamplay) // race/cts record display on HUD
+ return; // no records in the actual race
+
+ // clientside personal record
+ string rr;
+ if(gametype == MAPINFO_TYPE_CTS)
+ rr = CTS_RECORD;
+ else
+ rr = RACE_RECORD;
+ t = stof(db_get(ClientProgsDB, strcat(shortmapname, rr, "time")));
+
+ if(score && (score < t || !t)) {
+ db_put(ClientProgsDB, strcat(shortmapname, rr, "time"), ftos(score));
+ if(autocvar_cl_autodemo_delete_keeprecords)
+ {
+ f = autocvar_cl_autodemo_delete;
+ f &= ~1;
+ cvar_set("cl_autodemo_delete", ftos(f)); // don't delete demo with new record!
+ }
+ }
+
+ if(t != crecordtime_prev) {
+ crecordtime_prev = t;
+ crecordtime_change_time = time;
+ }
+
+ vector textPos, medalPos;
+ float squareSize;
+ if(mySize.x > mySize.y) {
+ // text on left side
+ squareSize = min(mySize.y, mySize.x/2);
+ textPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eY * 0.5 * (mySize.y - squareSize);
+ medalPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eX * 0.5 * mySize.x + eY * 0.5 * (mySize.y - squareSize);
+ } else {
+ // text on top
+ squareSize = min(mySize.x, mySize.y/2);
+ textPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eX * 0.5 * (mySize.x - squareSize);
+ medalPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eY * 0.5 * mySize.y + eX * 0.5 * (mySize.x - squareSize);
+ }
+
+ f = time - crecordtime_change_time;
+
+ if (f > 1) {
+ drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ } else {
+ drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect_expanding(pos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
+ drawstring_aspect_expanding(pos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
+ }
+
+ // server record
+ t = race_server_record;
+ if(t != srecordtime_prev) {
+ srecordtime_prev = t;
+ srecordtime_change_time = time;
+ }
+ f = time - srecordtime_change_time;
+
+ if (f > 1) {
+ drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ } else {
+ drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect_expanding(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
+ drawstring_aspect_expanding(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f);
+ }
+
+ if (race_status != race_status_prev || race_status_name != race_status_name_prev) {
+ race_status_time = time + 5;
+ race_status_prev = race_status;
+ if (race_status_name_prev)
+ strunzone(race_status_name_prev);
+ race_status_name_prev = strzone(race_status_name);
+ }
+
+ // race "awards"
+ float a;
+ a = bound(0, race_status_time - time, 1);
+
+ string s;
+ s = textShortenToWidth(race_status_name, squareSize, '1 1 0' * 0.1 * squareSize, stringwidth_colors);
+
+ float rank;
+ if(race_status > 0)
+ rank = race_CheckName(race_status_name);
+ else
+ rank = 0;
+ string rankname;
+ rankname = count_ordinal(rank);
+
+ vector namepos;
+ namepos = medalPos + '0 0.8 0' * squareSize;
+ vector rankpos;
+ rankpos = medalPos + '0 0.15 0' * squareSize;
+
+ if(race_status == 0)
+ drawpic_aspect_skin(medalPos, "race_newfail", '1 1 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ else if(race_status == 1) {
+ drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newtime", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ } else if(race_status == 2) {
+ if(race_status_name == GetPlayerName(player_localnum) || !race_myrank || race_myrank < rank)
+ drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankgreen", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ else
+ drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankyellow", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ } else if(race_status == 3) {
+ drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrecordserver", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ }
+
+ if (race_status_time - time <= 0) {
+ race_status_prev = -1;
+ race_status = -1;
+ if(race_status_name)
+ strunzone(race_status_name);
+ race_status_name = string_null;
+ if(race_status_name_prev)
+ strunzone(race_status_name_prev);
+ race_status_name_prev = string_null;
+ }
+}
+
+void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i)
+{
+ float stat = -1;
+ string pic = "";
+ vector color = '0 0 0';
+ switch(i)
+ {
+ case 0:
+ stat = getstatf(STAT_DOM_PPS_RED);
+ pic = "dom_icon_red";
+ color = '1 0 0';
+ break;
+ case 1:
+ stat = getstatf(STAT_DOM_PPS_BLUE);
+ pic = "dom_icon_blue";
+ color = '0 0 1';
+ break;
+ case 2:
+ stat = getstatf(STAT_DOM_PPS_YELLOW);
+ pic = "dom_icon_yellow";
+ color = '1 1 0';
+ break;
+ default:
+ case 3:
+ stat = getstatf(STAT_DOM_PPS_PINK);
+ pic = "dom_icon_pink";
+ color = '1 0 1';
+ break;
+ }
+ float pps_ratio = stat / getstatf(STAT_DOM_TOTAL_PPS);
+
+ if(mySize.x/mySize.y > aspect_ratio)
+ {
+ i = aspect_ratio * mySize.y;
+ myPos.x = myPos.x + (mySize.x - i) / 2;
+ mySize.x = i;
+ }
+ else
+ {
+ i = 1/aspect_ratio * mySize.x;
+ myPos.y = myPos.y + (mySize.y - i) / 2;
+ mySize.y = i;
+ }
+
+ if (layout) // show text too
+ {
+ //draw the text
+ color *= 0.5 + pps_ratio * (1 - 0.5); // half saturated color at min, full saturated at max
+ if (layout == 2) // average pps
+ drawstring_aspect(myPos + eX * mySize.y, ftos_decimals(stat, 2), eX * (2/3) * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ else // percentage of average pps
+ drawstring_aspect(myPos + eX * mySize.y, strcat( ftos(floor(pps_ratio*100 + 0.5)), "%" ), eX * (2/3) * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+
+ //draw the icon
+ drawpic_aspect_skin(myPos, pic, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ if (stat > 0)
+ {
+ drawsetcliparea(myPos.x, myPos.y + mySize.y * (1 - pps_ratio), mySize.y, mySize.y * pps_ratio);
+ drawpic_aspect_skin(myPos, strcat(pic, "-highlighted"), '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawresetcliparea();
+ }
+}
+
+void HUD_Mod_Dom(vector myPos, vector mySize)
+{
+ mod_active = 1; // required in each mod function that always shows something
+
+ int layout = autocvar_hud_panel_modicons_dom_layout;
+ int rows, columns;
+ float aspect_ratio;
+ aspect_ratio = (layout) ? 3 : 1;
+ rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
+ columns = ceil(team_count/rows);
+
+ int i;
+ float row = 0, column = 0;
+ vector pos, itemSize;
+ itemSize = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
+ for(i=0; i<team_count; ++i)
+ {
+ pos = myPos + eX * column * itemSize.x + eY * row * itemSize.y;
+
+ DrawDomItem(pos, itemSize, aspect_ratio, layout, i);
+
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ ++column;
+ }
+ }
+}
+
+void HUD_ModIcons_SetFunc()
+{
+ switch(gametype)
+ {
+ case MAPINFO_TYPE_KEYHUNT: HUD_ModIcons_GameType = HUD_Mod_KH; break;
+ case MAPINFO_TYPE_CTF: HUD_ModIcons_GameType = HUD_Mod_CTF; break;
+ case MAPINFO_TYPE_NEXBALL: HUD_ModIcons_GameType = HUD_Mod_NexBall; break;
+ case MAPINFO_TYPE_CTS:
+ case MAPINFO_TYPE_RACE: HUD_ModIcons_GameType = HUD_Mod_Race; break;
+ case MAPINFO_TYPE_CA:
+ case MAPINFO_TYPE_FREEZETAG: HUD_ModIcons_GameType = HUD_Mod_CA; break;
+ case MAPINFO_TYPE_DOMINATION: HUD_ModIcons_GameType = HUD_Mod_Dom; break;
+ case MAPINFO_TYPE_KEEPAWAY: HUD_ModIcons_GameType = HUD_Mod_Keepaway; break;
+ }
+}
+
+int mod_prev; // previous state of mod_active to check for a change
+float mod_alpha;
+float mod_change; // "time" when mod_active changed
+
+void HUD_ModIcons()
+{
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_modicons) return;
+ if(!HUD_ModIcons_GameType) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+
+ draw_beginBoldFont();
+
+ if(mod_active != mod_prev) {
+ mod_change = time;
+ mod_prev = mod_active;
+ }
+
+ if(mod_active || autocvar__hud_configure)
+ mod_alpha = bound(0, (time - mod_change) * 2, 1);
+ else
+ mod_alpha = bound(0, 1 - (time - mod_change) * 2, 1);
+
+ if(mod_alpha)
+ HUD_Panel_DrawBg(mod_alpha);
+
+ if(panel_bg_padding)
+ {
+ panel_pos += '1 1 0' * panel_bg_padding;
+ panel_size -= '2 2 0' * panel_bg_padding;
+ }
+
+ if(autocvar__hud_configure)
+ HUD_Mod_CTF(panel_pos, panel_size);
+ else
+ HUD_ModIcons_GameType(panel_pos, panel_size);
+
+ draw_endBoldFont();
+}
--- /dev/null
+// Notification area (#4)
+
+void HUD_Notify_Push(string icon, string attacker, string victim)
+{
+ if (icon == "")
+ return;
+
+ ++notify_count;
+ --notify_index;
+
+ if (notify_index == -1)
+ notify_index = NOTIFY_MAX_ENTRIES-1;
+
+ // Free old strings
+ if (notify_attackers[notify_index])
+ strunzone(notify_attackers[notify_index]);
+
+ if (notify_victims[notify_index])
+ strunzone(notify_victims[notify_index]);
+
+ if (notify_icons[notify_index])
+ strunzone(notify_icons[notify_index]);
+
+ // Allocate new strings
+ if (victim != "")
+ {
+ notify_attackers[notify_index] = strzone(attacker);
+ notify_victims[notify_index] = strzone(victim);
+ }
+ else
+ {
+ // In case of a notification without a victim, the attacker
+ // is displayed on the victim's side. Instead of special
+ // treatment later on, we can simply switch them here.
+ notify_attackers[notify_index] = string_null;
+ notify_victims[notify_index] = strzone(attacker);
+ }
+
+ notify_icons[notify_index] = strzone(icon);
+ notify_times[notify_index] = time;
+}
+
+void HUD_Notify()
+{
+ if (!autocvar__hud_configure)
+ if (!autocvar_hud_panel_notify)
+ return;
+
+ HUD_Panel_UpdateCvars();
+ HUD_Panel_DrawBg(1);
+
+ if (!autocvar__hud_configure)
+ if (notify_count == 0)
+ return;
+
+ vector pos, size;
+ pos = panel_pos;
+ size = panel_size;
+
+ if (panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ size -= '2 2 0' * panel_bg_padding;
+ }
+
+ float fade_start = max(0, autocvar_hud_panel_notify_time);
+ float fade_time = max(0, autocvar_hud_panel_notify_fadetime);
+ float icon_aspect = max(1, autocvar_hud_panel_notify_icon_aspect);
+
+ int entry_count = bound(1, floor(NOTIFY_MAX_ENTRIES * size.y / size.x), NOTIFY_MAX_ENTRIES);
+ float entry_height = size.y / entry_count;
+
+ float panel_width_half = size.x * 0.5;
+ float icon_width_half = entry_height * icon_aspect / 2;
+ float name_maxwidth = panel_width_half - icon_width_half - size.x * NOTIFY_ICON_MARGIN;
+
+ vector font_size = '0.5 0.5 0' * entry_height * autocvar_hud_panel_notify_fontsize;
+ vector icon_size = (eX * icon_aspect + eY) * entry_height;
+ vector icon_left = eX * (panel_width_half - icon_width_half);
+ vector attacker_right = eX * name_maxwidth;
+ vector victim_left = eX * (size.x - name_maxwidth);
+
+ vector attacker_pos, victim_pos, icon_pos;
+ string attacker, victim, icon;
+ int i, j, count, step, limit;
+ float alpha;
+
+ if (autocvar_hud_panel_notify_flip)
+ {
+ // Order items from the top down
+ i = 0;
+ step = +1;
+ limit = entry_count;
+ }
+ else
+ {
+ // Order items from the bottom up
+ i = entry_count - 1;
+ step = -1;
+ limit = -1;
+ }
+
+ for (j = notify_index, count = 0; i != limit; i += step, ++j, ++count)
+ {
+ if(autocvar__hud_configure)
+ {
+ attacker = sprintf(_("Player %d"), count + 1);
+ victim = sprintf(_("Player %d"), count + 2);
+ icon = get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).model2;
+ alpha = bound(0, 1.2 - count / entry_count, 1);
+ }
+ else
+ {
+ if (j == NOTIFY_MAX_ENTRIES)
+ j = 0;
+
+ if (notify_times[j] + fade_start > time)
+ alpha = 1;
+ else if (fade_time != 0)
+ {
+ alpha = bound(0, (notify_times[j] + fade_start + fade_time - time) / fade_time, 1);
+ if (alpha == 0)
+ break;
+ }
+ else
+ break;
+
+ attacker = notify_attackers[j];
+ victim = notify_victims[j];
+ icon = notify_icons[j];
+ }
+
+ if (icon != "" && victim != "")
+ {
+ vector name_top = eY * (i * entry_height + 0.5 * (entry_height - font_size.y));
+
+ icon_pos = pos + icon_left + eY * i * entry_height;
+ drawpic_aspect_skin(icon_pos, icon, icon_size, '1 1 1', panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
+
+ victim = textShortenToWidth(victim, name_maxwidth, font_size, stringwidth_colors);
+ victim_pos = pos + victim_left + name_top;
+ drawcolorcodedstring(victim_pos, victim, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
+
+ if (attacker != "")
+ {
+ attacker = textShortenToWidth(attacker, name_maxwidth, font_size, stringwidth_colors);
+ attacker_pos = pos + attacker_right - eX * stringwidth(attacker, true, font_size) + name_top;
+ drawcolorcodedstring(attacker_pos, attacker, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
+ }
+ }
+ }
+
+ notify_count = count;
+}
--- /dev/null
+// Physics panel (#15)
+
+vector acc_prevspeed;
+float acc_prevtime, acc_avg, top_speed, top_speed_time;
+float physics_update_time, discrete_speed, discrete_acceleration;
+void HUD_Physics()
+{
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_physics) return;
+ if(spectatee_status == -1 && (autocvar_hud_panel_physics == 1 || autocvar_hud_panel_physics == 3)) return;
+ if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+
+ draw_beginBoldFont();
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ panel_pos += '1 1 0' * panel_bg_padding;
+ panel_size -= '2 2 0' * panel_bg_padding;
+ }
+
+ float acceleration_progressbar_scale = 0;
+ if(autocvar_hud_panel_physics_progressbar && autocvar_hud_panel_physics_acceleration_progressbar_scale > 1)
+ acceleration_progressbar_scale = autocvar_hud_panel_physics_acceleration_progressbar_scale;
+
+ float text_scale;
+ if (autocvar_hud_panel_physics_text_scale <= 0)
+ text_scale = 1;
+ else
+ text_scale = min(autocvar_hud_panel_physics_text_scale, 1);
+
+ //compute speed
+ float speed, conversion_factor;
+ string unit;
+
+ switch(autocvar_hud_panel_physics_speed_unit)
+ {
+ default:
+ case 1:
+ unit = _(" qu/s");
+ conversion_factor = 1.0;
+ break;
+ case 2:
+ unit = _(" m/s");
+ conversion_factor = 0.0254;
+ break;
+ case 3:
+ unit = _(" km/h");
+ conversion_factor = 0.0254 * 3.6;
+ break;
+ case 4:
+ unit = _(" mph");
+ conversion_factor = 0.0254 * 3.6 * 0.6213711922;
+ break;
+ case 5:
+ unit = _(" knots");
+ conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
+ break;
+ }
+
+ vector vel = (csqcplayer ? csqcplayer.velocity : pmove_vel);
+
+ float max_speed = floor( autocvar_hud_panel_physics_speed_max * conversion_factor + 0.5 );
+ if (autocvar__hud_configure)
+ speed = floor( max_speed * 0.65 + 0.5 );
+ else if(autocvar_hud_panel_physics_speed_vertical)
+ speed = floor( vlen(vel) * conversion_factor + 0.5 );
+ else
+ speed = floor( vlen(vel - vel.z * '0 0 1') * conversion_factor + 0.5 );
+
+ //compute acceleration
+ float acceleration, f;
+ if (autocvar__hud_configure)
+ acceleration = autocvar_hud_panel_physics_acceleration_max * 0.3;
+ else
+ {
+ // 1 m/s = 0.0254 qu/s; 1 g = 9.80665 m/s^2
+ f = time - acc_prevtime;
+ if(autocvar_hud_panel_physics_acceleration_vertical)
+ acceleration = (vlen(vel) - vlen(acc_prevspeed));
+ else
+ acceleration = (vlen(vel - '0 0 1' * vel.z) - vlen(acc_prevspeed - '0 0 1' * acc_prevspeed.z));
+
+ acceleration = acceleration * (1 / max(0.0001, f)) * (0.0254 / 9.80665);
+
+ acc_prevspeed = vel;
+ acc_prevtime = time;
+
+ if(autocvar_hud_panel_physics_acceleration_movingaverage)
+ {
+ f = bound(0, f * 10, 1);
+ acc_avg = acc_avg * (1 - f) + acceleration * f;
+ acceleration = acc_avg;
+ }
+ }
+
+ int acc_decimals = 2;
+ if(time > physics_update_time)
+ {
+ // workaround for ftos_decimals returning a negative 0
+ if(discrete_acceleration > -1 / pow(10, acc_decimals) && discrete_acceleration < 0)
+ discrete_acceleration = 0;
+ discrete_acceleration = acceleration;
+ discrete_speed = speed;
+ physics_update_time += autocvar_hud_panel_physics_update_interval;
+ }
+
+ //compute layout
+ float panel_ar = panel_size.x/panel_size.y;
+ vector speed_offset = '0 0 0', acceleration_offset = '0 0 0';
+ if (panel_ar >= 5 && !acceleration_progressbar_scale)
+ {
+ panel_size.x *= 0.5;
+ if (autocvar_hud_panel_physics_flip)
+ speed_offset.x = panel_size.x;
+ else
+ acceleration_offset.x = panel_size.x;
+ }
+ else
+ {
+ panel_size.y *= 0.5;
+ if (autocvar_hud_panel_physics_flip)
+ speed_offset.y = panel_size.y;
+ else
+ acceleration_offset.y = panel_size.y;
+ }
+ int speed_baralign, acceleration_baralign;
+ if (autocvar_hud_panel_physics_baralign == 1)
+ acceleration_baralign = speed_baralign = 1;
+ else if(autocvar_hud_panel_physics_baralign == 4)
+ acceleration_baralign = speed_baralign = 2;
+ else if (autocvar_hud_panel_physics_flip)
+ {
+ acceleration_baralign = (autocvar_hud_panel_physics_baralign == 2);
+ speed_baralign = (autocvar_hud_panel_physics_baralign == 3);
+ }
+ else
+ {
+ speed_baralign = (autocvar_hud_panel_physics_baralign == 2);
+ acceleration_baralign = (autocvar_hud_panel_physics_baralign == 3);
+ }
+ if (autocvar_hud_panel_physics_acceleration_progressbar_mode == 0)
+ acceleration_baralign = 3; //override hud_panel_physics_baralign value for acceleration
+
+ //draw speed
+ if(speed)
+ if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2)
+ HUD_Panel_DrawProgressBar(panel_pos + speed_offset, panel_size, "progressbar", speed/max_speed, 0, speed_baralign, autocvar_hud_progressbar_speed_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ vector tmp_offset = '0 0 0', tmp_size = '0 0 0';
+ if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2)
+ {
+ tmp_size.x = panel_size.x * 0.75;
+ tmp_size.y = panel_size.y * text_scale;
+ if (speed_baralign)
+ tmp_offset.x = panel_size.x - tmp_size.x;
+ //else
+ //tmp_offset_x = 0;
+ tmp_offset.y = (panel_size.y - tmp_size.y) / 2;
+ drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(discrete_speed), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+
+ //draw speed unit
+ if (speed_baralign)
+ tmp_offset.x = 0;
+ else
+ tmp_offset.x = tmp_size.x;
+ if (autocvar_hud_panel_physics_speed_unit_show)
+ {
+ //tmp_offset_y = 0;
+ tmp_size.x = panel_size.x * (1 - 0.75);
+ tmp_size.y = panel_size.y * 0.4 * text_scale;
+ tmp_offset.y = (panel_size.y * 0.4 - tmp_size.y) / 2;
+ drawstring_aspect(panel_pos + speed_offset + tmp_offset, unit, tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ }
+
+ //compute and draw top speed
+ if (autocvar_hud_panel_physics_topspeed)
+ if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2)
+ {
+ if (autocvar__hud_configure)
+ {
+ top_speed = floor( max_speed * 0.75 + 0.5 );
+ f = 1;
+ }
+ else
+ {
+ if (speed >= top_speed)
+ {
+ top_speed = speed;
+ top_speed_time = time;
+ }
+ if (top_speed != 0)
+ {
+ f = max(1, autocvar_hud_panel_physics_topspeed_time);
+ // divide by f to make it start from 1
+ f = cos( ((time - top_speed_time) / f) * PI/2 );
+ }
+ else //hide top speed 0, it would be stupid
+ f = 0;
+ }
+ if (f > 0)
+ {
+ //top speed progressbar peak
+ if(speed < top_speed)
+ if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2)
+ {
+ float peak_offsetX;
+ vector peak_size = '0 0 0';
+ if (speed_baralign == 0)
+ peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x;
+ else if (speed_baralign == 1)
+ peak_offsetX = (1 - min(top_speed, max_speed)/max_speed) * panel_size.x;
+ else // if (speed_baralign == 2)
+ peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x * 0.5;
+ peak_size.x = floor(panel_size.x * 0.01 + 1.5);
+ peak_size.y = panel_size.y;
+ if (speed_baralign == 2) // draw two peaks, on both sides
+ {
+ drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x + peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x - peak_offsetX + peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ else
+ drawfill(panel_pos + speed_offset + eX * (peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+
+ //top speed
+ tmp_offset.y = panel_size.y * 0.4;
+ tmp_size.x = panel_size.x * (1 - 0.75);
+ tmp_size.y = (panel_size.y - tmp_offset.y) * text_scale;
+ tmp_offset.y += (panel_size.y - tmp_offset.y - tmp_size.y) / 2;
+ drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(top_speed), tmp_size, '1 0 0', f * panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ else
+ top_speed = 0;
+ }
+
+ //draw acceleration
+ if(acceleration)
+ if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 3)
+ {
+ vector progressbar_color;
+ if(acceleration < 0)
+ progressbar_color = autocvar_hud_progressbar_acceleration_neg_color;
+ else
+ progressbar_color = autocvar_hud_progressbar_acceleration_color;
+
+ f = acceleration/autocvar_hud_panel_physics_acceleration_max;
+ if (autocvar_hud_panel_physics_acceleration_progressbar_nonlinear)
+ f = (f >= 0 ? sqrt(f) : -sqrt(-f));
+
+ if (acceleration_progressbar_scale) // allow progressbar to go out of panel bounds
+ {
+ tmp_size = acceleration_progressbar_scale * panel_size.x * eX + panel_size.y * eY;
+
+ if (acceleration_baralign == 1)
+ tmp_offset.x = panel_size.x - tmp_size.x;
+ else if (acceleration_baralign == 2 || acceleration_baralign == 3)
+ tmp_offset.x = (panel_size.x - tmp_size.x) / 2;
+ else
+ tmp_offset.x = 0;
+ tmp_offset.y = 0;
+ }
+ else
+ {
+ tmp_size = panel_size;
+ tmp_offset = '0 0 0';
+ }
+
+ HUD_Panel_DrawProgressBar(panel_pos + acceleration_offset + tmp_offset, tmp_size, "accelbar", f, 0, acceleration_baralign, progressbar_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+
+ if(autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 3)
+ {
+ tmp_size.x = panel_size.x;
+ tmp_size.y = panel_size.y * text_scale;
+ tmp_offset.x = 0;
+ tmp_offset.y = (panel_size.y - tmp_size.y) / 2;
+
+ drawstring_aspect(panel_pos + acceleration_offset + tmp_offset, strcat(ftos_decimals(discrete_acceleration, acc_decimals), "g"), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+
+ draw_endBoldFont();
+}
--- /dev/null
+// Powerups (#2)
+
+// Powerup item fields (reusing existing fields)
+.string message; // Human readable name
+.string netname; // Icon name
+.vector colormod; // Color
+.float count; // Time left
+.float lifetime; // Maximum time
+
+entity powerupItems;
+int powerupItemsCount;
+
+void resetPowerupItems()
+{
+ entity item;
+ for(item = powerupItems; item; item = item.chain)
+ item.count = 0;
+
+ powerupItemsCount = 0;
+}
+
+void addPowerupItem(string name, string icon, vector color, float currentTime, float lifeTime)
+{
+ if(!powerupItems)
+ powerupItems = spawn();
+
+ entity item;
+ for(item = powerupItems; item.count; item = item.chain)
+ if(!item.chain)
+ item.chain = spawn();
+
+ item.message = name;
+ item.netname = icon;
+ item.colormod = color;
+ item.count = currentTime;
+ item.lifetime = lifeTime;
+
+ ++powerupItemsCount;
+}
+
+int getPowerupItemAlign(int align, int column, int row, int columns, int rows, bool isVertical)
+{
+ if(align < 2)
+ return align;
+
+ bool isTop = isVertical && rows > 1 && row == 0;
+ bool isBottom = isVertical && rows > 1 && row == rows-1;
+ bool isLeft = !isVertical && columns > 1 && column == 0;
+ bool isRight = !isVertical && columns > 1 && column == columns-1;
+
+ if(isTop || isLeft) return (align == 2) ? 1 : 0;
+ if(isBottom || isRight) return (align == 2) ? 0 : 1;
+
+ return 2;
+}
+
+void HUD_Powerups()
+{
+ int allItems = getstati(STAT_ITEMS, 0, 24);
+ int allBuffs = getstati(STAT_BUFFS, 0, 24);
+ int strengthTime, shieldTime, superTime;
+
+ // Initialize items
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_powerups) return;
+ if(spectatee_status == -1) return;
+ if(getstati(STAT_HEALTH) <= 0) return;
+ if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return;
+
+ strengthTime = bound(0, STAT(STRENGTH_FINISHED) - time, 99);
+ shieldTime = bound(0, STAT(INVINCIBLE_FINISHED) - time, 99);
+ superTime = bound(0, getstatf(STAT_SUPERWEAPONS_FINISHED) - time, 99);
+
+ if(allItems & IT_UNLIMITED_SUPERWEAPONS)
+ superTime = 99;
+
+ // Prevent stuff to show up on mismatch that will be fixed next frame
+ if(!(allItems & IT_SUPERWEAPON))
+ superTime = 0;
+ }
+ else
+ {
+ strengthTime = 15;
+ shieldTime = 27;
+ superTime = 13;
+ allBuffs = 0;
+ }
+
+ // Add items to linked list
+ resetPowerupItems();
+
+ if(strengthTime)
+ addPowerupItem("Strength", "strength", autocvar_hud_progressbar_strength_color, strengthTime, 30);
+ if(shieldTime)
+ addPowerupItem("Shield", "shield", autocvar_hud_progressbar_shield_color, shieldTime, 30);
+ if(superTime)
+ addPowerupItem("Superweapons", "superweapons", autocvar_hud_progressbar_superweapons_color, superTime, 30);
+
+ FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA(
+ addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, getstatf(STAT_BUFF_TIME) - time, 99), 60);
+ ));
+
+ if(!powerupItemsCount)
+ return;
+
+ // Draw panel background
+ HUD_Panel_UpdateCvars();
+ HUD_Panel_DrawBg(1);
+
+ // Set drawing area
+ vector pos = panel_pos;
+ vector size = panel_size;
+ bool isVertical = size.y > size.x;
+
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ size -= '2 2 0' * panel_bg_padding;
+ }
+
+ // Find best partitioning of the drawing area
+ const float DESIRED_ASPECT = 6;
+ float aspect = 0, a;
+ int columns = 0, c;
+ int rows = 0, r;
+ int i = 1;
+
+ do
+ {
+ c = floor(powerupItemsCount / i);
+ r = ceil(powerupItemsCount / c);
+ a = isVertical ? (size.y/r) / (size.x/c) : (size.x/c) / (size.y/r);
+
+ if(i == 1 || fabs(DESIRED_ASPECT - a) < fabs(DESIRED_ASPECT - aspect))
+ {
+ aspect = a;
+ columns = c;
+ rows = r;
+ }
+ }
+ while(++i <= powerupItemsCount);
+
+ // Prevent single items from getting too wide
+ if(powerupItemsCount == 1 && aspect > DESIRED_ASPECT)
+ {
+ if(isVertical)
+ {
+ size.y *= 0.5;
+ pos.y += size.y * 0.5;
+ }
+ else
+ {
+ size.x *= 0.5;
+ pos.x += size.x * 0.5;
+ }
+ }
+
+ // Draw items from linked list
+ vector itemPos = pos;
+ vector itemSize = eX * (size.x / columns) + eY * (size.y / rows);
+ vector textColor = '1 1 1';
+
+ int fullSeconds = 0;
+ int align = 0;
+ int column = 0;
+ int row = 0;
+
+ draw_beginBoldFont();
+ for(entity item = powerupItems; item.count; item = item.chain)
+ {
+ itemPos = eX * (pos.x + column * itemSize.x) + eY * (pos.y + row * itemSize.y);
+
+ // Draw progressbar
+ if(autocvar_hud_panel_powerups_progressbar)
+ {
+ align = getPowerupItemAlign(autocvar_hud_panel_powerups_baralign, column, row, columns, rows, isVertical);
+ HUD_Panel_DrawProgressBar(itemPos, itemSize, "progressbar", item.count / item.lifetime, isVertical, align, item.colormod, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+
+ // Draw icon and text
+ if(autocvar_hud_panel_powerups_text)
+ {
+ align = getPowerupItemAlign(autocvar_hud_panel_powerups_iconalign, column, row, columns, rows, isVertical);
+ fullSeconds = ceil(item.count);
+ textColor = '0.6 0.6 0.6' + (item.colormod * 0.4);
+
+ if(item.count > 1)
+ DrawNumIcon(itemPos, itemSize, fullSeconds, item.netname, isVertical, align, textColor, panel_fg_alpha);
+ if(item.count <= 5)
+ DrawNumIcon_expanding(itemPos, itemSize, fullSeconds, item.netname, isVertical, align, textColor, panel_fg_alpha, bound(0, (fullSeconds - item.count) / 0.5, 1));
+ }
+
+ // Determine next section
+ if(isVertical)
+ {
+ if(++column >= columns)
+ {
+ column = 0;
+ ++row;
+ }
+ }
+ else
+ {
+ if(++row >= rows)
+ {
+ row = 0;
+ ++column;
+ }
+ }
+ }
+ draw_endBoldFont();
+}
--- /dev/null
+/** Draw pressed keys (#11) */
+void HUD_PressedKeys()
+{
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_pressedkeys) return;
+ if(spectatee_status <= 0 && autocvar_hud_panel_pressedkeys < 2) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ // force custom aspect
+ float aspect = autocvar_hud_panel_pressedkeys_aspect;
+ if(aspect)
+ {
+ vector newSize = '0 0 0';
+ if(mySize.x/mySize.y > aspect)
+ {
+ newSize.x = aspect * mySize.y;
+ newSize.y = mySize.y;
+
+ pos.x = pos.x + (mySize.x - newSize.x) / 2;
+ }
+ else
+ {
+ newSize.y = 1/aspect * mySize.x;
+ newSize.x = mySize.x;
+
+ pos.y = pos.y + (mySize.y - newSize.y) / 2;
+ }
+ mySize = newSize;
+ }
+
+ vector keysize;
+ keysize = eX * mySize.x * (1/3.0) + eY * mySize.y * (1/(3.0 - !autocvar_hud_panel_pressedkeys_attack));
+ float pressedkeys;
+ pressedkeys = getstatf(STAT_PRESSED_KEYS);
+
+ if(autocvar_hud_panel_pressedkeys_attack)
+ {
+ drawpic_aspect_skin(pos + eX * keysize.x * 0.5, ((pressedkeys & KEY_ATCK) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawpic_aspect_skin(pos + eX * keysize.x * 1.5, ((pressedkeys & KEY_ATCK2) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ pos.y += keysize.y;
+ }
+
+ drawpic_aspect_skin(pos, ((pressedkeys & KEY_CROUCH) ? "key_crouch_inv.tga" : "key_crouch.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_FORWARD) ? "key_forward_inv.tga" : "key_forward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_JUMP) ? "key_jump_inv.tga" : "key_jump.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ pos.y += keysize.y;
+ drawpic_aspect_skin(pos, ((pressedkeys & KEY_LEFT) ? "key_left_inv.tga" : "key_left.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_BACKWARD) ? "key_backward_inv.tga" : "key_backward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_RIGHT) ? "key_right_inv.tga" : "key_right.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+}
--- /dev/null
+// QuickMenu (#23)
+
+#include "../../quickmenu.qc"
--- /dev/null
+/** Race timer (#8) */
+void HUD_RaceTimer ()
+{
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_racetimer) return;
+ if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
+ if(spectatee_status == -1) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ // always force 4:1 aspect
+ vector newSize = '0 0 0';
+ if(mySize.x/mySize.y > 4)
+ {
+ newSize.x = 4 * mySize.y;
+ newSize.y = mySize.y;
+
+ pos.x = pos.x + (mySize.x - newSize.x) / 2;
+ }
+ else
+ {
+ newSize.y = 1/4 * mySize.x;
+ newSize.x = mySize.x;
+
+ pos.y = pos.y + (mySize.y - newSize.y) / 2;
+ }
+ mySize = newSize;
+
+ float a, t;
+ string s, forcetime;
+
+ if(autocvar__hud_configure)
+ {
+ s = "0:13:37";
+ draw_beginBoldFont();
+ drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.60 0.60 0' * mySize.y), s, '0.60 0.60 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ draw_endBoldFont();
+ s = _("^1Intermediate 1 (+15.42)");
+ drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.60 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
+ s = sprintf(_("^1PENALTY: %.1f (%s)"), 2, "missing a checkpoint");
+ drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.80 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ else if(race_checkpointtime)
+ {
+ a = bound(0, 2 - (time - race_checkpointtime), 1);
+ s = "";
+ forcetime = "";
+ if(a > 0) // just hit a checkpoint?
+ {
+ if(race_checkpoint != 254)
+ {
+ if(race_time && race_previousbesttime)
+ s = MakeRaceString(race_checkpoint, TIME_DECODE(race_time) - TIME_DECODE(race_previousbesttime), 0, 0, race_previousbestname);
+ else
+ s = MakeRaceString(race_checkpoint, 0, -1, 0, race_previousbestname);
+ if(race_time)
+ forcetime = TIME_ENCODED_TOSTRING(race_time);
+ }
+ }
+ else
+ {
+ if(race_laptime && race_nextbesttime && race_nextcheckpoint != 254)
+ {
+ a = bound(0, 2 - ((race_laptime + TIME_DECODE(race_nextbesttime)) - (time + TIME_DECODE(race_penaltyaccumulator))), 1);
+ if(a > 0) // next one?
+ {
+ s = MakeRaceString(race_nextcheckpoint, (time + TIME_DECODE(race_penaltyaccumulator)) - race_laptime, TIME_DECODE(race_nextbesttime), 0, race_nextbestname);
+ }
+ }
+ }
+
+ if(s != "" && a > 0)
+ {
+ drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ }
+
+ if(race_penaltytime)
+ {
+ a = bound(0, 2 - (time - race_penaltyeventtime), 1);
+ if(a > 0)
+ {
+ s = sprintf(_("^1PENALTY: %.1f (%s)"), race_penaltytime * 0.1, race_penaltyreason);
+ drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.8 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ }
+ }
+
+ draw_beginBoldFont();
+
+ if(forcetime != "")
+ {
+ a = bound(0, (time - race_checkpointtime) / 0.5, 1);
+ drawstring_expanding(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(forcetime, false, '1 1 0' * 0.6 * mySize.y), forcetime, '1 1 0' * 0.6 * mySize.y, '1 1 1', panel_fg_alpha, 0, a);
+ }
+ else
+ a = 1;
+
+ if(race_laptime && race_checkpoint != 255)
+ {
+ s = TIME_ENCODED_TOSTRING(TIME_ENCODE(time + TIME_DECODE(race_penaltyaccumulator) - race_laptime));
+ drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.6 0.6 0' * mySize.y), s, '0.6 0.6 0' * mySize.y, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ }
+
+ draw_endBoldFont();
+ }
+ else
+ {
+ if(race_mycheckpointtime)
+ {
+ a = bound(0, 2 - (time - race_mycheckpointtime), 1);
+ s = MakeRaceString(race_mycheckpoint, TIME_DECODE(race_mycheckpointdelta), -(race_mycheckpointenemy == ""), race_mycheckpointlapsdelta, race_mycheckpointenemy);
+ drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ }
+ if(race_othercheckpointtime && race_othercheckpointenemy != "")
+ {
+ a = bound(0, 2 - (time - race_othercheckpointtime), 1);
+ s = MakeRaceString(race_othercheckpoint, -TIME_DECODE(race_othercheckpointdelta), -(race_othercheckpointenemy == ""), race_othercheckpointlapsdelta, race_othercheckpointenemy);
+ drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ }
+
+ if(race_penaltytime && !race_penaltyaccumulator)
+ {
+ t = race_penaltytime * 0.1 + race_penaltyeventtime;
+ a = bound(0, (1 + t - time), 1);
+ if(a > 0)
+ {
+ if(time < t)
+ s = sprintf(_("^1PENALTY: %.1f (%s)"), (t - time) * 0.1, race_penaltyreason);
+ else
+ s = sprintf(_("^2PENALTY: %.1f (%s)"), 0, race_penaltyreason);
+ drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ }
+ }
+ }
+}
--- /dev/null
+// Radar (#6)
+
+float HUD_Radar_Clickable()
+{
+ return hud_panel_radar_mouse && !hud_panel_radar_temp_hidden;
+}
+
+void HUD_Radar_Show_Maximized(bool doshow,float clickable)
+{
+ hud_panel_radar_maximized = doshow;
+ hud_panel_radar_temp_hidden = 0;
+
+ if ( doshow )
+ {
+ if (clickable)
+ {
+ if(autocvar_hud_cursormode)
+ setcursormode(1);
+ hud_panel_radar_mouse = 1;
+ }
+ }
+ else if ( hud_panel_radar_mouse )
+ {
+ hud_panel_radar_mouse = 0;
+ mouseClicked = 0;
+ if(autocvar_hud_cursormode)
+ if(!mv_active)
+ setcursormode(0);
+ }
+}
+void HUD_Radar_Hide_Maximized()
+{
+ HUD_Radar_Show_Maximized(false,false);
+}
+
+
+float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary)
+{
+ if(!hud_panel_radar_maximized || !hud_panel_radar_mouse ||
+ autocvar__hud_configure || mv_active)
+ return false;
+
+ if(bInputType == 3)
+ {
+ mousepos_x = nPrimary;
+ mousepos_y = nSecondary;
+ return true;
+ }
+
+ if(nPrimary == K_MOUSE1)
+ {
+ if(bInputType == 0) // key pressed
+ mouseClicked |= S_MOUSE1;
+ else if(bInputType == 1) // key released
+ mouseClicked -= (mouseClicked & S_MOUSE1);
+ }
+ else if(nPrimary == K_MOUSE2)
+ {
+ if(bInputType == 0) // key pressed
+ mouseClicked |= S_MOUSE2;
+ else if(bInputType == 1) // key released
+ mouseClicked -= (mouseClicked & S_MOUSE2);
+ }
+ else if ( nPrimary == K_ESCAPE && bInputType == 0 )
+ {
+ HUD_Radar_Hide_Maximized();
+ }
+ else
+ {
+ // allow console/use binds to work without hiding the map
+ string con_keys;
+ float keys;
+ float i;
+ con_keys = strcat(findkeysforcommand("toggleconsole", 0)," ",findkeysforcommand("+use", 0)) ;
+ keys = tokenize(con_keys); // findkeysforcommand returns data for this
+ for (i = 0; i < keys; ++i)
+ {
+ if(nPrimary == stof(argv(i)))
+ return false;
+ }
+
+ if ( getstati(STAT_HEALTH) <= 0 )
+ {
+ // Show scoreboard
+ if ( bInputType < 2 )
+ {
+ con_keys = findkeysforcommand("+showscores", 0);
+ keys = tokenize(con_keys);
+ for (i = 0; i < keys; ++i)
+ {
+ if ( nPrimary == stof(argv(i)) )
+ {
+ hud_panel_radar_temp_hidden = bInputType == 0;
+ return false;
+ }
+ }
+ }
+ }
+ else if ( bInputType == 0 )
+ HUD_Radar_Hide_Maximized();
+
+ return false;
+ }
+
+ return true;
+}
+
+void HUD_Radar_Mouse()
+{
+ if ( !hud_panel_radar_mouse ) return;
+ if(mv_active) return;
+
+ if ( intermission )
+ {
+ HUD_Radar_Hide_Maximized();
+ return;
+ }
+
+ if(mouseClicked & S_MOUSE2)
+ {
+ HUD_Radar_Hide_Maximized();
+ return;
+ }
+
+ if(!autocvar_hud_cursormode)
+ {
+ mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed;
+
+ mousepos_x = bound(0, mousepos_x, vid_conwidth);
+ mousepos_y = bound(0, mousepos_y, vid_conheight);
+ }
+
+ HUD_Panel_UpdateCvars();
+
+
+ panel_size = autocvar_hud_panel_radar_maximized_size;
+ panel_size_x = bound(0.2, panel_size_x, 1) * vid_conwidth;
+ panel_size_y = bound(0.2, panel_size_y, 1) * vid_conheight;
+ panel_pos_x = (vid_conwidth - panel_size_x) / 2;
+ panel_pos_y = (vid_conheight - panel_size_y) / 2;
+
+ if(mouseClicked & S_MOUSE1)
+ {
+ // click outside
+ if ( mousepos_x < panel_pos_x || mousepos_x > panel_pos_x + panel_size_x ||
+ mousepos_y < panel_pos_y || mousepos_y > panel_pos_y + panel_size_y )
+ {
+ HUD_Radar_Hide_Maximized();
+ return;
+ }
+ vector pos = teamradar_texcoord_to_3dcoord(teamradar_2dcoord_to_texcoord(mousepos),view_origin_z);
+ localcmd(sprintf("cmd ons_spawn %f %f %f",pos_x,pos_y,pos_z));
+
+ HUD_Radar_Hide_Maximized();
+ return;
+ }
+
+
+ const vector cursor_size = '32 32 0';
+ drawpic(mousepos-'8 4 0', strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursor_size, '1 1 1', 0.8, DRAWFLAG_NORMAL);
+}
+
+void HUD_Radar()
+{
+ if (!autocvar__hud_configure)
+ {
+ if (hud_panel_radar_maximized)
+ {
+ if (!hud_draw_maximized) return;
+ }
+ else
+ {
+ if (autocvar_hud_panel_radar == 0) return;
+ if (autocvar_hud_panel_radar != 2 && !teamplay) return;
+ if(radar_panel_modified)
+ {
+ panel.update_time = time; // forces reload of panel attributes
+ radar_panel_modified = false;
+ }
+ }
+ }
+
+ if ( hud_panel_radar_temp_hidden )
+ return;
+
+ HUD_Panel_UpdateCvars();
+
+ float f = 0;
+
+ if (hud_panel_radar_maximized && !autocvar__hud_configure)
+ {
+ panel_size = autocvar_hud_panel_radar_maximized_size;
+ panel_size.x = bound(0.2, panel_size.x, 1) * vid_conwidth;
+ panel_size.y = bound(0.2, panel_size.y, 1) * vid_conheight;
+ panel_pos.x = (vid_conwidth - panel_size.x) / 2;
+ panel_pos.y = (vid_conheight - panel_size.y) / 2;
+
+ string panel_bg;
+ panel_bg = strcat(hud_skin_path, "/border_default"); // always use the default border when maximized
+ if(precache_pic(panel_bg) == "")
+ panel_bg = "gfx/hud/default/border_default"; // fallback
+ if(!radar_panel_modified && panel_bg != panel.current_panel_bg)
+ radar_panel_modified = true;
+ if(panel.current_panel_bg)
+ strunzone(panel.current_panel_bg);
+ panel.current_panel_bg = strzone(panel_bg);
+
+ switch(hud_panel_radar_maximized_zoommode)
+ {
+ default:
+ case 0:
+ f = current_zoomfraction;
+ break;
+ case 1:
+ f = 1 - current_zoomfraction;
+ break;
+ case 2:
+ f = 0;
+ break;
+ case 3:
+ f = 1;
+ break;
+ }
+
+ switch(hud_panel_radar_maximized_rotation)
+ {
+ case 0:
+ teamradar_angle = view_angles.y - 90;
+ break;
+ default:
+ teamradar_angle = 90 * hud_panel_radar_maximized_rotation;
+ break;
+ }
+ }
+ if (!hud_panel_radar_maximized && !autocvar__hud_configure)
+ {
+ switch(hud_panel_radar_zoommode)
+ {
+ default:
+ case 0:
+ f = current_zoomfraction;
+ break;
+ case 1:
+ f = 1 - current_zoomfraction;
+ break;
+ case 2:
+ f = 0;
+ break;
+ case 3:
+ f = 1;
+ break;
+ }
+
+ switch(hud_panel_radar_rotation)
+ {
+ case 0:
+ teamradar_angle = view_angles.y - 90;
+ break;
+ default:
+ teamradar_angle = 90 * hud_panel_radar_rotation;
+ break;
+ }
+ }
+
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ int color2;
+ entity tm;
+ float scale2d, normalsize, bigsize;
+
+ teamradar_origin2d = pos + 0.5 * mySize;
+ teamradar_size2d = mySize;
+
+ if(minimapname == "")
+ return;
+
+ teamradar_loadcvars();
+
+ scale2d = vlen_maxnorm2d(mi_picmax - mi_picmin);
+ teamradar_size2d = mySize;
+
+ teamradar_extraclip_mins = teamradar_extraclip_maxs = '0 0 0'; // we always center
+
+ // pixels per world qu to match the teamradar_size2d_x range in the longest dimension
+ if((hud_panel_radar_rotation == 0 && !hud_panel_radar_maximized) || (hud_panel_radar_maximized_rotation == 0 && hud_panel_radar_maximized))
+ {
+ // max-min distance must fit the radar in any rotation
+ bigsize = vlen_minnorm2d(teamradar_size2d) * scale2d / (1.05 * vlen2d(mi_scale));
+ }
+ else
+ {
+ vector c0, c1, c2, c3, span;
+ c0 = rotate(mi_min, teamradar_angle * DEG2RAD);
+ c1 = rotate(mi_max, teamradar_angle * DEG2RAD);
+ c2 = rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD);
+ c3 = rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD);
+ span = '0 0 0';
+ span.x = max(c0_x, c1_x, c2_x, c3_x) - min(c0_x, c1_x, c2_x, c3_x);
+ span.y = max(c0_y, c1_y, c2_y, c3_y) - min(c0_y, c1_y, c2_y, c3_y);
+
+ // max-min distance must fit the radar in x=x, y=y
+ bigsize = min(
+ teamradar_size2d.x * scale2d / (1.05 * span.x),
+ teamradar_size2d.y * scale2d / (1.05 * span.y)
+ );
+ }
+
+ normalsize = vlen_maxnorm2d(teamradar_size2d) * scale2d / hud_panel_radar_scale;
+ if(bigsize > normalsize)
+ normalsize = bigsize;
+
+ teamradar_size =
+ f * bigsize
+ + (1 - f) * normalsize;
+ teamradar_origin3d_in_texcoord = teamradar_3dcoord_to_texcoord(
+ f * mi_center
+ + (1 - f) * view_origin);
+
+ drawsetcliparea(
+ pos.x,
+ pos.y,
+ mySize.x,
+ mySize.y
+ );
+
+ draw_teamradar_background(hud_panel_radar_foreground_alpha);
+
+ for(tm = world; (tm = find(tm, classname, "radarlink")); )
+ draw_teamradar_link(tm.origin, tm.velocity, tm.team);
+
+ vector coord;
+ vector brightcolor;
+ for(tm = world; (tm = findflags(tm, teamradar_icon, 0xFFFFFF)); )
+ {
+ if ( hud_panel_radar_mouse )
+ if ( tm.health > 0 )
+ if ( tm.team == myteam+1 )
+ {
+ coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(tm.origin));
+ if ( vlen(mousepos-coord) < 8 )
+ {
+ brightcolor_x = min(1,tm.teamradar_color_x*1.5);
+ brightcolor_y = min(1,tm.teamradar_color_y*1.5);
+ brightcolor_z = min(1,tm.teamradar_color_z*1.5);
+ drawpic(coord - '8 8 0', "gfx/teamradar_icon_glow", '16 16 0', brightcolor, panel_fg_alpha, 0);
+ }
+ }
+ entity icon = RadarIcons_from(tm.teamradar_icon);
+ draw_teamradar_icon(tm.origin, icon, tm, spritelookupcolor(tm, icon.netname, tm.teamradar_color), panel_fg_alpha);
+ }
+ for(tm = world; (tm = find(tm, classname, "entcs_receiver")); )
+ {
+ color2 = GetPlayerColor(tm.sv_entnum);
+ //if(color == NUM_SPECTATOR || color == color2)
+ draw_teamradar_player(tm.origin, tm.angles, Team_ColorRGB(color2));
+ }
+ draw_teamradar_player(view_origin, view_angles, '1 1 1');
+
+ drawresetcliparea();
+
+ if ( hud_panel_radar_mouse )
+ {
+ string message = "Click to select teleport destination";
+
+ if ( getstati(STAT_HEALTH) <= 0 )
+ {
+ message = "Click to select spawn location";
+ }
+
+ drawcolorcodedstring(pos + '0.5 0 0' * (mySize_x - stringwidth(message, true, hud_fontsize)) - '0 1 0' * hud_fontsize_y * 2,
+ message, hud_fontsize, hud_panel_radar_foreground_alpha, DRAWFLAG_NORMAL);
+
+ hud_panel_radar_bottom = pos_y + mySize_y + hud_fontsize_y;
+ }
+}
--- /dev/null
+// Score (#7)
+
+void HUD_UpdatePlayerTeams();
+void HUD_Score_Rankings(vector pos, vector mySize, entity me)
+{
+ float score;
+ entity tm = world, pl;
+ int SCOREPANEL_MAX_ENTRIES = 6;
+ float SCOREPANEL_ASPECTRATIO = 2;
+ int entries = bound(1, floor(SCOREPANEL_MAX_ENTRIES * mySize.y/mySize.x * SCOREPANEL_ASPECTRATIO), SCOREPANEL_MAX_ENTRIES);
+ vector fontsize = '1 1 0' * (mySize.y/entries);
+
+ vector rgb, score_color;
+ rgb = '1 1 1';
+ score_color = '1 1 1';
+
+ float name_size = mySize.x*0.75;
+ float spacing_size = mySize.x*0.04;
+ const float highlight_alpha = 0.2;
+ int i = 0, first_pl = 0;
+ bool me_printed = false;
+ string s;
+ if (autocvar__hud_configure)
+ {
+ float players_per_team = 0;
+ if (team_count)
+ {
+ // show team scores in the first line
+ float score_size = mySize.x / team_count;
+ players_per_team = max(2, ceil((entries - 1) / team_count));
+ for(i=0; i<team_count; ++i) {
+ if (i == floor((entries - 2) / players_per_team) || (entries == 1 && i == 0))
+ HUD_Panel_DrawHighlight(pos + eX * score_size * i, eX * score_size + eY * fontsize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(pos + eX * score_size * i, ftos(175 - 23*i), eX * score_size + eY * fontsize.y, Team_ColorRGB(ColorByTeam(i)) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ first_pl = 1;
+ pos.y += fontsize.y;
+ }
+ score = 10 + SCOREPANEL_MAX_ENTRIES * 3;
+ for (i=first_pl; i<entries; ++i)
+ {
+ //simulate my score is lower than all displayed players,
+ //so that I don't appear at all showing pure rankings.
+ //This is to better show the difference between the 2 ranking views
+ if (i == entries-1 && autocvar_hud_panel_score_rankings == 1)
+ {
+ rgb = '1 1 0';
+ drawfill(pos, eX * mySize.x + eY * fontsize.y, rgb, highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ s = GetPlayerName(player_localnum);
+ score = 7;
+ }
+ else
+ {
+ s = sprintf(_("Player %d"), i + 1 - first_pl);
+ score -= 3;
+ }
+
+ if (team_count)
+ score_color = Team_ColorRGB(ColorByTeam(floor((i - first_pl) / players_per_team))) * 0.8;
+ s = textShortenToWidth(s, name_size, fontsize, stringwidth_colors);
+ drawcolorcodedstring(pos + eX * (name_size - stringwidth(s, true, fontsize)), s, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring(pos + eX * (name_size + spacing_size), ftos(score), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ pos.y += fontsize.y;
+ }
+ return;
+ }
+
+ if (!scoreboard_fade_alpha) // the scoreboard too calls HUD_UpdatePlayerTeams
+ HUD_UpdatePlayerTeams();
+ if (team_count)
+ {
+ // show team scores in the first line
+ float score_size = mySize.x / team_count;
+ for(tm = teams.sort_next; tm; tm = tm.sort_next) {
+ if(tm.team == NUM_SPECTATOR)
+ continue;
+ if (tm.team == myteam)
+ drawfill(pos + eX * score_size * i, eX * score_size + eY * fontsize.y, '1 1 1', highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(pos + eX * score_size * i, ftos(tm.(teamscores[ts_primary])), eX * score_size + eY * fontsize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
+ ++i;
+ }
+ first_pl = 1;
+ pos.y += fontsize.y;
+ tm = teams.sort_next;
+ }
+ i = first_pl;
+
+ do
+ for (pl = players.sort_next; pl && i<entries; pl = pl.sort_next)
+ {
+ if ((team_count && pl.team != tm.team) || pl.team == NUM_SPECTATOR)
+ continue;
+
+ if (i == entries-1 && !me_printed && pl != me)
+ if (autocvar_hud_panel_score_rankings == 1 && spectatee_status != -1)
+ {
+ for (pl = me.sort_next; pl; pl = pl.sort_next)
+ if (pl.team != NUM_SPECTATOR)
+ break;
+
+ if (pl)
+ rgb = '1 1 0'; //not last but not among the leading players: yellow
+ else
+ rgb = '1 0 0'; //last: red
+ pl = me;
+ }
+
+ if (pl == me)
+ {
+ if (i == first_pl)
+ rgb = '0 1 0'; //first: green
+ me_printed = true;
+ drawfill(pos, eX * mySize.x + eY * fontsize.y, rgb, highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ if (team_count)
+ score_color = Team_ColorRGB(pl.team) * 0.8;
+ s = textShortenToWidth(GetPlayerName(pl.sv_entnum), name_size, fontsize, stringwidth_colors);
+ drawcolorcodedstring(pos + eX * (name_size - stringwidth(s, true, fontsize)), s, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring(pos + eX * (name_size + spacing_size), ftos(pl.(scores[ps_primary])), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ pos.y += fontsize.y;
+ ++i;
+ }
+ while (i<entries && team_count && (tm = tm.sort_next) && (tm.team != NUM_SPECTATOR || (tm = tm.sort_next)));
+}
+
+void HUD_Score()
+{
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_score) return;
+ if(spectatee_status == -1 && (gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ float score, distribution = 0;
+ string sign;
+ vector distribution_color;
+ entity tm, pl, me;
+
+ me = playerslots[current_player];
+
+ if((scores_flags[ps_primary] & SFL_TIME) && !teamplay) { // race/cts record display on HUD
+ string timer, distrtimer;
+
+ pl = players.sort_next;
+ if(pl == me)
+ pl = pl.sort_next;
+ if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
+ if(pl.scores[ps_primary] == 0)
+ pl = world;
+
+ score = me.(scores[ps_primary]);
+ timer = TIME_ENCODED_TOSTRING(score);
+
+ draw_beginBoldFont();
+ if (pl && ((!(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)) || score)) {
+ // distribution display
+ distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
+
+ distrtimer = ftos_decimals(fabs(distribution/pow(10, TIME_DECIMALS)), TIME_DECIMALS);
+
+ if (distribution <= 0) {
+ distribution_color = '0 1 0';
+ sign = "-";
+ }
+ else {
+ distribution_color = '1 0 0';
+ sign = "+";
+ }
+ drawstring_aspect(pos + eX * 0.75 * mySize.x, strcat(sign, distrtimer), eX * 0.25 * mySize.x + eY * (1/3) * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ // race record display
+ if (distribution <= 0)
+ HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(pos, timer, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ draw_endBoldFont();
+ } else if (!teamplay) { // non-teamgames
+ if ((spectatee_status == -1 && !autocvar__hud_configure) || autocvar_hud_panel_score_rankings)
+ {
+ HUD_Score_Rankings(pos, mySize, me);
+ return;
+ }
+ // me vector := [team/connected frags id]
+ pl = players.sort_next;
+ if(pl == me)
+ pl = pl.sort_next;
+
+ if(autocvar__hud_configure)
+ distribution = 42;
+ else if(pl)
+ distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
+ else
+ distribution = 0;
+
+ score = me.(scores[ps_primary]);
+ if(autocvar__hud_configure)
+ score = 123;
+
+ if(distribution >= 5)
+ distribution_color = eY;
+ else if(distribution >= 0)
+ distribution_color = '1 1 1';
+ else if(distribution >= -5)
+ distribution_color = '1 1 0';
+ else
+ distribution_color = eX;
+
+ string distribution_str;
+ distribution_str = ftos(distribution);
+ draw_beginBoldFont();
+ if (distribution >= 0)
+ {
+ if (distribution > 0)
+ distribution_str = strcat("+", distribution_str);
+ HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(pos + eX * 0.75 * mySize.x, distribution_str, eX * 0.25 * mySize.x + eY * (1/3) * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ draw_endBoldFont();
+ } else { // teamgames
+ float row, column, rows = 0, columns = 0;
+ vector offset = '0 0 0';
+ vector score_pos, score_size; //for scores other than myteam
+ if(autocvar_hud_panel_score_rankings)
+ {
+ HUD_Score_Rankings(pos, mySize, me);
+ return;
+ }
+ if(spectatee_status == -1)
+ {
+ rows = HUD_GetRowCount(team_count, mySize, 3);
+ columns = ceil(team_count/rows);
+ score_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows);
+
+ float newSize;
+ if(score_size.x/score_size.y > 3)
+ {
+ newSize = 3 * score_size.y;
+ offset.x = score_size.x - newSize;
+ pos.x += offset.x/2;
+ score_size.x = newSize;
+ }
+ else
+ {
+ newSize = 1/3 * score_size.x;
+ offset.y = score_size.y - newSize;
+ pos.y += offset.y/2;
+ score_size.y = newSize;
+ }
+ }
+ else
+ score_size = eX * mySize.x*(1/4) + eY * mySize.y*(1/3);
+
+ float max_fragcount;
+ max_fragcount = -99;
+ draw_beginBoldFont();
+ row = column = 0;
+ for(tm = teams.sort_next; tm; tm = tm.sort_next) {
+ if(tm.team == NUM_SPECTATOR)
+ continue;
+ score = tm.(teamscores[ts_primary]);
+ if(autocvar__hud_configure)
+ score = 123;
+
+ if (score > max_fragcount)
+ max_fragcount = score;
+
+ if (spectatee_status == -1)
+ {
+ score_pos = pos + eX * column * (score_size.x + offset.x) + eY * row * (score_size.y + offset.y);
+ if (max_fragcount == score)
+ HUD_Panel_DrawHighlight(score_pos, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(score_pos, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ ++column;
+ }
+ }
+ else if(tm.team == myteam) {
+ if (max_fragcount == score)
+ HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
+ } else {
+ if (max_fragcount == score)
+ HUD_Panel_DrawHighlight(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL);
+ ++rows;
+ }
+ }
+ draw_endBoldFont();
+ }
+}
--- /dev/null
+void HUD_Timer()
+{
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_timer) return;
+ }
+
+ HUD_Panel_UpdateCvars();
+
+ draw_beginBoldFont();
+
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(1);
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ string timer;
+ float timelimit, elapsedTime, timeleft, minutesLeft;
+
+ timelimit = getstatf(STAT_TIMELIMIT);
+
+ timeleft = max(0, timelimit * 60 + STAT(GAMESTARTTIME) - time);
+ timeleft = ceil(timeleft);
+
+ minutesLeft = floor(timeleft / 60);
+
+ vector timer_color;
+ if(minutesLeft >= 5 || warmup_stage || timelimit == 0) //don't use red or yellow in warmup or when there is no timelimit
+ timer_color = '1 1 1'; //white
+ else if(minutesLeft >= 1)
+ timer_color = '1 1 0'; //yellow
+ else
+ timer_color = '1 0 0'; //red
+
+ if (autocvar_hud_panel_timer_increment || timelimit == 0 || warmup_stage) {
+ if (time < STAT(GAMESTARTTIME)) {
+ //while restart is still active, show 00:00
+ timer = seconds_tostring(0);
+ } else {
+ elapsedTime = floor(time - STAT(GAMESTARTTIME)); //127
+ timer = seconds_tostring(elapsedTime);
+ }
+ } else {
+ timer = seconds_tostring(timeleft);
+ }
+
+ drawstring_aspect(pos, timer, mySize, timer_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+
+ draw_endBoldFont();
+}
--- /dev/null
+/** Vote window (#9) */
+void HUD_Vote()
+{
+ if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS)))
+ {
+ vote_active = 1;
+ if (autocvar__hud_configure)
+ {
+ vote_yescount = 0;
+ vote_nocount = 0;
+ LOG_INFO(_("^1You must answer before entering hud configure mode\n"));
+ cvar_set("_hud_configure", "0");
+ }
+ if(vote_called_vote)
+ strunzone(vote_called_vote);
+ vote_called_vote = strzone(_("^2Name ^7instead of \"^1Anonymous player^7\" in stats"));
+ uid2name_dialog = 1;
+ }
+
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_vote) return;
+
+ panel_fg_alpha = autocvar_hud_panel_fg_alpha;
+ panel_bg_alpha_str = autocvar_hud_panel_vote_bg_alpha;
+
+ if(panel_bg_alpha_str == "") {
+ panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha);
+ }
+ panel_bg_alpha = stof(panel_bg_alpha_str);
+ }
+ else
+ {
+ vote_yescount = 3;
+ vote_nocount = 2;
+ vote_needed = 4;
+ }
+
+ string s;
+ float a;
+ if(vote_active != vote_prev) {
+ vote_change = time;
+ vote_prev = vote_active;
+ }
+
+ if(vote_active || autocvar__hud_configure)
+ vote_alpha = bound(0, (time - vote_change) * 2, 1);
+ else
+ vote_alpha = bound(0, 1 - (time - vote_change) * 2, 1);
+
+ if(!vote_alpha)
+ return;
+
+ HUD_Panel_UpdateCvars();
+
+ if(uid2name_dialog)
+ {
+ panel_pos = eX * 0.3 * vid_conwidth + eY * 0.1 * vid_conheight;
+ panel_size = eX * 0.4 * vid_conwidth + eY * 0.3 * vid_conheight;
+ }
+
+ // these must be below above block
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ a = vote_alpha * (vote_highlighted ? autocvar_hud_panel_vote_alreadyvoted_alpha : 1);
+ HUD_Panel_DrawBg(a);
+ a = panel_fg_alpha * a;
+
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ // always force 3:1 aspect
+ vector newSize = '0 0 0';
+ if(mySize.x/mySize.y > 3)
+ {
+ newSize.x = 3 * mySize.y;
+ newSize.y = mySize.y;
+
+ pos.x = pos.x + (mySize.x - newSize.x) / 2;
+ }
+ else
+ {
+ newSize.y = 1/3 * mySize.x;
+ newSize.x = mySize.x;
+
+ pos.y = pos.y + (mySize.y - newSize.y) / 2;
+ }
+ mySize = newSize;
+
+ s = _("A vote has been called for:");
+ if(uid2name_dialog)
+ s = _("Allow servers to store and display your name?");
+ drawstring_aspect(pos, s, eX * mySize.x + eY * (2/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+ s = textShortenToWidth(vote_called_vote, mySize.x, '1 1 0' * mySize.y * (1/8), stringwidth_colors);
+ if(autocvar__hud_configure)
+ s = _("^1Configure the HUD");
+ drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, a, DRAWFLAG_NORMAL);
+
+ // print the yes/no counts
+ s = sprintf(_("Yes (%s): %d"), getcommandkey("vyes", "vyes"), vote_yescount);
+ drawstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '0 1 0', a, DRAWFLAG_NORMAL);
+ s = sprintf(_("No (%s): %d"), getcommandkey("vno", "vno"), vote_nocount);
+ drawstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '1 0 0', a, DRAWFLAG_NORMAL);
+
+ // draw the progress bar backgrounds
+ drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_back", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+
+ // draw the highlights
+ if(vote_highlighted == 1) {
+ drawsetcliparea(pos.x, pos.y, mySize.x * 0.5, mySize.y);
+ drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+ }
+ else if(vote_highlighted == -1) {
+ drawsetcliparea(pos.x + 0.5 * mySize.x, pos.y, mySize.x * 0.5, mySize.y);
+ drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+ }
+
+ // draw the progress bars
+ if(vote_yescount && vote_needed)
+ {
+ drawsetcliparea(pos.x, pos.y, mySize.x * 0.5 * (vote_yescount/vote_needed), mySize.y);
+ drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+ }
+
+ if(vote_nocount && vote_needed)
+ {
+ drawsetcliparea(pos.x + mySize.x - mySize.x * 0.5 * (vote_nocount/vote_needed), pos.y, mySize.x * 0.5, mySize.y);
+ drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL);
+ }
+
+ drawresetcliparea();
+}
--- /dev/null
+// Weapon icons (#0)
+
+entity weaponorder[Weapons_MAX];
+void weaponorder_swap(int i, int j, entity pass)
+{
+ entity h = weaponorder[i];
+ weaponorder[i] = weaponorder[j];
+ weaponorder[j] = h;
+}
+
+string weaponorder_cmp_str;
+int weaponorder_cmp(int i, int j, entity pass)
+{
+ int ai, aj;
+ ai = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[i].weapon), 0);
+ aj = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[j].weapon), 0);
+ return aj - ai; // the string is in REVERSE order (higher prio at the right is what we want, but higher prio first is the string)
+}
+
+void HUD_Weapons()
+{
+ SELFPARAM();
+ // declarations
+ WepSet weapons_stat = WepSet_GetFromStat();
+ int i;
+ float f, a;
+ float screen_ar;
+ vector center = '0 0 0';
+ int weapon_count, weapon_id;
+ int row, column, rows = 0, columns = 0;
+ bool vertical_order = true;
+ float aspect = autocvar_hud_panel_weapons_aspect;
+
+ float timeout = autocvar_hud_panel_weapons_timeout;
+ float timein_effect_length = autocvar_hud_panel_weapons_timeout_speed_in; //? 0.375 : 0);
+ float timeout_effect_length = autocvar_hud_panel_weapons_timeout_speed_out; //? 0.75 : 0);
+
+ vector barsize = '0 0 0', baroffset = '0 0 0';
+ vector ammo_color = '1 0 1';
+ float ammo_alpha = 1;
+
+ float when = max(1, autocvar_hud_panel_weapons_complainbubble_time);
+ float fadetime = max(0, autocvar_hud_panel_weapons_complainbubble_fadetime);
+
+ vector weapon_pos, weapon_size = '0 0 0';
+ vector color;
+
+ // check to see if we want to continue
+ if(hud != HUD_NORMAL) return;
+
+ if(!autocvar__hud_configure)
+ {
+ if((!autocvar_hud_panel_weapons) || (spectatee_status == -1))
+ return;
+ if(timeout && time >= weapontime + timeout + timeout_effect_length)
+ if(autocvar_hud_panel_weapons_timeout_effect == 3 || (autocvar_hud_panel_weapons_timeout_effect == 1 && !(autocvar_hud_panel_weapons_timeout_fadebgmin + autocvar_hud_panel_weapons_timeout_fadefgmin)))
+ {
+ weaponprevtime = time;
+ return;
+ }
+ }
+
+ // update generic hud functions
+ HUD_Panel_UpdateCvars();
+
+ // figure out weapon order (how the weapons are sorted) // TODO make this configurable
+ if(weaponorder_bypriority != autocvar_cl_weaponpriority || !weaponorder[0])
+ {
+ int weapon_cnt;
+ if(weaponorder_bypriority)
+ strunzone(weaponorder_bypriority);
+ if(weaponorder_byimpulse)
+ strunzone(weaponorder_byimpulse);
+
+ weaponorder_bypriority = strzone(autocvar_cl_weaponpriority);
+ weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(W_FixWeaponOrder_ForceComplete(W_NumberWeaponOrder(weaponorder_bypriority))));
+ weaponorder_cmp_str = strcat(" ", weaponorder_byimpulse, " ");
+
+ weapon_cnt = 0;
+ for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+ {
+ setself(get_weaponinfo(i));
+ if(self.impulse >= 0)
+ {
+ weaponorder[weapon_cnt] = self;
+ ++weapon_cnt;
+ }
+ }
+ for(i = weapon_cnt; i < Weapons_MAX; ++i)
+ weaponorder[i] = world;
+ heapsort(weapon_cnt, weaponorder_swap, weaponorder_cmp, world);
+
+ weaponorder_cmp_str = string_null;
+ }
+
+ if(!autocvar_hud_panel_weapons_complainbubble || autocvar__hud_configure || time - complain_weapon_time >= when + fadetime)
+ complain_weapon = 0;
+
+ if(autocvar__hud_configure)
+ {
+ if(!weapons_stat)
+ for(i = WEP_FIRST; i <= WEP_LAST; i += floor((WEP_LAST-WEP_FIRST)/5))
+ weapons_stat |= WepSet_FromWeapon(i);
+
+ #if 0
+ /// debug code
+ if(cvar("wep_add"))
+ {
+ weapons_stat = '0 0 0';
+ float countw = 1 + floor((floor(time * cvar("wep_add"))) % (Weapons_COUNT - 1));
+ for(i = WEP_FIRST; i <= countw; ++i)
+ weapons_stat |= WepSet_FromWeapon(i);
+ }
+ #endif
+ }
+
+ // determine which weapons are going to be shown
+ if (autocvar_hud_panel_weapons_onlyowned)
+ {
+ if(autocvar__hud_configure)
+ {
+ if(menu_enabled != 2)
+ HUD_Panel_DrawBg(1); // also draw the bg of the entire panel
+ }
+
+ // do we own this weapon?
+ weapon_count = 0;
+ for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i)
+ if((weapons_stat & WepSet_FromWeapon(weaponorder[i].weapon)) || (weaponorder[i].weapon == complain_weapon))
+ ++weapon_count;
+
+
+ // might as well commit suicide now, no reason to live ;)
+ if (weapon_count == 0)
+ return;
+
+ vector old_panel_size = panel_size;
+ vector padded_panel_size = panel_size - '2 2 0' * panel_bg_padding;
+
+ // get the all-weapons layout
+ int nHidden = 0;
+ WepSet weapons_stat = WepSet_GetFromStat();
+ for (int i = WEP_FIRST; i <= WEP_LAST; ++i) {
+ WepSet weapons_wep = WepSet_FromWeapon(i);
+ if (weapons_stat & weapons_wep) continue;
+ Weapon w = get_weaponinfo(i);
+ if (w.spawnflags & WEP_FLAG_MUTATORBLOCKED) nHidden += 1;
+ }
+ vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1) - nHidden, padded_panel_size, aspect);
+ columns = table_size.x;
+ rows = table_size.y;
+ weapon_size.x = padded_panel_size.x / columns;
+ weapon_size.y = padded_panel_size.y / rows;
+
+ // NOTE: although weapons should aways look the same even if onlyowned is enabled,
+ // we enlarge them a bit when possible to better match the desired aspect ratio
+ if(padded_panel_size.x / padded_panel_size.y < aspect)
+ {
+ // maximum number of rows that allows to display items with the desired aspect ratio
+ int max_rows = floor(padded_panel_size.y / (weapon_size.x / aspect));
+ columns = min(columns, ceil(weapon_count / max_rows));
+ rows = ceil(weapon_count / columns);
+ weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect);
+ weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y);
+ vertical_order = false;
+ }
+ else
+ {
+ int max_columns = floor(padded_panel_size.x / (weapon_size.y * aspect));
+ rows = min(rows, ceil(weapon_count / max_columns));
+ columns = ceil(weapon_count / rows);
+ weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y);
+ weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect);
+ vertical_order = true;
+ }
+
+ // reduce size of the panel
+ panel_size.x = columns * weapon_size.x;
+ panel_size.y = rows * weapon_size.y;
+ panel_size += '2 2 0' * panel_bg_padding;
+
+ // center the resized panel, or snap it to the screen edge when close enough
+ if(panel_pos.x > vid_conwidth * 0.001)
+ {
+ if(panel_pos.x + old_panel_size.x > vid_conwidth * 0.999)
+ panel_pos.x += old_panel_size.x - panel_size.x;
+ else
+ panel_pos.x += (old_panel_size.x - panel_size.x) / 2;
+ }
+ else if(old_panel_size.x > vid_conwidth * 0.999)
+ panel_pos.x += (old_panel_size.x - panel_size.x) / 2;
+
+ if(panel_pos.y > vid_conheight * 0.001)
+ {
+ if(panel_pos.y + old_panel_size.y > vid_conheight * 0.999)
+ panel_pos.y += old_panel_size.y - panel_size.y;
+ else
+ panel_pos.y += (old_panel_size.y - panel_size.y) / 2;
+ }
+ else if(old_panel_size.y > vid_conheight * 0.999)
+ panel_pos.y += (old_panel_size.y - panel_size.y) / 2;
+ }
+ else
+ weapon_count = (Weapons_COUNT - 1);
+
+ // animation for fading in/out the panel respectively when not in use
+ if(!autocvar__hud_configure)
+ {
+ if (timeout && time >= weapontime + timeout) // apply timeout effect if needed
+ {
+ f = bound(0, (time - (weapontime + timeout)) / timeout_effect_length, 1);
+
+ // fade the panel alpha
+ if(autocvar_hud_panel_weapons_timeout_effect == 1)
+ {
+ panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * f + (1 - f));
+ panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * f + (1 - f));
+ }
+ else if(autocvar_hud_panel_weapons_timeout_effect == 3)
+ {
+ panel_bg_alpha *= (1 - f);
+ panel_fg_alpha *= (1 - f);
+ }
+
+ // move the panel off the screen
+ if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3)
+ {
+ f *= f; // for a cooler movement
+ center.x = panel_pos.x + panel_size.x/2;
+ center.y = panel_pos.y + panel_size.y/2;
+ screen_ar = vid_conwidth/vid_conheight;
+ if (center.x/center.y < screen_ar) //bottom left
+ {
+ if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom
+ panel_pos.y += f * (vid_conheight - panel_pos.y);
+ else //left
+ panel_pos.x -= f * (panel_pos.x + panel_size.x);
+ }
+ else //top right
+ {
+ if ((vid_conwidth - center.x)/center.y < screen_ar) //right
+ panel_pos.x += f * (vid_conwidth - panel_pos.x);
+ else //top
+ panel_pos.y -= f * (panel_pos.y + panel_size.y);
+ }
+ if(f == 1)
+ center.x = -1; // mark the panel as off screen
+ }
+ weaponprevtime = time - (1 - f) * timein_effect_length;
+ }
+ else if (timeout && time < weaponprevtime + timein_effect_length) // apply timein effect if needed
+ {
+ f = bound(0, (time - weaponprevtime) / timein_effect_length, 1);
+
+ // fade the panel alpha
+ if(autocvar_hud_panel_weapons_timeout_effect == 1)
+ {
+ panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * (1 - f) + f);
+ panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * (1 - f) + f);
+ }
+ else if(autocvar_hud_panel_weapons_timeout_effect == 3)
+ {
+ panel_bg_alpha *= (f);
+ panel_fg_alpha *= (f);
+ }
+
+ // move the panel back on screen
+ if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3)
+ {
+ f *= f; // for a cooler movement
+ f = 1 - f;
+ center.x = panel_pos.x + panel_size.x/2;
+ center.y = panel_pos.y + panel_size.y/2;
+ screen_ar = vid_conwidth/vid_conheight;
+ if (center.x/center.y < screen_ar) //bottom left
+ {
+ if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom
+ panel_pos.y += f * (vid_conheight - panel_pos.y);
+ else //left
+ panel_pos.x -= f * (panel_pos.x + panel_size.x);
+ }
+ else //top right
+ {
+ if ((vid_conwidth - center.x)/center.y < screen_ar) //right
+ panel_pos.x += f * (vid_conwidth - panel_pos.x);
+ else //top
+ panel_pos.y -= f * (panel_pos.y + panel_size.y);
+ }
+ }
+ }
+ }
+
+ // draw the background, then change the virtual size of it to better fit other items inside
+ HUD_Panel_DrawBg(1);
+
+ if(center.x == -1)
+ return;
+
+ if(panel_bg_padding)
+ {
+ panel_pos += '1 1 0' * panel_bg_padding;
+ panel_size -= '2 2 0' * panel_bg_padding;
+ }
+
+ // after the sizing and animations are done, update the other values
+
+ if(!rows) // if rows is > 0 onlyowned code has already updated these vars
+ {
+ vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1), panel_size, aspect);
+ columns = table_size.x;
+ rows = table_size.y;
+ weapon_size.x = panel_size.x / columns;
+ weapon_size.y = panel_size.y / rows;
+ vertical_order = (panel_size.x / panel_size.y >= aspect);
+ }
+
+ // calculate position/size for visual bar displaying ammount of ammo status
+ if (autocvar_hud_panel_weapons_ammo)
+ {
+ ammo_color = stov(autocvar_hud_panel_weapons_ammo_color);
+ ammo_alpha = panel_fg_alpha * autocvar_hud_panel_weapons_ammo_alpha;
+
+ if(weapon_size.x/weapon_size.y > aspect)
+ {
+ barsize.x = aspect * weapon_size.y;
+ barsize.y = weapon_size.y;
+ baroffset.x = (weapon_size.x - barsize.x) / 2;
+ }
+ else
+ {
+ barsize.y = 1/aspect * weapon_size.x;
+ barsize.x = weapon_size.x;
+ baroffset.y = (weapon_size.y - barsize.y) / 2;
+ }
+ }
+ if(autocvar_hud_panel_weapons_accuracy)
+ Accuracy_LoadColors();
+
+ // draw items
+ row = column = 0;
+ vector label_size = '1 1 0' * min(weapon_size.x, weapon_size.y) * bound(0, autocvar_hud_panel_weapons_label_scale, 1);
+ vector noncurrent_pos = '0 0 0';
+ vector noncurrent_size = weapon_size * bound(0, autocvar_hud_panel_weapons_noncurrent_scale, 1);
+ float noncurrent_alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_weapons_noncurrent_alpha, 1);
+ bool isCurrent;
+
+ for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i)
+ {
+ // retrieve information about the current weapon to be drawn
+ setself(weaponorder[i]);
+ weapon_id = self.impulse;
+ isCurrent = (self.weapon == switchweapon);
+
+ // skip if this weapon doesn't exist
+ if(!self || weapon_id < 0) { continue; }
+
+ // skip this weapon if we don't own it (and onlyowned is enabled)-- or if weapons_complainbubble is showing for this weapon
+ if(autocvar_hud_panel_weapons_onlyowned)
+ if (!((weapons_stat & WepSet_FromWeapon(self.weapon)) || (self.weapon == complain_weapon)))
+ continue;
+
+ // figure out the drawing position of weapon
+ weapon_pos = (panel_pos + eX * column * weapon_size.x + eY * row * weapon_size.y);
+ noncurrent_pos.x = weapon_pos.x + (weapon_size.x - noncurrent_size.x) / 2;
+ noncurrent_pos.y = weapon_pos.y + (weapon_size.y - noncurrent_size.y) / 2;
+
+ // draw background behind currently selected weapon
+ if(isCurrent)
+ drawpic_aspect_skin(weapon_pos, "weapon_current_bg", weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+
+ // draw the weapon accuracy
+ if(autocvar_hud_panel_weapons_accuracy)
+ {
+ float panel_weapon_accuracy = weapon_accuracy[self.weapon-WEP_FIRST];
+ if(panel_weapon_accuracy >= 0)
+ {
+ color = Accuracy_GetColor(panel_weapon_accuracy);
+ drawpic_aspect_skin(weapon_pos, "weapon_accuracy", weapon_size, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ }
+
+ // drawing all the weapon items
+ if(weapons_stat & WepSet_FromWeapon(self.weapon))
+ {
+ // draw the weapon image
+ if(isCurrent)
+ drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ else
+ drawpic_aspect_skin(noncurrent_pos, self.model2, noncurrent_size, '1 1 1', noncurrent_alpha, DRAWFLAG_NORMAL);
+
+ // draw weapon label string
+ switch(autocvar_hud_panel_weapons_label)
+ {
+ case 1: // weapon number
+ drawstring(weapon_pos, ftos(weapon_id), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ break;
+
+ case 2: // bind
+ drawstring(weapon_pos, getcommandkey(ftos(weapon_id), strcat("weapon_group_", ftos(weapon_id))), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ break;
+
+ case 3: // weapon name
+ drawstring(weapon_pos, strtolower(self.m_name), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ break;
+
+ default: // nothing
+ break;
+ }
+
+ // draw ammo status bar
+ if(autocvar_hud_panel_weapons_ammo && (self.ammo_field != ammo_none))
+ {
+ float ammo_full;
+ a = getstati(GetAmmoStat(self.ammo_field)); // how much ammo do we have?
+
+ if(a > 0)
+ {
+ switch(self.ammo_field)
+ {
+ case ammo_shells: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break;
+ case ammo_nails: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break;
+ case ammo_rockets: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break;
+ case ammo_cells: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break;
+ case ammo_plasma: ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma; break;
+ case ammo_fuel: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break;
+ default: ammo_full = 60;
+ }
+
+ drawsetcliparea(
+ weapon_pos.x + baroffset.x,
+ weapon_pos.y + baroffset.y,
+ barsize.x * bound(0, a/ammo_full, 1),
+ barsize.y
+ );
+
+ drawpic_aspect_skin(
+ weapon_pos,
+ "weapon_ammo",
+ weapon_size,
+ ammo_color,
+ ammo_alpha,
+ DRAWFLAG_NORMAL
+ );
+
+ drawresetcliparea();
+ }
+ }
+ }
+ else // draw a "ghost weapon icon" if you don't have the weapon
+ {
+ drawpic_aspect_skin(noncurrent_pos, self.model2, noncurrent_size, '0.2 0.2 0.2', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
+ }
+
+ // draw the complain message
+ if(self.weapon == complain_weapon)
+ {
+ if(fadetime)
+ a = ((complain_weapon_time + when > time) ? 1 : bound(0, (complain_weapon_time + when + fadetime - time) / fadetime, 1));
+ else
+ a = ((complain_weapon_time + when > time) ? 1 : 0);
+
+ string s;
+ if(complain_weapon_type == 0) {
+ s = _("Out of ammo");
+ color = stov(autocvar_hud_panel_weapons_complainbubble_color_outofammo);
+ }
+ else if(complain_weapon_type == 1) {
+ s = _("Don't have");
+ color = stov(autocvar_hud_panel_weapons_complainbubble_color_donthave);
+ }
+ else {
+ s = _("Unavailable");
+ color = stov(autocvar_hud_panel_weapons_complainbubble_color_unavailable);
+ }
+ float padding = autocvar_hud_panel_weapons_complainbubble_padding;
+ drawpic_aspect_skin(weapon_pos + '1 1 0' * padding, "weapon_complainbubble", weapon_size - '2 2 0' * padding, color, a * panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(weapon_pos + '1 1 0' * padding, s, weapon_size - '2 2 0' * padding, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ }
+
+ #if 0
+ /// debug code
+ if(!autocvar_hud_panel_weapons_onlyowned)
+ {
+ drawfill(weapon_pos + '1 1 0', weapon_size - '2 2 0', '1 1 1', panel_fg_alpha * 0.2, DRAWFLAG_NORMAL);
+ drawstring(weapon_pos, ftos(i + 1), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ #endif
+
+ // continue with new position for the next weapon
+ if(vertical_order)
+ {
+ ++column;
+ if(column >= columns)
+ {
+ column = 0;
+ ++row;
+ }
+ }
+ else
+ {
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ ++column;
+ }
+ }
+ }
+}
+++ /dev/null
-#include "hud_config.qh"
-
-#include "hud.qh"
-
-#define HUD_Write(s) fputs(fh, s)
-// q: quoted, n: not quoted
-#define HUD_Write_Cvar_n(cvar) HUD_Write(strcat("seta ", cvar, " ", cvar_string(cvar), "\n"))
-#define HUD_Write_Cvar_q(cvar) HUD_Write(strcat("seta ", cvar, " \"", cvar_string(cvar), "\"\n"))
-#define HUD_Write_PanelCvar_n(cvar_suf) HUD_Write_Cvar_n(strcat("hud_panel_", panel.panel_name, cvar_suf))
-#define HUD_Write_PanelCvar_q(cvar_suf) HUD_Write_Cvar_q(strcat("hud_panel_", panel.panel_name, cvar_suf))
-// Save the config
-void HUD_Panel_ExportCfg(string cfgname)
-{
- float fh;
- string filename = strcat("hud_", autocvar_hud_skin, "_", cfgname, ".cfg");
- fh = fopen(filename, FILE_WRITE);
- if(fh >= 0)
- {
- HUD_Write_Cvar_q("hud_skin");
- HUD_Write_Cvar_q("hud_panel_bg");
- HUD_Write_Cvar_q("hud_panel_bg_color");
- HUD_Write_Cvar_q("hud_panel_bg_color_team");
- HUD_Write_Cvar_q("hud_panel_bg_alpha");
- HUD_Write_Cvar_q("hud_panel_bg_border");
- HUD_Write_Cvar_q("hud_panel_bg_padding");
- HUD_Write_Cvar_q("hud_panel_fg_alpha");
- HUD_Write("\n");
-
- HUD_Write_Cvar_q("hud_dock");
- HUD_Write_Cvar_q("hud_dock_color");
- HUD_Write_Cvar_q("hud_dock_color_team");
- HUD_Write_Cvar_q("hud_dock_alpha");
- HUD_Write("\n");
-
- HUD_Write_Cvar_q("hud_progressbar_alpha");
- HUD_Write_Cvar_q("hud_progressbar_strength_color");
- HUD_Write_Cvar_q("hud_progressbar_shield_color");
- HUD_Write_Cvar_q("hud_progressbar_health_color");
- HUD_Write_Cvar_q("hud_progressbar_armor_color");
- HUD_Write_Cvar_q("hud_progressbar_fuel_color");
- HUD_Write_Cvar_q("hud_progressbar_nexball_color");
- HUD_Write_Cvar_q("hud_progressbar_speed_color");
- HUD_Write_Cvar_q("hud_progressbar_acceleration_color");
- HUD_Write_Cvar_q("hud_progressbar_acceleration_neg_color");
- HUD_Write("\n");
-
- HUD_Write_Cvar_q("_hud_panelorder");
- HUD_Write("\n");
-
- HUD_Write_Cvar_q("hud_configure_grid");
- HUD_Write_Cvar_q("hud_configure_grid_xsize");
- HUD_Write_Cvar_q("hud_configure_grid_ysize");
- HUD_Write("\n");
-
- // common cvars for all panels
- for (int i = 0; i < hud_panels_COUNT; ++i)
- {
- panel = hud_panels[i];
-
- HUD_Write_PanelCvar_n("");
- HUD_Write_PanelCvar_q("_pos");
- HUD_Write_PanelCvar_q("_size");
- HUD_Write_PanelCvar_q("_bg");
- HUD_Write_PanelCvar_q("_bg_color");
- HUD_Write_PanelCvar_q("_bg_color_team");
- HUD_Write_PanelCvar_q("_bg_alpha");
- HUD_Write_PanelCvar_q("_bg_border");
- HUD_Write_PanelCvar_q("_bg_padding");
- switch(panel) {
- case HUD_PANEL_WEAPONS:
- HUD_Write_PanelCvar_q("_accuracy");
- HUD_Write_PanelCvar_q("_label");
- HUD_Write_PanelCvar_q("_label_scale");
- HUD_Write_PanelCvar_q("_complainbubble");
- HUD_Write_PanelCvar_q("_complainbubble_padding");
- HUD_Write_PanelCvar_q("_complainbubble_time");
- HUD_Write_PanelCvar_q("_complainbubble_fadetime");
- HUD_Write_PanelCvar_q("_complainbubble_color_outofammo");
- HUD_Write_PanelCvar_q("_complainbubble_color_donthave");
- HUD_Write_PanelCvar_q("_complainbubble_color_unavailable");
- HUD_Write_PanelCvar_q("_ammo");
- HUD_Write_PanelCvar_q("_ammo_color");
- HUD_Write_PanelCvar_q("_ammo_alpha");
- HUD_Write_PanelCvar_q("_aspect");
- HUD_Write_PanelCvar_q("_timeout");
- HUD_Write_PanelCvar_q("_timeout_effect");
- HUD_Write_PanelCvar_q("_timeout_fadebgmin");
- HUD_Write_PanelCvar_q("_timeout_fadefgmin");
- HUD_Write_PanelCvar_q("_timeout_speed_in");
- HUD_Write_PanelCvar_q("_timeout_speed_out");
- HUD_Write_PanelCvar_q("_onlyowned");
- HUD_Write_PanelCvar_q("_noncurrent_alpha");
- HUD_Write_PanelCvar_q("_noncurrent_scale");
- break;
- case HUD_PANEL_AMMO:
- HUD_Write_PanelCvar_q("_onlycurrent");
- HUD_Write_PanelCvar_q("_noncurrent_alpha");
- HUD_Write_PanelCvar_q("_noncurrent_scale");
- HUD_Write_PanelCvar_q("_iconalign");
- HUD_Write_PanelCvar_q("_progressbar");
- HUD_Write_PanelCvar_q("_progressbar_name");
- HUD_Write_PanelCvar_q("_progressbar_xoffset");
- HUD_Write_PanelCvar_q("_text");
- break;
- case HUD_PANEL_POWERUPS:
- HUD_Write_PanelCvar_q("_iconalign");
- HUD_Write_PanelCvar_q("_baralign");
- HUD_Write_PanelCvar_q("_progressbar");
- HUD_Write_PanelCvar_q("_text");
- break;
- case HUD_PANEL_HEALTHARMOR:
- HUD_Write_PanelCvar_q("_flip");
- HUD_Write_PanelCvar_q("_iconalign");
- HUD_Write_PanelCvar_q("_baralign");
- HUD_Write_PanelCvar_q("_progressbar");
- HUD_Write_PanelCvar_q("_progressbar_health");
- HUD_Write_PanelCvar_q("_progressbar_armor");
- HUD_Write_PanelCvar_q("_progressbar_gfx");
- HUD_Write_PanelCvar_q("_progressbar_gfx_smooth");
- HUD_Write_PanelCvar_q("_text");
- break;
- case HUD_PANEL_NOTIFY:
- HUD_Write_PanelCvar_q("_flip");
- HUD_Write_PanelCvar_q("_fontsize");
- HUD_Write_PanelCvar_q("_time");
- HUD_Write_PanelCvar_q("_fadetime");
- HUD_Write_PanelCvar_q("_icon_aspect");
- break;
- case HUD_PANEL_TIMER:
- HUD_Write_PanelCvar_q("_increment");
- break;
- case HUD_PANEL_RADAR:
- HUD_Write_PanelCvar_q("_foreground_alpha");
- HUD_Write_PanelCvar_q("_rotation");
- HUD_Write_PanelCvar_q("_zoommode");
- HUD_Write_PanelCvar_q("_scale");
- HUD_Write_PanelCvar_q("_maximized_scale");
- HUD_Write_PanelCvar_q("_maximized_size");
- HUD_Write_PanelCvar_q("_maximized_rotation");
- HUD_Write_PanelCvar_q("_maximized_zoommode");
- break;
- case HUD_PANEL_SCORE:
- HUD_Write_PanelCvar_q("_rankings");
- break;
- case HUD_PANEL_VOTE:
- HUD_Write_PanelCvar_q("_alreadyvoted_alpha");
- break;
- case HUD_PANEL_MODICONS:
- HUD_Write_PanelCvar_q("_ca_layout");
- HUD_Write_PanelCvar_q("_dom_layout");
- HUD_Write_PanelCvar_q("_freezetag_layout");
- break;
- case HUD_PANEL_PRESSEDKEYS:
- HUD_Write_PanelCvar_q("_aspect");
- HUD_Write_PanelCvar_q("_attack");
- break;
- case HUD_PANEL_ENGINEINFO:
- HUD_Write_PanelCvar_q("_framecounter_time");
- HUD_Write_PanelCvar_q("_framecounter_decimals");
- break;
- case HUD_PANEL_INFOMESSAGES:
- HUD_Write_PanelCvar_q("_flip");
- break;
- case HUD_PANEL_PHYSICS:
- HUD_Write_PanelCvar_q("_speed_unit");
- HUD_Write_PanelCvar_q("_speed_unit_show");
- HUD_Write_PanelCvar_q("_speed_max");
- HUD_Write_PanelCvar_q("_speed_vertical");
- HUD_Write_PanelCvar_q("_topspeed");
- HUD_Write_PanelCvar_q("_topspeed_time");
- HUD_Write_PanelCvar_q("_acceleration_max");
- HUD_Write_PanelCvar_q("_acceleration_vertical");
- HUD_Write_PanelCvar_q("_flip");
- HUD_Write_PanelCvar_q("_baralign");
- HUD_Write_PanelCvar_q("_progressbar");
- HUD_Write_PanelCvar_q("_progressbar_acceleration_mode");
- HUD_Write_PanelCvar_q("_progressbar_acceleration_scale");
- HUD_Write_PanelCvar_q("_progressbar_acceleration_nonlinear");
- HUD_Write_PanelCvar_q("_text");
- HUD_Write_PanelCvar_q("_text_scale");
- break;
- case HUD_PANEL_CENTERPRINT:
- HUD_Write_PanelCvar_q("_align");
- HUD_Write_PanelCvar_q("_flip");
- HUD_Write_PanelCvar_q("_fontscale");
- HUD_Write_PanelCvar_q("_time");
- HUD_Write_PanelCvar_q("_fade_in");
- HUD_Write_PanelCvar_q("_fade_out");
- HUD_Write_PanelCvar_q("_fade_subsequent");
- HUD_Write_PanelCvar_q("_fade_subsequent_passone");
- HUD_Write_PanelCvar_q("_fade_subsequent_passone_minalpha");
- HUD_Write_PanelCvar_q("_fade_subsequent_passtwo");
- HUD_Write_PanelCvar_q("_fade_subsequent_passtwo_minalpha");
- HUD_Write_PanelCvar_q("_fade_subsequent_minfontsize");
- HUD_Write_PanelCvar_q("_fade_minfontsize");
- break;
- case HUD_PANEL_ITEMSTIME:
- HUD_Write_PanelCvar_q("_iconalign");
- HUD_Write_PanelCvar_q("_progressbar");
- HUD_Write_PanelCvar_q("_progressbar_name");
- HUD_Write_PanelCvar_q("_progressbar_reduced");
- HUD_Write_PanelCvar_q("_text");
- HUD_Write_PanelCvar_q("_ratio");
- HUD_Write_PanelCvar_q("_dynamicsize");
- case HUD_PANEL_QUICKMENU:
- HUD_Write_PanelCvar_q("_align");
- break;
- }
- HUD_Write("\n");
- }
- HUD_Write("menu_sync\n"); // force the menu to reread the cvars, so that the dialogs are updated
-
- LOG_INFOF(_("^2Successfully exported to %s! (Note: It's saved in data/data/)\n"), filename);
- fclose(fh);
- }
- else
- LOG_INFOF(_("^1Couldn't write to %s\n"), filename);
-}
-
-void HUD_Configure_Exit_Force()
-{
- if (menu_enabled)
- {
- menu_enabled = 0;
- localcmd("togglemenu\n");
- }
- cvar_set("_hud_configure", "0");
-}
-
-// check if move will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector
-vector HUD_Panel_CheckMove(vector myPos, vector mySize)
-{
- vector myCenter, targCenter;
- vector myTarget = myPos;
- int i;
- for (i = 0; i < hud_panels_COUNT; ++i) {
- panel = hud_panels[i];
- if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue;
- if(panel == highlightedPanel) continue;
- HUD_Panel_UpdatePosSize();
- if(!panel_enabled) continue;
-
- panel_pos -= '1 1 0' * panel_bg_border;
- panel_size += '2 2 0' * panel_bg_border;
-
- if(myPos.y + mySize.y < panel_pos.y)
- continue;
- if(myPos.y > panel_pos.y + panel_size.y)
- continue;
-
- if(myPos.x + mySize.x < panel_pos.x)
- continue;
- if(myPos.x > panel_pos.x + panel_size.x)
- continue;
-
- // OK, there IS a collision.
-
- myCenter.x = myPos.x + 0.5 * mySize.x;
- myCenter.y = myPos.y + 0.5 * mySize.y;
-
- targCenter.x = panel_pos.x + 0.5 * panel_size.x;
- targCenter.y = panel_pos.y + 0.5 * panel_size.y;
-
- if(myCenter.x < targCenter.x && myCenter.y < targCenter.y) // top left (of the target panel)
- {
- if(myPos.x + mySize.x - panel_pos.x < myPos.y + mySize.y - panel_pos.y) // push it to the side
- myTarget.x = panel_pos.x - mySize.x;
- else // push it upwards
- myTarget.y = panel_pos.y - mySize.y;
- }
- else if(myCenter.x > targCenter.x && myCenter.y < targCenter.y) // top right
- {
- if(panel_pos.x + panel_size.x - myPos.x < myPos.y + mySize.y - panel_pos.y) // push it to the side
- myTarget.x = panel_pos.x + panel_size.x;
- else // push it upwards
- myTarget.y = panel_pos.y - mySize.y;
- }
- else if(myCenter.x < targCenter.x && myCenter.y > targCenter.y) // bottom left
- {
- if(myPos.x + mySize.x - panel_pos.x < panel_pos.y + panel_size.y - myPos.y) // push it to the side
- myTarget.x = panel_pos.x - mySize.x;
- else // push it downwards
- myTarget.y = panel_pos.y + panel_size.y;
- }
- else if(myCenter.x > targCenter.x && myCenter.y > targCenter.y) // bottom right
- {
- if(panel_pos.x + panel_size.x - myPos.x < panel_pos.y + panel_size.y - myPos.y) // push it to the side
- myTarget.x = panel_pos.x + panel_size.x;
- else // push it downwards
- myTarget.y = panel_pos.y + panel_size.y;
- }
- //if(cvar("hud_configure_checkcollisions_debug"))
- //drawfill(panel_pos, panel_size, '1 1 0', .3, DRAWFLAG_NORMAL);
- }
-
- return myTarget;
-}
-
-void HUD_Panel_SetPos(vector pos)
-{
- panel = highlightedPanel;
- HUD_Panel_UpdatePosSize();
- vector mySize;
- mySize = panel_size;
-
- //if(cvar("hud_configure_checkcollisions_debug"))
- //drawfill(pos, mySize, '1 1 1', .2, DRAWFLAG_NORMAL);
-
- if(autocvar_hud_configure_grid)
- {
- pos.x = floor((pos.x/vid_conwidth)/hud_configure_gridSize.x + 0.5) * hud_configure_realGridSize.x;
- pos.y = floor((pos.y/vid_conheight)/hud_configure_gridSize.y + 0.5) * hud_configure_realGridSize.y;
- }
-
- if(hud_configure_checkcollisions)
- pos = HUD_Panel_CheckMove(pos, mySize);
-
- pos.x = bound(0, pos.x, vid_conwidth - mySize.x);
- pos.y = bound(0, pos.y, vid_conheight - mySize.y);
-
- string s;
- s = strcat(ftos(pos.x/vid_conwidth), " ", ftos(pos.y/vid_conheight));
-
- cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s);
-}
-
-// check if resize will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector
-vector HUD_Panel_CheckResize(vector mySize, vector resizeorigin) {
- vector targEndPos;
- vector dist;
- float ratio = mySize.x/mySize.y;
- int i;
- for (i = 0; i < hud_panels_COUNT; ++i) {
- panel = hud_panels[i];
- if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue;
- if(panel == highlightedPanel) continue;
- HUD_Panel_UpdatePosSize();
- if(!panel_enabled) continue;
-
- panel_pos -= '1 1 0' * panel_bg_border;
- panel_size += '2 2 0' * panel_bg_border;
-
- targEndPos = panel_pos + panel_size;
-
- // resizeorigin is WITHIN target panel, just abort any collision testing against that particular panel to produce expected behaviour!
- if(resizeorigin.x > panel_pos.x && resizeorigin.x < targEndPos.x && resizeorigin.y > panel_pos.y && resizeorigin.y < targEndPos.y)
- continue;
-
- if (resizeCorner == 1)
- {
- // check if this panel is on our way
- if (resizeorigin.x <= panel_pos.x)
- continue;
- if (resizeorigin.y <= panel_pos.y)
- continue;
- if (targEndPos.x <= resizeorigin.x - mySize.x)
- continue;
- if (targEndPos.y <= resizeorigin.y - mySize.y)
- continue;
-
- // there is a collision:
- // detect which side of the panel we are facing is actually limiting the resizing
- // (which side the resize direction finds for first) and reduce the size up to there
- //
- // dist is the distance between resizeorigin and the "analogous" point of the panel
- // in this case between resizeorigin (bottom-right point) and the bottom-right point of the panel
- dist.x = resizeorigin.x - targEndPos.x;
- dist.y = resizeorigin.y - targEndPos.y;
- if (dist.y <= 0 || dist.x / dist.y > ratio)
- mySize.x = min(mySize.x, dist.x);
- else
- mySize.y = min(mySize.y, dist.y);
- }
- else if (resizeCorner == 2)
- {
- if (resizeorigin.x >= targEndPos.x)
- continue;
- if (resizeorigin.y <= panel_pos.y)
- continue;
- if (panel_pos.x >= resizeorigin.x + mySize.x)
- continue;
- if (targEndPos.y <= resizeorigin.y - mySize.y)
- continue;
-
- dist.x = panel_pos.x - resizeorigin.x;
- dist.y = resizeorigin.y - targEndPos.y;
- if (dist.y <= 0 || dist.x / dist.y > ratio)
- mySize.x = min(mySize.x, dist.x);
- else
- mySize.y = min(mySize.y, dist.y);
- }
- else if (resizeCorner == 3)
- {
- if (resizeorigin.x <= panel_pos.x)
- continue;
- if (resizeorigin.y >= targEndPos.y)
- continue;
- if (targEndPos.x <= resizeorigin.x - mySize.x)
- continue;
- if (panel_pos.y >= resizeorigin.y + mySize.y)
- continue;
-
- dist.x = resizeorigin.x - targEndPos.x;
- dist.y = panel_pos.y - resizeorigin.y;
- if (dist.y <= 0 || dist.x / dist.y > ratio)
- mySize.x = min(mySize.x, dist.x);
- else
- mySize.y = min(mySize.y, dist.y);
- }
- else if (resizeCorner == 4)
- {
- if (resizeorigin.x >= targEndPos.x)
- continue;
- if (resizeorigin.y >= targEndPos.y)
- continue;
- if (panel_pos.x >= resizeorigin.x + mySize.x)
- continue;
- if (panel_pos.y >= resizeorigin.y + mySize.y)
- continue;
-
- dist.x = panel_pos.x - resizeorigin.x;
- dist.y = panel_pos.y - resizeorigin.y;
- if (dist.y <= 0 || dist.x / dist.y > ratio)
- mySize.x = min(mySize.x, dist.x);
- else
- mySize.y = min(mySize.y, dist.y);
- }
- //if(cvar("hud_configure_checkcollisions_debug"))
- //drawfill(panel_pos, panel_size, '1 1 0', .3, DRAWFLAG_NORMAL);
- }
-
- return mySize;
-}
-
-void HUD_Panel_SetPosSize(vector mySize)
-{
- panel = highlightedPanel;
- HUD_Panel_UpdatePosSize();
- vector resizeorigin = panel_click_resizeorigin;
- vector myPos;
-
- // minimum panel size cap
- mySize.x = max(0.025 * vid_conwidth, mySize.x);
- mySize.y = max(0.025 * vid_conheight, mySize.y);
-
- if(highlightedPanel == HUD_PANEL(CHAT)) // some panels have their own restrictions, like the chat panel (which actually only moves the engine chat print around). Looks bad if it's too small.
- {
- mySize.x = max(17 * autocvar_con_chatsize, mySize.x);
- mySize.y = max(2 * autocvar_con_chatsize + 2 * panel_bg_padding, mySize.y);
- }
-
- // collision testing|
- // -----------------+
-
- // we need to know pos at this stage, but it might still change later if we hit a screen edge/other panel (?)
- if(resizeCorner == 1) {
- myPos.x = resizeorigin.x - mySize.x;
- myPos.y = resizeorigin.y - mySize.y;
- } else if(resizeCorner == 2) {
- myPos.x = resizeorigin.x;
- myPos.y = resizeorigin.y - mySize.y;
- } else if(resizeCorner == 3) {
- myPos.x = resizeorigin.x - mySize.x;
- myPos.y = resizeorigin.y;
- } else { // resizeCorner == 4
- myPos.x = resizeorigin.x;
- myPos.y = resizeorigin.y;
- }
-
- // left/top screen edges
- if(myPos.x < 0)
- mySize.x = mySize.x + myPos.x;
- if(myPos.y < 0)
- mySize.y = mySize.y + myPos.y;
-
- // bottom/right screen edges
- if(myPos.x + mySize.x > vid_conwidth)
- mySize.x = vid_conwidth - myPos.x;
- if(myPos.y + mySize.y > vid_conheight)
- mySize.y = vid_conheight - myPos.y;
-
- //if(cvar("hud_configure_checkcollisions_debug"))
- //drawfill(myPos, mySize, '1 1 1', .2, DRAWFLAG_NORMAL);
-
- // before checkresize, otherwise panel can be snapped partially inside another panel or panel aspect ratio can be broken
- if(autocvar_hud_configure_grid)
- {
- mySize.x = floor((mySize.x/vid_conwidth)/hud_configure_gridSize.x + 0.5) * hud_configure_realGridSize.x;
- mySize.y = floor((mySize.y/vid_conheight)/hud_configure_gridSize.y + 0.5) * hud_configure_realGridSize.y;
- }
-
- if(hud_configure_checkcollisions)
- mySize = HUD_Panel_CheckResize(mySize, resizeorigin);
-
- // minimum panel size cap, do this once more so we NEVER EVER EVER have a panel smaller than this, JUST IN CASE above code still makes the panel eg negative (impossible to resize back without changing cvars manually then)
- mySize.x = max(0.025 * vid_conwidth, mySize.x);
- mySize.y = max(0.025 * vid_conheight, mySize.y);
-
- // do another pos check, as size might have changed by now
- if(resizeCorner == 1) {
- myPos.x = resizeorigin.x - mySize.x;
- myPos.y = resizeorigin.y - mySize.y;
- } else if(resizeCorner == 2) {
- myPos.x = resizeorigin.x;
- myPos.y = resizeorigin.y - mySize.y;
- } else if(resizeCorner == 3) {
- myPos.x = resizeorigin.x - mySize.x;
- myPos.y = resizeorigin.y;
- } else { // resizeCorner == 4
- myPos.x = resizeorigin.x;
- myPos.y = resizeorigin.y;
- }
-
- //if(cvar("hud_configure_checkcollisions_debug"))
- //drawfill(myPos, mySize, '0 1 0', .3, DRAWFLAG_NORMAL);
-
- string s;
- s = strcat(ftos(mySize.x/vid_conwidth), " ", ftos(mySize.y/vid_conheight));
- cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s);
-
- s = strcat(ftos(myPos.x/vid_conwidth), " ", ftos(myPos.y/vid_conheight));
- cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s);
-}
-
-float pressed_key_time;
-vector highlightedPanel_initial_pos, highlightedPanel_initial_size;
-void HUD_Panel_Arrow_Action(float nPrimary)
-{
- if(!highlightedPanel)
- return;
-
- hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions);
-
- float step;
- if(autocvar_hud_configure_grid)
- {
- if (nPrimary == K_UPARROW || nPrimary == K_DOWNARROW)
- {
- if (hudShiftState & S_SHIFT)
- step = hud_configure_realGridSize.y;
- else
- step = 2 * hud_configure_realGridSize.y;
- }
- else
- {
- if (hudShiftState & S_SHIFT)
- step = hud_configure_realGridSize.x;
- else
- step = 2 * hud_configure_realGridSize.x;
- }
- }
- else
- {
- if (nPrimary == K_UPARROW || nPrimary == K_DOWNARROW)
- step = vid_conheight;
- else
- step = vid_conwidth;
- if (hudShiftState & S_SHIFT)
- step = (step / 256); // more precision
- else
- step = (step / 64) * (1 + 2 * (time - pressed_key_time));
- }
-
- panel = highlightedPanel;
- HUD_Panel_UpdatePosSize();
-
- highlightedPanel_initial_pos = panel_pos;
- highlightedPanel_initial_size = panel_size;
-
- if (hudShiftState & S_ALT) // resize
- {
- if(nPrimary == K_UPARROW)
- resizeCorner = 1;
- else if(nPrimary == K_RIGHTARROW)
- resizeCorner = 2;
- else if(nPrimary == K_LEFTARROW)
- resizeCorner = 3;
- else // if(nPrimary == K_DOWNARROW)
- resizeCorner = 4;
-
- // ctrl+arrow reduces the size, instead of increasing it
- // Note that ctrl disables collisions check too, but it's fine
- // since we don't collide with anything reducing the size
- if (hudShiftState & S_CTRL) {
- step = -step;
- resizeCorner = 5 - resizeCorner;
- }
-
- vector mySize;
- mySize = panel_size;
- panel_click_resizeorigin = panel_pos;
- if(resizeCorner == 1) {
- panel_click_resizeorigin += mySize;
- mySize.y += step;
- } else if(resizeCorner == 2) {
- panel_click_resizeorigin.y += mySize.y;
- mySize.x += step;
- } else if(resizeCorner == 3) {
- panel_click_resizeorigin.x += mySize.x;
- mySize.x += step;
- } else { // resizeCorner == 4
- mySize.y += step;
- }
- HUD_Panel_SetPosSize(mySize);
- }
- else // move
- {
- vector pos;
- pos = panel_pos;
- if(nPrimary == K_UPARROW)
- pos.y -= step;
- else if(nPrimary == K_DOWNARROW)
- pos.y += step;
- else if(nPrimary == K_LEFTARROW)
- pos.x -= step;
- else // if(nPrimary == K_RIGHTARROW)
- pos.x += step;
-
- HUD_Panel_SetPos(pos);
- }
-
- panel = highlightedPanel;
- HUD_Panel_UpdatePosSize();
-
- if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size)
- {
- // backup!
- panel_pos_backup = highlightedPanel_initial_pos;
- panel_size_backup = highlightedPanel_initial_size;
- highlightedPanel_backup = highlightedPanel;
- }
-}
-
-void HUD_Panel_EnableMenu();
-entity tab_panels[hud_panels_MAX];
-entity tab_panel;
-vector tab_panel_pos;
-float tab_backward;
-void HUD_Panel_FirstInDrawQ(float id);
-void reset_tab_panels()
-{
- int i;
- for(i = 0; i < hud_panels_COUNT; ++i)
- tab_panels[i] = world;
-}
-float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary)
-{
- string s;
-
- if(bInputType == 2)
- return false;
-
- if(!autocvar__hud_configure)
- return false;
-
- if(bInputType == 3)
- {
- mousepos.x = nPrimary;
- mousepos.y = nSecondary;
- return true;
- }
-
- // block any input while a menu dialog is fading
- // don't block mousepos read as it leads to cursor jumps in the interaction with the menu
- if(autocvar__menu_alpha)
- {
- hudShiftState = 0;
- mouseClicked = 0;
- return true;
- }
-
- // allow console bind to work
- string con_keys;
- float keys;
- con_keys = findkeysforcommand("toggleconsole", 0);
- keys = tokenize(con_keys); // findkeysforcommand returns data for this
-
- bool hit_con_bind = false;
- int i;
- for (i = 0; i < keys; ++i)
- {
- if(nPrimary == stof(argv(i)))
- hit_con_bind = true;
- }
-
- if(bInputType == 0) {
- if(nPrimary == K_ALT) hudShiftState |= S_ALT;
- if(nPrimary == K_CTRL) hudShiftState |= S_CTRL;
- if(nPrimary == K_SHIFT) hudShiftState |= S_SHIFT;
- }
- else if(bInputType == 1) {
- if(nPrimary == K_ALT) hudShiftState -= (hudShiftState & S_ALT);
- if(nPrimary == K_CTRL) hudShiftState -= (hudShiftState & S_CTRL);
- if(nPrimary == K_SHIFT) hudShiftState -= (hudShiftState & S_SHIFT);
- }
-
- if(nPrimary == K_CTRL)
- {
- if (bInputType == 1) //ctrl has been released
- {
- if (tab_panel)
- {
- //switch to selected panel
- highlightedPanel = tab_panel;
- highlightedAction = 0;
- HUD_Panel_FirstInDrawQ(highlightedPanel.panel_id);
- }
- tab_panel = world;
- reset_tab_panels();
- }
- }
-
- if(nPrimary == K_MOUSE1)
- {
- if(bInputType == 0) // key pressed
- mouseClicked |= S_MOUSE1;
- else if(bInputType == 1) // key released
- mouseClicked -= (mouseClicked & S_MOUSE1);
- }
- else if(nPrimary == K_MOUSE2)
- {
- if(bInputType == 0) // key pressed
- mouseClicked |= S_MOUSE2;
- else if(bInputType == 1) // key released
- mouseClicked -= (mouseClicked & S_MOUSE2);
- }
- else if(nPrimary == K_ESCAPE)
- {
- if (bInputType == 1)
- return true;
- menu_enabled = 1;
- localcmd("menu_showhudexit\n");
- }
- else if(nPrimary == K_BACKSPACE && hudShiftState & S_CTRL)
- {
- if (bInputType == 1)
- return true;
- if (!menu_enabled)
- cvar_set("_hud_configure", "0");
- }
- else if(nPrimary == K_TAB && hudShiftState & S_CTRL) // switch panel
- {
- if (bInputType == 1 || mouseClicked)
- return true;
-
- // FIXME minor bug: if a panel is highlighted, has the same pos_x and
- // lays in the same level of another panel then the next consecutive
- // CTRL TAB presses will reselect once more the highlighted panel
-
- entity starting_panel;
- entity old_tab_panel = tab_panel;
- if (!tab_panel) //first press of TAB
- {
- if (highlightedPanel)
- {
- panel = highlightedPanel;
- HUD_Panel_UpdatePosSize();
- }
- else
- panel_pos = '0 0 0';
- starting_panel = highlightedPanel;
- tab_panel_pos = panel_pos; //to compute level
- }
- else
- {
- if ( ((!tab_backward) && (hudShiftState & S_SHIFT)) || (tab_backward && !(hudShiftState & S_SHIFT)) ) //tab direction changed?
- reset_tab_panels();
- starting_panel = tab_panel;
- }
- tab_backward = (hudShiftState & S_SHIFT);
-
- float k, level = 0, start_posX;
- vector candidate_pos = '0 0 0';
- const float LEVELS_NUM = 4;
- float level_height = vid_conheight / LEVELS_NUM;
-:find_tab_panel
- level = floor(tab_panel_pos.y / level_height) * level_height; //starting level
- candidate_pos.x = (!tab_backward) ? vid_conwidth : 0;
- start_posX = tab_panel_pos.x;
- tab_panel = world;
- k=0;
- while(++k)
- {
- for(i = 0; i < hud_panels_COUNT; ++i)
- {
- panel = hud_panels[i];
- if(!(panel.panel_configflags & PANEL_CONFIG_MAIN))
- continue;
- if (panel == tab_panels[i] || panel == starting_panel)
- continue;
- HUD_Panel_UpdatePosSize();
- if (panel_pos.y >= level && (panel_pos.y - level) < level_height)
- if ( ( !tab_backward && panel_pos.x >= start_posX && (panel_pos.x < candidate_pos.x || (panel_pos.x == candidate_pos.x && panel_pos.y <= candidate_pos.y)) )
- || ( tab_backward && panel_pos.x <= start_posX && (panel_pos.x > candidate_pos.x || (panel_pos.x == candidate_pos.x && panel_pos.y >= candidate_pos.y)) ) )
- {
- tab_panel = panel;
- tab_panel_pos = candidate_pos = panel_pos;
- }
- }
- if (tab_panel)
- break;
- if (k == LEVELS_NUM) //tab_panel not found
- {
- reset_tab_panels();
- if (!old_tab_panel)
- {
- tab_panel = world;
- return true;
- }
- starting_panel = old_tab_panel;
- old_tab_panel = world;
- goto find_tab_panel; //u must find tab_panel!
- }
- if (!tab_backward)
- {
- level = (level + level_height) % vid_conheight;
- start_posX = 0;
- candidate_pos.x = vid_conwidth;
- }
- else
- {
- level = (level - level_height) % vid_conheight;
- start_posX = vid_conwidth;
- candidate_pos.x = 0;
- }
- }
-
- tab_panels[tab_panel.panel_id] = tab_panel;
- }
- else if(nPrimary == K_SPACE && hudShiftState & S_CTRL) // enable/disable highlighted panel or dock
- {
- if (bInputType == 1 || mouseClicked)
- return true;
-
- if (highlightedPanel)
- cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name))));
- else
- cvar_set(strcat("hud_dock"), (autocvar_hud_dock == "") ? "dock" : "");
- }
- else if(nPrimary == 'c' && hudShiftState & S_CTRL) // copy highlighted panel size
- {
- if (bInputType == 1 || mouseClicked)
- return true;
-
- if (highlightedPanel)
- {
- panel = highlightedPanel;
- HUD_Panel_UpdatePosSize();
- panel_size_copied = panel_size;
- }
- }
- else if(nPrimary == 'v' && hudShiftState & S_CTRL) // past copied size on the highlighted panel
- {
- if (bInputType == 1 || mouseClicked)
- return true;
-
- if (panel_size_copied == '0 0 0' || !highlightedPanel)
- return true;
-
- panel = highlightedPanel;
- HUD_Panel_UpdatePosSize();
-
- // reduce size if it'd go beyond screen boundaries
- vector tmp_size = panel_size_copied;
- if (panel_pos.x + panel_size_copied.x > vid_conwidth)
- tmp_size.x = vid_conwidth - panel_pos.x;
- if (panel_pos.y + panel_size_copied.y > vid_conheight)
- tmp_size.y = vid_conheight - panel_pos.y;
-
- if (panel_size == tmp_size)
- return true;
-
- // backup first!
- panel_pos_backup = panel_pos;
- panel_size_backup = panel_size;
- highlightedPanel_backup = highlightedPanel;
-
- s = strcat(ftos(tmp_size.x/vid_conwidth), " ", ftos(tmp_size.y/vid_conheight));
- cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s);
- }
- else if(nPrimary == 'z' && hudShiftState & S_CTRL) // undo last action
- {
- if (bInputType == 1 || mouseClicked)
- return true;
- //restore previous values
- if (highlightedPanel_backup)
- {
- s = strcat(ftos(panel_pos_backup.x/vid_conwidth), " ", ftos(panel_pos_backup.y/vid_conheight));
- cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_pos"), s);
- s = strcat(ftos(panel_size_backup.x/vid_conwidth), " ", ftos(panel_size_backup.y/vid_conheight));
- cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_size"), s);
- highlightedPanel_backup = world;
- }
- }
- else if(nPrimary == 's' && hudShiftState & S_CTRL) // save config
- {
- if (bInputType == 1 || mouseClicked)
- return true;
- localcmd("hud save myconfig\n");
- }
- else if(nPrimary == K_UPARROW || nPrimary == K_DOWNARROW || nPrimary == K_LEFTARROW || nPrimary == K_RIGHTARROW)
- {
- if (bInputType == 1)
- {
- pressed_key_time = 0;
- return true;
- }
- else if (pressed_key_time == 0)
- pressed_key_time = time;
-
- if (!mouseClicked)
- HUD_Panel_Arrow_Action(nPrimary); //move or resize panel
- }
- else if(nPrimary == K_ENTER || nPrimary == K_SPACE || nPrimary == K_KP_ENTER)
- {
- if (bInputType == 1)
- return true;
- if (highlightedPanel)
- HUD_Panel_EnableMenu();
- }
- else if(hit_con_bind || nPrimary == K_PAUSE)
- return false;
-
- return true;
-}
-
-float HUD_Panel_Check_Mouse_Pos(float allow_move)
-{
- int i, j = 0;
- while(j < hud_panels_COUNT)
- {
- i = panel_order[j];
- j += 1;
-
- panel = hud_panels[i];
- if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue;
- HUD_Panel_UpdatePosSize();
-
- float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
-
- // move
- if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y)
- {
- return 1;
- }
- // resize from topleft border
- else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + 0.5 * panel_size.y)
- {
- return 2;
- }
- // resize from topright border
- else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + 0.5 * panel_size.y)
- {
- return 3;
- }
- // resize from bottomleft border
- else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + panel_size.y + border)
- {
- return 3;
- }
- // resize from bottomright border
- else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + panel_size.y + border)
- {
- return 2;
- }
- }
- return 0;
-}
-
-// move a panel to the beginning of the panel order array (which means it gets drawn last, on top of everything else)
-void HUD_Panel_FirstInDrawQ(float id)
-{
- int i;
- int place = -1;
- // find out where in the array our current id is, save into place
- for(i = 0; i < hud_panels_COUNT; ++i)
- {
- if(panel_order[i] == id)
- {
- place = i;
- break;
- }
- }
- // place last if we didn't find a place for it yet (probably new panel, or screwed up cvar)
- if(place == -1)
- place = hud_panels_COUNT - 1;
-
- // move all ids up by one step in the array until "place"
- for(i = place; i > 0; --i)
- {
- panel_order[i] = panel_order[i-1];
- }
- // now save the new top id
- panel_order[0] = id;
-
- // let's save them into the cvar by some strcat trickery
- string s = "";
- for(i = 0; i < hud_panels_COUNT; ++i)
- {
- s = strcat(s, ftos(panel_order[i]), " ");
- }
- cvar_set("_hud_panelorder", s);
- if(hud_panelorder_prev)
- strunzone(hud_panelorder_prev);
- hud_panelorder_prev = strzone(autocvar__hud_panelorder); // prevent HUD_Main from doing useless update, we already updated here
-}
-
-void HUD_Panel_Highlight(float allow_move)
-{
- int i, j = 0;
-
- while(j < hud_panels_COUNT)
- {
- i = panel_order[j];
- j += 1;
-
- panel = hud_panels[i];
- if(!(panel.panel_configflags & PANEL_CONFIG_MAIN))
- continue;
- HUD_Panel_UpdatePosSize();
-
- float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize
-
- // move
- if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y)
- {
- highlightedPanel = hud_panels[i];
- HUD_Panel_FirstInDrawQ(i);
- highlightedAction = 1;
- panel_click_distance = mousepos - panel_pos;
- return;
- }
- // resize from topleft border
- else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + 0.5 * panel_size.y)
- {
- highlightedPanel = hud_panels[i];
- HUD_Panel_FirstInDrawQ(i);
- highlightedAction = 2;
- resizeCorner = 1;
- panel_click_distance = mousepos - panel_pos;
- panel_click_resizeorigin = panel_pos + panel_size;
- return;
- }
- // resize from topright border
- else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + 0.5 * panel_size.y)
- {
- highlightedPanel = hud_panels[i];
- HUD_Panel_FirstInDrawQ(i);
- highlightedAction = 2;
- resizeCorner = 2;
- panel_click_distance.x = panel_size.x - mousepos.x + panel_pos.x;
- panel_click_distance.y = mousepos.y - panel_pos.y;
- panel_click_resizeorigin = panel_pos + eY * panel_size.y;
- return;
- }
- // resize from bottomleft border
- else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + panel_size.y + border)
- {
- highlightedPanel = hud_panels[i];
- HUD_Panel_FirstInDrawQ(i);
- highlightedAction = 2;
- resizeCorner = 3;
- panel_click_distance.x = mousepos.x - panel_pos.x;
- panel_click_distance.y = panel_size.y - mousepos.y + panel_pos.y;
- panel_click_resizeorigin = panel_pos + eX * panel_size.x;
- return;
- }
- // resize from bottomright border
- else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + panel_size.y + border)
- {
- highlightedPanel = hud_panels[i];
- HUD_Panel_FirstInDrawQ(i);
- highlightedAction = 2;
- resizeCorner = 4;
- panel_click_distance = panel_size - mousepos + panel_pos;
- panel_click_resizeorigin = panel_pos;
- return;
- }
- }
- highlightedPanel = world;
- highlightedAction = 0;
-}
-
-void HUD_Panel_EnableMenu()
-{
- menu_enabled = 2;
- localcmd("menu_showhudoptions ", highlightedPanel.panel_name, "\n");
-}
-float mouse_over_panel;
-void HUD_Panel_Mouse()
-{
- if(autocvar__menu_alpha == 1)
- return;
-
- if (!autocvar_hud_cursormode)
- {
- mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed;
-
- mousepos.x = bound(0, mousepos.x, vid_conwidth);
- mousepos.y = bound(0, mousepos.y, vid_conheight);
- }
-
- if(mouseClicked)
- {
- if(prevMouseClicked == 0)
- {
- if (tab_panel)
- {
- //stop ctrl-tab selection
- tab_panel = world;
- reset_tab_panels();
- }
- HUD_Panel_Highlight(mouseClicked & S_MOUSE1); // sets highlightedPanel, highlightedAction, panel_click_distance, panel_click_resizeorigin
- // and calls HUD_Panel_UpdatePosSize() for the highlighted panel
- if (highlightedPanel)
- {
- highlightedPanel_initial_pos = panel_pos;
- highlightedPanel_initial_size = panel_size;
- }
- // doubleclick check
- if ((mouseClicked & S_MOUSE1) && time - prevMouseClickedTime < 0.4 && highlightedPanel && prevMouseClickedPos == mousepos)
- {
- mouseClicked = 0; // to prevent spam, I guess.
- HUD_Panel_EnableMenu();
- }
- else
- {
- if (mouseClicked & S_MOUSE1)
- {
- prevMouseClickedTime = time;
- prevMouseClickedPos = mousepos;
- }
- mouse_over_panel = HUD_Panel_Check_Mouse_Pos(mouseClicked & S_MOUSE1);
- }
- }
- else
- {
- panel = highlightedPanel;
- HUD_Panel_UpdatePosSize();
- }
-
- if (highlightedPanel)
- {
- drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL);
- if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size)
- {
- hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions);
- // backup!
- panel_pos_backup = highlightedPanel_initial_pos;
- panel_size_backup = highlightedPanel_initial_size;
- highlightedPanel_backup = highlightedPanel;
- }
- else
- // in case the clicked panel is inside another panel and we aren't
- // moving it, avoid the immediate "fix" of its position/size
- // (often unwanted and hateful) by disabling collisions check
- hud_configure_checkcollisions = false;
- }
-
- if(highlightedAction == 1)
- HUD_Panel_SetPos(mousepos - panel_click_distance);
- else if(highlightedAction == 2)
- {
- vector mySize = '0 0 0';
- if(resizeCorner == 1) {
- mySize.x = panel_click_resizeorigin.x - (mousepos.x - panel_click_distance.x);
- mySize.y = panel_click_resizeorigin.y - (mousepos.y - panel_click_distance.y);
- } else if(resizeCorner == 2) {
- mySize.x = mousepos.x + panel_click_distance.x - panel_click_resizeorigin.x;
- mySize.y = panel_click_distance.y + panel_click_resizeorigin.y - mousepos.y;
- } else if(resizeCorner == 3) {
- mySize.x = panel_click_resizeorigin.x + panel_click_distance.x - mousepos.x;
- mySize.y = mousepos.y + panel_click_distance.y - panel_click_resizeorigin.y;
- } else { // resizeCorner == 4
- mySize.x = mousepos.x - (panel_click_resizeorigin.x - panel_click_distance.x);
- mySize.y = mousepos.y - (panel_click_resizeorigin.y - panel_click_distance.y);
- }
- HUD_Panel_SetPosSize(mySize);
- }
- }
- else
- {
- if(prevMouseClicked)
- highlightedAction = 0;
- if(menu_enabled == 2)
- mouse_over_panel = 0;
- else
- mouse_over_panel = HUD_Panel_Check_Mouse_Pos(true);
- if (mouse_over_panel && !tab_panel)
- drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL);
- }
- // draw cursor after performing move/resize to have the panel pos/size updated before mouse_over_panel
- const vector cursorsize = '32 32 0';
- float cursor_alpha = 1 - autocvar__menu_alpha;
-
- if(!mouse_over_panel)
- drawpic(mousepos, strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL);
- else if(mouse_over_panel == 1)
- drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_move.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL);
- else if(mouse_over_panel == 2)
- drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_resize.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL);
- else
- drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_resize2.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL);
-
- prevMouseClicked = mouseClicked;
-}
-void HUD_Configure_DrawGrid()
-{
- float i;
- if(autocvar_hud_configure_grid && autocvar_hud_configure_grid_alpha)
- {
- hud_configure_gridSize.x = bound(0.005, cvar("hud_configure_grid_xsize"), 0.2);
- hud_configure_gridSize.y = bound(0.005, cvar("hud_configure_grid_ysize"), 0.2);
- hud_configure_realGridSize.x = hud_configure_gridSize.x * vid_conwidth;
- hud_configure_realGridSize.y = hud_configure_gridSize.y * vid_conheight;
- vector s;
- // x-axis
- s = eX + eY * vid_conheight;
- for(i = 1; i < 1/hud_configure_gridSize.x; ++i)
- drawfill(eX * i * hud_configure_realGridSize.x, s, '0.5 0.5 0.5', autocvar_hud_configure_grid_alpha, DRAWFLAG_NORMAL);
- // y-axis
- s = eY + eX * vid_conwidth;
- for(i = 1; i < 1/hud_configure_gridSize.y; ++i)
- drawfill(eY * i * hud_configure_realGridSize.y, s, '0.5 0.5 0.5', autocvar_hud_configure_grid_alpha, DRAWFLAG_NORMAL);
- }
-}
-
-float _menu_alpha_prev;
-void HUD_Configure_Frame()
-{
- int i;
- if(autocvar__hud_configure)
- {
- if(isdemo() || intermission == 2)
- {
- HUD_Configure_Exit_Force();
- return;
- }
-
- if(!hud_configure_prev)
- {
- if(autocvar_hud_cursormode)
- setcursormode(1);
- hudShiftState = 0;
- for(i = hud_panels_COUNT - 1; i >= 0; --i)
- hud_panels[panel_order[i]].update_time = time;
- }
-
- // NOTE this check is necessary because _menu_alpha isn't updated the frame the menu gets enabled
- if(autocvar__menu_alpha != _menu_alpha_prev)
- {
- if(autocvar__menu_alpha == 0)
- menu_enabled = 0;
- _menu_alpha_prev = autocvar__menu_alpha;
- }
-
- HUD_Configure_DrawGrid();
- }
- else if(hud_configure_prev)
- {
- if(menu_enabled)
- menu_enabled = 0;
- if(autocvar_hud_cursormode)
- setcursormode(0);
- }
-}
-
-const float hlBorderSize = 2;
-const string hlBorder = "gfx/hud/default/border_highlighted";
-const string hlBorder2 = "gfx/hud/default/border_highlighted2";
-void HUD_Panel_HlBorder(float myBorder, vector color, float theAlpha)
-{
- drawfill(panel_pos - '1 1 0' * myBorder, panel_size + '2 2 0' * myBorder, '0 0.5 1', .5 * theAlpha, DRAWFLAG_NORMAL);
- drawpic_tiled(panel_pos - '1 1 0' * myBorder, hlBorder, '8 1 0' * hlBorderSize, eX * (panel_size.x + 2 * myBorder) + eY * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL);
- drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * (panel_size.y + 2 * myBorder - hlBorderSize), hlBorder, '8 1 0' * hlBorderSize, eX * (panel_size.x + 2 * myBorder) + eY * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL);
- drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * hlBorderSize, hlBorder2, '1 8 0' * hlBorderSize, eY * (panel_size.y + 2 * myBorder - 2 * hlBorderSize) + eX * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL);
- drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * hlBorderSize + eX * (panel_size.x + 2 * myBorder - hlBorderSize), hlBorder2, '1 8 0' * hlBorderSize, eY * (panel_size.y + 2 * myBorder - 2 * hlBorderSize) + eX * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL);
-}
-
-void HUD_Configure_PostDraw()
-{
- if(autocvar__hud_configure)
- {
- if(tab_panel)
- {
- panel = tab_panel;
- HUD_Panel_UpdatePosSize();
- drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .2, DRAWFLAG_NORMAL);
- }
- if(highlightedPanel)
- {
- panel = highlightedPanel;
- HUD_Panel_UpdatePosSize();
- HUD_Panel_HlBorder(panel_bg_border * hlBorderSize, '0 0.5 1', 0.4 * (1 - autocvar__menu_alpha));
- }
- }
-}
+++ /dev/null
-#ifndef CLIENT_HUD_CONFIG_H
-#define CLIENT_HUD_CONFIG_H
-
-const int S_MOUSE1 = 1;
-const int S_MOUSE2 = 2;
-const int S_MOUSE3 = 4;
-int mouseClicked;
-int prevMouseClicked; // previous state
-float prevMouseClickedTime; // time during previous left mouse click, to check for doubleclicks
-vector prevMouseClickedPos; // pos during previous left mouse click, to check for doubleclicks
-
-void HUD_Panel_ExportCfg(string cfgname);
-
-void HUD_Panel_Mouse();
-
-void HUD_Configure_Frame();
-
-void HUD_Configure_PostDraw();
-
-float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary);
-
-#endif
+++ /dev/null
-#include "laser.qh"
-
-#include "../lib/csqcmodel/interpolate.qh"
-
-// a laser goes from origin in direction angles
-// it has color 'colormod'
-// and stops when something is in the way
-entityclass(Laser);
-class(Laser) .int cnt; // end effect
-class(Laser) .vector colormod;
-class(Laser) .int state; // on-off
-class(Laser) .int count; // flags for the laser
-class(Laser) .vector velocity;
-class(Laser) .float alpha;
-class(Laser) .float scale; // scaling factor of the thickness
-class(Laser) .float modelscale; // scaling factor of the dlight
-
-void Draw_Laser(entity this)
-{
- if(!self.state)
- return;
- InterpolateOrigin_Do();
- if(self.count & 0x80)
- {
- if(self.count & 0x10)
- {
- trace_endpos = self.velocity;
- trace_dphitq3surfaceflags = 0;
- }
- else
- traceline(self.origin, self.velocity, 0, self);
- }
- else
- {
- if(self.count & 0x10)
- {
- makevectors(self.angles);
- trace_endpos = self.origin + v_forward * 1048576;
- trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY;
- }
- else
- {
- makevectors(self.angles);
- traceline(self.origin, self.origin + v_forward * 32768, 0, self);
- if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
- trace_endpos = self.origin + v_forward * 1048576;
- }
- }
- if(self.scale != 0)
- {
- if(self.alpha)
- {
- Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, self.alpha, DRAWFLAG_NORMAL, view_origin);
- }
- else
- {
- Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, 0.5, DRAWFLAG_ADDITIVE, view_origin);
- }
- }
- if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
- {
- if(self.cnt >= 0)
- pointparticles(self.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
- if(self.colormod != '0 0 0' && self.modelscale != 0)
- adddynamiclight(trace_endpos + trace_plane_normal * 1, self.modelscale, self.colormod * 5);
- }
-}
-
-void Ent_Laser()
-{
- InterpolateOrigin_Undo();
-
- // 30 bytes, or 13 bytes for just moving
- int f = ReadByte();
- self.count = (f & 0xF0);
-
- if(self.count & 0x80)
- self.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
- else
- self.iflags = IFLAG_ANGLES | IFLAG_ORIGIN;
-
- if(f & 1)
- {
- self.origin_x = ReadCoord();
- self.origin_y = ReadCoord();
- self.origin_z = ReadCoord();
- setorigin(self, self.origin);
- }
- if(f & 8)
- {
- self.colormod_x = ReadByte() / 255.0;
- self.colormod_y = ReadByte() / 255.0;
- self.colormod_z = ReadByte() / 255.0;
- if(f & 0x40)
- self.alpha = ReadByte() / 255.0;
- else
- self.alpha = 0;
- self.scale = 2;
- self.modelscale = 50;
- if(f & 0x20)
- {
- self.scale *= ReadByte() / 16.0; // beam radius
- self.modelscale *= ReadByte() / 16.0; // dlight radius
- }
- if((f & 0x80) || !(f & 0x10))
- self.cnt = ReadShort() - 1; // effect number
- else
- self.cnt = 0;
- }
- if(f & 2)
- {
- if(f & 0x80)
- {
- self.velocity_x = ReadCoord();
- self.velocity_y = ReadCoord();
- self.velocity_z = ReadCoord();
- }
- else
- {
- self.angles_x = ReadAngle();
- self.angles_y = ReadAngle();
- }
- }
- if(f & 4)
- self.state = ReadByte();
- InterpolateOrigin_Note();
- self.draw = Draw_Laser;
-}
+++ /dev/null
-#ifndef CLIENT_LASER_H
-#define CLIENT_LASER_H
-
-void Ent_Laser();
-
-#endif
#include "main.qh"
-#include "controlpoint.qh"
-#include "damage.qh"
-#include "effects.qh"
-#include "generator.qh"
-#include "gibs.qh"
+#include "../common/effects/qc/all.qh"
#include "hook.qh"
-#include "hud.qh"
-#include "hud_config.qh"
-#include "laser.qh"
+#include "hud/all.qh"
#include "mapvoting.qh"
-#include "modeleffects.qh"
#include "mutators/events.qh"
-#include "particles.qh"
#include "quickmenu.qh"
#include "scoreboard.qh"
#include "shownames.qh"
#include "../common/minigames/cl_minigames_hud.qh"
#include "../common/net_notice.qh"
#include "../common/triggers/include.qh"
-#include "../common/turrets/cl_turrets.qh"
#include "../common/vehicles/all.qh"
#include "../lib/csqcmodel/cl_model.qh"
#include "../lib/csqcmodel/interpolate.qh"
// BEGIN REQUIRED CSQC FUNCTIONS
//include "main.qh"
-entity clearentity_ent;
-void clearentity(entity e)
-{
- if (!clearentity_ent)
- {
- clearentity_ent = spawn();
- clearentity_ent.classname = "clearentity";
- }
- int n = e.entnum;
- copyentity(clearentity_ent, e);
- e.entnum = n;
-}
-
#define DP_CSQC_ENTITY_REMOVE_IS_B0RKED
-void menu_show_error()
-{
- drawstring('0 200 0', _("ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!"), '8 8 0', '1 0 0', 1, 0);
-}
// CSQC_Init : Called every time the CSQC code is initialized (essentially at map load)
// Useful for precaching things
-void menu_sub_null()
-{
-}
-
-void draw_null(entity this) { }
-
-string forcefog;
void ConsoleCommand_macro_init();
-void CSQC_Init(void)
+void CSQC_Init()
{
prvm_language = strzone(cvar_string("prvm_language"));
LOG_TRACEF("^4CSQC Build information: ^1%s\n", WATERMARK);
#endif
- int i;
-
binddb = db_create();
tempdb = db_create();
ClientProgsDB = db_load("client.db");
compressShortVector_init();
draw_endBoldFont();
- menu_visible = false;
- menu_show = menu_show_error;
- menu_action = func_null;
- for(i = 0; i < 255; ++i)
- if(getplayerkeyvalue(i, "viewentity") == "")
- break;
- maxclients = i;
+ {
+ int i = 0;
+ for ( ; i < 255; ++i)
+ if (getplayerkeyvalue(i, "viewentity") == "")
+ break;
+ maxclients = i;
+ }
//registercommand("hud_configure");
//registercommand("hud_save");
gametype = 0;
// hud_fields uses strunzone on the titles!
- for(i = 0; i < MAX_HUD_FIELDS; ++i)
+ for(int i = 0; i < MAX_HUD_FIELDS; ++i)
hud_title[i] = strzone("(null)");
Cmd_HUD_SetFields(0);
// needs to be done so early because of the constants they create
static_init();
static_init_late();
+ static_init_precache();
// precaches
- Projectile_Precache();
- Tuba_Precache();
-
if(autocvar_cl_reticle)
{
precache_pic("gfx/reticle_normal");
// weapon reticles are precached in weapon files
}
- get_mi_min_max_texcoords(1); // try the CLEVER way first
- minimapname = strcat("gfx/", mi_shortname, "_radar.tga");
- shortmapname = mi_shortname;
-
- if(precache_pic(minimapname) == "")
{
- // but maybe we have a non-clever minimap
- minimapname = strcat("gfx/", mi_shortname, "_mini.tga");
- if(precache_pic(minimapname) == "")
- minimapname = ""; // FAIL
- else
- get_mi_min_max_texcoords(0); // load new texcoords
- }
+ get_mi_min_max_texcoords(1); // try the CLEVER way first
+ minimapname = strcat("gfx/", mi_shortname, "_radar.tga");
+ shortmapname = mi_shortname;
- mi_center = (mi_min + mi_max) * 0.5;
- mi_scale = mi_max - mi_min;
- minimapname = strzone(minimapname);
+ if (precache_pic(minimapname) == "")
+ {
+ // but maybe we have a non-clever minimap
+ minimapname = strcat("gfx/", mi_shortname, "_mini.tga");
+ if (precache_pic(minimapname) == "")
+ minimapname = ""; // FAIL
+ else
+ get_mi_min_max_texcoords(0); // load new texcoords
+ }
- WarpZone_Init();
+ mi_center = (mi_min + mi_max) * 0.5;
+ mi_scale = mi_max - mi_min;
+ minimapname = strzone(minimapname);
+ }
hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin));
draw_currentSkin = strzone(strcat("gfx/menu/", cvar_string("menu_skin")));
}
// CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)
-void Shutdown(void)
+void Shutdown()
{
WarpZone_Shutdown();
}
void Playerchecker_Think()
-{SELFPARAM();
+{
+ SELFPARAM();
int i;
entity e;
for(i = 0; i < maxclients; ++i)
{
// player connected
if (!e)
- playerslots[i] = e = spawn();
+ {
+ playerslots[i] = e = new(playerslot);
+ make_pure(e);
+ }
e.sv_entnum = i;
e.ping = 0;
e.ping_packetloss = 0;
}
}
}
- self.nextthink = time + 0.2;
+ this.nextthink = time + 0.2;
}
void Porto_Init();
void TrueAim_Init();
-void PostInit(void)
+void PostInit()
{
- entity playerchecker;
- playerchecker = spawn();
+ entity playerchecker = new(playerchecker);
+ make_pure(playerchecker);
playerchecker.think = Playerchecker_Think;
playerchecker.nextthink = time + 0.2;
// In the case of mouse input after a setcursormode(1) call, nPrimary is xpos, nSecondary is ypos.
float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)
{
- float bSkipKey;
- bSkipKey = false;
-
if (HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary))
return true;
if (QuickMenu_InputEvent(bInputType, nPrimary, nSecondary))
return true;
- if ( HUD_Radar_InputEvent(bInputType, nPrimary, nSecondary) )
+ if (HUD_Radar_InputEvent(bInputType, nPrimary, nSecondary))
return true;
if (MapVote_InputEvent(bInputType, nPrimary, nSecondary))
if (HUD_Minigame_InputEvent(bInputType, nPrimary, nSecondary))
return true;
- if(menu_visible && menu_action)
- if(menu_action(bInputType, nPrimary, nSecondary))
- return true;
-
- return bSkipKey;
+ return false;
}
// END REQUIRED CSQC FUNCTIONS
// BEGIN OPTIONAL CSQC FUNCTIONS
void Ent_RemoveEntCS()
-{SELFPARAM();
- entcs_receiver[self.sv_entnum] = NULL;
+{
+ SELFPARAM();
+ entcs_receiver[this.sv_entnum] = NULL;
}
-void Ent_ReadEntCS()
-{SELFPARAM();
+
+NET_HANDLE(ENT_CLIENT_ENTCS, bool isnew)
+{
+ make_pure(this);
+ this.classname = "entcs_receiver";
InterpolateOrigin_Undo();
- self.classname = "entcs_receiver";
int sf = ReadByte();
if(sf & BIT(0))
- self.sv_entnum = ReadByte();
+ this.sv_entnum = ReadByte();
if (sf & BIT(1))
{
- self.origin_x = ReadShort();
- self.origin_y = ReadShort();
- self.origin_z = ReadShort();
- setorigin(self, self.origin);
+ this.origin_x = ReadShort();
+ this.origin_y = ReadShort();
+ this.origin_z = ReadShort();
+ setorigin(this, this.origin);
}
if (sf & BIT(2))
{
- self.angles_y = ReadByte() * 360.0 / 256;
- self.angles_x = self.angles_z = 0;
+ this.angles_y = ReadByte() * 360.0 / 256;
+ this.angles_x = this.angles_z = 0;
}
if (sf & BIT(3))
- self.healthvalue = ReadByte() * 10;
+ this.healthvalue = ReadByte() * 10;
if (sf & BIT(4))
- self.armorvalue = ReadByte() * 10;
+ this.armorvalue = ReadByte() * 10;
+
+ return = true;
- entcs_receiver[self.sv_entnum] = self;
- self.entremove = Ent_RemoveEntCS;
- self.iflags |= IFLAG_ORIGIN;
+ entcs_receiver[this.sv_entnum] = this;
+ this.entremove = Ent_RemoveEntCS;
+ this.iflags |= IFLAG_ORIGIN;
InterpolateOrigin_Note();
}
void Ent_Remove();
void Ent_RemovePlayerScore()
-{SELFPARAM();
- if(self.owner) {
- SetTeam(self.owner, -1);
- self.owner.gotscores = 0;
+{
+ SELFPARAM();
+ if(this.owner) {
+ SetTeam(this.owner, -1);
+ this.owner.gotscores = 0;
for(int i = 0; i < MAX_SCORE; ++i) {
- self.owner.(scores[i]) = 0; // clear all scores
+ this.owner.(scores[i]) = 0; // clear all scores
}
}
}
-void Ent_ReadPlayerScore()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_SCORES, bool isnew)
+{
+ make_pure(this);
int i, n;
bool isNew;
entity o;
// damnit -.- don't want to go change every single .sv_entnum in hud.qc AGAIN
// (no I've never heard of M-x replace-string, sed, or anything like that)
- isNew = !self.owner; // workaround for DP bug
+ isNew = !this.owner; // workaround for DP bug
n = ReadByte()-1;
#ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED
- if(!isNew && n != self.sv_entnum)
+ if(!isNew && n != this.sv_entnum)
{
//print("A CSQC entity changed its owner!\n");
- LOG_INFOF("A CSQC entity changed its owner! (edict: %d, classname: %s)\n", num_for_edict(self), self.classname);
+ LOG_INFOF("A CSQC entity changed its owner! (edict: %d, classname: %s)\n", num_for_edict(this), this.classname);
isNew = true;
Ent_Remove();
- self.enttype = ENT_CLIENT_SCORES;
}
#endif
- self.sv_entnum = n;
+ this.sv_entnum = n;
- if (!(playerslots[self.sv_entnum]))
- playerslots[self.sv_entnum] = spawn();
- o = self.owner = playerslots[self.sv_entnum];
- o.sv_entnum = self.sv_entnum;
+ o = playerslots[this.sv_entnum];
+ if (!o)
+ {
+ o = playerslots[this.sv_entnum] = new(playerslot);
+ make_pure(o);
+ }
+ this.owner = o;
+ o.sv_entnum = this.sv_entnum;
o.gotscores = 1;
//if (!o.sort_prev)
o.(scores[i]) = ReadChar();
}
+ return = true;
+
if(o.sort_prev)
HUD_UpdatePlayerPos(o); // if not registered, we cannot do this yet!
- self.entremove = Ent_RemovePlayerScore;
+ this.entremove = Ent_RemovePlayerScore;
}
-void Ent_ReadTeamScore()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_TEAMSCORES, bool isnew)
+{
+ make_pure(this);
int i;
entity o;
- self.team = ReadByte();
- o = self.owner = GetTeam(self.team, true); // these team numbers can always be trusted
+ this.team = ReadByte();
+ o = this.owner = GetTeam(this.team, true); // these team numbers can always be trusted
int sf, lf;
#if MAX_TEAMSCORE <= 8
o.(teamscores[i]) = ReadChar();
}
+ return = true;
+
HUD_UpdateTeamPos(o);
}
-void Ent_ClientData()
+NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew)
{
+ make_pure(this);
float newspectatee_status;
int f = ReadByte();
else
angles_held_status = 0;
+ return = true;
+
if(newspectatee_status != spectatee_status)
{
// clear race stuff
// we could get rid of spectatee_status, and derive it from player_localentnum and player_localnum
}
-void Ent_Nagger()
+NET_HANDLE(ENT_CLIENT_NAGGER, bool isnew)
{
+ make_pure(this);
int i, j, b, f;
int nags = ReadByte(); // NAGS NAGS NAGS NAGS NAGS NAGS NADZ NAGS NAGS NAGS
}
}
+ return = true;
+
ready_waiting = (nags & BIT(0));
ready_waiting_for_me = (nags & BIT(1));
vote_waiting = (nags & BIT(2));
warmup_stage = (nags & BIT(4));
}
-void Ent_EliminatedPlayers()
+NET_HANDLE(ENT_CLIENT_ELIMINATEDPLAYERS, bool isnew)
{
+ make_pure(this);
int i, j, b, f;
int sf = ReadByte();
playerslots[j].eliminated = 0;
}
}
+ return true;
}
-void Ent_RandomSeed()
+NET_HANDLE(ENT_CLIENT_RANDOMSEED, bool isnew)
{
- float s;
+ make_pure(this);
prandom_debug();
- s = ReadShort();
+ float s = ReadShort();
psrandom(s);
+ return true;
}
-void Ent_ReadAccuracy()
+NET_HANDLE(ENT_CLIENT_ACCURACY, bool isnew)
{
+ make_pure(this);
int sf = ReadInt24_t();
if (sf == 0) {
for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w)
weapon_accuracy[w] = -1;
- return;
+ return true;
}
int f = 1;
}
f = (f == 0x800000) ? 1 : f * 2;
}
+ return true;
}
void Spawn_Draw(entity this)
{
- pointparticles(this.cnt, this.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1));
+ __pointparticles(this.cnt, this.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1));
}
-void Ent_ReadSpawnPoint(float is_new) // entity for spawnpoint
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new)
+{
float teamnum = (ReadByte() - 1);
vector spn_origin;
spn_origin.x = ReadShort();
//if(is_new)
//{
- self.origin = spn_origin;
- setsize(self, PL_MIN_CONST, PL_MAX_CONST);
+ this.origin = spn_origin;
+ setsize(this, PL_MIN_CONST, PL_MAX_CONST);
//droptofloor();
/*if(autocvar_cl_spawn_point_model) // needs a model first
{
- self.mdl = "models/spawnpoint.md3";
- self.colormod = Team_ColorRGB(teamnum);
- precache_model(self.mdl);
- setmodel(self, self.mdl);
- self.drawmask = MASK_NORMAL;
- //self.movetype = MOVETYPE_NOCLIP;
- //self.draw = Spawn_Draw;
+ this.mdl = "models/spawnpoint.md3";
+ this.colormod = Team_ColorRGB(teamnum);
+ precache_model(this.mdl);
+ setmodel(this, this.mdl);
+ this.drawmask = MASK_NORMAL;
+ //this.movetype = MOVETYPE_NOCLIP;
+ //this.draw = Spawn_Draw;
}*/
if(autocvar_cl_spawn_point_particles)
{
{
switch(teamnum)
{
- case NUM_TEAM_1: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_RED); break;
- case NUM_TEAM_2: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_BLUE); break;
- case NUM_TEAM_3: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_YELLOW); break;
- case NUM_TEAM_4: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_PINK); break;
- default: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); break;
+ case NUM_TEAM_1: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_RED); break;
+ case NUM_TEAM_2: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_BLUE); break;
+ case NUM_TEAM_3: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_YELLOW); break;
+ case NUM_TEAM_4: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_PINK); break;
+ default: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); break;
}
}
- else { self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); }
+ else { this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); }
- self.draw = Spawn_Draw;
+ this.draw = Spawn_Draw;
}
//}
- //printf("Ent_ReadSpawnPoint(is_new = %d); origin = %s, team = %d, effect = %d\n", is_new, vtos(self.origin), teamnum, self.cnt);
+ //printf("Ent_ReadSpawnPoint(is_new = %d); origin = %s, team = %d, effect = %d\n", is_new, vtos(this.origin), teamnum, this.cnt);
+ return true;
}
-void Ent_ReadSpawnEvent(float is_new)
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_SPAWNEVENT, bool is_new)
+{
// If entnum is 0, ONLY do the local spawn actions
// this way the server can disable the sending of
// spawn origin or such to clients if wanted.
if(entnum)
{
- self.origin_x = ReadShort();
- self.origin_y = ReadShort();
- self.origin_z = ReadShort();
+ this.origin_x = ReadShort();
+ this.origin_y = ReadShort();
+ this.origin_z = ReadShort();
if(is_new)
{
{
switch(teamnum)
{
- case NUM_TEAM_1: pointparticles(particleeffectnum(EFFECT_SPAWN_RED), self.origin, '0 0 0', 1); break;
- case NUM_TEAM_2: pointparticles(particleeffectnum(EFFECT_SPAWN_BLUE), self.origin, '0 0 0', 1); break;
- case NUM_TEAM_3: pointparticles(particleeffectnum(EFFECT_SPAWN_YELLOW), self.origin, '0 0 0', 1); break;
- case NUM_TEAM_4: pointparticles(particleeffectnum(EFFECT_SPAWN_PINK), self.origin, '0 0 0', 1); break;
- default: pointparticles(particleeffectnum(EFFECT_SPAWN_NEUTRAL), self.origin, '0 0 0', 1); break;
+ case NUM_TEAM_1: pointparticles(EFFECT_SPAWN_RED, this.origin, '0 0 0', 1); break;
+ case NUM_TEAM_2: pointparticles(EFFECT_SPAWN_BLUE, this.origin, '0 0 0', 1); break;
+ case NUM_TEAM_3: pointparticles(EFFECT_SPAWN_YELLOW, this.origin, '0 0 0', 1); break;
+ case NUM_TEAM_4: pointparticles(EFFECT_SPAWN_PINK, this.origin, '0 0 0', 1); break;
+ default: pointparticles(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); break;
}
}
if(autocvar_cl_spawn_event_sound)
{
- sound(self, CH_TRIGGER, SND_SPAWN, VOL_BASE, ATTEN_NORM);
+ sound(this, CH_TRIGGER, SND_SPAWN, VOL_BASE, ATTEN_NORM);
}
}
}
+ return = true;
// local spawn actions
if(is_new && (!entnum || (entnum == player_localentnum)))
}
}
HUD_Radar_Hide_Maximized();
- //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(self.origin), entnum, player_localentnum);
+ //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(this.origin), entnum, player_localentnum);
}
// CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.
// The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.
-void Ent_RadarLink();
-void Ent_Init();
-void Ent_ScoresInfo();
void CSQC_Ent_Update(float bIsNewEntity)
-{SELFPARAM();
+{
+ SELFPARAM();
+ this.sourceLocLine = __LINE__;
+ this.sourceLocFile = __FILE__;
int t = ReadByte();
- if(autocvar_developer_csqcentities)
- LOG_INFOF("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t);
-
// set up the "time" global for received entities to be correct for interpolation purposes
float savetime = time;
if(servertime)
}
#ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED
- if(self.enttype)
+ if(this.enttype)
{
- if(t != self.enttype || bIsNewEntity)
+ if(t != this.enttype || bIsNewEntity)
{
- LOG_INFOF("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(self), self.entnum, self.enttype, t);
+ LOG_INFOF("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(this), this.entnum, this.enttype, t);
Ent_Remove();
- clearentity(self);
+ clearentity(this);
bIsNewEntity = 1;
}
}
{
if(!bIsNewEntity)
{
- LOG_INFOF("A CSQC entity appeared out of nowhere! (edict: %d, server: %d, type: %d)\n", num_for_edict(self), self.entnum, t);
+ LOG_INFOF("A CSQC entity appeared out of nowhere! (edict: %d, server: %d, type: %d)\n", num_for_edict(this), this.entnum, t);
bIsNewEntity = 1;
}
}
#endif
- self.enttype = t;
+ this.enttype = t;
bool done = false;
FOREACH(LinkedEntities, it.m_id == t, LAMBDA(
- it.m_read(self, bIsNewEntity);
- done = true;
+ this.classname = it.netname;
+ if (autocvar_developer_csqcentities)
+ LOG_INFOF("CSQC_Ent_Update(%d) with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", bIsNewEntity, this, this.entnum, this.enttype, it.netname, t);
+ done = it.m_read(this, bIsNewEntity);
break;
));
+ time = savetime;
if (!done)
- switch(t)
{
- case ENT_CLIENT_MUTATOR: {
- int mutID = ReadMutator();
- if (!MUTATOR_CALLHOOK(CSQC_Ent_Update, mutID, bIsNewEntity))
- error(sprintf("Unknown mutator type in CSQC_Ent_Update (mutID: %d, edict: %d, classname: %s)\n", mutID, num_for_edict(self), self.classname));
- break;
- }
- case ENT_CLIENT_ENTCS: Ent_ReadEntCS(); break;
- case ENT_CLIENT_SCORES: Ent_ReadPlayerScore(); break;
- case ENT_CLIENT_TEAMSCORES: Ent_ReadTeamScore(); break;
- case ENT_CLIENT_POINTPARTICLES: Ent_PointParticles(); break;
- case ENT_CLIENT_RAINSNOW: Ent_RainOrSnow(); break;
- case ENT_CLIENT_LASER: Ent_Laser(); break;
- case ENT_CLIENT_NAGGER: Ent_Nagger(); break;
- case ENT_CLIENT_ELIMINATEDPLAYERS: Ent_EliminatedPlayers(); break;
- case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break;
- case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break;
- case ENT_CLIENT_GIBSPLASH: Ent_GibSplash(bIsNewEntity); break;
- case ENT_CLIENT_DAMAGEINFO: Ent_DamageInfo(bIsNewEntity); break;
- case ENT_CLIENT_INIT: Ent_Init(); break;
- case ENT_CLIENT_SCORES_INFO: Ent_ScoresInfo(); break;
- case ENT_CLIENT_MAPVOTE: Ent_MapVote(); break;
- case ENT_CLIENT_CLIENTDATA: Ent_ClientData(); break;
- case ENT_CLIENT_RANDOMSEED: Ent_RandomSeed(); break;
- case ENT_CLIENT_WALL: Ent_Wall(); break;
- case ENT_CLIENT_MODELEFFECT: Ent_ModelEffect(bIsNewEntity); break;
- case ENT_CLIENT_TUBANOTE: Ent_TubaNote(bIsNewEntity); break;
- case ENT_CLIENT_WARPZONE: WarpZone_Read(bIsNewEntity); break;
- case ENT_CLIENT_WARPZONE_CAMERA: WarpZone_Camera_Read(bIsNewEntity); break;
- case ENT_CLIENT_WARPZONE_TELEPORTED: WarpZone_Teleported_Read(bIsNewEntity); break;
- case ENT_CLIENT_TRIGGER_MUSIC: Ent_ReadTriggerMusic(); break;
- case ENT_CLIENT_HOOK: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_HOOK); break;
- case ENT_CLIENT_INVENTORY: Inventory_Read(self); break;
- case ENT_CLIENT_ARC_BEAM: Ent_ReadArcBeam(bIsNewEntity); break;
- case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break;
- case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break;
- case ENT_CLIENT_TURRET: ent_turret(); break;
- case ENT_CLIENT_GENERATOR: ent_generator(); break;
- case ENT_CLIENT_CONTROLPOINT_ICON: ent_cpicon(this); break;
- case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break;
- case ENT_CLIENT_ITEM: ItemRead(bIsNewEntity); break;
- case ENT_CLIENT_BUMBLE_RAYGUN: bumble_raygun_read(bIsNewEntity); break;
- case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break;
- case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break;
- case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break;
- case ENT_CLIENT_MINIGAME: ent_read_minigame(); break;
- case ENT_CLIENT_VIEWLOC: ent_viewloc(); break;
- case ENT_CLIENT_VIEWLOC_TRIGGER: ent_viewloc_trigger(); break;
- case ENT_CLIENT_LADDER: ent_func_ladder(); break;
- case ENT_CLIENT_TRIGGER_PUSH: ent_trigger_push(); break;
- case ENT_CLIENT_TARGET_PUSH: ent_target_push(); break;
- case ENT_CLIENT_CONVEYOR: ent_conveyor(); break;
- case ENT_CLIENT_DOOR: ent_door(); break;
- case ENT_CLIENT_PLAT: ent_plat(); break;
- case ENT_CLIENT_SWAMP: ent_swamp(); break;
- case ENT_CLIENT_CORNER: ent_corner(); break;
- case ENT_CLIENT_KEYLOCK: ent_keylock(); break;
- case ENT_CLIENT_TRAIN: ent_train(); break;
- case ENT_CLIENT_TRIGGER_IMPULSE: ent_trigger_impulse(); break;
- case ENT_CLIENT_EFFECT: Read_Effect(bIsNewEntity); break;
-
- default:
- //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype));
- error(sprintf("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n", self.enttype, num_for_edict(self), self.classname));
- break;
+ //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), this.enttype));
+ error(sprintf("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n", this.enttype, num_for_edict(this), this.classname));
}
-
- time = savetime;
}
+
// Destructor, but does NOT deallocate the entity by calling remove(). Also
// used when an entity changes its type. For an entity that someone interacts
// with others, make sure it can no longer do so.
void Ent_Remove()
-{SELFPARAM();
- if(self.entremove)
- self.entremove();
+{
+ SELFPARAM();
+ if(this.entremove) this.entremove();
- if(self.skeletonindex)
+ if(this.skeletonindex)
{
- skel_delete(self.skeletonindex);
- self.skeletonindex = 0;
+ skel_delete(this.skeletonindex);
+ this.skeletonindex = 0;
}
- if(self.snd_looping > 0)
+ if(this.snd_looping > 0)
{
- sound(self, self.snd_looping, SND_Null, VOL_BASE, autocvar_g_jetpack_attenuation);
- self.snd_looping = 0;
+ sound(this, this.snd_looping, SND_Null, VOL_BASE, autocvar_g_jetpack_attenuation);
+ this.snd_looping = 0;
}
- self.enttype = 0;
- self.classname = "";
- self.draw = draw_null;
- self.entremove = menu_sub_null;
+ this.enttype = 0;
+ this.classname = "";
+ this.draw = func_null;
+ this.entremove = func_null;
// TODO possibly set more stuff to defaults
}
-// CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed. Essentially call remove(self) as well.
+// CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed. Essentially call remove(this) as well.
void CSQC_Ent_Remove()
-{SELFPARAM();
- if(autocvar_developer_csqcentities)
- LOG_INFOF("CSQC_Ent_Remove() with self=%i self.entnum=%d self.enttype=%d\n", self, self.entnum, self.enttype);
-
- if(wasfreed(self))
+{
+ SELFPARAM();
+ if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Remove() with this=%i {.entnum=%d, .enttype=%d}\n", this, this.entnum, this.enttype);
+ if (wasfreed(this))
{
- LOG_INFO("WARNING: CSQC_Ent_Remove called for already removed entity. Packet loss?\n");
+ LOG_WARNING("CSQC_Ent_Remove called for already removed entity. Packet loss?\n");
return;
}
- if(self.enttype)
- Ent_Remove();
- remove(self);
+ if (this.enttype) Ent_Remove();
+ remove(this);
}
void Gamemode_Init()
// CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided. To execute standard behavior, simply execute localcmd with the string.
void CSQC_Parse_StuffCmd(string strMessage)
{
- if(autocvar_developer_csqcentities)
- LOG_INFOF("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage);
-
+ if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage);
localcmd(strMessage);
}
// CSQC_Parse_Print : Provides the print string in the first parameter that the server provided. To execute standard behavior, simply execute print with the string.
void CSQC_Parse_Print(string strMessage)
{
- if(autocvar_developer_csqcentities)
- LOG_INFOF("CSQC_Parse_Print(\"%s\")\n", strMessage);
-
+ if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Parse_Print(\"%s\")\n", strMessage);
print(ColorTranslateRGB(strMessage));
}
// CSQC_Parse_CenterPrint : Provides the centerprint_hud string in the first parameter that the server provided.
void CSQC_Parse_CenterPrint(string strMessage)
{
- if(autocvar_developer_csqcentities)
- LOG_INFOF("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage);
-
+ if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage);
centerprint_hud(strMessage);
}
-string notranslate_fogcmd1 = "\nfog ";
-string notranslate_fogcmd2 = "\nr_fog_exp2 0\nr_drawfog 1\n";
-void Fog_Force()
+// CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.
+// You must ALWAYS first acquire the temporary ID, which is sent as a byte.
+// Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.
+bool CSQC_Parse_TempEntity()
{
- // TODO somehow thwart prvm_globalset client ...
+ // Acquire TE ID
+ int nTEID = ReadByte();
+
+ FOREACH(TempEntities, it.m_id == nTEID, LAMBDA(
+ if (autocvar_developer_csqcentities)
+ LOG_INFOF("CSQC_Parse_TempEntity() nTEID=%s (%d)\n", it.netname, nTEID);
+ return it.m_read(NULL, true);
+ ));
- if(autocvar_cl_orthoview && autocvar_cl_orthoview_nofog)
- { localcmd("\nr_drawfog 0\n"); }
- else if(forcefog != "")
- { localcmd(strcat(notranslate_fogcmd1, forcefog, notranslate_fogcmd2)); }
+ if (autocvar_developer_csqcentities)
+ LOG_INFOF("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID);
+
+ // No special logic for this temporary entity; return 0 so the engine can handle it
+ return false;
+}
+
+/** TODO somehow thwart prvm_globalset client ... */
+string forcefog;
+void Fog_Force()
+{
+ if (autocvar_cl_orthoview && autocvar_cl_orthoview_nofog)
+ localcmd("\nr_drawfog 0\n");
+ else if (forcefog != "")
+ localcmd(sprintf("\nfog %s\nr_fog_exp2 0\nr_drawfog 1\n", forcefog));
}
void Gamemode_Init();
-void Ent_ScoresInfo()
-{SELFPARAM();
- int i;
- self.classname = "ent_client_scores_info";
+NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew)
+{
+ make_pure(this);
gametype = ReadInt24_t();
HUD_ModIcons_SetFunc();
- for(i = 0; i < MAX_SCORE; ++i)
+ for (int i = 0; i < MAX_SCORE; ++i)
{
- if(scores_label[i])
- strunzone(scores_label[i]);
+ if (scores_label[i]) strunzone(scores_label[i]);
scores_label[i] = strzone(ReadString());
scores_flags[i] = ReadByte();
}
- for(i = 0; i < MAX_TEAMSCORE; ++i)
+ for (int i = 0; i < MAX_TEAMSCORE; ++i)
{
- if(teamscores_label[i])
- strunzone(teamscores_label[i]);
+ if (teamscores_label[i]) strunzone(teamscores_label[i]);
teamscores_label[i] = strzone(ReadString());
teamscores_flags[i] = ReadByte();
}
+ return = true;
HUD_InitScores();
Gamemode_Init();
}
-void Ent_Init()
-{SELFPARAM();
- self.classname = "ent_client_init";
-
+NET_HANDLE(ENT_CLIENT_INIT, bool isnew)
+{
nb_pb_period = ReadByte() / 32; //Accuracy of 1/32th
hook_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
- if(forcefog)
- strunzone(forcefog);
+ if (forcefog) strunzone(forcefog);
forcefog = strzone(ReadString());
armorblockpercent = ReadByte() / 255.0;
g_trueaim_minrange = ReadCoord();
g_balance_porto_secondary = ReadByte();
+ return = true;
+
+ MUTATOR_CALLHOOK(Ent_Init);
- if(!postinit)
- PostInit();
+ if (!postinit) PostInit();
}
-void Net_ReadRace()
+NET_HANDLE(TE_CSQC_RACE, bool isNew)
{
- float b;
+ int b = ReadByte();
- b = ReadByte();
-
- switch(b)
+ switch (b)
{
case RACE_NET_CHECKPOINT_HIT_QUALIFYING:
race_checkpoint = ReadByte();
strunzone(race_status_name);
race_status_name = strzone(ReadString());
}
+ return true;
}
-void Net_TeamNagger()
+NET_HANDLE(TE_CSQC_TEAMNAGGER, bool isNew)
{
teamnagger = 1;
+ return true;
}
-void Net_ReadPingPLReport()
+NET_HANDLE(TE_CSQC_PINGPLREPORT, bool isNew)
{
- int e, pi, pl, ml;
- e = ReadByte();
- pi = ReadShort();
- pl = ReadByte();
- ml = ReadByte();
- if (!(playerslots[e]))
- return;
- playerslots[e].ping = pi;
- playerslots[e].ping_packetloss = pl / 255.0;
- playerslots[e].ping_movementloss = ml / 255.0;
+ int i = ReadByte();
+ int pi = ReadShort();
+ int pl = ReadByte();
+ int ml = ReadByte();
+ return = true;
+ entity e = playerslots[i];
+ if (!e) return;
+ e.ping = pi;
+ e.ping_packetloss = pl / 255.0;
+ e.ping_movementloss = ml / 255.0;
}
-void Net_WeaponComplain()
+NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew)
{
complain_weapon = ReadByte();
-
- if(complain_weapon_name)
- strunzone(complain_weapon_name);
+ if (complain_weapon_name) strunzone(complain_weapon_name);
complain_weapon_name = strzone(WEP_NAME(complain_weapon));
-
complain_weapon_type = ReadByte();
+ return = true;
complain_weapon_time = time;
weapontime = time; // ping the weapon panel
}
}
-// CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.
-// You must ALWAYS first acquire the temporary ID, which is sent as a byte.
-// Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.
-bool CSQC_Parse_TempEntity()
-{
- // Acquire TE ID
- int nTEID = ReadByte();
-
- if (autocvar_developer_csqcentities)
- LOG_INFOF("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID);
-
- FOREACH(TempEntities, it.m_id == nTEID, LAMBDA(
- it.m_read(NULL, true);
- return true;
- ));
- switch (nTEID)
- {
- case TE_CSQC_MUTATOR:
- int mutID = ReadMutator();
- if (MUTATOR_CALLHOOK(CSQC_Parse_TempEntity, mutID))
- return true;
- case TE_CSQC_TARGET_MUSIC:
- Net_TargetMusic();
- return true;
- case TE_CSQC_PICTURE:
- Net_MapVote_Picture();
- return true;
- case TE_CSQC_RACE:
- Net_ReadRace();
- return true;
- case TE_CSQC_VORTEXBEAMPARTICLE:
- Net_ReadVortexBeamParticle();
- return true;
- case TE_CSQC_TEAMNAGGER:
- Net_TeamNagger();
- return true;
- case TE_CSQC_ARC:
- Net_ReadArc();
- return true;
- case TE_CSQC_PINGPLREPORT:
- Net_ReadPingPLReport();
- return true;
- case TE_CSQC_WEAPONCOMPLAIN:
- Net_WeaponComplain();
- return true;
- case TE_CSQC_VEHICLESETUP:
- Net_VehicleSetup();
- return true;
- case TE_CSQC_SVNOTICE:
- cl_notice_read();
- return true;
- case TE_CSQC_SHOCKWAVEPARTICLE:
- Net_ReadShockwaveParticle();
- return true;
- default:
- // No special logic for this temporary entity; return 0 so the engine can handle it
- return false;
- }
-}
-
string getcommandkey(string text, string command)
{
string keys;
#define DATABUF_NEXT (5*maxclients)
-void() menu_show_error;
-void() menu_sub_null;
-
-float menu_visible;
-var void() menu_show;
-var float(float bInputType, float nPrimary, float nSecondary) menu_action;
-
// --------------------------------------------------------------------------
// Onslaught
.void(entity) draw;
.void(entity) draw2d;
-.void(void) entremove;
+.void() entremove;
float drawframetime;
vector view_origin, view_forward, view_right, view_up;
int activeweapon;
int switchingweapon;
-int switchweapon;
+#define switchweapon STAT(SWITCHWEAPON)
float current_viewzoom;
float zoomin_effect;
float warmup_stage;
#include "mapvoting.qh"
-#include "hud.qh"
+#include "hud/all.qh"
#include "scoreboard.qh"
#include "../common/mapinfo.qh"
mv_ownvote = ReadByte()-1;
}
-void Ent_MapVote()
+NET_HANDLE(ENT_CLIENT_MAPVOTE, bool isnew)
{
+ make_pure(self);
int sf = ReadByte();
+ return = true;
if(sf & 1)
MapVote_Init();
MapVote_UpdateVotes();
}
+NET_HANDLE(TE_CSQC_PICTURE, bool isNew)
+{
+ Net_MapVote_Picture();
+ return true;
+}
+
void Net_MapVote_Picture()
{
int type = ReadByte();
float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary);
-void Ent_MapVote();
-
void Net_MapVote_Picture();
float mv_active;
#include "miscfunctions.qh"
-#include "hud.qh"
+#include "hud/all.qh"
#include "../common/command/generic.qh"
return teamslots[num];
if (!add)
return world;
- entity tm = spawn();
+ entity tm = new(team);
+ make_pure(tm);
tm.team = Team;
teamslots[num] = tm;
RegisterTeam(tm);
return false;
}
-vector rotate(vector v, float a)
-{
- vector w = '0 0 0';
- // FTEQCC SUCKS AGAIN
- w.x = v.x * cos(a) + v.y * sin(a);
- w.y = -1 * v.x * sin(a) + v.y * cos(a);
- return w;
-}
-
// decolorizes and team colors the player name when needed
string playername(string thename, float teamid)
{
return vec;
}
-void dummyfunction(float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8)
-{
-}
-
float expandingbox_sizefactor_from_fadelerp(float fadelerp)
{
return 1.2 / (1.2 - fadelerp);
sz = expandingbox_sizefactor_from_fadelerp(fadelerp);
drawfontscale = sz * '1 1 0';
- dummyfunction(0, 0, 0, 0, 0, 0, 0, 0);
drawstring(position + expandingbox_resize_centered_box_offset(sz, theScale, stringwidth(text, false, theScale * (sz / drawfontscale.x)) / (theScale.x * sz)), text, theScale * (sz / drawfontscale.x), rgb, theAlpha * (1 - fadelerp), flag);
// width parameter:
// (scale_x * sz / drawfontscale_x) * drawfontscale_x * SIZE1 / (scale_x * sz)
sz = expandingbox_sizefactor_from_fadelerp(fadelerp);
drawfontscale = sz * '1 1 0';
- dummyfunction(0, 0, 0, 0, 0, 0, 0, 0);
drawcolorcodedstring(position + expandingbox_resize_centered_box_offset(sz, theScale, stringwidth(text, true, theScale * (sz / drawfontscale.x)) / (theScale.x * sz)), text, theScale * (sz / drawfontscale.x), theAlpha * (1 - fadelerp), flag);
drawfontscale = '1 1 0';
}
return false;
}
+/** engine callback */
void URI_Get_Callback(int id, float status, string data)
{
if(url_URI_Get_Callback(id, status, data))
}
}
-void draw_beginBoldFont()
-{
- drawfont = FONT_USER+2;
-}
-
-void draw_endBoldFont()
-{
- drawfont = FONT_USER+1;
-}
-
void Accuracy_LoadLevels()
{
if(autocvar_accuracy_color_levels != acc_color_levels)
vector project_3d_to_2d(vector vec);
-void dummyfunction(float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8);
+#define draw_beginBoldFont() do { drawfont = FONT_USER + 2; } while (0)
+#define draw_endBoldFont() do { drawfont = FONT_USER + 1; } while (0)
float expandingbox_sizefactor_from_fadelerp(float fadelerp);
float getplayerisdead(float pl);
-void URI_Get_Callback(int id, float status, string data);
-
-void draw_beginBoldFont();
-
-void draw_endBoldFont();
-
-
const int MAX_ACCURACY_LEVELS = 10;
float acc_lev[MAX_ACCURACY_LEVELS];
vector acc_col[MAX_ACCURACY_LEVELS];
+++ /dev/null
-#include "modeleffects.qh"
-
-.float cnt;
-.float scale;
-.float alpha;
-
-void ModelEffect_Draw(entity this)
-{
- self.angles = self.angles + frametime * self.avelocity;
- setorigin(self, self.origin + frametime * self.velocity);
- self.scale = self.scale1 + (self.scale2 - self.scale1) * (time - self.teleport_time) / (self.lifetime + self.fadetime - self.teleport_time);
- self.alpha = self.cnt * bound(0, 1 - (time - self.lifetime) / self.fadetime, 1);
- if(self.alpha < ALPHA_MIN_VISIBLE)
- {
- remove(self);
- return;
- }
- self.drawmask = MASK_NORMAL;
- if(self.scale <= 0)
- {
- self.drawmask = 0;
- return;
- }
-}
-
-void Ent_ModelEffect(bool isNew)
-{SELFPARAM();
- self.classname = "modeleffect_spawner";
-
- int f = ReadByte();
-
- entity e = spawn();
- e.classname = "modeleffect";
- e.model = "from network";
- e.modelindex = ReadShort();
- e.skin = ReadByte();
- e.frame = ReadByte();
- e.frame1time = time;
- e.origin_x = ReadCoord();
- e.origin_y = ReadCoord();
- e.origin_z = ReadCoord();
- setorigin(e, e.origin);
- if(f & 1)
- {
- e.velocity_x = ReadCoord();
- e.velocity_y = ReadCoord();
- e.velocity_z = ReadCoord();
- }
- if(f & 2)
- {
- e.angles_x = ReadAngle();
- e.angles_y = ReadAngle();
- e.angles_z = ReadAngle();
- }
- if(f & 4)
- {
- e.avelocity_x = ReadAngle();
- e.avelocity_y = ReadAngle();
- e.avelocity_z = ReadAngle();
- }
- e.scale1 = ReadShort() / 256.0;
- e.scale2 = ReadShort() / 256.0;
- e.lifetime = time + ReadByte() * 0.01;
- e.fadetime = ReadByte() * 0.01;
- e.teleport_time = time;
- e.cnt = ReadByte() / 255.0; // actually alpha
-
- e.draw = ModelEffect_Draw;
-
- if(!isNew)
- remove(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then
-}
+++ /dev/null
-#ifndef CLIENT_MODELEFFECTS_H
-#define CLIENT_MODELEFFECTS_H
-
-entityclass(ModelEffect);
-class(ModelEffect) .float frame1time;
-class(ModelEffect) .float lifetime, fadetime;
-class(ModelEffect) .float teleport_time;
-class(ModelEffect) .float scale1, scale2;
-
-void ModelEffect_Draw(entity this);
-
-void Ent_ModelEffect(bool isNew);
-#endif
/* Called when the crosshair is being updated */
MUTATOR_HOOKABLE(UpdateCrosshair, EV_NO_ARGS);
-/**
- * Called when a temp entity is parsed
- * NOTE: hooks MUST start with:
- * if (MUTATOR_RETURNVALUE) return;
- * if (!ReadMutatorEquals(mutator_argv_int_0, name_of_mutator)) return;
- * return = true;
- */
-#define EV_CSQC_Parse_TempEntity(i, o) \
- /** mutator id */ i(int, mutator_argv_int_0) \
- /**/
-MUTATOR_HOOKABLE(CSQC_Parse_TempEntity, EV_CSQC_Parse_TempEntity);
-
-/**
- * Called when a shared entity is updated
- * if (MUTATOR_RETURNVALUE) return;
- * if (!ReadMutatorEquals(mutator_argv_int_0, name_of_mutator)) return;
- * return = true;
- */
-#define EV_CSQC_Ent_Update(i, o) \
- /** mutator id */ i(int, mutator_argv_int_0) \
- /** bIsNewEntity */ i(bool, mutator_argv_bool_0) \
- /**/
-MUTATOR_HOOKABLE(CSQC_Ent_Update, EV_CSQC_Ent_Update);
-
/** Called when a projectile is linked with CSQC */
#define EV_Ent_Projectile(i, o) \
/** entity id */ i(entity, __self) \
/**/
MUTATOR_HOOKABLE(WantEventchase, EV_WantEventchase);
+#define EV_AnnouncerOption(i, o) \
+ /**/ i(string, ret_string) \
+ /**/ o(string, ret_string) \
+ /**/
+MUTATOR_HOOKABLE(AnnouncerOption, EV_AnnouncerOption);
+
+MUTATOR_HOOKABLE(Ent_Init, EV_NO_ARGS);
#endif
+++ /dev/null
-#include "particles.qh"
-
-#include "../common/stats.qh"
-
-#include "../lib/warpzone/common.qh"
-
-void Net_ReadVortexBeamParticle()
-{
- vector shotorg, endpos;
- float charge;
- shotorg.x = ReadCoord(); shotorg.y = ReadCoord(); shotorg.z = ReadCoord();
- endpos.x = ReadCoord(); endpos.y = ReadCoord(); endpos.z = ReadCoord();
- charge = ReadByte() / 255.0;
-
- pointparticles(particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH), shotorg, normalize(endpos - shotorg) * 1000, 1);
-
- //draw either the old v2.3 beam or the new beam
- charge = sqrt(charge); // divide evenly among trail spacing and alpha
- particles_alphamin = particles_alphamax = particles_fade = charge;
-
- if (autocvar_cl_particles_oldvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo()))
- WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM_OLD), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
- else
- WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
-}
+++ /dev/null
-#ifndef CLIENT_PARTICLES_H
-#define CLIENT_PARTICLES_H
-.int dphitcontentsmask;
-
-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
-
-void Draw_PointParticles(entity this);
-
-void Ent_PointParticles_Remove();
-
-void Ent_PointParticles();
-
-class(PointParticles) .float glow_color; // palette index
-
-void Net_ReadVortexBeamParticle();
-#endif
#include "../lib/_all.inc"
#include "_all.qh"
+#include "../common/effects/qc/all.qc"
+
#include "announcer.qc"
#include "bgmscript.qc"
-#include "controlpoint.qc"
#include "csqcmodel_hooks.qc"
-#include "damage.qc"
-#include "effects.qc"
-#include "generator.qc"
-#include "gibs.qc"
#include "hook.qc"
-#include "hud.qc"
-#include "hud_config.qc"
+#include "hud/all.qc"
#include "main.qc"
#include "mapvoting.qc"
#include "miscfunctions.qc"
-#include "modeleffects.qc"
#include "movelib.qc"
-#include "particles.qc"
#include "player_skeleton.qc"
-#include "rubble.qc"
#include "scoreboard.qc"
#include "shownames.qc"
#include "teamradar.qc"
#include "../lib/csqcmodel/cl_player.qc"
#include "../lib/csqcmodel/interpolate.qc"
-// TODO: move to common
-#include "../server/mutators/mutator/mutator_multijump.qc"
-#define IMPLEMENTATION
-#include "../server/mutators/mutator/mutator_multijump.qc"
-#undef IMPLEMENTATION
-
#include "../lib/warpzone/anglestransform.qc"
#include "../lib/warpzone/client.qc"
#include "../lib/warpzone/common.qc"
#include "quickmenu.qh"
-#include "hud.qh"
-#include "hud_config.qh"
+#include "hud/all.qh"
#include "mapvoting.qh"
// QUICKMENU_MAXLINES must be <= 10
drawcolorcodedstring(pos, entry, fontsize, panel_fg_alpha, DRAWFLAG_ADDITIVE);
}
-void HUD_QuickMenu(void)
+void HUD_QuickMenu()
{
if(!autocvar__hud_configure)
{
+++ /dev/null
-#include "rubble.qh"
-
-// LordHavoc: rewrote this file, it was really bad code
-
-void RubbleLimit(string cname, float limit, void() deleteproc)
-{SELFPARAM();
- entity e;
- entity oldest;
- float c;
- float oldesttime;
-
- // remove rubble of the same type if it's at the limit
- // remove multiple rubble if the limit has been decreased
- while(1)
- {
- e = findchain(classname,cname);
- if (e == world)
- break;
- // walk the list and count the entities, find the oldest
- // initialize our search with the first entity
- c = 1;
- oldest = e;
- oldesttime = e.creationtime;
- e = e.chain;
- // compare to all other matching entities
- while (e)
- {
- c = c + 1;
- if (oldesttime > e.creationtime)
- {
- oldesttime = e.creationtime;
- oldest = e;
- }
- e = e.chain;
- }
-
- // stop if there are less than the limit already
- if (c <= limit)
- break;
-
- // delete this oldest one and search again
- WITH(entity, self, oldest, deleteproc());
- }
-}
-
-entity RubbleNew(string cname)
-{
- entity e;
- // spawn a new entity and return it
- e = spawn();
- e.classname = cname;
- e.creationtime = time;
- return e;
-}
+++ /dev/null
-#ifndef CLIENT_RUBBLE_H
-#define CLIENT_RUBBLE_H
-entityclass(Rubble);
-class(Rubble) .float creationtime;
-void RubbleLimit(string cname, float limit, void() deleteproc);
-entity RubbleNew(string cname);
-#endif
#include "scoreboard.qh"
#include "quickmenu.qh"
-#include "hud.qh"
+#include "hud/all.qh"
#include "../common/constants.qh"
#include "../common/mapinfo.qh"
void HUD_InitScores();
void HUD_UpdatePlayerPos(entity pl);
void HUD_UpdateTeamPos(entity Team);
-float HUD_WouldDrawScoreboard(void);
+float HUD_WouldDrawScoreboard();
#endif
#include "shownames.qh"
-#include "hud.qh"
+#include "hud/all.qh"
#include "../common/constants.qh"
#include "../common/mapinfo.qh"
e = shownames_ent[i];
if(!e)
{
- e = spawn();
- e.classname = "shownames_tag";
+ e = new(shownames_tag);
e.sv_entnum = i+1;
shownames_ent[i] = e;
}
#include "teamradar.qh"
-#include "hud.qh"
+#include "hud/all.qh"
#include "../common/mutators/mutator/waypoints/all.qh"
return out;
}
-vector yinvert(vector v)
-{
- v.y = 1 - v.y;
- return v;
-}
-
void draw_teamradar_background(float fg)
{
float fga;
// radar links
-void Ent_RadarLink()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_RADARLINK, bool isnew)
+{
int sendflags = ReadByte();
InterpolateOrigin_Undo();
self.team = ReadByte();
}
+ return = true;
+
InterpolateOrigin_Note();
}
vector teamradar_texcoord_to_3dcoord(vector in,float z);
-vector yinvert(vector v);
-
void draw_teamradar_background(float fg);
void draw_teamradar_player(vector coord3d, vector pangles, vector rgb);
void teamradar_loadcvars();
-// radar links
-
-void Ent_RadarLink();
-
#endif
#include "../common/constants.qh"
-#define TUBA_STARTNOTE(i, n) W_Sound(strcat("tuba", (i ? ftos(i) : ""), "_loopnote", ftos(n)))
+#define TUBA_STARTNOTE(i, n) _Sound_fixpath(W_Sound(strcat("tuba", (i ? ftos(i) : ""), "_loopnote", ftos(n))))
const int TUBA_MIN = -18;
const int TUBA_MAX = 27;
self.enemy = world;
}
-void Ent_TubaNote(bool isNew)
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_TUBANOTE, bool isNew)
+{
bool upd = false;
int f = ReadByte();
if (f & 1) {
Ent_TubaNote_StopSound();
}
} else {
- self.enemy = spawn();
- self.enemy.classname = "tuba_note";
+ self.enemy = new(tuba_note);
if (Tuba_PitchStep) {
- self.enemy.enemy = spawn();
- self.enemy.enemy.classname = "tuba_note_2";
+ self.enemy.enemy = new(tuba_note_2);
}
isNew = true;
}
if (upd) {
Ent_TubaNote_UpdateSound();
}
+ return true;
}
-void Tuba_Precache()
+PRECACHE(Tuba)
{
Tuba_PitchStep = autocvar_g_balance_tuba_pitchstep;
if (Tuba_PitchStep) {
#ifndef CLIENT_TUBA_H
#define CLIENT_TUBA_H
-void Ent_TubaNote(bool isNew);
-void Tuba_Precache();
entityclass(Tuba);
#include "announcer.qh"
#include "hook.qh"
-#include "hud.qh"
-#include "hud_config.qh"
+#include "hud/all.qh"
#include "mapvoting.qh"
#include "scoreboard.qh"
#include "shownames.qh"
#include "mutators/events.qh"
#include "../common/constants.qh"
+#include "../common/debug.qh"
#include "../common/mapinfo.qh"
#include "../common/gamemodes/all.qh"
#include "../common/nades/all.qh"
void Porto_Init()
{
- porto = spawn();
- porto.classname = "porto";
+ porto = new(porto);
+ make_pure(porto);
porto.draw = Porto_Draw;
porto.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
}
void TrueAim_Init()
{
- trueaim = spawn();
- trueaim.classname = "trueaim";
+ trueaim = new(trueaim);
+ make_pure(trueaim);
trueaim.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
- trueaim_rifle = spawn();
- trueaim_rifle.classname = "trueaim_rifle";
+ trueaim_rifle = new(trueaim_rifle);
+ make_pure(trueaim_rifle);
trueaim_rifle.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
}
return SHOTTYPE_HITWORLD;
}
-void PostInit(void);
+void PostInit();
void CSQC_Demo_Camera();
float HUD_WouldDrawScoreboard();
float camera_mode;
vortex_charge = getstatf(STAT_VORTEX_CHARGE);
vortex_chargepool = getstatf(STAT_VORTEX_CHARGEPOOL);
- float arc_heat = getstatf(STAT_ARC_HEAT);
+ float arc_heat = STAT(ARC_HEAT);
if(vortex_charge_movingavg == 0) // this should only happen if we have just loaded up the game
vortex_charge_movingavg = vortex_charge;
++framecount;
+ stats_get();
hud = getstati(STAT_HUD);
if(hud != HUD_NORMAL && lasthud == HUD_NORMAL)
ColorTranslateMode = autocvar_cl_stripcolorcodes;
- // next WANTED weapon (for HUD)
- switchweapon = getstati(STAT_SWITCHWEAPON);
-
// currently switching-to weapon (for crosshair)
switchingweapon = getstati(STAT_SWITCHINGWEAPON);
if(!nightvision_noise)
{
- nightvision_noise = spawn();
- nightvision_noise.classname = "nightvision_noise";
+ nightvision_noise = new(nightvision_noise);
}
if(!nightvision_noise2)
{
- nightvision_noise2 = spawn();
- nightvision_noise2.classname = "nightvision_noise2";
+ nightvision_noise2 = new(nightvision_noise2);
}
// color tint in yellow
}
// edge detection postprocess handling done second (used by hud_powerup)
- float sharpen_intensity = 0, strength_finished = getstatf(STAT_STRENGTH_FINISHED), invincible_finished = getstatf(STAT_INVINCIBLE_FINISHED);
+ float sharpen_intensity = 0, strength_finished = STAT(STRENGTH_FINISHED), invincible_finished = STAT(INVINCIBLE_FINISHED);
if (strength_finished - time > 0) { sharpen_intensity += (strength_finished - time); }
if (invincible_finished - time > 0) { sharpen_intensity += (invincible_finished - time); }
else if(cvar("r_glsl_postprocess") == 2)
cvar_set("r_glsl_postprocess", "0");
- if(menu_visible)
- menu_show();
-
/*if(gametype == MAPINFO_TYPE_CTF)
{
ctf_view();
WITH(entity, self, e, e.draw2d(e));
}
Draw_ShowNames_All();
+ Debug_Draw();
scoreboard_active = HUD_WouldDrawScoreboard();
self.bgmscript = string_null;
}
-void Ent_Wall()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_WALL, bool isnew)
+{
int f;
var .vector fld;
BGMScript_InitEntity(self);
}
+ return = true;
+
InterpolateOrigin_Note();
self.saved = self.(fld);
void Ent_Wall_Remove();
-void Ent_Wall();
#endif
if (this.traileffect)
{
particles_alphamin = particles_alphamax = particles_fade = sqrt(this.alpha);
- boxparticles(particleeffectnum(Effects[this.traileffect]), this, from, to, this.velocity, this.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL);
+ boxparticles(particleeffectnum(Effects_from(this.traileffect)), this, from, to, this.velocity, this.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL);
}
}
}
}
-void Ent_Projectile()
+NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew)
{
- SELFPARAM();
- int f;
-
// projectile properties:
// kind (interpolated, or clientside)
//
//
// projectiles don't send angles, because they always follow the velocity
- f = ReadByte();
+ int f = ReadByte();
self.count = (f & 0x80);
self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN;
self.solid = SOLID_TRIGGER;
setsize(self, self.mins, self.maxs);
}
+ return = true;
+
if (self.gravity)
{
if (self.move_movetype == MOVETYPE_FLY)
self.entremove = Ent_RemoveProjectile;
}
-void Projectile_Precache()
+PRECACHE(Projectiles)
{
MUTATOR_CALLHOOK(PrecacheProjectiles);
}
void Ent_RemoveProjectile();
-void Ent_Projectile();
-
-void Projectile_Precache();
-
#endif
#include "../teams.qh"
#include "../util.qh"
-REGISTRY(Buffs, BIT(4))
+REGISTRY(Buffs, BITS(4))
+#define Buffs_from(i) _Buffs_from(i, BUFF_Null)
REGISTER_REGISTRY(RegisterBuffs)
+REGISTRY_CHECK(Buffs)
#define REGISTER_BUFF(id) \
REGISTER(RegisterBuffs, BUFF, Buffs, id, m_id, NEW(Buff)); \
#define COMMON_COMMANDS_ALL_H
#include "command.qh"
-REGISTRY(GENERIC_COMMANDS, 50)
+REGISTRY(GENERIC_COMMANDS, BITS(7))
+#define GENERIC_COMMANDS_from(i) _GENERIC_COMMANDS_from(i, NULL)
REGISTER_REGISTRY(RegisterGENERIC_COMMANDS)
-REGISTRY_SORT(GENERIC_COMMANDS, m_name, 0)
+REGISTRY_SORT(GENERIC_COMMANDS, 0)
#define GENERIC_COMMAND(id, description) \
CLASS(genericcommand_##id, Command) \
// =========================================================
// used by generic commands for better help/usage information
-string GetProgramCommandPrefix(void)
+string GetProgramCommandPrefix()
{
#ifdef SVQC
return "sv_cmd";
float GenericCommand(string command);
// Returns command prefix specific for whatever program it is compiled in
-string GetProgramCommandPrefix(void);
+string GetProgramCommandPrefix();
// used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file
#define CMD_Write(s) fputs(fh, s)
// Revision 22: hook shot origin
#define CSQC_REVISION 22
-const int AS_STRING = 1;
-const int AS_INT = 2;
-const int AS_FLOAT_TRUNCATED = 2;
-const int AS_FLOAT = 8;
-
-const int TE_CSQC_MUTATOR = 99;
-#define MUTATOR_HASH(s) crc16(true, s)
-#define WriteMutator(to, s) do { \
- WriteByte(to, TE_CSQC_MUTATOR); \
- WriteLong(to, MUTATOR_HASH(#s)); \
-} while (0)
-#define ReadMutator() ReadLong()
-#define ReadMutatorEquals(read, s) (read == MUTATOR_HASH(#s))
-const int TE_CSQC_PICTURE = 100;
-const int TE_CSQC_RACE = 101;
-const int TE_CSQC_VORTEXBEAMPARTICLE = 103;
-const int TE_CSQC_ARC = 104;
-const int TE_CSQC_TEAMNAGGER = 105;
-const int TE_CSQC_PINGPLREPORT = 106;
-const int TE_CSQC_TARGET_MUSIC = 107;
-const int TE_CSQC_WEAPONCOMPLAIN = 108;
-const int TE_CSQC_VORTEX_SCOPE = 109;
-const int TE_CSQC_MINELAYER_MAXMINES = 110;
-const int TE_CSQC_HAGAR_MAXROCKETS = 111;
-const int TE_CSQC_VEHICLESETUP = 112;
-const int TE_CSQC_SVNOTICE = 113;
-const int TE_CSQC_SHOCKWAVEPARTICLE = 114;
+REGISTER_NET_TEMP(TE_CSQC_PICTURE)
+REGISTER_NET_TEMP(TE_CSQC_RACE)
+REGISTER_NET_TEMP(TE_CSQC_TEAMNAGGER)
+REGISTER_NET_TEMP(TE_CSQC_PINGPLREPORT)
+REGISTER_NET_TEMP(TE_CSQC_WEAPONCOMPLAIN)
+REGISTER_NET_TEMP(TE_CSQC_VEHICLESETUP)
const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
const int RACE_NET_CHECKPOINT_CLEAR = 1;
const int RACE_NET_SERVER_STATUS = 12;
const int RANKINGS_CNT = 15;
-const int ENT_CLIENT = 0;
-const int ENT_CLIENT_DEAD = 1;
-const int ENT_CLIENT_ENTCS = 2;
-const int ENT_CLIENT_SCORES_INFO = 3;
-const int ENT_CLIENT_SCORES = 4;
-const int ENT_CLIENT_TEAMSCORES = 5;
-const int ENT_CLIENT_POINTPARTICLES = 6;
-const int ENT_CLIENT_RAINSNOW = 7;
-const int ENT_CLIENT_LASER = 8;
-const int ENT_CLIENT_NAGGER = 9; // flags [votecalledvote]
-const int ENT_CLIENT_RADARLINK = 11; // flags [startorigin] [endorigin] [startcolor+16*endcolor]
-const int ENT_CLIENT_PROJECTILE = 12;
-const int ENT_CLIENT_GIBSPLASH = 13;
-const int ENT_CLIENT_DAMAGEINFO = 14;
-const int ENT_CLIENT_INIT = 16;
-const int ENT_CLIENT_MAPVOTE = 17;
-const int ENT_CLIENT_CLIENTDATA = 18;
-const int ENT_CLIENT_RANDOMSEED = 19;
-const int ENT_CLIENT_WALL = 20;
-const int ENT_CLIENT_SPIDERBOT = 21;
-const int ENT_CLIENT_MODELEFFECT = 22;
-const int ENT_CLIENT_TUBANOTE = 23;
-const int ENT_CLIENT_WARPZONE = 24;
-const int ENT_CLIENT_WARPZONE_CAMERA = 25;
-const int ENT_CLIENT_TRIGGER_MUSIC = 26;
-const int ENT_CLIENT_HOOK = 27;
-const int ENT_CLIENT_INVENTORY = 28;
-const int ENT_CLIENT_ARC_BEAM = 29; // WEAPONTODO: fix numbers
-const int ENT_CLIENT_ACCURACY = 30;
-const int ENT_CLIENT_SHOWNAMES = 31;
-const int ENT_CLIENT_WARPZONE_TELEPORTED = 32;
-const int ENT_CLIENT_MODEL = 33;
-const int ENT_CLIENT_ITEM = 34;
-const int ENT_CLIENT_BUMBLE_RAYGUN = 35;
-const int ENT_CLIENT_SPAWNPOINT = 36;
-const int ENT_CLIENT_SPAWNEVENT = 37;
-const int ENT_CLIENT_NOTIFICATION = 38;
-const int ENT_CLIENT_ELIMINATEDPLAYERS = 39;
-const int ENT_CLIENT_TURRET = 40;
-const int ENT_CLIENT_AUXILIARYXHAIR = 50;
-const int ENT_CLIENT_VEHICLE = 60;
-const int ENT_CLIENT_LADDER = 61;
-const int ENT_CLIENT_TRIGGER_PUSH = 62;
-const int ENT_CLIENT_TARGET_PUSH = 63;
-const int ENT_CLIENT_CONVEYOR = 64;
-const int ENT_CLIENT_DOOR = 65;
-const int ENT_CLIENT_TRAIN = 66;
-const int ENT_CLIENT_PLAT = 67;
-const int ENT_CLIENT_TRIGGER_IMPULSE = 68;
-const int ENT_CLIENT_SWAMP = 69;
-const int ENT_CLIENT_CORNER = 70;
-const int ENT_CLIENT_KEYLOCK = 71;
-const int ENT_CLIENT_GENERATOR = 72;
-const int ENT_CLIENT_CONTROLPOINT_ICON = 73;
-const int ENT_CLIENT_EFFECT = 74;
-const int ENT_CLIENT_MINIGAME = 75;
-const int ENT_CLIENT_VIEWLOC = 78;
-const int ENT_CLIENT_VIEWLOC_TRIGGER = 79;
-
-const int ENT_CLIENT_MUTATOR = TE_CSQC_MUTATOR; // 99
+REGISTER_NET_LINKED(_ENT_CLIENT_INIT)
+#ifdef CSQC
+NET_HANDLE(_ENT_CLIENT_INIT, bool isnew) { return true; }
+#endif
+/** Sent as a temp entity from a persistent linked entity */
+REGISTER_NET_TEMP(ENT_CLIENT_INIT)
+
+REGISTER_NET_LINKED(ENT_CLIENT_ENTCS)
+REGISTER_NET_LINKED(ENT_CLIENT_SCORES_INFO)
+REGISTER_NET_LINKED(ENT_CLIENT_SCORES)
+REGISTER_NET_LINKED(ENT_CLIENT_TEAMSCORES)
+REGISTER_NET_LINKED(ENT_CLIENT_NAGGER) // flags [votecalledvote]
+REGISTER_NET_LINKED(ENT_CLIENT_RADARLINK) // flags [startorigin] [endorigin] [startcolor+16*endcolor]
+REGISTER_NET_LINKED(ENT_CLIENT_PROJECTILE)
+REGISTER_NET_LINKED(ENT_CLIENT_MAPVOTE)
+REGISTER_NET_LINKED(ENT_CLIENT_CLIENTDATA)
+REGISTER_NET_LINKED(ENT_CLIENT_RANDOMSEED)
+REGISTER_NET_LINKED(ENT_CLIENT_ACCURACY)
+REGISTER_NET_LINKED(ENT_CLIENT_ELIMINATEDPLAYERS)
+
+REGISTER_NET_LINKED(ENT_CLIENT_MODEL)
+
+REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE)
+REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_CAMERA)
+REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_TELEPORTED)
+
+REGISTER_NET_LINKED(ENT_CLIENT_ARC_BEAM)
+REGISTER_NET_LINKED(ENT_CLIENT_HOOK)
+REGISTER_NET_LINKED(ENT_CLIENT_TUBANOTE)
+
+REGISTER_NET_LINKED(ENT_CLIENT_SPAWNPOINT)
+REGISTER_NET_LINKED(ENT_CLIENT_SPAWNEVENT)
+REGISTER_NET_LINKED(ENT_CLIENT_WALL)
const int SPRITERULE_DEFAULT = 0;
const int SPRITERULE_TEAMPLAY = 1;
///////////////////////////
// csqc communication stuff
-const int CTF_STATE_ATTACK = 1;
-const int CTF_STATE_DEFEND = 2;
-const int CTF_STATE_COMMANDER = 3;
-
const int HUD_NORMAL = 0;
const int HUD_BUMBLEBEE_GUN = 25;
const int SP_DMGTAKEN = 11;
// game mode specific indices are not in common/, but in server/scores_rules.qc!
-const int CH_INFO = 0;
-const int CH_TRIGGER = -3;
-const int CH_WEAPON_A = -1;
-const int CH_WEAPON_SINGLE = 1;
-const int CH_VOICE = -2;
-const int CH_BGM_SINGLE = 8;
-const int CH_AMBIENT = -9;
-const int CH_TRIGGER_SINGLE = 3;
-const int CH_SHOTS = -4;
-const int CH_SHOTS_SINGLE = 4;
-const int CH_WEAPON_B = -1;
-const int CH_PAIN = -6;
-const int CH_PAIN_SINGLE = 6;
-const int CH_PLAYER = -7;
-const int CH_PLAYER_SINGLE = 7;
-const int CH_TUBA_SINGLE = 5;
-
-const float ATTEN_NONE = 0;
-const float ATTEN_MIN = 0.015625;
-const float ATTEN_NORM = 0.5;
-const float ATTEN_LARGE = 1;
-const float ATTEN_IDLE = 2;
-const float ATTEN_STATIC = 3;
-const float ATTEN_MAX = 3.984375;
-
-const float VOL_BASE = 0.7;
-const float VOL_BASEVOICE = 1.0;
-
// WEAPONTODO: move this into separate/new projectile handling code // this sets sounds and other properties of the projectiles in csqc
const int PROJECTILE_ELECTRO = 1;
const int PROJECTILE_ROCKET = 2;
CSQCPlayer_SetViewLocation();
// force updates of player entities that often even if unchanged
+#ifndef CSQCPLAYER_FORCE_UPDATES
#define CSQCPLAYER_FORCE_UPDATES 0.25
+#endif
// mod must define:
//vector PL_MIN = ...;
string Deathtype_Name(int deathtype)
{
if (DEATH_ISSPECIAL(deathtype)) {
- entity deathent = Deathtypes[deathtype - DT_FIRST];
+ entity deathent = Deathtypes_from(deathtype - DT_FIRST);
if (!deathent) { backtrace("Deathtype_Name: Could not find deathtype entity!\n"); return ""; }
return deathent.nent_name;
}
#include "../notifications.qh"
-REGISTRY(Deathtypes, BIT(6))
+REGISTRY(Deathtypes, BITS(8))
+#define Deathtypes_from(i) _Deathtypes_from(i, NULL)
REGISTER_REGISTRY(RegisterDeathtypes)
+REGISTRY_CHECK(Deathtypes)
.entity death_msgself;
.entity death_msgmurder;
#define REGISTER_DEATHTYPE(id, msg_death, msg_death_by, extra) \
REGISTER(RegisterDeathtypes, DEATH, Deathtypes, id, m_id, new(deathtype)) { \
+ make_pure(this); \
this.m_id += DT_FIRST; \
this.nent_name = #id; \
this.death_msgextra = extra; \
const int DT_FIRST = BIT(13);
#define DEATH_ISSPECIAL(t) (t >= DT_FIRST)
-#define DEATH_IS(t, dt) (DEATH_ISSPECIAL(t) && (Deathtypes[t - DT_FIRST]) == dt)
-#define DEATH_ENT(t) (DEATH_ISSPECIAL(t) ? (Deathtypes[t - DT_FIRST]) : NULL)
-#define DEATH_ISVEHICLE(t) (DEATH_ISSPECIAL(t) && (Deathtypes[t - DT_FIRST]).death_msgextra == "vehicle")
-#define DEATH_ISTURRET(t) (DEATH_ISSPECIAL(t) && (Deathtypes[t - DT_FIRST]).death_msgextra == "turret")
-#define DEATH_ISMONSTER(t) (DEATH_ISSPECIAL(t) && (Deathtypes[t - DT_FIRST]).death_msgextra == "monster")
+#define DEATH_IS(t, dt) (DEATH_ISSPECIAL(t) && (Deathtypes_from(t - DT_FIRST)) == dt)
+#define DEATH_ENT(t) (DEATH_ISSPECIAL(t) ? (Deathtypes_from(t - DT_FIRST)) : NULL)
+#define DEATH_ISVEHICLE(t) (DEATH_ISSPECIAL(t) && (Deathtypes_from(t - DT_FIRST)).death_msgextra == "vehicle")
+#define DEATH_ISTURRET(t) (DEATH_ISSPECIAL(t) && (Deathtypes_from(t - DT_FIRST)).death_msgextra == "turret")
+#define DEATH_ISMONSTER(t) (DEATH_ISSPECIAL(t) && (Deathtypes_from(t - DT_FIRST)).death_msgextra == "monster")
#define DEATH_WEAPONOF(t) (DEATH_ISSPECIAL(t) ? WEP_Null : get_weaponinfo((t) & DEATH_WEAPONMASK))
#define DEATH_ISWEAPON(t, w) (DEATH_WEAPONOF(t) == (w))
--- /dev/null
+#ifndef DEBUG_H
+#define DEBUG_H
+
+.bool debug;
+.int sv_entnum;
+REGISTER_NET_TEMP(net_debug)
+#ifdef CSQC
+ NET_HANDLE(net_debug, bool isNew)
+ {
+ Net_Accept(net_debug);
+ this.sv_entnum = ReadShort();
+ if (ReadByte()) make_pure(this);
+ this.origin_x = ReadCoord();
+ this.origin_y = ReadCoord();
+ this.origin_z = ReadCoord();
+ setorigin(this, this.origin);
+ this.debug = true; // identify server entities by this
+ this.classname = strzone(ReadString());
+ this.sourceLocFile = strzone(ReadString());
+ this.sourceLocLine = ReadInt24_t();
+ return true;
+ }
+#endif
+
+#ifdef SVQC
+ bool debug_send(entity this, entity to, int sf)
+ {
+ int channel = MSG_ONE;
+ msg_entity = to;
+ WriteHeader(channel, net_debug);
+ WriteShort(channel, num_for_edict(this));
+ WriteByte(channel, is_pure(this));
+ WriteCoord(channel, this.origin.x);
+ WriteCoord(channel, this.origin.y);
+ WriteCoord(channel, this.origin.z);
+ WriteString(channel, this.classname);
+ WriteString(channel, this.sourceLocFile);
+ WriteInt24_t(channel, this.sourceLocLine);
+ return true;
+ }
+#endif
+
+bool autocvar_debugdraw;
+
+#ifdef CSQC
+ .int debugdraw_last;
+ vector project_3d_to_2d(vector vec);
+ void Debug_Draw()
+ {
+ if (!autocvar_debugdraw) return;
+ static int debugdraw_frame;
+ ++debugdraw_frame;
+ const int size = 8;
+ for (entity e1 = NULL; (e1 = nextent(e1)); )
+ {
+ if (e1.debugdraw_last == debugdraw_frame) continue;
+ int ofs = 0;
+ for (entity e = findradius(e1.origin, 100); e; e = e.chain)
+ {
+ if (e.debugdraw_last == debugdraw_frame) continue;
+ e.debugdraw_last = debugdraw_frame;
+ vector rgb = (e.debug) ? '0 0 1' : '1 0 0';
+ if (is_pure(e))
+ {
+ if (autocvar_debugdraw < 2) continue;
+ rgb.y = 1;
+ }
+ vector pos = project_3d_to_2d(e.origin);
+ if (pos.z < 0) continue;
+ pos.z = 0;
+ pos.y += ofs * size;
+ drawcolorcodedstring2(pos,
+ sprintf("%d: '%s'@%s:%d", (e.debug ? e.sv_entnum : num_for_edict(e)),
+ e.classname, e.sourceLocFile, e.sourceLocLine),
+ size * '1 1 0', rgb, 0.5, DRAWFLAG_NORMAL);
+ ++ofs;
+ }
+ }
+ }
+#endif
+
+#ifdef SVQC
+ GENERIC_COMMAND(debugdraw_sv, "Dump all server entities")
+ {
+ switch (request)
+ {
+ case CMD_REQUEST_COMMAND:
+ {
+ if (!autocvar_debugdraw) return;
+ int n = 1000;
+ int rem = n;
+ for (entity e = NULL; (e = findfloat(e, debug, 0)) && rem > 0; )
+ {
+ if (autocvar_debugdraw < 2 && is_pure(e)) continue;
+ debug_send(e, nextent(NULL), 0);
+ e.debug = true;
+ --rem;
+ }
+ LOG_INFOF("%d server entities sent\n", n - rem);
+ return;
+ }
+
+ default:
+ case CMD_REQUEST_USAGE:
+ {
+ LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " debugdraw_sv"));
+ return;
+ }
+ }
+ }
+#endif
+
+#endif
EFFECT(0, ARC_BEAM_HEAL, "arc_beam_heal")
EFFECT(0, ARC_BEAM_HEAL_IMPACT, "arc_beam_healimpact")
EFFECT(0, ARC_BEAM_HEAL_IMPACT2, "healray_impact")
+EFFECT(0, ARC_OVERHEAT, "arc_overheat")
+EFFECT(0, ARC_OVERHEAT_FIRE, "arc_overheat_fire")
+EFFECT(0, ARC_SMOKE, "arc_smoke")
EFFECT(0, ARC_LIGHTNING, "arc_lightning")
EFFECT(0, ARC_LIGHTNING2, "electro_lightning")
EFFECT(0, SEEKER_MUZZLEFLASH, "seeker_muzzleflash")
-EFFECT(0, FLAK_BOUNCE, "flak_bounce")
-
EFFECT(1, FIREBALL, "fireball")
EFFECT(0, FIREBALL_BFGDAMAGE, "fireball_bfgdamage")
EFFECT(0, FIREBALL_EXPLODE, "fireball_explode")
EFFECT(1, PASS_YELLOW, "yellow_pass")
EFFECT(1, PASS_PINK, "pink_pass")
EFFECT(1, PASS_NEUTRAL, "neutral_pass")
+entity EFFECT_PASS(int teamid)
+{
+ switch (teamid) {
+ case NUM_TEAM_1: return EFFECT_PASS_RED;
+ case NUM_TEAM_2: return EFFECT_PASS_BLUE;
+ case NUM_TEAM_3: return EFFECT_PASS_YELLOW;
+ case NUM_TEAM_4: return EFFECT_PASS_PINK;
+ default: return EFFECT_PASS_NEUTRAL;
+ }
+}
EFFECT(0, CAP_RED, "red_cap")
EFFECT(0, CAP_BLUE, "blue_cap")
#include "all.qh"
+REGISTER_NET_TEMP(net_effect)
#ifdef CSQC
-void Read_Effect(bool is_new)
+NET_HANDLE(net_effect, bool isNew)
{
int net_name = (Effects_COUNT >= 255) ? ReadShort() : ReadByte();
- entity eff = Effects[net_name];
+ entity eff = Effects_from(net_name);
- vector v, vel = '0 0 0';
+ vector vel = '0 0 0';
int eff_cnt = 1;
bool eff_trail = eff.eent_eff_trail;
+ vector v;
v_x = ReadCoord();
v_y = ReadCoord();
v_z = ReadCoord();
if(!eff_trail)
eff_cnt = ReadByte();
- if(is_new)
- {
- if(eff_trail)
- WarpZone_TrailParticles(world, particleeffectnum(eff), v, vel);
- else
- pointparticles(particleeffectnum(eff), v, vel, eff_cnt);
- }
+ if(eff_trail)
+ WarpZone_TrailParticles(world, particleeffectnum(eff), v, vel);
+ else
+ pointparticles(eff, v, vel, eff_cnt);
+ return true;
}
#endif
#ifdef SVQC
bool Net_Write_Effect(entity this, entity client, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_EFFECT);
+ int channel = MSG_ONE;
+ msg_entity = client;
+ WriteHeader(channel, net_effect);
(Effects_COUNT >= 255)
- ? WriteShort(MSG_ENTITY, self.m_id)
- : WriteByte(MSG_ENTITY, self.m_id);
- WriteCoord(MSG_ENTITY, self.eent_net_location_x);
- WriteCoord(MSG_ENTITY, self.eent_net_location_y);
- WriteCoord(MSG_ENTITY, self.eent_net_location_z);
+ ? WriteShort(channel, this.m_id)
+ : WriteByte(channel, this.m_id);
+ WriteCoord(channel, this.eent_net_location_x);
+ WriteCoord(channel, this.eent_net_location_y);
+ WriteCoord(channel, this.eent_net_location_z);
// attempt to save a tiny bit more bandwidth by not sending velocity if it isn't set
- if(self.eent_net_velocity)
+ if(this.eent_net_velocity)
{
- WriteByte(MSG_ENTITY, true);
- WriteCoord(MSG_ENTITY, self.eent_net_velocity_x);
- WriteCoord(MSG_ENTITY, self.eent_net_velocity_y);
- WriteCoord(MSG_ENTITY, self.eent_net_velocity_z);
+ WriteByte(channel, true);
+ WriteCoord(channel, this.eent_net_velocity_x);
+ WriteCoord(channel, this.eent_net_velocity_y);
+ WriteCoord(channel, this.eent_net_velocity_z);
}
- else { WriteByte(MSG_ENTITY, false); }
+ else { WriteByte(channel, false); }
- if(!self.eent_eff_trail) { WriteByte(MSG_ENTITY, self.eent_net_count); }
+ if(!this.eent_eff_trail) { WriteByte(channel, this.eent_net_count); }
return true;
}
{
if(!eff) { return; }
if(!eff.eent_eff_trail && !eff_cnt) { return; } // effect has no count!
- entity net_eff = spawn();
+ entity net_eff = new(net_effect);
+ make_pure(net_eff);
net_eff.owner = eff;
- net_eff.classname = "net_effect";
//net_eff.eent_broadcast = broadcast;
net_eff.m_id = eff.m_id;
net_eff.eent_net_velocity = eff_vel;
net_eff.eent_net_count = eff_cnt;
net_eff.eent_eff_trail = eff.eent_eff_trail;
- net_eff.think = SUB_Remove;
- net_eff.nextthink = time + 0.2; // don't need to keep this long
-
- Net_LinkEntity(net_eff, false, 0, Net_Write_Effect);
+ entity e; FOR_EACH_REALCLIENT(e) Net_Write_Effect(net_eff, e, 0);
+ remove(net_eff);
}
void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt)
return;
));
// revert to engine handling
- pointparticles(_particleeffectnum(eff_name), eff_loc, eff_vel, eff_cnt);
+ __pointparticles(_particleeffectnum(eff_name), eff_loc, eff_vel, eff_cnt);
}
#endif
#include "effect.qh"
-#ifdef CSQC
-void Read_Effect(bool is_new);
-#elif defined(SVQC)
+#ifdef SVQC
void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt);
void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt);
#endif
-REGISTRY(Effects, BIT(8))
+REGISTRY(Effects, BITS(8))
+#define Effects_from(i) _Effects_from(i, EFFECT_Null)
REGISTER_REGISTRY(RegisterEffects)
+REGISTRY_CHECK(Effects)
#define EFFECT(istrail, name, realname) \
REGISTER(RegisterEffects, EFFECT, Effects, name, m_id, Create_Effect_Entity(realname, istrail));
-void RegisterEffects_First()
-{
- #ifdef SVQC
- #define dedi (server_is_dedicated ? "a dedicated " : "")
- #else
- #define dedi ""
- #endif
-
- LOG_TRACEF("Beginning effect initialization on %s%s program...\n", dedi, PROGNAME);
- #undef dedi
-}
-
-void RegisterEffects_Done()
-{
- LOG_TRACE("Effects initialization successful!\n");
-}
-
-// NOW we actually activate the declarations
-ACCUMULATE_FUNCTION(RegisterEffects, RegisterEffects_First)
EFFECT(0, Null, string_null)
#include "all.inc"
-ACCUMULATE_FUNCTION(RegisterEffects, RegisterEffects_Done)
#endif
#ifndef EFFECT_H
#define EFFECT_H
-#define particleeffectnum(e) _particleeffectnum(e.eent_eff_name)
+#define particleeffectnum(e) \
+ _particleeffectnum(e.eent_eff_name)
+
+#if defined(SVQC)
+ #define pointparticles(effect, org, vel, howmany) \
+ Send_Effect(effect, org, vel, howmany)
+ #define trailparticles(e, effect, org, vel) \
+ ((!e) ? Send_Effect(effect, org, vel, 0) \
+ : __trailparticles(e, particleeffectnum(effect), org, vel))
+#elif defined(CSQC)
+ #define pointparticles(effect, org, vel, howmany) \
+ __pointparticles(particleeffectnum(effect), org, vel, howmany)
+ #define trailparticles(e, effect, org, vel) \
+ __trailparticles(e, particleeffectnum(effect), org, vel)
+#endif
.int m_id;
.string eent_eff_name;
entity Create_Effect_Entity(string eff_name, bool eff_trail)
{
entity this = new(effect_entity);
+ make_pure(this);
this.eent_eff_name = eff_name;
this.eent_eff_trail = eff_trail;
return this;
}
-REGISTRY(EffectInfos, BIT(9))
+REGISTRY(EffectInfos, BITS(9))
+#define EffectInfos_from(i) _EffectInfos_from(i, NULL)
REGISTER_REGISTRY(RegisterEffectInfos)
#define EFFECTINFO(name) \
[[accumulate]] void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) { } \
--- /dev/null
+#include "casings.qc"
+#include "damageeffects.qc"
+#include "gibs.qc"
+#include "lightningarc.qc"
+#include "modeleffects.qc"
--- /dev/null
+#include "all.qh"
+
+#define IMPLEMENTATION
+#include "all.inc"
+#undef IMPLEMENTATION
--- /dev/null
+#ifndef EFFECTS_QC
+#define EFFECTS_QC
+#include "all.inc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+
+#include "../../util.qh"
+
+#ifdef CSQC
+#include "../../movetypes/movetypes.qh"
+#include "rubble.qh"
+#endif
+
+REGISTER_NET_TEMP(casings)
+
+#ifdef SVQC
+void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner)
+{SELFPARAM();
+ .entity weaponentity = weaponentities[0]; // TODO: parameter
+ entity wep = self.(weaponentity);
+ vector org = self.origin + self.view_ofs + wep.spawnorigin.x * v_forward - wep.spawnorigin.y * v_right + wep.spawnorigin.z * v_up;
+
+ if (!sound_allowed(MSG_BROADCAST, casingowner))
+ casingtype |= 0x80;
+
+ WriteHeader(MSG_ALL, casings);
+ WriteByte(MSG_ALL, casingtype);
+ WriteCoord(MSG_ALL, org.x);
+ WriteCoord(MSG_ALL, org.y);
+ WriteCoord(MSG_ALL, org.z);
+ WriteShort(MSG_ALL, compressShortVector(vel)); // actually compressed velocity
+ WriteByte(MSG_ALL, ang.x * 256 / 360);
+ WriteByte(MSG_ALL, ang.y * 256 / 360);
+ WriteByte(MSG_ALL, ang.z * 256 / 360);
+}
+#endif
+
+#ifdef CSQC
+entityclass(Casing);
+class(Casing) .float alpha;
+class(Casing) .bool silent;
+class(Casing) .int state;
+class(Casing) .float cnt;
+
+void Casing_Delete()
+{SELFPARAM();
+ remove(self);
+}
+
+void Casing_Draw(entity this)
+{
+ if (self.move_flags & FL_ONGROUND)
+ {
+ self.move_angles_x = 0;
+ self.move_angles_z = 0;
+ self.flags &= ~FL_ONGROUND;
+ }
+
+ Movetype_Physics_MatchTicrate(autocvar_cl_casings_ticrate, autocvar_cl_casings_sloppy);
+ if (wasfreed(self))
+ return; // deleted by touch function
+
+ self.renderflags = 0;
+ self.alpha = bound(0, self.cnt - time, 1);
+
+ if (self.alpha < ALPHA_MIN_VISIBLE)
+ {
+ Casing_Delete();
+ self.drawmask = 0;
+ }
+}
+
+SOUND(BRASS1, W_Sound("brass1"));
+SOUND(BRASS2, W_Sound("brass2"));
+SOUND(BRASS3, W_Sound("brass3"));
+Sound SND_BRASS_RANDOM() {
+ return Sounds_from(SND_BRASS1.m_id + floor(prandom() * 3));
+}
+SOUND(CASINGS1, W_Sound("casings1"));
+SOUND(CASINGS2, W_Sound("casings2"));
+SOUND(CASINGS3, W_Sound("casings3"));
+Sound SND_CASINGS_RANDOM() {
+ return Sounds_from(SND_CASINGS1.m_id + floor(prandom() * 3));
+}
+
+void Casing_Touch()
+{SELFPARAM();
+ if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+ {
+ Casing_Delete();
+ return;
+ }
+
+ if (!self.silent)
+ if (!trace_ent || trace_ent.solid == SOLID_BSP)
+ {
+ if (vlen(self.velocity) > 50)
+ {
+ if (time >= self.nextthink)
+ {
+ Sound s;
+ switch (self.state)
+ {
+ case 1:
+ s = SND_CASINGS_RANDOM();
+ break;
+ default:
+ s = SND_BRASS_RANDOM();
+ break;
+ }
+
+ sound (self, CH_SHOTS, s, VOL_BASE, ATTEN_LARGE);
+ }
+ }
+ }
+
+ self.nextthink = time + 0.2;
+}
+
+void Casing_Damage(float thisdmg, int hittype, vector org, vector thisforce)
+{SELFPARAM();
+ if (thisforce.z < 0)
+ thisforce.z = 0;
+ self.move_velocity = self.move_velocity + thisforce + '0 0 100';
+ self.move_flags &= ~FL_ONGROUND;
+}
+
+NET_HANDLE(casings, bool isNew)
+{
+ int _state = ReadByte();
+ vector org;
+ org_x = ReadCoord();
+ org_y = ReadCoord();
+ org_z = ReadCoord();
+ vector vel = decompressShortVector(ReadShort());
+ vector ang;
+ ang_x = ReadByte() * 360 / 256;
+ ang_y = ReadByte() * 360 / 256;
+ ang_z = ReadByte() * 360 / 256;
+ return = true;
+
+ if (!autocvar_cl_casings) return;
+
+ Casing casing = RubbleNew("casing");
+ casing.silent = (_state & 0x80);
+ casing.state = (_state & 0x7F);
+ casing.origin = org;
+ setorigin(casing, casing.origin);
+ casing.velocity = vel;
+ casing.angles = ang;
+ casing.drawmask = MASK_NORMAL;
+
+ casing.draw = Casing_Draw;
+ casing.move_origin = casing.origin;
+ casing.move_velocity = casing.velocity + 2 * prandomvec();
+ casing.move_angles = casing.angles;
+ casing.move_avelocity = '0 250 0' + 100 * prandomvec();
+ casing.move_movetype = MOVETYPE_BOUNCE;
+ casing.move_touch = Casing_Touch;
+ casing.move_time = time;
+ casing.event_damage = Casing_Damage;
+ casing.solid = SOLID_TRIGGER;
+
+ switch (casing.state)
+ {
+ case 1:
+ setmodel(casing, MDL_CASING_SHELL);
+ casing.cnt = time + autocvar_cl_casings_shell_time;
+ break;
+ default:
+ setmodel(casing, MDL_CASING_BULLET);
+ casing.cnt = time + autocvar_cl_casings_bronze_time;
+ break;
+ }
+
+ setsize(casing, '0 0 -1', '0 0 -1');
+
+ RubbleLimit("casing", autocvar_cl_casings_maxcount, Casing_Delete);
+}
+
+#endif
+#endif
--- /dev/null
+#ifndef DAMAGEEFFECTS_H
+#define DAMAGEEFFECTS_H
+
+#ifdef CSQC
+#include "../../deathtypes/all.qh"
+#include "../../movetypes/movetypes.qh"
+#include "../../vehicles/all.qh"
+#include "../../weapons/all.qh"
+#endif
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+REGISTER_NET_LINKED(ENT_CLIENT_DAMAGEINFO)
+
+#ifdef SVQC
+
+bool Damage_DamageInfo_SendEntity(entity this, entity to, int sf)
+{
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_DAMAGEINFO);
+ WriteShort(MSG_ENTITY, self.projectiledeathtype);
+ WriteCoord(MSG_ENTITY, floor(self.origin.x));
+ WriteCoord(MSG_ENTITY, floor(self.origin.y));
+ WriteCoord(MSG_ENTITY, floor(self.origin.z));
+ WriteByte(MSG_ENTITY, bound(1, self.dmg, 255));
+ WriteByte(MSG_ENTITY, bound(0, self.dmg_radius, 255));
+ WriteByte(MSG_ENTITY, bound(1, self.dmg_edge, 255));
+ WriteShort(MSG_ENTITY, self.oldorigin.x);
+ WriteByte(MSG_ENTITY, self.species);
+ return true;
+}
+
+void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner)
+{
+ // TODO maybe call this from non-edgedamage too?
+ // TODO maybe make the client do the particle effects for the weapons and the impact sounds using this info?
+
+ entity e;
+
+ if(!sound_allowed(MSG_BROADCAST, dmgowner))
+ deathtype |= 0x8000;
+
+ e = new(damageinfo);
+ make_pure(e);
+ setorigin(e, org);
+ e.projectiledeathtype = deathtype;
+ e.dmg = coredamage;
+ e.dmg_edge = edgedamage;
+ e.dmg_radius = rad;
+ e.dmg_force = vlen(force);
+ e.velocity = force;
+ e.oldorigin_x = compressShortVector(e.velocity);
+ e.species = bloodtype;
+
+ Net_LinkEntity(e, false, 0.2, Damage_DamageInfo_SendEntity);
+}
+
+#endif
+
+#ifdef CSQC
+
+/** number of effects which currently are attached to a player */
+.int total_damages;
+
+.entity tag_entity;
+
+.float cnt;
+.int state;
+.bool isplayermodel;
+
+void DamageEffect_Think()
+{SELFPARAM();
+ // if particle distribution is enabled, slow ticrate by total number of damages
+ if(autocvar_cl_damageeffect_distribute)
+ self.nextthink = time + autocvar_cl_damageeffect_ticrate * self.owner.total_damages;
+ else
+ self.nextthink = time + autocvar_cl_damageeffect_ticrate;
+
+ if(time >= self.cnt || !self.owner || !self.owner.modelindex || !self.owner.drawmask)
+ {
+ // time is up or the player got gibbed / disconnected
+ self.owner.total_damages = max(0, self.owner.total_damages - 1);
+ remove(self);
+ return;
+ }
+ if(self.state && !self.owner.csqcmodel_isdead)
+ {
+ // if the player was dead but is now alive, it means he respawned
+ // if so, clear his damage effects, or damages from his dead body will be copied back
+ self.owner.total_damages = max(0, self.owner.total_damages - 1);
+ remove(self);
+ return;
+ }
+ self.state = self.owner.csqcmodel_isdead;
+ if(self.owner.isplayermodel && (self.owner.entnum == player_localentnum) && !autocvar_chase_active)
+ return; // if we aren't using a third person camera, hide our own effects
+
+ // now generate the particles
+ vector org;
+ org = gettaginfo(self, 0); // origin at attached location
+ __pointparticles(self.team, org, '0 0 0', 1);
+}
+
+string species_prefix(int specnum)
+{
+ switch(specnum)
+ {
+ case SPECIES_HUMAN: return "";
+ case SPECIES_ALIEN: return "alien_";
+ case SPECIES_ROBOT_SHINY: return "robot_";
+ case SPECIES_ROBOT_RUSTY: return "robot_"; // use the same effects, only different gibs
+ case SPECIES_ROBOT_SOLID: return "robot_"; // use the same effects, only different gibs
+ case SPECIES_ANIMAL: return "animal_";
+ case SPECIES_RESERVED: return "reserved_";
+ default: return "";
+ }
+}
+
+void DamageEffect(vector hitorg, float thedamage, int type, int specnum)
+{SELFPARAM();
+ // particle effects for players and objects damaged by weapons (eg: flames coming out of victims shot with rockets)
+
+ int nearestbone = 0;
+ float life;
+ string specstr, effectname;
+ entity e;
+
+ if(!autocvar_cl_damageeffect || autocvar_cl_gentle || autocvar_cl_gentle_damage)
+ return;
+ if(!self || !self.modelindex || !self.drawmask)
+ return;
+
+ // if this is a rigged mesh, the effect will show on the bone where damage was dealt
+ // we do this by choosing the skeletal bone closest to the impact, and attaching our entity to it
+ // if there's no skeleton, object origin will automatically be selected
+ FOR_EACH_TAG(self)
+ {
+ if(!tagnum)
+ continue; // skip empty bones
+ // blacklist bones positioned outside the mesh, or the effect will be floating
+ // TODO: Do we have to do it this way? Why do these bones exist at all?
+ if(gettaginfo_name == "master" || gettaginfo_name == "knee_L" || gettaginfo_name == "knee_R" || gettaginfo_name == "leg_L" || gettaginfo_name == "leg_R")
+ continue; // player model bone blacklist
+
+ // now choose the bone closest to impact origin
+ if(nearestbone == 0 || vlen(hitorg - gettaginfo(self, tagnum)) <= vlen(hitorg - gettaginfo(self, nearestbone)))
+ nearestbone = tagnum;
+ }
+ gettaginfo(self, nearestbone); // set gettaginfo_name
+
+ // return if we reached our damage effect limit or damages are disabled
+ // TODO: When the limit is reached, it would be better if the oldest damage was removed instead of not adding a new one
+ if(nearestbone)
+ {
+ if(self.total_damages >= autocvar_cl_damageeffect_bones)
+ return; // allow multiple damages on skeletal models
+ }
+ else
+ {
+ if(autocvar_cl_damageeffect < 2 || self.total_damages)
+ return; // allow a single damage on non-skeletal models
+ }
+
+ life = bound(autocvar_cl_damageeffect_lifetime_min, thedamage * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max);
+
+ effectname = DEATH_WEAPONOF(type).netname;
+
+ if(substring(effectname, strlen(effectname) - 5, 5) == "BLOOD")
+ {
+ if(self.isplayermodel)
+ {
+ specstr = species_prefix(specnum);
+ specstr = substring(specstr, 0, strlen(specstr) - 1);
+ effectname = strreplace("BLOOD", specstr, effectname);
+ }
+ else { return; } // objects don't bleed
+ }
+
+ e = new(damage);
+ make_pure(e);
+ setmodel(e, MDL_Null); // necessary to attach and read origin
+ setattachment(e, self, gettaginfo_name); // attach to the given bone
+ e.owner = self;
+ e.cnt = time + life;
+ e.team = _particleeffectnum(effectname);
+ e.think = DamageEffect_Think;
+ e.nextthink = time;
+ self.total_damages += 1;
+}
+
+NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew)
+{
+ make_pure(this);
+ float thedamage, rad, edge, thisdmg;
+ bool hitplayer = false;
+ int species, forcemul;
+ vector force, thisforce;
+
+ w_deathtype = ReadShort();
+ w_issilent = (w_deathtype & 0x8000);
+ w_deathtype = (w_deathtype & 0x7FFF);
+
+ w_org.x = ReadCoord();
+ w_org.y = ReadCoord();
+ w_org.z = ReadCoord();
+
+ thedamage = ReadByte();
+ rad = ReadByte();
+ edge = ReadByte();
+ force = decompressShortVector(ReadShort());
+ species = ReadByte();
+
+ return = true;
+
+ if (!isNew)
+ return;
+
+ if(rad < 0)
+ {
+ rad = -rad;
+ forcemul = -1;
+ }
+ else
+ forcemul = 1;
+
+ for(entity e = findradius(w_org, rad + MAX_DAMAGEEXTRARADIUS); e; e = e.chain)
+ {
+ setself(e);
+ // attached ents suck
+ if(self.tag_entity)
+ continue;
+
+ vector nearest = NearestPointOnBox(self, w_org);
+ if(rad)
+ {
+ thisdmg = ((vlen (nearest - w_org) - bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
+ if(thisdmg >= 1)
+ continue;
+ if(thisdmg < 0)
+ thisdmg = 0;
+ if(thedamage)
+ {
+ thisdmg = thedamage + (edge - thedamage) * thisdmg;
+ thisforce = forcemul * vlen(force) * (thisdmg / thedamage) * normalize(self.origin - w_org);
+ }
+ else
+ {
+ thisdmg = 0;
+ thisforce = forcemul * vlen(force) * normalize(self.origin - w_org);
+ }
+ }
+ else
+ {
+ if(vlen(nearest - w_org) > bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS))
+ continue;
+
+ thisdmg = thedamage;
+ thisforce = forcemul * force;
+ }
+
+ if(self.damageforcescale)
+ if(vlen(thisforce))
+ {
+ self.move_velocity = self.move_velocity + damage_explosion_calcpush(self.damageforcescale * thisforce, self.move_velocity, autocvar_g_balance_damagepush_speedfactor);
+ self.move_flags &= ~FL_ONGROUND;
+ }
+
+ if(w_issilent)
+ self.silent = 1;
+
+ if(self.event_damage)
+ self.event_damage(thisdmg, w_deathtype, w_org, thisforce);
+
+ DamageEffect(w_org, thisdmg, w_deathtype, species);
+
+ if(self.isplayermodel)
+ hitplayer = true; // this impact damaged a player
+ }
+ setself(this);
+
+ if(DEATH_ISVEHICLE(w_deathtype))
+ {
+ traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
+ if(trace_plane_normal != '0 0 0')
+ w_backoff = trace_plane_normal;
+ else
+ w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16));
+
+ setorigin(self, w_org + w_backoff * 2); // for sound() calls
+
+ switch(DEATH_ENT(w_deathtype))
+ {
+ case DEATH_VH_CRUSH:
+ break;
+
+ // spiderbot
+ case DEATH_VH_SPID_MINIGUN:
+ sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_SPIDERBOT_MINIGUN_IMPACT, self.origin, w_backoff * 1000, 1);
+ break;
+ case DEATH_VH_SPID_ROCKET:
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_SPIDERBOT_ROCKET_EXPLODE, self.origin, w_backoff * 1000, 1);
+ break;
+ case DEATH_VH_SPID_DEATH:
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN);
+ pointparticles(EFFECT_EXPLOSION_BIG, self.origin, w_backoff * 1000, 1);
+ break;
+
+ case DEATH_VH_WAKI_GUN:
+ sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_RACER_IMPACT, self.origin, w_backoff * 1000, 1);
+ break;
+ case DEATH_VH_WAKI_ROCKET:
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_RACER_ROCKET_EXPLODE, self.origin, w_backoff * 1000, 1);
+ break;
+ case DEATH_VH_WAKI_DEATH:
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN);
+ pointparticles(EFFECT_EXPLOSION_BIG, self.origin, w_backoff * 1000, 1);
+ break;
+
+ case DEATH_VH_RAPT_CANNON:
+ sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_RAPTOR_CANNON_IMPACT, self.origin, w_backoff * 1000, 1);
+ break;
+ case DEATH_VH_RAPT_FRAGMENT:
+ float i;
+ vector ang, vel;
+ for(i = 1; i < 4; ++i)
+ {
+ vel = normalize(w_org - (w_org + normalize(force) * 16)) + randomvec() * 128;
+ ang = vectoangles(vel);
+ RaptorCBShellfragToss(w_org, vel, ang + '0 0 1' * (120 * i));
+ }
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_RAPTOR_BOMB_SPREAD, self.origin, w_backoff * 1000, 1);
+ break;
+ case DEATH_VH_RAPT_BOMB:
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_RAPTOR_BOMB_IMPACT, self.origin, w_backoff * 1000, 1);
+ break;
+ case DEATH_VH_RAPT_DEATH:
+ sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_MIN);
+ pointparticles(EFFECT_EXPLOSION_BIG, self.origin, w_backoff * 1000, 1);
+ break;
+ case DEATH_VH_BUMB_GUN:
+ sound(self, CH_SHOTS, SND_FIREBALL_IMPACT2, VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_BIGPLASMA_IMPACT, self.origin, w_backoff * 1000, 1);
+ break;
+ }
+ }
+
+
+ if(DEATH_ISTURRET(w_deathtype))
+ {
+ traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
+ if(trace_plane_normal != '0 0 0')
+ w_backoff = trace_plane_normal;
+ else
+ w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16));
+
+ setorigin(self, w_org + w_backoff * 2); // for sound() calls
+
+ switch(DEATH_ENT(w_deathtype))
+ {
+ case DEATH_TURRET_EWHEEL:
+ sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_MIN);
+ pointparticles(EFFECT_BLASTER_IMPACT, self.origin, w_backoff * 1000, 1);
+ break;
+
+ case DEATH_TURRET_FLAC:
+ pointparticles(EFFECT_HAGAR_EXPLODE, w_org, '0 0 0', 1);
+ sound(self, CH_SHOTS, SND_HAGEXP_RANDOM(), VOL_BASE, ATTEN_NORM);
+ break;
+
+ case DEATH_TURRET_MLRS:
+ case DEATH_TURRET_HK:
+ case DEATH_TURRET_WALK_ROCKET:
+ case DEATH_TURRET_HELLION:
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN);
+ pointparticles(EFFECT_ROCKET_EXPLODE, self.origin, w_backoff * 1000, 1);
+ break;
+
+ case DEATH_TURRET_MACHINEGUN:
+ case DEATH_TURRET_WALK_GUN:
+ sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_MACHINEGUN_IMPACT, self.origin, w_backoff * 1000, 1);
+ break;
+
+ case DEATH_TURRET_PLASMA:
+ sound(self, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_MIN);
+ pointparticles(EFFECT_ELECTRO_IMPACT, self.origin, w_backoff * 1000, 1);
+ break;
+
+ case DEATH_TURRET_WALK_MELEE:
+ sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_MIN);
+ pointparticles(EFFECT_TE_SPARK, self.origin, w_backoff * 1000, 1);
+ break;
+
+ case DEATH_TURRET_PHASER:
+ break;
+
+ case DEATH_TURRET_TESLA:
+ te_smallflash(self.origin);
+ break;
+
+ }
+ }
+
+ // TODO spawn particle effects and sounds based on w_deathtype
+ if(!DEATH_ISSPECIAL(w_deathtype))
+ if(!hitplayer || rad) // don't show ground impacts for hitscan weapons if a player was hit
+ {
+ Weapon hitwep = DEATH_WEAPONOF(w_deathtype);
+ w_random = prandom();
+
+ traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world);
+ if(trace_fraction < 1 && hitwep != WEP_VORTEX && hitwep != WEP_VAPORIZER)
+ w_backoff = trace_plane_normal;
+ else
+ w_backoff = -1 * normalize(force);
+ setorigin(self, w_org + w_backoff * 2); // for sound() calls
+
+ if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) {
+ hitwep.wr_impacteffect(hitwep);
+ }
+ }
+}
+
+#endif
+
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_NET_TEMP(net_gibsplash)
+
+#ifdef SVQC
+
+.int state;
+
+bool Violence_GibSplash_SendEntity(entity this, entity to, int sf)
+{
+ int channel = MSG_ONE;
+ msg_entity = to;
+ WriteHeader(channel, net_gibsplash);
+ WriteByte(channel, this.state); // actually type
+ WriteByte(channel, bound(1, this.cnt * 16, 255)); // gibbage amount multiplier
+ WriteShort(channel, floor(this.origin.x / 4)); // not using a coord here, as gibs don't need this accuracy
+ WriteShort(channel, floor(this.origin.y / 4)); // not using a coord here, as gibs don't need this accuracy
+ WriteShort(channel, floor(this.origin.z / 4)); // not using a coord here, as gibs don't need this accuracy
+ WriteShort(channel, this.oldorigin.x); // acrually compressed velocity
+ return true;
+}
+
+void Violence_GibSplash_At(vector org, vector dir, float type, float amount, entity gibowner, entity attacker)
+{SELFPARAM();
+ if(g_cts) // no gibs in CTS
+ return;
+
+ entity e = new(gibsplash);
+ e.cnt = amount;
+ e.state = type; // should stay smaller than 15
+ if(!sound_allowed(MSG_BROADCAST, gibowner) || !sound_allowed(MSG_BROADCAST, attacker))
+ e.state |= 0x40; // "silence" bit
+ e.state |= 8 * self.species; // gib type, ranges from 0 to 15
+
+ // if this is a copied dead body, send the num of its player instead
+ // TODO: remove this field, read from model txt files
+ if(self.classname == "body")
+ e.team = num_for_edict(self.enemy);
+ else
+ e.team = num_for_edict(self);
+
+ setorigin(e, org);
+ e.velocity = dir;
+
+ e.oldorigin_x = compressShortVector(e.velocity);
+
+ entity cl; FOR_EACH_REALCLIENT(cl) Violence_GibSplash_SendEntity(e, cl, 0);
+ remove(e);
+}
+
+void Violence_GibSplash(entity source, float type, float amount, entity attacker)
+{
+ Violence_GibSplash_At(source.origin + source.view_ofs, source.velocity, type, amount, source, attacker);
+}
+#endif
+
+#ifdef CSQC
+
+.vector colormod;
+.bool silent;
+
+#include "rubble.qh"
+#include "../common/movetypes/movetypes.qh"
+
+.float scale;
+.float alpha;
+.float cnt;
+.float gravity;
+
+void Gib_Delete()
+{SELFPARAM();
+ remove(self);
+}
+
+string species_prefix(int specnum);
+
+void Gib_setmodel(entity gib, string mdlname, int specnum)
+{
+ switch(specnum)
+ {
+ case SPECIES_ROBOT_RUSTY:
+ case SPECIES_ROBOT_SHINY:
+ case SPECIES_ROBOT_SOLID:
+ if(specnum != SPECIES_ROBOT_SOLID || mdlname == "models/gibs/chunk.mdl")
+ {
+ if(mdlname == "models/gibs/bloodyskull.md3")
+ setmodel(gib, MDL_GIB_ROBO);
+ else
+ setmodel(gib, MDL_GIB_ROBO_RANDOM());
+ if(specnum == SPECIES_ROBOT_SHINY)
+ {
+ gib.skin = 1;
+ gib.colormod = '2 2 2';
+ }
+ gib.scale = 1;
+ break;
+ }
+ default:
+ _setmodel(gib, mdlname);
+ gib.skin = specnum;
+ break;
+ }
+}
+
+void new_te_bloodshower (int ef, vector org, float explosionspeed, int howmany)
+{
+ float i, pmod;
+ pmod = autocvar_cl_particles_quality;
+ for (i = 0; i < 50 * pmod; ++i)
+ __pointparticles(ef, org, randomvec() * explosionspeed, howmany / 50);
+}
+
+void SUB_RemoveOnNoImpact()
+{
+ if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+ Gib_Delete();
+}
+
+void Gib_Touch()
+{SELFPARAM();
+ // TODO maybe bounce of walls, make more gibs, etc.
+
+ if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+ {
+ Gib_Delete();
+ return;
+ }
+
+ if(!self.silent)
+ sound(self, CH_PAIN, SND_GIB_SPLAT_RANDOM(), VOL_BASE, ATTEN_NORM);
+ __pointparticles(_particleeffectnum(strcat(species_prefix(self.cnt), "blood")), self.origin + '0 0 1', '0 0 30', 10);
+
+ Gib_Delete();
+}
+
+void Gib_Draw(entity this)
+{
+ vector oldorg;
+ oldorg = self.origin;
+
+ Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
+ if(wasfreed(self))
+ return;
+
+ if(self.touch == Gib_Touch) // don't do this for the "chunk" thingie...
+ // TODO somehow make it spray in a direction dependent on self.angles
+ __trailparticles(self, _particleeffectnum(strcat(species_prefix(self.cnt), EFFECT_TR_SLIGHTBLOOD.eent_eff_name)), oldorg, self.origin);
+ else
+ __trailparticles(self, _particleeffectnum(strcat(species_prefix(self.cnt), EFFECT_TR_BLOOD.eent_eff_name)), oldorg, self.origin);
+
+ self.renderflags = 0;
+
+ // make gibs die faster at low view quality
+ // if view_quality is 0.5, we want to have them die twice as fast
+ self.nextthink -= frametime * (1 / bound(0.01, view_quality, 1.00) - 1);
+
+ self.alpha = bound(0, self.nextthink - time, 1);
+
+ if(self.alpha < ALPHA_MIN_VISIBLE)
+ {
+ self.drawmask = 0;
+ Gib_Delete();
+ }
+}
+
+void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector vrand, int specnum, bool destroyontouch, bool issilent)
+{
+ entity gib;
+
+ // TODO remove some gibs according to cl_nogibs
+ gib = RubbleNew("gib");
+ gib.move_movetype = MOVETYPE_BOUNCE;
+ gib.gravity = 1;
+ gib.solid = SOLID_CORPSE;
+ gib.cnt = specnum;
+ gib.silent = issilent;
+ Gib_setmodel(gib, mdlname, specnum);
+
+ setsize (gib, '-8 -8 -8', '8 8 8');
+
+ gib.draw = Gib_Draw;
+ if(destroyontouch)
+ gib.move_touch = Gib_Touch;
+ else
+ gib.move_touch = SUB_RemoveOnNoImpact;
+
+ // don't spawn gibs inside solid - just don't
+ if(org != safeorg)
+ {
+ tracebox(safeorg, gib.mins, gib.maxs, org, MOVE_NOMONSTERS, gib);
+ org = trace_endpos;
+ }
+
+ gib.move_origin = org;
+ setorigin(gib, org);
+ gib.move_velocity = vconst * autocvar_cl_gibs_velocity_scale + vrand * autocvar_cl_gibs_velocity_random + '0 0 1' * autocvar_cl_gibs_velocity_up;
+ gib.move_avelocity = prandomvec() * vlen(gib.move_velocity) * autocvar_cl_gibs_avelocity_scale;
+ gib.move_time = time;
+ gib.damageforcescale = autocvar_cl_gibs_damageforcescale;
+
+ gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15);
+ gib.drawmask = MASK_NORMAL;
+
+ RubbleLimit("gib", autocvar_cl_gibs_maxcount, Gib_Delete);
+}
+
+NET_HANDLE(net_gibsplash, bool isNew)
+{
+ Net_Accept(net_gibsplash);
+
+ string gentle_prefix = "morphed_";
+
+ int type = ReadByte(); // gibbage type
+ int amount = ReadByte() / 16.0; // gibbage amount
+ vector org;
+ org.x = ReadShort() * 4 + 2;
+ org.y = ReadShort() * 4 + 2;
+ org.z = ReadShort() * 4 + 2;
+ vector vel = decompressShortVector(ReadShort());
+
+ return = true;
+
+ float cl_gentle_gibs = autocvar_cl_gentle_gibs;
+ if(cl_gentle_gibs || autocvar_cl_gentle)
+ type |= 0x80; // set gentle bit
+
+ if(type & 0x80)
+ {
+ if(cl_gentle_gibs == 2)
+ gentle_prefix = "";
+ else if(cl_gentle_gibs == 3)
+ gentle_prefix = "happy_";
+ }
+ else if(autocvar_cl_particlegibs)
+ {
+ type |= 0x80;
+ gentle_prefix = "particlegibs_";
+ }
+
+ if (!(cl_gentle_gibs || autocvar_cl_gentle))
+ amount *= 1 - autocvar_cl_nogibs;
+
+ if(autocvar_ekg)
+ amount *= 5;
+
+ if(amount <= 0 || !isNew)
+ return;
+
+ setorigin(this, org); // for the sounds
+
+ int specnum = (type & 0x78) / 8; // blood/gibmodel type: using four bits (0..7, bit indexes 3,4,5)
+ bool issilent = (type & 0x40);
+ type = type & 0x87; // remove the species bits: bit 7 = gentle, bit 0,1,2 = kind of gib
+ string specstr = species_prefix(specnum);
+
+ switch(type)
+ {
+ case 0x01:
+ if(!issilent)
+ sound (this, CH_PAIN, SND_GIB, VOL_BASE, ATTEN_NORM);
+
+ if(prandom() < amount)
+ TossGib ("models/gibs/eye.md3", org, org, vel, prandomvec() * 150, specnum, 0, issilent);
+ new_te_bloodshower(_particleeffectnum(strcat(specstr, "bloodshower")), org, 1200, amount);
+ if(prandom() < amount)
+ TossGib ("models/gibs/bloodyskull.md3", org, org + 16 * prandomvec(), vel, prandomvec() * 100, specnum, 0, issilent);
+
+ for(int c = 0; c < amount; ++c)
+ {
+ int randomvalue = amount - c;
+
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent);
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent);
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/chest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent);
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/smallchest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent);
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/leg1.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent);
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/leg2.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent);
+
+ // these splat on impact
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
+ if(prandom() < randomvalue)
+ TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent);
+ }
+ break;
+ case 0x02:
+ __pointparticles(_particleeffectnum(strcat(specstr, "blood")), org, vel, amount * 16);
+ break;
+ case 0x03:
+ if(prandom() < amount)
+ TossGib ("models/gibs/chunk.mdl", org, org, vel, prandomvec() * (prandom() * 30 + 20), specnum, 1, issilent); // TODO maybe adjust to more randomization?
+ break;
+ case 0x81:
+ __pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_dissolve")), org, vel, amount);
+ break;
+ case 0x82:
+ __pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_hit")), org, vel, amount * 16);
+ break;
+ case 0x83:
+ // no gibs in gentle mode, sorry
+ break;
+ }
+ remove(this);
+}
+#endif
+
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_NET_TEMP(TE_CSQC_ARC)
+
+#if defined(SVQC)
+
+ void te_csqc_lightningarc(vector from, vector to)
+ {
+ WriteHeader(MSG_BROADCAST, TE_CSQC_ARC);
+
+ WriteCoord(MSG_BROADCAST, from.x);
+ WriteCoord(MSG_BROADCAST, from.y);
+ WriteCoord(MSG_BROADCAST, from.z);
+ WriteCoord(MSG_BROADCAST, to.x);
+ WriteCoord(MSG_BROADCAST, to.y);
+ WriteCoord(MSG_BROADCAST, to.z);
+ }
+
+#elif defined(CSQC)
+
+/*
+.vector fx_start;
+.vector fx_end;
+.float fx_with;
+.string fx_texture;
+.float fx_lifetime;
+
+void b_draw()
+{
+ //Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, 0, time * 3, '1 1 1', 0.7, DRAWFLAG_ADDITIVE, view_origin);
+ Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, (self.fx_with/256), 0, '1 1 1', 1, DRAWFLAG_ADDITIVE, view_origin);
+
+}
+void b_make(vector s,vector e, string t,float l,float z)
+{
+ entity b;
+ b = spawn();
+ b.fx_texture = t;
+ b.fx_start = s;
+ b.fx_end = e;
+ b.fx_with = z;
+ b.think = SUB_Remove;
+ b.nextthink = time + l;
+ b.draw = b_draw;
+
+ //b.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
+}
+*/
+
+ void cl_effects_lightningarc(vector from, vector to, float seglength, float drifts, float drifte,
+ float branchfactor, float branchfactor_add)
+ {
+ float length = vlen(from - to);
+ if (length < 1) return;
+
+ // Use at most 16 te_lightning1 segments, as these eat up beam list segments.
+ // TODO: Change this to R_BeginPolygon code, then we no longer have this limit.
+ int steps = min(16, floor(length / seglength));
+ if (steps < 1)
+ {
+ te_lightning1(world, from, to);
+ return;
+ }
+
+ float steplength = length / steps;
+ vector direction = normalize(to - from);
+ vector pos_l = from;
+ if (length > seglength)
+ {
+ for (int i = 1; i < steps; i += 1)
+ {
+ float drift = drifts * (1 - (i / steps)) + drifte * (i / steps);
+ vector dirnew = normalize(direction * (1 - drift) + randomvec() * drift);
+ vector pos = pos_l + dirnew * steplength;
+ te_lightning1(world, pos_l, pos);
+ // WTF endless recursion if branchfactor is 1.0 (possibly due to adding branchfactor_add). FIXME
+ // if(random() < branchfactor)
+ // cl_effects_lightningarc(pos, pos + (dirnew * length * 0.25),seglength,drifts,drifte,min(branchfactor + branchfactor_add,1),branchfactor_add);
+
+ pos_l = pos;
+ }
+ te_lightning1(world, pos_l, to);
+ }
+ else
+ {
+ te_lightning1(world, from, to);
+ }
+ }
+
+ NET_HANDLE(TE_CSQC_ARC, bool isNew)
+ {
+ vector from;
+ from.x = ReadCoord();
+ from.y = ReadCoord();
+ from.z = ReadCoord();
+ vector to;
+ to.x = ReadCoord();
+ to.y = ReadCoord();
+ to.z = ReadCoord();
+ return = true;
+
+ if (autocvar_cl_effects_lightningarc_simple)
+ {
+ te_lightning1(world, from, to);
+ }
+ else
+ {
+ float seglength = autocvar_cl_effects_lightningarc_segmentlength;
+ float drifts = autocvar_cl_effects_lightningarc_drift_start;
+ float drifte = autocvar_cl_effects_lightningarc_drift_end;
+ float branchfactor = autocvar_cl_effects_lightningarc_branchfactor_start;
+ float branchfactor_add = autocvar_cl_effects_lightningarc_branchfactor_add;
+
+ cl_effects_lightningarc(from, to, seglength, drifts, drifte, branchfactor, branchfactor_add);
+ }
+ }
+
+#endif
+
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+
+REGISTER_NET_LINKED(ENT_CLIENT_MODELEFFECT)
+
+#ifdef SVQC
+
+.float scale2;
+
+bool modeleffect_SendEntity(entity this, entity to, int sf)
+{
+ float f;
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
+
+ f = 0;
+ if(self.velocity != '0 0 0')
+ f |= 1;
+ if(self.angles != '0 0 0')
+ f |= 2;
+ if(self.avelocity != '0 0 0')
+ f |= 4;
+
+ WriteByte(MSG_ENTITY, f);
+ WriteShort(MSG_ENTITY, self.modelindex);
+ WriteByte(MSG_ENTITY, self.skin);
+ WriteByte(MSG_ENTITY, self.frame);
+ WriteCoord(MSG_ENTITY, self.origin.x);
+ WriteCoord(MSG_ENTITY, self.origin.y);
+ WriteCoord(MSG_ENTITY, self.origin.z);
+ if(f & 1)
+ {
+ WriteCoord(MSG_ENTITY, self.velocity.x);
+ WriteCoord(MSG_ENTITY, self.velocity.y);
+ WriteCoord(MSG_ENTITY, self.velocity.z);
+ }
+ if(f & 2)
+ {
+ WriteCoord(MSG_ENTITY, self.angles.x);
+ WriteCoord(MSG_ENTITY, self.angles.y);
+ WriteCoord(MSG_ENTITY, self.angles.z);
+ }
+ if(f & 4)
+ {
+ WriteCoord(MSG_ENTITY, self.avelocity.x);
+ WriteCoord(MSG_ENTITY, self.avelocity.y);
+ WriteCoord(MSG_ENTITY, self.avelocity.z);
+ }
+ WriteShort(MSG_ENTITY, self.scale * 256.0);
+ WriteShort(MSG_ENTITY, self.scale2 * 256.0);
+ WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
+ WriteByte(MSG_ENTITY, self.fade_time * 100.0);
+ WriteByte(MSG_ENTITY, self.alpha * 255.0);
+
+ return true;
+}
+
+void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2)
+{
+ entity e = new(modeleffect);
+ _setmodel(e, m);
+ e.frame = f;
+ setorigin(e, o);
+ e.velocity = v;
+ e.angles = ang;
+ e.avelocity = angv;
+ e.alpha = a;
+ e.teleport_time = t1;
+ e.fade_time = t2;
+ e.skin = s;
+ if(s0 >= 0)
+ e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
+ else
+ e.scale = -s0;
+ if(s2 >= 0)
+ e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
+ else
+ e.scale2 = -s2;
+ float sz = max(e.scale, e.scale2);
+ setsize(e, e.mins * sz, e.maxs * sz);
+ Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
+}
+
+#endif
+
+#ifdef CSQC
+
+entityclass(ModelEffect);
+class(ModelEffect) .float frame1time;
+class(ModelEffect) .float lifetime, fadetime;
+class(ModelEffect) .float teleport_time;
+class(ModelEffect) .float scale1, scale2;
+
+.float cnt;
+.float scale;
+.float alpha;
+
+void ModelEffect_Draw(entity this)
+{
+ self.angles = self.angles + frametime * self.avelocity;
+ setorigin(self, self.origin + frametime * self.velocity);
+ self.scale = self.scale1 + (self.scale2 - self.scale1) * (time - self.teleport_time) / (self.lifetime + self.fadetime - self.teleport_time);
+ self.alpha = self.cnt * bound(0, 1 - (time - self.lifetime) / self.fadetime, 1);
+ if(self.alpha < ALPHA_MIN_VISIBLE)
+ {
+ remove(self);
+ return;
+ }
+ self.drawmask = MASK_NORMAL;
+ if(self.scale <= 0)
+ {
+ self.drawmask = 0;
+ return;
+ }
+}
+
+NET_HANDLE(ENT_CLIENT_MODELEFFECT, bool isnew)
+{
+ make_pure(self);
+
+ int f = ReadByte();
+
+ entity e = new(modeleffect);
+ e.model = "from network";
+ e.modelindex = ReadShort();
+ e.skin = ReadByte();
+ e.frame = ReadByte();
+ e.frame1time = time;
+ e.origin_x = ReadCoord();
+ e.origin_y = ReadCoord();
+ e.origin_z = ReadCoord();
+ setorigin(e, e.origin);
+ if(f & 1)
+ {
+ e.velocity_x = ReadCoord();
+ e.velocity_y = ReadCoord();
+ e.velocity_z = ReadCoord();
+ }
+ if(f & 2)
+ {
+ e.angles_x = ReadAngle();
+ e.angles_y = ReadAngle();
+ e.angles_z = ReadAngle();
+ }
+ if(f & 4)
+ {
+ e.avelocity_x = ReadAngle();
+ e.avelocity_y = ReadAngle();
+ e.avelocity_z = ReadAngle();
+ }
+ e.scale1 = ReadShort() / 256.0;
+ e.scale2 = ReadShort() / 256.0;
+ e.lifetime = time + ReadByte() * 0.01;
+ e.fadetime = ReadByte() * 0.01;
+ e.teleport_time = time;
+ e.cnt = ReadByte() / 255.0; // actually alpha
+
+ e.draw = ModelEffect_Draw;
+
+ if (!isnew) remove(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then
+ return true;
+}
+#endif
+
+#endif
--- /dev/null
+#ifndef RUBBLE_H
+#define RUBBLE_H
+
+#ifdef CSQC
+
+entityclass(Rubble);
+class(Rubble).float creationtime;
+
+void RubbleLimit(string cname, float limit, void() deleteproc)
+{
+ SELFPARAM();
+ entity e;
+ entity oldest;
+ float c;
+ float oldesttime;
+
+ // remove rubble of the same type if it's at the limit
+ // remove multiple rubble if the limit has been decreased
+ while (1)
+ {
+ e = findchain(classname, cname);
+ if (e == world) break;
+ // walk the list and count the entities, find the oldest
+ // initialize our search with the first entity
+ c = 1;
+ oldest = e;
+ oldesttime = e.creationtime;
+ e = e.chain;
+ // compare to all other matching entities
+ while (e)
+ {
+ c = c + 1;
+ if (oldesttime > e.creationtime)
+ {
+ oldesttime = e.creationtime;
+ oldest = e;
+ }
+ e = e.chain;
+ }
+
+ // stop if there are less than the limit already
+ if (c <= limit) break;
+
+ // delete this oldest one and search again
+ WITH(entity, self, oldest, deleteproc());
+ }
+}
+
+entity RubbleNew(string cname)
+{
+ // spawn a new entity and return it
+ entity e = spawn();
+ e.classname = cname;
+ e.creationtime = time;
+ return e;
+}
+
+#endif
+
+#endif
-#include "gamemode/nexball/nexball.qc"
+#include "gamemode/nexball/module.inc"
+#include "gamemode/onslaught/module.inc"
--- /dev/null
+#include "nexball.qc"
+#include "weapon.qc"
GameLogEcho(s);
}
-void ball_restart(void)
+void ball_restart()
{SELFPARAM();
if(self.owner)
DropBall(self, self.owner.origin, '0 0 0');
ResetBall();
}
-void nexball_setstatus(void)
+void nexball_setstatus()
{SELFPARAM();
self.items &= ~IT_KEY1;
if(self.ballcarried)
}
}
-void relocate_nexball(void)
+void relocate_nexball()
{SELFPARAM();
tracebox(self.origin, BALL_MINS, BALL_MAXS, self.origin, true, self);
if(trace_startsolid)
}
}
-void DropOwner(void)
+void DropOwner()
{SELFPARAM();
entity ownr;
ownr = self.owner;
void GiveBall(entity plyr, entity ball)
{SELFPARAM();
- entity ownr;
-
- if((ownr = ball.owner))
+ .entity weaponentity = weaponentities[0]; // TODO: find ballstealer
+ entity ownr = ball.owner;
+ if(ownr)
{
ownr.effects &= ~autocvar_g_nexball_basketball_effects_default;
ownr.ballcarried = world;
if(ownr.metertime)
{
ownr.metertime = 0;
- ownr.weaponentity.state = WS_READY;
+ ownr.(weaponentity).state = WS_READY;
}
WaypointSprite_Kill(ownr.waypointsprite_attachedforcarrier);
}
ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold;
}
- plyr.weaponentity.weapons = plyr.weapons;
- plyr.weaponentity.switchweapon = plyr.weapon;
+ plyr.(weaponentity).weapons = plyr.weapons;
+ plyr.(weaponentity).switchweapon = plyr.weapon;
plyr.weapons = WEPSET(NEXBALL);
setself(plyr);
Weapon w = WEP_NEXBALL;
if(ball.owner.metertime)
{
ball.owner.metertime = 0;
- ball.owner.weaponentity.state = WS_READY;
+ .entity weaponentity = weaponentities[0]; // TODO: find ballstealer
+ ball.owner.(weaponentity).state = WS_READY;
}
WaypointSprite_Kill(ball.owner.waypointsprite_attachedforcarrier);
ball.owner = world;
}
-void InitBall(void)
+void InitBall()
{SELFPARAM();
if(gameover) return;
self.flags &= ~FL_ONGROUND;
LogNB("init", world);
}
-void ResetBall(void)
+void ResetBall()
{SELFPARAM();
if(self.cnt < 2) // step 1
{
}
}
-void football_touch(void)
+void football_touch()
{SELFPARAM();
if(other.solid == SOLID_BSP)
{
self.avelocity = -250 * v_forward; // maybe there is a way to make it look better?
}
-void basketball_touch(void)
+void basketball_touch()
{SELFPARAM();
if(other.ballcarried)
{
}
}
-void GoalTouch(void)
+void GoalTouch()
{SELFPARAM();
entity ball;
float isclient, pscore, otherteam;
void nb_spawnteam(string teamname, float teamcolor)
{
LOG_TRACE("^2spawned team ", teamname, "\n");
- entity e;
- e = spawn();
- e.classname = "nexball_team";
+ entity e = new(nexball_team);
e.netname = teamname;
e.cnt = teamcolor;
e.team = e.cnt + 1;
nb_teams += 1;
}
-void nb_spawnteams(void)
+void nb_spawnteams()
{
bool t_red = false, t_blue = false, t_yellow = false, t_pink = false;
entity e;
}
}
-void nb_delayedinit(void)
+void nb_delayedinit()
{
if(find(world, classname, "nexball_team") == world)
nb_spawnteams();
// spawnfuncs //
//=======================//
-void SpawnBall(void)
+void SpawnBall()
{SELFPARAM();
if(!g_nexball) { remove(self); return; }
if(!autocvar_g_nexball_sound_bounce)
self.noise = "";
else if(self.noise == "")
- self.noise = SND(NB_BOUNCE);
+ self.noise = strzone(SND(NB_BOUNCE));
//bounce sound placeholder (FIXME)
if(self.noise1 == "")
- self.noise1 = SND(NB_DROP);
+ self.noise1 = strzone(SND(NB_DROP));
//ball drop sound placeholder (FIXME)
if(self.noise2 == "")
- self.noise2 = SND(NB_STEAL);
+ self.noise2 = strzone(SND(NB_STEAL));
//stealing sound placeholder (FIXME)
if(self.noise) precache_sound(self.noise);
precache_sound(self.noise1);
return true;
}
-void SpawnGoal(void)
+void SpawnGoal()
{SELFPARAM();
if(!g_nexball) { remove(self); return; }
{
self.team = GOAL_FAULT;
if(self.noise == "")
- self.noise = SND(TYPEHIT);
+ self.noise = strzone(SND(TYPEHIT));
SpawnGoal();
}
{
self.team = GOAL_OUT;
if(self.noise == "")
- self.noise = SND(TYPEHIT);
+ self.noise = strzone(SND(TYPEHIT));
SpawnGoal();
}
self.nextthink = time;
}
-void W_Nexball_Touch(void)
+void W_Nexball_Touch()
{SELFPARAM();
entity ball, attacker;
attacker = self.owner;
vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
-void W_Nexball_Attack2(void)
+void W_Nexball_Attack2()
{SELFPARAM();
if(self.ballcarried.enemy)
{
return;
W_SetupShot(self, false, 2, SND(NB_SHOOT2), CH_WEAPON_A, 0);
- entity missile = spawn();
+ entity missile = new(ballstealer);
missile.owner = self;
- missile.classname = "ballstealer";
missile.movetype = MOVETYPE_FLY;
PROJECTILE_MAKETRIGGER(missile);
return true;
}
- METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, .entity weaponentity, int fire))
{
- if(fire1)
- if(weapon_prepareattack(thiswep, actor, false, autocvar_g_balance_nexball_primary_refire))
+ if(fire & 1)
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_balance_nexball_primary_refire))
if(autocvar_g_nexball_basketball_meter)
{
if(self.ballcarried && !self.metertime)
self.metertime = time;
else
- weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
}
else
{
W_Nexball_Attack(-1);
- weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
}
- if(fire2)
- if(weapon_prepareattack(thiswep, actor, true, autocvar_g_balance_nexball_secondary_refire))
+ if(fire & 2)
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, autocvar_g_balance_nexball_secondary_refire))
{
W_Nexball_Attack2();
- weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready);
}
- if(!fire1 && self.metertime && self.ballcarried)
+ if(!(fire & 1) && self.metertime && self.ballcarried)
{
W_Nexball_Attack(time - self.metertime);
// DropBall or stealing will set metertime back to 0
- weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
}
}
METHOD(BallStealer, wr_setup, void(BallStealer thiswep))
}
else
{
- if(self.weaponentity.weapons)
+ .entity weaponentity = weaponentities[0]; // TODO
+ if(self.(weaponentity).weapons)
{
- self.weapons = self.weaponentity.weapons;
+ self.weapons = self.(weaponentity).weapons;
Weapon w = WEP_NEXBALL;
w.wr_resetplayer(w);
- self.switchweapon = self.weaponentity.switchweapon;
+ self.switchweapon = self.(weaponentity).switchweapon;
W_SwitchWeapon(self.switchweapon);
- self.weaponentity.weapons = '0 0 0';
+ self.(weaponentity).weapons = '0 0 0';
}
}
{
SELFPARAM();
this.metertime = 0;
- this.weaponentity.weapons = '0 0 0';
+ .entity weaponentity = weaponentities[0];
+ this.(weaponentity).weapons = '0 0 0';
if (nexball_mode & NBM_BASKETBALL)
this.weapons |= WEPSET(NEXBALL);
REGISTER_MUTATOR(nb, g_nexball)
{
- ActivateTeamplay();
- SetLimits(autocvar_g_nexball_goallimit, autocvar_g_nexball_goalleadlimit, -1, -1);
- have_team_spawns = -1; // request team spawns
-
MUTATOR_ONADD
{
g_nexball_meter_period = autocvar_g_nexball_meter_period;
InitializeEntity(world, nb_delayedinit, INITPRIO_GAMETYPE);
WEP_NEXBALL.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+
+ ActivateTeamplay();
+ SetLimits(autocvar_g_nexball_goallimit, autocvar_g_nexball_goalleadlimit, -1, -1);
+ have_team_spawns = -1; // request team spawns
}
MUTATOR_ONROLLBACK_OR_REMOVE
#ifndef GAMEMODE_NEXBALL_H
#define GAMEMODE_NEXBALL_H
-#include "weapon.qc"
-
#ifdef SVQC
//EF_BRIGHTFIELD|EF_BRIGHTLIGHT|EF_DIMLIGHT|EF_BLUE|EF_RED|EF_FLAME
const float BALL_EFFECTMASK = 1229;
/* flags */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(BallStealer, impulse, int, 0);
/* refname */ ATTRIB(BallStealer, netname, string, "ballstealer");
-/* wepname */ ATTRIB(BallStealer, message, string, _("Ball Stealer"));
+/* wepname */ ATTRIB(BallStealer, m_name, string, _("Ball Stealer"));
ENDCLASS(BallStealer)
REGISTER_WEAPON(NEXBALL, NEW(BallStealer));
--- /dev/null
+#include "cl_controlpoint.qh"
+
+.vector colormod;
+.float alpha;
+.int count;
+.float pain_finished;
+
+.bool iscaptured;
+
+.vector cp_origin, cp_bob_origin;
+.float cp_bob_spd;
+
+.vector cp_bob_dmg;
+
+.vector punchangle;
+
+.float max_health;
+
+.entity icon_realmodel;
+
+void cpicon_draw(entity this)
+{
+ if(time < this.move_time) { return; }
+
+ if(this.cp_bob_dmg_z > 0)
+ this.cp_bob_dmg_z = this.cp_bob_dmg_z - 3 * frametime;
+ else
+ this.cp_bob_dmg_z = 0;
+ this.cp_bob_origin_z = 4 * PI * (1 - cos(this.cp_bob_spd));
+ this.cp_bob_spd = this.cp_bob_spd + 1.875 * frametime;
+ this.colormod = '1 1 1' * (2 - bound(0, (this.pain_finished - time) / 10, 1));
+
+ if(!this.iscaptured) this.alpha = this.health / this.max_health;
+
+ if(this.iscaptured)
+ {
+ if (this.punchangle_x > 0)
+ {
+ this.punchangle_x = this.punchangle_x - 60 * frametime;
+ if (this.punchangle_x < 0)
+ this.punchangle_x = 0;
+ }
+ else if (this.punchangle_x < 0)
+ {
+ this.punchangle_x = this.punchangle_x + 60 * frametime;
+ if (this.punchangle_x > 0)
+ this.punchangle_x = 0;
+ }
+
+ if (this.punchangle_y > 0)
+ {
+ this.punchangle_y = this.punchangle_y - 60 * frametime;
+ if (this.punchangle_y < 0)
+ this.punchangle_y = 0;
+ }
+ else if (this.punchangle_y < 0)
+ {
+ this.punchangle_y = this.punchangle_y + 60 * frametime;
+ if (this.punchangle_y > 0)
+ this.punchangle_y = 0;
+ }
+
+ if (this.punchangle_z > 0)
+ {
+ this.punchangle_z = this.punchangle_z - 60 * frametime;
+ if (this.punchangle_z < 0)
+ this.punchangle_z = 0;
+ }
+ else if (this.punchangle_z < 0)
+ {
+ this.punchangle_z = this.punchangle_z + 60 * frametime;
+ if (this.punchangle_z > 0)
+ this.punchangle_z = 0;
+ }
+
+ this.angles_x = this.punchangle_x;
+ this.angles_y = this.punchangle_y + this.move_angles_y;
+ this.angles_z = this.punchangle_z;
+ this.move_angles_y = this.move_angles_y + 45 * frametime;
+ }
+
+ setorigin(this, this.cp_origin + this.cp_bob_origin + this.cp_bob_dmg);
+}
+
+void cpicon_damage(entity this, float hp)
+{
+ if(!this.iscaptured) { return; }
+
+ if(hp < this.max_health * 0.25)
+ setmodel(this, MDL_ONS_CP3);
+ else if(hp < this.max_health * 0.50)
+ setmodel(this, MDL_ONS_CP2);
+ else if(hp < this.max_health * 0.75)
+ setmodel(this, MDL_ONS_CP1);
+ else if(hp <= this.max_health || hp >= this.max_health)
+ setmodel(this, MDL_ONS_CP);
+
+ this.punchangle = (2 * randomvec() - '1 1 1') * 45;
+
+ this.cp_bob_dmg_z = (2 * random() - 1) * 15;
+ this.pain_finished = time + 1;
+ this.colormod = '2 2 2';
+
+ setsize(this, CPICON_MIN, CPICON_MAX);
+}
+
+void cpicon_construct(entity this)
+{
+ this.netname = "Control Point Icon";
+
+ setmodel(this, MDL_ONS_CP);
+ setsize(this, CPICON_MIN, CPICON_MAX);
+
+ if(this.icon_realmodel == world)
+ {
+ this.icon_realmodel = spawn();
+ setmodel(this.icon_realmodel, MDL_Null);
+ setorigin(this.icon_realmodel, this.origin);
+ setsize(this.icon_realmodel, CPICON_MIN, CPICON_MAX);
+ this.icon_realmodel.movetype = MOVETYPE_NOCLIP;
+ this.icon_realmodel.solid = SOLID_NOT;
+ this.icon_realmodel.move_origin = this.icon_realmodel.origin;
+ }
+
+ if(this.iscaptured) { this.icon_realmodel.solid = SOLID_BBOX; }
+
+ this.move_movetype = MOVETYPE_NOCLIP;
+ this.solid = SOLID_NOT;
+ this.movetype = MOVETYPE_NOCLIP;
+ this.move_origin = this.origin;
+ this.move_time = time;
+ this.drawmask = MASK_NORMAL;
+ this.alpha = 1;
+ this.draw = cpicon_draw;
+ this.cp_origin = this.origin;
+ this.cp_bob_origin = '0 0 0.1';
+ this.cp_bob_spd = 0;
+}
+
+.vector glowmod;
+void cpicon_changeteam(entity this)
+{
+ if(this.team)
+ {
+ this.glowmod = Team_ColorRGB(this.team - 1);
+ this.teamradar_color = Team_ColorRGB(this.team - 1);
+ this.colormap = 1024 + (this.team - 1) * 17;
+ }
+ else
+ {
+ this.colormap = 1024;
+ this.glowmod = '1 1 0';
+ this.teamradar_color = '1 1 0';
+ }
+}
+
+NET_HANDLE(ENT_CLIENT_CONTROLPOINT_ICON, bool isnew)
+{
+ return = true;
+ int sf = ReadByte();
+
+ if(sf & CPSF_SETUP)
+ {
+ this.origin_x = ReadCoord();
+ this.origin_y = ReadCoord();
+ this.origin_z = ReadCoord();
+ setorigin(this, this.origin);
+
+ this.health = ReadByte();
+ this.max_health = ReadByte();
+ this.count = ReadByte();
+ this.team = ReadByte();
+ this.iscaptured = ReadByte();
+
+ if(!this.count)
+ this.count = (this.health - this.max_health) * frametime;
+
+ cpicon_changeteam(this);
+ cpicon_construct(this);
+ }
+
+ if(sf & CPSF_STATUS)
+ {
+ int _tmp = ReadByte();
+ if(_tmp != this.team)
+ {
+ this.team = _tmp;
+ cpicon_changeteam(this);
+ }
+
+ _tmp = ReadByte();
+
+ if(_tmp != this.health)
+ cpicon_damage(this, _tmp);
+
+ this.health = _tmp;
+ }
+}
--- /dev/null
+#ifndef CLIENT_CONTROLPOINT_H
+#define CLIENT_CONTROLPOINT_H
+
+const vector CPICON_MIN = '-32 -32 -9';
+const vector CPICON_MAX = '32 32 25';
+
+const int CPSF_STATUS = 4;
+const int CPSF_SETUP = 8;
+
+#endif
--- /dev/null
+#include "cl_generator.qh"
+
+.float alpha;
+.float scale;
+.int count;
+.float max_health;
+
+void ons_generator_ray_draw(entity this)
+{
+ if(time < self.move_time)
+ return;
+
+ self.move_time = time + 0.05;
+
+ if(self.count > 10)
+ {
+ remove(self);
+ return;
+ }
+
+ if(self.count > 5)
+ self.alpha -= 0.1;
+ else
+ self.alpha += 0.1;
+
+ self.scale += 0.2;
+ self.count +=1;
+}
+
+void ons_generator_ray_spawn(vector org)
+{
+ entity e = new(ons_ray);
+ setmodel(e, MDL_ONS_RAY);
+ setorigin(e, org);
+ e.angles = randomvec() * 360;
+ e.move_origin = org;
+ e.movetype = MOVETYPE_NONE;
+ e.alpha = 0;
+ e.scale = random() * 5 + 8;
+ e.move_time = time + 0.05;
+ e.drawmask = MASK_NORMAL;
+ e.draw = ons_generator_ray_draw;
+}
+
+void generator_draw(entity this)
+{
+ if(time < self.move_time)
+ return;
+
+ if(self.health > 0)
+ {
+ // damaged fx (less probable the more damaged is the generator)
+ if(random() < 0.9 - self.health / self.max_health)
+ if(random() < 0.01)
+ {
+ pointparticles(EFFECT_ELECTRO_BALLEXPLODE, self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);
+ sound(self, CH_TRIGGER, SND_ONS_ELECTRICITY_EXPLODE, VOL_BASE, ATTEN_NORM);
+ }
+ else
+ pointparticles(EFFECT_ONS_GENERATOR_DAMAGED, self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);
+
+ self.move_time = time + 0.1;
+
+ return;
+ }
+
+ if(self.count <= 0)
+ return;
+
+ vector org;
+ int i;
+
+ // White shockwave
+ if(self.count==40||self.count==20)
+ {
+ sound(self, CH_TRIGGER, SND_ONS_SHOCKWAVE, VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_ELECTRO_COMBO, self.origin, '0 0 0', 6);
+ }
+
+ // rays
+ if(random() > 0.25)
+ {
+ ons_generator_ray_spawn(self.origin);
+ }
+
+ // Spawn fire balls
+ for(i=0;i < 10;++i)
+ {
+ org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');
+ pointparticles(EFFECT_ONS_GENERATOR_GIB, org, '0 0 0', 1);
+ }
+
+ // Short explosion sound + small explosion
+ if(random() < 0.25)
+ {
+ te_explosion(self.origin);
+ sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
+ }
+
+ // Particles
+ org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');
+ pointparticles(EFFECT_ONS_GENERATOR_EXPLODE, org, '0 0 0', 1);
+
+ // Final explosion
+ if(self.count==1)
+ {
+ org = self.origin;
+ te_explosion(org);
+ pointparticles(EFFECT_ONS_GENERATOR_EXPLODE2, org, '0 0 0', 1);
+ sound(self, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+ }
+
+ self.move_time = time + 0.05;
+
+ self.count -= 1;
+}
+
+void generator_damage(float hp)
+{SELFPARAM();
+ if(hp <= 0)
+ setmodel(self, MDL_ONS_GEN_DEAD);
+ else if(hp < self.max_health * 0.10)
+ setmodel(self, MDL_ONS_GEN9);
+ else if(hp < self.max_health * 0.20)
+ setmodel(self, MDL_ONS_GEN8);
+ else if(hp < self.max_health * 0.30)
+ setmodel(self, MDL_ONS_GEN7);
+ else if(hp < self.max_health * 0.40)
+ setmodel(self, MDL_ONS_GEN6);
+ else if(hp < self.max_health * 0.50)
+ setmodel(self, MDL_ONS_GEN5);
+ else if(hp < self.max_health * 0.60)
+ setmodel(self, MDL_ONS_GEN4);
+ else if(hp < self.max_health * 0.70)
+ setmodel(self, MDL_ONS_GEN3);
+ else if(hp < self.max_health * 0.80)
+ setmodel(self, MDL_ONS_GEN2);
+ else if(hp < self.max_health * 0.90)
+ setmodel(self, MDL_ONS_GEN1);
+ else if(hp <= self.max_health || hp >= self.max_health)
+ setmodel(self, MDL_ONS_GEN);
+
+ setsize(self, GENERATOR_MIN, GENERATOR_MAX);
+}
+
+void generator_construct()
+{SELFPARAM();
+ self.netname = "Generator";
+ self.classname = "onslaught_generator";
+
+ setorigin(self, self.origin);
+ setmodel(self, MDL_ONS_GEN);
+ setsize(self, GENERATOR_MIN, GENERATOR_MAX);
+
+ self.move_movetype = MOVETYPE_NOCLIP;
+ self.solid = SOLID_BBOX;
+ self.movetype = MOVETYPE_NOCLIP;
+ self.move_origin = self.origin;
+ self.move_time = time;
+ self.drawmask = MASK_NORMAL;
+ self.alpha = 1;
+ self.draw = generator_draw;
+}
+
+.vector glowmod;
+void generator_changeteam()
+{SELFPARAM();
+ if(self.team)
+ {
+ self.glowmod = Team_ColorRGB(self.team - 1);
+ self.teamradar_color = Team_ColorRGB(self.team - 1);
+ self.colormap = 1024 + (self.team - 1) * 17;
+ }
+ else
+ {
+ self.colormap = 1024;
+ self.glowmod = '1 1 0';
+ self.teamradar_color = '1 1 0';
+ }
+}
+
+NET_HANDLE(ENT_CLIENT_GENERATOR, bool isnew)
+{
+ return = true;
+ int sf = ReadByte();
+
+ if(sf & GSF_SETUP)
+ {
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ setorigin(self, self.origin);
+
+ self.health = ReadByte();
+ self.max_health = ReadByte();
+ self.count = ReadByte();
+ self.team = ReadByte();
+
+ if(!self.count)
+ self.count = 40;
+
+ generator_changeteam();
+ generator_construct();
+ }
+
+ if(sf & GSF_STATUS)
+ {
+ int _tmp;
+ _tmp = ReadByte();
+ if(_tmp != self.team)
+ {
+ self.team = _tmp;
+ generator_changeteam();
+ }
+
+ _tmp = ReadByte();
+
+ if(_tmp != self.health)
+ generator_damage(_tmp);
+
+ self.health = _tmp;
+ }
+}
--- /dev/null
+#ifndef CLIENT_GENERATOR_H
+#define CLIENT_GENERATOR_H
+const vector GENERATOR_MIN = '-52 -52 -14';
+const vector GENERATOR_MAX = '52 52 75';
+
+const int GSF_STATUS = 4;
+const int GSF_SETUP = 8;
+
+#endif
--- /dev/null
+#ifndef ONS_CONSTANTS
+ #define ONS_CONSTANTS
+ REGISTER_NET_LINKED(ENT_CLIENT_GENERATOR)
+ REGISTER_NET_LINKED(ENT_CLIENT_CONTROLPOINT_ICON)
+#endif
+
+#if defined(SVQC)
+ #include "onslaught.qc"
+ #ifndef IMPLEMENTATION
+ #include "sv_controlpoint.qh"
+ #include "sv_generator.qh"
+ #else
+ #include "sv_controlpoint.qc"
+ #include "sv_generator.qc"
+ #endif
+#elif defined(CSQC)
+ #ifndef IMPLEMENTATION
+ #include "cl_controlpoint.qh"
+ #include "cl_generator.qh"
+ #else
+ #include "cl_controlpoint.qc"
+ #include "cl_generator.qc"
+ #endif
+#endif
--- /dev/null
+#ifndef GAMEMODE_ONSLAUGHT_H
+#define GAMEMODE_ONSLAUGHT_H
+
+float autocvar_g_onslaught_point_limit;
+void ons_Initialize();
+
+REGISTER_MUTATOR(ons, false)
+{
+ MUTATOR_ONADD
+ {
+ if (time > 1) // game loads at time 1
+ error("This is a game type and it cannot be added at runtime.");
+ ons_Initialize();
+
+ ActivateTeamplay();
+ SetLimits(autocvar_g_onslaught_point_limit, -1, -1, -1);
+ have_team_spawns = -1; // request team spawns
+ }
+
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ // we actually cannot roll back ons_Initialize here
+ // BUT: we don't need to! If this gets called, adding always
+ // succeeds.
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ LOG_INFO("This is a game type and it cannot be removed at runtime.");
+ return -1;
+ }
+
+ return false;
+}
+
+#ifdef SVQC
+
+.entity ons_toucher; // player who touched the control point
+
+// control point / generator constants
+const float ONS_CP_THINKRATE = 0.2;
+const float GEN_THINKRATE = 1;
+#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13))
+const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128');
+const vector CPICON_OFFSET = ('0 0 96');
+
+// list of generators on the map
+entity ons_worldgeneratorlist;
+.entity ons_worldgeneratornext;
+.entity ons_stalegeneratornext;
+
+// list of control points on the map
+entity ons_worldcplist;
+.entity ons_worldcpnext;
+.entity ons_stalecpnext;
+
+// list of links on the map
+entity ons_worldlinklist;
+.entity ons_worldlinknext;
+.entity ons_stalelinknext;
+
+// definitions
+.entity sprite;
+.string target2;
+.int iscaptured;
+.int islinked;
+.int isshielded;
+.float lasthealth;
+.int lastteam;
+.int lastshielded;
+.int lastcaptured;
+
+.bool waslinked;
+
+bool ons_stalemate;
+
+.float teleport_antispam;
+
+.bool ons_roundlost;
+
+// waypoint sprites
+.entity bot_basewaypoint; // generator waypointsprite
+
+.bool isgenneighbor[17];
+.bool iscpneighbor[17];
+float ons_notification_time[17];
+
+.float ons_overtime_damagedelay;
+
+.vector ons_deathloc;
+
+.entity ons_spawn_by;
+
+// declarations for functions used outside gamemode_onslaught.qc
+void ons_Generator_UpdateSprite(entity e);
+void ons_ControlPoint_UpdateSprite(entity e);
+bool ons_ControlPoint_Attackable(entity cp, int teamnumber);
+
+// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet
+float ons_captureshield_force; // push force of the shield
+
+// bot player logic
+const int HAVOCBOT_ONS_ROLE_NONE = 0;
+const int HAVOCBOT_ONS_ROLE_DEFENSE = 2;
+const int HAVOCBOT_ONS_ROLE_ASSISTANT = 4;
+const int HAVOCBOT_ONS_ROLE_OFFENSE = 8;
+
+.entity havocbot_ons_target;
+
+.int havocbot_role_flags;
+.float havocbot_attack_time;
+
+void havocbot_role_ons_defense();
+void havocbot_role_ons_offense();
+void havocbot_role_ons_assistant();
+
+void havocbot_ons_reset_role(entity bot);
+void havocbot_goalrating_items(float ratingscale, vector org, float sradius);
+void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius);
+
+// score rule declarations
+const int ST_ONS_CAPS = 1;
+const int SP_ONS_CAPS = 4;
+const int SP_ONS_TAKES = 6;
+
+#endif
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "sv_controlpoint.qh"
+#include "sv_generator.qh"
+
+bool g_onslaught;
+
+float autocvar_g_onslaught_debug;
+float autocvar_g_onslaught_teleport_wait;
+bool autocvar_g_onslaught_spawn_at_controlpoints;
+bool autocvar_g_onslaught_spawn_at_generator;
+float autocvar_g_onslaught_cp_proxydecap;
+float autocvar_g_onslaught_cp_proxydecap_distance = 512;
+float autocvar_g_onslaught_cp_proxydecap_dps = 100;
+float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5;
+float autocvar_g_onslaught_spawn_at_controlpoints_random;
+float autocvar_g_onslaught_spawn_at_generator_chance;
+float autocvar_g_onslaught_spawn_at_generator_random;
+float autocvar_g_onslaught_cp_buildhealth;
+float autocvar_g_onslaught_cp_buildtime;
+float autocvar_g_onslaught_cp_health;
+float autocvar_g_onslaught_cp_regen;
+float autocvar_g_onslaught_gen_health;
+float autocvar_g_onslaught_shield_force = 100;
+float autocvar_g_onslaught_allow_vehicle_touch;
+float autocvar_g_onslaught_round_timelimit;
+float autocvar_g_onslaught_warmup;
+float autocvar_g_onslaught_teleport_radius;
+float autocvar_g_onslaught_spawn_choose;
+float autocvar_g_onslaught_click_radius;
+
+void FixSize(entity e);
+
+// =======================
+// CaptureShield Functions
+// =======================
+
+bool ons_CaptureShield_Customize()
+{SELFPARAM();
+ entity e = WaypointSprite_getviewentity(other);
+
+ if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, e.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return false; }
+ if(SAME_TEAM(self, e)) { return false; }
+
+ return true;
+}
+
+void ons_CaptureShield_Touch()
+{SELFPARAM();
+ if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, other.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return; }
+ if(!IS_PLAYER(other)) { return; }
+ if(SAME_TEAM(other, self)) { return; }
+
+ vector mymid = (self.absmin + self.absmax) * 0.5;
+ vector othermid = (other.absmin + other.absmax) * 0.5;
+
+ Damage(other, self, self, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(othermid - mymid) * ons_captureshield_force);
+
+ if(IS_REAL_CLIENT(other))
+ {
+ play2(other, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
+
+ if(self.enemy.classname == "onslaught_generator")
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED);
+ else
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED);
+ }
+}
+
+void ons_CaptureShield_Reset()
+{SELFPARAM();
+ self.colormap = self.enemy.colormap;
+ self.team = self.enemy.team;
+}
+
+void ons_CaptureShield_Spawn(entity generator, bool is_generator)
+{
+ entity shield = new(ons_captureshield);
+
+ shield.enemy = generator;
+ shield.team = generator.team;
+ shield.colormap = generator.colormap;
+ shield.reset = ons_CaptureShield_Reset;
+ shield.touch = ons_CaptureShield_Touch;
+ shield.customizeentityforclient = ons_CaptureShield_Customize;
+ shield.effects = EF_ADDITIVE;
+ shield.movetype = MOVETYPE_NOCLIP;
+ shield.solid = SOLID_TRIGGER;
+ shield.avelocity = '7 0 11';
+ shield.scale = 1;
+ shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3");
+
+ precache_model(shield.model);
+ setorigin(shield, generator.origin);
+ _setmodel(shield, shield.model);
+ setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
+}
+
+
+// ==========
+// Junk Pile
+// ==========
+
+void ons_debug(string input)
+{
+ switch(autocvar_g_onslaught_debug)
+ {
+ case 1: LOG_TRACE(input); break;
+ case 2: LOG_INFO(input); break;
+ }
+}
+
+void setmodel_fixsize(entity e, Model m)
+{
+ setmodel(e, m);
+ FixSize(e);
+}
+
+void onslaught_updatelinks()
+{
+ entity l;
+ // first check if the game has ended
+ ons_debug("--- updatelinks ---\n");
+ // mark generators as being shielded and networked
+ for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
+ {
+ if (l.iscaptured)
+ ons_debug(strcat(etos(l), " (generator) belongs to team ", ftos(l.team), "\n"));
+ else
+ ons_debug(strcat(etos(l), " (generator) is destroyed\n"));
+ l.islinked = l.iscaptured;
+ l.isshielded = l.iscaptured;
+ l.sprite.SendFlags |= 16;
+ }
+ // mark points as shielded and not networked
+ for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+ {
+ l.islinked = false;
+ l.isshielded = true;
+ int i;
+ for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; }
+ ons_debug(strcat(etos(l), " (point) belongs to team ", ftos(l.team), "\n"));
+ l.sprite.SendFlags |= 16;
+ }
+ // flow power outward from the generators through the network
+ bool stop = false;
+ while (!stop)
+ {
+ stop = true;
+ for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
+ {
+ // if both points are captured by the same team, and only one of
+ // them is powered, mark the other one as powered as well
+ if (l.enemy.iscaptured && l.goalentity.iscaptured)
+ if (l.enemy.islinked != l.goalentity.islinked)
+ if(SAME_TEAM(l.enemy, l.goalentity))
+ {
+ if (!l.goalentity.islinked)
+ {
+ stop = false;
+ l.goalentity.islinked = true;
+ ons_debug(strcat(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n"));
+ }
+ else if (!l.enemy.islinked)
+ {
+ stop = false;
+ l.enemy.islinked = true;
+ ons_debug(strcat(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n"));
+ }
+ }
+ }
+ }
+ // now that we know which points are powered we can mark their neighbors
+ // as unshielded if team differs
+ for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
+ {
+ if (l.goalentity.islinked)
+ {
+ if(DIFF_TEAM(l.goalentity, l.enemy))
+ {
+ ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n"));
+ l.enemy.isshielded = false;
+ }
+ if(l.goalentity.classname == "onslaught_generator")
+ l.enemy.isgenneighbor[l.goalentity.team] = true;
+ else
+ l.enemy.iscpneighbor[l.goalentity.team] = true;
+ }
+ if (l.enemy.islinked)
+ {
+ if(DIFF_TEAM(l.goalentity, l.enemy))
+ {
+ ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n"));
+ l.goalentity.isshielded = false;
+ }
+ if(l.enemy.classname == "onslaught_generator")
+ l.goalentity.isgenneighbor[l.enemy.team] = true;
+ else
+ l.goalentity.iscpneighbor[l.enemy.team] = true;
+ }
+ }
+ // now update the generators
+ for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
+ {
+ if (l.isshielded)
+ {
+ ons_debug(strcat(etos(l), " (generator) is shielded\n"));
+ l.takedamage = DAMAGE_NO;
+ l.bot_attack = false;
+ }
+ else
+ {
+ ons_debug(strcat(etos(l), " (generator) is not shielded\n"));
+ l.takedamage = DAMAGE_AIM;
+ l.bot_attack = true;
+ }
+
+ ons_Generator_UpdateSprite(l);
+ }
+ // now update the takedamage and alpha variables on control point icons
+ for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+ {
+ if (l.isshielded)
+ {
+ ons_debug(strcat(etos(l), " (point) is shielded\n"));
+ if (l.goalentity)
+ {
+ l.goalentity.takedamage = DAMAGE_NO;
+ l.goalentity.bot_attack = false;
+ }
+ }
+ else
+ {
+ ons_debug(strcat(etos(l), " (point) is not shielded\n"));
+ if (l.goalentity)
+ {
+ l.goalentity.takedamage = DAMAGE_AIM;
+ l.goalentity.bot_attack = true;
+ }
+ }
+ ons_ControlPoint_UpdateSprite(l);
+ }
+ l = findchain(classname, "ons_captureshield");
+ while(l)
+ {
+ l.team = l.enemy.team;
+ l.colormap = l.enemy.colormap;
+ l = l.chain;
+ }
+}
+
+
+// ===================
+// Main Link Functions
+// ===================
+
+bool ons_Link_Send(entity this, entity to, int sendflags)
+{
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_RADARLINK);
+ WriteByte(MSG_ENTITY, sendflags);
+ if(sendflags & 1)
+ {
+ WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
+ WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
+ WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
+ }
+ if(sendflags & 2)
+ {
+ WriteCoord(MSG_ENTITY, self.enemy.origin_x);
+ WriteCoord(MSG_ENTITY, self.enemy.origin_y);
+ WriteCoord(MSG_ENTITY, self.enemy.origin_z);
+ }
+ if(sendflags & 4)
+ {
+ WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
+ }
+ return true;
+}
+
+void ons_Link_CheckUpdate()
+{SELFPARAM();
+ // TODO check if the two sides have moved (currently they won't move anyway)
+ float cc = 0, cc1 = 0, cc2 = 0;
+
+ if(self.goalentity.islinked || self.goalentity.iscaptured) { cc1 = (self.goalentity.team - 1) * 0x01; }
+ if(self.enemy.islinked || self.enemy.iscaptured) { cc2 = (self.enemy.team - 1) * 0x10; }
+
+ cc = cc1 + cc2;
+
+ if(cc != self.clientcolors)
+ {
+ self.clientcolors = cc;
+ self.SendFlags |= 4;
+ }
+
+ self.nextthink = time;
+}
+
+void ons_DelayedLinkSetup()
+{SELFPARAM();
+ self.goalentity = find(world, targetname, self.target);
+ self.enemy = find(world, targetname, self.target2);
+ if(!self.goalentity) { objerror("can not find target\n"); }
+ if(!self.enemy) { objerror("can not find target2\n"); }
+
+ ons_debug(strcat(etos(self.goalentity), " linked with ", etos(self.enemy), "\n"));
+ self.SendFlags |= 3;
+ self.think = ons_Link_CheckUpdate;
+ self.nextthink = time;
+}
+
+
+// =============================
+// Main Control Point Functions
+// =============================
+
+int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber)
+{
+ if(cp.isgenneighbor[teamnumber]) { return 2; }
+ if(cp.iscpneighbor[teamnumber]) { return 1; }
+
+ return 0;
+}
+
+int ons_ControlPoint_Attackable(entity cp, int teamnumber)
+ // -2: SAME TEAM, attackable by enemy!
+ // -1: SAME TEAM!
+ // 0: off limits
+ // 1: attack it
+ // 2: touch it
+ // 3: attack it (HIGH PRIO)
+ // 4: touch it (HIGH PRIO)
+{
+ int a;
+
+ if(cp.isshielded)
+ {
+ return 0;
+ }
+ else if(cp.goalentity)
+ {
+ // if there's already an icon built, nothing happens
+ if(cp.team == teamnumber)
+ {
+ a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
+ if(a) // attackable by enemy?
+ return -2; // EMERGENCY!
+ return -1;
+ }
+ // we know it can be linked, so no need to check
+ // but...
+ a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
+ if(a == 2) // near our generator?
+ return 3; // EMERGENCY!
+ return 1;
+ }
+ else
+ {
+ // free point
+ if(ons_ControlPoint_CanBeLinked(cp, teamnumber))
+ {
+ a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t
+ if(a == 2)
+ return 4; // GET THIS ONE NOW!
+ else
+ return 2; // TOUCH ME
+ }
+ }
+ return 0;
+}
+
+void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{SELFPARAM();
+ if(damage <= 0) { return; }
+
+ if (self.owner.isshielded)
+ {
+ // this is protected by a shield, so ignore the damage
+ if (time > self.pain_finished)
+ if (IS_PLAYER(attacker))
+ {
+ play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
+ self.pain_finished = time + 1;
+ attacker.typehitsound += 1; // play both sounds (shield is way too quiet)
+ }
+
+ return;
+ }
+
+ if(IS_PLAYER(attacker))
+ if(time - ons_notification_time[self.team] > 10)
+ {
+ play2team(self.team, SND(ONS_CONTROLPOINT_UNDERATTACK));
+ ons_notification_time[self.team] = time;
+ }
+
+ self.health = self.health - damage;
+ if(self.owner.iscaptured)
+ WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+ else
+ WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / ONS_CP_THINKRATE));
+ self.pain_finished = time + 1;
+ // particles on every hit
+ pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1);
+ //sound on every hit
+ if (random() < 0.5)
+ sound(self, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM);
+ else
+ sound(self, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM);
+
+ if (self.health < 0)
+ {
+ sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
+ pointparticles(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_CPDESTROYED_), self.owner.message, attacker.netname);
+
+ PlayerScore_Add(attacker, SP_ONS_TAKES, 1);
+ PlayerScore_Add(attacker, SP_SCORE, 10);
+
+ self.owner.goalentity = world;
+ self.owner.islinked = false;
+ self.owner.iscaptured = false;
+ self.owner.team = 0;
+ self.owner.colormap = 1024;
+
+ WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
+
+ onslaught_updatelinks();
+
+ // Use targets now (somebody make sure this is in the right place..)
+ setself(self.owner);
+ activator = self;
+ SUB_UseTargets ();
+ setself(this);
+
+ self.owner.waslinked = self.owner.islinked;
+ if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
+ setmodel_fixsize(self.owner, MDL_ONS_CP_PAD1);
+ //setsize(self, '-32 -32 0', '32 32 8');
+
+ remove(self);
+ }
+
+ self.SendFlags |= CPSF_STATUS;
+}
+
+void ons_ControlPoint_Icon_Think()
+{SELFPARAM();
+ self.nextthink = time + ONS_CP_THINKRATE;
+
+ if(autocvar_g_onslaught_cp_proxydecap)
+ {
+ int _enemy_count = 0;
+ int _friendly_count = 0;
+ float _dist;
+ entity _player;
+
+ FOR_EACH_PLAYER(_player)
+ {
+ if(!_player.deadflag)
+ {
+ _dist = vlen(_player.origin - self.origin);
+ if(_dist < autocvar_g_onslaught_cp_proxydecap_distance)
+ {
+ if(SAME_TEAM(_player, self))
+ ++_friendly_count;
+ else
+ ++_enemy_count;
+ }
+ }
+ }
+
+ _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
+ _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
+
+ self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health);
+ self.SendFlags |= CPSF_STATUS;
+ if(self.health <= 0)
+ {
+ ons_ControlPoint_Icon_Damage(self, self, 1, 0, self.origin, '0 0 0');
+ return;
+ }
+ }
+
+ if (time > self.pain_finished + 5)
+ {
+ if(self.health < self.max_health)
+ {
+ self.health = self.health + self.count;
+ if (self.health >= self.max_health)
+ self.health = self.max_health;
+ WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+ }
+ }
+
+ if(self.owner.islinked != self.owner.waslinked)
+ {
+ // unteam the spawnpoint if needed
+ int t = self.owner.team;
+ if(!self.owner.islinked)
+ self.owner.team = 0;
+
+ setself(self.owner);
+ activator = self;
+ SUB_UseTargets ();
+ setself(this);
+
+ self.owner.team = t;
+
+ self.owner.waslinked = self.owner.islinked;
+ }
+
+ // damaged fx
+ if(random() < 0.6 - self.health / self.max_health)
+ {
+ Send_Effect(EFFECT_ELECTRIC_SPARKS, self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
+
+ if(random() > 0.8)
+ sound(self, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM);
+ else if (random() > 0.5)
+ sound(self, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM);
+ }
+}
+
+void ons_ControlPoint_Icon_BuildThink()
+{SELFPARAM();
+ int a;
+
+ self.nextthink = time + ONS_CP_THINKRATE;
+
+ // only do this if there is power
+ a = ons_ControlPoint_CanBeLinked(self.owner, self.owner.team);
+ if(!a)
+ return;
+
+ self.health = self.health + self.count;
+
+ self.SendFlags |= CPSF_STATUS;
+
+ if (self.health >= self.max_health)
+ {
+ self.health = self.max_health;
+ self.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on
+ self.think = ons_ControlPoint_Icon_Think;
+ sound(self, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM);
+ self.owner.iscaptured = true;
+ self.solid = SOLID_BBOX;
+
+ Send_Effect(EFFECT_CAP(self.owner.team), self.owner.origin, '0 0 0', 1);
+
+ WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
+ WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+
+ if(IS_PLAYER(self.owner.ons_toucher))
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, self.owner.ons_toucher.netname, self.owner.message);
+ Send_Notification(NOTIF_ALL_EXCEPT, self.owner.ons_toucher, MSG_CENTER, APP_TEAM_ENT_4(self.owner.ons_toucher, CENTER_ONS_CAPTURE_), self.owner.message);
+ Send_Notification(NOTIF_ONE, self.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, self.owner.message);
+ PlayerScore_Add(self.owner.ons_toucher, SP_ONS_CAPS, 1);
+ PlayerTeamScore_AddScore(self.owner.ons_toucher, 10);
+ }
+
+ self.owner.ons_toucher = world;
+
+ onslaught_updatelinks();
+
+ // Use targets now (somebody make sure this is in the right place..)
+ setself(self.owner);
+ activator = self;
+ SUB_UseTargets ();
+ setself(this);
+
+ self.SendFlags |= CPSF_SETUP;
+ }
+ if(self.owner.model != MDL_ONS_CP_PAD2.model_str())
+ setmodel_fixsize(self.owner, MDL_ONS_CP_PAD2);
+
+ if(random() < 0.9 - self.health / self.max_health)
+ Send_Effect(EFFECT_RAGE, self.origin + 10 * randomvec(), '0 0 -1', 1);
+}
+
+void onslaught_controlpoint_icon_link(entity e, void() spawnproc);
+
+void ons_ControlPoint_Icon_Spawn(entity cp, entity player)
+{
+ entity e = new(onslaught_controlpoint_icon);
+
+ setsize(e, CPICON_MIN, CPICON_MAX);
+ setorigin(e, cp.origin + CPICON_OFFSET);
+
+ e.owner = cp;
+ e.max_health = autocvar_g_onslaught_cp_health;
+ e.health = autocvar_g_onslaught_cp_buildhealth;
+ e.solid = SOLID_NOT;
+ e.takedamage = DAMAGE_AIM;
+ e.bot_attack = true;
+ e.event_damage = ons_ControlPoint_Icon_Damage;
+ e.team = player.team;
+ e.colormap = 1024 + (e.team - 1) * 17;
+ e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
+
+ sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM);
+
+ cp.goalentity = e;
+ cp.team = e.team;
+ cp.colormap = e.colormap;
+
+ Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1);
+
+ WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE));
+ WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY);
+ cp.sprite.SendFlags |= 16;
+
+ onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink);
+}
+
+entity ons_ControlPoint_Waypoint(entity e)
+{
+ if(e.team)
+ {
+ int a = ons_ControlPoint_Attackable(e, e.team);
+
+ if(a == -2) { return WP_OnsCPDefend; } // defend now
+ if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch
+ if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack
+ }
+ else
+ return WP_OnsCP;
+
+ return WP_Null;
+}
+
+void ons_ControlPoint_UpdateSprite(entity e)
+{
+ entity s1 = ons_ControlPoint_Waypoint(e);
+ WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
+
+ bool sh;
+ sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4));
+
+ if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
+ {
+ if(e.iscaptured) // don't mess up build bars!
+ {
+ if(sh)
+ {
+ WaypointSprite_UpdateMaxHealth(e.sprite, 0);
+ }
+ else
+ {
+ WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);
+ WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);
+ }
+ }
+ if(e.lastshielded)
+ {
+ if(e.team)
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false));
+ else
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');
+ }
+ else
+ {
+ if(e.team)
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false));
+ else
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
+ }
+ WaypointSprite_Ping(e.sprite);
+
+ e.lastteam = e.team + 2;
+ e.lastshielded = sh;
+ e.lastcaptured = e.iscaptured;
+ }
+}
+
+void ons_ControlPoint_Touch()
+{SELFPARAM();
+ entity toucher = other;
+ int attackable;
+
+ if(IS_VEHICLE(toucher) && toucher.owner)
+ if(autocvar_g_onslaught_allow_vehicle_touch)
+ toucher = toucher.owner;
+ else
+ return;
+
+ if(!IS_PLAYER(toucher)) { return; }
+ if(toucher.frozen) { return; }
+ if(toucher.deadflag != DEAD_NO) { return; }
+
+ if ( SAME_TEAM(self,toucher) )
+ if ( self.iscaptured )
+ {
+ if(time <= toucher.teleport_antispam)
+ Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time));
+ else
+ Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT);
+ }
+
+ attackable = ons_ControlPoint_Attackable(self, toucher.team);
+ if(attackable != 2 && attackable != 4)
+ return;
+ // we've verified that this player has a legitimate claim to this point,
+ // so start building the captured point icon (which only captures this
+ // point if it successfully builds without being destroyed first)
+ ons_ControlPoint_Icon_Spawn(self, toucher);
+
+ self.ons_toucher = toucher;
+
+ onslaught_updatelinks();
+}
+
+void ons_ControlPoint_Think()
+{SELFPARAM();
+ self.nextthink = time + ONS_CP_THINKRATE;
+ CSQCMODEL_AUTOUPDATE(self);
+}
+
+void ons_ControlPoint_Reset()
+{SELFPARAM();
+ if(self.goalentity)
+ remove(self.goalentity);
+
+ self.goalentity = world;
+ self.team = 0;
+ self.colormap = 1024;
+ self.iscaptured = false;
+ self.islinked = false;
+ self.isshielded = true;
+ self.think = ons_ControlPoint_Think;
+ self.ons_toucher = world;
+ self.nextthink = time + ONS_CP_THINKRATE;
+ setmodel_fixsize(self, MDL_ONS_CP_PAD1);
+
+ WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+ WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY);
+
+ onslaught_updatelinks();
+
+ activator = self;
+ SUB_UseTargets(); // to reset the structures, playerspawns etc.
+
+ CSQCMODEL_AUTOUPDATE(self);
+}
+
+void ons_DelayedControlPoint_Setup()
+{SELFPARAM();
+ onslaught_updatelinks();
+
+ // captureshield setup
+ ons_CaptureShield_Spawn(self, false);
+
+ CSQCMODEL_AUTOINIT(self);
+}
+
+void ons_ControlPoint_Setup(entity cp)
+{SELFPARAM();
+ // declarations
+ setself(cp); // for later usage with droptofloor()
+
+ // main setup
+ cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist
+ ons_worldcplist = cp;
+
+ cp.netname = "Control point";
+ cp.team = 0;
+ cp.solid = SOLID_BBOX;
+ cp.movetype = MOVETYPE_NONE;
+ cp.touch = ons_ControlPoint_Touch;
+ cp.think = ons_ControlPoint_Think;
+ cp.nextthink = time + ONS_CP_THINKRATE;
+ cp.reset = ons_ControlPoint_Reset;
+ cp.colormap = 1024;
+ cp.iscaptured = false;
+ cp.islinked = false;
+ cp.isshielded = true;
+
+ if(cp.message == "") { cp.message = "a"; }
+
+ // appearence
+ setmodel_fixsize(cp, MDL_ONS_CP_PAD1);
+
+ // control point placement
+ if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location
+ {
+ cp.noalign = true;
+ cp.movetype = MOVETYPE_NONE;
+ }
+ else // drop to floor, automatically find a platform and set that as spawn origin
+ {
+ setorigin(cp, cp.origin + '0 0 20');
+ cp.noalign = false;
+ setself(cp);
+ droptofloor();
+ cp.movetype = MOVETYPE_TOSS;
+ }
+
+ // waypointsprites
+ WaypointSprite_SpawnFixed(WP_Null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE);
+ WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY);
+
+ InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION);
+}
+
+
+// =========================
+// Main Generator Functions
+// =========================
+
+entity ons_Generator_Waypoint(entity e)
+{
+ if (e.isshielded)
+ return WP_OnsGenShielded;
+ return WP_OnsGen;
+}
+
+void ons_Generator_UpdateSprite(entity e)
+{
+ entity s1 = ons_Generator_Waypoint(e);
+ WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
+
+ if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
+ {
+ e.lastteam = e.team + 2;
+ e.lastshielded = e.isshielded;
+ if(e.lastshielded)
+ {
+ if(e.team)
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false));
+ else
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
+ }
+ else
+ {
+ if(e.team)
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false));
+ else
+ WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
+ }
+ WaypointSprite_Ping(e.sprite);
+ }
+}
+
+void ons_GeneratorDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{SELFPARAM();
+ if(damage <= 0) { return; }
+ if(warmup_stage || gameover) { return; }
+ if(!round_handler_IsRoundStarted()) { return; }
+
+ if (attacker != self)
+ {
+ if (self.isshielded)
+ {
+ // this is protected by a shield, so ignore the damage
+ if (time > self.pain_finished)
+ if (IS_PLAYER(attacker))
+ {
+ play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
+ attacker.typehitsound += 1;
+ self.pain_finished = time + 1;
+ }
+ return;
+ }
+ if (time > self.pain_finished)
+ {
+ self.pain_finished = time + 10;
+ entity head;
+ FOR_EACH_REALPLAYER(head) if(SAME_TEAM(head, self)) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK); }
+ play2team(self.team, SND(ONS_GENERATOR_UNDERATTACK));
+ }
+ }
+ self.health = self.health - damage;
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+ // choose an animation frame based on health
+ self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
+ // see if the generator is still functional, or dying
+ if (self.health > 0)
+ {
+ self.lasthealth = self.health;
+ }
+ else
+ {
+ if (attacker == self)
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_));
+ else
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_));
+ PlayerScore_Add(attacker, SP_SCORE, 100);
+ }
+ self.iscaptured = false;
+ self.islinked = false;
+ self.isshielded = false;
+ self.takedamage = DAMAGE_NO; // can't be hurt anymore
+ self.event_damage = func_null; // won't do anything if hurt
+ self.count = 0; // reset counter
+ self.think = func_null;
+ self.nextthink = 0;
+ //self.think(); // do the first explosion now
+
+ WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+ WaypointSprite_Ping(self.sprite);
+ //WaypointSprite_Kill(self.sprite); // can't do this yet, code too poor
+
+ onslaught_updatelinks();
+ }
+
+ // Throw some flaming gibs on damage, more damage = more chance for gib
+ if(random() < damage/220)
+ {
+ sound(self, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+ }
+ else
+ {
+ // particles on every hit
+ Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1);
+
+ //sound on every hit
+ if (random() < 0.5)
+ sound(self, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM);
+ else
+ sound(self, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM);
+ }
+
+ self.SendFlags |= GSF_STATUS;
+}
+
+void ons_GeneratorThink()
+{SELFPARAM();
+ entity e;
+ self.nextthink = time + GEN_THINKRATE;
+ if (!gameover)
+ {
+ if(!self.isshielded && self.wait < time)
+ {
+ self.wait = time + 5;
+ FOR_EACH_REALPLAYER(e)
+ {
+ if(SAME_TEAM(e, self))
+ {
+ Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM);
+ soundto(MSG_ONE, e, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound?
+ }
+ else
+ Send_Notification(NOTIF_ONE, e, MSG_CENTER, APP_TEAM_NUM_4(self.team, CENTER_ONS_NOTSHIELDED_));
+ }
+ }
+ }
+}
+
+void ons_GeneratorReset()
+{SELFPARAM();
+ self.team = self.team_saved;
+ self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
+ self.takedamage = DAMAGE_AIM;
+ self.bot_attack = true;
+ self.iscaptured = true;
+ self.islinked = true;
+ self.isshielded = true;
+ self.event_damage = ons_GeneratorDamage;
+ self.think = ons_GeneratorThink;
+ self.nextthink = time + GEN_THINKRATE;
+
+ Net_LinkEntity(self, false, 0, generator_send);
+
+ self.SendFlags = GSF_SETUP; // just incase
+ self.SendFlags |= GSF_STATUS;
+
+ WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+ WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY);
+
+ onslaught_updatelinks();
+}
+
+void ons_DelayedGeneratorSetup()
+{SELFPARAM();
+ // bot waypoints
+ waypoint_spawnforitem_force(self, self.origin);
+ self.nearestwaypointtimeout = 0; // activate waypointing again
+ self.bot_basewaypoint = self.nearestwaypoint;
+
+ // captureshield setup
+ ons_CaptureShield_Spawn(self, true);
+
+ onslaught_updatelinks();
+
+ Net_LinkEntity(self, false, 0, generator_send);
+}
+
+
+void onslaught_generator_touch()
+{SELFPARAM();
+ if ( IS_PLAYER(other) )
+ if ( SAME_TEAM(self,other) )
+ if ( self.iscaptured )
+ {
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT);
+ }
+}
+
+void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc
+{SELFPARAM();
+ // declarations
+ int teamnumber = gen.team;
+ setself(gen); // for later usage with droptofloor()
+
+ // main setup
+ gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist
+ ons_worldgeneratorlist = gen;
+
+ gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber));
+ gen.classname = "onslaught_generator";
+ gen.solid = SOLID_BBOX;
+ gen.team_saved = teamnumber;
+ gen.movetype = MOVETYPE_NONE;
+ gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health;
+ gen.takedamage = DAMAGE_AIM;
+ gen.bot_attack = true;
+ gen.event_damage = ons_GeneratorDamage;
+ gen.reset = ons_GeneratorReset;
+ gen.think = ons_GeneratorThink;
+ gen.nextthink = time + GEN_THINKRATE;
+ gen.iscaptured = true;
+ gen.islinked = true;
+ gen.isshielded = true;
+ gen.touch = onslaught_generator_touch;
+
+ // appearence
+ // model handled by CSQC
+ setsize(gen, GENERATOR_MIN, GENERATOR_MAX);
+ setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET));
+ gen.colormap = 1024 + (teamnumber - 1) * 17;
+
+ // generator placement
+ setself(gen);
+ droptofloor();
+
+ // waypointsprites
+ WaypointSprite_SpawnFixed(WP_Null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE);
+ WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY);
+ WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+
+ InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION);
+}
+
+
+// ===============
+// Round Handler
+// ===============
+
+int total_generators;
+void Onslaught_count_generators()
+{
+ entity e;
+ total_generators = redowned = blueowned = yellowowned = pinkowned = 0;
+ for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext)
+ {
+ ++total_generators;
+ redowned += (e.team == NUM_TEAM_1 && e.health > 0);
+ blueowned += (e.team == NUM_TEAM_2 && e.health > 0);
+ yellowowned += (e.team == NUM_TEAM_3 && e.health > 0);
+ pinkowned += (e.team == NUM_TEAM_4 && e.health > 0);
+ }
+}
+
+int Onslaught_GetWinnerTeam()
+{
+ int winner_team = 0;
+ if(redowned > 0)
+ winner_team = NUM_TEAM_1;
+ if(blueowned > 0)
+ {
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_2;
+ }
+ if(yellowowned > 0)
+ {
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_3;
+ }
+ if(pinkowned > 0)
+ {
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_4;
+ }
+ if(winner_team)
+ return winner_team;
+ return -1; // no generators left?
+}
+
+void nades_Clear(entity e);
+
+#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0))
+#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1)
+bool Onslaught_CheckWinner()
+{
+ entity e;
+
+ if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0))
+ {
+ ons_stalemate = true;
+
+ if (!wpforenemy_announced)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT);
+ sound(world, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE);
+
+ wpforenemy_announced = true;
+ }
+
+ entity tmp_entity; // temporary entity
+ float d;
+ for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay)
+ {
+ // tmp_entity.max_health / 300 gives 5 minutes of overtime.
+ // control points reduce the overtime duration.
+ d = 1;
+ for(e = ons_worldcplist; e; e = e.ons_worldcpnext)
+ {
+ if(DIFF_TEAM(e, tmp_entity))
+ if(e.islinked)
+ d = d + 1;
+ }
+
+ if(autocvar_g_campaign && autocvar__campaign_testrun)
+ d = d * tmp_entity.max_health;
+ else
+ d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
+
+ Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0');
+
+ tmp_entity.sprite.SendFlags |= 16;
+
+ tmp_entity.ons_overtime_damagedelay = time + 1;
+ }
+ }
+ else { wpforenemy_announced = false; ons_stalemate = false; }
+
+ Onslaught_count_generators();
+
+ if(ONS_OWNED_GENERATORS_OK())
+ return 0;
+
+ int winner_team = Onslaught_GetWinnerTeam();
+
+ if(winner_team > 0)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
+ TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1);
+ }
+ else if(winner_team == -1)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
+ }
+
+ ons_stalemate = false;
+
+ play2all(SND(CTF_CAPTURE(winner_team)));
+
+ round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
+
+ FOR_EACH_PLAYER(e)
+ {
+ e.ons_roundlost = true;
+ e.player_blocked = true;
+
+ nades_Clear(e);
+ }
+
+ return 1;
+}
+
+bool Onslaught_CheckPlayers()
+{
+ return 1;
+}
+
+void Onslaught_RoundStart()
+{
+ entity tmp_entity;
+ FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = false; }
+
+ for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
+ tmp_entity.sprite.SendFlags |= 16;
+
+ for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+ tmp_entity.sprite.SendFlags |= 16;
+}
+
+
+// ================
+// Bot player logic
+// ================
+
+// NOTE: LEGACY CODE, needs to be re-written!
+
+void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius)
+{SELFPARAM();
+ entity head;
+ float t, c;
+ int i;
+ bool needarmor = false, needweapons = false;
+
+ // Needs armor/health?
+ if(self.health<100)
+ needarmor = true;
+
+ // Needs weapons?
+ c = 0;
+ for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
+ {
+ // Find weapon
+ if(self.weapons & WepSet_FromWeapon(i))
+ if(++c>=4)
+ break;
+ }
+
+ if(c<4)
+ needweapons = true;
+
+ if(!needweapons && !needarmor)
+ return;
+
+ ons_debug(strcat(self.netname, " needs weapons ", ftos(needweapons) , "\n"));
+ ons_debug(strcat(self.netname, " needs armor ", ftos(needarmor) , "\n"));
+
+ // See what is around
+ head = findchainfloat(bot_pickup, true);
+ while (head)
+ {
+ // gather health and armor only
+ if (head.solid)
+ if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) )
+ if (vlen(head.origin - org) < sradius)
+ {
+ t = head.bot_pickupevalfunc(self, head);
+ if (t > 0)
+ navigation_routerating(head, t * ratingscale, 500);
+ }
+ head = head.chain;
+ }
+}
+
+void havocbot_role_ons_setrole(entity bot, int role)
+{
+ ons_debug(strcat(bot.netname," switched to "));
+ switch(role)
+ {
+ case HAVOCBOT_ONS_ROLE_DEFENSE:
+ ons_debug("defense");
+ bot.havocbot_role = havocbot_role_ons_defense;
+ bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE;
+ bot.havocbot_role_timeout = 0;
+ break;
+ case HAVOCBOT_ONS_ROLE_ASSISTANT:
+ ons_debug("assistant");
+ bot.havocbot_role = havocbot_role_ons_assistant;
+ bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT;
+ bot.havocbot_role_timeout = 0;
+ break;
+ case HAVOCBOT_ONS_ROLE_OFFENSE:
+ ons_debug("offense");
+ bot.havocbot_role = havocbot_role_ons_offense;
+ bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE;
+ bot.havocbot_role_timeout = 0;
+ break;
+ }
+ ons_debug("\n");
+}
+
+int havocbot_ons_teamcount(entity bot, int role)
+{SELFPARAM();
+ int c = 0;
+ entity head;
+
+ FOR_EACH_PLAYER(head)
+ if(SAME_TEAM(head, self))
+ if(head.havocbot_role_flags & role)
+ ++c;
+
+ return c;
+}
+
+void havocbot_goalrating_ons_controlpoints_attack(float ratingscale)
+{SELFPARAM();
+ entity cp, cp1, cp2, best, pl, wp;
+ float radius, bestvalue;
+ int c;
+ bool found;
+
+ // Filter control points
+ for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext)
+ {
+ cp2.wpcost = c = 0;
+ cp2.wpconsidered = false;
+
+ if(cp2.isshielded)
+ continue;
+
+ // Ignore owned controlpoints
+ if(!(cp2.isgenneighbor[self.team] || cp2.iscpneighbor[self.team]))
+ continue;
+
+ // Count team mates interested in this control point
+ // (easier and cleaner than keeping counters per cp and teams)
+ FOR_EACH_PLAYER(pl)
+ if(SAME_TEAM(pl, self))
+ if(pl.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE)
+ if(pl.havocbot_ons_target==cp2)
+ ++c;
+
+ // NOTE: probably decrease the cost of attackable control points
+ cp2.wpcost = c;
+ cp2.wpconsidered = true;
+ }
+
+ // We'll consider only the best case
+ bestvalue = 99999999999;
+ cp = world;
+ for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext)
+ {
+ if (!cp1.wpconsidered)
+ continue;
+
+ if(cp1.wpcost<bestvalue)
+ {
+ bestvalue = cp1.wpcost;
+ cp = cp1;
+ self.havocbot_ons_target = cp1;
+ }
+ }
+
+ if (!cp)
+ return;
+
+ ons_debug(strcat(self.netname, " chose cp ranked ", ftos(bestvalue), "\n"));
+
+ if(cp.goalentity)
+ {
+ // Should be attacked
+ // Rate waypoints near it
+ found = false;
+ best = world;
+ bestvalue = 99999999999;
+ for(radius=0; radius<1000 && !found; radius+=500)
+ {
+ for(wp=findradius(cp.origin,radius); wp; wp=wp.chain)
+ {
+ if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
+ if(wp.classname=="waypoint")
+ if(checkpvs(wp.origin,cp))
+ {
+ found = true;
+ if(wp.cnt<bestvalue)
+ {
+ best = wp;
+ bestvalue = wp.cnt;
+ }
+ }
+ }
+ }
+
+ if(best)
+ {
+ navigation_routerating(best, ratingscale, 10000);
+ best.cnt += 1;
+
+ self.havocbot_attack_time = 0;
+ if(checkpvs(self.view_ofs,cp))
+ if(checkpvs(self.view_ofs,best))
+ self.havocbot_attack_time = time + 2;
+ }
+ else
+ {
+ navigation_routerating(cp, ratingscale, 10000);
+ }
+ ons_debug(strcat(self.netname, " found an attackable controlpoint at ", vtos(cp.origin) ,"\n"));
+ }
+ else
+ {
+ // Should be touched
+ ons_debug(strcat(self.netname, " found a touchable controlpoint at ", vtos(cp.origin) ,"\n"));
+ found = false;
+
+ // Look for auto generated waypoint
+ if (!bot_waypoints_for_items)
+ for (wp = findradius(cp.origin,100); wp; wp = wp.chain)
+ {
+ if(wp.classname=="waypoint")
+ {
+ navigation_routerating(wp, ratingscale, 10000);
+ found = true;
+ }
+ }
+
+ // Nothing found, rate the controlpoint itself
+ if (!found)
+ navigation_routerating(cp, ratingscale, 10000);
+ }
+}
+
+bool havocbot_goalrating_ons_generator_attack(float ratingscale)
+{SELFPARAM();
+ entity g, wp, bestwp;
+ bool found;
+ int best;
+
+ for(g = ons_worldgeneratorlist; g; g = g.ons_worldgeneratornext)
+ {
+ if(SAME_TEAM(g, self) || g.isshielded)
+ continue;
+
+ // Should be attacked
+ // Rate waypoints near it
+ found = false;
+ bestwp = world;
+ best = 99999999999;
+
+ for(wp=findradius(g.origin,400); wp; wp=wp.chain)
+ {
+ if(wp.classname=="waypoint")
+ if(checkpvs(wp.origin,g))
+ {
+ found = true;
+ if(wp.cnt<best)
+ {
+ bestwp = wp;
+ best = wp.cnt;
+ }
+ }
+ }
+
+ if(bestwp)
+ {
+ ons_debug("waypoints found around generator\n");
+ navigation_routerating(bestwp, ratingscale, 10000);
+ bestwp.cnt += 1;
+
+ self.havocbot_attack_time = 0;
+ if(checkpvs(self.view_ofs,g))
+ if(checkpvs(self.view_ofs,bestwp))
+ self.havocbot_attack_time = time + 5;
+
+ return true;
+ }
+ else
+ {
+ ons_debug("generator found without waypoints around\n");
+ // if there aren't waypoints near the generator go straight to it
+ navigation_routerating(g, ratingscale, 10000);
+ self.havocbot_attack_time = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+void havocbot_role_ons_offense()
+{SELFPARAM();
+ if(self.deadflag != DEAD_NO)
+ {
+ self.havocbot_attack_time = 0;
+ havocbot_ons_reset_role(self);
+ return;
+ }
+
+ // Set the role timeout if necessary
+ if (!self.havocbot_role_timeout)
+ self.havocbot_role_timeout = time + 120;
+
+ if (time > self.havocbot_role_timeout)
+ {
+ havocbot_ons_reset_role(self);
+ return;
+ }
+
+ if(self.havocbot_attack_time>time)
+ return;
+
+ if (self.bot_strategytime < time)
+ {
+ navigation_goalrating_start();
+ havocbot_goalrating_enemyplayers(20000, self.origin, 650);
+ if(!havocbot_goalrating_ons_generator_attack(20000))
+ havocbot_goalrating_ons_controlpoints_attack(20000);
+ havocbot_goalrating_ons_offenseitems(10000, self.origin, 10000);
+ navigation_goalrating_end();
+
+ self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+ }
+}
+
+void havocbot_role_ons_assistant()
+{SELFPARAM();
+ havocbot_ons_reset_role(self);
+}
+
+void havocbot_role_ons_defense()
+{SELFPARAM();
+ havocbot_ons_reset_role(self);
+}
+
+void havocbot_ons_reset_role(entity bot)
+{SELFPARAM();
+ entity head;
+ int c = 0;
+
+ if(self.deadflag != DEAD_NO)
+ return;
+
+ bot.havocbot_ons_target = world;
+
+ // TODO: Defend control points or generator if necessary
+
+ // if there is only me on the team switch to offense
+ c = 0;
+ FOR_EACH_PLAYER(head)
+ if(SAME_TEAM(head, self))
+ ++c;
+
+ if(c==1)
+ {
+ havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
+ return;
+ }
+
+ havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
+}
+
+
+/*
+ * Find control point or generator owned by the same team self which is nearest to pos
+ * if max_dist is positive, only control points within this range will be considered
+ */
+entity ons_Nearest_ControlPoint(vector pos, float max_dist)
+{SELFPARAM();
+ entity tmp_entity, closest_target = world;
+ tmp_entity = findchain(classname, "onslaught_controlpoint");
+ while(tmp_entity)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(tmp_entity.iscaptured)
+ if(max_dist <= 0 || vlen(tmp_entity.origin - pos) <= max_dist)
+ if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world)
+ closest_target = tmp_entity;
+ tmp_entity = tmp_entity.chain;
+ }
+ tmp_entity = findchain(classname, "onslaught_generator");
+ while(tmp_entity)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(max_dist <= 0 || vlen(tmp_entity.origin - pos) < max_dist)
+ if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world)
+ closest_target = tmp_entity;
+ tmp_entity = tmp_entity.chain;
+ }
+
+ return closest_target;
+}
+
+/*
+ * Find control point or generator owned by the same team self which is nearest to pos
+ * if max_dist is positive, only control points within this range will be considered
+ * This function only check distances on the XY plane, disregarding Z
+ */
+entity ons_Nearest_ControlPoint_2D(vector pos, float max_dist)
+{SELFPARAM();
+ entity tmp_entity, closest_target = world;
+ vector delta;
+ float smallest_distance = 0, distance;
+
+ tmp_entity = findchain(classname, "onslaught_controlpoint");
+ while(tmp_entity)
+ {
+ delta = tmp_entity.origin - pos;
+ delta_z = 0;
+ distance = vlen(delta);
+
+ if(SAME_TEAM(tmp_entity, self))
+ if(tmp_entity.iscaptured)
+ if(max_dist <= 0 || distance <= max_dist)
+ if(closest_target == world || distance <= smallest_distance )
+ {
+ closest_target = tmp_entity;
+ smallest_distance = distance;
+ }
+
+ tmp_entity = tmp_entity.chain;
+ }
+ tmp_entity = findchain(classname, "onslaught_generator");
+ while(tmp_entity)
+ {
+ delta = tmp_entity.origin - pos;
+ delta_z = 0;
+ distance = vlen(delta);
+
+ if(SAME_TEAM(tmp_entity, self))
+ if(max_dist <= 0 || distance <= max_dist)
+ if(closest_target == world || distance <= smallest_distance )
+ {
+ closest_target = tmp_entity;
+ smallest_distance = distance;
+ }
+
+ tmp_entity = tmp_entity.chain;
+ }
+
+ return closest_target;
+}
+/**
+ * find the number of control points and generators in the same team as self
+ */
+int ons_Count_SelfControlPoints()
+{SELFPARAM();
+ entity tmp_entity;
+ tmp_entity = findchain(classname, "onslaught_controlpoint");
+ int n = 0;
+ while(tmp_entity)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(tmp_entity.iscaptured)
+ n++;
+ tmp_entity = tmp_entity.chain;
+ }
+ tmp_entity = findchain(classname, "onslaught_generator");
+ while(tmp_entity)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ n++;
+ tmp_entity = tmp_entity.chain;
+ }
+ return n;
+}
+
+/**
+ * Teleport player to a random position near tele_target
+ * if tele_effects is true, teleport sound+particles are created
+ * return false on failure
+ */
+bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects)
+{
+ if ( !tele_target )
+ return false;
+
+ int i;
+ vector loc;
+ float theta;
+ // narrow the range for each iteration to increase chances that a spawnpoint
+ // can be found even if there's little room around the control point
+ float iteration_scale = 1;
+ for(i = 0; i < 16; ++i)
+ {
+ iteration_scale -= i / 16;
+ theta = random() * 2 * M_PI;
+ loc_y = sin(theta);
+ loc_x = cos(theta);
+ loc_z = 0;
+ loc *= random() * range * iteration_scale;
+
+ loc += tele_target.origin + '0 0 128' * iteration_scale;
+
+ tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, player);
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the world
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ if ( tele_effects )
+ {
+ Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1);
+ sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM);
+ }
+ setorigin(player, loc);
+ player.angles = '0 1 0' * ( theta * RAD2DEG + 180 );
+ makevectors(player.angles);
+ player.fixangle = true;
+ player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait;
+
+ if ( tele_effects )
+ Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// ==============
+// Hook Functions
+// ==============
+
+MUTATOR_HOOKFUNCTION(ons, reset_map_global)
+{SELFPARAM();
+ entity e;
+ FOR_EACH_PLAYER(e)
+ {
+ e.ons_roundlost = false;
+ e.ons_deathloc = '0 0 0';
+ WITH(entity, self, e, PutClientInServer());
+ }
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, ClientDisconnect)
+{SELFPARAM();
+ self.ons_deathloc = '0 0 0';
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver)
+{SELFPARAM();
+ self.ons_deathloc = '0 0 0';
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, PlayerSpawn)
+{SELFPARAM();
+ if(!round_handler_IsRoundStarted())
+ {
+ self.player_blocked = true;
+ return false;
+ }
+
+ entity l;
+ for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
+ {
+ l.sprite.SendFlags |= 16;
+ }
+ for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+ {
+ l.sprite.SendFlags |= 16;
+ }
+
+ if(ons_stalemate) { Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); }
+
+ if ( autocvar_g_onslaught_spawn_choose )
+ if ( self.ons_spawn_by )
+ if ( ons_Teleport(self,self.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) )
+ {
+ self.ons_spawn_by = world;
+ return false;
+ }
+
+ if(autocvar_g_onslaught_spawn_at_controlpoints)
+ if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance)
+ {
+ float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random;
+ entity tmp_entity, closest_target = world;
+ vector spawn_loc = self.ons_deathloc;
+
+ // new joining player or round reset, don't bother checking
+ if(spawn_loc == '0 0 0') { return false; }
+
+ if(random_target) { RandomSelection_Init(); }
+
+ for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(random_target)
+ RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
+ else if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world)
+ closest_target = tmp_entity;
+ }
+
+ if(random_target) { closest_target = RandomSelection_chosen_ent; }
+
+ if(closest_target)
+ {
+ float i;
+ vector loc;
+ float iteration_scale = 1;
+ for(i = 0; i < 10; ++i)
+ {
+ iteration_scale -= i / 10;
+ loc = closest_target.origin + '0 0 96' * iteration_scale;
+ loc += ('0 1 0' * random()) * 128 * iteration_scale;
+ tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self);
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ setorigin(self, loc);
+ self.angles = normalize(loc - closest_target.origin) * RAD2DEG;
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ if(autocvar_g_onslaught_spawn_at_generator)
+ if(random() <= autocvar_g_onslaught_spawn_at_generator_chance)
+ {
+ float random_target = autocvar_g_onslaught_spawn_at_generator_random;
+ entity tmp_entity, closest_target = world;
+ vector spawn_loc = self.ons_deathloc;
+
+ // new joining player or round reset, don't bother checking
+ if(spawn_loc == '0 0 0') { return false; }
+
+ if(random_target) { RandomSelection_Init(); }
+
+ for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+ {
+ if(random_target)
+ RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
+ else
+ {
+ if(SAME_TEAM(tmp_entity, self))
+ if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world)
+ closest_target = tmp_entity;
+ }
+ }
+
+ if(random_target) { closest_target = RandomSelection_chosen_ent; }
+
+ if(closest_target)
+ {
+ float i;
+ vector loc;
+ float iteration_scale = 1;
+ for(i = 0; i < 10; ++i)
+ {
+ iteration_scale -= i / 10;
+ loc = closest_target.origin + '0 0 128' * iteration_scale;
+ loc += ('0 1 0' * random()) * 256 * iteration_scale;
+ tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self);
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world
+ if(trace_fraction == 1.0 && !trace_startsolid)
+ {
+ setorigin(self, loc);
+ self.angles = normalize(loc - closest_target.origin) * RAD2DEG;
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, PlayerDies)
+{SELFPARAM();
+ frag_target.ons_deathloc = frag_target.origin;
+ entity l;
+ for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
+ {
+ l.sprite.SendFlags |= 16;
+ }
+ for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
+ {
+ l.sprite.SendFlags |= 16;
+ }
+
+ if ( autocvar_g_onslaught_spawn_choose )
+ if ( ons_Count_SelfControlPoints() > 1 )
+ stuffcmd(self, "qc_cmd_cl hud clickradar\n");
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, MonsterMove)
+{SELFPARAM();
+ entity e = find(world, targetname, self.target);
+ if (e != world)
+ self.team = e.team;
+
+ return false;
+}
+
+void ons_MonsterSpawn_Delayed()
+{SELFPARAM();
+ entity e, own = self.owner;
+
+ if(!own) { remove(self); return; }
+
+ if(own.targetname)
+ {
+ e = find(world, target, own.targetname);
+ if(e != world)
+ {
+ own.team = e.team;
+
+ activator = e;
+ own.use();
+ }
+ }
+
+ remove(self);
+}
+
+MUTATOR_HOOKFUNCTION(ons, MonsterSpawn)
+{SELFPARAM();
+ entity e = spawn();
+ e.owner = self;
+ InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET);
+
+ return false;
+}
+
+void ons_TurretSpawn_Delayed()
+{SELFPARAM();
+ entity e, own = self.owner;
+
+ if(!own) { remove(self); return; }
+
+ if(own.targetname)
+ {
+ e = find(world, target, own.targetname);
+ if(e != world)
+ {
+ own.team = e.team;
+ own.active = ACTIVE_NOT;
+
+ activator = e;
+ own.use();
+ }
+ }
+
+ remove(self);
+}
+
+MUTATOR_HOOKFUNCTION(ons, TurretSpawn)
+{SELFPARAM();
+ entity e = spawn();
+ e.owner = self;
+ InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET);
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole)
+{SELFPARAM();
+ havocbot_ons_reset_role(self);
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(ons, GetTeamCount)
+{
+ // onslaught is special
+ for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
+ {
+ switch(tmp_entity.team)
+ {
+ case NUM_TEAM_1: c1 = 0; break;
+ case NUM_TEAM_2: c2 = 0; break;
+ case NUM_TEAM_3: c3 = 0; break;
+ case NUM_TEAM_4: c4 = 0; break;
+ }
+ }
+
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(ons, SpectateCopy)
+{SELFPARAM();
+ self.ons_roundlost = other.ons_roundlost; // make spectators see it too
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand)
+{SELFPARAM();
+ if(MUTATOR_RETURNVALUE) // command was already handled?
+ return false;
+
+ if ( cmd_name == "ons_spawn" )
+ {
+ vector pos = self.origin;
+ if(cmd_argc > 1)
+ pos_x = stof(argv(1));
+ if(cmd_argc > 2)
+ pos_y = stof(argv(2));
+ if(cmd_argc > 3)
+ pos_z = stof(argv(3));
+
+ if ( IS_PLAYER(self) )
+ {
+ if ( !self.frozen )
+ {
+ entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius);
+
+ if ( !source_point && self.health > 0 )
+ {
+ sprint(self, "\nYou need to be next to a control point\n");
+ return 1;
+ }
+
+
+ entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius);
+
+ if ( closest_target == world )
+ {
+ sprint(self, "\nNo control point found\n");
+ return 1;
+ }
+
+ if ( self.health <= 0 )
+ {
+ self.ons_spawn_by = closest_target;
+ self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
+ }
+ else
+ {
+ if ( source_point == closest_target )
+ {
+ sprint(self, "\nTeleporting to the same point\n");
+ return 1;
+ }
+
+ if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,true) )
+ sprint(self, "\nUnable to teleport there\n");
+ }
+
+ return 1;
+ }
+
+ sprint(self, "\nNo teleportation for you\n");
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ons, PlayerUseKey)
+{SELFPARAM();
+ if(MUTATOR_RETURNVALUE || gameover) { return false; }
+
+ if((time > self.teleport_antispam) && (self.deadflag == DEAD_NO) && !self.vehicle)
+ {
+ entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius);
+ if ( source_point )
+ {
+ stuffcmd(self, "qc_cmd_cl hud clickradar\n");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, PlayHitsound)
+{
+ return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded)
+ || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded);
+}
+
+MUTATOR_HOOKFUNCTION(ons, SendWaypoint)
+{
+ if(wp_sendflags & 16)
+ {
+ if(self.owner.classname == "onslaught_controlpoint")
+ {
+ entity wp_owner = self.owner;
+ entity e = WaypointSprite_getviewentity(wp_sendto);
+ if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; }
+ if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; }
+ }
+ if(self.owner.classname == "onslaught_generator")
+ {
+ entity wp_owner = self.owner;
+ if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; }
+ if(wp_owner.health <= 0) { wp_flag |= 2; }
+ }
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget)
+{
+ if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job!
+ {
+ ret_float = -3;
+ return true;
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ons, TurretThink)
+{
+ // ONS uses somewhat backwards linking.
+ if(self.target)
+ {
+ entity e = find(world, targetname, self.target);
+ if (e != world)
+ self.team = e.team;
+ }
+
+ if(self.team != self.tur_head.team)
+ turret_respawn();
+
+ return false;
+}
+
+
+// ==========
+// Spawnfuncs
+// ==========
+
+/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
+ Link between control points.
+
+ This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.
+
+keys:
+"target" - first control point.
+"target2" - second control point.
+ */
+spawnfunc(onslaught_link)
+{
+ if(!g_onslaught) { remove(self); return; }
+
+ if (self.target == "" || self.target2 == "")
+ objerror("target and target2 must be set\n");
+
+ self.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist
+ ons_worldlinklist = self;
+
+ InitializeEntity(self, ons_DelayedLinkSetup, INITPRIO_FINDTARGET);
+ Net_LinkEntity(self, false, 0, ons_Link_Send);
+}
+
+/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
+ Control point. Be sure to give this enough clearance so that the shootable part has room to exist
+
+ This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
+
+keys:
+"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
+"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
+"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
+ */
+
+spawnfunc(onslaught_controlpoint)
+{
+ if(!g_onslaught) { remove(self); return; }
+
+ ons_ControlPoint_Setup(self);
+}
+
+/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
+ Base generator.
+
+ spawnfunc_onslaught_link entities can target this.
+
+keys:
+"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
+"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
+ */
+spawnfunc(onslaught_generator)
+{
+ if(!g_onslaught) { remove(self); return; }
+ if(!self.team) { objerror("team must be set"); }
+
+ ons_GeneratorSetup(self);
+}
+
+// scoreboard setup
+void ons_ScoreRules()
+{
+ CheckAllowedTeams(world);
+ ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true);
+ ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY);
+ ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY);
+ ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0);
+ ScoreRules_basics_end();
+}
+
+void ons_DelayedInit() // Do this check with a delay so we can wait for teams to be set up
+{
+ ons_ScoreRules();
+
+ round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart);
+ round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
+}
+
+void ons_Initialize()
+{
+ g_onslaught = true;
+ ons_captureshield_force = autocvar_g_onslaught_shield_force;
+
+ addstat(STAT_ROUNDLOST, AS_INT, ons_roundlost);
+
+ InitializeEntity(world, ons_DelayedInit, INITPRIO_GAMETYPE);
+}
+
+#endif
--- /dev/null
+#include "sv_controlpoint.qh"
+
+.bool iscaptured;
+
+bool cpicon_send(entity this, entity to, int sf)
+{
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_CONTROLPOINT_ICON);
+ WriteByte(MSG_ENTITY, sf);
+ if(sf & CPSF_SETUP)
+ {
+ WriteCoord(MSG_ENTITY, self.origin_x);
+ WriteCoord(MSG_ENTITY, self.origin_y);
+ WriteCoord(MSG_ENTITY, self.origin_z);
+
+ WriteByte(MSG_ENTITY, self.health);
+ WriteByte(MSG_ENTITY, self.max_health);
+ WriteByte(MSG_ENTITY, self.count);
+ WriteByte(MSG_ENTITY, self.team);
+ WriteByte(MSG_ENTITY, self.owner.iscaptured);
+ }
+
+ if(sf & CPSF_STATUS)
+ {
+ WriteByte(MSG_ENTITY, self.team);
+
+ if(self.health <= 0)
+ WriteByte(MSG_ENTITY, 0);
+ else
+ WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255));
+ }
+
+ return true;
+}
+
+void onslaught_controlpoint_icon_link(entity e, void() spawnproc)
+{
+ Net_LinkEntity(e, true, 0, cpicon_send);
+ e.think = spawnproc;
+ e.nextthink = time * sys_frametime;
+}
--- /dev/null
+#ifndef CONTROLPOINT_H
+#define CONTROLPOINT_H
+
+const vector CPICON_MIN = '-32 -32 -9';
+const vector CPICON_MAX = '32 32 25';
+
+const int CPSF_STATUS = 4;
+const int CPSF_SETUP = 8;
+
+#endif
--- /dev/null
+#include "sv_generator.qh"
+
+bool generator_send(entity this, entity to, int sf)
+{
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_GENERATOR);
+ WriteByte(MSG_ENTITY, sf);
+ if(sf & GSF_SETUP)
+ {
+ WriteCoord(MSG_ENTITY, self.origin_x);
+ WriteCoord(MSG_ENTITY, self.origin_y);
+ WriteCoord(MSG_ENTITY, self.origin_z);
+
+ WriteByte(MSG_ENTITY, self.health);
+ WriteByte(MSG_ENTITY, self.max_health);
+ WriteByte(MSG_ENTITY, self.count);
+ WriteByte(MSG_ENTITY, self.team);
+ }
+
+ if(sf & GSF_STATUS)
+ {
+ WriteByte(MSG_ENTITY, self.team);
+
+ if(self.health <= 0)
+ WriteByte(MSG_ENTITY, 0);
+ else
+ WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255));
+ }
+
+ return true;
+}
+
+void generator_link(void() spawnproc)
+{SELFPARAM();
+ Net_LinkEntity(self, true, 0, generator_send);
+ self.think = spawnproc;
+ self.nextthink = time;
+}
--- /dev/null
+#ifndef GENERATOR_H
+#define GENERATOR_H
+const vector GENERATOR_MIN = '-52 -52 -14';
+const vector GENERATOR_MAX = '52 52 75';
+
+const int GSF_STATUS = 4;
+const int GSF_SETUP = 8;
+
+bool generator_send(entity this, entity to, int sf);
+#endif
#include "item.qh"
-REGISTRY(Items, BIT(5))
+REGISTRY(Items, BITS(5))
+#define Items_from(i) _Items_from(i, NULL)
REGISTER_REGISTRY(RegisterItems)
/** If you register a new item, make sure to add it to all.inc */
#define REGISTER_ITEM(id, class) REGISTER(RegisterItems, ITEM, Items, id, m_id, NEW(class))
-REGISTRY_SORT(Items, m_name, 0)
+REGISTRY_SORT(Items, 0)
+REGISTRY_CHECK(Items)
STATIC_INIT(Items) { FOREACH(Items, true, LAMBDA(it.m_id = i)); }
void Dump_Items();
#endif
#endif
-
-#include "inventory.qh"
/** Player inventory; Inventories also have one inventory for storing the previous state */
.Inventory inventory;
+REGISTER_NET_LINKED(ENT_CLIENT_INVENTORY)
+
#ifdef CSQC
-void Inventory_Read(Inventory data)
+NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew)
{
+ make_pure(this);
const int bits = ReadInt24_t();
FOREACH(Items, bits & BIT(it.m_id), LAMBDA(
.int fld = inv_items[it.m_id];
- int prev = data.(fld);
- int next = data.(fld) = ReadByte();
+ int prev = this.(fld);
+ int next = this.(fld) = ReadByte();
LOG_TRACEF("%s: %.0f -> %.0f\n", it.m_name, prev, next);
));
+ return true;
}
#endif
#ifdef SVQC
bool Inventory_Send(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_INVENTORY);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_INVENTORY);
entity e = self.owner;
if (IS_SPEC(e)) e = e.enemy;
Inventory data = e.inventory;
void Inventory_new(entity e)
{
Inventory inv = new(Inventory), bak = new(Inventory);
- inv.classname = "inventory", bak.classname = "inventory";
+ make_pure(inv); make_pure(bak);
inv.inventory = bak;
inv.drawonlytoclient = e;
Net_LinkEntity((inv.owner = e).inventory = inv, false, 0, Inventory_Send);
#ifndef MENUQC
MODEL(ArmorSmall_ITEM, Item_Model("item_armor_small.md3"));
+SOUND(ArmorSmall, "misc/armor1");
#endif
REGISTER_ITEM(ArmorSmall, Armor) {
#ifndef MENUQC
this.m_model = MDL_ArmorSmall_ITEM;
+ this.m_sound = SND_ArmorSmall;
#endif
- this.m_sound = "misc/armor1.wav";
this.m_name = "5 Armor";
this.m_icon = "armor";
#ifdef SVQC
#ifndef MENUQC
MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3"));
+SOUND(ArmorMedium, "misc/armor10");
#endif
REGISTER_ITEM(ArmorMedium, Armor) {
#ifndef MENUQC
this.m_model = MDL_ArmorMedium_ITEM;
+ this.m_sound = SND_ArmorMedium;
#endif
- this.m_sound = "misc/armor10.wav";
this.m_name = "25 Armor";
this.m_icon = "armor";
#ifdef SVQC
#ifndef MENUQC
MODEL(ArmorLarge_ITEM, Item_Model("item_armor_big.md3"));
+SOUND(ArmorLarge, "misc/armor17_5");
#endif
REGISTER_ITEM(ArmorLarge, Armor) {
#ifndef MENUQC
this.m_model = MDL_ArmorLarge_ITEM;
+ this.m_sound = SND_ArmorLarge;
#endif
- this.m_sound = "misc/armor17_5.wav";
this.m_name = "50 Armor";
this.m_icon = "armor";
this.m_color = '0 1 0';
#ifndef MENUQC
MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3"));
+SOUND(ArmorMega, "misc/armor25");
#endif
REGISTER_ITEM(ArmorMega, Armor) {
#ifndef MENUQC
this.m_model = MDL_ArmorMega_ITEM;
+ this.m_sound = SND_ArmorMega;
#endif
- this.m_sound = "misc/armor25.wav";
this.m_name = "100 Armor";
this.m_icon = "item_large_armor";
this.m_color = '0 1 0';
#include "pickup.qh"
CLASS(Armor, Pickup)
#ifdef SVQC
+ ATTRIB(Armor, m_mins, vector, '-16 -16 0')
+ ATTRIB(Armor, m_maxs, vector, '16 16 48')
ATTRIB(Armor, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc)
#endif
ENDCLASS(Armor)
#ifndef MENUQC
MODEL(HealthSmall_ITEM, Item_Model("g_h1.md3"));
+SOUND(HealthSmall, "misc/minihealth");
#endif
REGISTER_ITEM(HealthSmall, Health) {
#ifndef MENUQC
this.m_model = MDL_HealthSmall_ITEM;
+ this.m_sound = SND_HealthSmall;
#endif
- this.m_sound = "misc/minihealth.wav";
this.m_name = "5 Health";
this.m_icon = "health";
#ifdef SVQC
#ifndef MENUQC
MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3"));
+SOUND(HealthMedium, "misc/mediumhealth");
#endif
REGISTER_ITEM(HealthMedium, Health) {
#ifndef MENUQC
this.m_model = MDL_HealthMedium_ITEM;
+ this.m_sound = SND_HealthMedium;
#endif
- this.m_sound = "misc/mediumhealth.wav";
this.m_name = "25 Health";
this.m_icon = "health";
#ifdef SVQC
#ifndef MENUQC
MODEL(HealthLarge_ITEM, Item_Model("g_h50.md3"));
+SOUND(HealthLarge, "misc/mediumhealth");
#endif
REGISTER_ITEM(HealthLarge, Health) {
#ifndef MENUQC
this.m_model = MDL_HealthLarge_ITEM;
+ this.m_sound = SND_HealthLarge;
#endif
- this.m_sound = "misc/mediumhealth.wav";
this.m_name = "50 Health";
this.m_icon = "health";
this.m_color = '1 0 0';
#ifndef MENUQC
MODEL(HealthMega_ITEM, Item_Model("g_h100.md3"));
+SOUND(HealthMega, "misc/megahealth");
#endif
REGISTER_ITEM(HealthMega, Health) {
#ifndef MENUQC
this.m_model = MDL_HealthMega_ITEM;
+ this.m_sound = SND_HealthMega;
#endif
- this.m_sound = "misc/megahealth.wav";
this.m_name = "100 Health";
this.m_icon = "item_mega_health";
this.m_color = '1 0 0';
#include "pickup.qh"
CLASS(Health, Pickup)
#ifdef SVQC
+ ATTRIB(Health, m_mins, vector, '-16 -16 0')
+ ATTRIB(Health, m_maxs, vector, '16 16 48')
ATTRIB(Health, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc)
#endif
ENDCLASS(Health)
#ifdef SVQC
bool ITEM_HANDLE(Pickup, entity this, entity item, entity player) {
- bool b = this.giveTo(this, item, player);
- if (b) {
- LOG_TRACEF("entity %i picked up %s\n", player, this.m_name);
- player.inventory.inv_items[this.m_id]++;
- Inventory_update(player);
- }
- return b;
+ return this.giveTo(this, item, player);
}
#endif
#ifndef PICKUP_H
#define PICKUP_H
+#include "../inventory.qh"
#include "../item.qh"
CLASS(Pickup, GameItem)
#ifndef MENUQC
ATTRIB(Pickup, m_model, Model, NULL)
+ ATTRIB(Pickup, m_sound, Sound, SND_ITEMPICKUP)
#endif
- ATTRIB(Pickup, m_sound, string, "misc/itempickup.wav")
ATTRIB(Pickup, m_name, string, string_null)
METHOD(Pickup, show, void(entity this));
void Pickup_show(entity this) { LOG_INFOF("%s: %s\n", etos(this), this.m_name); }
#ifdef SVQC
+ ATTRIB(Pickup, m_mins, vector, '-16 -16 0')
+ ATTRIB(Pickup, m_maxs, vector, '16 16 32')
ATTRIB(Pickup, m_botvalue, int, 0)
ATTRIB(Pickup, m_itemflags, int, 0)
ATTRIB(Pickup, m_itemid, int, 0)
+ float generic_pickupevalfunc(entity player, entity item);
ATTRIB(Pickup, m_pickupevalfunc, float(entity player, entity item), generic_pickupevalfunc)
ATTRIB(Pickup, m_respawntime, float(), func_null)
ATTRIB(Pickup, m_respawntimejitter, float(), func_null)
- METHOD(Pickup, giveTo, bool(entity this, entity item, entity player));
- bool Pickup_giveTo(entity this, entity item, entity player) { return Item_GiveTo(item, player); }
+ float Item_GiveTo(entity item, entity player);
+ METHOD(Pickup, giveTo, bool(entity this, entity item, entity player))
+ {
+ bool b = Item_GiveTo(item, player);
+ if (b) {
+ LOG_TRACEF("entity %i picked up %s\n", player, this.m_name);
+ player.inventory.inv_items[this.m_id]++;
+ Inventory_update(player);
+ }
+ return b;
+ }
bool ITEM_HANDLE(Pickup, entity this, entity item, entity player);
#endif
ENDCLASS(Pickup)
-#ifdef SVQC
-// For g_pickup_respawntime
-#include "../../../server/defs.qh"
-// Getters to dynamically retrieve the values of g_pickup_respawntime*
-GETTER(float, g_pickup_respawntime_weapon)
-GETTER(float, g_pickup_respawntime_superweapon)
-GETTER(float, g_pickup_respawntime_ammo)
-GETTER(float, g_pickup_respawntime_short)
-GETTER(float, g_pickup_respawntime_medium)
-GETTER(float, g_pickup_respawntime_long)
-GETTER(float, g_pickup_respawntime_powerup)
-GETTER(float, g_pickup_respawntimejitter_weapon)
-GETTER(float, g_pickup_respawntimejitter_superweapon)
-GETTER(float, g_pickup_respawntimejitter_ammo)
-GETTER(float, g_pickup_respawntimejitter_short)
-GETTER(float, g_pickup_respawntimejitter_medium)
-GETTER(float, g_pickup_respawntimejitter_long)
-GETTER(float, g_pickup_respawntimejitter_powerup)
-
-#endif
-
#endif
#ifndef MENUQC
MODEL(Strength_ITEM, Item_Model("g_strength.md3"));
+SOUND(Strength, "misc/powerup");
#endif
REGISTER_ITEM(Strength, Powerup) {
#ifndef MENUQC
this.m_model = MDL_Strength_ITEM;
+ this.m_sound = SND_Strength;
#endif
- this.m_sound = "misc/powerup.wav";
this.m_name = "Strength Powerup";
this.m_icon = "strength";
this.m_color = '0 0 1';
#ifndef MENUQC
MODEL(Shield_ITEM, Item_Model("g_invincible.md3"));
+SOUND(Shield, "misc/powerup_shield");
#endif
REGISTER_ITEM(Shield, Powerup) {
#ifndef MENUQC
this.m_model = MDL_Shield_ITEM;
+ this.m_sound = SND_Shield;
#endif
- this.m_sound = "misc/powerup_shield.wav";
this.m_name = "Shield";
this.m_icon = "shield";
this.m_color = '1 0 1';
#include "pickup.qh"
CLASS(Powerup, Pickup)
#ifdef SVQC
+ ATTRIB(Powerup, m_mins, vector, '-16 -16 0')
+ ATTRIB(Powerup, m_maxs, vector, '16 16 48')
ATTRIB(Powerup, m_botvalue, int, 100000)
ATTRIB(Powerup, m_itemflags, int, FL_POWERUP)
ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup))
}
ENDCLASS(Gametype)
-REGISTRY(Gametypes, BIT(4))
+REGISTRY(Gametypes, BITS(4))
+#define Gametypes_from(i) _Gametypes_from(i, NULL)
REGISTER_REGISTRY(RegisterGametypes)
+REGISTRY_CHECK(Gametypes)
int MAPINFO_TYPE_ALL;
#define REGISTER_GAMETYPE(hname, sname, g_name, NAME, gteamplay, mutators, defaults, gdescription) \
int MAPINFO_TYPE_##NAME; \
}
#define g_race IS_GAMETYPE(RACE)
-REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,false,"","timelimit=20",_("Race for fastest time."));
+REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,false,"cloaked","timelimit=20",_("Race for fastest time."));
#define g_cts IS_GAMETYPE(CTS)
REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,true,"","timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team"))
if ( !self.owner )
LOG_TRACE("Got a minigame entity without a minigame!\n");
}
-void ent_read_minigame()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew)
+{
float sf = ReadByte();
if ( sf & MINIG_SF_CREATE )
{
" classname:",self.classname," enttype:",ftos(self.enttype) );
LOG_DEBUG(" sf:",ftos(sf)," netname:",self.netname,"\n\n");
}
+ return true;
}
#undef ReadString
#undef FIELD
#define minigame_cmd(...) minigame_cmd_workaround(0,__VA_ARGS__)
void minigame_cmd_workaround(float dummy, string...cmdargc);
-// Read a minigame entity from the server
-void ent_read_minigame();
-
// Prompt the player to play in the current minigame
// (ie: it's their turn and they should get back to the minigame)
void minigame_prompt();
while( (entityvar = findentity(entityvar,owner,active_minigame)) )
-REGISTRY(Minigames, BIT(3))
+REGISTRY(Minigames, BITS(3))
+#define Minigames_from(i) _Minigames_from(i, NULL)
REGISTER_REGISTRY(RegisterMinigames)
+REGISTRY_CHECK(Minigames)
#define REGISTER_MINIGAME(name,nicename) \
- REGISTER(RegisterMinigames, MINIGAME, Minigames, name, m_id, spawn()); \
+ REGISTER(RegisterMinigames, MINIGAME, Minigames, name, m_id, new(minigame_descriptor)); \
void name##_hud_board(vector, vector); \
void name##_hud_status(vector, vector); \
int name##_client_event(entity, string, ...); \
REGISTER_INIT_POST(MINIGAME, name) { \
- this.classname = "minigame_descriptor"; \
+ make_pure(this); \
this.netname = strzone(strtolower(#name)); \
this.message = nicename; \
this.minigame_hud_board = name##_hud_board; \
void nmm_spawn_tile(string id, entity minig, int distance)
{
// TODO global variable + list_next for simpler tile loops
- entity e = spawn();
+ entity e = new(minigame_nmm_tile);
e.origin = minigame_tile_pos(id,7,7);
- e.classname = "minigame_nmm_tile";
e.netname = id;
e.owner = minig;
e.team = 0;
if ( spawnai )
{
- entity aiplayer = spawn();
- aiplayer.classname = "minigame_player";
+ entity aiplayer = new(minigame_player);
aiplayer.owner = minigame;
aiplayer.team = ai;
aiplayer.minigame_playerslot = 0;
#include "minigames.qh"
+REGISTER_NET_LINKED(ENT_CLIENT_MINIGAME)
+
entity minigame_get_descriptor(string id)
{
FOREACH(Minigames, true, LAMBDA(
// only use on minigame entities or entities with a minigame owner
bool minigame_SendEntity(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_MINIGAME);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_MINIGAME);
WriteByte(MSG_ENTITY, sf);
if ( sf & MINIG_SF_CREATE )
return 0;
minigame_rmplayer(player.active_minigame,player);
}
- entity player_pointer = spawn();
+ entity player_pointer = new(minigame_player);
int mgteam = minigame_session.minigame_event(minigame_session,"join",player,player_pointer);
if ( mgteam )
{
- player_pointer.classname = "minigame_player";
player_pointer.owner = minigame_session;
player_pointer.minigame_players = player;
player_pointer.team = mgteam;
entity e = minigame_get_descriptor(minigame);
if ( e )
{
- entity minig = spawn();
- minig.classname = "minigame";
+ entity minig = new(minigame);
minig.netname = strzone(strcat(e.netname,"_",ftos(num_for_edict(minig))));
minig.descriptor = e;
minig.minigame_event = e.minigame_event;
bool minigame_SendEntity(entity this, entity to, int sf);
-REGISTRY(Minigames, BIT(3))
+REGISTRY(Minigames, BITS(3))
+#define Minigames_from(i) _Minigames_from(i, NULL)
REGISTER_REGISTRY(RegisterMinigames)
+REGISTRY_CHECK(Minigames)
#define REGISTER_MINIGAME(name,nicename) \
- REGISTER(RegisterMinigames, MINIGAME, Minigames, name, m_id, spawn()); \
+ REGISTER(RegisterMinigames, MINIGAME, Minigames, name, m_id, new(minigame_descriptor)); \
int name##_server_event(entity, string, ...); \
REGISTER_INIT_POST(MINIGAME, name) { \
- this.classname = "minigame_descriptor"; \
+ make_pure(this); \
this.netname = strzone(strtolower(#name)); \
this.message = nicename; \
this.minigame_event = name##_server_event; \
MODEL(CTF_SHIELD, "models/ctf/shield.md3");
MODEL(CTF_CAPTURE, "models/ctf/shockwavetransring.md3");
+MODEL(CTF_FLAG, "models/ctf/flags.md3");
MODEL(DOM_NEUTRAL, "models/domination/dom_unclaimed.md3");
MODEL(DOM_RED, "models/domination/dom_red.md3");
MODEL(GIB_ROBO_8, "models/gibs/robo8.md3");
Model MDL_GIB_ROBO_RANDOM() {
int i = floor(random() * 8);
- return Models[MDL_GIB_ROBO_1.m_id + i];
+ return Models_from(MDL_GIB_ROBO_1.m_id + i);
}
MODEL(CASING_SHELL, "models/casing_shell.mdl");
MODEL(10, "models/sprites/10.spr32");
Model MDL_NUM(int i) {
if ((i >= 0 && i <= 10))
- return Models[MDL_0.m_id + i];
+ return Models_from(MDL_0.m_id + i);
return MDL_Null;
}
#include "model.qh"
-REGISTRY(Models, BIT(9))
+REGISTRY(Models, BITS(9))
+#define Models_from(i) _Models_from(i, MDL_Null)
REGISTER_REGISTRY(RegisterModels)
#define MODEL(name, path) \
string MDL_##name##_get() { return path; } \
REGISTER(RegisterModels, MDL, Models, name, m_id, NEW(Model, MDL_##name##_get))
-STATIC_INIT(RegisterModels_precache) {
+PRECACHE(Models) {
FOREACH(Models, true, LAMBDA({
it.model_precache(it);
}));
#ifndef MONSTERS_ALL_C
#define MONSTERS_ALL_C
+string M_Model(string m_mdl)
+{
+ string output = strcat("models/monsters/", m_mdl);
+#ifdef SVQC
+ MUTATOR_CALLHOOK(MonsterModel, m_mdl, output);
+ return monster_model_output;
+#else
+ return output;
+#endif
+}
+
#include "all.qh"
#define IMPLEMENTATION
#include "monster.qh"
-REGISTRY(Monsters, BIT(4))
+string M_Model(string m_mdl);
+
+REGISTRY(Monsters, BITS(5))
+#define Monsters_from(i) _Monsters_from(i, MON_Null)
+#define get_monsterinfo(i) Monsters_from(i)
REGISTER_REGISTRY(RegisterMonsters)
+REGISTRY_CHECK(Monsters)
const int MON_FIRST = 1;
#define MON_LAST (Monsters_COUNT - 1)
/** If you register a new monster, make sure to add it to all.inc */
REGISTER_MONSTER(Null, NEW(Monster));
-Monster get_monsterinfo(int id)
-{
- if (id >= MON_FIRST && id <= MON_LAST) {
- Monster m = Monsters[id];
- if (m) return m;
- }
- return MON_Null;
-}
#include "all.inc"
const int MON_FLAG_MELEE = 1024;
const int MON_FLAG_CRUSH = 2048; // monster can be stomped in special modes
const int MON_FLAG_RIDE = 4096; // monster can be ridden in special modes
+const int MONSTER_SIZE_QUAKE = 8192;
// entity properties of monsterinfo:
.bool(int, entity targ) monster_attackfunc;
#define MAGE_H
#ifndef MENUQC
-MODEL(MON_MAGE, "models/monsters/mage.dpm");
+MODEL(MON_MAGE, M_Model("mage.dpm"));
#endif
CLASS(Mage, Monster)
/* flags */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(MageSpike, impulse, int, 9);
/* refname */ ATTRIB(MageSpike, netname, string, "magespike");
-/* wepname */ ATTRIB(MageSpike, message, string, _("Mage spike"));
+/* wepname */ ATTRIB(MageSpike, m_name, string, _("Mage spike"));
ENDCLASS(MageSpike)
REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike));
#ifdef SVQC
+SOUND(MageSpike_FIRE, W_Sound("electro_fire"));
void M_Mage_Attack_Spike(vector dir);
void M_Mage_Attack_Push();
-METHOD(MageSpike, wr_think, void(MageSpike thiswep, entity actor, bool fire1, bool fire2)) {
- if (fire1)
- if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, false, 0.2)) {
+METHOD(MageSpike, wr_think, void(MageSpike thiswep, entity actor, .entity weaponentity, int fire)) {
+ if (fire & 1)
+ if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) {
if (!actor.target_range) actor.target_range = autocvar_g_monsters_target_range;
actor.enemy = Monster_FindTarget(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(MageSpike_FIRE), CH_WEAPON_B, 0);
if (!IS_PLAYER(actor)) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
M_Mage_Attack_Spike(w_shotdir);
- weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready);
}
- if (fire2)
- if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, true, 0.5)) {
+ if (fire & 2)
+ if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, weaponentity, true, 0.5)) {
M_Mage_Attack_Push();
- weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready);
}
}
if(washealed)
{
setanim(self, self.anim_shoot, true, true, true);
- self.attack_finished_single = time + (autocvar_g_monster_mage_heal_delay);
+ self.attack_finished_single[0] = time + (autocvar_g_monster_mage_heal_delay);
self.anim_finished = time + 1.5;
}
}
Send_Effect(EFFECT_TE_EXPLOSION, self.origin, '0 0 0', 1);
setanim(self, self.anim_shoot, true, true, true);
- self.attack_finished_single = time + (autocvar_g_monster_mage_attack_push_delay);
+ self.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_push_delay);
}
void M_Mage_Attack_Teleport()
self.fixangle = true;
self.velocity *= 0.5;
- self.attack_finished_single = time + 0.2;
+ self.attack_finished_single[0] = time + 0.2;
}
void M_Mage_Defend_Shield_Remove()
self.armorvalue = (autocvar_g_monster_mage_shield_blockpercent);
self.mage_shield_time = time + (autocvar_g_monster_mage_shield_time);
setanim(self, self.anim_shoot, true, true, true);
- self.attack_finished_single = time + 1;
+ self.attack_finished_single[0] = time + 1;
self.anim_finished = time + 1;
}
float M_Mage_Attack(float attack_type, entity targ)
{SELFPARAM();
+ .entity weaponentity = weaponentities[0];
switch(attack_type)
{
case MONSTER_ATTACK_MELEE:
if(random() <= 0.7)
{
Weapon wep = WEP_MAGE_SPIKE;
- wep.wr_think(wep, self, false, true);
+
+ wep.wr_think(wep, self, weaponentity, 2);
return true;
}
else
{
setanim(self, self.anim_shoot, true, true, true);
- self.attack_finished_single = time + (autocvar_g_monster_mage_attack_spike_delay);
+ self.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_spike_delay);
self.anim_finished = time + 1;
Weapon wep = WEP_MAGE_SPIKE;
- wep.wr_think(wep, self, true, false);
+ wep.wr_think(wep, self, weaponentity, 1);
return true;
}
}
}
if(self.health < (autocvar_g_monster_mage_heal_minhealth) || need_help)
- if(time >= self.attack_finished_single)
+ if(time >= self.attack_finished_single[0])
if(random() < 0.5)
M_Mage_Defend_Heal();
#define SHAMBLER_H
#ifndef MENUQC
-MODEL(MON_SHAMBLER, "models/monsters/shambler.mdl");
+MODEL(MON_SHAMBLER, M_Model("shambler.mdl"));
#endif
CLASS(Shambler, Monster)
if(r && Monster_Attack_Melee(self.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((r) ? self.anim_melee2 : self.anim_melee3), self.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW.m_id, true))
{
Monster_Delay(1, 0, 0.5, M_Shambler_Attack_Swing);
- self.attack_finished_single += 0.5;
- self.anim_finished = self.attack_finished_single;
+ self.attack_finished_single[0] += 0.5;
+ self.anim_finished = self.attack_finished_single[0];
}
}
-#include "../../../server/csqceffects.qh"
+#include "../../effects/qc/all.qh"
void M_Shambler_Attack_Lightning_Explode()
{SELFPARAM();
monster_makevectors(self.enemy);
- gren = spawn ();
+ gren = new(grenade);
gren.owner = gren.realowner = self;
- gren.classname = "grenade";
gren.bot_dodge = true;
gren.bot_dodgerating = (autocvar_g_monster_shambler_attack_lightning_damage);
gren.movetype = MOVETYPE_BOUNCE;
{
setanim(self, self.anim_melee2, true, true, false);
Monster_Delay(1, 0, 0.7, M_Shambler_Attack_Smash);
- self.attack_finished_single = time + 1.1;
+ self.attack_finished_single[0] = time + 1.1;
self.anim_finished = time + 1.1;
self.state = MONSTER_ATTACK_MELEE; // kinda a melee attack
self.shambler_lastattack = time + 3 + random() * 1.5;
{
setanim(self, self.anim_shoot, true, true, false);
self.state = MONSTER_ATTACK_MELEE; // maybe we should rename this to something more general
- self.attack_finished_single = time + 1.1;
+ self.attack_finished_single[0] = time + 1.1;
self.anim_finished = 1.1;
self.shambler_lastattack = time + 3 + random() * 1.5;
Monster_Delay(1, 0, 0.6, M_Shambler_Attack_Lightning);
#define SPIDER_H
#ifndef MENUQC
-MODEL(MON_SPIDER, "models/monsters/spider.dpm");
+MODEL(MON_SPIDER, M_Model("spider.dpm"));
#endif
CLASS(Spider, Monster)
/* flags */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(SpiderAttack, impulse, int, 9);
/* refname */ ATTRIB(SpiderAttack, netname, string, "spider");
-/* wepname */ ATTRIB(SpiderAttack, message, string, _("Spider attack"));
+/* wepname */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack"));
ENDCLASS(SpiderAttack)
REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack));
void M_Spider_Attack_Web();
-METHOD(SpiderAttack, wr_think, void(SpiderAttack thiswep, entity actor, bool fire1, bool fire2)) {
+SOUND(SpiderAttack_FIRE, W_Sound("electro_fire"));
+METHOD(SpiderAttack, wr_think, void(SpiderAttack thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if ((!isPlayer && time >= actor.spider_web_delay) || weapon_prepareattack(thiswep, actor, false, autocvar_g_monster_spider_attack_web_delay)) {
+ if (fire & 1)
+ if ((!isPlayer && time >= actor.spider_web_delay) || weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_monster_spider_attack_web_delay)) {
if (!isPlayer) {
actor.spider_web_delay = time + 3;
setanim(actor, actor.anim_shoot, true, true, true);
- actor.attack_finished_single = time + (autocvar_g_monster_spider_attack_web_delay);
+ actor.attack_finished_single[0] = time + (autocvar_g_monster_spider_attack_web_delay);
actor.anim_finished = time + 1;
}
if (isPlayer) actor.enemy = Monster_FindTarget(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(SpiderAttack_FIRE), CH_WEAPON_B, 0);
if (!isPlayer) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
M_Spider_Attack_Web();
- weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready);
return;
}
- if (fire2)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, true, 0.5)) {
+ if (fire & 2)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, true, 0.5)) {
if (isPlayer) {
actor.enemy = Monster_FindTarget(actor);
actor.attack_range = 60;
}
Monster_Attack_Melee(actor.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? self.anim_melee : self.anim_shoot), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER.m_id, true);
- weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready);
}
}
sound(self, CH_SHOTS, SND_ELECTRO_FIRE2, VOL_BASE, ATTEN_NORM);
- entity proj = spawn ();
- proj.classname = "plasma";
+ entity proj = new(plasma);
proj.owner = proj.realowner = self;
proj.use = M_Spider_Attack_Web_Explode;
proj.think = adaptor_think2use_hittype_splash;
bool M_Spider_Attack(int attack_type, entity targ)
{SELFPARAM();
+ .entity weaponentity = weaponentities[0];
switch(attack_type)
{
Weapon wep = WEP_SPIDER_ATTACK;
case MONSTER_ATTACK_MELEE:
{
- wep.wr_think(wep, self, false, true);
+ wep.wr_think(wep, self, weaponentity, 2);
return true;
}
case MONSTER_ATTACK_RANGED:
{
- wep.wr_think(wep, self, true, false);
+ wep.wr_think(wep, self, weaponentity, 1);
return true;
}
}
#define WYVERN_H
#ifndef MENUQC
-MODEL(MON_WYVERN, "models/monsters/wizard.mdl");
+MODEL(MON_WYVERN, M_Model("wizard.mdl"));
#endif
CLASS(Wyvern, Monster)
/* flags */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(WyvernAttack, impulse, int, 9);
/* refname */ ATTRIB(WyvernAttack, netname, string, "wyvern");
-/* wepname */ ATTRIB(WyvernAttack, message, string, _("Wyvern attack"));
+/* wepname */ ATTRIB(WyvernAttack, m_name, string, _("Wyvern attack"));
ENDCLASS(WyvernAttack)
REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack));
void M_Wyvern_Attack_Fireball_Explode();
void M_Wyvern_Attack_Fireball_Touch();
-METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, bool fire1, bool fire2)) {
- if (fire1)
- if (time > actor.attack_finished_single || weapon_prepareattack(thiswep, actor, false, 1.2)) {
- if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+SOUND(WyvernAttack_FIRE, W_Sound("electro_fire"));
+METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, .entity weaponentity, int fire)) {
+ if (fire & 1)
+ if (time > actor.attack_finished_single[0] || weapon_prepareattack(thiswep, actor, weaponentity, false, 1.2)) {
+ if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, v_forward, false, 0, SND(WyvernAttack_FIRE), CH_WEAPON_B, 0);
if (IS_MONSTER(actor)) {
- actor.attack_finished_single = time + 1.2;
+ actor.attack_finished_single[0] = time + 1.2;
actor.anim_finished = time + 1.2;
monster_makevectors(actor.enemy);
}
missile.touch = M_Wyvern_Attack_Fireball_Touch;
CSQCProjectile(missile, true, PROJECTILE_FIREMINE, true);
- weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready);
}
}
float M_Wyvern_Attack(float attack_type, entity targ)
{
SELFPARAM();
+ .entity weaponentity = weaponentities[0];
switch(attack_type)
{
case MONSTER_ATTACK_MELEE:
{
w_shotdir = normalize((self.enemy.origin + '0 0 10') - self.origin);
Weapon wep = WEP_WYVERN_ATTACK;
- wep.wr_think(wep, self, true, false);
+ wep.wr_think(wep, self, weaponentity, 1);
return true;
}
}
#define ZOMBIE_H
#ifndef MENUQC
-MODEL(MON_ZOMBIE, "models/monsters/zombie.dpm");
+MODEL(MON_ZOMBIE, M_Model("zombie.dpm"));
#endif
CLASS(Zombie, Monster)
{SELFPARAM();
self.armorvalue = 0.9;
self.state = MONSTER_ATTACK_MELEE; // freeze monster
- self.attack_finished_single = time + 2.1;
- self.anim_finished = self.attack_finished_single;
+ self.attack_finished_single[0] = time + 2.1;
+ self.anim_finished = self.attack_finished_single[0];
setanim(self, self.anim_blockstart, false, true, true);
Monster_Delay(1, 0, 2, M_Zombie_Defend_Block_End);
return;
vector org = self.origin + ((self.mins + self.maxs) * 0.5);
- entity e = spawn();
+ entity e = new(droppedweapon); // use weapon handling to remove it on touch
e.spawnfunc_checked = true;
e.monster_loot = self.monster_loot;
setorigin(e, org);
e.velocity = randomvec() * 175 + '0 0 325';
e.item_spawnshieldtime = time + 0.7;
- e.classname = "droppedweapon"; // use weapon handling to remove it on touch
SUB_SetFade(e, time + autocvar_g_monsters_drop_time, 1);
setself(this);
}
if(delaytoo)
if(time < self.msound_delay)
return; // too early
- GlobalSound(self.(samplefield), chan, VOICETYPE_PLAYERSOUND);
+ _GlobalSound(self.(samplefield), chan, VOICETYPE_PLAYERSOUND, false);
self.msound_delay = time + sound_delay;
}
setanim(self, anim, false, true, false);
if(self.animstate_endtime > time && (self.flags & FL_MONSTER))
- self.attack_finished_single = self.anim_finished = self.animstate_endtime;
+ self.attack_finished_single[0] = self.anim_finished = self.animstate_endtime;
else
- self.attack_finished_single = self.anim_finished = time + animtime;
+ self.attack_finished_single[0] = self.anim_finished = time + animtime;
monster_makevectors(targ);
return false; // not on the ground
if(self.health <= 0)
return false; // called when dead?
- if(time < self.attack_finished_single)
+ if(time < self.attack_finished_single[0])
return false; // still attacking
vector old = self.velocity;
setanim(self, anm, false, true, false);
if(self.animstate_endtime > time && (self.flags & FL_MONSTER))
- self.attack_finished_single = self.anim_finished = self.animstate_endtime;
+ self.attack_finished_single[0] = self.anim_finished = self.animstate_endtime;
else
- self.attack_finished_single = self.anim_finished = time + animtime;
+ self.attack_finished_single[0] = self.anim_finished = time + animtime;
if(self.flags & FL_MONSTER)
self.state = MONSTER_ATTACK_RANGED;
{
if((e == world || targ == world)
|| (!e.monster_attackfunc)
- || (time < e.attack_finished_single)
+ || (time < e.attack_finished_single[0])
) { return; }
float targ_vlen = vlen(targ.origin - e.origin);
self.touch = Monster_Touch;
}
- if(self.state && time >= self.attack_finished_single)
+ if(self.state && time >= self.attack_finished_single[0])
self.state = 0; // attack is over
if(self.state != MONSTER_ATTACK_MELEE) // don't move if set
void Monster_Remove(entity mon)
{
+ .entity weaponentity = weaponentities[0];
if(!mon) { return; }
if(!MUTATOR_CALLHOOK(MonsterRemove, mon))
Send_Effect(EFFECT_ITEM_PICKUP, mon.origin, '0 0 0', 1);
- if(mon.weaponentity) { remove(mon.weaponentity); }
+ if(mon.(weaponentity)) { remove(mon.(weaponentity)); }
if(mon.iceblock) { remove(mon.iceblock); }
WaypointSprite_Kill(mon.sprite);
remove(mon);
self.velocity = '0 0 0';
self.enemy = world;
self.goalentity = world;
- self.attack_finished_single = 0;
+ self.attack_finished_single[0] = 0;
self.moveto = self.origin;
}
self.touch = Monster_Touch; // reset incase monster was pouncing
self.reset = func_null;
self.state = 0;
- self.attack_finished_single = 0;
+ self.attack_finished_single[0] = 0;
self.effects = 0;
if(!((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
movelib_move_simple_gravity(v_forward, mspeed, 1);
if(time > self.pain_finished)
- if(time > self.attack_finished_single)
+ if(time > self.attack_finished_single[0])
if(vlen(self.velocity) > 10)
setanim(self, self.anim_walk, true, false, false);
else
self.movetype = MOVETYPE_FLY;
}
- if(mon.spawnflags & MONSTER_SIZE_BROKEN)
if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
- self.scale *= 1.3;
+ {
+ if(mon.spawnflags & MONSTER_SIZE_BROKEN)
+ self.scale *= 1.3;
+
+ if(mon.spawnflags & MONSTER_SIZE_QUAKE)
+ if(autocvar_g_monsters_quake_resize)
+ self.scale *= 1.3;
+ }
setsize(self, mon.mins * self.scale, mon.maxs * self.scale);
#define MOVETYPES_H
.float move_ltime;
-.void(void)move_think;
+.void()move_think;
.float move_nextthink;
-.void(void)move_blocked;
+.void()move_blocked;
.float move_movetype;
.float move_time;
.int move_flags;
.int move_watertype;
.int move_waterlevel;
-.void(void)move_touch;
+.void()move_touch;
.void(float, float)contentstransition;
.float move_bounce_factor;
.float move_bounce_stopspeed;
-#include "mutator/casings.qc"
-#include "mutator/damagetext.qc"
-#include "mutator/instagib/instagib.qc"
+#include "mutator/buffs/module.inc"
#include "mutator/itemstime.qc"
-#include "mutator/waypoints/waypointsprites.qc"
+#include "mutator/multijump/module.inc"
+#include "mutator/nades/module.inc"
+#include "mutator/superspec/module.inc"
+#include "mutator/waypoints/module.inc"
+
+// completely self contained
+
+#include "mutator/bloodloss/module.inc"
+#include "mutator/breakablehook/module.inc"
+#include "mutator/campcheck/module.inc"
+#include "mutator/cloaked/module.inc"
+#include "mutator/damagetext/module.inc"
+#include "mutator/dodging/module.inc"
+#include "mutator/hook/module.inc"
+#include "mutator/instagib/module.inc"
+#include "mutator/invincibleproj/module.inc"
+#include "mutator/melee_only/module.inc"
+#include "mutator/midair/module.inc"
+#include "mutator/new_toys/module.inc"
+#include "mutator/nix/module.inc"
+#include "mutator/overkill/module.inc"
+#include "mutator/physical_items/module.inc"
+#include "mutator/pinata/module.inc"
+#include "mutator/random_gravity/module.inc"
+#include "mutator/rocketflying/module.inc"
+#include "mutator/rocketminsta/module.inc"
+#include "mutator/running_guns/module.inc"
+#include "mutator/sandbox/module.inc"
+#include "mutator/spawn_near_teammate/module.inc"
+#include "mutator/touchexplode/module.inc"
+#include "mutator/vampirehook/module.inc"
+#include "mutator/vampire/module.inc"
+#include "mutator/weaponarena_random/module.inc"
}
ENDCLASS(Mutator)
-REGISTRY(Mutators, BITS(6))
+REGISTRY(Mutators, BITS(7))
+#define Mutators_from(i) _Mutators_from(i, NULL)
Mutator loaded_mutators[Mutators_MAX];
bool Mutator_Add(Mutator mut)
_(x, string) \
/**/
-#define MUTATOR_NEWGLOBAL(x, type) type mutator_argv_##type##_##x;
+#define MUTATOR_ARGV(x, type) MUTATOR_ARGV_##x##_##type
+#define MUTATOR_NEWGLOBAL(x, type) type MUTATOR_ARGV(x, type);
MUTATOR_TYPES(MUTATOR_NEWGLOBAL, 0)
MUTATOR_TYPES(MUTATOR_NEWGLOBAL, 1)
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss"));
+
+.float bloodloss_timer;
+
+MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink)
+{SELFPARAM();
+ if(IS_PLAYER(self))
+ if(self.health <= autocvar_g_bloodloss && self.deadflag == DEAD_NO)
+ {
+ self.BUTTON_CROUCH = true;
+
+ if(time >= self.bloodloss_timer)
+ {
+ if(self.vehicle)
+ vehicles_exit(VHEF_RELEASE);
+ if(self.event_damage)
+ self.event_damage(self, self, 1, DEATH_ROT.m_id, self.origin, '0 0 0');
+ self.bloodloss_timer = time + 0.5 + random() * 0.5;
+ }
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump)
+{SELFPARAM();
+ if(self.health <= autocvar_g_bloodloss)
+ return true;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":bloodloss");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Blood loss");
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "bloodloss.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+#include "../../../deathtypes/all.qh"
+#include "../../../../server/g_hook.qh"
+
+REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook"));
+
+bool autocvar_g_breakablehook; // allow toggling mid match?
+bool autocvar_g_breakablehook_owner;
+
+MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate)
+{
+ if(frag_target.classname == "grapplinghook")
+ {
+ if((!autocvar_g_breakablehook)
+ || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner)
+ ) { frag_damage = 0; }
+
+ // hurt the owner of the hook
+ if(DIFF_TEAM(frag_attacker, frag_target.realowner))
+ {
+ Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0');
+ RemoveGrapplingHook(frag_target.realowner);
+ return false; // dead
+ }
+ }
+
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "breakablehook.qc"
+#endif
--- /dev/null
+#ifndef MUTATOR_BUFFS_H
+#define MUTATOR_BUFFS_H
+
+#include "../instagib/module.inc"
+
+bool autocvar_g_buffs_effects;
+float autocvar_g_buffs_waypoint_distance;
+bool autocvar_g_buffs_randomize;
+float autocvar_g_buffs_random_lifetime;
+bool autocvar_g_buffs_random_location;
+int autocvar_g_buffs_random_location_attempts;
+int autocvar_g_buffs_spawn_count;
+bool autocvar_g_buffs_replace_powerups;
+float autocvar_g_buffs_cooldown_activate;
+float autocvar_g_buffs_cooldown_respawn;
+float autocvar_g_buffs_resistance_blockpercent;
+float autocvar_g_buffs_medic_survive_chance;
+float autocvar_g_buffs_medic_survive_health;
+float autocvar_g_buffs_medic_rot;
+float autocvar_g_buffs_medic_max;
+float autocvar_g_buffs_medic_regen;
+float autocvar_g_buffs_vengeance_damage_multiplier;
+float autocvar_g_buffs_bash_force;
+float autocvar_g_buffs_bash_force_self;
+float autocvar_g_buffs_disability_slowtime;
+float autocvar_g_buffs_disability_speed;
+float autocvar_g_buffs_disability_rate;
+float autocvar_g_buffs_disability_weaponspeed;
+float autocvar_g_buffs_speed_speed;
+float autocvar_g_buffs_speed_rate;
+float autocvar_g_buffs_speed_weaponspeed;
+float autocvar_g_buffs_speed_damage_take;
+float autocvar_g_buffs_speed_regen;
+float autocvar_g_buffs_vampire_damage_steal;
+float autocvar_g_buffs_invisible_alpha;
+float autocvar_g_buffs_flight_gravity;
+float autocvar_g_buffs_jump_height;
+float autocvar_g_buffs_inferno_burntime_factor;
+float autocvar_g_buffs_inferno_burntime_min_time;
+float autocvar_g_buffs_inferno_burntime_target_damage;
+float autocvar_g_buffs_inferno_burntime_target_time;
+float autocvar_g_buffs_inferno_damagemultiplier;
+float autocvar_g_buffs_swapper_range;
+float autocvar_g_buffs_magnet_range_item;
+
+// ammo
+.float buff_ammo_prev_infitems;
+.int buff_ammo_prev_clipload;
+// invisible
+.float buff_invisible_prev_alpha;
+// flight
+.float buff_flight_prev_gravity;
+// disability
+.float buff_disability_time;
+.float buff_disability_effect_time;
+// common buff variables
+.float buff_effect_delay;
+
+// buff definitions
+.float buff_active;
+.float buff_activetime;
+.float buff_activetime_updated;
+.entity buff_waypoint;
+.int oldbuffs; // for updating effects
+.entity buff_model; // controls effects (TODO: make csqc)
+
+const vector BUFF_MIN = ('-16 -16 -20');
+const vector BUFF_MAX = ('16 16 20');
+
+// client side options
+.float cvar_cl_buffs_autoreplace;
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "../../../triggers/target/music.qh"
+#include "../../../gamemodes/all.qh"
+#include "../../../buffs/all.qh"
+
+.float buff_time;
+void buffs_DelayedInit();
+
+REGISTER_MUTATOR(buffs, cvar("g_buffs"))
+{
+ MUTATOR_ONADD
+ {
+ addstat(STAT_BUFFS, AS_INT, buffs);
+ addstat(STAT_BUFF_TIME, AS_FLOAT, buff_time);
+
+ InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET);
+ }
+}
+
+entity buff_FirstFromFlags(int _buffs)
+{
+ if (flags)
+ {
+ FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it));
+ }
+ return BUFF_Null;
+}
+
+bool buffs_BuffModel_Customize()
+{SELFPARAM();
+ entity player, myowner;
+ bool same_team;
+
+ player = WaypointSprite_getviewentity(other);
+ myowner = self.owner;
+ same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner));
+
+ if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0)
+ return false;
+
+ if(MUTATOR_CALLHOOK(BuffModel_Customize, self, player))
+ return false;
+
+ if(player == myowner || (IS_SPEC(other) && other.enemy == myowner))
+ {
+ // somewhat hide the model, but keep the glow
+ self.effects = 0;
+ self.alpha = -1;
+ }
+ else
+ {
+ self.effects = EF_FULLBRIGHT | EF_LOWPRECISION;
+ self.alpha = 1;
+ }
+ return true;
+}
+
+void buffs_BuffModel_Spawn(entity player)
+{
+ player.buff_model = spawn();
+ setmodel(player.buff_model, MDL_BUFF);
+ setsize(player.buff_model, '0 0 -40', '0 0 40');
+ setattachment(player.buff_model, player, "");
+ setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1));
+ player.buff_model.owner = player;
+ player.buff_model.scale = 0.7;
+ player.buff_model.pflags = PFLAGS_FULLDYNAMIC;
+ player.buff_model.light_lev = 200;
+ player.buff_model.customizeentityforclient = buffs_BuffModel_Customize;
+}
+
+vector buff_GlowColor(entity buff)
+{
+ //if(buff.team) { return Team_ColorRGB(buff.team); }
+ return buff.m_color;
+}
+
+void buff_Effect(entity player, string eff)
+{SELFPARAM();
+ if(!autocvar_g_buffs_effects) { return; }
+
+ if(time >= self.buff_effect_delay)
+ {
+ Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1);
+ self.buff_effect_delay = time + 0.05; // prevent spam
+ }
+}
+
+// buff item
+float buff_Waypoint_visible_for_player(entity plr)
+{SELFPARAM();
+ if(!self.owner.buff_active && !self.owner.buff_activetime)
+ return false;
+
+ if (plr.buffs)
+ {
+ return plr.cvar_cl_buffs_autoreplace == false || plr.buffs != self.owner.buffs;
+ }
+
+ return WaypointSprite_visible_for_player(plr);
+}
+
+void buff_Waypoint_Spawn(entity e)
+{
+ entity buff = buff_FirstFromFlags(e.buffs);
+ entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, world, e.team, e, buff_waypoint, true, RADARICON_Buff);
+ wp.wp_extra = buff.m_id;
+ WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod);
+ e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player;
+}
+
+void buff_SetCooldown(float cd)
+{SELFPARAM();
+ cd = max(0, cd);
+
+ if(!self.buff_waypoint)
+ buff_Waypoint_Spawn(self);
+
+ WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + cd);
+ self.buff_activetime = cd;
+ self.buff_active = !cd;
+}
+
+void buff_Respawn(entity ent)
+{SELFPARAM();
+ if(gameover) { return; }
+
+ vector oldbufforigin = ent.origin;
+
+ if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256))
+ {
+ entity spot = SelectSpawnPoint(true);
+ setorigin(ent, spot.origin + '0 0 200');
+ ent.angles = spot.angles;
+ }
+
+ tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent);
+
+ setorigin(ent, trace_endpos); // attempt to unstick
+
+ ent.movetype = MOVETYPE_TOSS;
+
+ makevectors(ent.angles);
+ ent.velocity = '0 0 200';
+ ent.angles = '0 0 0';
+ if(autocvar_g_buffs_random_lifetime > 0)
+ ent.lifetime = time + autocvar_g_buffs_random_lifetime;
+
+ Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1);
+ Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(ent), '0 0 0', 1);
+
+ WaypointSprite_Ping(ent.buff_waypoint);
+
+ sound(ent, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+}
+
+void buff_Touch()
+{SELFPARAM();
+ if(gameover) { return; }
+
+ if(ITEM_TOUCH_NEEDKILL())
+ {
+ buff_Respawn(self);
+ return;
+ }
+
+ if((self.team && DIFF_TEAM(other, self))
+ || (other.frozen)
+ || (other.vehicle)
+ || (!self.buff_active)
+ )
+ {
+ // can't touch this
+ return;
+ }
+
+ if(MUTATOR_CALLHOOK(BuffTouch, self, other))
+ return;
+
+ if(!IS_PLAYER(other))
+ return; // incase mutator changed other
+
+ if (other.buffs)
+ {
+ if (other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs)
+ {
+ int buffid = buff_FirstFromFlags(other.buffs).m_id;
+ //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, buffid);
+
+ other.buffs = 0;
+ //sound(other, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
+ }
+ else { return; } // do nothing
+ }
+
+ self.owner = other;
+ self.buff_active = false;
+ self.lifetime = 0;
+ int buffid = buff_FirstFromFlags(self.buffs).m_id;
+ Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, buffid);
+ Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, buffid);
+
+ Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+ sound(other, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM);
+ other.buffs |= (self.buffs);
+}
+
+float buff_Available(entity buff)
+{
+ if (buff == BUFF_Null)
+ return false;
+ if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only"))))
+ return false;
+ if (buff == BUFF_VAMPIRE && cvar("g_vampire"))
+ return false;
+ return cvar(strcat("g_buffs_", buff.m_name));
+}
+
+.int buff_seencount;
+
+void buff_NewType(entity ent, float cb)
+{
+ RandomSelection_Init();
+ FOREACH(Buffs, buff_Available(it), LAMBDA(
+ it.buff_seencount += 1;
+ // if it's already been chosen, give it a lower priority
+ RandomSelection_Add(world, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount));
+ ));
+ ent.buffs = RandomSelection_chosen_float;
+}
+
+void buff_Think()
+{SELFPARAM();
+ if(self.buffs != self.oldbuffs)
+ {
+ entity buff = buff_FirstFromFlags(self.buffs);
+ self.color = buff.m_color;
+ self.glowmod = buff_GlowColor(buff);
+ self.skin = buff.m_skin;
+
+ setmodel(self, MDL_BUFF);
+
+ if(self.buff_waypoint)
+ {
+ //WaypointSprite_Disown(self.buff_waypoint, 1);
+ WaypointSprite_Kill(self.buff_waypoint);
+ buff_Waypoint_Spawn(self);
+ if(self.buff_activetime)
+ WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + self.buff_activetime - frametime);
+ }
+
+ self.oldbuffs = self.buffs;
+ }
+
+ if(!gameover)
+ if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+ if(!self.buff_activetime_updated)
+ {
+ buff_SetCooldown(self.buff_activetime);
+ self.buff_activetime_updated = true;
+ }
+
+ if(!self.buff_active && !self.buff_activetime)
+ if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
+ {
+ buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime);
+ self.owner = world;
+ if(autocvar_g_buffs_randomize)
+ buff_NewType(self, self.buffs);
+
+ if(autocvar_g_buffs_random_location || (self.spawnflags & 64))
+ buff_Respawn(self);
+ }
+
+ if(self.buff_activetime)
+ if(!gameover)
+ if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+ {
+ self.buff_activetime = max(0, self.buff_activetime - frametime);
+
+ if(!self.buff_activetime)
+ {
+ self.buff_active = true;
+ sound(self, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM);
+ Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+ }
+ }
+
+ if(self.buff_active)
+ {
+ if(self.team && !self.buff_waypoint)
+ buff_Waypoint_Spawn(self);
+
+ if(self.lifetime)
+ if(time >= self.lifetime)
+ buff_Respawn(self);
+ }
+
+ self.nextthink = time;
+ //self.angles_y = time * 110.1;
+}
+
+void buff_Waypoint_Reset()
+{SELFPARAM();
+ WaypointSprite_Kill(self.buff_waypoint);
+
+ if(self.buff_activetime) { buff_Waypoint_Spawn(self); }
+}
+
+void buff_Reset()
+{SELFPARAM();
+ if(autocvar_g_buffs_randomize)
+ buff_NewType(self, self.buffs);
+ self.owner = world;
+ buff_SetCooldown(autocvar_g_buffs_cooldown_activate);
+ buff_Waypoint_Reset();
+ self.buff_activetime_updated = false;
+
+ if(autocvar_g_buffs_random_location || (self.spawnflags & 64))
+ buff_Respawn(self);
+}
+
+float buff_Customize()
+{SELFPARAM();
+ entity player = WaypointSprite_getviewentity(other);
+ if(!self.buff_active || (self.team && DIFF_TEAM(player, self)))
+ {
+ self.alpha = 0.3;
+ if(self.effects & EF_FULLBRIGHT) { self.effects &= ~(EF_FULLBRIGHT); }
+ self.pflags = 0;
+ }
+ else
+ {
+ self.alpha = 1;
+ if(!(self.effects & EF_FULLBRIGHT)) { self.effects |= EF_FULLBRIGHT; }
+ self.light_lev = 220 + 36 * sin(time);
+ self.pflags = PFLAGS_FULLDYNAMIC;
+ }
+ return true;
+}
+
+void buff_Init(entity ent)
+{SELFPARAM();
+ if(!cvar("g_buffs")) { remove(ent); return; }
+
+ if(!teamplay && ent.team) { ent.team = 0; }
+
+ entity buff = buff_FirstFromFlags(self.buffs);
+
+ setself(ent);
+ if(!self.buffs || buff_Available(buff))
+ buff_NewType(self, 0);
+
+ self.classname = "item_buff";
+ self.solid = SOLID_TRIGGER;
+ self.flags = FL_ITEM;
+ self.think = buff_Think;
+ self.touch = buff_Touch;
+ self.reset = buff_Reset;
+ self.nextthink = time + 0.1;
+ self.gravity = 1;
+ self.movetype = MOVETYPE_TOSS;
+ self.scale = 1;
+ self.skin = buff.m_skin;
+ self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW;
+ self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
+ self.customizeentityforclient = buff_Customize;
+ //self.gravity = 100;
+ self.color = buff.m_color;
+ self.glowmod = buff_GlowColor(self);
+ buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime);
+ self.buff_active = !self.buff_activetime;
+ self.pflags = PFLAGS_FULLDYNAMIC;
+
+ if(self.spawnflags & 1)
+ self.noalign = true;
+
+ if(self.noalign)
+ self.movetype = MOVETYPE_NONE; // reset by random location
+
+ setmodel(self, MDL_BUFF);
+ setsize(self, BUFF_MIN, BUFF_MAX);
+
+ if(cvar("g_buffs_random_location") || (self.spawnflags & 64))
+ buff_Respawn(self);
+
+ setself(this);
+}
+
+void buff_Init_Compat(entity ent, entity replacement)
+{
+ if (ent.spawnflags & 2)
+ ent.team = NUM_TEAM_1;
+ else if (ent.spawnflags & 4)
+ ent.team = NUM_TEAM_2;
+
+ ent.buffs = replacement.m_itemid;
+
+ buff_Init(ent);
+}
+
+void buff_SpawnReplacement(entity ent, entity old)
+{
+ setorigin(ent, old.origin);
+ ent.angles = old.angles;
+ ent.noalign = (old.noalign || (old.spawnflags & 1));
+
+ buff_Init(ent);
+}
+
+void buff_Vengeance_DelayedDamage()
+{SELFPARAM();
+ if(self.enemy)
+ Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF.m_id, self.enemy.origin, '0 0 0');
+
+ remove(self);
+ return;
+}
+
+float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base)
+{
+ return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base);
+}
+
+// mutator hooks
+MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor)
+{
+ if(frag_deathtype == DEATH_BUFF.m_id) { return false; }
+
+ if(frag_target.buffs & BUFF_RESISTANCE.m_itemid)
+ {
+ vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage);
+ damage_take = v.x;
+ damage_save = v.y;
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate)
+{
+ if(frag_deathtype == DEATH_BUFF.m_id) { return false; }
+
+ if(frag_target.buffs & BUFF_SPEED.m_itemid)
+ if(frag_target != frag_attacker)
+ frag_damage *= autocvar_g_buffs_speed_damage_take;
+
+ if(frag_target.buffs & BUFF_MEDIC.m_itemid)
+ if((frag_target.health - frag_damage) <= 0)
+ if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+ if(frag_attacker)
+ if(random() <= autocvar_g_buffs_medic_survive_chance)
+ frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health);
+
+ if(frag_target.buffs & BUFF_JUMP.m_itemid)
+ if(frag_deathtype == DEATH_FALL.m_id)
+ frag_damage = 0;
+
+ if(frag_target.buffs & BUFF_VENGEANCE.m_itemid)
+ if(frag_attacker)
+ if(frag_attacker != frag_target)
+ if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+ {
+ entity dmgent = spawn();
+
+ dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier;
+ dmgent.enemy = frag_attacker;
+ dmgent.owner = frag_target;
+ dmgent.think = buff_Vengeance_DelayedDamage;
+ dmgent.nextthink = time + 0.1;
+ }
+
+ if(frag_target.buffs & BUFF_BASH.m_itemid)
+ if(frag_attacker != frag_target)
+ if(vlen(frag_force))
+ frag_force = '0 0 0';
+
+ if(frag_attacker.buffs & BUFF_BASH.m_itemid)
+ if(vlen(frag_force))
+ if(frag_attacker == frag_target)
+ frag_force *= autocvar_g_buffs_bash_force_self;
+ else
+ frag_force *= autocvar_g_buffs_bash_force;
+
+ if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid)
+ if(frag_target != frag_attacker)
+ frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime;
+
+ if(frag_attacker.buffs & BUFF_MEDIC.m_itemid)
+ if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC)
+ if(SAME_TEAM(frag_attacker, frag_target))
+ if(frag_attacker != frag_target)
+ {
+ frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage);
+ frag_damage = 0;
+ }
+
+ if(frag_attacker.buffs & BUFF_INFERNO.m_itemid)
+ if(frag_target != frag_attacker) {
+ float time = buff_Inferno_CalculateTime(
+ frag_damage,
+ 0,
+ autocvar_g_buffs_inferno_burntime_min_time,
+ autocvar_g_buffs_inferno_burntime_target_damage,
+ autocvar_g_buffs_inferno_burntime_target_time,
+ autocvar_g_buffs_inferno_burntime_factor
+ );
+ Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier) * time, time, DEATH_BUFF.m_id);
+ }
+
+ // this... is ridiculous (TODO: fix!)
+ if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid)
+ if(!frag_target.vehicle)
+ if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC)
+ if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+ if(frag_target.deadflag == DEAD_NO)
+ if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target))
+ if(frag_attacker != frag_target)
+ if(!frag_target.frozen)
+ if(frag_target.takedamage)
+ if(DIFF_TEAM(frag_attacker, frag_target))
+ {
+ frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max);
+ if(frag_target.armorvalue)
+ frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max);
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs,PlayerSpawn)
+{SELFPARAM();
+ self.buffs = 0;
+ // reset timers here to prevent them continuing after re-spawn
+ self.buff_disability_time = 0;
+ self.buff_disability_effect_time = 0;
+ return false;
+}
+
+.float stat_sv_maxspeed;
+.float stat_sv_airspeedlimit_nonqw;
+.float stat_sv_jumpvelocity;
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics)
+{SELFPARAM();
+ if(self.buffs & BUFF_SPEED.m_itemid)
+ {
+ self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed;
+ self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed;
+ }
+
+ if(time < self.buff_disability_time)
+ {
+ self.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed;
+ self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed;
+ }
+
+ if(self.buffs & BUFF_JUMP.m_itemid)
+ {
+ // automatically reset, no need to worry
+ self.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height;
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerJump)
+{SELFPARAM();
+ if(self.buffs & BUFF_JUMP.m_itemid)
+ player_jumpheight = autocvar_g_buffs_jump_height;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
+{SELFPARAM();
+ if(time < self.buff_disability_time)
+ {
+ monster_speed_walk *= autocvar_g_buffs_disability_speed;
+ monster_speed_run *= autocvar_g_buffs_disability_speed;
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerDies)
+{SELFPARAM();
+ if(self.buffs)
+ {
+ int buffid = buff_FirstFromFlags(self.buffs).m_id;
+ Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid);
+ self.buffs = 0;
+
+ if(self.buff_model)
+ {
+ remove(self.buff_model);
+ self.buff_model = world;
+ }
+ }
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST)
+{SELFPARAM();
+ if(MUTATOR_RETURNVALUE || gameover) { return false; }
+ if(self.buffs)
+ {
+ int buffid = buff_FirstFromFlags(self.buffs).m_id;
+ Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid);
+ Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid);
+
+ self.buffs = 0;
+ sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
+ return true;
+ }
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon)
+{SELFPARAM();
+ if(MUTATOR_RETURNVALUE || gameover) { return false; }
+
+ if(self.buffs & BUFF_SWAPPER.m_itemid)
+ {
+ float best_distance = autocvar_g_buffs_swapper_range;
+ entity closest = world;
+ entity player;
+ FOR_EACH_PLAYER(player)
+ if(DIFF_TEAM(self, player))
+ if(player.deadflag == DEAD_NO && !player.frozen && !player.vehicle)
+ if(vlen(self.origin - player.origin) <= best_distance)
+ {
+ best_distance = vlen(self.origin - player.origin);
+ closest = player;
+ }
+
+ if(closest)
+ {
+ vector my_org, my_vel, my_ang, their_org, their_vel, their_ang;
+
+ my_org = self.origin;
+ my_vel = self.velocity;
+ my_ang = self.angles;
+ their_org = closest.origin;
+ their_vel = closest.velocity;
+ their_ang = closest.angles;
+
+ Drop_Special_Items(closest);
+
+ MUTATOR_CALLHOOK(PortalTeleport, self); // initiate flag dropper
+
+ setorigin(self, their_org);
+ setorigin(closest, my_org);
+
+ closest.velocity = my_vel;
+ closest.angles = my_ang;
+ closest.fixangle = true;
+ closest.oldorigin = my_org;
+ closest.oldvelocity = my_vel;
+ self.velocity = their_vel;
+ self.angles = their_ang;
+ self.fixangle = true;
+ self.oldorigin = their_org;
+ self.oldvelocity = their_vel;
+
+ // set pusher so self gets the kill if they fall into void
+ closest.pusher = self;
+ closest.pushltime = time + autocvar_g_maxpushtime;
+ closest.istypefrag = closest.BUTTON_CHAT;
+
+ Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1);
+ Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1);
+
+ sound(self, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM);
+ sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM);
+
+ // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam
+ self.buffs = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool buffs_RemovePlayer(entity player)
+{
+ if(player.buff_model)
+ {
+ remove(player.buff_model);
+ player.buff_model = world;
+ }
+
+ // also reset timers here to prevent them continuing after spectating
+ player.buff_disability_time = 0;
+ player.buff_disability_effect_time = 0;
+
+ return false;
+}
+MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { return buffs_RemovePlayer(self); }
+MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { return buffs_RemovePlayer(self); }
+
+MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint)
+{SELFPARAM();
+ entity e = WaypointSprite_getviewentity(other);
+
+ // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
+ // but only apply this to real players, not to spectators
+ if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == other))
+ if(DIFF_TEAM(self.owner, e))
+ return true;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST)
+{SELFPARAM();
+ if(autocvar_g_buffs_replace_powerups)
+ switch(self.classname)
+ {
+ case "item_strength":
+ case "item_invincible":
+ {
+ entity e = spawn();
+ buff_SpawnReplacement(e, self);
+ return true;
+ }
+ }
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor)
+{SELFPARAM();
+ if(self.buffs & BUFF_SPEED.m_itemid)
+ weapon_rate *= autocvar_g_buffs_speed_rate;
+
+ if(time < self.buff_disability_time)
+ weapon_rate *= autocvar_g_buffs_disability_rate;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor)
+{SELFPARAM();
+ if(self.buffs & BUFF_SPEED.m_itemid)
+ ret_float *= autocvar_g_buffs_speed_weaponspeed;
+
+ if(time < self.buff_disability_time)
+ ret_float *= autocvar_g_buffs_disability_weaponspeed;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
+{SELFPARAM();
+ if(gameover || self.deadflag != DEAD_NO) { return false; }
+
+ if(time < self.buff_disability_time)
+ if(time >= self.buff_disability_effect_time)
+ {
+ Send_Effect(EFFECT_SMOKING, self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1);
+ self.buff_disability_effect_time = time + 0.5;
+ }
+
+ // handle buff lost status
+ // 1: notify everyone else
+ // 2: notify carrier as well
+ int buff_lost = 0;
+
+ if(self.buff_time)
+ if(time >= self.buff_time)
+ buff_lost = 2;
+
+ if(self.frozen) { buff_lost = 1; }
+
+ if(buff_lost)
+ {
+ if(self.buffs)
+ {
+ int buffid = buff_FirstFromFlags(self.buffs).m_id;
+ Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid);
+ if(buff_lost >= 2)
+ {
+ Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message?
+ sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
+ }
+ self.buffs = 0;
+ }
+ }
+
+ if(self.buffs & BUFF_MAGNET.m_itemid)
+ {
+ vector pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item;
+ for(other = world; (other = findflags(other, flags, FL_ITEM)); )
+ if(boxesoverlap(self.absmin - pickup_size, self.absmax + pickup_size, other.absmin, other.absmax))
+ {
+ setself(other);
+ other = this;
+ if(self.touch)
+ self.touch();
+ other = self;
+ setself(this);
+ }
+ }
+
+ if(self.buffs & BUFF_AMMO.m_itemid)
+ if(self.clip_size)
+ self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size;
+
+ if((self.buffs & BUFF_INVISIBLE.m_itemid) && (self.oldbuffs & BUFF_INVISIBLE.m_itemid))
+ if(self.alpha != autocvar_g_buffs_invisible_alpha)
+ self.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO)
+
+#define BUFF_ONADD(b) if ( (self.buffs & (b).m_itemid) && !(self.oldbuffs & (b).m_itemid))
+#define BUFF_ONREM(b) if (!(self.buffs & (b).m_itemid) && (self.oldbuffs & (b).m_itemid))
+
+ if(self.buffs != self.oldbuffs)
+ {
+ entity buff = buff_FirstFromFlags(self.buffs);
+ float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0;
+ self.buff_time = (bufftime) ? time + bufftime : 0;
+
+ BUFF_ONADD(BUFF_AMMO)
+ {
+ self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO);
+ self.items |= IT_UNLIMITED_WEAPON_AMMO;
+
+ if(self.clip_load)
+ self.buff_ammo_prev_clipload = self.clip_load;
+ self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size;
+ }
+
+ BUFF_ONREM(BUFF_AMMO)
+ {
+ if(self.buff_ammo_prev_infitems)
+ self.items |= IT_UNLIMITED_WEAPON_AMMO;
+ else
+ self.items &= ~IT_UNLIMITED_WEAPON_AMMO;
+
+ if(self.buff_ammo_prev_clipload)
+ self.clip_load = self.buff_ammo_prev_clipload;
+ }
+
+ BUFF_ONADD(BUFF_INVISIBLE)
+ {
+ if(time < self.strength_finished && g_instagib)
+ self.alpha = autocvar_g_instagib_invis_alpha;
+ else
+ self.alpha = self.buff_invisible_prev_alpha;
+ self.alpha = autocvar_g_buffs_invisible_alpha;
+ }
+
+ BUFF_ONREM(BUFF_INVISIBLE)
+ self.alpha = self.buff_invisible_prev_alpha;
+
+ BUFF_ONADD(BUFF_FLIGHT)
+ {
+ self.buff_flight_prev_gravity = self.gravity;
+ self.gravity = autocvar_g_buffs_flight_gravity;
+ }
+
+ BUFF_ONREM(BUFF_FLIGHT)
+ self.gravity = self.buff_flight_prev_gravity;
+
+ self.oldbuffs = self.buffs;
+ if(self.buffs)
+ {
+ if(!self.buff_model)
+ buffs_BuffModel_Spawn(self);
+
+ self.buff_model.color = buff.m_color;
+ self.buff_model.glowmod = buff_GlowColor(self.buff_model);
+ self.buff_model.skin = buff.m_skin;
+
+ self.effects |= EF_NOSHADOW;
+ }
+ else
+ {
+ remove(self.buff_model);
+ self.buff_model = world;
+
+ self.effects &= ~(EF_NOSHADOW);
+ }
+ }
+
+ if(self.buff_model)
+ {
+ self.buff_model.effects = self.effects;
+ self.buff_model.effects |= EF_LOWPRECISION;
+ self.buff_model.effects = self.buff_model.effects & EFMASK_CHEAP; // eat performance
+
+ self.buff_model.alpha = self.alpha;
+ }
+
+#undef BUFF_ONADD
+#undef BUFF_ONREM
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, SpectateCopy)
+{SELFPARAM();
+ self.buffs = other.buffs;
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, VehicleEnter)
+{
+ vh_vehicle.buffs = vh_player.buffs;
+ vh_player.buffs = 0;
+ vh_vehicle.buff_time = vh_player.buff_time - time;
+ vh_player.buff_time = 0;
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, VehicleExit)
+{
+ vh_player.buffs = vh_player.oldbuffs = vh_vehicle.buffs;
+ vh_vehicle.buffs = 0;
+ vh_player.buff_time = time + vh_vehicle.buff_time;
+ vh_vehicle.buff_time = 0;
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, PlayerRegen)
+{SELFPARAM();
+ if(self.buffs & BUFF_MEDIC.m_itemid)
+ {
+ regen_mod_rot = autocvar_g_buffs_medic_rot;
+ regen_mod_limit = regen_mod_max = autocvar_g_buffs_medic_max;
+ regen_mod_regen = autocvar_g_buffs_medic_regen;
+ }
+
+ if(self.buffs & BUFF_SPEED.m_itemid)
+ regen_mod_regen = autocvar_g_buffs_speed_regen;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":Buffs");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Buffs");
+ return false;
+}
+
+void buffs_DelayedInit()
+{
+ if(autocvar_g_buffs_spawn_count > 0)
+ if(find(world, classname, "item_buff") == world)
+ {
+ float i;
+ for(i = 0; i < autocvar_g_buffs_spawn_count; ++i)
+ {
+ entity e = spawn();
+ e.spawnflags |= 64; // always randomize
+ e.velocity = randomvec() * 250; // this gets reset anyway if random location works
+ buff_Init(e);
+ }
+ }
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "buffs.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+float autocvar_g_campcheck_damage;
+float autocvar_g_campcheck_distance;
+float autocvar_g_campcheck_interval;
+
+REGISTER_MUTATOR(campcheck, cvar("g_campcheck"));
+
+.float campcheck_nextcheck;
+.float campcheck_traveled_distance;
+
+MUTATOR_HOOKFUNCTION(campcheck, PlayerDies)
+{SELFPARAM();
+ Kill_Notification(NOTIF_ONE, self, MSG_CENTER_CPID, CPID_CAMPCHECK);
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate)
+{
+ if(IS_PLAYER(frag_target))
+ if(IS_PLAYER(frag_attacker))
+ if(frag_attacker != frag_target)
+ {
+ frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance;
+ frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance;
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink)
+{SELFPARAM();
+ if(!gameover)
+ if(!warmup_stage) // don't consider it camping during warmup?
+ if(time >= game_starttime)
+ if(IS_PLAYER(self))
+ if(IS_REAL_CLIENT(self)) // bots may camp, but that's no reason to constantly kill them
+ if(self.deadflag == DEAD_NO)
+ if(!self.frozen)
+ if(!self.BUTTON_CHAT)
+ if(autocvar_g_campcheck_interval)
+ {
+ vector dist;
+
+ // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
+ dist = self.prevorigin - self.origin;
+ dist.z = 0;
+ self.campcheck_traveled_distance += fabs(vlen(dist));
+
+ if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
+ {
+ self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2;
+ self.campcheck_traveled_distance = 0;
+ }
+
+ if(time > self.campcheck_nextcheck)
+ {
+ if(self.campcheck_traveled_distance < autocvar_g_campcheck_distance)
+ {
+ Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_CAMPCHECK);
+ if(self.vehicle)
+ Damage(self.vehicle, self, self, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, self.vehicle.origin, '0 0 0');
+ else
+ Damage(self, self, self, bound(0, autocvar_g_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, self.origin, '0 0 0');
+ }
+ self.campcheck_nextcheck = time + autocvar_g_campcheck_interval;
+ self.campcheck_traveled_distance = 0;
+ }
+
+ return false;
+ }
+
+ self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn)
+{SELFPARAM();
+ self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2;
+ self.campcheck_traveled_distance = 0;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":CampCheck");
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "campcheck.qc"
+#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-
-#include "../../util.qh"
-
-#ifdef CSQC
-#include "../../movetypes/movetypes.qh"
-#include "../../../client/rubble.qh"
-#endif
-
-REGISTER_MUTATOR(casings, true);
-
-#ifdef SVQC
-void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner)
-{SELFPARAM();
- vector org = self.origin + self.view_ofs + self.weaponentity.spawnorigin.x * v_forward - self.weaponentity.spawnorigin.y * v_right + self.weaponentity.spawnorigin.z * v_up;
-
- if (!sound_allowed(MSG_BROADCAST, casingowner))
- casingtype |= 0x80;
-
- WriteByte(MSG_ALL, SVC_TEMPENTITY);
- WriteMutator(MSG_ALL, casings);
- WriteByte(MSG_ALL, casingtype);
- WriteCoord(MSG_ALL, org.x);
- WriteCoord(MSG_ALL, org.y);
- WriteCoord(MSG_ALL, org.z);
- WriteShort(MSG_ALL, compressShortVector(vel)); // actually compressed velocity
- WriteByte(MSG_ALL, ang.x * 256 / 360);
- WriteByte(MSG_ALL, ang.y * 256 / 360);
- WriteByte(MSG_ALL, ang.z * 256 / 360);
-}
-#endif
-
-#ifdef CSQC
-entityclass(Casing);
-class(Casing) .float alpha;
-class(Casing) .bool silent;
-class(Casing) .int state;
-class(Casing) .float cnt;
-
-void Casing_Delete()
-{SELFPARAM();
- remove(self);
-}
-
-void Casing_Draw(entity this)
-{
- if (self.move_flags & FL_ONGROUND)
- {
- self.move_angles_x = 0;
- self.move_angles_z = 0;
- self.flags &= ~FL_ONGROUND;
- }
-
- Movetype_Physics_MatchTicrate(autocvar_cl_casings_ticrate, autocvar_cl_casings_sloppy);
- if (wasfreed(self))
- return; // deleted by touch function
-
- self.renderflags = 0;
- self.alpha = bound(0, self.cnt - time, 1);
-
- if (self.alpha < ALPHA_MIN_VISIBLE)
- {
- Casing_Delete();
- self.drawmask = 0;
- }
-}
-
-SOUND(BRASS1, W_Sound("brass1"));
-SOUND(BRASS2, W_Sound("brass2"));
-SOUND(BRASS3, W_Sound("brass3"));
-Sound SND_BRASS_RANDOM() {
- return Sounds[SND_BRASS1.m_id + floor(prandom() * 3)];
-}
-SOUND(CASINGS1, W_Sound("casings1"));
-SOUND(CASINGS2, W_Sound("casings2"));
-SOUND(CASINGS3, W_Sound("casings3"));
-Sound SND_CASINGS_RANDOM() {
- return Sounds[SND_CASINGS1.m_id + floor(prandom() * 3)];
-}
-
-void Casing_Touch()
-{SELFPARAM();
- if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
- {
- Casing_Delete();
- return;
- }
-
- if (!self.silent)
- if (!trace_ent || trace_ent.solid == SOLID_BSP)
- {
- if (vlen(self.velocity) > 50)
- {
- if (time >= self.nextthink)
- {
- Sound s;
- switch (self.state)
- {
- case 1:
- s = SND_CASINGS_RANDOM();
- break;
- default:
- s = SND_BRASS_RANDOM();
- break;
- }
-
- sound (self, CH_SHOTS, s, VOL_BASE, ATTEN_LARGE);
- }
- }
- }
-
- self.nextthink = time + 0.2;
-}
-
-void Casing_Damage(float thisdmg, int hittype, vector org, vector thisforce)
-{SELFPARAM();
- if (thisforce.z < 0)
- thisforce.z = 0;
- self.move_velocity = self.move_velocity + thisforce + '0 0 100';
- self.move_flags &= ~FL_ONGROUND;
-}
-
-MUTATOR_HOOKFUNCTION(casings, CSQC_Parse_TempEntity)
-{
- if (MUTATOR_RETURNVALUE) return;
- if (!ReadMutatorEquals(mutator_argv_int_0, casings)) return;
- return = true;
-
- int _state = ReadByte();
- vector org;
- org_x = ReadCoord();
- org_y = ReadCoord();
- org_z = ReadCoord();
- vector vel = decompressShortVector(ReadShort());
- vector ang;
- ang_x = ReadByte() * 360 / 256;
- ang_y = ReadByte() * 360 / 256;
- ang_z = ReadByte() * 360 / 256;
-
- if (!autocvar_cl_casings) return;
-
- Casing casing = RubbleNew("casing");
- casing.silent = (_state & 0x80);
- casing.state = (_state & 0x7F);
- casing.origin = org;
- setorigin(casing, casing.origin);
- casing.velocity = vel;
- casing.angles = ang;
- casing.drawmask = MASK_NORMAL;
-
- casing.draw = Casing_Draw;
- casing.move_origin = casing.origin;
- casing.move_velocity = casing.velocity + 2 * prandomvec();
- casing.move_angles = casing.angles;
- casing.move_avelocity = '0 250 0' + 100 * prandomvec();
- casing.move_movetype = MOVETYPE_BOUNCE;
- casing.move_touch = Casing_Touch;
- casing.move_time = time;
- casing.event_damage = Casing_Damage;
- casing.solid = SOLID_TRIGGER;
-
- switch (casing.state)
- {
- case 1:
- setmodel(casing, MDL_CASING_SHELL);
- casing.cnt = time + autocvar_cl_casings_shell_time;
- break;
- default:
- setmodel(casing, MDL_CASING_BULLET);
- casing.cnt = time + autocvar_cl_casings_bronze_time;
- break;
- }
-
- setsize(casing, '0 0 -1', '0 0 -1');
-
- RubbleLimit("casing", autocvar_cl_casings_maxcount, Casing_Delete);
-}
-
-#endif
-#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+
+REGISTER_MUTATOR(cloaked, cvar("g_cloaked"));
+
+float autocvar_g_balance_cloaked_alpha;
+
+MUTATOR_HOOKFUNCTION(cloaked, SetDefaultAlpha)
+{
+ default_player_alpha = autocvar_g_balance_cloaked_alpha;
+ default_weapon_alpha = default_player_alpha;
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(cloaked, BuildMutatorsPrettyString)
+{
+ if (!g_cts) ret_string = strcat(ret_string, ", Cloaked");
+}
+
+#endif
--- /dev/null
+#ifdef SVQC
+#include "cloaked.qc"
+#endif
+++ /dev/null
-#ifndef MUTATOR_DAMAGETEXT_H
-#define MUTATOR_DAMAGETEXT_H
-
-#ifdef MENUQC
-#include "../../../menu/xonotic/tab.qc"
-#endif
-
-#endif
-
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(damagetext, true);
-
-#if defined(CSQC) || defined(MENUQC)
-AUTOCVAR_SAVE(cl_damagetext, bool, false, _("Draw damage dealt. 0: disabled, 1: enabled"));
-AUTOCVAR_SAVE(cl_damagetext_format, string, "-%3$d", _("How to format the damage text. 1$ is health, 2$ is armor, 3$ is both"));
-AUTOCVAR_SAVE(cl_damagetext_color, vector, '1 1 0', _("Default damage text color"));
-AUTOCVAR_SAVE(cl_damagetext_color_per_weapon, bool, false, _("Damage text uses weapon color"));
-AUTOCVAR_SAVE(cl_damagetext_size, float, 8, _("Damage text font size"));
-AUTOCVAR_SAVE(cl_damagetext_alpha_start, float, 1, _("Damage text initial alpha"));
-AUTOCVAR_SAVE(cl_damagetext_alpha_lifetime, float, 3, _("Damage text lifetime in seconds"));
-AUTOCVAR_SAVE(cl_damagetext_velocity, vector, '0 0 20', _("Damage text move direction"));
-AUTOCVAR_SAVE(cl_damagetext_offset, vector, '0 -40 0', _("Damage text offset"));
-AUTOCVAR_SAVE(cl_damagetext_accumulate_range, float, 30, _("Damage text spawned within this range is accumulated"));
-#endif
-
-#ifdef CSQC
-CLASS(DamageText, Object)
- ATTRIB(DamageText, m_color, vector, autocvar_cl_damagetext_color)
- ATTRIB(DamageText, m_size, float, autocvar_cl_damagetext_size)
- ATTRIB(DamageText, alpha, float, autocvar_cl_damagetext_alpha_start)
- ATTRIB(DamageText, fade_rate, float, 1 / autocvar_cl_damagetext_alpha_lifetime)
- ATTRIB(DamageText, velocity, vector, autocvar_cl_damagetext_velocity)
- ATTRIB(DamageText, m_group, int, 0)
- ATTRIB(DamageText, m_damage, int, 0)
- ATTRIB(DamageText, m_armordamage, int, 0)
- ATTRIB(DamageText, m_deathtype, int, 0)
- ATTRIB(DamageText, time_prev, float, time)
-
- void DamageText_draw2d(DamageText this) {
- float dt = time - this.time_prev;
- this.time_prev = time;
- setorigin(this, this.origin + dt * this.velocity);
- this.alpha -= dt * this.fade_rate;
- if (this.alpha < 0) remove(this);
- vector pos = project_3d_to_2d(this.origin) + autocvar_cl_damagetext_offset;
- if (pos.z >= 0 && this.m_size > 0) {
- pos.z = 0;
- vector rgb = this.m_color;
- if (autocvar_cl_damagetext_color_per_weapon) {
- Weapon w = DEATH_WEAPONOF(this.m_deathtype);
- if (w != WEP_Null) rgb = w.wpcolor;
- }
- string s = sprintf(autocvar_cl_damagetext_format, this.m_damage, this.m_armordamage, this.m_damage + this.m_armordamage);
- drawcolorcodedstring2(pos, s, this.m_size * '1 1 0', rgb, this.alpha, DRAWFLAG_NORMAL);
- }
- }
- ATTRIB(DamageText, draw2d, void(DamageText), DamageText_draw2d)
-
- void DamageText_update(DamageText this, vector _origin, int _health, int _armor, int _deathtype) {
- this.m_damage = _health;
- this.m_armordamage = _armor;
- this.m_deathtype = _deathtype;
- setorigin(this, _origin);
- this.alpha = 1;
- }
-
- CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor, int _deathtype) {
- CONSTRUCT(DamageText);
- this.m_group = _group;
- DamageText_update(this, _origin, _health, _armor, _deathtype);
- }
-ENDCLASS(DamageText)
-#endif
-
-#ifdef SVQC
-AUTOCVAR(sv_damagetext, int, 2, _("<= 0: disabled, >= 1: spectators, >= 2: players, >= 3: all players"));
-#define SV_DAMAGETEXT_DISABLED() (autocvar_sv_damagetext <= 0 /* disabled */)
-#define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1 /* spectators only */)
-#define SV_DAMAGETEXT_PLAYERS() (autocvar_sv_damagetext >= 2 /* players */)
-#define SV_DAMAGETEXT_ALL() (autocvar_sv_damagetext >= 3 /* all players */)
-MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
- if (SV_DAMAGETEXT_DISABLED()) return;
- const entity attacker = mutator_argv_entity_0;
- const entity hit = mutator_argv_entity_1; if (hit == attacker) return;
- const int health = mutator_argv_int_0;
- const int armor = mutator_argv_int_1;
- const int deathtype = mutator_argv_int_2;
- const vector location = hit.origin;
- entity e;
- FOR_EACH_REALCLIENT(e) if (
- (SV_DAMAGETEXT_ALL()) ||
- (SV_DAMAGETEXT_PLAYERS() && e == attacker) ||
- (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(e) && e.enemy == attacker) ||
- (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(e))
- ) {
- msg_entity = e;
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteMutator(MSG_ONE, damagetext);
- WriteShort(MSG_ONE, health);
- WriteShort(MSG_ONE, armor);
- WriteEntity(MSG_ONE, hit);
- WriteCoord(MSG_ONE, location.x);
- WriteCoord(MSG_ONE, location.y);
- WriteCoord(MSG_ONE, location.z);
- WriteInt24_t(MSG_ONE, deathtype);
- }
-}
-#endif
-
-#ifdef CSQC
-MUTATOR_HOOKFUNCTION(damagetext, CSQC_Parse_TempEntity) {
- if (MUTATOR_RETURNVALUE) return false;
- if (!ReadMutatorEquals(mutator_argv_int_0, damagetext)) return false;
- int health = ReadShort();
- int armor = ReadShort();
- int group = ReadShort();
- vector location = vec3(ReadCoord(), ReadCoord(), ReadCoord());
- int deathtype = ReadInt24_t();
- if (autocvar_cl_damagetext) {
- if (autocvar_cl_damagetext_accumulate_range) {
- for (entity e = findradius(location, autocvar_cl_damagetext_accumulate_range); e; e = e.chain) {
- if (e.instanceOfDamageText && e.m_group == group) {
- DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor, deathtype);
- return true;
- }
- }
- }
- NEW(DamageText, group, location, health, armor, deathtype);
- }
- return true;
-}
-#endif
-
-#ifdef MENUQC
-CLASS(XonoticDamageTextSettings, XonoticTab)
- #include "../../../menu/gamesettings.qh"
- REGISTER_SETTINGS(damagetext, NEW(XonoticDamageTextSettings));
- ATTRIB(XonoticDamageTextSettings, title, string, _("Damage text"))
- ATTRIB(XonoticDamageTextSettings, intendedWidth, float, 0.9)
- ATTRIB(XonoticDamageTextSettings, rows, float, 13)
- ATTRIB(XonoticDamageTextSettings, columns, float, 5)
- INIT(XonoticDamageTextSettings) { this.configureDialog(this); }
- METHOD(XonoticDamageTextSettings, showNotify, void(entity this)) { loadAllCvars(this); }
- METHOD(XonoticDamageTextSettings, fill, void(entity this))
- {
- this.gotoRC(this, 0, 1); this.setFirstColumn(this, this.currentColumn);
- this.TD(this, 1, 3, makeXonoticCheckBox(0, "cl_damagetext", _("Draw damage numbers")));
- this.TR(this);
- this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Font size:")));
- this.TD(this, 1, 2, makeXonoticSlider(0, 50, 1, "cl_damagetext_size"));
- this.TR(this);
- this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Accumulate range:")));
- this.TD(this, 1, 2, makeXonoticSlider(0, 500, 1, "cl_damagetext_accumulate_range"));
- this.TR(this);
- this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Lifetime:")));
- this.TD(this, 1, 2, makeXonoticSlider(0, 10, 1, "cl_damagetext_alpha_lifetime"));
- this.TR(this);
- this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Color:")));
- this.TD(this, 2, 2, makeXonoticColorpickerString("cl_damagetext_color", "cl_damagetext_color"));
- }
-ENDCLASS(XonoticDamageTextSettings)
-#endif
-#endif
--- /dev/null
+#ifndef MUTATOR_DAMAGETEXT_H
+#define MUTATOR_DAMAGETEXT_H
+
+#ifdef MENUQC
+#include "../../../../menu/xonotic/tab.qc"
+#endif
+
+#endif
+
+#ifdef IMPLEMENTATION
+REGISTER_MUTATOR(damagetext, true);
+
+#if defined(CSQC) || defined(MENUQC)
+AUTOCVAR_SAVE(cl_damagetext, bool, false, _("Draw damage dealt. 0: disabled, 1: enabled"));
+AUTOCVAR_SAVE(cl_damagetext_format, string, "-%3$d", _("How to format the damage text. 1$ is health, 2$ is armor, 3$ is both"));
+AUTOCVAR_SAVE(cl_damagetext_color, vector, '1 1 0', _("Default damage text color"));
+AUTOCVAR_SAVE(cl_damagetext_color_per_weapon, bool, false, _("Damage text uses weapon color"));
+AUTOCVAR_SAVE(cl_damagetext_size, float, 8, _("Damage text font size"));
+AUTOCVAR_SAVE(cl_damagetext_alpha_start, float, 1, _("Damage text initial alpha"));
+AUTOCVAR_SAVE(cl_damagetext_alpha_lifetime, float, 3, _("Damage text lifetime in seconds"));
+AUTOCVAR_SAVE(cl_damagetext_velocity, vector, '0 0 20', _("Damage text move direction"));
+AUTOCVAR_SAVE(cl_damagetext_offset, vector, '0 -40 0', _("Damage text offset"));
+AUTOCVAR_SAVE(cl_damagetext_accumulate_range, float, 30, _("Damage text spawned within this range is accumulated"));
+#endif
+
+#ifdef CSQC
+CLASS(DamageText, Object)
+ ATTRIB(DamageText, m_color, vector, autocvar_cl_damagetext_color)
+ ATTRIB(DamageText, m_size, float, autocvar_cl_damagetext_size)
+ ATTRIB(DamageText, alpha, float, autocvar_cl_damagetext_alpha_start)
+ ATTRIB(DamageText, fade_rate, float, 1 / autocvar_cl_damagetext_alpha_lifetime)
+ ATTRIB(DamageText, velocity, vector, autocvar_cl_damagetext_velocity)
+ ATTRIB(DamageText, m_group, int, 0)
+ ATTRIB(DamageText, m_damage, int, 0)
+ ATTRIB(DamageText, m_armordamage, int, 0)
+ ATTRIB(DamageText, m_deathtype, int, 0)
+ ATTRIB(DamageText, time_prev, float, time)
+
+ void DamageText_draw2d(DamageText this) {
+ float dt = time - this.time_prev;
+ this.time_prev = time;
+ setorigin(this, this.origin + dt * this.velocity);
+ this.alpha -= dt * this.fade_rate;
+ if (this.alpha < 0) remove(this);
+ vector pos = project_3d_to_2d(this.origin) + autocvar_cl_damagetext_offset;
+ if (pos.z >= 0 && this.m_size > 0) {
+ pos.z = 0;
+ vector rgb = this.m_color;
+ if (autocvar_cl_damagetext_color_per_weapon) {
+ Weapon w = DEATH_WEAPONOF(this.m_deathtype);
+ if (w != WEP_Null) rgb = w.wpcolor;
+ }
+ string s = sprintf(autocvar_cl_damagetext_format, this.m_damage, this.m_armordamage, this.m_damage + this.m_armordamage);
+ drawcolorcodedstring2(pos, s, this.m_size * '1 1 0', rgb, this.alpha, DRAWFLAG_NORMAL);
+ }
+ }
+ ATTRIB(DamageText, draw2d, void(DamageText), DamageText_draw2d)
+
+ void DamageText_update(DamageText this, vector _origin, int _health, int _armor, int _deathtype) {
+ this.m_damage = _health;
+ this.m_armordamage = _armor;
+ this.m_deathtype = _deathtype;
+ setorigin(this, _origin);
+ this.alpha = 1;
+ }
+
+ CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor, int _deathtype) {
+ CONSTRUCT(DamageText);
+ this.m_group = _group;
+ DamageText_update(this, _origin, _health, _armor, _deathtype);
+ }
+ENDCLASS(DamageText)
+#endif
+
+REGISTER_NET_TEMP(damagetext)
+
+#ifdef SVQC
+AUTOCVAR(sv_damagetext, int, 2, _("<= 0: disabled, >= 1: spectators, >= 2: players, >= 3: all players"));
+#define SV_DAMAGETEXT_DISABLED() (autocvar_sv_damagetext <= 0 /* disabled */)
+#define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1 /* spectators only */)
+#define SV_DAMAGETEXT_PLAYERS() (autocvar_sv_damagetext >= 2 /* players */)
+#define SV_DAMAGETEXT_ALL() (autocvar_sv_damagetext >= 3 /* all players */)
+MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
+ if (SV_DAMAGETEXT_DISABLED()) return;
+ const entity attacker = MUTATOR_ARGV(0, entity);
+ const entity hit = MUTATOR_ARGV(1, entity); if (hit == attacker) return;
+ const int health = MUTATOR_ARGV(0, int);
+ const int armor = MUTATOR_ARGV(1, int);
+ const int deathtype = MUTATOR_ARGV(2, int);
+ const vector location = hit.origin;
+ entity e;
+ FOR_EACH_REALCLIENT(e) if (
+ (SV_DAMAGETEXT_ALL()) ||
+ (SV_DAMAGETEXT_PLAYERS() && e == attacker) ||
+ (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(e) && e.enemy == attacker) ||
+ (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(e))
+ ) {
+ msg_entity = e;
+ WriteHeader(MSG_ONE, damagetext);
+ WriteShort(MSG_ONE, health);
+ WriteShort(MSG_ONE, armor);
+ WriteEntity(MSG_ONE, hit);
+ WriteCoord(MSG_ONE, location.x);
+ WriteCoord(MSG_ONE, location.y);
+ WriteCoord(MSG_ONE, location.z);
+ WriteInt24_t(MSG_ONE, deathtype);
+ }
+}
+#endif
+
+#ifdef CSQC
+NET_HANDLE(damagetext, bool isNew)
+{
+ int health = ReadShort();
+ int armor = ReadShort();
+ int group = ReadShort();
+ vector location = vec3(ReadCoord(), ReadCoord(), ReadCoord());
+ int deathtype = ReadInt24_t();
+ return = true;
+ if (autocvar_cl_damagetext) {
+ if (autocvar_cl_damagetext_accumulate_range) {
+ for (entity e = findradius(location, autocvar_cl_damagetext_accumulate_range); e; e = e.chain) {
+ if (e.instanceOfDamageText && e.m_group == group) {
+ DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor, deathtype);
+ return;
+ }
+ }
+ }
+ NEW(DamageText, group, location, health, armor, deathtype);
+ }
+}
+#endif
+
+#ifdef MENUQC
+CLASS(XonoticDamageTextSettings, XonoticTab)
+ #include "../../../../menu/gamesettings.qh"
+ REGISTER_SETTINGS(damagetext, NEW(XonoticDamageTextSettings));
+ ATTRIB(XonoticDamageTextSettings, title, string, _("Damage text"))
+ ATTRIB(XonoticDamageTextSettings, intendedWidth, float, 0.9)
+ ATTRIB(XonoticDamageTextSettings, rows, float, 13)
+ ATTRIB(XonoticDamageTextSettings, columns, float, 5)
+ INIT(XonoticDamageTextSettings) { this.configureDialog(this); }
+ METHOD(XonoticDamageTextSettings, showNotify, void(entity this)) { loadAllCvars(this); }
+ METHOD(XonoticDamageTextSettings, fill, void(entity this))
+ {
+ this.gotoRC(this, 0, 1); this.setFirstColumn(this, this.currentColumn);
+ this.TD(this, 1, 3, makeXonoticCheckBox(0, "cl_damagetext", _("Draw damage numbers")));
+ this.TR(this);
+ this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Font size:")));
+ this.TD(this, 1, 2, makeXonoticSlider(0, 50, 1, "cl_damagetext_size"));
+ this.TR(this);
+ this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Accumulate range:")));
+ this.TD(this, 1, 2, makeXonoticSlider(0, 500, 1, "cl_damagetext_accumulate_range"));
+ this.TR(this);
+ this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Lifetime:")));
+ this.TD(this, 1, 2, makeXonoticSlider(0, 10, 1, "cl_damagetext_alpha_lifetime"));
+ this.TR(this);
+ this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Color:")));
+ this.TD(this, 2, 2, makeXonoticColorpickerString("cl_damagetext_color", "cl_damagetext_color"));
+ }
+ENDCLASS(XonoticDamageTextSettings)
+#endif
+#endif
--- /dev/null
+#include "damagetext.qc"
--- /dev/null
+#ifdef IMPLEMENTATION
+
+#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
+
+ float autocvar_sv_dodging_delay;
+ float autocvar_sv_dodging_height_threshold;
+ float autocvar_sv_dodging_horiz_speed;
+ float autocvar_sv_dodging_horiz_speed_frozen;
+ float autocvar_sv_dodging_ramp_time;
+ bool autocvar_sv_dodging_sound;
+ float autocvar_sv_dodging_up_speed;
+ float autocvar_sv_dodging_wall_distance_threshold;
+ bool autocvar_sv_dodging_wall_dodging;
+ bool autocvar_sv_dodging_frozen_doubletap;
+#endif
+
+#ifdef SVQC
+
+float g_dodging;
+
+// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
+.float dodging_action;
+
+// the jump part of the dodge cannot be ramped
+.float dodging_single_action;
+
+#include "../../../animdecide.qh"
+#include "../../../physics.qh"
+
+.float cvar_cl_dodging_timeout;
+
+.float stat_dodging;
+.float stat_dodging_delay;
+.float stat_dodging_horiz_speed_frozen;
+.float stat_dodging_frozen_nodoubletap;
+.float stat_dodging_frozen;
+.float stat_dodging_horiz_speed;
+.float stat_dodging_height_threshold;
+.float stat_dodging_distance_threshold;
+.float stat_dodging_ramp_time;
+.float stat_dodging_up_speed;
+.float stat_dodging_wall;
+
+REGISTER_MUTATOR(dodging, cvar("g_dodging"))
+{
+ // this just turns on the cvar.
+ MUTATOR_ONADD
+ {
+ g_dodging = cvar("g_dodging");
+ addstat(STAT_DODGING, AS_INT, stat_dodging);
+ addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay);
+ addstat(STAT_DODGING_TIMEOUT, AS_FLOAT, cvar_cl_dodging_timeout); // we stat this, so it is updated on the client when updated on server (otherwise, chaos)
+ addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap);
+ addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen);
+ addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen);
+ addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed);
+ addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold);
+ addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold);
+ addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time);
+ addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed);
+ addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall);
+ }
+
+ // this just turns off the cvar.
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ g_dodging = 0;
+ }
+
+ return false;
+}
+
+#endif
+
+// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
+.float dodging_action;
+
+// the jump part of the dodge cannot be ramped
+.float dodging_single_action;
+
+
+// these are used to store the last key press time for each of the keys..
+.float last_FORWARD_KEY_time;
+.float last_BACKWARD_KEY_time;
+.float last_LEFT_KEY_time;
+.float last_RIGHT_KEY_time;
+
+// these store the movement direction at the time of the dodge action happening.
+.vector dodging_direction;
+
+// this indicates the last time a dodge was executed. used to check if another one is allowed
+// and to ramp up the dodge acceleration in the physics hook.
+.float last_dodging_time;
+
+// This is the velocity gain to be added over the ramp time.
+// It will decrease from frame to frame during dodging_action = 1
+// until it's 0.
+.float dodging_velocity_gain;
+
+#ifdef CSQC
+.int pressedkeys;
+
+#elif defined(SVQC)
+
+void dodging_UpdateStats()
+{SELFPARAM();
+ self.stat_dodging = PHYS_DODGING;
+ self.stat_dodging_delay = PHYS_DODGING_DELAY;
+ self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN;
+ self.stat_dodging_frozen = PHYS_DODGING_FROZEN;
+ self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP;
+ self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD;
+ self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD;
+ self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME;
+ self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED;
+ self.stat_dodging_wall = PHYS_DODGING_WALL;
+}
+
+#endif
+
+// returns 1 if the player is close to a wall
+bool check_close_to_wall(float threshold)
+{SELFPARAM();
+ if (PHYS_DODGING_WALL == 0) { return false; }
+
+ #define X(OFFSET) \
+ tracebox(self.origin, self.mins, self.maxs, self.origin + OFFSET, true, self); \
+ if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold) \
+ return true;
+ X(1000*v_right);
+ X(-1000*v_right);
+ X(1000*v_forward);
+ X(-1000*v_forward);
+ #undef X
+
+ return false;
+}
+
+bool check_close_to_ground(float threshold)
+{SELFPARAM();
+ return IS_ONGROUND(self) ? true : false;
+}
+
+float PM_dodging_checkpressedkeys()
+{SELFPARAM();
+ if(!PHYS_DODGING)
+ return false;
+
+ float frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN);
+ float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
+
+ // first check if the last dodge is far enough back in time so we can dodge again
+ if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY)
+ return false;
+
+ makevectors(self.angles);
+
+ if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1
+ && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1)
+ return true;
+
+ float tap_direction_x = 0;
+ float tap_direction_y = 0;
+ float dodge_detected = 0;
+
+ #define X(COND,BTN,RESULT) \
+ if (self.movement_##COND) \
+ /* is this a state change? */ \
+ if(!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_##BTN) || frozen_no_doubletap) { \
+ tap_direction_##RESULT; \
+ if ((time - self.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(self)) \
+ dodge_detected = 1; \
+ self.last_##BTN##_KEY_time = time; \
+ }
+ X(x < 0, BACKWARD, x--);
+ X(x > 0, FORWARD, x++);
+ X(y < 0, LEFT, y--);
+ X(y > 0, RIGHT, y++);
+ #undef X
+
+ if (dodge_detected == 1)
+ {
+ self.last_dodging_time = time;
+
+ self.dodging_action = 1;
+ self.dodging_single_action = 1;
+
+ self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
+
+ self.dodging_direction_x = tap_direction_x;
+ self.dodging_direction_y = tap_direction_y;
+
+ // normalize the dodging_direction vector.. (unlike UT99) XD
+ float length = self.dodging_direction_x * self.dodging_direction_x
+ + self.dodging_direction_y * self.dodging_direction_y;
+ length = sqrt(length);
+
+ self.dodging_direction_x = self.dodging_direction_x * 1.0 / length;
+ self.dodging_direction_y = self.dodging_direction_y * 1.0 / length;
+ return true;
+ }
+ return false;
+}
+
+void PM_dodging()
+{SELFPARAM();
+ if (!PHYS_DODGING)
+ return;
+
+#ifdef SVQC
+ dodging_UpdateStats();
+#endif
+
+ if (PHYS_DEAD(self))
+ return;
+
+ // when swimming, no dodging allowed..
+ if (self.waterlevel >= WATERLEVEL_SWIMMING)
+ {
+ self.dodging_action = 0;
+ self.dodging_direction_x = 0;
+ self.dodging_direction_y = 0;
+ return;
+ }
+
+ // make sure v_up, v_right and v_forward are sane
+ makevectors(self.angles);
+
+ // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
+ // will be called ramp_time/frametime times = 2 times. so, we need to
+ // add 0.5 * the total speed each frame until the dodge action is done..
+ float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
+
+ // if ramp time is smaller than frametime we get problems ;D
+ common_factor = min(common_factor, 1);
+
+ float horiz_speed = PHYS_FROZEN(self) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
+ float new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
+ new_velocity_gain = max(0, new_velocity_gain);
+
+ float velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
+
+ // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
+ if (self.dodging_action == 1)
+ {
+ //disable jump key during dodge accel phase
+ if(self.movement_z > 0) { self.movement_z = 0; }
+
+ self.velocity += ((self.dodging_direction_y * velocity_difference) * v_right)
+ + ((self.dodging_direction_x * velocity_difference) * v_forward);
+
+ self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference;
+ }
+
+ // the up part of the dodge is a single shot action
+ if (self.dodging_single_action == 1)
+ {
+ UNSET_ONGROUND(self);
+
+ self.velocity += PHYS_DODGING_UP_SPEED * v_up;
+
+#ifdef SVQC
+ if (autocvar_sv_dodging_sound)
+ PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+
+ animdecide_setaction(self, ANIMACTION_JUMP, true);
+#endif
+
+ self.dodging_single_action = 0;
+ }
+
+ // are we done with the dodging ramp yet?
+ if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
+ {
+ // reset state so next dodge can be done correctly
+ self.dodging_action = 0;
+ self.dodging_direction_x = 0;
+ self.dodging_direction_y = 0;
+ }
+}
+
+#ifdef SVQC
+
+MUTATOR_HOOKFUNCTION(dodging, GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics)
+{
+ // print("dodging_PlayerPhysics\n");
+ PM_dodging();
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys)
+{
+ PM_dodging_checkpressedkeys();
+
+ return false;
+}
+
+#endif
+
+#endif
--- /dev/null
+#ifdef SVQC
+#include "dodging.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up"));
+#ifdef SVQC
+REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) {
+ MUTATOR_ONADD {
+ g_grappling_hook = true;
+ WEP_HOOK.ammo_factor = 0;
+ }
+ MUTATOR_ONROLLBACK_OR_REMOVE {
+ g_grappling_hook = false;
+ WEP_HOOK.ammo_factor = 1;
+ }
+}
+
+MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":grappling_hook");
+}
+
+MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Hook");
+}
+
+MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString)
+{
+ ret_string = strcat(ret_string, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
+}
+
+MUTATOR_HOOKFUNCTION(hook, PlayerSpawn)
+{
+ SELFPARAM();
+ self.offhand = OFFHAND_HOOK;
+}
+
+MUTATOR_HOOKFUNCTION(hook, FilterItem)
+{
+ return self.weapon == WEP_HOOK.m_id;
+}
+
+#endif
+#endif
--- /dev/null
+#ifdef SVQC
+#include "hook.qc"
+#endif
#include "items.qc"
+#ifdef SVQC
+float autocvar_g_instagib_invis_alpha;
+#endif
+
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
+int autocvar_g_instagib_ammo_drop;
+int autocvar_g_instagib_extralives;
+float autocvar_g_instagib_speed_highspeed;
+
#include "../../../../server/cl_client.qh"
#include "../../../buffs/all.qh"
{
if (!g_instagib) { remove(self); return; }
if (!self.ammo_cells) self.ammo_cells = autocvar_g_instagib_ammo_drop;
- StartItemA(ITEM_VaporizerCells);
+ StartItem(this, ITEM_VaporizerCells);
}
void instagib_invisibility()
{SELFPARAM();
self.strength_finished = autocvar_g_balance_powerup_strength_time;
- StartItemA(ITEM_Invisibility);
+ StartItem(this, ITEM_Invisibility);
}
void instagib_extralife()
{SELFPARAM();
self.max_health = 1;
- StartItemA(ITEM_ExtraLife);
+ StartItem(this, ITEM_ExtraLife);
}
void instagib_speed()
{SELFPARAM();
self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
- StartItemA(ITEM_Speed);
+ StartItem(this, ITEM_Speed);
}
.float instagib_nextthink;
e.noalign = self.noalign;
e.cnt = self.cnt;
e.team = self.team;
+ e.spawnfunc_checked = true;
WITH(entity, self, e, spawnfunc_item_minst_cells(e));
return true;
}
#ifndef MENUQC
MODEL(VaporizerCells_ITEM, Item_Model("a_cells.md3"));
+SOUND(VaporizerCells, "misc/itempickup");
#endif
REGISTER_ITEM(VaporizerCells, Ammo) {
#ifndef MENUQC
this.m_model = MDL_VaporizerCells_ITEM;
+ this.m_sound = SND_VaporizerCells;
#endif
- this.m_sound = "misc/itempickup.wav";
this.m_name = "Vaporizer Ammo";
this.m_icon = "ammo_supercells";
#ifdef SVQC
#ifndef MENUQC
MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3"));
+SOUND(ExtraLife, "misc/megahealth");
#endif
REGISTER_ITEM(ExtraLife, Powerup) {
#ifndef MENUQC
this.m_model = MDL_ExtraLife_ITEM;
+ this.m_sound = SND_ExtraLife;
#endif
- this.m_sound = "misc/megahealth.wav";
this.m_name = "Extra life";
this.m_icon = "item_mega_health";
this.m_color = '1 0 0';
#ifndef MENUQC
MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
+SOUND(Invisibility, "misc/powerup");
#endif
REGISTER_ITEM(Invisibility, Powerup) {
#ifndef MENUQC
this.m_model = MDL_Invisibility_ITEM;
+ this.m_sound = SND_Invisibility;
#endif
- this.m_sound = "misc/powerup.wav";
this.m_name = "Invisibility";
this.m_icon = "strength";
this.m_color = '0 0 1';
#ifndef MENUQC
MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
+SOUND(Speed, "misc/powerup_shield");
#endif
REGISTER_ITEM(Speed, Powerup) {
#ifndef MENUQC
this.m_model = MDL_Speed_ITEM;
+ this.m_sound = SND_Speed;
#endif
- this.m_sound = "misc/powerup_shield.wav";
this.m_name = "Speed";
this.m_icon = "shield";
this.m_color = '1 0 1';
--- /dev/null
+#include "instagib.qc"
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles"));
+
+MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile)
+{
+ if(other.health)
+ {
+ // disable health which in effect disables damage calculations
+ other.health = 0;
+ }
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":InvincibleProjectiles");
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Invincible Projectiles");
+ return 0;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "invincibleproj.qc"
+#endif
#ifdef IMPLEMENTATION
REGISTER_MUTATOR(itemstime, true);
+REGISTER_NET_TEMP(itemstime)
+
#ifdef SVQC
void IT_Write(entity e, int i, float f) {
if (!IS_REAL_CLIENT(e)) return;
msg_entity = e;
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteMutator(MSG_ONE, itemstime);
+ WriteHeader(MSG_ONE, itemstime);
WriteByte(MSG_ONE, i);
WriteFloat(MSG_ONE, f);
}
#ifdef CSQC
float ItemsTime_time[Items_MAX];
float ItemsTime_availableTime[Items_MAX];
-MUTATOR_HOOKFUNCTION(itemstime, CSQC_Parse_TempEntity) {
- if (MUTATOR_RETURNVALUE) return false;
- if (!ReadMutatorEquals(mutator_argv_int_0, itemstime)) return false;
+NET_HANDLE(itemstime, bool isNew)
+{
int i = ReadByte();
float f = ReadFloat();
+ return = true;
ItemsTime_time[i] = f;
- return true;
}
#endif
return;
GameItem item = e.itemdef;
- it_times[item.m_id] = t;
+ if (item.instanceOfGameItem && !item.instanceOfWeaponPickup)
+ {
+ it_times[item.m_id] = t;
+ }
}
void Item_ItemsTime_SetTimesForAllPlayers()
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball);
+
+MUTATOR_HOOKFUNCTION(melee_only, SetStartItems)
+{
+ start_ammo_shells = warmup_start_ammo_shells = 0;
+ start_weapons = warmup_start_weapons = WEPSET(SHOTGUN);
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon)
+{
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(melee_only, FilterItem)
+{SELFPARAM();
+ switch (self.items)
+ {
+ case ITEM_HealthSmall.m_itemid:
+ case ITEM_ArmorSmall.m_itemid:
+ return false;
+ }
+
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":MeleeOnly");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Melee Only Arena");
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "melee_only.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+
+float autocvar_g_midair_shieldtime;
+
+REGISTER_MUTATOR(midair, cvar("g_midair"));
+
+.float midair_shieldtime;
+
+MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate)
+{SELFPARAM();
+ if(IS_PLAYER(frag_attacker))
+ if(IS_PLAYER(frag_target))
+ if(time < self.midair_shieldtime)
+ frag_damage = false;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(midair, PlayerPowerups)
+{SELFPARAM();
+ if(time >= game_starttime)
+ if(self.flags & FL_ONGROUND)
+ {
+ self.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
+ self.midair_shieldtime = max(self.midair_shieldtime, time + autocvar_g_midair_shieldtime);
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(midair, PlayerSpawn)
+{SELFPARAM();
+ if(IS_BOT_CLIENT(self))
+ self.bot_moveskill = 0; // disable bunnyhopping
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":midair");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Midair");
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "midair.qc"
+#endif
--- /dev/null
+#ifndef MENUQC
+#include "multijump.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+#ifdef SVQC
+ #include "../../../../server/antilag.qh"
+#endif
+#include "../../../physics.qh"
+
+.int multijump_count;
+.bool multijump_ready;
+.bool cvar_cl_multijump;
+
+#ifdef CSQC
+
+#define PHYS_MULTIJUMP getstati(STAT_MULTIJUMP)
+#define PHYS_MULTIJUMP_SPEED getstatf(STAT_MULTIJUMP_SPEED)
+#define PHYS_MULTIJUMP_ADD getstati(STAT_MULTIJUMP_ADD)
+#define PHYS_MULTIJUMP_MAXSPEED getstatf(STAT_MULTIJUMP_MAXSPEED)
+#define PHYS_MULTIJUMP_DODGING getstati(STAT_MULTIJUMP_DODGING)
+
+#elif defined(SVQC)
+
+int autocvar_g_multijump;
+float autocvar_g_multijump_add;
+float autocvar_g_multijump_speed;
+float autocvar_g_multijump_maxspeed;
+float autocvar_g_multijump_dodging = 1;
+
+#define PHYS_MULTIJUMP autocvar_g_multijump
+#define PHYS_MULTIJUMP_SPEED autocvar_g_multijump_speed
+#define PHYS_MULTIJUMP_ADD autocvar_g_multijump_add
+#define PHYS_MULTIJUMP_MAXSPEED autocvar_g_multijump_maxspeed
+#define PHYS_MULTIJUMP_DODGING autocvar_g_multijump_dodging
+
+.float stat_multijump;
+.float stat_multijump_speed;
+.float stat_multijump_add;
+.float stat_multijump_maxspeed;
+.float stat_multijump_dodging;
+
+void multijump_UpdateStats()
+{SELFPARAM();
+ self.stat_multijump = PHYS_MULTIJUMP;
+ self.stat_multijump_speed = PHYS_MULTIJUMP_SPEED;
+ self.stat_multijump_add = PHYS_MULTIJUMP_ADD;
+ self.stat_multijump_maxspeed = PHYS_MULTIJUMP_MAXSPEED;
+ self.stat_multijump_dodging = PHYS_MULTIJUMP_DODGING;
+}
+
+void multijump_AddStats()
+{
+ addstat(STAT_MULTIJUMP, AS_INT, stat_multijump);
+ addstat(STAT_MULTIJUMP_SPEED, AS_FLOAT, stat_multijump_speed);
+ addstat(STAT_MULTIJUMP_ADD, AS_INT, stat_multijump_add);
+ addstat(STAT_MULTIJUMP_MAXSPEED, AS_FLOAT, stat_multijump_maxspeed);
+ addstat(STAT_MULTIJUMP_DODGING, AS_INT, stat_multijump_dodging);
+}
+
+#endif
+
+void PM_multijump()
+{SELFPARAM();
+ if(!PHYS_MULTIJUMP) { return; }
+
+ if(IS_ONGROUND(self))
+ {
+ self.multijump_count = 0;
+ }
+}
+
+bool PM_multijump_checkjump()
+{SELFPARAM();
+ if(!PHYS_MULTIJUMP) { return false; }
+
+#ifdef SVQC
+ bool client_multijump = self.cvar_cl_multijump;
+#elif defined(CSQC)
+ bool client_multijump = cvar("cl_multijump");
+
+ if(cvar("cl_multijump") > 1)
+ return false; // nope
+#endif
+
+ if (!IS_JUMP_HELD(self) && !IS_ONGROUND(self) && client_multijump) // jump button pressed this frame and we are in midair
+ self.multijump_ready = true; // this is necessary to check that we released the jump button and pressed it again
+ else
+ self.multijump_ready = false;
+
+ int phys_multijump = PHYS_MULTIJUMP;
+
+#ifdef CSQC
+ phys_multijump = (PHYS_MULTIJUMP) ? -1 : 0;
+#endif
+
+ if(!player_multijump && self.multijump_ready && (self.multijump_count < phys_multijump || phys_multijump == -1) && self.velocity_z > PHYS_MULTIJUMP_SPEED && (!PHYS_MULTIJUMP_MAXSPEED || vlen(self.velocity) <= PHYS_MULTIJUMP_MAXSPEED))
+ {
+ if (PHYS_MULTIJUMP)
+ {
+ if (!PHYS_MULTIJUMP_ADD) // in this case we make the z velocity == jumpvelocity
+ {
+ if (self.velocity_z < PHYS_JUMPVELOCITY)
+ {
+ player_multijump = true;
+ self.velocity_z = 0;
+ }
+ }
+ else
+ player_multijump = true;
+
+ if(player_multijump)
+ {
+ if(PHYS_MULTIJUMP_DODGING)
+ if(self.movement_x != 0 || self.movement_y != 0) // don't remove all speed if player isnt pressing any movement keys
+ {
+ float curspeed;
+ vector wishvel, wishdir;
+
+/*#ifdef SVQC
+ curspeed = max(
+ vlen(vec2(self.velocity)), // current xy speed
+ vlen(vec2(antilag_takebackavgvelocity(self, max(self.lastteleporttime + sys_frametime, time - 0.25), time))) // average xy topspeed over the last 0.25 secs
+ );
+#elif defined(CSQC)*/
+ curspeed = vlen(vec2(self.velocity));
+//#endif
+
+ makevectors(self.v_angle_y * '0 1 0');
+ wishvel = v_forward * self.movement_x + v_right * self.movement_y;
+ wishdir = normalize(wishvel);
+
+ self.velocity_x = wishdir_x * curspeed; // allow "dodging" at a multijump
+ self.velocity_y = wishdir_y * curspeed;
+ // keep velocity_z unchanged!
+ }
+ if (PHYS_MULTIJUMP > 0)
+ {
+ self.multijump_count += 1;
+ }
+ }
+ }
+ self.multijump_ready = false; // require releasing and pressing the jump button again for the next jump
+ }
+
+ return false;
+}
+
+#ifdef SVQC
+REGISTER_MUTATOR(multijump, cvar("g_multijump"))
+{
+ MUTATOR_ONADD
+ {
+ multijump_AddStats();
+ }
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(multijump, PlayerPhysics)
+{
+ multijump_UpdateStats();
+ PM_multijump();
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(multijump, PlayerJump)
+{
+ return PM_multijump_checkjump();
+}
+
+MUTATOR_HOOKFUNCTION(multijump, GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_multijump, "cl_multijump");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":multijump");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Multi jump");
+ return false;
+}
+
+#endif
+#endif
--- /dev/null
+#ifdef SVQC
+#include "nades.qc"
+#endif
--- /dev/null
+#ifndef MUTATOR_NADES_H
+#define MUTATOR_NADES_H
+
+#ifdef SVQC
+#include "../../../../server/mutators/mutator/gamemode_freezetag.qc"
+#endif
+
+.entity nade;
+.entity fake_nade;
+.float nade_timer;
+.float nade_refire;
+.float bonus_nades;
+.float nade_special_time;
+.float bonus_nade_score;
+.float nade_type;
+.string pokenade_type;
+.entity nade_damage_target;
+.float cvar_cl_nade_type;
+.string cvar_cl_pokenade_type;
+.float toss_time;
+.float stat_healing_orb;
+.float stat_healing_orb_alpha;
+.float nade_show_particles;
+
+// Remove nades that are being thrown
+void nades_Clear(entity player);
+
+// Give a bonus grenade to a player
+void(entity player, float score) nades_GiveBonus;
+
+/**
+ * called to adjust nade damage and force on hit
+ */
+#define EV_Nade_Damage(i, o) \
+ /** weapon */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** force */ i(vector, MUTATOR_ARGV_0_vector) \
+ /**/ o(vector, MUTATOR_ARGV_0_vector) \
+ /** damage */ i(float, MUTATOR_ARGV_0_float) \
+ /**/ o(float, MUTATOR_ARGV_0_float) \
+ /**/
+MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage);
+
+#endif
+
+#ifdef IMPLEMENTATION
+
+#include "../../../nades/all.qh"
+#include "../../../gamemodes/all.qh"
+#include "../../../monsters/spawn.qh"
+#include "../../../monsters/sv_monsters.qh"
+#include "../../../../server/g_subs.qh"
+
+REGISTER_MUTATOR(nades, cvar("g_nades"))
+{
+ MUTATOR_ONADD
+ {
+ addstat(STAT_NADE_TIMER, AS_FLOAT, nade_timer);
+ addstat(STAT_NADE_BONUS, AS_FLOAT, bonus_nades);
+ addstat(STAT_NADE_BONUS_TYPE, AS_INT, nade_type);
+ addstat(STAT_NADE_BONUS_SCORE, AS_FLOAT, bonus_nade_score);
+ addstat(STAT_HEALING_ORB, AS_FLOAT, stat_healing_orb);
+ addstat(STAT_HEALING_ORB_ALPHA, AS_FLOAT, stat_healing_orb_alpha);
+ }
+
+ return false;
+}
+
+.float nade_time_primed;
+
+.entity nade_spawnloc;
+
+void nade_timer_think()
+{SELFPARAM();
+ self.skin = 8 - (self.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10);
+ self.nextthink = time;
+ if(!self.owner || wasfreed(self.owner))
+ remove(self);
+}
+
+void nade_burn_spawn(entity _nade)
+{
+ CSQCProjectile(_nade, true, Nades_from(_nade.nade_type).m_projectile[true], true);
+}
+
+void nade_spawn(entity _nade)
+{
+ entity timer = new(nade_timer);
+ setmodel(timer, MDL_NADE_TIMER);
+ setattachment(timer, _nade, "");
+ timer.colormap = _nade.colormap;
+ timer.glowmod = _nade.glowmod;
+ timer.think = nade_timer_think;
+ timer.nextthink = time;
+ timer.wait = _nade.wait;
+ timer.owner = _nade;
+ timer.skin = 10;
+
+ _nade.effects |= EF_LOWPRECISION;
+
+ CSQCProjectile(_nade, true, Nades_from(_nade.nade_type).m_projectile[false], true);
+}
+
+void napalm_damage(float dist, float damage, float edgedamage, float burntime)
+{SELFPARAM();
+ entity e;
+ float d;
+ vector p;
+
+ if ( damage < 0 )
+ return;
+
+ RandomSelection_Init();
+ for(e = WarpZone_FindRadius(self.origin, dist, true); e; e = e.chain)
+ if(e.takedamage == DAMAGE_AIM)
+ if(self.realowner != e || autocvar_g_nades_napalm_selfdamage)
+ if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self))
+ if(!e.frozen)
+ {
+ p = e.origin;
+ p.x += e.mins.x + random() * (e.maxs.x - e.mins.x);
+ p.y += e.mins.y + random() * (e.maxs.y - e.mins.y);
+ p.z += e.mins.z + random() * (e.maxs.z - e.mins.z);
+ d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p);
+ if(d < dist)
+ {
+ e.fireball_impactvec = p;
+ RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
+ }
+ }
+ if(RandomSelection_chosen_ent)
+ {
+ d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
+ d = damage + (edgedamage - damage) * (d / dist);
+ Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
+ //trailparticles(self, particleeffectnum(EFFECT_FIREBALL_LASER), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
+ Send_Effect(EFFECT_FIREBALL_LASER, self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
+ }
+}
+
+
+void napalm_ball_think()
+{SELFPARAM();
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ {
+ remove(self);
+ return;
+ }
+
+ if(time > self.pushltime)
+ {
+ remove(self);
+ return;
+ }
+
+ 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; }
+ }
+
+ self.angles = vectoangles(self.velocity);
+
+ napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage,
+ autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime);
+
+ self.nextthink = time + 0.1;
+}
+
+
+void nade_napalm_ball()
+{SELFPARAM();
+ entity proj;
+ vector kick;
+
+ spamsound(self, CH_SHOTS, SND(FIREBALL_FIRE), VOL_BASE, ATTEN_NORM);
+
+ proj = new(grenade);
+ proj.owner = self.owner;
+ proj.realowner = self.realowner;
+ proj.team = self.owner.team;
+ proj.bot_dodge = true;
+ proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage;
+ proj.movetype = MOVETYPE_BOUNCE;
+ proj.projectiledeathtype = DEATH_NADE_NAPALM.m_id;
+ PROJECTILE_MAKETRIGGER(proj);
+ setmodel(proj, MDL_Null);
+ proj.scale = 1;//0.5;
+ setsize(proj, '-4 -4 -4', '4 4 4');
+ setorigin(proj, self.origin);
+ proj.think = napalm_ball_think;
+ proj.nextthink = time;
+ proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale;
+ proj.effects = EF_LOWPRECISION | EF_FLAME;
+
+ kick.x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
+ kick.y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
+ kick.z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread;
+ proj.velocity = kick;
+
+ proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime;
+
+ proj.angles = vectoangles(proj.velocity);
+ proj.flags = FL_PROJECTILE;
+ proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
+
+ //CSQCProjectile(proj, true, PROJECTILE_NAPALM_FIRE, true);
+}
+
+
+void napalm_fountain_think()
+{SELFPARAM();
+
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ {
+ remove(self);
+ return;
+ }
+
+ if(time >= self.ltime)
+ {
+ remove(self);
+ return;
+ }
+
+ 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; }
+
+ UpdateCSQCProjectile(self);
+ }
+
+ napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage,
+ autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime);
+
+ self.nextthink = time + 0.1;
+ if(time >= self.nade_special_time)
+ {
+ self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay;
+ nade_napalm_ball();
+ }
+}
+
+void nade_napalm_boom()
+{SELFPARAM();
+ entity fountain;
+ int c;
+ for (c = 0; c < autocvar_g_nades_napalm_ball_count; c++)
+ nade_napalm_ball();
+
+
+ fountain = spawn();
+ fountain.owner = self.owner;
+ fountain.realowner = self.realowner;
+ fountain.origin = self.origin;
+ setorigin(fountain, fountain.origin);
+ fountain.think = napalm_fountain_think;
+ fountain.nextthink = time;
+ fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime;
+ fountain.pushltime = fountain.ltime;
+ fountain.team = self.team;
+ fountain.movetype = MOVETYPE_TOSS;
+ fountain.projectiledeathtype = DEATH_NADE_NAPALM.m_id;
+ fountain.bot_dodge = true;
+ fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage;
+ fountain.nade_special_time = time;
+ setsize(fountain, '-16 -16 -16', '16 16 16');
+ CSQCProjectile(fountain, true, PROJECTILE_NAPALM_FOUNTAIN, true);
+}
+
+void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time)
+{
+ frost_target.frozen_by = freezefield.realowner;
+ Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1);
+ Freeze(frost_target, 1/freeze_time, 3, false);
+
+ Drop_Special_Items(frost_target);
+}
+
+void nade_ice_think()
+{SELFPARAM();
+
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ {
+ remove(self);
+ return;
+ }
+
+ if(time >= self.ltime)
+ {
+ if ( autocvar_g_nades_ice_explode )
+ {
+ entity expef = EFFECT_NADE_EXPLODE(self.realowner.team);
+ Send_Effect(expef, self.origin + '0 0 1', '0 0 0', 1);
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+
+ RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+ autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+ Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+ autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+ }
+ remove(self);
+ return;
+ }
+
+
+ self.nextthink = time+0.1;
+
+ // gaussian
+ float randomr;
+ randomr = random();
+ randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius;
+ float randomw;
+ randomw = random()*M_PI*2;
+ vector randomp;
+ randomp.x = randomr*cos(randomw);
+ randomp.y = randomr*sin(randomw);
+ randomp.z = 1;
+ Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, self.origin + randomp, '0 0 0', 1);
+
+ if(time >= self.nade_special_time)
+ {
+ self.nade_special_time = time+0.7;
+
+ Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1);
+ Send_Effect(EFFECT_ICEFIELD, self.origin, '0 0 0', 1);
+ }
+
+
+ float current_freeze_time = self.ltime - time - 0.1;
+
+ entity e;
+ for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain)
+ if(e != self)
+ if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner))
+ if(e.takedamage && e.deadflag == DEAD_NO)
+ if(e.health > 0)
+ if(!e.revival_time || ((time - e.revival_time) >= 1.5))
+ if(!e.frozen)
+ if(current_freeze_time > 0)
+ nade_ice_freeze(self, e, current_freeze_time);
+}
+
+void nade_ice_boom()
+{SELFPARAM();
+ entity fountain;
+ fountain = spawn();
+ fountain.owner = self.owner;
+ fountain.realowner = self.realowner;
+ fountain.origin = self.origin;
+ setorigin(fountain, fountain.origin);
+ fountain.think = nade_ice_think;
+ fountain.nextthink = time;
+ fountain.ltime = time + autocvar_g_nades_ice_freeze_time;
+ fountain.pushltime = fountain.wait = fountain.ltime;
+ fountain.team = self.team;
+ fountain.movetype = MOVETYPE_TOSS;
+ fountain.projectiledeathtype = DEATH_NADE_ICE.m_id;
+ fountain.bot_dodge = false;
+ setsize(fountain, '-16 -16 -16', '16 16 16');
+ fountain.nade_special_time = time+0.3;
+ fountain.angles = self.angles;
+
+ if ( autocvar_g_nades_ice_explode )
+ {
+ setmodel(fountain, MDL_PROJECTILE_GRENADE);
+ entity timer = new(nade_timer);
+ setmodel(timer, MDL_NADE_TIMER);
+ setattachment(timer, fountain, "");
+ timer.colormap = self.colormap;
+ timer.glowmod = self.glowmod;
+ timer.think = nade_timer_think;
+ timer.nextthink = time;
+ timer.wait = fountain.ltime;
+ timer.owner = fountain;
+ timer.skin = 10;
+ }
+ else
+ setmodel(fountain, MDL_Null);
+}
+
+void nade_translocate_boom()
+{SELFPARAM();
+ if(self.realowner.vehicle)
+ return;
+
+ vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins.z - 24);
+ tracebox(locout, self.realowner.mins, self.realowner.maxs, locout, MOVE_NOMONSTERS, self.realowner);
+ locout = trace_endpos;
+
+ makevectors(self.realowner.angles);
+
+ MUTATOR_CALLHOOK(PortalTeleport, self.realowner);
+
+ TeleportPlayer(self, self.realowner, locout, self.realowner.angles, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
+}
+
+void nade_spawn_boom()
+{SELFPARAM();
+ entity spawnloc = spawn();
+ setorigin(spawnloc, self.origin);
+ setsize(spawnloc, self.realowner.mins, self.realowner.maxs);
+ spawnloc.movetype = MOVETYPE_NONE;
+ spawnloc.solid = SOLID_NOT;
+ spawnloc.drawonlytoclient = self.realowner;
+ spawnloc.effects = EF_STARDUST;
+ spawnloc.cnt = autocvar_g_nades_spawn_count;
+
+ if(self.realowner.nade_spawnloc)
+ {
+ remove(self.realowner.nade_spawnloc);
+ self.realowner.nade_spawnloc = world;
+ }
+
+ self.realowner.nade_spawnloc = spawnloc;
+}
+
+void nade_heal_think()
+{SELFPARAM();
+ if(time >= self.ltime)
+ {
+ remove(self);
+ return;
+ }
+
+ self.nextthink = time;
+
+ if(time >= self.nade_special_time)
+ {
+ self.nade_special_time = time+0.25;
+ self.nade_show_particles = 1;
+ }
+ else
+ self.nade_show_particles = 0;
+}
+
+void nade_heal_touch()
+{SELFPARAM();
+ float maxhealth;
+ float health_factor;
+ if(IS_PLAYER(other) || IS_MONSTER(other))
+ if(other.deadflag == DEAD_NO)
+ if(!other.frozen)
+ {
+ health_factor = autocvar_g_nades_heal_rate*frametime/2;
+ if ( other != self.realowner )
+ {
+ if ( SAME_TEAM(other,self) )
+ health_factor *= autocvar_g_nades_heal_friend;
+ else
+ health_factor *= autocvar_g_nades_heal_foe;
+ }
+ if ( health_factor > 0 )
+ {
+ maxhealth = (IS_MONSTER(other)) ? other.max_health : g_pickup_healthmega_max;
+ if ( other.health < maxhealth )
+ {
+ if ( self.nade_show_particles )
+ Send_Effect(EFFECT_HEALING, other.origin, '0 0 0', 1);
+ other.health = min(other.health+health_factor, maxhealth);
+ }
+ other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
+ }
+ else if ( health_factor < 0 )
+ {
+ Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL.m_id,other.origin,'0 0 0');
+ }
+
+ }
+
+ if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) )
+ {
+ entity show_red = (IS_VEHICLE(other)) ? other.owner : other;
+ show_red.stat_healing_orb = time+0.1;
+ show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime;
+ }
+}
+
+void nade_heal_boom()
+{SELFPARAM();
+ entity healer;
+ healer = spawn();
+ healer.owner = self.owner;
+ healer.realowner = self.realowner;
+ setorigin(healer, self.origin);
+ healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar
+ healer.ltime = time + healer.healer_lifetime;
+ healer.team = self.realowner.team;
+ healer.bot_dodge = false;
+ healer.solid = SOLID_TRIGGER;
+ healer.touch = nade_heal_touch;
+
+ setmodel(healer, MDL_NADE_HEAL);
+ healer.healer_radius = autocvar_g_nades_nade_radius;
+ vector size = '1 1 1' * healer.healer_radius / 2;
+ setsize(healer,-size,size);
+
+ Net_LinkEntity(healer, true, 0, healer_send);
+
+ healer.think = nade_heal_think;
+ healer.nextthink = time;
+ healer.SendFlags |= 1;
+}
+
+void nade_monster_boom()
+{SELFPARAM();
+ entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, false, false, 1);
+
+ if(autocvar_g_nades_pokenade_monster_lifetime > 0)
+ e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime;
+ e.monster_skill = MONSTER_SKILL_INSANE;
+}
+
+void nade_boom()
+{SELFPARAM();
+ entity expef = NULL;
+ bool nade_blast = true;
+
+ switch ( Nades_from(self.nade_type) )
+ {
+ case NADE_TYPE_NAPALM:
+ nade_blast = autocvar_g_nades_napalm_blast;
+ expef = EFFECT_EXPLOSION_MEDIUM;
+ break;
+ case NADE_TYPE_ICE:
+ nade_blast = false;
+ expef = EFFECT_ELECTRO_COMBO; // hookbomb_explode electro_combo bigplasma_impact
+ break;
+ case NADE_TYPE_TRANSLOCATE:
+ nade_blast = false;
+ break;
+ case NADE_TYPE_MONSTER:
+ case NADE_TYPE_SPAWN:
+ nade_blast = false;
+ switch(self.realowner.team)
+ {
+ case NUM_TEAM_1: expef = EFFECT_SPAWN_RED; break;
+ case NUM_TEAM_2: expef = EFFECT_SPAWN_BLUE; break;
+ case NUM_TEAM_3: expef = EFFECT_SPAWN_YELLOW; break;
+ case NUM_TEAM_4: expef = EFFECT_SPAWN_PINK; break;
+ default: expef = EFFECT_SPAWN_NEUTRAL; break;
+ }
+ break;
+ case NADE_TYPE_HEAL:
+ nade_blast = false;
+ expef = EFFECT_SPAWN_RED;
+ break;
+
+ default:
+ case NADE_TYPE_NORMAL:
+ expef = EFFECT_NADE_EXPLODE(self.realowner.team);
+ break;
+ }
+
+ if(expef)
+ Send_Effect(expef, findbetterlocation(self.origin, 8), '0 0 0', 1);
+
+ sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+
+ self.event_damage = func_null; // prevent somehow calling damage in the next call
+
+ if(nade_blast)
+ {
+ RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+ autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+ Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+ }
+
+ if(self.takedamage)
+ switch ( Nades_from(self.nade_type) )
+ {
+ case NADE_TYPE_NAPALM: nade_napalm_boom(); break;
+ case NADE_TYPE_ICE: nade_ice_boom(); break;
+ case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break;
+ case NADE_TYPE_SPAWN: nade_spawn_boom(); break;
+ case NADE_TYPE_HEAL: nade_heal_boom(); break;
+ case NADE_TYPE_MONSTER: nade_monster_boom(); break;
+ }
+
+ entity head;
+ for(head = world; (head = find(head, classname, "grapplinghook")); )
+ if(head.aiment == self)
+ RemoveGrapplingHook(head.realowner);
+
+ remove(self);
+}
+
+void nade_touch()
+{SELFPARAM();
+ /*float is_weapclip = 0;
+ if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
+ if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID))
+ if (!(trace_dphitcontents & DPCONTENTS_OPAQUE))
+ is_weapclip = 1;*/
+ if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip)
+ {
+ entity head;
+ for(head = world; (head = find(head, classname, "grapplinghook")); )
+ if(head.aiment == self)
+ RemoveGrapplingHook(head.realowner);
+ remove(self);
+ return;
+ }
+
+ PROJECTILE_TOUCH;
+
+ //setsize(self, '-2 -2 -2', '2 2 2');
+ //UpdateCSQCProjectile(self);
+ if(self.health == self.max_health)
+ {
+ spamsound(self, CH_SHOTS, SND(GRENADE_BOUNCE_RANDOM()), VOL_BASE, ATTEN_NORM);
+ return;
+ }
+
+ self.enemy = other;
+ nade_boom();
+}
+
+void nade_beep()
+{SELFPARAM();
+ sound(self, CH_SHOTS_SINGLE, SND_NADE_BEEP, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX));
+ self.think = nade_boom;
+ self.nextthink = max(self.wait, time);
+}
+
+void nade_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{SELFPARAM();
+ if(ITEM_DAMAGE_NEEDKILL(deathtype))
+ {
+ self.takedamage = DAMAGE_NO;
+ nade_boom();
+ return;
+ }
+
+ if(self.nade_type == NADE_TYPE_TRANSLOCATE.m_id || self.nade_type == NADE_TYPE_SPAWN.m_id)
+ return;
+
+ if (MUTATOR_CALLHOOK(Nade_Damage, DEATH_WEAPONOF(deathtype), force, damage)) {}
+ else if(DEATH_ISWEAPON(deathtype, WEP_BLASTER))
+ {
+ force *= 1.5;
+ damage = 0;
+ }
+ else if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER) && (deathtype & HITTYPE_SECONDARY))
+ {
+ force *= 0.5; // too much
+ damage = 0;
+ }
+ else if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER))
+ {
+ force *= 6;
+ damage = self.max_health * 0.55;
+ }
+ else if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN))
+ damage = self.max_health * 0.1;
+ else if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) // WEAPONTODO
+ {
+ if(deathtype & HITTYPE_SECONDARY)
+ {
+ damage = self.max_health * 0.1;
+ force *= 10;
+ }
+ else
+ damage = self.max_health * 1.15;
+ }
+
+ self.velocity += force;
+ UpdateCSQCProjectile(self);
+
+ if(damage <= 0 || ((self.flags & FL_ONGROUND) && IS_PLAYER(attacker)))
+ return;
+
+ if(self.health == self.max_health)
+ {
+ sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX));
+ self.nextthink = max(time + autocvar_g_nades_nade_lifetime, time);
+ self.think = nade_beep;
+ }
+
+ self.health -= damage;
+
+ if ( self.nade_type != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker) )
+ self.realowner = attacker;
+
+ if(self.health <= 0)
+ W_PrepareExplosionByDamage(attacker, nade_boom);
+ else
+ nade_burn_spawn(self);
+}
+
+void toss_nade(entity e, vector _velocity, float _time)
+{SELFPARAM();
+ if(e.nade == world)
+ return;
+
+ entity _nade = e.nade;
+ e.nade = world;
+
+ remove(e.fake_nade);
+ e.fake_nade = world;
+
+ makevectors(e.v_angle);
+
+ W_SetupShot(e, false, false, "", CH_WEAPON_A, 0);
+
+ Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES);
+
+ vector offset = (v_forward * autocvar_g_nades_throw_offset.x)
+ + (v_right * autocvar_g_nades_throw_offset.y)
+ + (v_up * autocvar_g_nades_throw_offset.z);
+ if(autocvar_g_nades_throw_offset == '0 0 0')
+ offset = '0 0 0';
+
+ setorigin(_nade, w_shotorg + offset + (v_right * 25) * -1);
+ //setmodel(_nade, MDL_PROJECTILE_NADE);
+ //setattachment(_nade, world, "");
+ PROJECTILE_MAKETRIGGER(_nade);
+ setsize(_nade, '-16 -16 -16', '16 16 16');
+ _nade.movetype = MOVETYPE_BOUNCE;
+
+ tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, false, _nade);
+ if (trace_startsolid)
+ setorigin(_nade, e.origin);
+
+ if(self.v_angle.x >= 70 && self.v_angle.x <= 110 && self.BUTTON_CROUCH)
+ _nade.velocity = '0 0 100';
+ else if(autocvar_g_nades_nade_newton_style == 1)
+ _nade.velocity = e.velocity + _velocity;
+ else if(autocvar_g_nades_nade_newton_style == 2)
+ _nade.velocity = _velocity;
+ else
+ _nade.velocity = W_CalculateProjectileVelocity(e.velocity, _velocity, true);
+
+ _nade.touch = nade_touch;
+ _nade.health = autocvar_g_nades_nade_health;
+ _nade.max_health = _nade.health;
+ _nade.takedamage = DAMAGE_AIM;
+ _nade.event_damage = nade_damage;
+ _nade.customizeentityforclient = func_null;
+ _nade.exteriormodeltoclient = world;
+ _nade.traileffectnum = 0;
+ _nade.teleportable = true;
+ _nade.pushable = true;
+ _nade.gravity = 1;
+ _nade.missile_flags = MIF_SPLASH | MIF_ARC;
+ _nade.damagedbycontents = true;
+ _nade.angles = vectoangles(_nade.velocity);
+ _nade.flags = FL_PROJECTILE;
+ _nade.projectiledeathtype = DEATH_NADE.m_id;
+ _nade.toss_time = time;
+ _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX);
+
+ if(_nade.nade_type == NADE_TYPE_TRANSLOCATE.m_id || _nade.nade_type == NADE_TYPE_SPAWN.m_id)
+ _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+ else
+ _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
+
+ nade_spawn(_nade);
+
+ if(_time)
+ {
+ _nade.think = nade_boom;
+ _nade.nextthink = _time;
+ }
+
+ e.nade_refire = time + autocvar_g_nades_nade_refire;
+ e.nade_timer = 0;
+}
+
+void nades_GiveBonus(entity player, float score)
+{
+ if (autocvar_g_nades)
+ if (autocvar_g_nades_bonus)
+ if (IS_REAL_CLIENT(player))
+ if (IS_PLAYER(player) && player.bonus_nades < autocvar_g_nades_bonus_max)
+ if (player.frozen == 0)
+ if (player.deadflag == DEAD_NO)
+ {
+ if ( player.bonus_nade_score < 1 )
+ player.bonus_nade_score += score/autocvar_g_nades_bonus_score_max;
+
+ if ( player.bonus_nade_score >= 1 )
+ {
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS);
+ play2(player, SND(KH_ALARM));
+ player.bonus_nades++;
+ player.bonus_nade_score -= 1;
+ }
+ }
+}
+
+/** Remove all bonus nades from a player */
+void nades_RemoveBonus(entity player)
+{
+ player.bonus_nades = player.bonus_nade_score = 0;
+}
+
+MUTATOR_HOOKFUNCTION(nades, PutClientInServer)
+{
+ nades_RemoveBonus(self);
+}
+
+float nade_customize()
+{SELFPARAM();
+ //if(IS_SPEC(other)) { return false; }
+ if(other == self.realowner || (IS_SPEC(other) && other.enemy == self.realowner))
+ {
+ // somewhat hide the model, but keep the glow
+ //self.effects = 0;
+ if(self.traileffectnum)
+ self.traileffectnum = 0;
+ self.alpha = -1;
+ }
+ else
+ {
+ //self.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+ if(!self.traileffectnum)
+ self.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades_from(self.nade_type).m_projectile[false], self.team).eent_eff_name);
+ self.alpha = 1;
+ }
+
+ return true;
+}
+
+void nade_prime()
+{SELFPARAM();
+ if(autocvar_g_nades_bonus_only)
+ if(!self.bonus_nades)
+ return; // only allow bonus nades
+
+ if(self.nade)
+ remove(self.nade);
+
+ if(self.fake_nade)
+ remove(self.fake_nade);
+
+ entity n = new(nade), fn = new(fake_nade);
+
+ if(self.items & ITEM_Strength.m_itemid && autocvar_g_nades_bonus_onstrength)
+ n.nade_type = self.nade_type;
+ else if (self.bonus_nades >= 1)
+ {
+ n.nade_type = self.nade_type;
+ n.pokenade_type = self.pokenade_type;
+ self.bonus_nades -= 1;
+ }
+ else
+ {
+ n.nade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_nade_type : autocvar_g_nades_nade_type);
+ n.pokenade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type);
+ }
+
+ n.nade_type = bound(1, n.nade_type, Nades_COUNT);
+
+ setmodel(n, MDL_PROJECTILE_NADE);
+ //setattachment(n, self, "bip01 l hand");
+ n.exteriormodeltoclient = self;
+ n.customizeentityforclient = nade_customize;
+ n.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades_from(n.nade_type).m_projectile[false], self.team).eent_eff_name);
+ n.colormod = Nades_from(n.nade_type).m_color;
+ n.realowner = self;
+ n.colormap = self.colormap;
+ n.glowmod = self.glowmod;
+ n.wait = time + autocvar_g_nades_nade_lifetime;
+ n.nade_time_primed = time;
+ n.think = nade_beep;
+ n.nextthink = max(n.wait - 3, time);
+ n.projectiledeathtype = DEATH_NADE.m_id;
+
+ setmodel(fn, MDL_NADE_VIEW);
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+ setattachment(fn, self.(weaponentity), "");
+ fn.realowner = fn.owner = self;
+ fn.colormod = Nades_from(n.nade_type).m_color;
+ fn.colormap = self.colormap;
+ fn.glowmod = self.glowmod;
+ fn.think = SUB_Remove;
+ fn.nextthink = n.wait;
+
+ self.nade = n;
+ self.fake_nade = fn;
+}
+
+float CanThrowNade()
+{SELFPARAM();
+ if(self.vehicle)
+ return false;
+
+ if(gameover)
+ return false;
+
+ if(self.deadflag != DEAD_NO)
+ return false;
+
+ if (!autocvar_g_nades)
+ return false; // allow turning them off mid match
+
+ if(forbidWeaponUse(self))
+ return false;
+
+ if (!IS_PLAYER(self))
+ return false;
+
+ return true;
+}
+
+.bool nade_altbutton;
+
+void nades_CheckThrow()
+{SELFPARAM();
+ if(!CanThrowNade())
+ return;
+
+ entity held_nade = self.nade;
+ if (!held_nade)
+ {
+ self.nade_altbutton = true;
+ if(time > self.nade_refire)
+ {
+ Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_NADE_THROW);
+ nade_prime();
+ self.nade_refire = time + autocvar_g_nades_nade_refire;
+ }
+ }
+ else
+ {
+ self.nade_altbutton = false;
+ if (time >= held_nade.nade_time_primed + 1) {
+ makevectors(self.v_angle);
+ float _force = time - held_nade.nade_time_primed;
+ _force /= autocvar_g_nades_nade_lifetime;
+ _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce));
+ toss_nade(self, (v_forward * 0.75 + v_up * 0.2 + v_right * 0.05) * _force, 0);
+ }
+ }
+}
+
+void nades_Clear(entity player)
+{
+ if(player.nade)
+ remove(player.nade);
+ if(player.fake_nade)
+ remove(player.fake_nade);
+
+ player.nade = player.fake_nade = world;
+ player.nade_timer = 0;
+}
+
+MUTATOR_HOOKFUNCTION(nades, VehicleEnter)
+{
+ if(vh_player.nade)
+ toss_nade(vh_player, '0 0 100', max(vh_player.nade.wait, time + 0.05));
+
+ return false;
+}
+
+CLASS(NadeOffhand, OffhandWeapon)
+ METHOD(NadeOffhand, offhand_think, void(NadeOffhand this, entity player, bool key_pressed))
+ {
+ entity held_nade = player.nade;
+ if (held_nade)
+ {
+ player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / autocvar_g_nades_nade_lifetime, 1);
+ // LOG_TRACEF("%d %d\n", player.nade_timer, time - held_nade.nade_time_primed);
+ makevectors(player.angles);
+ held_nade.velocity = player.velocity;
+ setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0);
+ held_nade.angles_y = player.angles.y;
+
+ if (time + 0.1 >= held_nade.wait)
+ toss_nade(player, '0 0 0', time + 0.05);
+ }
+
+ if (!CanThrowNade()) return;
+ if (!(time > player.nade_refire)) return;
+ if (key_pressed) {
+ if (!held_nade) {
+ nade_prime();
+ held_nade = player.nade;
+ }
+ } else if (time >= held_nade.nade_time_primed + 1) {
+ if (held_nade) {
+ makevectors(player.v_angle);
+ float _force = time - held_nade.nade_time_primed;
+ _force /= autocvar_g_nades_nade_lifetime;
+ _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce));
+ toss_nade(player, (v_forward * 0.7 + v_up * 0.2 + v_right * 0.1) * _force, 0);
+ }
+ }
+ }
+ENDCLASS(NadeOffhand)
+NadeOffhand OFFHAND_NADE; STATIC_INIT(OFFHAND_NADE) { OFFHAND_NADE = NEW(NadeOffhand); }
+
+MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST)
+{
+ if (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)) || autocvar_g_nades_override_dropweapon) {
+ nades_CheckThrow();
+ return true;
+ }
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, PlayerPreThink)
+{SELFPARAM();
+ if (!IS_PLAYER(self)) { return false; }
+
+ if (self.nade && (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, self, self.nade_altbutton);
+
+ if(IS_PLAYER(self))
+ {
+ if ( autocvar_g_nades_bonus && autocvar_g_nades )
+ {
+ entity key;
+ float key_count = 0;
+ FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; }
+
+ float time_score;
+ if(self.flagcarried || self.ballcarried) // this player is important
+ time_score = autocvar_g_nades_bonus_score_time_flagcarrier;
+ else
+ time_score = autocvar_g_nades_bonus_score_time;
+
+ if(key_count)
+ time_score = autocvar_g_nades_bonus_score_time_flagcarrier * key_count; // multiply by the number of keys the player is holding
+
+ if(autocvar_g_nades_bonus_client_select)
+ {
+ self.nade_type = self.cvar_cl_nade_type;
+ self.pokenade_type = self.cvar_cl_pokenade_type;
+ }
+ else
+ {
+ self.nade_type = autocvar_g_nades_bonus_type;
+ self.pokenade_type = autocvar_g_nades_pokenade_monster_type;
+ }
+
+ self.nade_type = bound(1, self.nade_type, Nades_COUNT);
+
+ if(self.bonus_nade_score >= 0 && autocvar_g_nades_bonus_score_max)
+ nades_GiveBonus(self, time_score / autocvar_g_nades_bonus_score_max);
+ }
+ else
+ {
+ self.bonus_nades = self.bonus_nade_score = 0;
+ }
+ }
+
+ float n = 0;
+ entity o = world;
+ if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
+ n = -1;
+ else
+ {
+ vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
+ n = 0;
+ FOR_EACH_PLAYER(other) if(self != other)
+ {
+ if(other.deadflag == DEAD_NO)
+ if(other.frozen == 0)
+ if(SAME_TEAM(other, self))
+ if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+ {
+ if(!o)
+ o = other;
+ if(self.frozen == 1)
+ other.reviving = true;
+ ++n;
+ }
+ }
+ }
+
+ if(n && self.frozen == 3) // OK, there is at least one teammate reviving us
+ {
+ self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
+ self.health = max(1, self.revive_progress * start_health);
+
+ if(self.revive_progress >= 1)
+ {
+ Unfreeze(self);
+
+ Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname);
+ Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname);
+ }
+
+ FOR_EACH_PLAYER(other) if(other.reviving)
+ {
+ other.revive_progress = self.revive_progress;
+ other.reviving = false;
+ }
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, PlayerSpawn)
+{SELFPARAM();
+ if(autocvar_g_nades_spawn)
+ self.nade_refire = time + autocvar_g_spawnshieldtime;
+ else
+ self.nade_refire = time + autocvar_g_nades_nade_refire;
+
+ if(autocvar_g_nades_bonus_client_select)
+ self.nade_type = self.cvar_cl_nade_type;
+
+ self.nade_timer = 0;
+
+ if (!self.offhand) self.offhand = OFFHAND_NADE;
+
+ if(self.nade_spawnloc)
+ {
+ setorigin(self, self.nade_spawnloc.origin);
+ self.nade_spawnloc.cnt -= 1;
+
+ if(self.nade_spawnloc.cnt <= 0)
+ {
+ remove(self.nade_spawnloc);
+ self.nade_spawnloc = world;
+ }
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, PlayerDies, CBC_ORDER_LAST)
+{
+ if(frag_target.nade)
+ if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade)
+ toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05));
+
+ float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor);
+
+ if(IS_PLAYER(frag_attacker))
+ {
+ if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target)
+ nades_RemoveBonus(frag_attacker);
+ else if(frag_target.flagcarried)
+ nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium);
+ else if(autocvar_g_nades_bonus_score_spree && frag_attacker.killcount > 1)
+ {
+ #define SPREE_ITEM(counta,countb,center,normal,gentle) \
+ case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; }
+ switch(frag_attacker.killcount)
+ {
+ KILL_SPREE_LIST
+ default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break;
+ }
+ #undef SPREE_ITEM
+ }
+ else
+ nades_GiveBonus(frag_attacker, killcount_bonus);
+ }
+
+ nades_RemoveBonus(frag_target);
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, PlayerDamage_Calculate)
+{
+ if(frag_target.frozen)
+ if(autocvar_g_freezetag_revive_nade)
+ if(frag_attacker == frag_target)
+ if(frag_deathtype == DEATH_NADE.m_id)
+ if(time - frag_inflictor.toss_time <= 0.1)
+ {
+ Unfreeze(frag_target);
+ frag_target.health = autocvar_g_freezetag_revive_nade_health;
+ Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3);
+ frag_damage = 0;
+ frag_force = '0 0 0';
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname);
+ Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, MonsterDies)
+{SELFPARAM();
+ if(IS_PLAYER(frag_attacker))
+ if(DIFF_TEAM(frag_attacker, self))
+ if(!(self.spawnflags & MONSTERFLAG_SPAWNED))
+ nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor);
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, DropSpecialItems)
+{
+ if(frag_target.nade)
+ toss_nade(frag_target, '0 0 0', time + 0.05);
+
+ return false;
+}
+
+bool nades_RemovePlayer()
+{SELFPARAM();
+ nades_Clear(self);
+ nades_RemoveBonus(self);
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, MakePlayerObserver) { nades_RemovePlayer(); }
+MUTATOR_HOOKFUNCTION(nades, ClientDisconnect) { nades_RemovePlayer(); }
+MUTATOR_HOOKFUNCTION(nades, reset_map_global) { nades_RemovePlayer(); }
+
+MUTATOR_HOOKFUNCTION(nades, SpectateCopy)
+{SELFPARAM();
+ self.nade_timer = other.nade_timer;
+ self.nade_type = other.nade_type;
+ self.pokenade_type = other.pokenade_type;
+ self.bonus_nades = other.bonus_nades;
+ self.bonus_nade_score = other.bonus_nade_score;
+ self.stat_healing_orb = other.stat_healing_orb;
+ self.stat_healing_orb_alpha = other.stat_healing_orb_alpha;
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nade_type, "cl_nade_type");
+ GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_pokenade_type, "cl_pokenade_type");
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":Nades");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Nades");
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "new_toys.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+/*
+
+CORE laser vortex lg rl cry gl elec hagar fireb hook
+ vaporizer porto
+ tuba
+
+NEW rifle hlac minel seeker
+IDEAS OPEN flak OPEN FUN FUN FUN FUN
+
+
+
+How this mutator works:
+ =======================
+
+When a gun tries to spawn, this mutator is called. It will provide alternate
+weaponreplace lists.
+
+Entity:
+
+{
+"classname" "weapon_vortex"
+"new_toys" "rifle"
+}
+-> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise.
+
+{
+"classname" "weapon_vortext"
+"new_toys" "vortex rifle"
+}
+-> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise.
+
+{
+"classname" "weapon_vortex"
+"new_toys" "vortex"
+}
+-> This is always a Vortex.
+
+If the map specifies no "new_toys" argument
+
+There will be two default replacements selectable: "replace all" and "replace random".
+In "replace all" mode, e.g. Vortex will have the default replacement "rifle".
+In "replace random" mode, Vortex will have the default replacement "vortex rifle".
+
+This mutator's replacements run BEFORE regular weaponreplace!
+
+The New Toys guns do NOT get a spawn function, so they can only ever be spawned
+when this mutator is active.
+
+Likewise, warmup, give all, give ALL and impulse 99 will not give them unless
+this mutator is active.
+
+Outside this mutator, they still can be spawned by:
+- setting their start weapon cvar to 1
+- give weaponname
+- weaponreplace
+- weaponarena (but all and most weapons arena again won't include them)
+
+This mutator performs the default replacements on the DEFAULTS of the
+start weapon selection.
+
+These weapons appear in the menu's priority list, BUT get a suffix
+"(Mutator weapon)".
+
+Picking up a "new toys" weapon will not play standard weapon pickup sound, but
+roflsound "New toys, new toys!" sound.
+
+*/
+
+bool nt_IsNewToy(int w);
+
+REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill"))
+{
+ MUTATOR_ONADD
+ {
+ if(time > 1) // game loads at time 1
+ error("This cannot be added at runtime\n");
+
+ // mark the guns as ok to use by e.g. impulse 99
+ for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
+ if(nt_IsNewToy(i))
+ get_weaponinfo(i).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+ }
+
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
+ if(nt_IsNewToy(i))
+ get_weaponinfo(i).spawnflags |= WEP_FLAG_MUTATORBLOCKED;
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ LOG_INFO("This cannot be removed at runtime\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+.string new_toys;
+
+float autocvar_g_new_toys_autoreplace;
+bool autocvar_g_new_toys_use_pickupsound = true;
+const float NT_AUTOREPLACE_NEVER = 0;
+const float NT_AUTOREPLACE_ALWAYS = 1;
+const float NT_AUTOREPLACE_RANDOM = 2;
+
+MUTATOR_HOOKFUNCTION(nt, SetModname)
+{
+ modname = "NewToys";
+ return 0;
+}
+
+bool nt_IsNewToy(int w)
+{
+ switch(w)
+ {
+ case WEP_SEEKER.m_id:
+ case WEP_MINE_LAYER.m_id:
+ case WEP_HLAC.m_id:
+ case WEP_RIFLE.m_id:
+ case WEP_SHOCKWAVE.m_id:
+ return true;
+ default:
+ return false;
+ }
+}
+
+string nt_GetFullReplacement(string w)
+{
+ switch(w)
+ {
+ case "hagar": return "seeker";
+ case "devastator": return "minelayer";
+ case "machinegun": return "hlac";
+ case "vortex": return "rifle";
+ //case "shotgun": return "shockwave";
+ default: return string_null;
+ }
+}
+
+string nt_GetReplacement(string w, float m)
+{
+ if(m == NT_AUTOREPLACE_NEVER)
+ return w;
+ string s = nt_GetFullReplacement(w);
+ if (!s)
+ return w;
+ if(m == NT_AUTOREPLACE_RANDOM)
+ s = strcat(w, " ", s);
+ return s;
+}
+
+MUTATOR_HOOKFUNCTION(nt, SetStartItems)
+{
+ // rearrange start_weapon_default
+ // apply those bits that are set by start_weapon_defaultmask
+ // same for warmup
+
+ float i, j, k, n;
+
+ WepSet newdefault;
+ WepSet warmup_newdefault;
+
+ newdefault = '0 0 0';
+ warmup_newdefault = '0 0 0';
+
+ for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+ {
+ entity e = get_weaponinfo(i);
+ if(!e.weapon)
+ continue;
+
+ n = tokenize_console(nt_GetReplacement(e.netname, autocvar_g_new_toys_autoreplace));
+
+ for(j = 0; j < n; ++j)
+ for(k = WEP_FIRST; k <= WEP_LAST; ++k)
+ if(get_weaponinfo(k).netname == argv(j))
+ {
+ if(start_weapons & WepSet_FromWeapon(i))
+ newdefault |= WepSet_FromWeapon(k);
+ if(warmup_start_weapons & WepSet_FromWeapon(i))
+ warmup_newdefault |= WepSet_FromWeapon(k);
+ }
+ }
+
+ newdefault &= start_weapons_defaultmask;
+ start_weapons &= ~start_weapons_defaultmask;
+ start_weapons |= newdefault;
+
+ warmup_newdefault &= warmup_start_weapons_defaultmask;
+ warmup_start_weapons &= ~warmup_start_weapons_defaultmask;
+ warmup_start_weapons |= warmup_newdefault;
+
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace)
+{SELFPARAM();
+ // otherwise, we do replace
+ if(self.new_toys)
+ {
+ // map defined replacement:
+ ret_string = self.new_toys;
+ }
+ else
+ {
+ // auto replacement:
+ ret_string = nt_GetReplacement(other.netname, autocvar_g_new_toys_autoreplace);
+ }
+
+ // apply regular weaponreplace
+ ret_string = W_Apply_Weaponreplace(ret_string);
+
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(nt, FilterItem)
+{SELFPARAM();
+ if(nt_IsNewToy(self.weapon) && autocvar_g_new_toys_use_pickupsound) {
+ self.item_pickupsound = string_null;
+ self.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS;
+ }
+ return 0;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "nix.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+int autocvar_g_balance_nix_ammo_cells;
+int autocvar_g_balance_nix_ammo_plasma;
+int autocvar_g_balance_nix_ammo_fuel;
+int autocvar_g_balance_nix_ammo_nails;
+int autocvar_g_balance_nix_ammo_rockets;
+int autocvar_g_balance_nix_ammo_shells;
+int autocvar_g_balance_nix_ammoincr_cells;
+int autocvar_g_balance_nix_ammoincr_plasma;
+int autocvar_g_balance_nix_ammoincr_fuel;
+int autocvar_g_balance_nix_ammoincr_nails;
+int autocvar_g_balance_nix_ammoincr_rockets;
+int autocvar_g_balance_nix_ammoincr_shells;
+float autocvar_g_balance_nix_incrtime;
+float autocvar_g_balance_nix_roundtime;
+bool autocvar_g_nix_with_healtharmor;
+bool autocvar_g_nix_with_blaster;
+bool autocvar_g_nix_with_powerups;
+int autocvar_g_pickup_cells_max;
+int autocvar_g_pickup_plasma_max;
+int autocvar_g_pickup_fuel_max;
+int autocvar_g_pickup_nails_max;
+int autocvar_g_pickup_rockets_max;
+int autocvar_g_pickup_shells_max;
+
+float g_nix_with_blaster;
+// WEAPONTODO
+int nix_weapon;
+float nix_nextchange;
+float nix_nextweapon;
+.float nix_lastchange_id;
+.float nix_lastinfotime;
+.float nix_nextincr;
+
+bool NIX_CanChooseWeapon(int wpn);
+
+REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill"))
+{
+ MUTATOR_ONADD
+ {
+ g_nix_with_blaster = autocvar_g_nix_with_blaster;
+
+ nix_nextchange = 0;
+ nix_nextweapon = 0;
+
+ for (int i = WEP_FIRST; i <= WEP_LAST; ++i)
+ if (NIX_CanChooseWeapon(i)) {
+ Weapon w = get_weaponinfo(i);
+ w.wr_init(w);
+ }
+ }
+
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ // nothing to roll back
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ // as the PlayerSpawn hook will no longer run, NIX is turned off by this!
+ entity e;
+ FOR_EACH_PLAYER(e) if(e.deadflag == DEAD_NO)
+ {
+ e.ammo_cells = start_ammo_cells;
+ e.ammo_plasma = start_ammo_plasma;
+ e.ammo_shells = start_ammo_shells;
+ e.ammo_nails = start_ammo_nails;
+ e.ammo_rockets = start_ammo_rockets;
+ e.ammo_fuel = start_ammo_fuel;
+ e.weapons = start_weapons;
+ if(!client_hasweapon(e, e.weapon, true, false))
+ e.switchweapon = w_getbestweapon(self);
+ }
+ }
+
+ return 0;
+}
+
+bool NIX_CanChooseWeapon(int wpn)
+{
+ entity e = get_weaponinfo(wpn);
+ if(!e.weapon) // skip dummies
+ return false;
+ if(g_weaponarena)
+ {
+ if(!(g_weaponarena_weapons & WepSet_FromWeapon(wpn)))
+ return false;
+ }
+ else
+ {
+ if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster)
+ return false;
+ if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
+ return false;
+ if (!(e.spawnflags & WEP_FLAG_NORMAL))
+ return false;
+ }
+ return true;
+}
+void NIX_ChooseNextWeapon()
+{
+ float j;
+ RandomSelection_Init();
+ for(j = WEP_FIRST; j <= WEP_LAST; ++j)
+ if(NIX_CanChooseWeapon(j))
+ RandomSelection_Add(world, j, string_null, 1, (j != nix_weapon));
+ nix_nextweapon = RandomSelection_chosen_float;
+}
+
+void NIX_GiveCurrentWeapon()
+{SELFPARAM();
+ float dt;
+
+ if(!nix_nextweapon)
+ NIX_ChooseNextWeapon();
+
+ dt = ceil(nix_nextchange - time);
+
+ if(dt <= 0)
+ {
+ nix_weapon = nix_nextweapon;
+ nix_nextweapon = 0;
+ if (!nix_nextchange) // no round played yet?
+ nix_nextchange = time; // start the first round now!
+ else
+ nix_nextchange = time + autocvar_g_balance_nix_roundtime;
+ // Weapon w = get_weaponinfo(nix_weapon);
+ // w.wr_init(w); // forget it, too slow
+ }
+
+ // get weapon info
+ entity e = get_weaponinfo(nix_weapon);
+
+ if(nix_nextchange != self.nix_lastchange_id) // this shall only be called once per round!
+ {
+ self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = self.ammo_plasma = self.ammo_fuel = 0;
+
+ if(self.items & IT_UNLIMITED_WEAPON_AMMO)
+ {
+ switch(e.ammo_field)
+ {
+ case ammo_shells: self.ammo_shells = autocvar_g_pickup_shells_max; break;
+ case ammo_nails: self.ammo_nails = autocvar_g_pickup_nails_max; break;
+ case ammo_rockets: self.ammo_rockets = autocvar_g_pickup_rockets_max; break;
+ case ammo_cells: self.ammo_cells = autocvar_g_pickup_cells_max; break;
+ case ammo_plasma: self.ammo_plasma = autocvar_g_pickup_plasma_max; break;
+ case ammo_fuel: self.ammo_fuel = autocvar_g_pickup_fuel_max; break;
+ }
+ }
+ else
+ {
+ switch(e.ammo_field)
+ {
+ case ammo_shells: self.ammo_shells = autocvar_g_balance_nix_ammo_shells; break;
+ case ammo_nails: self.ammo_nails = autocvar_g_balance_nix_ammo_nails; break;
+ case ammo_rockets: self.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break;
+ case ammo_cells: self.ammo_cells = autocvar_g_balance_nix_ammo_cells; break;
+ case ammo_plasma: self.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break;
+ case ammo_fuel: self.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break;
+ }
+ }
+
+ self.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
+ if(dt >= 1 && dt <= 5)
+ self.nix_lastinfotime = -42;
+ else
+ Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon);
+
+ Weapon w = get_weaponinfo(nix_weapon);
+ w.wr_resetplayer(w);
+
+ // all weapons must be fully loaded when we spawn
+ if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
+ self.(weapon_load[nix_weapon]) = e.reloading_ammo;
+
+ // vortex too
+ if(WEP_CVAR(vortex, charge))
+ {
+ if(WEP_CVAR_SEC(vortex, chargepool))
+ self.vortex_chargepool_ammo = 1;
+ self.vortex_charge = WEP_CVAR(vortex, charge_start);
+ }
+
+ // set last change info
+ self.nix_lastchange_id = nix_nextchange;
+ }
+ if(self.nix_lastinfotime != dt)
+ {
+ self.nix_lastinfotime = dt; // initial value 0 should count as "not seen"
+ if(dt >= 1 && dt <= 5)
+ Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt);
+ }
+
+ if(!(self.items & IT_UNLIMITED_WEAPON_AMMO) && time > self.nix_nextincr)
+ {
+ switch(e.ammo_field)
+ {
+ case ammo_shells: self.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break;
+ case ammo_nails: self.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break;
+ case ammo_rockets: self.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break;
+ case ammo_cells: self.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break;
+ case ammo_plasma: self.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break;
+ case ammo_fuel: self.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break;
+ }
+
+ self.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
+ }
+
+ self.weapons = '0 0 0';
+ if(g_nix_with_blaster)
+ self.weapons |= WEPSET(BLASTER);
+ self.weapons |= WepSet_FromWeapon(nix_weapon);
+
+ if(self.switchweapon != nix_weapon)
+ if(!client_hasweapon(self, self.switchweapon, true, false))
+ if(client_hasweapon(self, nix_weapon, true, false))
+ W_SwitchWeapon(nix_weapon);
+}
+
+MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon)
+{
+ return 1; // no throwing in NIX
+}
+
+MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":NIX");
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", NIX");
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(nix, FilterItem)
+{SELFPARAM();
+ switch (self.items)
+ {
+ case ITEM_HealthSmall.m_itemid:
+ case ITEM_HealthMedium.m_itemid:
+ case ITEM_HealthLarge.m_itemid:
+ case ITEM_HealthMega.m_itemid:
+ case ITEM_ArmorSmall.m_itemid:
+ case ITEM_ArmorMedium.m_itemid:
+ case ITEM_ArmorLarge.m_itemid:
+ case ITEM_ArmorMega.m_itemid:
+ if (autocvar_g_nix_with_healtharmor)
+ return 0;
+ break;
+ case ITEM_Strength.m_itemid:
+ case ITEM_Shield.m_itemid:
+ if (autocvar_g_nix_with_powerups)
+ return 0;
+ break;
+ }
+
+ return 1; // delete all other items
+}
+
+MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn)
+{SELFPARAM();
+ if(self.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo)
+ return 1;
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(nix, PlayerPreThink)
+{SELFPARAM();
+ if(!intermission_running)
+ if(self.deadflag == DEAD_NO)
+ if(IS_PLAYER(self))
+ NIX_GiveCurrentWeapon();
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(nix, PlayerSpawn)
+{SELFPARAM();
+ self.nix_lastchange_id = -1;
+ NIX_GiveCurrentWeapon(); // overrides the weapons you got when spawning
+ self.items |= IT_UNLIMITED_SUPERWEAPONS;
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST)
+{
+ modname = "NIX";
+ return 0;
+}
+#endif
--- /dev/null
+#ifndef IMPLEMENTATION
+CLASS(HeavyMachineGun, Weapon)
+/* ammotype */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails)
+/* impulse */ ATTRIB(HeavyMachineGun, impulse, int, 3)
+/* flags */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON);
+/* rating */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
+/* color */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0');
+/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg");
+#ifndef MENUQC
+/* model */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM);
+#endif
+/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi");
+/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6);
+/* wepimg */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg");
+/* refname */ ATTRIB(HeavyMachineGun, netname, string, "hmg");
+/* wepname */ ATTRIB(HeavyMachineGun, m_name, string, _("Heavy Machine Gun"));
+ENDCLASS(HeavyMachineGun)
+REGISTER_WEAPON(HMG, NEW(HeavyMachineGun));
+
+#define HMG_SETTINGS(w_cvar,w_prop) HMG_SETTINGS_LIST(w_cvar, w_prop, HMG, hmg)
+#define HMG_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
+ w_cvar(id, sn, NONE, spread_min) \
+ w_cvar(id, sn, NONE, spread_max) \
+ w_cvar(id, sn, NONE, spread_add) \
+ w_cvar(id, sn, NONE, solidpenetration) \
+ w_cvar(id, sn, NONE, damage) \
+ w_cvar(id, sn, NONE, force) \
+ w_cvar(id, sn, NONE, refire) \
+ w_cvar(id, sn, NONE, ammo) \
+ w_prop(id, sn, float, reloading_ammo, reload_ammo) \
+ w_prop(id, sn, float, reloading_time, reload_time) \
+ w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \
+ w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \
+ w_prop(id, sn, string, weaponreplace, weaponreplace) \
+ w_prop(id, sn, float, weaponstart, weaponstart) \
+ w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \
+ w_prop(id, sn, float, weaponthrowable, weaponthrowable)
+
+#ifdef SVQC
+HMG_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
+#endif
+#endif
+#ifdef IMPLEMENTATION
+#ifdef SVQC
+
+REGISTER_MUTATOR(hmg_nadesupport, true);
+MUTATOR_HOOKFUNCTION(hmg_nadesupport, Nade_Damage)
+{
+ if (MUTATOR_ARGV(0, entity) != WEP_HMG) return;
+ return = true;
+ MUTATOR_ARGV(0, float) /* damage */ = self.max_health * 0.1;
+}
+
+spawnfunc(weapon_hmg) { weapon_defaultspawnfunc(this, WEP_HMG); }
+
+void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
+{
+ if (!actor.BUTTON_ATCK)
+ {
+ w_ready(thiswep, actor, weaponentity, fire);
+ return;
+ }
+
+ Weapon w = get_weaponinfo(actor.weapon);
+ if(!w.wr_checkammo1(w))
+ if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
+ {
+ W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
+ w_ready(thiswep, actor, weaponentity, fire);
+ return;
+ }
+
+ W_DecreaseAmmo(WEP_HMG, self, WEP_CVAR(hmg, ammo));
+
+ W_SetupShot (actor, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(hmg, damage));
+
+ if(!autocvar_g_norecoil)
+ {
+ actor.punchangle_x = random () - 0.5;
+ actor.punchangle_y = random () - 0.5;
+ }
+
+ float hmg_spread = bound(WEP_CVAR(hmg, spread_min), WEP_CVAR(hmg, spread_min) + (WEP_CVAR(hmg, spread_add) * actor.misc_bulletcounter), WEP_CVAR(hmg, spread_max));
+ fireBullet(w_shotorg, w_shotdir, hmg_spread, WEP_CVAR(hmg, solidpenetration), WEP_CVAR(hmg, damage), WEP_CVAR(hmg, force), WEP_HMG.m_id, 0);
+
+ actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
+
+ Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
+
+ W_MachineGun_MuzzleFlash();
+ W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
+
+ if (autocvar_g_casings >= 2) // casing code
+ SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
+
+ int slot = weaponslot(weaponentity);
+ ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor();
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(hmg, refire), W_HeavyMachineGun_Attack_Auto);
+}
+
+ METHOD(HeavyMachineGun, wr_aim, void(entity thiswep))
+ {
+ if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200)
+ self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false);
+ else
+ self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false);
+ }
+ METHOD(HeavyMachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
+ {
+ if(WEP_CVAR(hmg, reload_ammo) && actor.clip_load < WEP_CVAR(hmg, ammo)) { // forced reload
+ Weapon w = get_weaponinfo(actor.weapon);
+ w.wr_reload(w);
+ } else
+ {
+ if (fire & 1)
+ if (weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
+ {
+ actor.misc_bulletcounter = 0;
+ W_HeavyMachineGun_Attack_Auto(thiswep, actor, weaponentity, fire);
+ }
+ }
+ }
+ METHOD(HeavyMachineGun, wr_init, void(entity thiswep))
+ {
+ HMG_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
+ }
+ METHOD(HeavyMachineGun, wr_checkammo1, bool(entity thiswep))
+ {
+ float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo);
+
+ if(autocvar_g_balance_hmg_reload_ammo)
+ ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
+
+ return ammo_amount;
+ }
+ METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep))
+ {
+ float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo);
+
+ if(autocvar_g_balance_hmg_reload_ammo)
+ ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
+
+ return ammo_amount;
+ }
+ METHOD(HeavyMachineGun, wr_config, void(entity thiswep))
+ {
+ HMG_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
+ }
+ METHOD(HeavyMachineGun, wr_reload, void(entity thiswep))
+ {
+ W_Reload(self, WEP_CVAR(hmg, ammo), SND(RELOAD));
+ }
+ METHOD(HeavyMachineGun, wr_suicidemessage, int(entity thiswep))
+ {
+ return WEAPON_THINKING_WITH_PORTALS;
+ }
+ METHOD(HeavyMachineGun, wr_killmessage, int(entity thiswep))
+ {
+ if(w_deathtype & HITTYPE_SECONDARY)
+ return WEAPON_HMG_MURDER_SNIPE;
+ else
+ return WEAPON_HMG_MURDER_SPRAY;
+ }
+
+#endif
+#ifdef CSQC
+
+ METHOD(HeavyMachineGun, wr_impacteffect, void(entity thiswep))
+ {
+ vector org2;
+ org2 = w_org + w_backoff * 2;
+ pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1);
+ if(!w_issilent)
+ sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM);
+ }
+
+#endif
+#endif
--- /dev/null
+#include "hmg.qc"
+#include "rpc.qc"
+
+#ifdef SVQC
+ #include "overkill.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+bool autocvar_g_overkill_powerups_replace;
+float autocvar_g_overkill_superguns_respawn_time;
+bool autocvar_g_overkill_100h_anyway;
+bool autocvar_g_overkill_100a_anyway;
+bool autocvar_g_overkill_ammo_charge;
+float autocvar_g_overkill_ammo_charge_notice;
+float autocvar_g_overkill_ammo_charge_limit;
+
+.vector ok_deathloc;
+.float ok_spawnsys_timer;
+.float ok_lastwep;
+.float ok_item;
+
+.float ok_notice_time;
+.float ammo_charge[Weapons_MAX];
+.float ok_use_ammocharge;
+.float ok_ammo_charge;
+
+.float ok_pauseregen_finished;
+
+void(entity ent, float wep) ok_DecreaseCharge;
+
+void ok_Initialize();
+
+REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
+{
+ MUTATOR_ONADD
+ {
+ ok_Initialize();
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
+ WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo)
+{
+ entity actor = MUTATOR_ARGV(0, entity);
+ if (actor.ok_use_ammocharge)
+ {
+ ok_DecreaseCharge(actor, actor.weapon);
+ return true;
+ }
+}
+
+MUTATOR_HOOKFUNCTION(ok, W_Reload)
+{
+ entity actor = MUTATOR_ARGV(0, entity);
+ return actor.ok_use_ammocharge;
+}
+
+void W_Blaster_Attack(entity, float, float, float, float, float, float, float, float, float, float);
+spawnfunc(weapon_hmg);
+spawnfunc(weapon_rpc);
+
+void ok_DecreaseCharge(entity ent, int wep)
+{
+ if(!ent.ok_use_ammocharge) return;
+
+ entity wepent = get_weaponinfo(wep);
+
+ if(wepent.weapon == 0)
+ return; // dummy
+
+ ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
+}
+
+void ok_IncreaseCharge(entity ent, int wep)
+{
+ entity wepent = get_weaponinfo(wep);
+
+ if(wepent.weapon == 0)
+ return; // dummy
+
+ if(ent.ok_use_ammocharge)
+ if(!ent.BUTTON_ATCK) // not while attacking?
+ ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME);
+}
+
+float ok_CheckWeaponCharge(entity ent, int wep)
+{
+ if(!ent.ok_use_ammocharge) return true;
+
+ entity wepent = get_weaponinfo(wep);
+
+ if(wepent.weapon == 0)
+ return 0; // dummy
+
+ return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
+}
+
+MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST)
+{
+ if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target))
+ if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
+ {
+ frag_damage = 0;
+
+ if(frag_attacker != frag_target)
+ if(frag_target.health > 0)
+ if(frag_target.frozen == 0)
+ if(frag_target.deadflag == DEAD_NO)
+ {
+ Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE);
+ frag_force = '0 0 0';
+ }
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, PlayerDamage_SplitHealthArmor)
+{SELFPARAM();
+ if(damage_take)
+ self.ok_pauseregen_finished = max(self.ok_pauseregen_finished, time + 2);
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, PlayerDies)
+{SELFPARAM();
+ entity targ = ((frag_attacker) ? frag_attacker : frag_target);
+
+ if(IS_MONSTER(self))
+ {
+ remove(other); // remove default item
+ other = world;
+ }
+
+ setself(new(droppedweapon)); // hax
+ self.ok_item = true;
+ self.noalign = true;
+ self.pickup_anyway = true;
+ spawnfunc_item_armor_small(this);
+ self.movetype = MOVETYPE_TOSS;
+ self.gravity = 1;
+ self.reset = SUB_Remove;
+ setorigin(self, frag_target.origin + '0 0 32');
+ self.velocity = '0 0 200' + normalize(targ.origin - self.origin) * 500;
+ SUB_SetFade(self, time + 5, 1);
+ setself(this);
+
+ self.ok_lastwep = self.switchweapon;
+
+ return false;
+}
+MUTATOR_HOOKFUNCTION(ok, MonsterDropItem) { ok_PlayerDies(); }
+
+MUTATOR_HOOKFUNCTION(ok, PlayerRegen)
+{SELFPARAM();
+ // overkill's values are different, so use custom regen
+ if(!self.frozen)
+ {
+ self.armorvalue = CalcRotRegen(self.armorvalue, autocvar_g_balance_armor_regenstable, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, 1 * frametime * (time > self.ok_pauseregen_finished), 0, 0, 1, 1 * frametime * (time > self.pauserotarmor_finished), autocvar_g_balance_armor_limit);
+ self.health = CalcRotRegen(self.health, autocvar_g_balance_health_regenstable, 0, 100, 1 * frametime * (time > self.ok_pauseregen_finished), 200, 0, autocvar_g_balance_health_rotlinear, 1 * frametime * (time > self.pauserothealth_finished), autocvar_g_balance_health_limit);
+
+ float minf, maxf, limitf;
+
+ maxf = autocvar_g_balance_fuel_rotstable;
+ minf = autocvar_g_balance_fuel_regenstable;
+ limitf = autocvar_g_balance_fuel_limit;
+
+ self.ammo_fuel = CalcRotRegen(self.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > self.pauseregen_finished) * ((self.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > self.pauserotfuel_finished), limitf);
+ }
+ return true; // return true anyway, as frozen uses no regen
+}
+
+MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon)
+{
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
+{SELFPARAM();
+ if(intermission_running || gameover)
+ return false;
+
+ if(self.deadflag != DEAD_NO || !IS_PLAYER(self) || self.frozen)
+ return false;
+
+ if(self.ok_lastwep)
+ {
+ self.switchweapon = self.ok_lastwep;
+ self.ok_lastwep = 0;
+ }
+
+ ok_IncreaseCharge(self, self.weapon);
+
+ if(self.BUTTON_ATCK2)
+ if(!forbidWeaponUse(self) || self.weapon_blocked) // allow if weapon is blocked
+ if(time >= self.jump_interval)
+ {
+ self.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor();
+ makevectors(self.v_angle);
+
+ int oldwep = self.weapon;
+ self.weapon = WEP_BLASTER.m_id;
+ W_Blaster_Attack(
+ self,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(vaporizer, shotangle),
+ WEP_CVAR_SEC(vaporizer, damage),
+ WEP_CVAR_SEC(vaporizer, edgedamage),
+ WEP_CVAR_SEC(vaporizer, radius),
+ WEP_CVAR_SEC(vaporizer, force),
+ WEP_CVAR_SEC(vaporizer, speed),
+ WEP_CVAR_SEC(vaporizer, spread),
+ WEP_CVAR_SEC(vaporizer, delay),
+ WEP_CVAR_SEC(vaporizer, lifetime)
+ );
+ self.weapon = oldwep;
+ }
+
+ self.weapon_blocked = false;
+
+ self.ok_ammo_charge = self.ammo_charge[self.weapon];
+
+ if(self.ok_use_ammocharge)
+ if(!ok_CheckWeaponCharge(self, self.weapon))
+ {
+ if(autocvar_g_overkill_ammo_charge_notice && time > self.ok_notice_time && self.BUTTON_ATCK && IS_REAL_CLIENT(self) && self.weapon == self.switchweapon)
+ {
+ //Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERKILL_CHARGE);
+ self.ok_notice_time = time + 2;
+ play2(self, SND(DRYFIRE));
+ }
+ Weapon wpn = get_weaponinfo(self.weapon);
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+ if(self.(weaponentity).state != WS_CLEAR)
+ w_ready(wpn, self, weaponentity, (self.BUTTON_ATCK ? 1 : 0) | (self.BUTTON_ATCK2 ? 2 : 0));
+
+ self.weapon_blocked = true;
+ }
+
+ self.BUTTON_ATCK2 = 0;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, PlayerSpawn)
+{SELFPARAM();
+ if(autocvar_g_overkill_ammo_charge)
+ {
+ for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
+ self.ammo_charge[i] = autocvar_g_overkill_ammo_charge_limit;
+
+ self.ok_use_ammocharge = 1;
+ self.ok_notice_time = time;
+ }
+ else
+ self.ok_use_ammocharge = 0;
+
+ self.ok_pauseregen_finished = time + 2;
+
+ return false;
+}
+
+void _spawnfunc_weapon_hmg() { SELFPARAM(); spawnfunc_weapon_hmg(this); }
+void _spawnfunc_weapon_rpc() { SELFPARAM(); spawnfunc_weapon_rpc(this); }
+
+MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn)
+{SELFPARAM();
+ if(autocvar_g_powerups)
+ if(autocvar_g_overkill_powerups_replace)
+ {
+ if(self.classname == "item_strength")
+ {
+ entity wep = new(weapon_hmg);
+ setorigin(wep, self.origin);
+ setmodel(wep, MDL_OK_HMG);
+ wep.ok_item = true;
+ wep.noalign = self.noalign;
+ wep.cnt = self.cnt;
+ wep.team = self.team;
+ wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
+ wep.pickup_anyway = true;
+ wep.think = _spawnfunc_weapon_hmg;
+ wep.nextthink = time + 0.1;
+ return true;
+ }
+
+ if(self.classname == "item_invincible")
+ {
+ entity wep = new(weapon_rpc);
+ setorigin(wep, self.origin);
+ setmodel(wep, MDL_OK_RPC);
+ wep.ok_item = true;
+ wep.noalign = self.noalign;
+ wep.cnt = self.cnt;
+ wep.team = self.team;
+ wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
+ wep.pickup_anyway = true;
+ wep.think = _spawnfunc_weapon_rpc;
+ wep.nextthink = time + 0.1;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, FilterItem)
+{SELFPARAM();
+ if(self.ok_item)
+ return false;
+
+ switch(self.items)
+ {
+ case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway);
+ case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway);
+ }
+
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(ok, SpectateCopy)
+{SELFPARAM();
+ self.ammo_charge[self.weapon] = other.ammo_charge[other.weapon];
+ self.ok_use_ammocharge = other.ok_use_ammocharge;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, SetStartItems)
+{
+ WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN));
+
+ if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); }
+ if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); }
+
+ start_items |= IT_UNLIMITED_WEAPON_AMMO;
+ start_weapons = warmup_start_weapons = ok_start_items;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":OK");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Overkill");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, SetModname)
+{
+ modname = "Overkill";
+ return true;
+}
+
+void ok_SetCvars()
+{
+ // hack to force overkill playermodels
+ cvar_settemp("sv_defaultcharacter", "1");
+ cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
+ cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm");
+ cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
+}
+
+void ok_Initialize()
+{
+ ok_SetCvars();
+
+ precache_all_playermodels("models/ok_player/*.dpm");
+
+ addstat(STAT_OK_AMMO_CHARGE, AS_FLOAT, ok_use_ammocharge);
+ addstat(STAT_OK_AMMO_CHARGEPOOL, AS_FLOAT, ok_ammo_charge);
+
+ WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+ WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+
+ WEP_SHOTGUN.mdl = "ok_shotgun";
+ WEP_MACHINEGUN.mdl = "ok_mg";
+ WEP_VORTEX.mdl = "ok_sniper";
+}
+#endif
--- /dev/null
+#ifndef IMPLEMENTATION
+CLASS(RocketPropelledChainsaw, Weapon)
+/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets)
+/* impulse */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7)
+/* flags */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON);
+/* rating */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
+/* color */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0');
+/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl");
+#ifndef MENUQC
+/* model */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM);
+#endif
+/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher");
+/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6);
+/* wepimg */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc");
+/* refname */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc");
+/* wepname */ ATTRIB(RocketPropelledChainsaw, m_name, string, _("Rocket Propelled Chainsaw"));
+ENDCLASS(RocketPropelledChainsaw)
+REGISTER_WEAPON(RPC, NEW(RocketPropelledChainsaw));
+
+#define RPC_SETTINGS(w_cvar,w_prop) RPC_SETTINGS_LIST(w_cvar, w_prop, RPC, rpc)
+#define RPC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
+ w_cvar(id, sn, NONE, ammo) \
+ w_cvar(id, sn, NONE, animtime) \
+ w_cvar(id, sn, NONE, damage) \
+ w_cvar(id, sn, NONE, damage2) \
+ w_cvar(id, sn, NONE, damageforcescale) \
+ w_cvar(id, sn, NONE, edgedamage) \
+ w_cvar(id, sn, NONE, force) \
+ w_cvar(id, sn, NONE, health) \
+ w_cvar(id, sn, NONE, lifetime) \
+ w_cvar(id, sn, NONE, radius) \
+ w_cvar(id, sn, NONE, refire) \
+ w_cvar(id, sn, NONE, speed) \
+ w_cvar(id, sn, NONE, speedaccel) \
+ w_prop(id, sn, float, reloading_ammo, reload_ammo) \
+ w_prop(id, sn, float, reloading_time, reload_time) \
+ w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \
+ w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \
+ w_prop(id, sn, string, weaponreplace, weaponreplace) \
+ w_prop(id, sn, float, weaponstart, weaponstart) \
+ w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \
+ w_prop(id, sn, float, weaponthrowable, weaponthrowable)
+
+#ifdef SVQC
+RPC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
+#endif
+#endif
+#ifdef IMPLEMENTATION
+#ifdef SVQC
+spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(this, WEP_RPC); }
+
+void W_RocketPropelledChainsaw_Explode()
+{SELFPARAM();
+ self.event_damage = func_null;
+ self.takedamage = DAMAGE_NO;
+
+ RadiusDamage (self, self.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), world, world, WEP_CVAR(rpc, force), self.projectiledeathtype, other);
+
+ remove (self);
+}
+
+void W_RocketPropelledChainsaw_Touch ()
+{SELFPARAM();
+ if(WarpZone_Projectile_Touch())
+ if(wasfreed(self))
+ return;
+
+ W_RocketPropelledChainsaw_Explode();
+}
+
+void W_RocketPropelledChainsaw_Damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{SELFPARAM();
+ if (self.health <= 0)
+ return;
+
+ if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
+ return; // g_projectiles_damage says to halt
+
+ self.health = self.health - damage;
+
+ if (self.health <= 0)
+ W_PrepareExplosionByDamage(attacker, W_RocketPropelledChainsaw_Explode);
+}
+
+void W_RocketPropelledChainsaw_Think()
+{SELFPARAM();
+ if(self.cnt <= time)
+ {
+ remove(self);
+ return;
+ }
+
+ self.cnt = vlen(self.velocity);
+ self.wait = self.cnt * sys_frametime;
+ self.pos1 = normalize(self.velocity);
+
+ tracebox(self.origin, self.mins, self.maxs, self.origin + self.pos1 * (2 * self.wait), MOVE_NORMAL, self);
+ if(IS_PLAYER(trace_ent))
+ Damage (trace_ent, self, self.realowner, WEP_CVAR(rpc, damage2), self.projectiledeathtype, self.origin, normalize(self.origin - other.origin) * WEP_CVAR(rpc, force));
+
+ self.velocity = self.pos1 * (self.cnt + (WEP_CVAR(rpc, speedaccel) * sys_frametime));
+
+ UpdateCSQCProjectile(self);
+ self.nextthink = time;
+}
+
+void W_RocketPropelledChainsaw_Attack (Weapon thiswep)
+{SELFPARAM();
+ entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(self);
+ entity flash = spawn ();
+
+ W_DecreaseAmmo(thiswep, self, WEP_CVAR(rpc, ammo));
+ W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', false, 5, SND(ROCKET_FIRE), CH_WEAPON_A, WEP_CVAR(rpc, damage));
+ Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
+ PROJECTILE_MAKETRIGGER(missile);
+
+ missile.owner = missile.realowner = self;
+ missile.bot_dodge = true;
+ missile.bot_dodgerating = WEP_CVAR(rpc, damage) * 2;
+
+ missile.takedamage = DAMAGE_YES;
+ missile.damageforcescale = WEP_CVAR(rpc, damageforcescale);
+ missile.health = WEP_CVAR(rpc, health);
+ missile.event_damage = W_RocketPropelledChainsaw_Damage;
+ missile.damagedbycontents = true;
+ missile.movetype = MOVETYPE_FLY;
+
+ missile.projectiledeathtype = WEP_RPC.m_id;
+ setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
+
+ setorigin (missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point
+ W_SetupProjVelocity_Basic(missile, WEP_CVAR(rpc, speed), 0);
+
+ missile.touch = W_RocketPropelledChainsaw_Touch;
+
+ missile.think = W_RocketPropelledChainsaw_Think;
+ missile.cnt = time + WEP_CVAR(rpc, lifetime);
+ missile.nextthink = time;
+ missile.flags = FL_PROJECTILE;
+
+ CSQCProjectile(missile, true, PROJECTILE_RPC, false);
+
+ setmodel(flash, MDL_RPC_MUZZLEFLASH); // precision set below
+ SUB_SetFade (flash, time, 0.1);
+ flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+ W_AttachToShotorg(self, flash, '5 0 0');
+ missile.pos1 = missile.velocity;
+
+ MUTATOR_CALLHOOK(EditProjectile, self, missile);
+}
+
+ METHOD(RocketPropelledChainsaw, wr_aim, void(entity thiswep))
+ {
+ self.BUTTON_ATCK = bot_aim(WEP_CVAR(rpc, speed), 0, WEP_CVAR(rpc, lifetime), false);
+ }
+ METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
+ {
+ if(WEP_CVAR(rpc, reload_ammo) && actor.clip_load < WEP_CVAR(rpc, ammo)) {
+ Weapon w = get_weaponinfo(actor.weapon);
+ w.wr_reload(w);
+ } else
+ {
+ if (fire & 1)
+ {
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(rpc, refire)))
+ {
+ W_RocketPropelledChainsaw_Attack(thiswep);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready);
+ }
+ }
+
+ if (fire & 2)
+ {
+ // to-do
+ }
+ }
+ }
+ METHOD(RocketPropelledChainsaw, wr_init, void(entity thiswep))
+ {
+ RPC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
+ }
+ METHOD(RocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep))
+ {
+ float ammo_amount = self.WEP_AMMO(RPC) >= WEP_CVAR(rpc, ammo);
+ ammo_amount += self.(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR(rpc, ammo);
+ return ammo_amount;
+ }
+ METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep))
+ {
+ return false;
+ }
+ METHOD(RocketPropelledChainsaw, wr_config, void(entity thiswep))
+ {
+ RPC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
+ }
+ METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep))
+ {
+ W_Reload(self, WEP_CVAR(rpc, ammo), SND(RELOAD));
+ }
+ METHOD(RocketPropelledChainsaw, wr_suicidemessage, int(entity thiswep))
+ {
+ if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
+ return WEAPON_RPC_SUICIDE_SPLASH;
+ else
+ return WEAPON_RPC_SUICIDE_DIRECT;
+ }
+ METHOD(RocketPropelledChainsaw, wr_killmessage, int(entity thiswep))
+ {
+ if(w_deathtype & HITTYPE_SECONDARY)
+ return WEAPON_BLASTER_MURDER;
+ else if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
+ return WEAPON_RPC_MURDER_SPLASH;
+ else
+ return WEAPON_RPC_MURDER_DIRECT;
+ }
+
+#endif
+
+#ifdef CSQC
+
+ METHOD(RocketPropelledChainsaw, wr_impacteffect, void(entity thiswep))
+ {
+ vector org2;
+ org2 = w_org + w_backoff * 12;
+ pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1);
+ if(!w_issilent)
+ sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+ }
+
+#endif
+#endif
--- /dev/null
+#ifdef SVQC
+#include "physical_items.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+int autocvar_g_physical_items;
+float autocvar_g_physical_items_damageforcescale;
+float autocvar_g_physical_items_reset;
+
+REGISTER_MUTATOR(physical_items, cvar("g_physical_items"))
+{
+ // check if we have a physics engine
+ MUTATOR_ONADD
+ {
+ if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")))
+ {
+ LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n");
+ return -1;
+ }
+ }
+
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ // nothing to roll back
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ LOG_INFO("This cannot be removed at runtime\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+.vector spawn_origin, spawn_angles;
+
+void physical_item_think()
+{SELFPARAM();
+ self.nextthink = time;
+
+ self.alpha = self.owner.alpha; // apply fading and ghosting
+
+ if(!self.cnt) // map item, not dropped
+ {
+ // copy ghost item properties
+ self.colormap = self.owner.colormap;
+ self.colormod = self.owner.colormod;
+ self.glowmod = self.owner.glowmod;
+
+ // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there
+ if(autocvar_g_physical_items_reset)
+ {
+ if(self.owner.wait > time) // awaiting respawn
+ {
+ setorigin(self, self.spawn_origin);
+ self.angles = self.spawn_angles;
+ self.solid = SOLID_NOT;
+ self.alpha = -1;
+ self.movetype = MOVETYPE_NONE;
+ }
+ else
+ {
+ self.alpha = 1;
+ self.solid = SOLID_CORPSE;
+ self.movetype = MOVETYPE_PHYSICS;
+ }
+ }
+ }
+
+ if(!self.owner.modelindex)
+ remove(self); // the real item is gone, remove this
+}
+
+void physical_item_touch()
+{SELFPARAM();
+ if(!self.cnt) // not for dropped items
+ if (ITEM_TOUCH_NEEDKILL())
+ {
+ setorigin(self, self.spawn_origin);
+ self.angles = self.spawn_angles;
+ }
+}
+
+void physical_item_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{SELFPARAM();
+ if(!self.cnt) // not for dropped items
+ if(ITEM_DAMAGE_NEEDKILL(deathtype))
+ {
+ setorigin(self, self.spawn_origin);
+ self.angles = self.spawn_angles;
+ }
+}
+
+MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn)
+{SELFPARAM();
+ if(self.owner == world && autocvar_g_physical_items <= 1)
+ return false;
+ if (self.spawnflags & 1) // floating item
+ return false;
+
+ // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics.
+ // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed.
+ entity wep;
+ wep = spawn();
+ _setmodel(wep, self.model);
+ setsize(wep, self.mins, self.maxs);
+ setorigin(wep, self.origin);
+ wep.angles = self.angles;
+ wep.velocity = self.velocity;
+
+ wep.owner = self;
+ wep.solid = SOLID_CORPSE;
+ wep.movetype = MOVETYPE_PHYSICS;
+ wep.takedamage = DAMAGE_AIM;
+ wep.effects |= EF_NOMODELFLAGS; // disable the spinning
+ wep.colormap = self.owner.colormap;
+ wep.glowmod = self.owner.glowmod;
+ wep.damageforcescale = autocvar_g_physical_items_damageforcescale;
+ wep.dphitcontentsmask = self.dphitcontentsmask;
+ wep.cnt = (self.owner != world);
+
+ wep.think = physical_item_think;
+ wep.nextthink = time;
+ wep.touch = physical_item_touch;
+ wep.event_damage = physical_item_damage;
+
+ if(!wep.cnt)
+ {
+ // fix the spawn origin
+ setorigin(wep, wep.origin + '0 0 1');
+ entity oldself;
+ oldself = self;
+ WITH(entity, self, wep, builtin_droptofloor());
+ }
+
+ wep.spawn_origin = wep.origin;
+ wep.spawn_angles = self.angles;
+
+ self.effects |= EF_NODRAW; // hide the original weapon
+ self.movetype = MOVETYPE_FOLLOW;
+ self.aiment = wep; // attach the original weapon
+ self.SendEntity = func_null;
+
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "pinata.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill"));
+
+MUTATOR_HOOKFUNCTION(pinata, PlayerDies)
+{SELFPARAM();
+ for(int j = WEP_FIRST; j <= WEP_LAST; ++j)
+ if(self.weapons & WepSet_FromWeapon(j))
+ if(self.switchweapon != j)
+ if(W_IsWeaponThrowable(j))
+ W_ThrowNewWeapon(self, j, false, self.origin + (self.mins + self.maxs) * 0.5, randomvec() * 175 + '0 0 325');
+
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":Pinata");
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Piñata");
+ return false;
+}
+
+#endif
--- /dev/null
+#ifdef SVQC
+#include "random_gravity.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+// Random Gravity
+//
+// Mutator by Mario
+// Inspired by Player 2
+
+float autocvar_g_random_gravity_negative_chance;
+float autocvar_g_random_gravity_min;
+float autocvar_g_random_gravity_max;
+float autocvar_g_random_gravity_positive;
+float autocvar_g_random_gravity_negative;
+float autocvar_g_random_gravity_delay;
+
+REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity"))
+{
+ MUTATOR_ONADD
+ {
+ cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end
+ }
+
+ return false;
+}
+
+float gravity_delay;
+
+MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame)
+{
+ if(gameover || !cvar("g_random_gravity")) return false;
+ if(time < gravity_delay) return false;
+ if(time < game_starttime) return false;
+ if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false;
+
+ if(random() >= autocvar_g_random_gravity_negative_chance)
+ cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max)));
+ else
+ cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max)));
+
+ gravity_delay = time + autocvar_g_random_gravity_delay;
+
+ LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity), "\n");
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":RandomGravity");
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Random gravity");
+ return 0;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "rocketflying.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying"));
+
+MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile)
+{
+ if(other.classname == "rocket" || other.classname == "mine")
+ {
+ // kill detonate delay of rockets
+ other.spawnshieldtime = time;
+ }
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":RocketFlying");
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Rocket Flying");
+ return 0;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "rocketminsta.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+#include "../../../deathtypes/all.qh"
+#include "../../../../server/round_handler.qh"
+
+REGISTER_MUTATOR(rm, cvar("g_instagib"));
+
+MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate)
+{
+ // we do it this way, so rm can be toggled during the match
+ if(!autocvar_g_rm) { return false; }
+
+ if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR))
+ if(frag_attacker == frag_target || frag_target.classname == "nade")
+ frag_damage = 0;
+
+ if(autocvar_g_rm_laser)
+ if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO))
+ if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
+ frag_damage = 0;
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(rm, PlayerDies)
+{
+ // we do it this way, so rm can be toggled during the match
+ if(!autocvar_g_rm) { return false; }
+
+ if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO))
+ frag_damage = 1000; // always gib if it was a vaporizer death
+
+ return false;
+}
+
+#endif
--- /dev/null
+#ifdef SVQC
+#include "running_guns.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+
+bool autocvar_g_running_guns;
+
+REGISTER_MUTATOR(running_guns, autocvar_g_running_guns);
+
+MUTATOR_HOOKFUNCTION(running_guns, SetDefaultAlpha)
+{
+ default_player_alpha = -1;
+ default_weapon_alpha = +1;
+ return true;
+}
+
+#endif
--- /dev/null
+#ifdef SVQC
+#include "sandbox.qc"
+
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+int autocvar_g_sandbox_info;
+bool autocvar_g_sandbox_readonly;
+string autocvar_g_sandbox_storage_name;
+float autocvar_g_sandbox_storage_autosave;
+bool autocvar_g_sandbox_storage_autoload;
+float autocvar_g_sandbox_editor_flood;
+int autocvar_g_sandbox_editor_maxobjects;
+int autocvar_g_sandbox_editor_free;
+float autocvar_g_sandbox_editor_distance_spawn;
+float autocvar_g_sandbox_editor_distance_edit;
+float autocvar_g_sandbox_object_scale_min;
+float autocvar_g_sandbox_object_scale_max;
+float autocvar_g_sandbox_object_material_velocity_min;
+float autocvar_g_sandbox_object_material_velocity_factor;
+
+float autosave_time;
+void sandbox_Database_Load();
+
+REGISTER_MUTATOR(sandbox, cvar("g_sandbox"))
+{
+ MUTATOR_ONADD
+ {
+ autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame
+ if(autocvar_g_sandbox_storage_autoload)
+ sandbox_Database_Load();
+ }
+
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ // nothing to roll back
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ // nothing to remove
+ }
+
+ return false;
+}
+
+const float MAX_STORAGE_ATTACHMENTS = 16;
+float object_count;
+.float object_flood;
+.entity object_attach;
+.string material;
+
+.float touch_timer;
+void sandbox_ObjectFunction_Touch()
+{SELFPARAM();
+ // apply material impact effects
+
+ if(!self.material)
+ return;
+ if(self.touch_timer > time)
+ return; // don't execute each frame
+ self.touch_timer = time + 0.1;
+
+ // make particle count and sound volume depend on impact speed
+ float intensity;
+ intensity = vlen(self.velocity) + vlen(other.velocity);
+ if(intensity) // avoid divisions by 0
+ intensity /= 2; // average the two velocities
+ if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min))
+ return; // impact not strong enough to do anything
+ // now offset intensity and apply it to the effects
+ intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity
+ intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1);
+
+ _sound(self, CH_TRIGGER, strcat("object/impact_", self.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM);
+ Send_Effect_(strcat("impact_", self.material), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10
+}
+
+void sandbox_ObjectFunction_Think()
+{SELFPARAM();
+ entity e;
+
+ // decide if and how this object can be grabbed
+ if(autocvar_g_sandbox_readonly)
+ self.grab = 0; // no grabbing
+ else if(autocvar_g_sandbox_editor_free < 2 && self.crypto_idfp)
+ self.grab = 1; // owner only
+ else
+ self.grab = 3; // anyone
+
+ // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server).
+ // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this,
+ // since if the owning player disconnects, the object's owner should also be reset.
+ FOR_EACH_REALPLAYER(e) // bots can't have objects
+ {
+ if(self.crypto_idfp == e.crypto_idfp)
+ {
+ self.realowner = e;
+ break;
+ }
+ self.realowner = world;
+ }
+
+ self.nextthink = time;
+
+ CSQCMODEL_AUTOUPDATE(self);
+}
+
+.float old_solid, old_movetype;
+entity sandbox_ObjectEdit_Get(float permissions)
+{SELFPARAM();
+ // Returns the traced entity if the player can edit it, and world if not.
+ // If permissions if false, the object is returned regardless of editing rights.
+ // Attached objects are SOLID_NOT and do not get traced.
+
+ crosshair_trace_plusvisibletriggers(self);
+ if(vlen(self.origin - trace_ent.origin) > autocvar_g_sandbox_editor_distance_edit)
+ return world; // out of trace range
+ if(trace_ent.classname != "object")
+ return world; // entity is not an object
+ if(!permissions)
+ return trace_ent; // don't check permissions, anyone can edit this object
+ if(trace_ent.crypto_idfp == "")
+ return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it
+ if (!(trace_ent.realowner != self && autocvar_g_sandbox_editor_free < 2))
+ return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server
+ return world;
+}
+
+void sandbox_ObjectEdit_Scale(entity e, float f)
+{
+ e.scale = f;
+ if(e.scale)
+ {
+ e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max);
+ _setmodel(e, e.model); // reset mins and maxs based on mesh
+ setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size
+ }
+}
+
+void sandbox_ObjectAttach_Remove(entity e);
+void sandbox_ObjectAttach_Set(entity e, entity parent, string s)
+{
+ // attaches e to parent on string s
+
+ // we can't attach to an attachment, for obvious reasons
+ sandbox_ObjectAttach_Remove(e);
+
+ e.old_solid = e.solid; // persist solidity
+ e.old_movetype = e.movetype; // persist physics
+ e.movetype = MOVETYPE_FOLLOW;
+ e.solid = SOLID_NOT;
+ e.takedamage = DAMAGE_NO;
+
+ setattachment(e, parent, s);
+ e.owner = parent;
+}
+
+void sandbox_ObjectAttach_Remove(entity e)
+{
+ // detaches any object attached to e
+
+ entity head;
+ for(head = world; (head = find(head, classname, "object")); )
+ {
+ if(head.owner == e)
+ {
+ vector org;
+ org = gettaginfo(head, 0);
+ setattachment(head, world, "");
+ head.owner = world;
+
+ // objects change origin and angles when detached, so apply previous position
+ setorigin(head, org);
+ head.angles = e.angles; // don't allow detached objects to spin or roll
+
+ head.solid = head.old_solid; // restore persisted solidity
+ head.movetype = head.old_movetype; // restore persisted physics
+ head.takedamage = DAMAGE_AIM;
+ }
+ }
+}
+
+entity sandbox_ObjectSpawn(float database)
+{SELFPARAM();
+ // spawn a new object with default properties
+
+ entity e = new(object);
+ e.takedamage = DAMAGE_AIM;
+ e.damageforcescale = 1;
+ e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly
+ e.movetype = MOVETYPE_TOSS;
+ e.frame = 0;
+ e.skin = 0;
+ e.material = string_null;
+ e.touch = sandbox_ObjectFunction_Touch;
+ e.think = sandbox_ObjectFunction_Think;
+ e.nextthink = time;
+ //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects?
+
+ if(!database)
+ {
+ // set the object's owner via player UID
+ // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone
+ if(self.crypto_idfp != "")
+ e.crypto_idfp = strzone(self.crypto_idfp);
+ else
+ print_to(self, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!");
+
+ // set public object information
+ e.netname = strzone(self.netname); // name of the owner
+ e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time
+ e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time
+
+ // set origin and direction based on player position and view angle
+ makevectors(self.v_angle);
+ WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, self);
+ setorigin(e, trace_endpos);
+ e.angles_y = self.v_angle.y;
+ }
+
+ WITH(entity, self, e, CSQCMODEL_AUTOINIT(e));
+
+ object_count += 1;
+ return e;
+}
+
+void sandbox_ObjectRemove(entity e)
+{
+ sandbox_ObjectAttach_Remove(e); // detach child objects
+
+ // if the object being removed has been selected for attachment by a player, unset it
+ entity head;
+ FOR_EACH_REALPLAYER(head) // bots can't have objects
+ {
+ if(head.object_attach == e)
+ head.object_attach = world;
+ }
+
+ if(e.material) { strunzone(e.material); e.material = string_null; }
+ if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; }
+ if(e.netname) { strunzone(e.netname); e.netname = string_null; }
+ if(e.message) { strunzone(e.message); e.message = string_null; }
+ if(e.message2) { strunzone(e.message2); e.message2 = string_null; }
+ remove(e);
+ e = world;
+
+ object_count -= 1;
+}
+
+string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global
+
+string sandbox_ObjectPort_Save(entity e, float database)
+{
+ // save object properties, and return them as a string
+ float i = 0;
+ string s;
+ entity head;
+
+ for(head = world; (head = find(head, classname, "object")); )
+ {
+ // the main object needs to be first in the array [0] with attached objects following
+ float slot, physics, solidity;
+ if(head == e) // this is the main object, place it first
+ {
+ slot = 0;
+ solidity = head.solid; // applied solidity is normal solidity for children
+ physics = head.movetype; // applied physics are normal physics for parents
+ }
+ else if(head.owner == e) // child object, list them in order
+ {
+ i += 1; // children start from 1
+ slot = i;
+ solidity = head.old_solid; // persisted solidity is normal solidity for children
+ physics = head.old_movetype; // persisted physics are normal physics for children
+ gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below
+ }
+ else
+ continue;
+
+ // ---------------- OBJECT PROPERTY STORAGE: SAVE ----------------
+ if(slot)
+ {
+ // properties stored only for child objects
+ if(gettaginfo_name) port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none
+ }
+ else
+ {
+ // properties stored only for parent objects
+ if(database)
+ {
+ port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " ");
+ port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " ");
+ }
+ }
+ // properties stored for all objects
+ port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" ");
+ port_string[slot] = strcat(port_string[slot], ftos(head.skin), " ");
+ port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " ");
+ port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " ");
+ port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " ");
+ port_string[slot] = strcat(port_string[slot], ftos(head.frame), " ");
+ port_string[slot] = strcat(port_string[slot], ftos(head.scale), " ");
+ port_string[slot] = strcat(port_string[slot], ftos(solidity), " ");
+ port_string[slot] = strcat(port_string[slot], ftos(physics), " ");
+ port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " ");
+ if(head.material) port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none
+ if(database)
+ {
+ // properties stored only for the database
+ if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none
+ port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" ");
+ port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" ");
+ port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" ");
+ }
+ }
+
+ // now apply the array to a simple string, with the ; symbol separating objects
+ s = "";
+ for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
+ {
+ if(port_string[i])
+ s = strcat(s, port_string[i], "; ");
+ port_string[i] = string_null; // fully clear the string
+ }
+
+ return s;
+}
+
+entity sandbox_ObjectPort_Load(string s, float database)
+{
+ // load object properties, and spawn a new object with them
+ float n, i;
+ entity e = world, parent = world;
+
+ // separate objects between the ; symbols
+ n = tokenizebyseparator(s, "; ");
+ for(i = 0; i < n; ++i)
+ port_string[i] = argv(i);
+
+ // now separate and apply the properties of each object
+ for(i = 0; i < n; ++i)
+ {
+ float argv_num;
+ string tagname = string_null;
+ argv_num = 0;
+ tokenize_console(port_string[i]);
+ e = sandbox_ObjectSpawn(database);
+
+ // ---------------- OBJECT PROPERTY STORAGE: LOAD ----------------
+ if(i)
+ {
+ // properties stored only for child objects
+ if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num;
+ }
+ else
+ {
+ // properties stored only for parent objects
+ if(database)
+ {
+ setorigin(e, stov(argv(argv_num))); ++argv_num;
+ e.angles = stov(argv(argv_num)); ++argv_num;
+ }
+ parent = e; // mark parent objects as such
+ }
+ // properties stored for all objects
+ _setmodel(e, argv(argv_num)); ++argv_num;
+ e.skin = stof(argv(argv_num)); ++argv_num;
+ e.alpha = stof(argv(argv_num)); ++argv_num;
+ e.colormod = stov(argv(argv_num)); ++argv_num;
+ e.glowmod = stov(argv(argv_num)); ++argv_num;
+ e.frame = stof(argv(argv_num)); ++argv_num;
+ sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num;
+ e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num;
+ e.movetype = e.old_movetype = stof(argv(argv_num)); ++argv_num;
+ e.damageforcescale = stof(argv(argv_num)); ++argv_num;
+ if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num;
+ if(database)
+ {
+ // properties stored only for the database
+ if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num;
+ if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num;
+ if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num;
+ if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num;
+ }
+
+ // attach last
+ if(i)
+ sandbox_ObjectAttach_Set(e, parent, tagname);
+ }
+
+ for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
+ port_string[i] = string_null; // fully clear the string
+
+ return e;
+}
+
+void sandbox_Database_Save()
+{
+ // saves all objects to the database file
+ entity head;
+ string file_name;
+ float file_get;
+
+ file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
+ file_get = fopen(file_name, FILE_WRITE);
+ fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S")));
+ fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n"));
+
+ for(head = world; (head = find(head, classname, "object")); )
+ {
+ // attached objects are persisted separately, ignore them here
+ if(head.owner != world)
+ continue;
+
+ // use a line of text for each object, listing all properties
+ fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n"));
+ }
+ fclose(file_get);
+}
+
+void sandbox_Database_Load()
+{
+ // loads all objects from the database file
+ string file_read, file_name;
+ float file_get, i;
+
+ file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
+ file_get = fopen(file_name, FILE_READ);
+ if(file_get < 0)
+ {
+ if(autocvar_g_sandbox_info > 0)
+ LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n"));
+ }
+ else
+ {
+ for (;;)
+ {
+ file_read = fgets(file_get);
+ if(file_read == "")
+ break;
+ if(substring(file_read, 0, 2) == "//")
+ continue;
+ if(substring(file_read, 0, 1) == "#")
+ continue;
+
+ entity e;
+ e = sandbox_ObjectPort_Load(file_read, true);
+
+ if(e.material)
+ {
+ // since objects are being loaded for the first time, precache material sounds for each
+ for (i = 1; i <= 5; i++) // 5 sounds in total
+ precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav"));
+ }
+ }
+ if(autocvar_g_sandbox_info > 0)
+ LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n"));
+ }
+ fclose(file_get);
+}
+
+MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand)
+{SELFPARAM();
+ if(MUTATOR_RETURNVALUE) // command was already handled?
+ return false;
+ if(cmd_name == "g_sandbox")
+ {
+ if(autocvar_g_sandbox_readonly)
+ {
+ print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
+ return true;
+ }
+ if(cmd_argc < 2)
+ {
+ print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
+ return true;
+ }
+
+ switch(argv(1))
+ {
+ entity e;
+ float i;
+ string s;
+
+ // ---------------- COMMAND: HELP ----------------
+ case "help":
+ print_to(self, "You can use the following sandbox commands:");
+ print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model");
+ print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
+ print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
+ print_to(self, "^3copy value ^7- copies the properties of the object to the specified client cvar");
+ print_to(self, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\"");
+ print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
+ print_to(self, "^3get ^7- selects the object you are facing as the object to be attached");
+ print_to(self, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
+ print_to(self, "^3remove ^7- detaches all objects from the object you are facing");
+ print_to(self, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
+ print_to(self, "^3skin value ^7- changes the skin of the object");
+ print_to(self, "^3alpha value ^7- sets object transparency");
+ print_to(self, "^3colormod \"value_x value_y value_z\" ^7- main object color");
+ print_to(self, "^3glowmod \"value_x value_y value_z\" ^7- glow object color");
+ print_to(self, "^3frame value ^7- object animation frame, for self-animated models");
+ print_to(self, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size");
+ print_to(self, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid");
+ print_to(self, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical");
+ print_to(self, "^3force value ^7- amount of force applied to objects that are shot");
+ print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
+ print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it");
+ print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object");
+ print_to(self, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
+ print_to(self, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
+ print_to(self, "^3attachments ^7- prints information about the object's attachments");
+ print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
+ return true;
+
+ // ---------------- COMMAND: OBJECT, SPAWN ----------------
+ case "object_spawn":
+ if(time < self.object_flood)
+ {
+ print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object"));
+ return true;
+ }
+ self.object_flood = time + autocvar_g_sandbox_editor_flood;
+ if(object_count >= autocvar_g_sandbox_editor_maxobjects)
+ {
+ print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
+ return true;
+ }
+ if(cmd_argc < 3)
+ {
+ print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command");
+ return true;
+ }
+ if (!(fexists(argv(2))))
+ {
+ print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct");
+ return true;
+ }
+
+ e = sandbox_ObjectSpawn(false);
+ _setmodel(e, argv(2));
+
+ if(autocvar_g_sandbox_info > 0)
+ LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " spawned an object at origin ^3", vtos(e.origin), "\n"));
+ return true;
+
+ // ---------------- COMMAND: OBJECT, REMOVE ----------------
+ case "object_remove":
+ e = sandbox_ObjectEdit_Get(true);
+ if(e != world)
+ {
+ if(autocvar_g_sandbox_info > 0)
+ LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " removed an object at origin ^3", vtos(e.origin), "\n"));
+ sandbox_ObjectRemove(e);
+ return true;
+ }
+
+ print_to(self, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over");
+ return true;
+
+ // ---------------- COMMAND: OBJECT, DUPLICATE ----------------
+ case "object_duplicate":
+ switch(argv(2))
+ {
+ case "copy":
+ // copies customizable properties of the selected object to the clipboard cvar
+ e = sandbox_ObjectEdit_Get(autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
+ if(e != world)
+ {
+ s = sandbox_ObjectPort_Save(e, false);
+ s = strreplace("\"", "\\\"", s);
+ stuffcmd(self, strcat("set ", argv(3), " \"", s, "\""));
+
+ print_to(self, "^2SANDBOX - INFO: ^7Object copied to clipboard");
+ return true;
+ }
+ print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over");
+ return true;
+
+ case "paste":
+ // spawns a new object using the properties in the player's clipboard cvar
+ if(time < self.object_flood)
+ {
+ print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object"));
+ return true;
+ }
+ self.object_flood = time + autocvar_g_sandbox_editor_flood;
+ if(argv(3) == "") // no object in clipboard
+ {
+ print_to(self, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
+ return true;
+ }
+ if(object_count >= autocvar_g_sandbox_editor_maxobjects)
+ {
+ print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
+ return true;
+ }
+ e = sandbox_ObjectPort_Load(argv(3), false);
+
+ print_to(self, "^2SANDBOX - INFO: ^7Object pasted successfully");
+ if(autocvar_g_sandbox_info > 0)
+ LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " pasted an object at origin ^3", vtos(e.origin), "\n"));
+ return true;
+ }
+ return true;
+
+ // ---------------- COMMAND: OBJECT, ATTACH ----------------
+ case "object_attach":
+ switch(argv(2))
+ {
+ case "get":
+ // select e as the object as meant to be attached
+ e = sandbox_ObjectEdit_Get(true);
+ if(e != world)
+ {
+ self.object_attach = e;
+ print_to(self, "^2SANDBOX - INFO: ^7Object selected for attachment");
+ return true;
+ }
+ print_to(self, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over");
+ return true;
+ case "set":
+ if(self.object_attach == world)
+ {
+ print_to(self, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first.");
+ return true;
+ }
+
+ // attaches the previously selected object to e
+ e = sandbox_ObjectEdit_Get(true);
+ if(e != world)
+ {
+ sandbox_ObjectAttach_Set(self.object_attach, e, argv(3));
+ self.object_attach = world; // object was attached, no longer keep it scheduled for attachment
+ print_to(self, "^2SANDBOX - INFO: ^7Object attached successfully");
+ if(autocvar_g_sandbox_info > 1)
+ LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " attached objects at origin ^3", vtos(e.origin), "\n"));
+ return true;
+ }
+ print_to(self, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over");
+ return true;
+ case "remove":
+ // removes e if it was attached
+ e = sandbox_ObjectEdit_Get(true);
+ if(e != world)
+ {
+ sandbox_ObjectAttach_Remove(e);
+ print_to(self, "^2SANDBOX - INFO: ^7Child objects detached successfully");
+ if(autocvar_g_sandbox_info > 1)
+ LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " detached objects at origin ^3", vtos(e.origin), "\n"));
+ return true;
+ }
+ print_to(self, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over");
+ return true;
+ }
+ return true;
+
+ // ---------------- COMMAND: OBJECT, EDIT ----------------
+ case "object_edit":
+ if(argv(2) == "")
+ {
+ print_to(self, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit");
+ return true;
+ }
+
+ e = sandbox_ObjectEdit_Get(true);
+ if(e != world)
+ {
+ switch(argv(2))
+ {
+ case "skin":
+ e.skin = stof(argv(3));
+ break;
+ case "alpha":
+ e.alpha = stof(argv(3));
+ break;
+ case "color_main":
+ e.colormod = stov(argv(3));
+ break;
+ case "color_glow":
+ e.glowmod = stov(argv(3));
+ break;
+ case "frame":
+ e.frame = stof(argv(3));
+ break;
+ case "scale":
+ sandbox_ObjectEdit_Scale(e, stof(argv(3)));
+ break;
+ case "solidity":
+ switch(argv(3))
+ {
+ case "0": // non-solid
+ e.solid = SOLID_TRIGGER;
+ break;
+ case "1": // solid
+ e.solid = SOLID_BBOX;
+ break;
+ default:
+ break;
+ }
+ case "physics":
+ switch(argv(3))
+ {
+ case "0": // static
+ e.movetype = MOVETYPE_NONE;
+ break;
+ case "1": // movable
+ e.movetype = MOVETYPE_TOSS;
+ break;
+ case "2": // physical
+ e.movetype = MOVETYPE_PHYSICS;
+ break;
+ default:
+ break;
+ }
+ break;
+ case "force":
+ e.damageforcescale = stof(argv(3));
+ break;
+ case "material":
+ if(e.material) strunzone(e.material);
+ if(argv(3))
+ {
+ for (i = 1; i <= 5; i++) // precache material sounds, 5 in total
+ precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".wav"));
+ e.material = strzone(argv(3));
+ }
+ else
+ e.material = string_null; // no material
+ break;
+ default:
+ print_to(self, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'");
+ return true;
+ }
+
+ // update last editing time
+ if(e.message2) strunzone(e.message2);
+ e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S"));
+
+ if(autocvar_g_sandbox_info > 1)
+ LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n"));
+ return true;
+ }
+
+ print_to(self, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over");
+ return true;
+
+ // ---------------- COMMAND: OBJECT, CLAIM ----------------
+ case "object_claim":
+ // if the player can edit an object but is not its owner, this can be used to claim that object
+ if(self.crypto_idfp == "")
+ {
+ print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
+ return true;
+ }
+ e = sandbox_ObjectEdit_Get(true);
+ if(e != world)
+ {
+ // update the owner's name
+ // Do this before checking if you're already the owner and skipping if such, so we
+ // also update the player's nickname if he changed it (but has the same player UID)
+ if(e.netname != self.netname)
+ {
+ if(e.netname) strunzone(e.netname);
+ e.netname = strzone(self.netname);
+ print_to(self, "^2SANDBOX - INFO: ^7Object owner name updated");
+ }
+
+ if(e.crypto_idfp == self.crypto_idfp)
+ {
+ print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
+ return true;
+ }
+
+ if(e.crypto_idfp) strunzone(e.crypto_idfp);
+ e.crypto_idfp = strzone(self.crypto_idfp);
+
+ print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully");
+ }
+ print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
+ return true;
+
+ // ---------------- COMMAND: OBJECT, INFO ----------------
+ case "object_info":
+ // prints public information about the object to the player
+ e = sandbox_ObjectEdit_Get(false);
+ if(e != world)
+ {
+ switch(argv(2))
+ {
+ case "object":
+ print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\""));
+ return true;
+ case "mesh":
+ s = "";
+ FOR_EACH_TAG(e)
+ s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
+ print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s));
+ return true;
+ case "attachments":
+ // this should show the same info as 'mesh' but for attachments
+ s = "";
+ entity head;
+ i = 0;
+ for(head = world; (head = find(head, classname, "object")); )
+ {
+ if(head.owner == e)
+ {
+ ++i; // start from 1
+ gettaginfo(e, head.tag_index);
+ s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame));
+ s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
+ }
+ }
+ if(i) // object contains attachments
+ print_to(self, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s));
+ else
+ print_to(self, "^2SANDBOX - INFO: ^7Object contains no attachments");
+ return true;
+ }
+ }
+ print_to(self, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
+ return true;
+
+ // ---------------- COMMAND: DEFAULT ----------------
+ default:
+ print_to(self, "Invalid command. For usage information, type 'sandbox help'");
+ return true;
+ }
+ }
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame)
+{
+ if(!autocvar_g_sandbox_storage_autosave)
+ return false;
+ if(time < autosave_time)
+ return false;
+ autosave_time = time + autocvar_g_sandbox_storage_autosave;
+
+ sandbox_Database_Save();
+
+ return true;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "spawn_near_teammate.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+
+float autocvar_g_spawn_near_teammate_distance;
+int autocvar_g_spawn_near_teammate_ignore_spawnpoint;
+float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
+float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
+int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
+bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
+
+REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate") && teamplay);
+
+.entity msnt_lookat;
+
+.float msnt_timer;
+.vector msnt_deathloc;
+
+.float cvar_cl_spawn_near_teammate;
+
+MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score)
+{SELFPARAM();
+ if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate))
+ return 0;
+
+ entity p;
+
+ spawn_spot.msnt_lookat = world;
+
+ if(!teamplay)
+ return 0;
+
+ RandomSelection_Init();
+ FOR_EACH_PLAYER(p) if(p != self) if(p.team == self.team) if(!p.deadflag)
+ {
+ float l = vlen(spawn_spot.origin - p.origin);
+ if(l > autocvar_g_spawn_near_teammate_distance)
+ continue;
+ if(l < 48)
+ continue;
+ if(!checkpvs(spawn_spot.origin, p))
+ continue;
+ RandomSelection_Add(p, 0, string_null, 1, 1);
+ }
+
+ if(RandomSelection_chosen_ent)
+ {
+ spawn_spot.msnt_lookat = RandomSelection_chosen_ent;
+ spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
+ }
+ else if(self.team == spawn_spot.team)
+ spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
+
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
+{SELFPARAM();
+ // Note: when entering this, fixangle is already set.
+ if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate))
+ {
+ if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death)
+ self.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
+
+ entity team_mate, best_mate = world;
+ vector best_spot = '0 0 0';
+ float pc = 0, best_dist = 0, dist = 0;
+ FOR_EACH_PLAYER(team_mate)
+ {
+ if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && team_mate.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0)
+ if(team_mate.deadflag == DEAD_NO)
+ if(team_mate.msnt_timer < time)
+ if(SAME_TEAM(self, team_mate))
+ if(time > team_mate.spawnshieldtime) // spawn shielding
+ if(team_mate.frozen == 0)
+ if(team_mate != self)
+ {
+ tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate);
+ if(trace_fraction != 1.0)
+ if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
+ {
+ pc = pointcontents(trace_endpos + '0 0 1');
+ if(pc == CONTENT_EMPTY)
+ {
+ if(vlen(team_mate.velocity) > 5)
+ fixedmakevectors(vectoangles(team_mate.velocity));
+ else
+ fixedmakevectors(team_mate.angles);
+
+ for(pc = 0; pc != 5; ++pc) // test 5 diffrent spots close to mate
+ {
+ switch(pc)
+ {
+ case 0:
+ tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 128, MOVE_NORMAL, team_mate);
+ break;
+ case 1:
+ tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 128 , MOVE_NORMAL, team_mate);
+ break;
+ case 2:
+ tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate);
+ break;
+ case 3:
+ tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate);
+ break;
+ case 4:
+ tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_forward * 128, MOVE_NORMAL, team_mate);
+ break;
+ }
+
+ if(trace_fraction == 1.0)
+ {
+ traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NORMAL, team_mate);
+ if(trace_fraction != 1.0)
+ {
+ if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath)
+ {
+ dist = vlen(trace_endpos - self.msnt_deathloc);
+ if(dist < best_dist || best_dist == 0)
+ {
+ best_dist = dist;
+ best_spot = trace_endpos;
+ best_mate = team_mate;
+ }
+ }
+ else
+ {
+ setorigin(self, trace_endpos);
+ self.angles = team_mate.angles;
+ self.angles_z = 0; // never spawn tilted even if the spot says to
+ team_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath)
+ if(best_dist)
+ {
+ setorigin(self, best_spot);
+ self.angles = best_mate.angles;
+ self.angles_z = 0; // never spawn tilted even if the spot says to
+ best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
+ }
+ }
+ else if(spawn_spot.msnt_lookat)
+ {
+ self.angles = vectoangles(spawn_spot.msnt_lookat.origin - self.origin);
+ self.angles_x = -self.angles.x;
+ self.angles_z = 0; // never spawn tilted even if the spot says to
+ /*
+ sprint(self, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n");
+ sprint(self, "distance: ", vtos(spawn_spot.msnt_lookat.origin - self.origin), "\n");
+ sprint(self, "angles: ", vtos(self.angles), "\n");
+ */
+ }
+
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies)
+{SELFPARAM();
+ self.msnt_deathloc = self.origin;
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(spawn_near_teammate, GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_spawn_near_teammate, "cl_spawn_near_teammate");
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "superspec.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_MUTATOR(superspec, cvar("g_superspectate"));
+
+#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1"
+#define _ISLOCAL ((edict_num(1) == self) ? true : false)
+
+const float ASF_STRENGTH = BIT(0);
+const float ASF_SHIELD = BIT(1);
+const float ASF_MEGA_AR = BIT(2);
+const float ASF_MEGA_HP = BIT(3);
+const float ASF_FLAG_GRAB = BIT(4);
+const float ASF_OBSERVER_ONLY = BIT(5);
+const float ASF_SHOWWHAT = BIT(6);
+const float ASF_SSIM = BIT(7);
+const float ASF_FOLLOWKILLER = BIT(8);
+const float ASF_ALL = 0xFFFFFF;
+.float autospec_flags;
+
+const float SSF_SILENT = 1;
+const float SSF_VERBOSE = 2;
+const float SSF_ITEMMSG = 4;
+.float superspec_flags;
+
+.string superspec_itemfilter; //"classname1 classname2 ..."
+
+bool superspec_Spectate(entity _player)
+{SELFPARAM();
+ if(Spectate(_player) == 1)
+ self.classname = STR_SPECTATOR;
+
+ return true;
+}
+
+void superspec_save_client_conf()
+{SELFPARAM();
+ string fn = "superspec-local.options";
+ float fh;
+
+ if (!_ISLOCAL)
+ {
+ if(self.crypto_idfp == "")
+ return;
+
+ fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp));
+ }
+
+ fh = fopen(fn, FILE_WRITE);
+ if(fh < 0)
+ {
+ LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n");
+ }
+ else
+ {
+ fputs(fh, _SSMAGIX);
+ fputs(fh, "\n");
+ fputs(fh, ftos(self.autospec_flags));
+ fputs(fh, "\n");
+ fputs(fh, ftos(self.superspec_flags));
+ fputs(fh, "\n");
+ fputs(fh, self.superspec_itemfilter);
+ fputs(fh, "\n");
+ fclose(fh);
+ }
+}
+
+void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel)
+{
+ sprint(_to, strcat(_con_title, _msg));
+
+ if(_to.superspec_flags & SSF_SILENT)
+ return;
+
+ if(_spamlevel > 1)
+ if (!(_to.superspec_flags & SSF_VERBOSE))
+ return;
+
+ centerprint(_to, strcat(_center_title, _msg));
+}
+
+float superspec_filteritem(entity _for, entity _item)
+{
+ float i;
+
+ if(_for.superspec_itemfilter == "")
+ return true;
+
+ if(_for.superspec_itemfilter == "")
+ return true;
+
+ float l = tokenize_console(_for.superspec_itemfilter);
+ for(i = 0; i < l; ++i)
+ {
+ if(argv(i) == _item.classname)
+ return true;
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(superspec, ItemTouch)
+{SELFPARAM();
+ entity _item = self;
+
+ entity e;
+ FOR_EACH_CLIENT(e) if (IS_SPEC(e) || IS_OBSERVER(e))
+ {
+ setself(e);
+ if(self.superspec_flags & SSF_ITEMMSG)
+ if(superspec_filteritem(self, _item))
+ {
+ if(self.superspec_flags & SSF_VERBOSE)
+ superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n", other.netname, _item.netname), 1);
+ else
+ superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", other.netname, _item.netname, _item.classname), 1);
+ if((self.autospec_flags & ASF_SSIM) && self.enemy != other)
+ {
+ superspec_Spectate(other);
+
+ setself(this);
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+
+ if((self.autospec_flags & ASF_SHIELD && _item.invincible_finished) ||
+ (self.autospec_flags & ASF_STRENGTH && _item.strength_finished) ||
+ (self.autospec_flags & ASF_MEGA_AR && _item.itemdef == ITEM_ArmorMega) ||
+ (self.autospec_flags & ASF_MEGA_HP && _item.itemdef == ITEM_HealthMega) ||
+ (self.autospec_flags & ASF_FLAG_GRAB && _item.classname == "item_flag_team"))
+ {
+
+ if((self.enemy != other) || IS_OBSERVER(self))
+ {
+ if(self.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(self))
+ {
+ if(self.superspec_flags & SSF_VERBOSE)
+ superspec_msg("", "", self, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", other.netname, _item.netname), 2);
+ }
+ else
+ {
+ if(self.autospec_flags & ASF_SHOWWHAT)
+ superspec_msg("", "", self, sprintf("^7Following %s^7 due to picking up %s\n", other.netname, _item.netname), 2);
+
+ superspec_Spectate(other);
+ }
+ }
+ }
+ }
+
+ setself(this);
+
+ return MUT_ITEMTOUCH_CONTINUE;
+}
+
+MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand)
+{SELFPARAM();
+#define OPTIONINFO(flag,var,test,text,long,short) \
+ var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \
+ var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n")
+
+ if(MUTATOR_RETURNVALUE) // command was already handled?
+ return false;
+
+ if(IS_PLAYER(self))
+ return false;
+
+ if(cmd_name == "superspec_itemfilter")
+ {
+ if(argv(1) == "help")
+ {
+ string _aspeco;
+ _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n";
+ _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n");
+ _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n");
+ superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", self, _aspeco, 1);
+ }
+ else if(argv(1) == "clear")
+ {
+ if(self.superspec_itemfilter != "")
+ strunzone(self.superspec_itemfilter);
+
+ self.superspec_itemfilter = "";
+ }
+ else if(argv(1) == "show" || argv(1) == "")
+ {
+ if(self.superspec_itemfilter == "")
+ {
+ superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", self, "", 1);
+ return true;
+ }
+ float i;
+ float l = tokenize_console(self.superspec_itemfilter);
+ string _msg = "";
+ for(i = 0; i < l; ++i)
+ _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n");
+ //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i));
+
+ _msg = strcat(_msg,"\n");
+
+ superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", self, _msg, 1);
+ }
+ else
+ {
+ if(self.superspec_itemfilter != "")
+ strunzone(self.superspec_itemfilter);
+
+ self.superspec_itemfilter = strzone(argv(1));
+ }
+
+ return true;
+ }
+
+ if(cmd_name == "superspec")
+ {
+ string _aspeco;
+
+ if(cmd_argc > 1)
+ {
+ float i, _bits = 0, _start = 1;
+ if(argv(1) == "help")
+ {
+ _aspeco = "use cmd superspec [option] [on|off] to set options\n\n";
+ _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n");
+ _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n");
+ _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n");
+ _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n");
+ superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", self, _aspeco, 1);
+ return true;
+ }
+
+ if(argv(1) == "clear")
+ {
+ self.superspec_flags = 0;
+ _start = 2;
+ }
+
+ for(i = _start; i < cmd_argc; ++i)
+ {
+ if(argv(i) == "on" || argv(i) == "1")
+ {
+ self.superspec_flags |= _bits;
+ _bits = 0;
+ }
+ else if(argv(i) == "off" || argv(i) == "0")
+ {
+ if(_start == 1)
+ self.superspec_flags &= ~_bits;
+
+ _bits = 0;
+ }
+ else
+ {
+ if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ;
+ if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE;
+ if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG;
+ }
+ }
+ }
+
+ _aspeco = "";
+ OPTIONINFO(self.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si");
+ OPTIONINFO(self.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve");
+ OPTIONINFO(self.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im");
+
+ superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", self, _aspeco, 1);
+
+ return true;
+ }
+
+/////////////////////
+
+ if(cmd_name == "autospec")
+ {
+ string _aspeco;
+ if(cmd_argc > 1)
+ {
+ if(argv(1) == "help")
+ {
+ _aspeco = "use cmd autospec [option] [on|off] to set options\n\n";
+ _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n");
+ _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n");
+ _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n");
+ _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n");
+ _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n");
+ _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n");
+ _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n");
+ _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n");
+ _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n");
+ _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n");
+ superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", self, _aspeco, 1);
+ return true;
+ }
+
+ float i, _bits = 0, _start = 1;
+ if(argv(1) == "clear")
+ {
+ self.autospec_flags = 0;
+ _start = 2;
+ }
+
+ for(i = _start; i < cmd_argc; ++i)
+ {
+ if(argv(i) == "on" || argv(i) == "1")
+ {
+ self.autospec_flags |= _bits;
+ _bits = 0;
+ }
+ else if(argv(i) == "off" || argv(i) == "0")
+ {
+ if(_start == 1)
+ self.autospec_flags &= ~_bits;
+
+ _bits = 0;
+ }
+ else
+ {
+ if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH;
+ if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD;
+ if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP;
+ if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR;
+ if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB;
+ if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY;
+ if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT;
+ if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM;
+ if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER;
+ if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL;
+ }
+ }
+ }
+
+ _aspeco = "";
+ OPTIONINFO(self.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st");
+ OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh");
+ OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh");
+ OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma");
+ OPTIONINFO(self.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg");
+ OPTIONINFO(self.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo");
+ OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw");
+ OPTIONINFO(self.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im");
+ OPTIONINFO(self.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk");
+
+ superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", self, _aspeco, 1);
+ return true;
+ }
+
+ if(cmd_name == "followpowerup")
+ {
+ entity _player;
+ FOR_EACH_PLAYER(_player)
+ {
+ if(_player.strength_finished > time || _player.invincible_finished > time)
+ return superspec_Spectate(_player);
+ }
+
+ superspec_msg("", "", self, "No active powerup\n", 1);
+ return true;
+ }
+
+ if(cmd_name == "followstrength")
+ {
+ entity _player;
+ FOR_EACH_PLAYER(_player)
+ {
+ if(_player.strength_finished > time)
+ return superspec_Spectate(_player);
+ }
+
+ superspec_msg("", "", self, "No active Strength\n", 1);
+ return true;
+ }
+
+ if(cmd_name == "followshield")
+ {
+ entity _player;
+ FOR_EACH_PLAYER(_player)
+ {
+ if(_player.invincible_finished > time)
+ return superspec_Spectate(_player);
+ }
+
+ superspec_msg("", "", self, "No active Shield\n", 1);
+ return true;
+ }
+
+ return false;
+#undef OPTIONINFO
+}
+
+MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":SS");
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Super Spectators");
+ return 0;
+}
+
+void superspec_hello()
+{SELFPARAM();
+ if(self.enemy.crypto_idfp == "")
+ Send_Notification(NOTIF_ONE_ONLY, self.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID);
+
+ remove(self);
+}
+
+MUTATOR_HOOKFUNCTION(superspec, ClientConnect)
+{SELFPARAM();
+ if(!IS_REAL_CLIENT(self))
+ return false;
+
+ string fn = "superspec-local.options";
+ float fh;
+
+ self.superspec_flags = SSF_VERBOSE;
+ self.superspec_itemfilter = "";
+
+ entity _hello = spawn();
+ _hello.enemy = self;
+ _hello.think = superspec_hello;
+ _hello.nextthink = time + 5;
+
+ if (!_ISLOCAL)
+ {
+ if(self.crypto_idfp == "")
+ return false;
+
+ fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp));
+ }
+
+ fh = fopen(fn, FILE_READ);
+ if(fh < 0)
+ {
+ LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n");
+ }
+ else
+ {
+ string _magic = fgets(fh);
+ if(_magic != _SSMAGIX)
+ {
+ LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic\n");
+ }
+ else
+ {
+ self.autospec_flags = stof(fgets(fh));
+ self.superspec_flags = stof(fgets(fh));
+ self.superspec_itemfilter = strzone(fgets(fh));
+ }
+ fclose(fh);
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(superspec, PlayerDies)
+{SELFPARAM();
+ entity e;
+ FOR_EACH_SPEC(e)
+ {
+ setself(e);
+ if(self.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && self.enemy == this)
+ {
+ if(self.autospec_flags & ASF_SHOWWHAT)
+ superspec_msg("", "", self, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2);
+
+ superspec_Spectate(frag_attacker);
+ }
+ }
+
+ setself(this);
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect)
+{
+ superspec_save_client_conf();
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "touchexplode.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+float autocvar_g_touchexplode_radius;
+float autocvar_g_touchexplode_damage;
+float autocvar_g_touchexplode_edgedamage;
+float autocvar_g_touchexplode_force;
+
+REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode"));
+
+.float touchexplode_time;
+
+void PlayerTouchExplode(entity p1, entity p2)
+{SELFPARAM();
+ vector org = (p1.origin + p2.origin) * 0.5;
+ org.z += (p1.mins.z + p2.mins.z) * 0.5;
+
+ sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
+ Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1);
+
+ entity e = spawn();
+ setorigin(e, org);
+ RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, world);
+ remove(e);
+}
+
+MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink)
+{SELFPARAM();
+ if(time > self.touchexplode_time)
+ if(!gameover)
+ if(!self.frozen)
+ if(IS_PLAYER(self))
+ if(self.deadflag == DEAD_NO)
+ if (!IS_INDEPENDENT_PLAYER(self))
+ FOR_EACH_PLAYER(other) if(self != other)
+ {
+ if(time > other.touchexplode_time)
+ if(!other.frozen)
+ if(other.deadflag == DEAD_NO)
+ if (!IS_INDEPENDENT_PLAYER(other))
+ if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax))
+ {
+ PlayerTouchExplode(self, other);
+ self.touchexplode_time = other.touchexplode_time = time + 0.2;
+ }
+ }
+
+ return false;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "vampire.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib"));
+
+MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor)
+{
+ if(time >= frag_target.spawnshieldtime)
+ if(frag_target != frag_attacker)
+ if(frag_target.deadflag == DEAD_NO)
+ {
+ frag_attacker.health += bound(0, damage_take, frag_target.health);
+ frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit);
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString)
+{
+ ret_string = strcat(ret_string, ":Vampire");
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString)
+{
+ ret_string = strcat(ret_string, ", Vampire");
+ return 0;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+#include "vampirehook.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+REGISTER_MUTATOR(vh, cvar("g_vampirehook"));
+
+bool autocvar_g_vampirehook_teamheal;
+float autocvar_g_vampirehook_damage;
+float autocvar_g_vampirehook_damagerate;
+float autocvar_g_vampirehook_health_steal;
+
+.float last_dmg;
+
+MUTATOR_HOOKFUNCTION(vh, GrappleHookThink)
+{SELFPARAM();
+ entity dmgent = ((SAME_TEAM(self.owner, self.aiment) && autocvar_g_vampirehook_teamheal) ? self.owner : self.aiment);
+
+ if(IS_PLAYER(self.aiment))
+ if(self.last_dmg < time)
+ if(!self.aiment.frozen)
+ if(time >= game_starttime)
+ if(DIFF_TEAM(self.owner, self.aiment) || autocvar_g_vampirehook_teamheal)
+ if(self.aiment.health > 0)
+ if(autocvar_g_vampirehook_damage)
+ {
+ self.last_dmg = time + autocvar_g_vampirehook_damagerate;
+ self.owner.damage_dealt += autocvar_g_vampirehook_damage;
+ Damage(dmgent, self, self.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, self.origin, '0 0 0');
+ if(SAME_TEAM(self.owner, self.aiment))
+ self.aiment.health = min(self.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
+ else
+ self.owner.health = min(self.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
+
+ if(dmgent == self.owner)
+ dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?!
+ }
+
+ return false;
+}
+
+#endif
#include "waypointsprites.qh"
-REGISTRY(Waypoints, BIT(6))
+REGISTRY(Waypoints, BITS(6))
+#define Waypoints_from(i) _Waypoints_from(i, WP_Null)
REGISTER_REGISTRY(RegisterWaypoints)
+REGISTRY_CHECK(Waypoints)
+
/** If you register a new waypoint, make sure to add it to all.inc */
#define REGISTER_WAYPOINT_(id, init) REGISTER(RegisterWaypoints, WP, Waypoints, id, m_id, init)
#define REGISTER_WAYPOINT(id, text, color, blink) REGISTER_WAYPOINT_(id, NEW(Waypoint, #id, text, color, blink))
REGISTRY(RadarIcons, BITS(7))
+#define RadarIcons_from(i) _RadarIcons_from(i, RADARICON_NONE)
REGISTER_REGISTRY(RegisterRadarIcons)
+REGISTRY_CHECK(RadarIcons)
+
.int m_radaricon;
-#define REGISTER_RADARICON(id, num) REGISTER(RegisterRadarIcons, RADARICON, RadarIcons, id, m_id, new(RadarIcon)) { this.m_radaricon = num; this.netname = #id; }
+#define REGISTER_RADARICON(id, num) REGISTER(RegisterRadarIcons, RADARICON, RadarIcons, id, m_id, new(RadarIcon)) { make_pure(this); this.m_radaricon = num; this.netname = #id; }
REGISTER_WAYPOINT(Null, "", '0 0 0', 1);
--- /dev/null
+#include "waypointsprites.qc"
REGISTER_MUTATOR(waypointsprites, true);
+REGISTER_NET_LINKED(waypointsprites)
+
#ifdef SVQC
/** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */
bool WaypointSprite_SendEntity(entity this, entity to, float sendflags)
{
- WriteMutator(MSG_ENTITY, waypointsprites);
+ WriteHeader(MSG_ENTITY, waypointsprites);
sendflags = sendflags & 0x7F;
#ifdef CSQC
void Ent_WaypointSprite();
-MUTATOR_HOOKFUNCTION(waypointsprites, CSQC_Ent_Update) {
- if (MUTATOR_RETURNVALUE) return false;
- if (!ReadMutatorEquals(mutator_argv_int_0, waypointsprites)) return false;
+NET_HANDLE(waypointsprites, bool isnew) {
Ent_WaypointSprite();
return true;
}
if (get_weaponinfo(self.wp_extra).spawnflags & WEP_FLAG_SUPERWEAPON)
return 2;
}
- if (s == WP_Item.netname) return Items[self.wp_extra].m_waypointblink;
+ if (s == WP_Item.netname) return Items_from(self.wp_extra).m_waypointblink;
return 1;
}
vector spritelookupcolor(entity this, string s, vector def)
{
if (s == WP_Weapon.netname || s == RADARICON_Weapon.netname) return get_weaponinfo(this.wp_extra).wpcolor;
- if (s == WP_Item.netname || s == RADARICON_Item.netname) return Items[this.wp_extra].m_color;
- if (s == WP_Buff.netname || s == RADARICON_Buff.netname) return Buffs[this.wp_extra].m_color;
+ if (s == WP_Item.netname || s == RADARICON_Item.netname) return Items_from(this.wp_extra).m_color;
+ if (s == WP_Buff.netname || s == RADARICON_Buff.netname) return Buffs_from(this.wp_extra).m_color;
return def;
}
string spritelookuptext(string s)
{SELFPARAM();
if (s == WP_RaceStartFinish.netname) return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start");
- if (s == WP_Weapon.netname) return get_weaponinfo(self.wp_extra).message;
- if (s == WP_Item.netname) return Items[self.wp_extra].m_waypoint;
- if (s == WP_Buff.netname) return Buffs[self.wp_extra].m_prettyName;
+ if (s == WP_Weapon.netname) return get_weaponinfo(self.wp_extra).m_name;
+ if (s == WP_Item.netname) return Items_from(self.wp_extra).m_waypoint;
+ if (s == WP_Buff.netname) return Buffs_from(self.wp_extra).m_prettyName;
if (s == WP_Monster.netname) return get_monsterinfo(self.wp_extra).monster_name;
// need to loop, as our netname could be one of three
)
{
entity wp = new(sprite_waypoint);
+ make_pure(wp);
wp.teleport_time = time + _lifetime;
wp.fade_time = _lifetime;
wp.exteriormodeltoclient = ref;
--- /dev/null
+#ifdef SVQC
+#include "weaponarena_random.qc"
+#endif
--- /dev/null
+#ifdef IMPLEMENTATION
+// WEAPONTODO: rename the cvars
+REGISTER_MUTATOR(weaponarena_random, true);
+
+MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn) {
+ SELFPARAM();
+ if (!g_weaponarena_random) return;
+ if (g_weaponarena_random_with_blaster) this.weapons &= ~WEPSET(BLASTER);
+ W_RandomWeapons(this, g_weaponarena_random);
+ if (g_weaponarena_random_with_blaster) this.weapons |= WEPSET(BLASTER);
+}
+
+#endif
+#include "all.qh"
+
#if defined(CSQC)
#include "../../client/defs.qh"
- #include "all.qh"
#include "../buffs/all.qh"
#include "../movetypes/movetypes.qh"
#include "../../client/main.qh"
}
#endif // CSQC
-REGISTER_NET_LINKED(Nade_Heal, bool isNew)
+REGISTER_NET_LINKED(Nade_Heal)
#ifdef CSQC
+NET_HANDLE(Nade_Heal, bool isNew)
{
- Net_Accept();
+ Net_Accept(Nade_Heal);
int sf = ReadByte();
if (sf & 1) {
this.origin_x = ReadCoord();
// this.ltime = time + this.healer_lifetime;
healer_setup(this);
}
+ return true;
}
#endif
const int PROJECTILE_NADE_MONSTER = 82;
const int PROJECTILE_NADE_MONSTER_BURN = 83;
-REGISTRY(Nades, BIT(3))
+REGISTRY(Nades, BITS(4))
+#define Nades_from(i) _Nades_from(i, NADE_TYPE_Null)
REGISTER_REGISTRY(RegisterNades)
+REGISTRY_CHECK(Nades)
+
#define REGISTER_NADE(id) REGISTER(RegisterNades, NADE_TYPE, Nades, id, m_id, NEW(Nade))
CLASS(Nade, Object)
#include "net_notice.qh"
+REGISTER_NET_TEMP(TE_CSQC_SVNOTICE)
+
#ifdef SVQC
void sv_notice_join_think()
{SELFPARAM();
void sv_notice_to(entity _to, string _notice, float _howlong, float _modal)
{
msg_entity = _to;
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_SVNOTICE);
+ WriteHeader(MSG_ONE, TE_CSQC_SVNOTICE);
WriteString(MSG_ONE, _notice);
WriteLong(MSG_ONE, _howlong);
WriteByte(MSG_ONE, _modal);
#endif // SVQC
#ifdef CSQC
+NET_HANDLE(TE_CSQC_SVNOTICE, bool isNew)
+{
+ cl_notice_read();
+ return true;
+}
void cl_notice_read()
{
- entity _notice;
//float _done;
//float _modal;
- _notice = spawn();
- _notice.classname = "sv_notice";
+ entity _notice = new(sv_notice);
_notice.netname = strzone(ReadString());
_notice.alpha = ReadLong() + time;
_notice.skin = ReadByte();
#if defined(CSQC)
+ #include "../client/announcer.qh"
#elif defined(MENUQC)
#elif defined(SVQC)
#include "constants.qh"
remove(notif);
}
-void Destroy_All_Notifications(void)
+void Destroy_All_Notifications()
{
entity notif;
int i;
// Global Entity Setup
// =====================
entity notif = spawn();
+ make_pure(notif);
switch(typeId)
{
case MSG_ANNCE:
{
if(notif.nent_enabled)
{
- precache_sound(sprintf("announcer/%s/%s.wav", autocvar_cl_announcer, snd));
+ precache_sound(sprintf("announcer/%s/%s.wav", AnnouncerOption(), snd));
notif.nent_channel = channel;
notif.nent_snd = strzone(snd);
notif.nent_vol = vol;
// used by MSG_CHOICE to build list of choices
#ifdef SVQC
-void Notification_GetCvars(void)
+void Notification_GetCvars()
{
for(int i = 0; i <= NOTIF_CHOICE_COUNT; ++i)
{
soundchannel,
sprintf(
"announcer/%s/%s.wav",
- autocvar_cl_announcer,
+ AnnouncerOption(),
soundfile
),
soundvolume,
soundchannel,
sprintf(
"announcer/%s/%s.wav",
- autocvar_cl_announcer,
+ AnnouncerOption(),
soundfile
),
soundvolume,
soundchannel,
sprintf(
"announcer/%s/%s.wav",
- autocvar_cl_announcer,
+ AnnouncerOption(),
soundfile
),
soundvolume,
// Notification Networking
// =========================
+REGISTER_NET_LINKED(ENT_CLIENT_NOTIFICATION)
+
#ifdef CSQC
-void Read_Notification(float is_new)
+NET_HANDLE(ENT_CLIENT_NOTIFICATION, bool is_new)
{
int net_type = ReadByte();
int net_name = ReadShort();
+ return = true;
entity notif;
{
if(Notification_ShouldSend(self.nent_broadcast, client, self.nent_client))
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
WriteByte(MSG_ENTITY, self.nent_net_type);
WriteShort(MSG_ENTITY, self.nent_net_name);
for(int i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); }
if(killed_cpid != NO_CPID)
{
- net_notif = spawn();
- net_notif.classname = "net_kill_notification";
+ net_notif = new(net_kill_notification);
+ make_pure(net_notif);
net_notif.nent_broadcast = broadcast;
net_notif.nent_client = client;
net_notif.nent_net_type = MSG_CENTER_CPID;
}
else
{
- entity net_notif = spawn();
+ entity net_notif = new(net_notification);
+ make_pure(net_notif);
net_notif.owner = notif;
- net_notif.classname = "net_notification";
net_notif.nent_broadcast = broadcast;
net_notif.nent_client = client;
net_notif.nent_net_type = net_type;
VARITEM(3, 4, XPD(s1, s2, s3, f1, f2, f3, f4)) \
VARITEM(4, 4, XPD(s1, s2, s3, s4, f1, f2, f3, f4))
-void Destroy_All_Notifications(void);
+void Destroy_All_Notifications();
void Create_Notification_Entity(
float var_default,
float var_cvar,
float f1, float f2, float f3, float f4);
#ifdef CSQC // CLIENT ONLY
-void Read_Notification(float is_new);
string prev_soundfile;
float prev_soundtime;
#endif
#ifdef SVQC
.float FRAG_VERBOSE;
-void Notification_GetCvars(void);
+void Notification_GetCvars();
float autocvar_notification_server_allows_location = 1; // 0 = no, 1 = yes
#else
float autocvar_notification_item_centerprinttime = 1.5;
ARG_CASE(ARG_CS_SV, "spree_end", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \
ARG_CASE(ARG_CS_SV, "spree_lost", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \
ARG_CASE(ARG_CS_SV, "item_wepname", WEP_NAME(f1)) \
- ARG_CASE(ARG_CS_SV, "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buffs[f1].m_color), Buffs[f1].m_prettyName)) \
- ARG_CASE(ARG_CS_SV, "f3buffname", sprintf("%s%s", rgb_to_hexcolor(Buffs[f3].m_color), Buffs[f3].m_prettyName)) \
+ ARG_CASE(ARG_CS_SV, "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buffs_from(f1).m_color), Buffs_from(f1).m_prettyName)) \
+ ARG_CASE(ARG_CS_SV, "f3buffname", sprintf("%s%s", rgb_to_hexcolor(Buffs_from(f3).m_color), Buffs_from(f3).m_prettyName)) \
ARG_CASE(ARG_CS_SV, "item_wepammo", (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \
ARG_CASE(ARG_DC, "item_centime", ftos(autocvar_notification_item_centerprinttime)) \
ARG_CASE(ARG_SV, "death_team", Team_ColoredFullName(f1)) \
float NOTIF_CHOICE_COUNT;
// notification limits -- INCREASE AS NECESSARY
-const float NOTIF_ANNCE_MAX = 100;
-const float NOTIF_INFO_MAX = 350;
-const float NOTIF_CENTER_MAX = 250;
-const float NOTIF_MULTI_MAX = 200;
-const float NOTIF_CHOICE_MAX = 30;
+const float NOTIF_ANNCE_MAX = 400;
+const float NOTIF_INFO_MAX = 450;
+const float NOTIF_CENTER_MAX = 350;
+const float NOTIF_MULTI_MAX = 300;
+const float NOTIF_CHOICE_MAX = 50;
// notification entities
entity msg_annce_notifs[NOTIF_ANNCE_MAX];
returns true if handled
=============
*/
-bool PlayerJump (void)
+bool PlayerJump ()
{SELFPARAM();
if (PHYS_FROZEN(self))
return true; // no jumping in freezetag when frozen
return false;
}
-void PM_check_nickspam(void)
+void PM_check_nickspam()
{SELFPARAM();
#ifdef SVQC
if (time >= self.nickspamtime)
#endif
}
-void PM_check_spider(void)
+void PM_check_spider()
{SELFPARAM();
#ifdef SVQC
if (time >= self.spider_slowness)
}
// predict frozen movement, as frozen players CAN move in some cases
-void PM_check_frozen(void)
+void PM_check_frozen()
{SELFPARAM();
if (!PHYS_FROZEN(self))
return;
if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS))
{
if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
- GlobalSound(globalsound_metalfall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+ GlobalSound(GS_FALL_METAL, CH_PLAYER, VOICETYPE_PLAYERSOUND);
else
- GlobalSound(globalsound_fall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+ GlobalSound(GS_FALL, CH_PLAYER, VOICETYPE_PLAYERSOUND);
}
}
}
#endif
}
-void PM_check_blocked(void)
+void PM_check_blocked()
{SELFPARAM();
#ifdef SVQC
if (!self.player_blocked)
#endif
}
-void PM_check_vortex(void)
+void PM_check_vortex()
{SELFPARAM();
#ifdef SVQC
// WEAPONTODO
}
#ifdef SVQC
-void SV_PlayerPhysics(void)
+void SV_PlayerPhysics()
#elif defined(CSQC)
-void CSQC_ClientMovement_PlayerMove_Frame(void)
+void CSQC_ClientMovement_PlayerMove_Frame()
#endif
{SELFPARAM();
PM_Main();
void PM_multijump();
.float watertype;
+ .float waterlevel;
.int items;
.vector movement;
#endif
#ifdef SVQC
-void PlayerStats_Prematch(void)
+void PlayerStats_Prematch()
{
//foobar
}
LOG_TRACE("Added item ", sprintf("#%s", event), "=", data, " to PS_D_IN_DB\n");
}
-void PlayerStats_PlayerDetail(void)
+void PlayerStats_PlayerDetail()
{
// http://stats.xonotic.org/player/me
if((autocvar_g_playerstats_playerdetail_uri != "") && (crypto_getmyidstatus(0) > 0))
}
}
-void PlayerStats_PlayerDetail_CheckUpdate(void)
+void PlayerStats_PlayerDetail_CheckUpdate()
{
// determine whether we should retrieve playerdetail information again
float gamecount = cvar("cl_matchcount");
float PlayerStats_PlayerDetail_Status = PS_D_STATUS_IDLE;
string autocvar_g_playerstats_playerdetail_uri = "http://stats.xonotic.org/player/me";
float autocvar_g_playerstats_playerdetail_autoupdatetime = 1800; // automatically update every 30 minutes anyway
-void PlayerStats_PlayerDetail(void);
-void PlayerStats_PlayerDetail_CheckUpdate(void);
+void PlayerStats_PlayerDetail();
+void PlayerStats_PlayerDetail_CheckUpdate();
void PlayerStats_PlayerDetail_Handler(entity fh, entity p, float status);
#endif
#endif
SOUND(GRENADE_BOUNCE5, W_Sound("grenade_bounce5"));
SOUND(GRENADE_BOUNCE6, W_Sound("grenade_bounce6"));
Sound SND_GRENADE_BOUNCE_RANDOM() {
- return Sounds[SND_GRENADE_BOUNCE1.m_id + rint(random() * 5)];
+ return Sounds_from(SND_GRENADE_BOUNCE1.m_id + rint(random() * 5));
}
SOUND(GRENADE_FIRE, W_Sound("grenade_fire"));
SOUND(GRENADE_IMPACT, W_Sound("grenade_impact"));
SOUND(HAGEXP2, W_Sound("hagexp2"));
SOUND(HAGEXP3, W_Sound("hagexp3"));
Sound SND_HAGEXP_RANDOM() {
- return Sounds[SND_HAGEXP1.m_id + rint(random() * 2)];
+ return Sounds_from(SND_HAGEXP1.m_id + rint(random() * 2));
}
SOUND(HOOKBOMB_FIRE, W_Sound("hookbomb_fire"));
SOUND(NEXWHOOSH2, W_Sound("nexwhoosh2"));
SOUND(NEXWHOOSH3, W_Sound("nexwhoosh3"));
Sound SND_NEXWHOOSH_RANDOM() {
- return Sounds[SND_NEXWHOOSH1.m_id + rint(random() * 2)];
+ return Sounds_from(SND_NEXWHOOSH1.m_id + rint(random() * 2));
}
SOUND(RELOAD, W_Sound("reload")); // until weapons have individual reload sounds, precache the reload sound here
SOUND(RIC2, W_Sound("ric2"));
SOUND(RIC3, W_Sound("ric3"));
Sound SND_RIC_RANDOM() {
- return Sounds[SND_RIC1.m_id + rint(random() * 2)];
+ return Sounds_from(SND_RIC1.m_id + rint(random() * 2));
}
SOUND(ROCKET_DET, W_Sound("rocket_det"));
SOUND(WEAPONPICKUP_NEW_TOYS, W_Sound("weaponpickup_new_toys"));
SOUND(WEAPON_SWITCH, W_Sound("weapon_switch"));
-SOUND(CTF_CAPTURE_NEUTRAL, "ctf/capture.ogg");
-SOUND(CTF_CAPTURE_RED, "ctf/red_capture.wav");
-SOUND(CTF_CAPTURE_BLUE, "ctf/blue_capture.wav");
-SOUND(CTF_CAPTURE_YELLOW, "ctf/yellow_capture.ogg");
-SOUND(CTF_CAPTURE_PINK, "ctf/pink_capture.ogg");
+SOUND(CTF_CAPTURE_NEUTRAL, "ctf/capture");
+SOUND(CTF_CAPTURE_RED, "ctf/red_capture");
+SOUND(CTF_CAPTURE_BLUE, "ctf/blue_capture");
+SOUND(CTF_CAPTURE_YELLOW, "ctf/yellow_capture");
+SOUND(CTF_CAPTURE_PINK, "ctf/pink_capture");
Sound SND_CTF_CAPTURE(int teamid) {
switch (teamid) {
case NUM_TEAM_1: return SND_CTF_CAPTURE_RED;
}
}
-SOUND(CTF_DROPPED_NEUTRAL, "ctf/neutral_dropped.wav");
-SOUND(CTF_DROPPED_RED, "ctf/red_dropped.wav");
-SOUND(CTF_DROPPED_BLUE, "ctf/blue_dropped.wav");
-SOUND(CTF_DROPPED_YELLOW, "ctf/yellow_dropped.wav");
-SOUND(CTF_DROPPED_PINK, "ctf/pink_dropped.wav");
+SOUND(CTF_DROPPED_NEUTRAL, "ctf/neutral_dropped");
+SOUND(CTF_DROPPED_RED, "ctf/red_dropped");
+SOUND(CTF_DROPPED_BLUE, "ctf/blue_dropped");
+SOUND(CTF_DROPPED_YELLOW, "ctf/yellow_dropped");
+SOUND(CTF_DROPPED_PINK, "ctf/pink_dropped");
Sound SND_CTF_DROPPED(int teamid) {
switch (teamid) {
case NUM_TEAM_1: return SND_CTF_DROPPED_RED;
}
}
-SOUND(CTF_PASS, "ctf/pass.wav");
-SOUND(CTF_RESPAWN, "ctf/flag_respawn.wav");
+SOUND(CTF_PASS, "ctf/pass");
+SOUND(CTF_RESPAWN, "ctf/flag_respawn");
-SOUND(CTF_RETURNED_NEUTRAL, "ctf/return.wav");
-SOUND(CTF_RETURNED_RED, "ctf/red_returned.wav");
-SOUND(CTF_RETURNED_BLUE, "ctf/blue_returned.wav");
-SOUND(CTF_RETURNED_YELLOW, "ctf/yellow_returned.wav");
-SOUND(CTF_RETURNED_PINK, "ctf/pink_returned.wav");
+SOUND(CTF_RETURNED_NEUTRAL, "ctf/return");
+SOUND(CTF_RETURNED_RED, "ctf/red_returned");
+SOUND(CTF_RETURNED_BLUE, "ctf/blue_returned");
+SOUND(CTF_RETURNED_YELLOW, "ctf/yellow_returned");
+SOUND(CTF_RETURNED_PINK, "ctf/pink_returned");
Sound SND_CTF_RETURNED(int teamid) {
switch (teamid) {
case NUM_TEAM_1: return SND_CTF_RETURNED_RED;
}
}
-SOUND(CTF_TAKEN_NEUTRAL, "ctf/neutral_taken.wav");
-SOUND(CTF_TAKEN_RED, "ctf/red_taken.wav");
-SOUND(CTF_TAKEN_BLUE, "ctf/blue_taken.wav");
-SOUND(CTF_TAKEN_YELLOW, "ctf/yellow_taken.wav");
-SOUND(CTF_TAKEN_PINK, "ctf/pink_taken.wav");
+SOUND(CTF_TAKEN_NEUTRAL, "ctf/neutral_taken");
+SOUND(CTF_TAKEN_RED, "ctf/red_taken");
+SOUND(CTF_TAKEN_BLUE, "ctf/blue_taken");
+SOUND(CTF_TAKEN_YELLOW, "ctf/yellow_taken");
+SOUND(CTF_TAKEN_PINK, "ctf/pink_taken");
Sound SND_CTF_TAKEN(int teamid) {
switch (teamid) {
case NUM_TEAM_1: return SND_CTF_TAKEN_RED;
}
}
-SOUND(CTF_TOUCH, "ctf/touch.wav");
-
-SOUND(DOM_CLAIM, "domination/claim.wav");
-
-SOUND(KA_DROPPED, "keepaway/dropped.wav");
-SOUND(KA_PICKEDUP, "keepaway/pickedup.wav");
-SOUND(KA_RESPAWN, "keepaway/respawn.wav");
-SOUND(KA_TOUCH, "keepaway/touch.wav");
-
-SOUND(KH_ALARM, "kh/alarm.wav");
-SOUND(KH_CAPTURE, "kh/capture.wav");
-SOUND(KH_COLLECT, "kh/collect.wav");
-SOUND(KH_DESTROY, "kh/destroy.wav");
-SOUND(KH_DROP, "kh/drop.wav");
-
-SOUND(NB_BOUNCE, "nexball/bounce.ogg");
-SOUND(NB_DROP, "nexball/drop.ogg");
-SOUND(NB_SHOOT1, "nexball/shoot1.ogg");
-SOUND(NB_SHOOT2, "nexball/shoot2.ogg");
-SOUND(NB_STEAL, "nexball/steal.ogg");
-
-SOUND(ONS_CONTROLPOINT_BUILD, "onslaught/controlpoint_build.ogg");
-SOUND(ONS_CONTROLPOINT_BUILT, "onslaught/controlpoint_built.ogg");
-SOUND(ONS_CONTROLPOINT_UNDERATTACK, "onslaught/controlpoint_underattack.ogg");
-SOUND(ONS_DAMAGEBLOCKEDBYSHIELD, "onslaught/damageblockedbyshield.wav");
-SOUND(ONS_ELECTRICITY_EXPLODE, "onslaught/electricity_explode.ogg");
-SOUND(ONS_GENERATOR_DECAY, "onslaught/generator_decay.ogg");
-SOUND(ONS_GENERATOR_UNDERATTACK, "onslaught/generator_underattack.ogg");
-SOUND(ONS_HIT1, "onslaught/ons_hit1.ogg");
-SOUND(ONS_HIT2, "onslaught/ons_hit2.ogg");
-SOUND(ONS_SPARK1, "onslaught/ons_spark1.ogg");
-SOUND(ONS_SPARK2, "onslaught/ons_spark2.ogg");
-SOUND(ONS_SHOCKWAVE, "onslaught/shockwave.ogg");
-
-SOUND(PORTO_BOUNCE, "porto/bounce.ogg");
-SOUND(PORTO_CREATE, "porto/create.ogg");
-SOUND(PORTO_EXPIRE, "porto/expire.ogg");
-SOUND(PORTO_EXPLODE, "porto/explode.ogg");
-SOUND(PORTO_FIRE, "porto/fire.ogg");
-SOUND(PORTO_UNSUPPORTED, "porto/unsupported.ogg");
-
-SOUND(TUR_PHASER, "turrets/phaser.ogg");
-
-SOUND(VEH_ALARM, "vehicles/alarm.wav");
-SOUND(VEH_ALARM_SHIELD, "vehicles/alarm_shield.wav");
-SOUND(VEH_MISSILE_ALARM, "vehicles/missile_alarm.wav");
+SOUND(CTF_TOUCH, "ctf/touch");
+
+SOUND(DOM_CLAIM, "domination/claim");
+
+SOUND(KA_DROPPED, "keepaway/dropped");
+SOUND(KA_PICKEDUP, "keepaway/pickedup");
+SOUND(KA_RESPAWN, "keepaway/respawn");
+SOUND(KA_TOUCH, "keepaway/touch");
+
+SOUND(KH_ALARM, "kh/alarm");
+SOUND(KH_CAPTURE, "kh/capture");
+SOUND(KH_COLLECT, "kh/collect");
+SOUND(KH_DESTROY, "kh/destroy");
+SOUND(KH_DROP, "kh/drop");
+
+SOUND(NB_BOUNCE, "nexball/bounce");
+SOUND(NB_DROP, "nexball/drop");
+SOUND(NB_SHOOT1, "nexball/shoot1");
+SOUND(NB_SHOOT2, "nexball/shoot2");
+SOUND(NB_STEAL, "nexball/steal");
+
+SOUND(ONS_CONTROLPOINT_BUILD, "onslaught/controlpoint_build");
+SOUND(ONS_CONTROLPOINT_BUILT, "onslaught/controlpoint_built");
+SOUND(ONS_CONTROLPOINT_UNDERATTACK, "onslaught/controlpoint_underattack");
+SOUND(ONS_DAMAGEBLOCKEDBYSHIELD, "onslaught/damageblockedbyshield");
+SOUND(ONS_ELECTRICITY_EXPLODE, "onslaught/electricity_explode");
+SOUND(ONS_GENERATOR_DECAY, "onslaught/generator_decay");
+SOUND(ONS_GENERATOR_UNDERATTACK, "onslaught/generator_underattack");
+SOUND(ONS_HIT1, "onslaught/ons_hit1");
+SOUND(ONS_HIT2, "onslaught/ons_hit2");
+SOUND(ONS_SPARK1, "onslaught/ons_spark1");
+SOUND(ONS_SPARK2, "onslaught/ons_spark2");
+SOUND(ONS_SHOCKWAVE, "onslaught/shockwave");
+
+SOUND(PORTO_BOUNCE, "porto/bounce");
+SOUND(PORTO_CREATE, "porto/create");
+SOUND(PORTO_EXPIRE, "porto/expire");
+SOUND(PORTO_EXPLODE, "porto/explode");
+SOUND(PORTO_FIRE, "porto/fire");
+SOUND(PORTO_UNSUPPORTED, "porto/unsupported");
+
+SOUND(TUR_PHASER, "turrets/phaser");
+
+SOUND(VEH_ALARM, "vehicles/alarm");
+SOUND(VEH_ALARM_SHIELD, "vehicles/alarm_shield");
+SOUND(VEH_MISSILE_ALARM, "vehicles/missile_alarm");
SOUND(VEH_BUMBLEBEE_FIRE, W_Sound("flacexp3"));
-SOUND(VEH_RACER_BOOST, "vehicles/racer_boost.wav");
-SOUND(VEH_RACER_IDLE, "vehicles/racer_idle.wav");
-SOUND(VEH_RACER_MOVE, "vehicles/racer_move.wav");
+SOUND(VEH_RACER_BOOST, "vehicles/racer_boost");
+SOUND(VEH_RACER_IDLE, "vehicles/racer_idle");
+SOUND(VEH_RACER_MOVE, "vehicles/racer_move");
-SOUND(VEH_RAPTOR_FLY, "vehicles/raptor_fly.wav");
-SOUND(VEH_RAPTOR_SPEED, "vehicles/raptor_speed.wav");
+SOUND(VEH_RAPTOR_FLY, "vehicles/raptor_fly");
+SOUND(VEH_RAPTOR_SPEED, "vehicles/raptor_speed");
-SOUND(VEH_SPIDERBOT_DIE, "vehicles/spiderbot_die.wav");
-SOUND(VEH_SPIDERBOT_IDLE, "vehicles/spiderbot_idle.wav");
-SOUND(VEH_SPIDERBOT_JUMP, "vehicles/spiderbot_jump.wav");
-SOUND(VEH_SPIDERBOT_LAND, "vehicles/spiderbot_land.wav");
-SOUND(VEH_SPIDERBOT_STRAFE, "vehicles/spiderbot_strafe.wav");
-SOUND(VEH_SPIDERBOT_WALK, "vehicles/spiderbot_walk.wav");
+SOUND(VEH_SPIDERBOT_DIE, "vehicles/spiderbot_die");
+SOUND(VEH_SPIDERBOT_IDLE, "vehicles/spiderbot_idle");
+SOUND(VEH_SPIDERBOT_JUMP, "vehicles/spiderbot_jump");
+SOUND(VEH_SPIDERBOT_LAND, "vehicles/spiderbot_land");
+SOUND(VEH_SPIDERBOT_STRAFE, "vehicles/spiderbot_strafe");
+SOUND(VEH_SPIDERBOT_WALK, "vehicles/spiderbot_walk");
-SOUND(NADE_BEEP, "overkill/grenadebip.ogg");
+SOUND(NADE_BEEP, "overkill/grenadebip");
-SOUND(BUFF_LOST, "relics/relic_effect.wav");
+SOUND(BUFF_LOST, "relics/relic_effect");
-SOUND(POWEROFF, "misc/poweroff.wav");
-SOUND(POWERUP, "misc/powerup.ogg");
-SOUND(SHIELD_RESPAWN, "misc/shield_respawn.wav");
-SOUND(STRENGTH_RESPAWN, "misc/strength_respawn.wav");
+SOUND(POWEROFF, "misc/poweroff");
+SOUND(POWERUP, "misc/powerup");
+SOUND(SHIELD_RESPAWN, "misc/shield_respawn");
+SOUND(STRENGTH_RESPAWN, "misc/strength_respawn");
-SOUND(ARMOR25, "misc/armor25.wav");
-SOUND(ARMORIMPACT, "misc/armorimpact.wav");
-SOUND(BODYIMPACT1, "misc/bodyimpact1.wav");
-SOUND(BODYIMPACT2, "misc/bodyimpact2.wav");
+SOUND(ARMOR25, "misc/armor25");
+SOUND(ARMORIMPACT, "misc/armorimpact");
+SOUND(BODYIMPACT1, "misc/bodyimpact1");
+SOUND(BODYIMPACT2, "misc/bodyimpact2");
-SOUND(ITEMPICKUP, "misc/itempickup.ogg");
-SOUND(ITEMRESPAWNCOUNTDOWN, "misc/itemrespawncountdown.ogg");
-SOUND(ITEMRESPAWN, "misc/itemrespawn.ogg");
-SOUND(MEGAHEALTH, "misc/megahealth.ogg");
+SOUND(ITEMPICKUP, "misc/itempickup");
+SOUND(ITEMRESPAWNCOUNTDOWN, "misc/itemrespawncountdown");
+SOUND(ITEMRESPAWN, "misc/itemrespawn");
+SOUND(MEGAHEALTH, "misc/megahealth");
-SOUND(LAVA, "player/lava.wav");
-SOUND(SLIME, "player/slime.wav");
+SOUND(LAVA, "player/lava");
+SOUND(SLIME, "player/slime");
-SOUND(GIB, "misc/gib.wav");
-SOUND(GIB_SPLAT01, "misc/gib_splat01.wav");
-SOUND(GIB_SPLAT02, "misc/gib_splat02.wav");
-SOUND(GIB_SPLAT03, "misc/gib_splat03.wav");
-SOUND(GIB_SPLAT04, "misc/gib_splat04.wav");
+SOUND(GIB, "misc/gib");
+SOUND(GIB_SPLAT01, "misc/gib_splat01");
+SOUND(GIB_SPLAT02, "misc/gib_splat02");
+SOUND(GIB_SPLAT03, "misc/gib_splat03");
+SOUND(GIB_SPLAT04, "misc/gib_splat04");
Sound SND_GIB_SPLAT_RANDOM() {
- return Sounds[SND_GIB_SPLAT01.m_id + floor(prandom() * 4)];
+ return Sounds_from(SND_GIB_SPLAT01.m_id + floor(prandom() * 4));
}
-SOUND(HIT, "misc/hit.wav");
-SOUND(TYPEHIT, "misc/typehit.wav");
+SOUND(HIT, "misc/hit");
+SOUND(TYPEHIT, "misc/typehit");
-SOUND(SPAWN, "misc/spawn.ogg");
+SOUND(SPAWN, "misc/spawn");
-SOUND(TALK, "misc/talk.wav");
+SOUND(TALK, "misc/talk");
-SOUND(TELEPORT, "misc/teleport.ogg");
+SOUND(TELEPORT, "misc/teleport");
-SOUND(INVSHOT, "misc/invshot.wav");
+SOUND(INVSHOT, "misc/invshot");
-SOUND(JETPACK_FLY, "misc/jetpack_fly.ogg");
+SOUND(JETPACK_FLY, "misc/jetpack_fly");
--- /dev/null
+#ifdef SVQC
+
+bool autocvar_bot_sound_monopoly;
+
+.entity realowner;
+bool sound_allowed(int to, entity e)
+{
+ for ( ; ; )
+ {
+ if (e.classname == "body") e = e.enemy;
+ else if (e.realowner && e.realowner != e) e = e.realowner;
+ else if (e.owner && e.owner != e) e = e.owner;
+ else break;
+ }
+ // sounds to self may always pass
+ if (to == MSG_ONE && e == msg_entity) return true;
+ // sounds by players can be removed
+ if (autocvar_bot_sound_monopoly && IS_REAL_CLIENT(e)) return false;
+ // anything else may pass
+ return true;
+}
+
+/** hack: string precache_sound(string s) = #19; */
+int precache_sound_index(string s) = #19;
+
+const int SVC_SOUND = 6;
+const int SVC_STOPSOUND = 16;
+
+const int SND_VOLUME = BIT(0);
+const int SND_ATTENUATION = BIT(1);
+const int SND_LARGEENTITY = BIT(3);
+const int SND_LARGESOUND = BIT(4);
+
+void soundtoat(int to, entity e, vector o, int chan, string samp, float vol, float attenu)
+{
+ if (!sound_allowed(to, e)) return;
+ int entno = etof(e);
+ int idx = precache_sound_index(samp);
+ attenu = floor(attenu * 64);
+ vol = floor(vol * 255);
+ int sflags = 0;
+ if (vol != 255) sflags |= SND_VOLUME;
+ if (attenu != 64) sflags |= SND_ATTENUATION;
+ if (entno >= 8192 || chan < 0 || chan > 7) sflags |= SND_LARGEENTITY;
+ if (idx >= 256) sflags |= SND_LARGESOUND;
+ WriteByte(to, SVC_SOUND);
+ WriteByte(to, sflags);
+ if (sflags & SND_VOLUME) WriteByte(to, vol);
+ if (sflags & SND_ATTENUATION) WriteByte(to, attenu);
+ if (sflags & SND_LARGEENTITY)
+ {
+ WriteShort(to, entno);
+ WriteByte(to, chan);
+ }
+ else
+ {
+ WriteShort(to, (entno << 3) | chan);
+ }
+ if (sflags & SND_LARGESOUND) WriteShort(to, idx);
+ else WriteByte(to, idx);
+ WriteCoord(to, o.x);
+ WriteCoord(to, o.y);
+ WriteCoord(to, o.z);
+}
+
+void soundto(int _dest, entity e, int chan, string samp, float vol, float _atten)
+{
+ if (!sound_allowed(_dest, e)) return;
+ vector o = e.origin + 0.5 * (e.mins + e.maxs);
+ soundtoat(_dest, e, o, chan, samp, vol, _atten);
+}
+void soundat(entity e, vector o, int chan, string samp, float vol, float _atten)
+{
+ soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
+}
+void stopsoundto(int _dest, entity e, int chan)
+{
+ if (!sound_allowed(_dest, e)) return;
+ int entno = num_for_edict(e);
+ if (entno >= 8192 || chan < 0 || chan > 7)
+ {
+ int idx = precache_sound_index(SND(Null));
+ int sflags = SND_LARGEENTITY;
+ if (idx >= 256) sflags |= SND_LARGESOUND;
+ WriteByte(_dest, SVC_SOUND);
+ WriteByte(_dest, sflags);
+ WriteShort(_dest, entno);
+ WriteByte(_dest, chan);
+ if (sflags & SND_LARGESOUND) WriteShort(_dest, idx);
+ else WriteByte(_dest, idx);
+ WriteCoord(_dest, e.origin.x);
+ WriteCoord(_dest, e.origin.y);
+ WriteCoord(_dest, e.origin.z);
+ }
+ else
+ {
+ WriteByte(_dest, SVC_STOPSOUND);
+ WriteShort(_dest, entno * 8 + chan);
+ }
+}
+void stopsound(entity e, int chan)
+{
+ if (!sound_allowed(MSG_BROADCAST, e)) return;
+ stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
+ stopsoundto(MSG_ALL, e, chan); // in case of packet loss
+}
+
+void play2(entity e, string filename)
+{
+ msg_entity = e;
+ soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
+}
+
+.float spamtime;
+/** use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame) */
+float spamsound(entity e, int chan, string samp, float vol, float _atten)
+{
+ if (!sound_allowed(MSG_BROADCAST, e)) return false;
+ if (time > e.spamtime)
+ {
+ e.spamtime = time;
+ _sound(e, chan, samp, vol, _atten);
+ return true;
+ }
+ return false;
+}
+
+void play2team(float t, string filename)
+{
+ if (autocvar_bot_sound_monopoly) return;
+ entity head;
+ FOR_EACH_REALPLAYER(head)
+ {
+ if (head.team == t) play2(head, filename);
+ }
+}
+
+void play2all(string samp)
+{
+ if (autocvar_bot_sound_monopoly) return;
+ _sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
+}
+
+#endif
#include "sound.qh"
REGISTRY(Sounds, BITS(8))
+#define Sounds_from(i) _Sounds_from(i, SND_Null)
REGISTER_REGISTRY(RegisterSounds)
#define SOUND(name, path) \
REGISTER(RegisterSounds, SND, Sounds, name, m_id, NEW(Sound, SND_##name##_get))
// Used in places where a string is required
-#define SND(id) (SND_##id.sound_str())
+#define SND(id) Sound_fixpath(SND_##id)
-STATIC_INIT(RegisterSounds_precache) {
+PRECACHE(Sounds) {
FOREACH(Sounds, true, LAMBDA({
it.sound_precache(it);
}));
}
-SOUND(Null, "misc/null.wav");
+SOUND(Null, "misc/null");
#include "all.inc"
-
+#include "all.qc"
#endif
#ifndef SOUND_H
#define SOUND_H
+const int CH_INFO = 0;
+const int CH_TRIGGER = -3;
+const int CH_WEAPON_A = -1;
+const int CH_WEAPON_SINGLE = 1;
+const int CH_VOICE = -2;
+const int CH_BGM_SINGLE = 8;
+const int CH_AMBIENT = -9;
+const int CH_TRIGGER_SINGLE = 3;
+const int CH_SHOTS = -4;
+const int CH_SHOTS_SINGLE = 4;
+const int CH_WEAPON_B = -1;
+const int CH_PAIN = -6;
+const int CH_PAIN_SINGLE = 6;
+const int CH_PLAYER = -7;
+const int CH_PLAYER_SINGLE = 7;
+const int CH_TUBA_SINGLE = 5;
+
+const float ATTEN_NONE = 0;
+const float ATTEN_MIN = 0.015625;
+const float ATTEN_NORM = 0.5;
+const float ATTEN_LARGE = 1;
+const float ATTEN_IDLE = 2;
+const float ATTEN_STATIC = 3;
+const float ATTEN_MAX = 3.984375;
+
+const float VOL_BASE = 0.7;
+const float VOL_BASEVOICE = 1.0;
+
// Play all sounds via sound7, for access to the extra channels.
// Otherwise, channels 8 to 15 would be blocked for a weird QW feature.
#ifdef SVQC
- #define _sound(e, c, s, v, a) do { \
- entity __e = e; \
- if (!sound_allowed(MSG_BROADCAST, __e)) break; \
- sound7(__e, c, s, v, a, 0, 0); \
- } while (0)
+ #define _sound(e, c, s, v, a) \
+ do \
+ { \
+ entity __e = e; \
+ if (!sound_allowed(MSG_BROADCAST, __e)) break; \
+ sound7(__e, c, s, v, a, 0, 0); \
+ } \
+ while (0)
#else
- #define _sound(e, c, s, v, a) sound7(e, c, s, v, a, 0, 0)
+ #define _sound(e, c, s, v, a) sound7(e, c, s, v, a, 0, 0)
#endif
-#define sound(e, c, s, v, a) _sound(e, c, s.sound_str(), v, a)
+#define sound(e, c, s, v, a) _sound(e, c, Sound_fixpath(s), v, a)
+
+/**
+ * because sound7 didn't have origin
+ *
+ * @param e sound owner
+ * @param o sound origin
+ * @param chan sound channel
+ * @param samp sound filename
+ * @param vol sound volume
+ * @param atten sound attenuation
+ * @param speed
+ * @param sf
+ */
+#define sound8(e, o, chan, samp, vol, atten, speed, sf) \
+ do \
+ { \
+ entity __e = e; \
+ vector old_origin = __e.origin; \
+ vector old_mins = __e.mins; \
+ vector old_maxs = __e.maxs; \
+ setorigin(__e, o); \
+ setsize(__e, '0 0 0', '0 0 0'); \
+ sound7(__e, chan, samp, vol, atten, speed, sf); \
+ setorigin(__e, old_origin); \
+ setsize(__e, old_mins, old_maxs); \
+ } \
+ while (0)
CLASS(Sound, Object)
- ATTRIB(Sound, m_id, int, 0)
- ATTRIB(Sound, sound_str, string(), func_null)
- CONSTRUCTOR(Sound, string() path)
- {
- CONSTRUCT(Sound);
- this.sound_str = path;
- }
- METHOD(Sound, sound_precache, void(entity this)) {
- string s = this.sound_str();
- if (s && s != "" && !fexists(strcat("sound/", s))) {
- LOG_WARNINGF("Missing sound: \"%s\"\n", s);
- return;
- }
- LOG_TRACEF("precache_sound(\"%s\")\n", s);
- precache_sound(s);
- }
+ ATTRIB(Sound, m_id, int, 0)
+ ATTRIB(Sound, sound_str, string(), func_null)
+ CONSTRUCTOR(Sound, string() path)
+ {
+ CONSTRUCT(Sound);
+ this.sound_str = path;
+ }
+ #define Sound_fixpath(this) _Sound_fixpath((this).sound_str())
+ string _Sound_fixpath(string base)
+ {
+ if (base == "") return string_null;
+ #define extensions(x) \
+ x(wav) \
+ x(ogg) \
+ x(flac) \
+ /**/
+ string full, relative;
+ #define tryext(ext) { if (fexists(full = strcat("sound/", relative = strcat(base, "." #ext)))) break; }
+ do
+ {
+ extensions(tryext);
+#undef tryext
+#undef extensions
+ LOG_WARNINGF("Missing sound: \"%s\"\n", full);
+ return string_null;
+ }
+ while (0);
+ return relative;
+ }
+ METHOD(Sound, sound_precache, void(entity this))
+ {
+ string s = Sound_fixpath(this);
+ if (!s) return;
+ LOG_TRACEF("precache_sound(\"%s\")\n", s);
+ precache_sound(s);
+ }
ENDCLASS(Sound)
#endif
// 29 empty?
// 30 empty?
// 31 empty?
-const int STAT_KH_KEYS = 32;
-const int STAT_CTF_STATE = 33;
-// 34 empty?
-const int STAT_WEAPONS = 35;
-const int STAT_SWITCHWEAPON = 36;
-const int STAT_GAMESTARTTIME = 37;
-const int STAT_STRENGTH_FINISHED = 38;
-const int STAT_INVINCIBLE_FINISHED = 39;
-// 40 empty?
-const int STAT_ARC_HEAT = 41;
-const int STAT_PRESSED_KEYS = 42;
-const int STAT_ALLOW_OLDVORTEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config
-const int STAT_FUEL = 44;
-const int STAT_NB_METERSTART = 45;
-const int STAT_SHOTORG = 46; // compressShotOrigin
-const int STAT_LEADLIMIT = 47;
-const int STAT_WEAPON_CLIPLOAD = 48;
-const int STAT_WEAPON_CLIPSIZE = 49;
-const int STAT_VORTEX_CHARGE = 50;
-const int STAT_LAST_PICKUP = 51;
-const int STAT_HUD = 52;
-const int STAT_VORTEX_CHARGEPOOL = 53;
-const int STAT_HIT_TIME = 54;
-const int STAT_DAMAGE_DEALT_TOTAL = 55;
-const int STAT_TYPEHIT_TIME = 56;
-const int STAT_LAYED_MINES = 57;
-const int STAT_HAGAR_LOAD = 58;
-const int STAT_SWITCHINGWEAPON = 59;
-const int STAT_SUPERWEAPONS_FINISHED = 60;
-const int STAT_VEHICLESTAT_HEALTH = 61;
-const int STAT_VEHICLESTAT_SHIELD = 62;
-const int STAT_VEHICLESTAT_ENERGY = 63;
-const int STAT_VEHICLESTAT_AMMO1 = 64;
-const int STAT_VEHICLESTAT_RELOAD1 = 65;
-const int STAT_VEHICLESTAT_AMMO2 = 66;
-const int STAT_VEHICLESTAT_RELOAD2 = 67;
-const int STAT_VEHICLESTAT_W2MODE = 68;
-const int STAT_NADE_TIMER = 69;
-const int STAT_SECRETS_TOTAL = 70;
-const int STAT_SECRETS_FOUND = 71;
-const int STAT_RESPAWN_TIME = 72;
-const int STAT_ROUNDSTARTTIME = 73;
-const int STAT_WEAPONS2 = 74;
-const int STAT_WEAPONS3 = 75;
-const int STAT_MONSTERS_TOTAL = 76;
-const int STAT_MONSTERS_KILLED = 77;
-const int STAT_BUFFS = 78;
-const int STAT_NADE_BONUS = 79;
-const int STAT_NADE_BONUS_TYPE = 80;
-const int STAT_NADE_BONUS_SCORE = 81;
-const int STAT_HEALING_ORB = 82;
-const int STAT_HEALING_ORB_ALPHA = 83;
-const int STAT_PLASMA = 84;
-const int STAT_OK_AMMO_CHARGE = 85;
-const int STAT_OK_AMMO_CHARGEPOOL = 86;
-const int STAT_FROZEN = 87;
-const int STAT_REVIVE_PROGRESS = 88;
-// 89 empty?
-// 90 empty?
-// 91 empty?
-// 92 empty?
-// 93 empty?
-// 94 empty?
-// 95 empty?
-// 96 empty?
-// 97 empty?
-// 98 empty?
-const int STAT_ROUNDLOST = 99;
+
+enum {
+ STAT_WEAPONS = 32,
+ STAT_WEAPONS2,
+ STAT_WEAPONS3,
+
+ STAT_WEAPONSINMAP,
+ STAT_WEAPONSINMAP2,
+ STAT_WEAPONSINMAP3,
+
+ STAT_PL_VIEW_OFS1,
+ STAT_PL_VIEW_OFS2,
+ STAT_PL_VIEW_OFS3,
+
+ STAT_PL_CROUCH_VIEW_OFS1,
+ STAT_PL_CROUCH_VIEW_OFS2,
+ STAT_PL_CROUCH_VIEW_OFS3,
+
+ STAT_PL_MIN1,
+ STAT_PL_MIN2,
+ STAT_PL_MIN3,
+
+ STAT_PL_MAX1,
+ STAT_PL_MAX2,
+ STAT_PL_MAX3,
+
+ STAT_PL_CROUCH_MIN1,
+ STAT_PL_CROUCH_MIN2,
+ STAT_PL_CROUCH_MIN3,
+
+ STAT_PL_CROUCH_MAX1,
+ STAT_PL_CROUCH_MAX2,
+ STAT_PL_CROUCH_MAX3,
+
+ STAT_LAST_VECTOR
+};
+
+const int REGISTERED_STATS = 6;
+
+REGISTER_STAT(KH_KEYS, int)
+/** weapon requested to switch to; next WANTED weapon (for HUD) */
+REGISTER_STAT(SWITCHWEAPON, int)
+REGISTER_STAT(GAMESTARTTIME, float)
+REGISTER_STAT(STRENGTH_FINISHED, float)
+REGISTER_STAT(INVINCIBLE_FINISHED, float)
+/** arc heat in [0,1] */
+REGISTER_STAT(ARC_HEAT, float)
+
+enum {
+ STAT_FIRST_MAIN = (STAT_LAST_VECTOR - 1) + REGISTERED_STATS,
+
+ STAT_PRESSED_KEYS,
+ /** this stat could later contain some other bits of info, like, more server-side particle config */ STAT_ALLOW_OLDVORTEXBEAM,
+ STAT_FUEL,
+ STAT_NB_METERSTART,
+ /** compressShotOrigin */ STAT_SHOTORG,
+ STAT_LEADLIMIT,
+ STAT_WEAPON_CLIPLOAD,
+ STAT_WEAPON_CLIPSIZE,
+ STAT_VORTEX_CHARGE,
+ STAT_LAST_PICKUP,
+ STAT_HUD,
+ STAT_VORTEX_CHARGEPOOL,
+ STAT_HIT_TIME,
+ STAT_DAMAGE_DEALT_TOTAL,
+ STAT_TYPEHIT_TIME,
+ STAT_LAYED_MINES,
+ STAT_HAGAR_LOAD,
+ STAT_SWITCHINGWEAPON,
+ STAT_SUPERWEAPONS_FINISHED,
+ STAT_VEHICLESTAT_HEALTH,
+ STAT_VEHICLESTAT_SHIELD,
+ STAT_VEHICLESTAT_ENERGY,
+ STAT_VEHICLESTAT_AMMO1,
+ STAT_VEHICLESTAT_RELOAD1,
+ STAT_VEHICLESTAT_AMMO2,
+ STAT_VEHICLESTAT_RELOAD2,
+ STAT_VEHICLESTAT_W2MODE,
+ STAT_NADE_TIMER,
+ STAT_SECRETS_TOTAL,
+ STAT_SECRETS_FOUND,
+ STAT_RESPAWN_TIME,
+ STAT_ROUNDSTARTTIME,
+ STAT_MONSTERS_TOTAL,
+ STAT_MONSTERS_KILLED,
+ STAT_BUFFS,
+ STAT_NADE_BONUS,
+ STAT_NADE_BONUS_TYPE,
+ STAT_NADE_BONUS_SCORE,
+ STAT_HEALING_ORB,
+ STAT_HEALING_ORB_ALPHA,
+ STAT_PLASMA,
+ STAT_OK_AMMO_CHARGE,
+ STAT_OK_AMMO_CHARGEPOOL,
+ STAT_FROZEN,
+ STAT_REVIVE_PROGRESS,
+ STAT_ROUNDLOST,
+ STAT_BUFF_TIME,
+ STAT_CTF_FLAGSTATUS,
+ STAT_MULTIJUMP_DODGING,
+ STAT_MULTIJUMP_MAXSPEED,
+ STAT_GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND,
+ STAT_BUGRIGS_REVERSE_STOPPING,
+ STAT_BUGRIGS_REVERSE_SPINNING,
+ STAT_BUGRIGS_CAR_JUMPING,
+ STAT_BUGRIGS_FRICTION_AIR,
+ STAT_BUGRIGS_STEER,
+ STAT_BUGRIGS_SPEED_POW,
+ STAT_BUGRIGS_SPEED_REF,
+ STAT_BUGRIGS_ACCEL,
+ STAT_BUGRIGS_FRICTION_BRAKE,
+ STAT_BUGRIGS_AIR_STEERING,
+ STAT_BUGRIGS_FRICTION_FLOOR,
+ STAT_BUGRIGS_REVERSE_SPEEDING,
+ STAT_BUGRIGS_PLANAR_MOVEMENT,
+ STAT_BUGRIGS_ANGLE_SMOOTHING,
+ STAT_BUGRIGS,
+ STAT_GAMEPLAYFIX_STEPDOWN,
+ STAT_MOVEVARS_JUMPSTEP,
+ STAT_NOSTEP,
+ STAT_GAMEPLAYFIX_UNSTICKPLAYERS,
+ STAT_GAMEPLAYFIX_STEPMULTIPLETIMES,
+ STAT_GAMEPLAYFIX_DOWNTRACEONGROUND,
+ STAT_GAMEPLAYFIX_EASIERWATERJUMP,
+ STAT_MOVEVARS_FRICTION_SLICK,
+ STAT_MOVEVARS_FRICTION_ONLAND,
+ STAT_MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS,
+ STAT_MOVEVARS_TRACK_CANJUMP,
+ STAT_DOUBLEJUMP,
+ STAT_MOVEVARS_CL_TRACK_CANJUMP,
+ STAT_MULTIJUMP_ADD,
+ STAT_MULTIJUMP_SPEED,
+ STAT_MULTIJUMP,
+ STAT_DODGING_TIMEOUT,
+ STAT_DODGING_WALL,
+ STAT_DODGING_UP_SPEED,
+ STAT_DODGING_RAMP_TIME,
+ STAT_DODGING_HEIGHT_THRESHOLD,
+ STAT_DODGING_DISTANCE_THRESHOLD,
+ STAT_DODGING_HORIZ_SPEED,
+ STAT_DODGING_DELAY,
+ STAT_DODGING_FROZEN_NO_DOUBLETAP,
+ STAT_DODGING_HORIZ_SPEED_FROZEN,
+ STAT_DODGING,
+ STAT_DODGING_FROZEN,
+ STAT_JETPACK_MAXSPEED_UP,
+ STAT_JETPACK_MAXSPEED_SIDE,
+ STAT_JETPACK_FUEL,
+ STAT_JETPACK_ANTIGRAVITY,
+ STAT_JETPACK_ACCEL_SIDE,
+ STAT_JETPACK_ACCEL_UP,
+ STAT_MOVEVARS_HIGHSPEED,
+
+ STAT_LAST_MAIN
+};
+
+const int STAT_LAST = STAT_LAST_MAIN + 5;
/* The following stats change depending on the gamemode, so can share the same ID */
-// IDs 100 to 104 reserved for gamemodes
// freeze tag, clan arena, jailbreak
-const int STAT_REDALIVE = 100;
-const int STAT_BLUEALIVE = 101;
-const int STAT_YELLOWALIVE = 102;
-const int STAT_PINKALIVE = 103;
+enum {
+ STAT_REDALIVE = STAT_LAST_MAIN,
+ STAT_BLUEALIVE,
+ STAT_YELLOWALIVE,
+ STAT_PINKALIVE,
+};
// domination
-const int STAT_DOM_TOTAL_PPS = 100;
-const int STAT_DOM_PPS_RED = 101;
-const int STAT_DOM_PPS_BLUE = 102;
-const int STAT_DOM_PPS_YELLOW = 103;
-const int STAT_DOM_PPS_PINK = 104;
+enum {
+ STAT_DOM_TOTAL_PPS = STAT_LAST_MAIN,
+ STAT_DOM_PPS_RED,
+ STAT_DOM_PPS_BLUE,
+ STAT_DOM_PPS_YELLOW,
+ STAT_DOM_PPS_PINK,
+};
// vip
-const int STAT_VIP = 100;
-const int STAT_VIP_RED = 101;
-const int STAT_VIP_BLUE = 102;
-const int STAT_VIP_YELLOW = 103;
-const int STAT_VIP_PINK = 104;
+enum {
+ STAT_VIP = STAT_LAST_MAIN,
+ STAT_VIP_RED,
+ STAT_VIP_BLUE,
+ STAT_VIP_YELLOW,
+ STAT_VIP_PINK,
+};
// key hunt
-const int STAT_KH_REDKEY_TEAM = 100;
-const int STAT_KH_BLUEKEY_TEAM = 101;
-const int STAT_KH_YELLOWKEY_TEAM = 102;
-const int STAT_KH_PINKKEY_TEAM = 103;
+enum {
+ STAT_KH_REDKEY_TEAM = STAT_LAST_MAIN,
+ STAT_KH_BLUEKEY_TEAM,
+ STAT_KH_YELLOWKEY_TEAM,
+ STAT_KH_PINKKEY_TEAM,
+};
-/* Gamemode-specific stats end here */
+#define ASSERT_LESS(name, var, const) noref int name[(const - var + 1)];
+ASSERT_LESS(stat_limit, STAT_LAST, 220)
-const int STAT_PL_VIEW_OFS1 = 105;
-const int STAT_PL_VIEW_OFS2 = 106;
-const int STAT_PL_VIEW_OFS3 = 107;
-const int STAT_PL_MIN1 = 108;
-const int STAT_PL_MIN2 = 109;
-const int STAT_PL_MIN3 = 110;
-const int STAT_PL_MAX1 = 111;
-const int STAT_PL_MAX2 = 112;
-const int STAT_PL_MAX3 = 113;
-const int STAT_PL_CROUCH_MIN1 = 114;
-const int STAT_PL_CROUCH_MIN2 = 115;
-const int STAT_PL_CROUCH_MIN3 = 116;
-const int STAT_PL_CROUCH_MAX1 = 117;
-const int STAT_PL_CROUCH_MAX2 = 118;
-const int STAT_PL_CROUCH_MAX3 = 119;
-const int STAT_PL_CROUCH_VIEW_OFS1 = 117;
-const int STAT_PL_CROUCH_VIEW_OFS2 = 118;
-const int STAT_PL_CROUCH_VIEW_OFS3 = 119;
-const int STAT_WEAPONSINMAP = 120;
-const int STAT_WEAPONSINMAP2 = 121;
-const int STAT_WEAPONSINMAP3 = 122;
-const int STAT_BUFF_TIME = 123;
-const int STAT_CTF_FLAGSTATUS = 124;
-// 125 empty?
-// 126 empty?
-// 127 empty?
-// 128 empty?
-// 129 empty?
-// 130 empty?
-// 131 empty?
-// 132 empty?
-// 133 empty?
-// 134 empty?
-// 135 empty?
-// 136 empty?
-// 137 empty?
-// 138 empty?
-// 139 empty?
-// 140 reserved
-// 141 reserved
-// 142 reserved
-// 143 reserved
-// 144 reserved
-// 145 reserved
-// 146 reserved
-// 147 reserved
-// 148 reserved
-// 149 reserved
-// 150 reserved
-// 151 reserved
-// 152 reserved
-// 153 reserved
-// 154 reserved
-// 155 reserved
-// 156 empty?
-// 157 empty?
-// 158 empty?
-// 159 empty?
-// 160 empty?
-// 161 empty?
-// 162 empty?
-// 162 empty?
-// 163 empty?
-// 164 empty?
-// 165 empty?
-const int STAT_MULTIJUMP_DODGING = 166;
-const int STAT_MULTIJUMP_MAXSPEED = 167;
-const int STAT_GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND = 168;
-const int STAT_BUGRIGS_REVERSE_STOPPING = 169;
-const int STAT_BUGRIGS_REVERSE_SPINNING = 170;
-const int STAT_BUGRIGS_CAR_JUMPING = 171;
-const int STAT_BUGRIGS_FRICTION_AIR = 172;
-const int STAT_BUGRIGS_STEER = 173;
-const int STAT_BUGRIGS_SPEED_POW = 174;
-const int STAT_BUGRIGS_SPEED_REF = 175;
-const int STAT_BUGRIGS_ACCEL = 176;
-const int STAT_BUGRIGS_FRICTION_BRAKE = 177;
-const int STAT_BUGRIGS_AIR_STEERING = 178;
-const int STAT_BUGRIGS_FRICTION_FLOOR = 179;
-const int STAT_BUGRIGS_REVERSE_SPEEDING = 180;
-const int STAT_BUGRIGS_PLANAR_MOVEMENT = 181;
-const int STAT_BUGRIGS_ANGLE_SMOOTHING = 182;
-const int STAT_BUGRIGS = 183;
-const int STAT_GAMEPLAYFIX_STEPDOWN = 184;
-const int STAT_MOVEVARS_JUMPSTEP = 185;
-const int STAT_NOSTEP = 186;
-const int STAT_GAMEPLAYFIX_UNSTICKPLAYERS = 187;
-const int STAT_GAMEPLAYFIX_STEPMULTIPLETIMES = 188;
-const int STAT_GAMEPLAYFIX_DOWNTRACEONGROUND = 189;
-const int STAT_GAMEPLAYFIX_EASIERWATERJUMP = 190;
-const int STAT_MOVEVARS_FRICTION_SLICK = 191;
-const int STAT_MOVEVARS_FRICTION_ONLAND = 192;
-const int STAT_MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS = 193;
-const int STAT_MOVEVARS_TRACK_CANJUMP = 194;
-// 195 empty?
-const int STAT_DOUBLEJUMP = 196;
-const int STAT_MOVEVARS_CL_TRACK_CANJUMP = 197;
-const int STAT_MULTIJUMP_ADD = 198;
-const int STAT_MULTIJUMP_SPEED = 199;
-const int STAT_MULTIJUMP = 200;
-const int STAT_DODGING_TIMEOUT = 201;
-const int STAT_DODGING_WALL = 202;
-const int STAT_DODGING_UP_SPEED = 203;
-const int STAT_DODGING_RAMP_TIME = 204;
-const int STAT_DODGING_HEIGHT_THRESHOLD = 205;
-const int STAT_DODGING_DISTANCE_THRESHOLD = 206;
-const int STAT_DODGING_HORIZ_SPEED = 207;
-const int STAT_DODGING_DELAY = 208;
-const int STAT_DODGING_FROZEN_NO_DOUBLETAP = 209;
-const int STAT_DODGING_HORIZ_SPEED_FROZEN = 210;
-const int STAT_DODGING = 211;
-const int STAT_DODGING_FROZEN = 212;
-const int STAT_JETPACK_MAXSPEED_UP = 213;
-const int STAT_JETPACK_MAXSPEED_SIDE = 214;
-const int STAT_JETPACK_FUEL = 215;
-const int STAT_JETPACK_ANTIGRAVITY = 216;
-const int STAT_JETPACK_ACCEL_SIDE = 217;
-const int STAT_JETPACK_ACCEL_UP = 218;
-const int STAT_MOVEVARS_HIGHSPEED = 219;
const int STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR = 220;
const int STAT_MOVEVARS_AIRCONTROL_PENALTY = 221;
const int STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222;
return;
// wait for targets to spawn
- controller = spawn();
- controller.classname = "func_bobbing_controller";
+ controller = new(func_bobbing_controller);
controller.owner = self;
controller.nextthink = time + 1;
controller.think = func_bobbing_controller_think;
RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER.m_id, world);
if(self.cnt) // TODO
- pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
+ __pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
if(self.respawntime)
{
+REGISTER_NET_LINKED(ENT_CLIENT_CONVEYOR)
+
void conveyor_think()
{SELFPARAM();
#ifdef CSQC
bool conveyor_send(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_CONVEYOR);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_CONVEYOR);
WriteByte(MSG_ENTITY, sf);
if(sf & 1)
self.move_time = time;
}
-void ent_conveyor()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_CONVEYOR, bool isnew)
+{
float sf = ReadByte();
if(sf & 1)
if(sf & 2)
self.state = ReadByte();
+
+ return true;
}
#endif
+++ /dev/null
-#ifdef CSQC
-void ent_conveyor();
-#endif
}
}
+.float door_finished;
/*
================
{SELFPARAM();
if (!IS_PLAYER(other))
return;
- if (self.owner.attack_finished_single > time)
+ if (self.owner.door_finished > time)
return;
- self.owner.attack_finished_single = time + 2;
+ self.owner.door_finished = time + 2;
#ifdef SVQC
if (!(self.owner.dmg) && (self.owner.message != ""))
#endif
return;
- if (time < self.attack_finished_single)
+ if (time < self.door_finished)
return;
// check if door is locked
if (!door_check_keys(self, other))
return;
- self.attack_finished_single = time + 1;
+ self.door_finished = time + 1;
activator = other;
entity trigger;
vector t1 = fmins, t2 = fmaxs;
- trigger = spawn();
- trigger.classname = "doortriggerfield";
+ trigger = new(doortriggerfield);
trigger.movetype = MOVETYPE_NONE;
trigger.solid = SOLID_TRIGGER;
trigger.owner = self;
door_spawnfield(cmins, cmaxs);
}
+REGISTER_NET_LINKED(ENT_CLIENT_DOOR)
+
#ifdef SVQC
/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
if two doors touch, they are assumed to be connected and operate as a unit.
float door_send(entity to, float sf)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_DOOR);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_DOOR);
WriteByte(MSG_ENTITY, sf);
if(sf & SF_TRIGGER_INIT)
trigger_draw_generic(this);
}
-void ent_door()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_DOOR, bool isnew)
+{
float sf = ReadByte();
if(sf & SF_TRIGGER_INIT)
self.pos2_y = ReadCoord();
self.pos2_z = ReadCoord();
}
+ return true;
}
#endif
#ifdef CSQC
// stuff for preload
-void ent_door();
-// abused
-.float attack_finished_single;
+.float door_finished;
#endif
_sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM);
}
+.float door_finished;
+
void secret_blocked()
{SELFPARAM();
- if (time < self.attack_finished_single)
+ if (time < self.door_finished)
return;
- self.attack_finished_single = time + 0.5;
+ self.door_finished = 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);
}
{SELFPARAM();
if (!other.iscreature)
return;
- if (self.attack_finished_single > time)
+ if (self.door_finished > time)
return;
- self.attack_finished_single = time + 2;
+ self.door_finished = time + 2;
if (self.message)
{
self.active = ACTIVE_ACTIVE;
// wait for targets to spawn
- controller = spawn();
- controller.classname = "func_fourier_controller";
+ controller = new(func_fourier_controller);
controller.owner = self;
controller.nextthink = time + 1;
controller.think = func_fourier_controller_think;
#ifndef TRIGGERS_FUNC_INCLUDE_H
#define TRIGGERS_FUNC_INCLUDE_H
-#include "conveyor.qh"
#include "door.qh"
#include "ladder.qh"
-#include "plat.qh"
-#include "rainsnow.qh"
-#include "pointparticles.qh"
#include "train.qh"
#endif
+REGISTER_NET_LINKED(ENT_CLIENT_LADDER)
+
void func_ladder_touch()
{SELFPARAM();
#ifdef SVQC
#ifdef SVQC
bool func_ladder_send(entity this, entity to, float sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_LADDER);
WriteString(MSG_ENTITY, self.classname);
WriteByte(MSG_ENTITY, self.skin);
#elif defined(CSQC)
.float speed;
-void ent_func_ladder()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_LADDER, bool isnew)
+{
self.classname = strzone(ReadString());
self.skin = ReadByte();
self.speed = ReadByte();
self.model = strzone(ReadString());
trigger_common_read(false);
+
+ return = true;
+
self.mins = self.maxs = '0 0 0';
self.solid = SOLID_TRIGGER;
.float ladder_time;
.entity ladder_entity;
-
-#ifdef CSQC
-void ent_func_ladder();
-#endif
self.cnt = self.angles_z;
// wait for targets to spawn
- controller = spawn();
- controller.classname = "func_pendulum_controller";
+ controller = new(func_pendulum_controller);
controller.owner = self;
controller.nextthink = time + 1;
controller.think = func_pendulum_controller_think;
+REGISTER_NET_LINKED(ENT_CLIENT_PLAT)
+
#ifdef SVQC
void plat_link();
float plat_send(entity to, float sf)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_PLAT);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_PLAT);
WriteByte(MSG_ENTITY, sf);
if(sf & SF_TRIGGER_INIT)
//Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
}
-void ent_plat()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_PLAT, bool isnew)
+{
float sf = ReadByte();
if(sf & SF_TRIGGER_INIT)
self.move_angles = self.angles;
self.move_time = time;
}
+ return true;
}
#endif
+++ /dev/null
-#ifdef CSQC
-void ent_plat();
-#endif
-#ifdef CSQC
- #include "../../../client/particles.qh"
-#endif
+REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES)
#ifdef SVQC
// NOTE: also contains func_sparks
bool pointparticles_SendEntity(entity this, entity to, float fl)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
// optional features to save space
fl = fl & 0x0F;
}
#elif defined(CSQC)
+.int dphitcontentsmask;
+
+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
+class(PointParticles) .float glow_color; // palette index
+
void Draw_PointParticles(entity this)
{
float n, i, fail;
{
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);
+ __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);
+ __pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
}
if(self.noise != "")
{
self.bgmscript = string_null;
}
-void Ent_PointParticles()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew)
+{
float i;
vector v;
int f = ReadByte();
BGMScript_InitEntity(self);
}
+ return = true;
+
if(f & 2)
{
self.absolute = (self.impulse >= 0);
+++ /dev/null
-#ifdef CSQC
-
-void Ent_PointParticles();
-
-#endif
+REGISTER_NET_LINKED(ENT_CLIENT_RAINSNOW)
+
#ifdef SVQC
bool rainsnow_SendEntity(entity this, entity to, float sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_RAINSNOW);
WriteByte(MSG_ENTITY, self.state);
WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x);
WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y);
te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color);
}
-void Ent_RainOrSnow()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew)
+{
self.impulse = ReadByte(); // Rain, Snow, or Whatever
self.origin_x = ReadCoord();
self.origin_y = ReadCoord();
self.count = ReadShort() * 10;
self.glow_color = ReadByte(); // color
+ return = true;
+
self.mins = -0.5 * self.maxs;
self.maxs = 0.5 * self.maxs;
self.origin = self.origin - self.mins;
+++ /dev/null
-#ifdef CSQC
-void Ent_RainOrSnow();
-#endif
_sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE);
}
+REGISTER_NET_LINKED(ENT_CLIENT_TRAIN)
+
#ifdef SVQC
float train_send(entity to, float sf)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_TRAIN);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_TRAIN);
WriteByte(MSG_ENTITY, sf);
if(sf & SF_TRIGGER_INIT)
Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
}
-void ent_train()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew)
+{
float sf = ReadByte();
if(sf & SF_TRIGGER_INIT)
{
// TODO: make a reset function for trains
}
+
+ return true;
}
#endif
#ifdef CSQC
.float dmgtime;
-void ent_train();
#endif
self.destvec = self.origin - func_vectormamamam_origin(self, 0);
entity controller;
- controller = spawn();
- controller.classname = "func_vectormamamam_controller";
+ controller = new(func_vectormamamam_controller);
controller.owner = self;
controller.nextthink = time + 1;
controller.think = func_vectormamamam_controller_think;
// func
#include "func/include.qh"
-// misc
-#include "misc/include.qh"
-
// target
#include "target/include.qh"
+REGISTER_NET_LINKED(ENT_CLIENT_CORNER)
+
#ifdef SVQC
bool corner_send(entity to, int sf)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_CORNER);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_CORNER);
WriteString(MSG_ENTITY, self.platmovetype);
self.platmovetype = string_null;
}
-void ent_corner()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_CORNER, bool isnew)
+{
self.platmovetype = strzone(ReadString());
self.origin_x = ReadCoord();
self.wait = ReadByte();
+ return = true;
+
self.classname = "path_corner";
self.drawmask = MASK_NORMAL;
self.entremove = corner_remove;
+++ /dev/null
-#ifdef CSQC
-void ent_corner();
-#endif
-#include "include.qh"
-
#include "corner.qc"
#include "follow.qc"
#include "laser.qc"
+++ /dev/null
-#ifndef TRIGGERS_MISC_INCLUDE_H
-#define TRIGGERS_MISC_INCLUDE_H
-
-#include "corner.qh"
-#include "laser.qh"
-
-#endif
#elif defined(SVQC)
#endif
+REGISTER_NET_LINKED(ENT_CLIENT_LASER)
+
#ifdef SVQC
.float modelscale;
void misc_laser_aim()
bool laser_SendEntity(entity this, entity to, float fl)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_LASER);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_LASER);
fl = fl - (fl & 0xF0); // use that bit to indicate finite length laser
if(self.spawnflags & 2)
fl |= 0x80;
if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
{
if(self.cnt >= 0)
- pointparticles(self.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
+ __pointparticles(self.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
if(self.colormod != '0 0 0' && self.modelscale != 0)
adddynamiclight(trace_endpos + trace_plane_normal * 1, self.modelscale, self.colormod * 5);
}
}
-void Ent_Laser()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_LASER, bool isnew)
+{
InterpolateOrigin_Undo();
// 30 bytes, or 13 bytes for just moving
}
if(f & 4)
self.state = ReadByte();
+
+ return = true;
+
InterpolateOrigin_Note();
self.draw = Draw_Laser;
}
+++ /dev/null
-#ifdef CSQC
-void Ent_Laser();
-#endif
-void SUB_NullThink(void) { }
+void SUB_NullThink() { }
void() SUB_CalcMoveDone;
void() SUB_CalcAngleMoveDone;
==================
*/
.float friction;
-void SUB_Friction (void)
+void SUB_Friction ()
{SELFPARAM();
self.SUB_NEXTTHINK = time;
if(self.SUB_FLAGS & FL_ONGROUND)
}
}
-void SUB_SetFade_Think (void)
+void SUB_SetFade_Think ()
{SELFPARAM();
if(self.alpha == 0)
self.alpha = 1;
self.SUB_ORIGIN traveling at speed
===============
*/
-void SUB_CalcMoveDone (void)
+void SUB_CalcMoveDone ()
{SELFPARAM();
// After moving, set origin to exact final destination
}
.float platmovetype_turn;
-void SUB_CalcMove_controller_think (void)
+void SUB_CalcMove_controller_think ()
{SELFPARAM();
entity oldself;
float traveltime;
return;
}
- controller = spawn();
- controller.classname = "SUB_CalcMove_controller";
+ controller = new(SUB_CalcMove_controller);
controller.owner = self;
controller.platmovetype = self.platmovetype;
controller.platmovetype_start = self.platmovetype_start;
The calling function should make sure self.SUB_THINK is valid
===============
*/
-void SUB_CalcAngleMoveDone (void)
+void SUB_CalcAngleMoveDone ()
{SELFPARAM();
// After rotating, set angle to exact final angle
self.angles = self.finalangle;
#include "../../../server/defs.qh"
#endif
+REGISTER_NET_TEMP(TE_CSQC_TARGET_MUSIC)
+REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_MUSIC)
+
#ifdef SVQC
// values:
// when targetname is not set, THIS ONE is default
void target_music_sendto(float to, float is)
{SELFPARAM();
- WriteByte(to, SVC_TEMPENTITY);
- WriteByte(to, TE_CSQC_TARGET_MUSIC);
+ WriteHeader(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);
// when triggered, it is disabled/enabled for everyone
bool trigger_music_SendEntity(entity this, entity to, float sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC);
sf &= ~0x80;
if(self.cnt)
sf |= 0x80;
best = music_target;
if(music_trigger)
best = music_trigger;
- for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); ) if(e.noise)
+ for(e = world; (e = findfloat(e, enttype, NET_ENT_CLIENT_TRIGGER_MUSIC.m_id)); ) if(e.noise)
{
vol0 = e.lastvol;
if(getsoundtime(e, CH_BGM_SINGLE) < 0)
bgmtime = gettime(GETTIME_CDTRACK);
}
+NET_HANDLE(TE_CSQC_TARGET_MUSIC, bool isNew)
+{
+ Net_TargetMusic();
+ return true;
+}
+
void Net_TargetMusic()
{
int id = ReadShort();
string noi = ReadString();
entity e;
- for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); )
+ for(e = world; (e = findfloat(e, enttype, NET_ENT_CLIENT_TRIGGER_MUSIC.m_id)); )
{
if(e.count == id)
break;
if(!e)
{
e = spawn();
- e.enttype = ENT_CLIENT_TRIGGER_MUSIC;
+ e.enttype = NET_ENT_CLIENT_TRIGGER_MUSIC.m_id;
e.count = id;
}
if(e.noise != noi)
self.noise = string_null;
}
-void Ent_ReadTriggerMusic()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew)
+{
int f = ReadByte();
if(f & 4)
{
self.cnt = 1;
self.think = Ent_TriggerMusic_Think;
self.nextthink = time;
+ return true;
}
#endif
void Ent_TriggerMusic_Remove();
-void Ent_ReadTriggerMusic();
-
#elif defined(SVQC)
void target_music_kill();
#endif
return e;
}
-void teleport_findtarget (void)
+void teleport_findtarget ()
{SELFPARAM();
entity e;
float n;
entity Simple_TeleportPlayer(entity teleporter, entity player);
-void Teleport_Touch (void);
+void Teleport_Touch ();
-void teleport_findtarget (void);
+void teleport_findtarget ();
entity Teleport_Find(vector mi, vector ma);
#endif
}
+REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_IMPULSE)
+
/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
-------- KEYS --------
target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
#ifdef SVQC
bool trigger_impulse_send(entity to, int sf)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE);
WriteCoord(MSG_ENTITY, self.radius);
WriteCoord(MSG_ENTITY, self.strength);
trigger_impulse_link();
}
#elif defined(CSQC)
-void ent_trigger_impulse()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_TRIGGER_IMPULSE, bool isnew)
+{
self.radius = ReadCoord();
self.strength = ReadCoord();
self.falloff = ReadByte();
self.active = ReadByte();
trigger_common_read(true);
-
+ return = true;
self.classname = "trigger_impulse";
self.solid = SOLID_TRIGGER;
.float strength;
.float lastpushtime;
-#ifdef CSQC
-void ent_trigger_impulse();
-#endif
-
#endif
}
#endif
+REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH)
+REGISTER_NET_LINKED(ENT_CLIENT_TARGET_PUSH)
+
/*
trigger_push_calculatevelocity
#ifdef SVQC
float trigger_push_send(entity to, float sf)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH);
WriteByte(MSG_ENTITY, sf);
if(sf & 1)
bool target_push_send(entity this, entity to, float sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH);
WriteByte(MSG_ENTITY, self.cnt);
WriteString(MSG_ENTITY, self.targetname);
void target_push_link()
{SELFPARAM();
- Net_LinkEntity(self, false, 0, target_push_send);
- self.SendFlags |= 1; // update
+ //Net_LinkEntity(self, false, 0, target_push_send);
+ //self.SendFlags |= 1; // update
}
spawnfunc(target_push) { target_push_link(); }
spawnfunc(info_notnull) { target_push_link(); }
-spawnfunc(target_position) { target_push_link(); }
+spawnfunc(target_position) { make_pure(this); target_push_link(); }
#endif
#ifdef CSQC
-void ent_trigger_push()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH, bool isnew)
+{
float sf = ReadByte();
if(sf & 1)
self.team = ReadByte();
self.active = ReadByte();
}
+ return true;
}
void target_push_remove()
self.targetname = string_null;
}
-void ent_target_push()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_TARGET_PUSH, bool isnew)
+{
self.classname = "push_target";
self.cnt = ReadByte();
self.targetname = strzone(ReadString());
self.origin_x = ReadCoord();
self.origin_y = ReadCoord();
self.origin_z = ReadCoord();
+
+ return = true;
+
setorigin(self, self.origin);
self.drawmask = MASK_NORMAL;
void trigger_push_use();
#endif
-#ifdef CSQC
-void ent_trigger_push();
-
-void ent_target_push();
-#endif
-
/*
trigger_push_calculatevelocity
}
+REGISTER_NET_LINKED(ENT_CLIENT_KEYLOCK)
+
#ifdef SVQC
bool trigger_keylock_send(entity to, int sf)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_KEYLOCK);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_KEYLOCK);
WriteInt24_t(MSG_ENTITY, self.itemkeys);
WriteByte(MSG_ENTITY, self.height);
if(self.sounds == 1)
self.noise = "misc/secret.wav";
else if(self.sounds == 2)
- self.noise = SND(TALK);
+ self.noise = strzone(SND(TALK));
else //if (self.sounds == 3) {
self.noise = "misc/trigger1.wav";
}
self.targetname = string_null;
}
-void ent_keylock()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_KEYLOCK, bool isnew)
+{
self.itemkeys = ReadInt24_t();
self.height = ReadByte();
trigger_common_read(true);
+ return = true;
+
self.classname = "trigger_keylock";
self.drawmask = MASK_NORMAL;
self.draw = trigger_draw_generic;
#ifdef CSQC
-void ent_keylock();
bool item_keys_usekey(entity l, entity p)
{
int valid = (l.itemkeys & p.itemkeys);
}
else if (self.sounds == 2)
{
- self.noise = SND(TALK);
+ self.noise = strzone(SND(TALK));
}
else if (self.sounds == 3)
{
#ifdef SVQC
spawnfunc(trigger_swamp);
#endif
-void swamp_touch(void);
+void swamp_touch();
void swampslug_think();
*
* I do it this way becuz there is no "untouch" event.
*/
-void swampslug_think(void)
+void swampslug_think()
{SELFPARAM();
//Slowly kill the slug
self.health = self.health - 1;
self.nextthink = time + self.swamp_interval;
}
-void swamp_touch(void)
+void swamp_touch()
{SELFPARAM();
// If whatever thats touching the swamp is not a player
// or if its a dead player, just dont care abt it.
other.swampslug.health = 2;
}
+REGISTER_NET_LINKED(ENT_CLIENT_SWAMP)
+
#ifdef SVQC
float swamp_send(entity to, float sf)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_SWAMP);
WriteByte(MSG_ENTITY, self.dmg); // can probably get away with using a single byte here
WriteByte(MSG_ENTITY, self.swamp_slowdown);
#elif defined(CSQC)
-void ent_swamp()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_SWAMP, bool isnew)
+{
self.dmg = ReadByte();
self.swamp_slowdown = ReadByte();
self.swamp_interval = ReadByte();
trigger_common_read(false);
+ return = true;
+
self.classname = "trigger_swamp";
self.solid = SOLID_TRIGGER;
self.draw = trigger_draw_generic;
.float in_swamp; // bool
.entity swampslug; // Uses this to release from swamp ("untouch" fix)
-#ifdef CSQC
-void ent_swamp();
-#endif
-
#endif
#endif
}
-void Teleport_Touch (void)
+void Teleport_Touch ()
{SELFPARAM();
string s;
#include "../../../server/defs.qh"
#endif
+REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC)
+REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC_TRIGGER)
+
#ifdef SVQC
void viewloc_think()
bool trigger_viewloc_send(entity this, entity to, int sf)
{
// CSQC doesn't need to know our origin (yet), as we're only available for referencing
- WriteByte(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER);
WriteEntity(MSG_ENTITY, self.enemy);
WriteEntity(MSG_ENTITY, self.goalentity);
bool viewloc_send(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_VIEWLOC);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC);
WriteByte(MSG_ENTITY, self.cnt);
self.goalentity = findfloat(world, entnum, self.count);
}
-void ent_viewloc_trigger()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_VIEWLOC_TRIGGER, bool isnew)
+{
float point1 = ReadShort();
float point2 = ReadShort();
self.origin_x = ReadCoord();
self.origin_y = ReadCoord();
self.origin_z = ReadCoord();
+
+ return = true;
+
setorigin(self, self.origin);
self.cnt = point1;
self.drawmask = MASK_NORMAL; // not so concerned, but better keep it alive
}
-void ent_viewloc()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_VIEWLOC, bool isnew)
+{
self.cnt = ReadByte();
self.origin_x = ReadCoord();
self.movedir_y = ReadCoord();
self.movedir_z = ReadCoord();
+ return = true;
+
self.classname = ((self.cnt == 2) ? "target_viewlocation_end" : "target_viewlocation_start");
self.drawmask = MASK_NORMAL; // don't cull it
}
.entity goalentity;
.entity enemy;
.vector movedir;
-
-void ent_viewloc();
-void ent_viewloc_trigger();
#endif
#endif
if (self.delay)
{
// create a temp object to fire at a later time
- t = spawn();
- t.classname = "DelayedUse";
+ t = new(DelayedUse);
t.nextthink = time + self.delay;
t.think = DelayThink;
t.enemy = activator;
#include "all.qh"
+REGISTER_NET_LINKED(ENT_CLIENT_TURRET)
+
#ifdef SVQC
#include "sv_turrets.qh"
#endif
-#ifdef CSQC
-#include "cl_turrets.qh"
-#endif
-
#define IMPLEMENTATION
#include "all.inc"
#undef IMPLEMENTATION
#include "turret.qh"
-REGISTRY(Turrets, BIT(5))
+REGISTRY(Turrets, BITS(5))
+#define Turrets_from(i) _Turrets_from(i, TUR_Null)
+#define get_turretinfo(i) Turrets_from(i)
REGISTER_REGISTRY(RegisterTurrets)
+REGISTRY_CHECK(Turrets)
GENERIC_COMMAND(dumpturrets, "Dump all turrets into turrets_dump.txt")
REGISTER_TURRET(Null, NEW(Turret));
-Turret get_turretinfo(int id)
-{
- if (id >= TUR_FIRST && id <= TUR_LAST) {
- Turret t = Turrets[id];
- if (t) return t;
- }
- return TUR_Null;
-}
-
#include "all.inc"
#endif
if(self.health < 85)
if(dt < 0.01)
- pointparticles(particleeffectnum(EFFECT_SMOKE_LARGE), (self.origin + (randomvec() * 80)), '0 0 0', 1);
+ pointparticles(EFFECT_SMOKE_LARGE, (self.origin + (randomvec() * 80)), '0 0 0', 1);
if(self.health < 32)
if(dt < 0.015)
- pointparticles(particleeffectnum(EFFECT_SMOKE_SMALL), (self.origin + (randomvec() * 80)), '0 0 0', 1);
+ pointparticles(EFFECT_SMOKE_SMALL, (self.origin + (randomvec() * 80)), '0 0 0', 1);
}
float i;
sound (self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), self.origin, '0 0 0', 1);
+ pointparticles(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
for (i = 1; i < 5; i = i + 1)
turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', false);
if(trace_startsolid)
return world;
- gib = spawn();
+ gib = new(turret_gib);
setorigin(gib, _from);
_setmodel(gib, _model);
gib.colormod = _cmod;
gib.move_avelocity = prandomvec() * 32;
gib.move_time = time;
gib.damageforcescale = 1;
- gib.classname = "turret_gib";
return gib;
}
void turret_die()
{SELFPARAM();
sound (self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), self.origin, '0 0 0', 1);
+ pointparticles(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
if (!autocvar_cl_nogibs)
{
// Base
setmodel(self.tur_head, MDL_Null);
}
-void ent_turret()
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_TURRET, bool isnew)
+{
float sf;
sf = ReadByte();
self.health = _tmp;
}
//self.enemy.health = self.health / 255;
+ return true;
}
+++ /dev/null
-#ifndef CL_TURRETS_H
-#define CL_TURRETS_H
-
-void ent_turret();
-
-#endif
return 0;
}
-void Dump_Turret_Settings(void)
+void Dump_Turret_Settings()
{
float x, totalsettings = 0;
FOREACH(Turrets, it != TUR_Null, LAMBDA({
#ifdef SVQC
-void Dump_Turret_Settings(void);
+void Dump_Turret_Settings();
float tur_config_file;
float tur_config_alsoprint;
if(self.aim_flags & TFL_AIM_SIMPLE)
return real_origin(self.enemy);
- mintime = max(self.attack_finished_single - time,0) + sys_frametime;
+ mintime = max(self.attack_finished_single[0] - time,0) + sys_frametime;
// Baseline
pre_pos = real_origin(self.enemy);
bool turret_send(entity this, entity to, float sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_TURRET);
WriteByte(MSG_ENTITY, sf);
if(sf & TNSF_SETUP)
{
// Ready?
if (self.firecheck_flags & TFL_FIRECHECK_REFIRE)
- if (self.attack_finished_single > time) return 0;
+ if (self.attack_finished_single[0] > time) return 0;
// Special case: volly fire turret that has to fire a full volly if a shot was fired.
if (self.shoot_flags & TFL_SHOOT_VOLLYALWAYS)
Turret info = get_turretinfo(self.m_id);
info.tr_attack(info);
- self.attack_finished_single = time + self.shot_refire;
+ self.attack_finished_single[0] = time + self.shot_refire;
self.ammo -= self.shot_dmg;
self.volly_counter = self.volly_counter - 1;
self.enemy = world;
if (self.shot_volly > 1)
- self.attack_finished_single = time + self.shot_volly_refire;
+ self.attack_finished_single[0] = time + self.shot_volly_refire;
}
#ifdef TURRET_DEBUG
entity e = find(world, classname, "turret_manager");
if(!e)
{
- e = spawn();
- e.classname = "turret_manager";
+ e = new(turret_manager);
e.think = turrets_manager_think;
e.nextthink = time + 2;
}
self.nextthink = time + 1;
self.nextthink += turret_count * sys_frametime;
- self.tur_head = spawn();
+ self.tur_head = new(turret_head);
_setmodel(self.tur_head, tur.head_model);
setsize(self.tur_head, '0 0 0', '0 0 0');
setorigin(self.tur_head, '0 0 0');
setattachment(self.tur_head, self, "tag_head");
- self.tur_head.netname = self.tur_head.classname = "turret_head";
+ self.tur_head.netname = self.tur_head.classname;
self.tur_head.team = self.team;
self.tur_head.owner = self;
self.tur_head.takedamage = DAMAGE_NO;
}
ATTRIB(Turret, m_weapon, Weapon, WEP_Null)
+#ifdef SVQC
/** (SERVER) called when turret attacks */
METHOD(Turret, tr_attack, void(Turret this)) {
Weapon w = this.m_weapon;
- w.wr_think(w, self, true, false);
+ .entity weaponentity = weaponentities[0];
+ w.wr_think(w, self, weaponentity, 1);
}
+#endif
/** (ALL) */
METHOD(Turret, tr_config, void(Turret this)) {
// TODO
/* flags */ ATTRIB(EWheelAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(EWheelAttack, impulse, int, 5);
/* refname */ ATTRIB(EWheelAttack, netname, string, "turret_ewheel");
-/* wepname */ ATTRIB(EWheelAttack, message, string, _("eWheel"));
+/* wepname */ ATTRIB(EWheelAttack, m_name, string, _("eWheel"));
ENDCLASS(EWheelAttack)
REGISTER_WEAPON(EWHEEL, NEW(EWheelAttack));
#ifdef SVQC
void turret_initparams(entity);
-METHOD(EWheelAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+SOUND(EWheelAttack_FIRE, W_Sound("electro_fire"));
+METHOD(EWheelAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(EWheelAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
}
turret_do_updates(actor);
/* flags */ ATTRIB(FlacAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(FlacAttack, impulse, int, 5);
/* refname */ ATTRIB(FlacAttack, netname, string, "turret_flac");
-/* wepname */ ATTRIB(FlacAttack, message, string, _("FLAC"));
+/* wepname */ ATTRIB(FlacAttack, m_name, string, _("FLAC"));
ENDCLASS(FlacAttack)
REGISTER_WEAPON(FLAC, NEW(FlacAttack));
#ifdef SVQC
void turret_flac_projectile_think_explode();
-METHOD(FlacAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+SOUND(FlacAttack_FIRE, W_Sound("electro_fire"));
+METHOD(FlacAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(FlacAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
actor.tur_impacttime = 10;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
}
turret_tag_fire_update();
#ifdef SVQC
bool turret_fusionreactor_firecheck()
{SELFPARAM();
- if (self.attack_finished_single > time)
+ if (self.attack_finished_single[0] > time)
return false;
if (self.enemy.deadflag != DEAD_NO)
/* flags */ ATTRIB(HellionAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(HellionAttack, impulse, int, 9);
/* refname */ ATTRIB(HellionAttack, netname, string, "turret_hellion");
-/* wepname */ ATTRIB(HellionAttack, message, string, _("Hellion"));
+/* wepname */ ATTRIB(HellionAttack, m_name, string, _("Hellion"));
ENDCLASS(HellionAttack)
REGISTER_WEAPON(HELLION, NEW(HellionAttack));
float autocvar_g_turrets_unit_hellion_shot_speed_max;
void turret_hellion_missile_think();
-METHOD(HellionAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+SOUND(HellionAttack_FIRE, W_Sound("electro_fire"));
+METHOD(HellionAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(HellionAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
actor.shot_radius = 500;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
}
if (!isPlayer) {
if (actor.tur_head.frame != 0)
/* flags */ ATTRIB(HunterKillerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(HunterKillerAttack, impulse, int, 9);
/* refname */ ATTRIB(HunterKillerAttack, netname, string, "turret_hk");
-/* wepname */ ATTRIB(HunterKillerAttack, message, string, _("Hunter-Killer"));
+/* wepname */ ATTRIB(HunterKillerAttack, m_name, string, _("Hunter-Killer"));
ENDCLASS(HunterKillerAttack)
REGISTER_WEAPON(HK, NEW(HunterKillerAttack));
float autocvar_g_turrets_unit_hk_shot_speed_turnrate;
void turret_hk_missile_think();
-METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+SOUND(HunterKillerAttack_FIRE, W_Sound("electro_fire"));
+METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(HunterKillerAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
}
entity missile = turret_projectile(SND(ROCKET_FIRE), 6, 10, DEATH_TURRET_HK.m_id, PROJECTILE_ROCKET, FALSE, FALSE);
te_explosion (missile.origin);
/* flags */ ATTRIB(MachineGunTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(MachineGunTurretAttack, impulse, int, 9);
/* refname */ ATTRIB(MachineGunTurretAttack, netname, string, "turret_machinegun");
-/* wepname */ ATTRIB(MachineGunTurretAttack, message, string, _("Machinegun"));
+/* wepname */ ATTRIB(MachineGunTurretAttack, m_name, string, _("Machinegun"));
ENDCLASS(MachineGunTurretAttack)
REGISTER_WEAPON(TUR_MACHINEGUN, NEW(MachineGunTurretAttack));
#ifdef SVQC
void W_MachineGun_MuzzleFlash();
-
-METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+SOUND(MachineGunTurretAttack_FIRE, W_Sound("electro_fire"));
+METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR(machinegun, sustained_refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(machinegun, sustained_refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(MachineGunTurretAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
- weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready);
}
fireBullet (actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_MACHINEGUN.m_id, 0);
W_MachineGun_MuzzleFlash();
/* flags */ ATTRIB(MLRSTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(MLRSTurretAttack, impulse, int, 9);
/* refname */ ATTRIB(MLRSTurretAttack, netname, string, "turret_mlrs");
-/* wepname */ ATTRIB(MLRSTurretAttack, message, string, _("MLRS"));
+/* wepname */ ATTRIB(MLRSTurretAttack, m_name, string, _("MLRS"));
ENDCLASS(MLRSTurretAttack)
REGISTER_WEAPON(TUR_MLRS, NEW(MLRSTurretAttack));
#ifdef IMPLEMENTATION
#ifdef SVQC
-
-METHOD(MLRSTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+SOUND(MLRSTurretAttack_FIRE, W_Sound("electro_fire"));
+METHOD(MLRSTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR(machinegun, sustained_refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(machinegun, sustained_refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(MLRSTurretAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
actor.shot_radius = 500;
- weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready);
}
turret_tag_fire_update();
entity missile = turret_projectile(SND(ROCKET_FIRE), 6, 10, DEATH_TURRET_MLRS.m_id, PROJECTILE_ROCKET, TRUE, TRUE);
/* flags */ ATTRIB(PhaserTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(PhaserTurretAttack, impulse, int, 9);
/* refname */ ATTRIB(PhaserTurretAttack, netname, string, "turret_phaser");
-/* wepname */ ATTRIB(PhaserTurretAttack, message, string, _("Phaser"));
+/* wepname */ ATTRIB(PhaserTurretAttack, m_name, string, _("Phaser"));
ENDCLASS(PhaserTurretAttack)
REGISTER_WEAPON(PHASER, NEW(PhaserTurretAttack));
void beam_think();
.int fireflag;
-
-METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+SOUND(PhaserTurretAttack_FIRE, W_Sound("electro_fire"));
+METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(PhaserTurretAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
actor.shot_speed = 1;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
}
entity beam = spawn();
beam.ticrate = 0.1; //autocvar_sys_ticrate;
sound (beam, CH_SHOTS_SINGLE, SND_TUR_PHASER, VOL_BASE, ATTEN_NORM);
actor.fireflag = 1;
- beam.attack_finished_single = actor.attack_finished_single;
- actor.attack_finished_single = time; // + autocvar_sys_ticrate;
+ beam.attack_finished_single[0] = actor.attack_finished_single[0];
+ actor.attack_finished_single[0] = time; // + autocvar_sys_ticrate;
setattachment(beam,actor.tur_head, "tag_fire");
{SELFPARAM();
if ((time > self.cnt) || (self.owner.deadflag != DEAD_NO))
{
- self.owner.attack_finished_single = time + self.owner.shot_refire;
+ self.owner.attack_finished_single[0] = time + self.owner.shot_refire;
self.owner.fireflag = 2;
self.owner.tur_head.frame = 10;
sound (self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
self.nextthink = time + self.ticrate;
- self.owner.attack_finished_single = time + frametime;
+ self.owner.attack_finished_single[0] = time + frametime;
setself(self.owner);
FireImoBeam ( self.tur_shotorg,
self.tur_shotorg + self.tur_shotdir_updated * self.target_range,
CLASS(PlasmaDualAttack, PlasmaAttack)
/* refname */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual");
-/* wepname */ ATTRIB(PlasmaDualAttack, message, string, _("Dual plasma"));
+/* wepname */ ATTRIB(PlasmaDualAttack, m_name, string, _("Dual plasma"));
ENDCLASS(PlasmaDualAttack)
REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack));
/* flags */ ATTRIB(PlasmaAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(PlasmaAttack, impulse, int, 5);
/* refname */ ATTRIB(PlasmaAttack, netname, string, "turret_plasma");
-/* wepname */ ATTRIB(PlasmaAttack, message, string, _("Plasma"));
+/* wepname */ ATTRIB(PlasmaAttack, m_name, string, _("Plasma"));
ENDCLASS(PlasmaAttack)
REGISTER_WEAPON(PLASMA, NEW(PlasmaAttack));
#ifdef IMPLEMENTATION
#ifdef SVQC
-
-METHOD(PlasmaAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+SOUND(PlasmaAttack_FIRE, W_Sound("electro_fire"));
+METHOD(PlasmaAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(PlasmaAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
}
entity missile = turret_projectile(SND(HAGAR_FIRE), 1, 0, DEATH_TURRET_PLASMA.m_id, PROJECTILE_ELECTRO_BEAM, true, true);
missile.missile_flags = MIF_SPLASH;
{
self.tur_head.avelocity = '0 180 0' * (self.ammo / self.shot_dmg);
- if(self.attack_finished_single > time)
+ if(self.attack_finished_single[0] > time)
return;
float f;
/* flags */ ATTRIB(TeslaCoilTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(TeslaCoilTurretAttack, impulse, int, 9);
/* refname */ ATTRIB(TeslaCoilTurretAttack, netname, string, "turret_tesla");
-/* wepname */ ATTRIB(TeslaCoilTurretAttack, message, string, _("Tesla Coil"));
+/* wepname */ ATTRIB(TeslaCoilTurretAttack, m_name, string, _("Tesla Coil"));
ENDCLASS(TeslaCoilTurretAttack)
REGISTER_WEAPON(TESLA, NEW(TeslaCoilTurretAttack));
#ifdef SVQC
entity toast(entity from, float range, float damage);
-METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+SOUND(TeslaCoilTurretAttack_FIRE, W_Sound("electro_fire"));
+METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(TeslaCoilTurretAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
}
float d = actor.shot_dmg;
actor.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_TEAMCHECK;
- actor.attack_finished_single = time + actor.shot_refire;
+ actor.attack_finished_single[0] = time + actor.shot_refire;
for (int i = 0; i < 10; ++i) {
d *= 0.75;
r *= 0.85;
te_explosion (org);
- rocket = spawn ();
+ rocket = new(walker_rocket);
setorigin(rocket, org);
sound (self, CH_WEAPON_A, SND_HAGAR_FIRE, VOL_BASE, ATTEN_NORM);
setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
- rocket.classname = "walker_rocket";
rocket.owner = self;
rocket.bot_dodge = true;
rocket.bot_dodgerating = 50;
self.animflag = ANIM_MELEE;
}
}
- else if (self.tur_head.attack_finished_single < time)
+ else if (self.tur_head.attack_finished_single[0] < time)
{
if(self.tur_head.shot_volly)
{
self.tur_head.shot_volly = self.tur_head.shot_volly -1;
if(self.tur_head.shot_volly == 0)
- self.tur_head.attack_finished_single = time + (autocvar_g_turrets_unit_walker_rocket_refire);
+ self.tur_head.attack_finished_single[0] = time + (autocvar_g_turrets_unit_walker_rocket_refire);
else
- self.tur_head.attack_finished_single = time + 0.2;
+ self.tur_head.attack_finished_single[0] = time + 0.2;
if(self.tur_head.shot_volly > 1)
walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01")));
/* flags */ ATTRIB(WalkerTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(WalkerTurretAttack, impulse, int, 5);
/* refname */ ATTRIB(WalkerTurretAttack, netname, string, "turret_walker");
-/* wepname */ ATTRIB(WalkerTurretAttack, message, string, _("Walker"));
+/* wepname */ ATTRIB(WalkerTurretAttack, m_name, string, _("Walker"));
ENDCLASS(WalkerTurretAttack)
REGISTER_WEAPON(WALKER, NEW(WalkerTurretAttack));
#ifdef SVQC
-METHOD(WalkerTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+SOUND(WalkerTurretAttack_FIRE, W_Sound("electro_fire"));
+METHOD(WalkerTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
- if (fire1)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) {
+ if (fire & 1)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) {
if (isPlayer) {
turret_initparams(actor);
- W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0);
+ W_SetupShot_Dir(actor, v_forward, false, 0, SND(WalkerTurretAttack_FIRE), CH_WEAPON_B, 0);
actor.tur_shotdir_updated = w_shotdir;
actor.tur_shotorg = w_shotorg;
actor.tur_head = actor;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
}
sound (actor, CH_WEAPON_A, SND_UZI_FIRE, VOL_BASE, ATTEN_NORM);
fireBullet (actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_WALK_GUN.m_id, 0);
void mark_error(vector where,float lifetime)
{
- entity err;
-
- err = spawn();
- err.classname = "error_marker";
+ entity err = new(error_marker);
setmodel(err, MDL_MARKER);
setorigin(err,where);
err.movetype = MOVETYPE_NONE;
void mark_info(vector where,float lifetime)
{
- entity err;
-
- err = spawn();
- err.classname = "info_marker";
+ entity err = spawn(info_marker);
setmodel(err, MDL_MARKER);
setorigin(err,where);
err.movetype = MOVETYPE_NONE;
entity mark_misc(vector where,float lifetime)
{
- entity err;
-
- err = spawn();
- err.classname = "mark_misc";
+ entity err = spawn(mark_misc);
setmodel(err, MDL_MARKER);
setorigin(err,where);
err.movetype = MOVETYPE_NONE;
}
}
-// converts a number to a string with the indicated number of decimals
-// works for up to 10 decimals!
-string ftos_decimals(float number, float decimals)
-{
- // inhibit stupid negative zero
- if(number == 0)
- number = 0;
- // we have sprintf...
- return sprintf("%.*f", decimals, number);
-}
-
-// Databases (hash tables)
-const float DB_BUCKETS = 8192;
-void db_save(float db, string pFilename)
-{
- float fh, i, n;
- fh = fopen(pFilename, FILE_WRITE);
- if(fh < 0)
- {
- LOG_INFO(strcat("^1Can't write DB to ", pFilename));
- return;
- }
- n = buf_getsize(db);
- fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
- for(i = 0; i < n; ++i)
- fputs(fh, strcat(bufstr_get(db, i), "\n"));
- fclose(fh);
-}
-
-int db_create()
-{
- return buf_create();
-}
-
-int db_load(string pFilename)
-{
- float db, fh, i, j, n;
- string l;
- db = buf_create();
- if(db < 0)
- return -1;
- fh = fopen(pFilename, FILE_READ);
- if(fh < 0)
- return db;
- l = fgets(fh);
- if(stof(l) == DB_BUCKETS)
- {
- i = 0;
- while((l = fgets(fh)))
- {
- if(l != "")
- bufstr_set(db, i, l);
- ++i;
- }
- }
- else
- {
- // different count of buckets, or a dump?
- // need to reorganize the database then (SLOW)
- //
- // note: we also parse the first line (l) in case the DB file is
- // missing the bucket count
- do
- {
- n = tokenizebyseparator(l, "\\");
- for(j = 2; j < n; j += 2)
- db_put(db, argv(j-1), uri_unescape(argv(j)));
- }
- while((l = fgets(fh)));
- }
- fclose(fh);
- return db;
-}
-
-void db_dump(float db, string pFilename)
-{
- float fh, i, j, n, m;
- fh = fopen(pFilename, FILE_WRITE);
- if(fh < 0)
- error(strcat("Can't dump DB to ", pFilename));
- n = buf_getsize(db);
- fputs(fh, "0\n");
- for(i = 0; i < n; ++i)
- {
- m = tokenizebyseparator(bufstr_get(db, i), "\\");
- for(j = 2; j < m; j += 2)
- fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));
- }
- fclose(fh);
-}
-
-void db_close(float db)
-{
- buf_del(db);
-}
-
-string db_get(float db, string pKey)
-{
- float h;
- h = crc16(false, pKey) % DB_BUCKETS;
- return uri_unescape(infoget(bufstr_get(db, h), pKey));
-}
-
-void db_put(float db, string pKey, string pValue)
-{
- float h;
- h = crc16(false, pKey) % DB_BUCKETS;
- bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));
-}
-
-void db_test()
-{
- float db, i;
- LOG_INFO("LOAD...\n");
- db = db_load("foo.db");
- LOG_INFO("LOADED. FILL...\n");
- for(i = 0; i < DB_BUCKETS; ++i)
- db_put(db, ftos(random()), "X");
- LOG_INFO("FILLED. SAVE...\n");
- db_save(db, "foo.db");
- LOG_INFO("SAVED. CLOSE...\n");
- db_close(db);
- LOG_INFO("CLOSED.\n");
-}
-
-// Multiline text file buffers
-int buf_load(string pFilename)
-{
- float buf, fh, i;
- string l;
- buf = buf_create();
- if(buf < 0)
- return -1;
- fh = fopen(pFilename, FILE_READ);
- if(fh < 0)
- {
- buf_del(buf);
- return -1;
- }
- i = 0;
- while((l = fgets(fh)))
- {
- bufstr_set(buf, i, l);
- ++i;
- }
- fclose(fh);
- return buf;
-}
-
-void buf_save(float buf, string pFilename)
-{
- float fh, i, n;
- fh = fopen(pFilename, FILE_WRITE);
- if(fh < 0)
- error(strcat("Can't write buf to ", pFilename));
- n = buf_getsize(buf);
- for(i = 0; i < n; ++i)
- fputs(fh, strcat(bufstr_get(buf, i), "\n"));
- fclose(fh);
-}
-
-string format_time(float seconds)
-{
- float days, hours, minutes;
- seconds = floor(seconds + 0.5);
- days = floor(seconds / 864000);
- seconds -= days * 864000;
- hours = floor(seconds / 36000);
- seconds -= hours * 36000;
- minutes = floor(seconds / 600);
- seconds -= minutes * 600;
- if (days > 0)
- return sprintf(_("%d days, %02d:%02d:%02d"), days, hours, minutes, seconds);
- else
- return sprintf(_("%02d:%02d:%02d"), hours, minutes, seconds);
-}
-
-string mmsss(float tenths)
-{
- float minutes;
- string s;
- tenths = floor(tenths + 0.5);
- minutes = floor(tenths / 600);
- tenths -= minutes * 600;
- s = ftos(1000 + tenths);
- return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1));
-}
-
-string mmssss(float hundredths)
-{
- float minutes;
- string s;
- hundredths = floor(hundredths + 0.5);
- minutes = floor(hundredths / 6000);
- hundredths -= minutes * 6000;
- s = ftos(10000 + hundredths);
- return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2));
-}
-
string ScoreString(int pFlags, float pValue)
{
string valstr;
return order;
}
-float cvar_value_issafe(string s)
-{
- if(strstrofs(s, "\"", 0) >= 0)
- return 0;
- if(strstrofs(s, "\\", 0) >= 0)
- return 0;
- if(strstrofs(s, ";", 0) >= 0)
- return 0;
- if(strstrofs(s, "$", 0) >= 0)
- return 0;
- if(strstrofs(s, "\r", 0) >= 0)
- return 0;
- if(strstrofs(s, "\n", 0) >= 0)
- return 0;
- return 1;
-}
-
#ifndef MENUQC
void get_mi_min_max(float mode)
{
if(created_saved_value != -1)
{
// creating a new entity to keep track of this cvar
- e = spawn();
- e.classname = "saved_cvar_value";
+ e = new(saved_cvar_value);
+ make_pure(e);
e.netname = strzone(tmp_cvar);
e.message = strzone(cvar_string(tmp_cvar));
created_saved_value = 1;
return i;
}
-float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)
-{
- if(mi == ma)
- return 0;
- else if(ma == rgb.x)
- {
- if(rgb.y >= rgb.z)
- return (rgb.y - rgb.z) / (ma - mi);
- else
- return (rgb.y - rgb.z) / (ma - mi) + 6;
- }
- else if(ma == rgb.y)
- return (rgb.z - rgb.x) / (ma - mi) + 2;
- else // if(ma == rgb_z)
- return (rgb.x - rgb.y) / (ma - mi) + 4;
-}
-
-vector hue_mi_ma_to_rgb(float hue, float mi, float ma)
-{
- vector rgb;
-
- hue -= 6 * floor(hue / 6);
-
- //else if(ma == rgb_x)
- // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
- if(hue <= 1)
- {
- rgb.x = ma;
- rgb.y = hue * (ma - mi) + mi;
- rgb.z = mi;
- }
- //else if(ma == rgb_y)
- // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120;
- else if(hue <= 2)
- {
- rgb.x = (2 - hue) * (ma - mi) + mi;
- rgb.y = ma;
- rgb.z = mi;
- }
- else if(hue <= 3)
- {
- rgb.x = mi;
- rgb.y = ma;
- rgb.z = (hue - 2) * (ma - mi) + mi;
- }
- //else // if(ma == rgb_z)
- // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240;
- else if(hue <= 4)
- {
- rgb.x = mi;
- rgb.y = (4 - hue) * (ma - mi) + mi;
- rgb.z = ma;
- }
- else if(hue <= 5)
- {
- rgb.x = (hue - 4) * (ma - mi) + mi;
- rgb.y = mi;
- rgb.z = ma;
- }
- //else if(ma == rgb_x)
- // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
- else // if(hue <= 6)
- {
- rgb.x = ma;
- rgb.y = mi;
- rgb.z = (6 - hue) * (ma - mi) + mi;
- }
-
- return rgb;
-}
-
-vector rgb_to_hsv(vector rgb)
-{
- float mi, ma;
- vector hsv;
-
- mi = min(rgb.x, rgb.y, rgb.z);
- ma = max(rgb.x, rgb.y, rgb.z);
-
- hsv.x = rgb_mi_ma_to_hue(rgb, mi, ma);
- hsv.z = ma;
-
- if(ma == 0)
- hsv.y = 0;
- else
- hsv.y = 1 - mi/ma;
-
- return hsv;
-}
-
-vector hsv_to_rgb(vector hsv)
-{
- return hue_mi_ma_to_rgb(hsv.x, hsv.z * (1 - hsv.y), hsv.z);
-}
-
-vector rgb_to_hsl(vector rgb)
-{
- float mi, ma;
- vector hsl;
-
- mi = min(rgb.x, rgb.y, rgb.z);
- ma = max(rgb.x, rgb.y, rgb.z);
-
- hsl.x = rgb_mi_ma_to_hue(rgb, mi, ma);
-
- hsl.z = 0.5 * (mi + ma);
- if(mi == ma)
- hsl.y = 0;
- else if(hsl.z <= 0.5)
- hsl.y = (ma - mi) / (2*hsl.z);
- else // if(hsl_z > 0.5)
- hsl.y = (ma - mi) / (2 - 2*hsl.z);
-
- return hsl;
-}
-
-vector hsl_to_rgb(vector hsl)
-{
- float mi, ma, maminusmi;
-
- if(hsl.z <= 0.5)
- maminusmi = hsl.y * 2 * hsl.z;
- else
- maminusmi = hsl.y * (2 - 2 * hsl.z);
-
- // hsl_z = 0.5 * mi + 0.5 * ma
- // maminusmi = - mi + ma
- mi = hsl.z - 0.5 * maminusmi;
- ma = hsl.z + 0.5 * maminusmi;
-
- return hue_mi_ma_to_rgb(hsl.x, mi, ma);
-}
-
-string rgb_to_hexcolor(vector rgb)
-{
- return
- strcat(
- "^x",
- DEC_TO_HEXDIGIT(floor(rgb.x * 15 + 0.5)),
- DEC_TO_HEXDIGIT(floor(rgb.y * 15 + 0.5)),
- DEC_TO_HEXDIGIT(floor(rgb.z * 15 + 0.5))
- );
-}
-
float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
{
// STOP.
return 1;
}
-vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0
-{
- vector v;
- float D;
- v = '0 0 0';
- if(a == 0)
- {
- if(b != 0)
- {
- v.x = v.y = -c / b;
- v.z = 1;
- }
- else
- {
- if(c == 0)
- {
- // actually, every number solves the equation!
- v.z = 1;
- }
- }
- }
- else
- {
- D = b*b - 4*a*c;
- if(D >= 0)
- {
- D = sqrt(D);
- if(a > 0) // put the smaller solution first
- {
- v.x = ((-b)-D) / (2*a);
- v.y = ((-b)+D) / (2*a);
- }
- else
- {
- v.x = (-b+D) / (2*a);
- v.y = (-b-D) / (2*a);
- }
- v.z = 1;
- }
- else
- {
- // complex solutions!
- D = sqrt(-D);
- v.x = -b / (2*a);
- if(a > 0)
- v.y = D / (2*a);
- else
- v.y = -D / (2*a);
- v.z = 0;
- }
- }
- return v;
-}
-
vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)
{
vector ret;
return argv(n - 1);
}
-// from the GNU Scientific Library
-float gsl_ran_gaussian_lastvalue;
-float gsl_ran_gaussian_lastvalue_set;
-float gsl_ran_gaussian(float sigma)
-{
- float a, b;
- if(gsl_ran_gaussian_lastvalue_set)
- {
- gsl_ran_gaussian_lastvalue_set = 0;
- return sigma * gsl_ran_gaussian_lastvalue;
- }
- else
- {
- a = random() * 2 * M_PI;
- b = sqrt(-2 * log(random()));
- gsl_ran_gaussian_lastvalue = cos(a) * b;
- gsl_ran_gaussian_lastvalue_set = 1;
- return sigma * sin(a) * b;
- }
-}
-
float matchacl(string acl, string str)
{
string t, s;
return 1;
}
-float vercmp_recursive(string v1, string v2)
-{
- float dot1, dot2;
- string s1, s2;
- float r;
-
- dot1 = strstrofs(v1, ".", 0);
- dot2 = strstrofs(v2, ".", 0);
- if(dot1 == -1)
- s1 = v1;
- else
- s1 = substring(v1, 0, dot1);
- if(dot2 == -1)
- s2 = v2;
- else
- s2 = substring(v2, 0, dot2);
-
- r = stof(s1) - stof(s2);
- if(r != 0)
- return r;
-
- r = strcasecmp(s1, s2);
- if(r != 0)
- return r;
-
- if(dot1 == -1)
- if(dot2 == -1)
- return 0;
- else
- return -1;
- else
- if(dot2 == -1)
- return 1;
- else
- return vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999));
-}
-
-float vercmp(string v1, string v2)
-{
- if(strcasecmp(v1, v2) == 0) // early out check
- return 0;
-
- // "git" beats all
- if(v1 == "git")
- return 1;
- if(v2 == "git")
- return -1;
-
- return vercmp_recursive(v1, v2);
-}
-
// x-encoding (encoding as zero length invisible string)
const string XENCODE_2 = "xX";
const string XENCODE_22 = "0123456789abcdefABCDEF";
return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation);
}*/
-#ifdef CSQC
-entity ReadCSQCEntity()
-{
- int f = ReadShort();
- if(f == 0)
- return world;
- return findfloat(world, entnum, f);
-}
-#endif
-
float shutdown_running;
#ifdef SVQC
void SV_Shutdown()
cvar_settemp_restore(); // this must be done LAST, but in any case
}
-const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05;
-#define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
-#define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
-// this will use the value:
-// 128
-// accuracy near zero is APPROXPASTTIME_MAX/(256*255)
-// accuracy at x is 1/derivative, i.e.
-// APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536
-#ifdef SVQC
-void WriteApproxPastTime(float dst, float t)
-{
- float dt = time - t;
-
- // warning: this is approximate; do not resend when you don't have to!
- // be careful with sendflags here!
- // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75
-
- // map to range...
- dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt));
-
- // round...
- dt = rint(bound(0, dt, 255));
-
- WriteByte(dst, dt);
-}
-#endif
-#ifdef CSQC
-float ReadApproxPastTime()
-{
- float dt = ReadByte();
-
- // map from range...PPROXPASTTIME_MAX / 256
- dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt));
-
- return servertime - dt;
-}
-#endif
-
#ifndef MENUQC
.float skeleton_bones_index;
void Skeleton_SetBones(entity e)
float power2of(float e);
float log2of(float x);
-const string HEXDIGITS = "0123456789ABCDEF0123456789abcdef";
-#define HEXDIGIT_TO_DEC_RAW(d) (strstrofs(HEXDIGITS, (d), 0))
-#define HEXDIGIT_TO_DEC(d) ((HEXDIGIT_TO_DEC_RAW(d) | 0x10) - 0x10)
-#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS, (d), 1))
-
vector rgb_to_hsl(vector rgb);
vector hsl_to_rgb(vector hsl);
vector rgb_to_hsv(vector rgb);
float gsl_ran_gaussian(float sigma);
-string car(string s); // returns first word
-string cdr(string s); // returns all but first word
float matchacl(string acl, string str); // matches str against ACL acl (with entries +foo*, +foo, +*foo, +*foo*, and same with - for forbidding)
-float startsWith(string haystack, string needle);
-float startsWithNocase(string haystack, string needle);
string get_model_datafilename(string mod, float skn, string fil); // skin -1 will return wildcard, mod string_null will also put wildcard there
string get_model_parameters_modelname;
string xencode(float f);
float xdecode(string s);
-#ifdef CSQC
-entity ReadCSQCEntity();
-#endif
-
#ifndef MENUQC
string strtolower(string s);
#endif
#define normal_or_gentle(normal, gentle) (GENTLE ? ((gentle != "") ? gentle : normal) : normal)
#endif
-// allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example)
-#define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
-#define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
-#define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
-
vector vec3(float x, float y, float z);
#ifndef MENUQC
}
#endif
+REGISTER_NET_LINKED(ENT_CLIENT_AUXILIARYXHAIR)
+
#if defined(SVQC)
#include "sv_vehicles.qc"
#elif defined(CSQC)
#include "vehicle.qh"
-REGISTRY(Vehicles, BIT(3))
+REGISTRY(Vehicles, BITS(3))
+#define Vehicles_from(i) _Vehicles_from(i, VEH_Null)
+#define get_vehicleinfo(i) Vehicles_from(i)
REGISTER_REGISTRY(RegisterVehicles)
+REGISTRY_CHECK(Vehicles)
+
const int VEH_FIRST = 1;
#define VEH_LAST (Vehicles_COUNT - 1)
REGISTER_VEHICLE(Null, NEW(Vehicle));
-Vehicle get_vehicleinfo(int id)
-{
- if (id >= VEH_FIRST && id <= VEH_LAST) {
- Vehicle v = Vehicles[id];
- if (v) return v;
- }
- return VEH_Null;
-}
-
#include "all.inc"
#endif
self.draw2d = func_null;
}
-void Net_AuXair2(bool bIsNew)
+NET_HANDLE(ENT_CLIENT_AUXILIARYXHAIR, bool isnew)
{
int axh_id = bound(0, ReadByte(), MAX_AXH);
entity axh = AuxiliaryXhair[axh_id];
axh.colormod_z = ReadByte() / 255;
axh.cnt = time;
axh.draw2d = AuxiliaryXhair_Draw2D;
+ return true;
}
-void Net_VehicleSetup()
-{SELFPARAM();
+NET_HANDLE(TE_CSQC_VEHICLESETUP, bool isnew)
+{
int hud_id = ReadByte();
+ return = true;
// hud_id == 0 means we exited a vehicle, so stop alarm sound/s
if(hud_id == 0)
vector vehicleHud_Size;
vector vehicleHud_Pos;
-void Net_AuXair2(float bIsNew);
-
-void Net_VehicleSetup();
-
void RaptorCBShellfragDraw(entity this);
void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang);
bool SendAuxiliaryXhair(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR);
WriteByte(MSG_ENTITY, self.cnt);
msg_entity = own;
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_VEHICLESETUP);
+ WriteHeader(MSG_ONE, TE_CSQC_VEHICLESETUP);
WriteByte(MSG_ONE, vehicle_id);
}
void vehicles_clearreturn(entity veh)
{
- entity ret;
// Remove "return helper", if any.
- ret = findchain(classname, "vehicle_return");
- while(ret)
+ for (entity ret = findchain(classname, "vehicle_return"); ret; ret = ret.chain)
{
if(ret.wp00 == veh)
{
return;
}
- ret = ret.chain;
}
}
vehicles_clearreturn(veh);
- ret = spawn();
- ret.classname = "vehicle_return";
+ ret = new(vehicle_return);
ret.wp00 = veh;
ret.team = veh.team;
ret.think = vehicles_showwp;
float _ftmp;
_ftmp = self.owner.vehicle_health / 50;
self.pain_frame = time + 0.1 + (random() * 0.5 * _ftmp);
- pointparticles(particleeffectnum(EFFECT_SMOKE_SMALL), (self.origin + (randomvec() * 80)), '0 0 0', 1);
+ pointparticles(EFFECT_SMOKE_SMALL, (self.origin + (randomvec() * 80)), '0 0 0', 1);
if(self.vehicle_flags & VHF_DMGSHAKE)
self.velocity += randomvec() * 30;
self.vehicle_flags |= VHF_ISVEHICLE;
- self.vehicle_viewport = spawn();
- self.vehicle_hudmodel = spawn();
- self.tur_head = spawn();
+ self.vehicle_viewport = new(vehicle_viewport);
+ self.vehicle_hudmodel = new(vehicle_hudmodel);
+ self.tur_head = new(tur_head);
self.tur_head.owner = self;
self.takedamage = DAMAGE_NO;
self.bot_attack = true;
if(!forbidWeaponUse(gunner))
if(gunner.BUTTON_ATCK)
- if(time > gun.attack_finished_single)
+ if(time > gun.attack_finished_single[0])
if(gun.vehicle_energy >= autocvar_g_vehicle_bumblebee_cannon_cost)
{
gun.vehicle_energy -= autocvar_g_vehicle_bumblebee_cannon_cost;
bumblebee_fire_cannon(gun, "fire", gunner);
gun.delay = time;
- gun.attack_finished_single = time + autocvar_g_vehicle_bumblebee_cannon_refire;
+ gun.attack_finished_single[0] = time + autocvar_g_vehicle_bumblebee_cannon_refire;
}
VEHICLE_UPDATE_PLAYER(gunner, health, bumblebee);
self.vehicle_shieldent.alpha = -1;
self.vehicle_shieldent.effects = EF_LOWPRECISION | EF_NODRAW;
- self.gun1 = spawn();
- self.gun2 = spawn();
- self.gun3 = spawn();
+ self.gun1 = new(vehicle_playerslot);
+ self.gun2 = new(vehicle_playerslot);
+ self.gun3 = new(bumblebee_raygun);
self.vehicle_flags |= VHF_MULTISLOT;
self.gun2.owner = self;
self.gun3.owner = self;
- self.gun1.classname = self.gun2.classname = "vehicle_playerslot";
-
setmodel(self.gun1, MDL_VEH_BUMBLEBEE_CANNON_RIGHT);
setmodel(self.gun2, MDL_VEH_BUMBLEBEE_CANNON_LEFT);
setmodel(self.gun3, MDL_VEH_BUMBLEBEE_CANNON_CENTER);
#define BUMBLEBEE_H
#ifdef CSQC
-void bumble_raygun_read(bool bIsNew);
void CSQC_BUMBLE_GUN_HUD();
#endif
#ifdef IMPLEMENTATION
+REGISTER_NET_LINKED(ENT_CLIENT_BUMBLE_RAYGUN)
+
#ifdef SVQC
float autocvar_g_vehicle_bumblebee_cannon_cost;
bool bumble_raygun_send(entity this, entity to, float sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_BUMBLE_RAYGUN);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_BUMBLE_RAYGUN);
WriteByte(MSG_ENTITY, sf);
if(sf & BRG_SETUP)
void bumble_raygun_draw(entity this);
-void bumble_raygun_read(bool bIsNew)
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_BUMBLE_RAYGUN, bool isnew)
+{
int sf = ReadByte();
if(sf & BRG_SETUP)
self.move_origin_y = ReadCoord();
self.move_origin_z = ReadCoord();
}
+ return true;
}
+.float bumble_raygun_nextdraw;
void bumble_raygun_draw(entity this)
{
float _len;
_len = vlen(self.origin - self.move_origin);
_dir = normalize(self.move_origin - self.origin);
- if(self.total_damages < time)
+ if(self.bumble_raygun_nextdraw < time)
{
- boxparticles(particleeffectnum(Effects[self.traileffect]), self, self.origin, self.origin + _dir * -64, _dir * -_len , _dir * -_len, 1, PARTICLES_USEALPHA);
+ boxparticles(particleeffectnum(Effects_from(self.traileffect)), self, self.origin, self.origin + _dir * -64, _dir * -_len , _dir * -_len, 1, PARTICLES_USEALPHA);
boxparticles(self.lip, self, self.move_origin, self.move_origin + _dir * -64, _dir * -200 , _dir * -200, 1, PARTICLES_USEALPHA);
- self.total_damages = time + 0.1;
+ self.bumble_raygun_nextdraw = time + 0.1;
}
float i, df, sz, al;
self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta);
}
-void racer_fire_rocket_aim(string tagname, entity trg)
+void racer_fire_rocket_aim(entity player, string tagname, entity trg)
{
- SELFPARAM();
- vector v = gettaginfo(self, gettagindex(self, tagname));
- racer_fire_rocket(v, v_forward, trg);
+ entity racer = player.vehicle;
+ vector v = gettaginfo(racer, gettagindex(racer, tagname));
+ racer_fire_rocket(player, v, v_forward, trg);
}
float racer_frame()
{
#ifdef SVQC
if(time - racer.wait > 0.2)
- pointparticles(particleeffectnum(EFFECT_RACER_BOOSTER), self.origin - v_forward * 32, v_forward * vlen(self.velocity), 1);
+ pointparticles(EFFECT_RACER_BOOSTER, self.origin - v_forward * 32, v_forward * vlen(self.velocity), 1);
#endif
racer.wait = time;
{
traceline(racer.origin, racer.origin - '0 0 256', MOVE_NORMAL, self);
if(trace_fraction != 1.0)
- pointparticles(particleeffectnum(EFFECT_SMOKE_SMALL), trace_endpos, '0 0 0', 1);
+ pointparticles(EFFECT_SMOKE_SMALL, trace_endpos, '0 0 0', 1);
racer.invincible_finished = time + 0.1 + (random() * 0.1);
}
// Fix z-aim (for chase mode)
crosshair_trace(player);
w_shotdir.z = normalize(trace_endpos - org).z * 0.5;
- wep1.wr_think(wep1, self, true, false);
+ .entity weaponentity = weaponentities[0];
+ wep1.wr_think(wep1, self, weaponentity, 1);
}
if(autocvar_g_vehicle_racer_rocket_locktarget)
if(racer.misc_bulletcounter == 1)
{
- racer_fire_rocket_aim("tag_rocket_r", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
+ racer_fire_rocket_aim(player, "tag_rocket_r", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
player.vehicle_ammo2 = 50;
}
else if(racer.misc_bulletcounter == 2)
{
- racer_fire_rocket_aim("tag_rocket_l", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
+ racer_fire_rocket_aim(player, "tag_rocket_l", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
racer.lock_strength = 0;
racer.lock_target = world;
racer.misc_bulletcounter = 0;
/* flags */ ATTRIB(RacerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(RacerAttack, impulse, int, 3);
/* refname */ ATTRIB(RacerAttack, netname, string, "racercannon");
-/* wepname */ ATTRIB(RacerAttack, message, string, _("Racer cannon"));
+/* wepname */ ATTRIB(RacerAttack, m_name, string, _("Racer cannon"));
ENDCLASS(RacerAttack)
REGISTER_WEAPON(RACER, NEW(RacerAttack));
// TODO: move into implementation
#ifdef SVQC
float autocvar_g_vehicle_racer_rocket_refire;
-void racer_fire_rocket(vector org, vector dir, entity trg);
+void racer_fire_rocket(entity player, vector org, vector dir, entity trg);
#endif
#endif
float autocvar_g_vehicle_racer_rocket_climbspeed;
float autocvar_g_vehicle_racer_rocket_locked_maxangle;
-void racer_fire_rocket(vector org, vector dir, entity trg);
-METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+void racer_fire_rocket(entity player, vector org, vector dir, entity trg);
+METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
bool isPlayer = IS_PLAYER(actor);
entity player = isPlayer ? actor : actor.owner;
entity veh = player.vehicle;
- if (fire1)
- if (weapon_prepareattack(thiswep, player, false, autocvar_g_vehicle_racer_cannon_refire)) {
+ if (fire & 1)
+ if (weapon_prepareattack(thiswep, player, weaponentity, false, autocvar_g_vehicle_racer_cannon_refire)) {
if (veh) {
veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost;
veh.wait = time;
autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force, 0,
DEATH_VH_WAKI_GUN.m_id, PROJECTILE_WAKICANNON, 0, true, true, player);
bolt.velocity = normalize(dir) * autocvar_g_vehicle_racer_cannon_speed;
- weapon_thinkf(player, WFRAME_FIRE1, 0, w_ready);
+ weapon_thinkf(player, weaponentity, WFRAME_FIRE1, 0, w_ready);
}
- if (fire2)
- if (!isPlayer || weapon_prepareattack(thiswep, actor, false, 0.2)) {
+ if (fire & 2)
+ if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) {
if (isPlayer) W_SetupShot_Dir(actor, v_forward, false, 0, SND(Null), CH_WEAPON_B, 0);
- racer_fire_rocket(w_shotorg, w_shotdir, NULL);
- weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready);
+ racer_fire_rocket(player, w_shotorg, w_shotdir, NULL);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready);
}
}
void racer_rocket_tracker();
void racer_rocket_groundhugger();
-void racer_fire_rocket(vector org, vector dir, entity trg)
+void racer_fire_rocket(entity player, vector org, vector dir, entity trg)
{SELFPARAM();
entity rocket = vehicles_projectile(EFFECT_RACER_ROCKETLAUNCH.eent_eff_name, SND(ROCKET_FIRE),
org, dir * autocvar_g_vehicle_racer_rocket_speed,
autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3,
- DEATH_VH_WAKI_ROCKET.m_id, PROJECTILE_WAKIROCKET, 20, false, false, self.owner);
+ DEATH_VH_WAKI_ROCKET.m_id, PROJECTILE_WAKIROCKET, 20, false, false, player);
rocket.lip = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
rocket.wait = autocvar_g_vehicle_racer_rocket_turnrate;
if(player.BUTTON_ATCK)
if (wep1.wr_checkammo1(wep1))
{
- wep1.wr_think(wep1, self, true, false);
+ .entity weaponentity = weaponentities[0];
+ wep1.wr_think(wep1, self, weaponentity, 1);
}
if(self.vehicle_flags & VHF_SHIELDREGEN)
if(time > raptor.lip + autocvar_g_vehicle_raptor_bombs_refire)
if(player.BUTTON_ATCK2)
{
- wep2a.wr_think(wep2a, self, false, true);
+ .entity weaponentity = weaponentities[1];
+ wep2a.wr_think(wep2a, self, weaponentity, 2);
raptor.delay = time + autocvar_g_vehicle_raptor_bombs_refire;
raptor.lip = time;
}
if(time > raptor.lip + autocvar_g_vehicle_raptor_flare_refire)
if(player.BUTTON_ATCK2)
{
- wep2b.wr_think(wep2b, self, false, true);
+ .entity weaponentity = weaponentities[1];
+ wep2b.wr_think(wep2b, self, weaponentity, 2);
raptor.delay = time + autocvar_g_vehicle_raptor_flare_refire;
raptor.lip = time;
}
self.frame = 0;
- self.bomb1 = spawn();
- self.bomb2 = spawn();
- self.gun1 = spawn();
- self.gun2 = spawn();
+ self.bomb1 = new(raptor_bomb);
+ self.bomb2 = new(raptor_bomb);
+ self.gun1 = new(raptor_gun);
+ self.gun2 = new(raptor_gun);
setmodel(self.bomb1, MDL_VEH_RAPTOR_CB_FOLDED);
setmodel(self.bomb2, MDL_VEH_RAPTOR_CB_FOLDED);
self.angles = self.bomb1.angles;
self.bomb1.angles = '0 0 0';
- spinner = spawn();
+ spinner = new(raptor_spinner);
spinner.owner = self;
setmodel(spinner, MDL_VEH_RAPTOR_PROP);
setattachment(spinner, self, "engine_left");
spinner.avelocity = '0 90 0';
self.bomb1.gun1 = spinner;
- spinner = spawn();
+ spinner = new(raptor_spinner);
spinner.owner = self;
setmodel(spinner, MDL_VEH_RAPTOR_PROP);
setattachment(spinner, self, "engine_right");
/* flags */ ATTRIB(RaptorCannon, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(RaptorCannon, impulse, int, 3);
/* refname */ ATTRIB(RaptorCannon, netname, string, "raptorcannon");
-/* wepname */ ATTRIB(RaptorCannon, message, string, _("Raptor cannon"));
+/* wepname */ ATTRIB(RaptorCannon, m_name, string, _("Raptor cannon"));
ENDCLASS(RaptorCannon)
REGISTER_WEAPON(RAPTOR, NEW(RaptorCannon));
/* flags */ ATTRIB(RaptorBomb, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(RaptorBomb, impulse, int, 3);
/* refname */ ATTRIB(RaptorBomb, netname, string, "raptorbomb");
-/* wepname */ ATTRIB(RaptorBomb, message, string, _("Raptor bomb"));
+/* wepname */ ATTRIB(RaptorBomb, m_name, string, _("Raptor bomb"));
ENDCLASS(RaptorBomb)
REGISTER_WEAPON(RAPTOR_BOMB, NEW(RaptorBomb));
/* flags */ ATTRIB(RaptorFlare, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED);
/* impulse */ ATTRIB(RaptorFlare, impulse, int, 3);
/* refname */ ATTRIB(RaptorFlare, netname, string, "raptorflare");
-/* wepname */ ATTRIB(RaptorFlare, message, string, _("Raptor flare"));
+/* wepname */ ATTRIB(RaptorFlare, m_name, string, _("Raptor flare"));
ENDCLASS(RaptorFlare)
REGISTER_WEAPON(RAPTOR_FLARE, NEW(RaptorFlare));
float autocvar_g_vehicle_raptor_bomblet_force;
float autocvar_g_vehicle_raptor_bomblet_explode_delay;
-METHOD(RaptorCannon, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+METHOD(RaptorCannon, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
entity player = isPlayer ? actor : actor.owner;
entity veh = player.vehicle;
// 1 [wait] 1 [wait] 2 [wait] 2 [wait] [wait]
float t = autocvar_g_vehicle_raptor_cannon_refire * (1 + veh.misc_bulletcounter == 4);
- if (fire1)
- if (weapon_prepareattack(thiswep, player, false, t)) {
+ if (fire & 1)
+ if (weapon_prepareattack(thiswep, player, weaponentity, false, t)) {
if (isPlayer) W_SetupShot_Dir(player, v_forward, false, 0, SND(Null), CH_WEAPON_B, 0);
vector org = w_shotorg;
vector dir = w_shotdir;
org, normalize(dir + randomvec() * autocvar_g_vehicle_raptor_cannon_spread) * autocvar_g_vehicle_raptor_cannon_speed,
autocvar_g_vehicle_raptor_cannon_damage, autocvar_g_vehicle_raptor_cannon_radius, autocvar_g_vehicle_raptor_cannon_force, 0,
DEATH_VH_RAPT_CANNON.m_id, PROJECTILE_RAPTORCANNON, 0, true, true, player);
- weapon_thinkf(player, WFRAME_FIRE1, 0, w_ready);
+ weapon_thinkf(player, weaponentity, WFRAME_FIRE1, 0, w_ready);
}
}
METHOD(RaptorCannon, wr_checkammo1, bool(RacerAttack thiswep)) {
float autocvar_g_vehicle_raptor_bombs_refire;
void raptor_bombdrop();
-METHOD(RaptorBomb, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+METHOD(RaptorBomb, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
entity player = isPlayer ? actor : actor.owner;
entity veh = player.vehicle;
- if (fire2)
- if (!isPlayer || weapon_prepareattack(thiswep, player, true, autocvar_g_vehicle_raptor_bombs_refire)) {
+ if (fire & 2)
+ if (!isPlayer || weapon_prepareattack(thiswep, player, weaponentity, true, autocvar_g_vehicle_raptor_bombs_refire)) {
if (veh) setself(veh);
raptor_bombdrop();
- weapon_thinkf(player, WFRAME_FIRE2, 0, w_ready);
+ weapon_thinkf(player, weaponentity, WFRAME_FIRE2, 0, w_ready);
}
}
void raptor_flare_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
void raptor_flare_touch();
-METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) {
+METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) {
bool isPlayer = IS_PLAYER(actor);
entity player = isPlayer ? actor : actor.owner;
entity veh = player.vehicle;
- if (fire2)
- if (!isPlayer || weapon_prepareattack(thiswep, player, true, autocvar_g_vehicle_raptor_flare_refire)) {
+ if (fire & 2)
+ if (!isPlayer || weapon_prepareattack(thiswep, player, weaponentity, true, autocvar_g_vehicle_raptor_flare_refire)) {
for(int i = 0; i < 3; ++i) {
entity _flare = spawn();
setmodel(_flare, MDL_VEH_RAPTOR_FLARE);
_flare.tur_impacttime = time + autocvar_g_vehicle_raptor_flare_lifetime;
_flare.touch = raptor_flare_touch;
}
- weapon_thinkf(player, WFRAME_FIRE2, 0, w_ready);
+ weapon_thinkf(player, weaponentity, WFRAME_FIRE2, 0, w_ready);
}
}
if(player.BUTTON_ATCK)
{
spider.cnt = time;
- if(spider.vehicle_ammo1 >= autocvar_g_vehicle_spiderbot_minigun_ammo_cost && spider.tur_head.attack_finished_single <= time)
+ if(spider.vehicle_ammo1 >= autocvar_g_vehicle_spiderbot_minigun_ammo_cost && spider.tur_head.attack_finished_single[0] <= time)
{
entity gun;
vector v;
sound (gun, CH_WEAPON_A, SND_UZI_FIRE, VOL_BASE, ATTEN_NORM);
//trailparticles(self, _particleeffectnum("spiderbot_minigun_trail"), v, trace_endpos);
- pointparticles(particleeffectnum(EFFECT_SPIDERBOT_MINIGUN_MUZZLEFLASH), v, v_forward * 2500, 1);
+ pointparticles(EFFECT_SPIDERBOT_MINIGUN_MUZZLEFLASH, v, v_forward * 2500, 1);
setself(spider);
spider.vehicle_ammo1 -= autocvar_g_vehicle_spiderbot_minigun_ammo_cost;
- spider.tur_head.attack_finished_single = time + autocvar_g_vehicle_spiderbot_minigun_refire;
+ spider.tur_head.attack_finished_single[0] = time + autocvar_g_vehicle_spiderbot_minigun_refire;
player.vehicle_ammo1 = (spider.vehicle_ammo1 / autocvar_g_vehicle_spiderbot_minigun_ammo_max) * 100;
spider.gun1.angles_z += 45;
spider.gun2.angles_z -= 45;
if(spider.gun2.cnt <= time)
player.vehicle_reload2 = 100;
else
- player.vehicle_reload2 = 100 - ((spider.gun2.cnt - time) / spider.attack_finished_single) * 100;
+ player.vehicle_reload2 = 100 - ((spider.gun2.cnt - time) / spider.attack_finished_single[0]) * 100;
setorigin(player, spider.origin + '0 0 1' * spider.maxs_z);
player.velocity = spider.velocity;
self.tur_head.frame += 1;
if (self.tur_head.frame == 9)
- self.attack_finished_single = autocvar_g_vehicle_spiderbot_rocket_reload;
+ self.attack_finished_single[0] = autocvar_g_vehicle_spiderbot_rocket_reload;
else
- self.attack_finished_single = ((self.vehicle_weapon2mode == SBRM_VOLLY) ? autocvar_g_vehicle_spiderbot_rocket_refire2 : autocvar_g_vehicle_spiderbot_rocket_refire);
+ self.attack_finished_single[0] = ((self.vehicle_weapon2mode == SBRM_VOLLY) ? autocvar_g_vehicle_spiderbot_rocket_refire2 : autocvar_g_vehicle_spiderbot_rocket_refire);
- self.gun2.cnt = time + self.attack_finished_single;
+ self.gun2.cnt = time + self.attack_finished_single[0];
}
#endif
#include "weapon/seeker.qc"
#include "weapon/shockwave.qc"
#include "weapon/arc.qc"
-#include "weapon/hmg.qc"
-#include "weapon/rpc.qc"
string W_Sound(string w_snd)
{
- #define extensions(X) X(wav) X(ogg)
- #define tryext(ext) { if (fexists(strcat("sound/", output = strcat("weapons/", w_snd, "."#ext)))) break; }
- string output;
- do {
- extensions(tryext);
- #undef tryext
- #undef extensions
- output = strcat("weapons/", w_snd);
- } while (0);
-
+ string output = strcat("weapons/", w_snd);
#ifdef SVQC
MUTATOR_CALLHOOK(WeaponSound, w_snd, output);
return weapon_sound_output;
#define WEAPONS_ALL_H
#include "../command/all.qh"
+#include "../stats.qh"
#include "config.qh"
// weapon sets
#endif
REGISTRY(Weapons, 72) // Increase as needed. Can be up to 72.
+#define Weapons_from(i) _Weapons_from(i, WEP_Null)
+#define get_weaponinfo(i) Weapons_from(i)
REGISTER_REGISTRY(RegisterWeapons)
-entity get_weaponinfo(int id);
+STATIC_INIT(WeaponPickup) { FOREACH(Weapons, true, LAMBDA(it.m_pickup = NEW(WeaponPickup, it))); }
GENERIC_COMMAND(dumpweapons, "Dump all weapons into weapons_dump.txt") // WEAPONTODO: make this work with other progs than just server
#include "all.inc"
-entity get_weaponinfo(int id)
-{
- if (id >= WEP_FIRST && id <= WEP_LAST) {
- Weapon w = Weapons[id];
- if (w) return w;
- }
- return WEP_Null;
-}
-
// TODO: remove after 0.8.2. Retains impulse number compatibility because 0.8.1 clients don't reload the weapons.cfg
-#define WEP_HARDCODED_IMPULSES 22
+#define WEP_HARDCODED_IMPULSES 20
// TODO: invert after 0.8.2. Will require moving 'best weapon' impulses
#define WEP_IMPULSE_BEGIN 230
#define WEP_IMPULSE_END bound(WEP_IMPULSE_BEGIN, WEP_IMPULSE_BEGIN + (Weapons_COUNT - 1) - 1, 253)
-REGISTRY_SORT(Weapons, netname, WEP_HARDCODED_IMPULSES + 1)
+REGISTRY_SORT(Weapons, WEP_HARDCODED_IMPULSES + 1)
+REGISTRY_CHECK(Weapons)
STATIC_INIT(register_weapons_done)
{
#endif
weaponorder_byid = "";
for (int i = Weapons_MAX - 1; i >= 1; --i)
- if (Weapons[i])
+ if (Weapons_from(i))
weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i));
weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1));
}
return strcmp(wep_config_queue[root], wep_config_queue[child]);
}
-void Dump_Weapon_Settings(void)
+void Dump_Weapon_Settings()
{
int i, x, totalsettings = 0;
for(i = WEP_FIRST; i <= WEP_LAST; ++i)
// Balance Config Generator
// ==========================
-void Dump_Weapon_Settings(void);
+void Dump_Weapon_Settings();
int wep_config_file;
bool wep_config_alsoprint;
#ifndef WEAPON_H
#define WEAPON_H
+#include "../items/item/pickup.qh"
+
+const int MAX_WEAPONSLOTS = 2;
+.entity weaponentities[MAX_WEAPONSLOTS];
+
+int weaponslot(.entity weaponentity)
+{
+ for (int i = 0; i < MAX_WEAPONSLOTS; ++i)
+ {
+ if (weaponentities[i] == weaponentity)
+ {
+ return i;
+ }
+ }
+ return 0;
+}
.int ammo_shells;
.int ammo_nails;
/** M: refname : reference name name */
ATTRIB(Weapon, netname, string, "");
/** M: wepname : human readable name */
- ATTRIB(Weapon, message, string, "AOL CD Thrower");
+ ATTRIB(Weapon, m_name, string, "AOL CD Thrower");
+
+ ATTRIB(Weapon, m_pickup, entity, NULL);
/** (SERVER) setup weapon data */
METHOD(Weapon, wr_setup, void(Weapon this)) {}
/** (SERVER) logic to run every frame */
- METHOD(Weapon, wr_think, void(Weapon this, entity actor, bool fire1, bool fire2)) {}
+ METHOD(Weapon, wr_think, void(Weapon this, entity actor, .entity weaponentity, int fire)) {}
/** (SERVER) checks ammo for weapon primary */
METHOD(Weapon, wr_checkammo1, bool(Weapon this)) {return false;}
/** (SERVER) checks ammo for weapon second */
METHOD(Weapon, wr_pickup, void(Weapon this)) {}
METHOD(Weapon, display, void(entity this, void(string name, string icon) returns)) {
- returns(this.message, this.model2 ? sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.model2) : string_null);
+ returns(this.m_name, this.model2 ? sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.model2) : string_null);
}
ENDCLASS(Weapon)
+#include "../items/all.qh"
+CLASS(WeaponPickup, Pickup)
+ ATTRIB(WeaponPickup, m_weapon, Weapon, NULL)
+ ATTRIB(WeaponPickup, m_name, string, string_null)
+#ifndef MENUQC
+ ATTRIB(WeaponPickup, m_sound, Sound, SND_WEAPONPICKUP)
+#endif
+#ifdef SVQC
+ ATTRIB(WeaponPickup, m_itemflags, int, FL_WEAPON)
+ float weapon_pickupevalfunc(entity player, entity item);
+ ATTRIB(WeaponPickup, m_pickupevalfunc, float(entity player, entity item), weapon_pickupevalfunc)
+#endif
+ CONSTRUCTOR(WeaponPickup, Weapon w) {
+ CONSTRUCT(WeaponPickup);
+ this.m_weapon = w;
+ this.m_name = w.m_name;
+#ifndef MENUQC
+ this.m_model = w.m_model;
+#endif
+#ifdef SVQC
+ this.m_botvalue = w.bot_pickupbasevalue;
+#endif
+ }
+#ifdef SVQC
+ METHOD(WeaponPickup, giveTo, bool(entity this, entity item, entity player))
+ {
+ bool b = Item_GiveTo(item, player);
+ if (b) {
+ LOG_TRACEF("entity %i picked up %s\n", player, this.m_name);
+ }
+ return b;
+ }
+#endif
+ENDCLASS(WeaponPickup)
+
CLASS(OffhandWeapon, Object)
METHOD(OffhandWeapon, offhand_think, void(OffhandWeapon this, entity player, bool key_pressed)) {}
ENDCLASS(OffhandWeapon)
// other useful macros
#define WEP_AMMO(wpn) (WEP_##wpn.ammo_field) // only used inside weapon files/with direct name, don't duplicate prefix
-#define WEP_NAME(wpn) ((get_weaponinfo(wpn)).message)
+#define WEP_NAME(wpn) ((get_weaponinfo(wpn)).m_name)
#endif
/* crosshair */ ATTRIB(Arc, w_crosshair_size, float, 0.7);
/* wepimg */ ATTRIB(Arc, model2, string, "weaponarc");
/* refname */ ATTRIB(Arc, netname, string, "arc");
-/* wepname */ ATTRIB(Arc, message, string, _("Arc"));
+/* wepname */ ATTRIB(Arc, m_name, string, _("Arc"));
ENDCLASS(Arc)
REGISTER_WEAPON(ARC, NEW(Arc));
.float beam_heat; // (beam) amount of heat produced
.float arc_overheat; // (dropped arc/player) time during which it's too hot
.float arc_cooldown; // (dropped arc/player) cooling speed
-.float arc_heat_percent; // (player) arc heat in [0,1] (stat)
+.float arc_heat_percent = _STAT(ARC_HEAT);
.float arc_smoke_sound;
#endif
#ifdef CSQC
-void Ent_ReadArcBeam(float isnew);
.vector beam_color;
.float beam_alpha;
.float beam_thickness;
-.float beam_traileffect;
-.float beam_hiteffect;
+.entity beam_traileffect;
+.entity beam_hiteffect;
.float beam_hitlight[4]; // 0: radius, 123: rgb
-.float beam_muzzleeffect;
+.entity beam_muzzleeffect;
.float beam_muzzlelight[4]; // 0: radius, 123: rgb
.string beam_image;
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_arc) { weapon_defaultspawnfunc(WEP_ARC.m_id); }
+spawnfunc(weapon_arc) { weapon_defaultspawnfunc(this, WEP_ARC); }
bool W_Arc_Beam_Send(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_ARC_BEAM);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_ARC_BEAM);
// Truncate information when this beam is displayed to the owner client
// - The owner client has no use for beam start position or directions,
//dprint("Heat: ",ftos(player.arc_heat_percent*100),"%\n");
}
-void W_Arc_Beam_Think(void)
+void W_Arc_Beam_Think()
{SELFPARAM();
if(self != self.owner.arc_beam)
{
if ( WEP_CVAR(arc, overheat_max) > 0 && self.beam_heat >= WEP_CVAR(arc, overheat_max) )
{
- Send_Effect_("arc_overheat",
+ Send_Effect(EFFECT_ARC_OVERHEAT,
self.beam_start, self.beam_wantdir, 1 );
sound(self, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM);
}
if(time - self.beam_prev > 1)
sound(self, CH_WEAPON_A, SND_ARC_FIRE, VOL_BASE, ATTN_NORM);
- entity beam = self.arc_beam = spawn();
- beam.classname = "W_Arc_Beam";
+ entity beam = self.arc_beam = new(W_Arc_Beam);
beam.solid = SOLID_NOT;
beam.think = W_Arc_Beam_Think;
beam.owner = self;
if ( self.arc_overheat > time )
{
if ( random() < self.arc_heat_percent )
- Send_Effect_("arc_smoke", smoke_origin, '0 0 0', 1 );
+ Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 );
if ( self.BUTTON_ATCK || self.BUTTON_ATCK2 )
{
- Send_Effect_("arc_overheat_fire", smoke_origin, w_shotdir, 1 );
+ Send_Effect(EFFECT_ARC_OVERHEAT_FIRE, smoke_origin, w_shotdir, 1 );
if ( !self.arc_smoke_sound )
{
self.arc_smoke_sound = 1;
{
if ( random() < (self.arc_beam.beam_heat-WEP_CVAR(arc, overheat_min)) /
( WEP_CVAR(arc, overheat_max)-WEP_CVAR(arc, overheat_min) ) )
- Send_Effect_("arc_smoke", smoke_origin, '0 0 0', 1 );
+ Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 );
}
if ( self.arc_smoke_sound && ( self.arc_overheat <= time ||
);
}
}
- METHOD(Arc, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
Arc_Player_SetHeat(actor);
Arc_Smoke();
if (time >= actor.arc_overheat)
- if (fire1 || fire2 || actor.arc_beam.beam_bursting)
+ if ((fire & 1) || (fire & 2) || actor.arc_beam.beam_bursting)
{
if(actor.arc_BUTTON_ATCK_prev)
{
#if 0
if(actor.animstate_startframe == actor.anim_shoot.x && actor.animstate_numframes == actor.anim_shoot.y)
- weapon_thinkf(actor, WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready);
else
#endif
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
}
if((!actor.arc_beam) || wasfreed(actor.arc_beam))
{
- if(weapon_prepareattack(thiswep, actor, fire2, 0))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, boolean(fire & 2), 0))
{
- W_Arc_Beam(fire2);
+ W_Arc_Beam(boolean(fire & 2));
if(!actor.arc_BUTTON_ATCK_prev)
{
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
actor.arc_BUTTON_ATCK_prev = true;
}
}
if(actor.arc_BUTTON_ATCK_prev)
{
sound(actor, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
- ATTACK_FINISHED(actor) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor();
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
+ int slot = weaponslot(weaponentity);
+ ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor();
}
actor.arc_BUTTON_ATCK_prev = false;
#if 0
- if(fire2)
- if(weapon_prepareattack(thiswep, actor, true, autocvar_g_balance_arc_secondary_refire))
+ if(fire & 2)
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, autocvar_g_balance_arc_secondary_refire))
{
W_Arc_Attack2();
actor.arc_count = autocvar_g_balance_arc_secondary_count;
- weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack);
actor.arc_secondarytime = time + autocvar_g_balance_arc_secondary_refire2 * W_WeaponRateFactor();
}
#endif
Draw_ArcBeam_callback_last_bottom = WarpZone_UnTransformOrigin(WarpZone_trace_transform, bottom);
}
-void Reset_ArcBeam(void)
+void Reset_ArcBeam()
{
entity e;
for (e = world; (e = findfloat(e, beam_usevieworigin, 1)); ) {
Draw_ArcBeam_callback_last_bottom = '0 0 0';
}
-void Remove_ArcBeam(void)
+void Remove_ArcBeam()
{SELFPARAM();
remove(self.beam_muzzleentity);
sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
}
-void Ent_ReadArcBeam(float isnew)
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew)
+{
int sf = ReadByte();
entity flash;
self.beam_color = '1 1 1';
self.beam_alpha = 0.5;
self.beam_thickness = 8;
- self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM);
- self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING);
+ self.beam_traileffect = (EFFECT_ARC_BEAM);
+ self.beam_hiteffect = (EFFECT_ARC_LIGHTNING);
self.beam_hitlight[0] = 0;
self.beam_hitlight[1] = 1;
self.beam_hitlight[2] = 1;
self.beam_hitlight[3] = 1;
- self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH);
+ self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH);
self.beam_muzzlelight[0] = 0;
self.beam_muzzlelight[1] = 1;
self.beam_muzzlelight[2] = 1;
self.beam_muzzlelight[3] = 1;
- if(self.beam_muzzleeffect >= 0)
+ if(self.beam_muzzleeffect)
{
setmodel(flash, MDL_ARC_MUZZLEFLASH);
flash.alpha = self.beam_alpha;
self.beam_color = '1 1 1';
self.beam_alpha = 0.5;
self.beam_thickness = 8;
- self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM);
- self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING);
+ self.beam_traileffect = (EFFECT_ARC_BEAM);
+ self.beam_hiteffect = (EFFECT_ARC_LIGHTNING);
self.beam_hitlight[0] = 0;
self.beam_hitlight[1] = 1;
self.beam_hitlight[2] = 1;
self.beam_hitlight[3] = 1;
- self.beam_muzzleeffect = -1; // particleeffectnum(EFFECT_GRENADE_MUZZLEFLASH);
+ self.beam_muzzleeffect = NULL; // (EFFECT_GRENADE_MUZZLEFLASH);
self.beam_muzzlelight[0] = 0;
self.beam_muzzlelight[1] = 1;
self.beam_muzzlelight[2] = 1;
self.beam_muzzlelight[3] = 1;
self.beam_image = "particles/lgbeam";
- if(self.beam_muzzleeffect >= 0)
+ if(self.beam_muzzleeffect)
{
setmodel(flash, MDL_ARC_MUZZLEFLASH);
flash.alpha = self.beam_alpha;
self.beam_color = '1 1 1';
self.beam_alpha = 0.5;
self.beam_thickness = 8;
- self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM_HEAL);
- self.beam_hiteffect = particleeffectnum(EFFECT_ARC_BEAM_HEAL_IMPACT);
+ self.beam_traileffect = (EFFECT_ARC_BEAM_HEAL);
+ self.beam_hiteffect = (EFFECT_ARC_BEAM_HEAL_IMPACT);
self.beam_hitlight[0] = 0;
self.beam_hitlight[1] = 1;
self.beam_hitlight[2] = 1;
self.beam_hitlight[3] = 1;
- self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH);
+ self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH);
self.beam_muzzlelight[0] = 0;
self.beam_muzzlelight[1] = 1;
self.beam_muzzlelight[2] = 1;
self.beam_muzzlelight[3] = 1;
self.beam_image = "particles/lgbeam";
- if(self.beam_muzzleeffect >= 0)
+ if(self.beam_muzzleeffect)
{
setmodel(flash, MDL_ARC_MUZZLEFLASH);
flash.alpha = self.beam_alpha;
self.beam_color = '1 1 1';
self.beam_alpha = 0.5;
self.beam_thickness = 8;
- self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM);
- self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING);
+ self.beam_traileffect = (EFFECT_ARC_BEAM);
+ self.beam_hiteffect = (EFFECT_ARC_LIGHTNING);
self.beam_hitlight[0] = 20;
self.beam_hitlight[1] = 1;
self.beam_hitlight[2] = 0;
self.beam_hitlight[3] = 0;
- self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH);
+ self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH);
self.beam_muzzlelight[0] = 50;
self.beam_muzzlelight[1] = 1;
self.beam_muzzlelight[2] = 0;
self.beam_muzzlelight[3] = 0;
self.beam_image = "particles/lgbeam";
- if(self.beam_muzzleeffect >= 0)
+ if(self.beam_muzzleeffect)
{
setmodel(flash, MDL_ARC_MUZZLEFLASH);
flash.alpha = self.beam_alpha;
self.beam_color = '1 1 1';
self.beam_alpha = 0.5;
self.beam_thickness = 14;
- self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM);
- self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING);
+ self.beam_traileffect = (EFFECT_ARC_BEAM);
+ self.beam_hiteffect = (EFFECT_ARC_LIGHTNING);
self.beam_hitlight[0] = 0;
self.beam_hitlight[1] = 1;
self.beam_hitlight[2] = 1;
self.beam_hitlight[3] = 1;
- self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH);
+ self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH);
self.beam_muzzlelight[0] = 0;
self.beam_muzzlelight[1] = 1;
self.beam_muzzlelight[2] = 1;
self.beam_muzzlelight[3] = 1;
self.beam_image = "particles/lgbeam";
- if(self.beam_muzzleeffect >= 0)
+ if(self.beam_muzzleeffect)
{
setmodel(flash, MDL_ARC_MUZZLEFLASH);
flash.alpha = self.beam_alpha;
self.beam_color = '1 1 1';
self.beam_alpha = 0.5;
self.beam_thickness = 14;
- self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM);
- self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING);
+ self.beam_traileffect = (EFFECT_ARC_BEAM);
+ self.beam_hiteffect = (EFFECT_ARC_LIGHTNING);
self.beam_hitlight[0] = 0;
self.beam_hitlight[1] = 1;
self.beam_hitlight[2] = 1;
self.beam_hitlight[3] = 1;
- self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH);
+ self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH);
self.beam_muzzlelight[0] = 0;
self.beam_muzzlelight[1] = 1;
self.beam_muzzlelight[2] = 1;
self.beam_muzzlelight[3] = 1;
self.beam_image = "particles/lgbeam";
- if(self.beam_muzzleeffect >= 0)
+ if(self.beam_muzzleeffect)
{
setmodel(flash, MDL_ARC_MUZZLEFLASH);
flash.alpha = self.beam_alpha;
self.beam_color = '1 1 1';
self.beam_alpha = 0.5;
self.beam_thickness = 14;
- self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM_HEAL);
- self.beam_hiteffect = particleeffectnum(EFFECT_ARC_BEAM_HEAL_IMPACT2);
+ self.beam_traileffect = (EFFECT_ARC_BEAM_HEAL);
+ self.beam_hiteffect = (EFFECT_ARC_BEAM_HEAL_IMPACT2);
self.beam_hitlight[0] = 0;
self.beam_hitlight[1] = 1;
self.beam_hitlight[2] = 1;
self.beam_hitlight[3] = 1;
- self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH);
+ self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH);
self.beam_muzzlelight[0] = 0;
self.beam_muzzlelight[1] = 1;
self.beam_muzzlelight[2] = 1;
self.beam_muzzlelight[3] = 1;
self.beam_image = "particles/lgbeam";
- if(self.beam_muzzleeffect >= 0)
+ if(self.beam_muzzleeffect)
{
setmodel(flash, MDL_ARC_MUZZLEFLASH);
flash.alpha = self.beam_alpha;
self.beam_color = '1 1 1';
self.beam_alpha = 0.5;
self.beam_thickness = 14;
- self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM);
- self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING);
+ self.beam_traileffect = (EFFECT_ARC_BEAM);
+ self.beam_hiteffect = (EFFECT_ARC_LIGHTNING);
self.beam_hitlight[0] = 0;
self.beam_hitlight[1] = 1;
self.beam_hitlight[2] = 1;
self.beam_hitlight[3] = 1;
- self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH);
+ self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH);
self.beam_muzzlelight[0] = 0;
self.beam_muzzlelight[1] = 1;
self.beam_muzzlelight[2] = 1;
self.beam_muzzlelight[3] = 1;
self.beam_image = "particles/lgbeam";
- if(self.beam_muzzleeffect >= 0)
+ if(self.beam_muzzleeffect)
{
setmodel(flash, MDL_ARC_MUZZLEFLASH);
flash.alpha = self.beam_alpha;
self.beam_color = randomvec();
self.beam_alpha = 1;
self.beam_thickness = 8;
- self.beam_traileffect = false;
- self.beam_hiteffect = false;
+ self.beam_traileffect = NULL;
+ self.beam_hiteffect = NULL;
self.beam_hitlight[0] = 0;
self.beam_hitlight[1] = 1;
self.beam_hitlight[2] = 1;
self.beam_hitlight[3] = 1;
- self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH);
+ self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH);
self.beam_muzzlelight[0] = 0;
self.beam_muzzlelight[1] = 1;
self.beam_muzzlelight[2] = 1;
self.beam_muzzlelight[3] = 1;
self.beam_image = "particles/lgbeam";
- if(self.beam_muzzleeffect >= 0)
+ if(self.beam_muzzleeffect)
{
setmodel(flash, MDL_ARC_MUZZLEFLASH);
flash.alpha = self.beam_alpha;
{
InterpolateOrigin_Note();
}
+ return true;
}
#endif
/* crosshair */ ATTRIB(Blaster, w_crosshair_size, float, 0.5);
/* wepimg */ ATTRIB(Blaster, model2, string, "weaponlaser");
/* refname */ ATTRIB(Blaster, netname, string, "blaster");
-/* wepname */ ATTRIB(Blaster, message, string, _("Blaster"));
+/* wepname */ ATTRIB(Blaster, m_name, string, _("Blaster"));
ENDCLASS(Blaster)
REGISTER_WEAPON(BLASTER, NEW(Blaster));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_blaster) { weapon_defaultspawnfunc(WEP_BLASTER.m_id); }
+spawnfunc(weapon_blaster) { weapon_defaultspawnfunc(this, WEP_BLASTER); }
spawnfunc(weapon_laser) { spawnfunc_weapon_blaster(this); }
-void W_Blaster_Touch(void)
+void W_Blaster_Touch()
{SELFPARAM();
PROJECTILE_TOUCH;
remove(self);
}
-void W_Blaster_Think(void)
+void W_Blaster_Think()
{SELFPARAM();
self.movetype = MOVETYPE_FLY;
self.think = SUB_Remove;
W_SetupShot_Dir(actor, s_forward, false, 3, SND(LASERGUN_FIRE), CH_WEAPON_B, atk_damage);
Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- entity missile = spawn();
+ entity missile = new(blasterbolt);
missile.owner = missile.realowner = actor;
- missile.classname = "blasterbolt";
missile.bot_dodge = true;
missile.bot_dodgerating = atk_damage;
PROJECTILE_MAKETRIGGER(missile);
{ self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), false); }
}
- METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, .entity weaponentity, int fire))
{
- if(fire1)
+ if(fire & 1)
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(blaster, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(blaster, refire)))
{
W_Blaster_Attack(
actor,
WEP_CVAR_PRI(blaster, delay),
WEP_CVAR_PRI(blaster, lifetime)
);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready);
}
}
- else if(fire2)
+ else if(fire & 2)
{
switch(WEP_CVAR(blaster, secondary))
{
case 1: // normal projectile secondary
{
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(blaster, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(blaster, refire)))
{
W_Blaster_Attack(
actor,
WEP_CVAR_SEC(blaster, delay),
WEP_CVAR_SEC(blaster, lifetime)
);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready);
}
break;
{
vector org2;
org2 = w_org + w_backoff * 6;
- pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1);
+ pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1);
if(!w_issilent) { sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); }
}
/* crosshair */ ATTRIB(Crylink, w_crosshair_size, float, 0.5);
/* wepimg */ ATTRIB(Crylink, model2, string, "weaponcrylink");
/* refname */ ATTRIB(Crylink, netname, string, "crylink");
-/* wepname */ ATTRIB(Crylink, message, string, _("Crylink"));
+/* wepname */ ATTRIB(Crylink, m_name, string, _("Crylink"));
ENDCLASS(Crylink)
REGISTER_WEAPON(CRYLINK, NEW(Crylink));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_crylink) { weapon_defaultspawnfunc(WEP_CRYLINK.m_id); }
+spawnfunc(weapon_crylink) { weapon_defaultspawnfunc(this, WEP_CRYLINK); }
void W_Crylink_CheckLinks(entity e)
{
W_Crylink_Dequeue_Raw(e.realowner, e.queueprev, e, e.queuenext);
}
-void W_Crylink_Reset(void)
+void W_Crylink_Reset()
{SELFPARAM();
W_Crylink_Dequeue(self);
remove(self);
return targ_origin;
}
-void W_Crylink_LinkJoinEffect_Think(void)
+void W_Crylink_LinkJoinEffect_Think()
{SELFPARAM();
// is there at least 2 projectiles very close?
entity e, p;
}
// NO bounce protection, as bounces are limited!
-void W_Crylink_Touch(void)
+void W_Crylink_Touch()
{SELFPARAM();
float finalhit;
float f;
// CSQCProjectile(proj, true, PROJECTILE_CRYLINK, true);
}
-void W_Crylink_Fadethink(void)
+void W_Crylink_Fadethink()
{SELFPARAM();
W_Crylink_Dequeue(self);
remove(self);
proj = prevproj = firstproj = world;
for(counter = 0; counter < shots; ++counter)
{
- proj = spawn();
+ proj = new(spike);
proj.reset = W_Crylink_Reset;
proj.realowner = proj.owner = self;
- proj.classname = "spike";
proj.bot_dodge = true;
proj.bot_dodgerating = WEP_CVAR_PRI(crylink, damage);
if(shots == 1) {
proj = prevproj = firstproj = world;
for(counter = 0; counter < shots; ++counter)
{
- proj = spawn();
+ proj = new(spike);
proj.reset = W_Crylink_Reset;
proj.realowner = proj.owner = self;
- proj.classname = "spike";
proj.bot_dodge = true;
proj.bot_dodgerating = WEP_CVAR_SEC(crylink, damage);
if(shots == 1) {
else
self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(crylink, speed), 0, WEP_CVAR_SEC(crylink, middle_lifetime), false);
}
- METHOD(Crylink, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(autocvar_g_balance_crylink_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo))) { // forced reload
Weapon w = get_weaponinfo(actor.weapon);
w.wr_reload(w);
}
- if(fire1)
+ if(fire & 1)
{
if(actor.crylink_waitrelease != 1)
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(crylink, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(crylink, refire)))
{
W_Crylink_Attack(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready);
}
}
- if(fire2 && autocvar_g_balance_crylink_secondary)
+ if((fire & 2) && autocvar_g_balance_crylink_secondary)
{
if(actor.crylink_waitrelease != 2)
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(crylink, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(crylink, refire)))
{
W_Crylink_Attack2(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready);
}
}
- if((actor.crylink_waitrelease == 1 && !fire1) || (actor.crylink_waitrelease == 2 && !fire2))
+ if((actor.crylink_waitrelease == 1 && !(fire & 1)) || (actor.crylink_waitrelease == 2 && !(fire & 2)))
{
if(!actor.crylink_lastgroup || time > actor.crylink_lastgroup.teleport_time)
{
pos = W_Crylink_LinkJoin(actor.crylink_lastgroup, WEP_CVAR_BOTH(crylink, isprimary, joinspread) * WEP_CVAR_BOTH(crylink, isprimary, speed));
- linkjoineffect = spawn();
+ linkjoineffect = new(linkjoineffect);
linkjoineffect.think = W_Crylink_LinkJoinEffect_Think;
- linkjoineffect.classname = "linkjoineffect";
linkjoineffect.nextthink = time + w_crylink_linkjoin_time;
linkjoineffect.owner = actor;
setorigin(linkjoineffect, pos);
org2 = w_org + w_backoff * 2;
if(w_deathtype & HITTYPE_SECONDARY)
{
- pointparticles(particleeffectnum(EFFECT_CRYLINK_IMPACT2), org2, '0 0 0', 1);
+ pointparticles(EFFECT_CRYLINK_IMPACT2, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_CRYLINK_IMPACT2, VOL_BASE, ATTN_NORM);
}
else
{
- pointparticles(particleeffectnum(EFFECT_CRYLINK_IMPACT), org2, '0 0 0', 1);
+ pointparticles(EFFECT_CRYLINK_IMPACT, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_CRYLINK_IMPACT, VOL_BASE, ATTN_NORM);
}
/* crosshair */ ATTRIB(Devastator, w_crosshair_size, float, 0.7);
/* wepimg */ ATTRIB(Devastator, model2, string, "weaponrocketlauncher");
/* refname */ ATTRIB(Devastator, netname, string, "devastator");
-/* wepname */ ATTRIB(Devastator, message, string, _("Devastator"));
+/* wepname */ ATTRIB(Devastator, m_name, string, _("Devastator"));
ENDCLASS(Devastator)
REGISTER_WEAPON(DEVASTATOR, NEW(Devastator));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_devastator) { weapon_defaultspawnfunc(WEP_DEVASTATOR.m_id); }
+spawnfunc(weapon_devastator) { weapon_defaultspawnfunc(this, WEP_DEVASTATOR); }
spawnfunc(weapon_rocketlauncher) { spawnfunc_weapon_devastator(this); }
.entity lastrocket;
-void W_Devastator_Unregister(void)
+void W_Devastator_Unregister()
{SELFPARAM();
if(self.realowner && self.realowner.lastrocket == self)
{
}
}
-void W_Devastator_Explode(void)
+void W_Devastator_Explode()
{SELFPARAM();
W_Devastator_Unregister();
if(!(self.realowner.items & IT_UNLIMITED_WEAPON_AMMO))
{
self.realowner.cnt = WEP_DEVASTATOR.m_id;
- ATTACK_FINISHED(self.realowner) = time;
+ int slot = 0; // TODO: unhardcode
+ ATTACK_FINISHED(self.realowner, slot) = time;
self.realowner.switchweapon = w_getbestweapon(self.realowner);
}
}
remove(self);
}
-void W_Devastator_DoRemoteExplode(void)
+void W_Devastator_DoRemoteExplode(.entity weaponentity)
{SELFPARAM();
W_Devastator_Unregister();
if(!(self.realowner.items & IT_UNLIMITED_WEAPON_AMMO))
{
self.realowner.cnt = WEP_DEVASTATOR.m_id;
- ATTACK_FINISHED(self.realowner) = time;
+ int slot = weaponslot(weaponentity);
+ ATTACK_FINISHED(self.realowner, slot) = time;
self.realowner.switchweapon = w_getbestweapon(self.realowner);
}
}
remove(self);
}
-void W_Devastator_RemoteExplode(void)
+void W_Devastator_RemoteExplode(.entity weaponentity)
{SELFPARAM();
if(self.realowner.deadflag == DEAD_NO)
if(self.realowner.lastrocket)
: (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > WEP_CVAR(devastator, remote_radius)) // safety device
)
{
- W_Devastator_DoRemoteExplode();
+ W_Devastator_DoRemoteExplode(weaponentity);
}
}
}
// normalize(thisdir + goaldir)
// normalize(0)
-void W_Devastator_Think(void)
+void W_Devastator_Think()
{SELFPARAM();
vector desireddir, olddir, newdir, desiredorigin, goal;
float velspeed, f;
}
}
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
if(self.rl_detonate_later)
- W_Devastator_RemoteExplode();
+ W_Devastator_RemoteExplode(weaponentity);
}
if(self.csqcprojectile_clientanimate == 0)
UpdateCSQCProjectile(self);
}
-void W_Devastator_Touch(void)
+void W_Devastator_Touch()
{SELFPARAM();
if(WarpZone_Projectile_Touch())
{
}
}
#endif
- METHOD(Devastator, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(WEP_CVAR(devastator, reload_ammo) && actor.clip_load < WEP_CVAR(devastator, ammo)) { // forced reload
Weapon w = get_weaponinfo(actor.weapon);
w.wr_reload(w);
} else {
- if(fire1)
+ if(fire & 1)
{
if(actor.rl_release || WEP_CVAR(devastator, guidestop))
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(devastator, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(devastator, refire)))
{
W_Devastator_Attack(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready);
actor.rl_release = 0;
}
}
else
actor.rl_release = 1;
- if(fire2)
+ if(fire & 2)
if(actor.switchweapon == WEP_DEVASTATOR.m_id)
{
entity rock;
{
#if 0
// don't switch while guiding a missile
- if(ATTACK_FINISHED(self) <= time || self.weapon != WEP_DEVASTATOR.m_id)
+ if(ATTACK_FINISHED(self, slot) <= time || self.weapon != WEP_DEVASTATOR.m_id)
{
ammo_amount = false;
if(WEP_CVAR(devastator, reload_ammo))
{
vector org2;
org2 = w_org + w_backoff * 12;
- pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), org2, '0 0 0', 1);
+ pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTN_NORM);
}
/* crosshair */ ATTRIB(Electro, w_crosshair_size, float, 0.6);
/* wepimg */ ATTRIB(Electro, model2, string, "weaponelectro");
/* refname */ ATTRIB(Electro, netname, string, "electro");
-/* wepname */ ATTRIB(Electro, message, string, _("Electro"));
+/* wepname */ ATTRIB(Electro, m_name, string, _("Electro"));
ENDCLASS(Electro)
REGISTER_WEAPON(ELECTRO, NEW(Electro));
ELECTRO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
.float electro_count;
.float electro_secondarytime;
-void W_Electro_ExplodeCombo(void);
+void W_Electro_ExplodeCombo();
#endif
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_electro) { weapon_defaultspawnfunc(WEP_ELECTRO.m_id); }
+spawnfunc(weapon_electro) { weapon_defaultspawnfunc(this, WEP_ELECTRO); }
void W_Electro_TriggerCombo(vector org, float rad, entity own)
{
}
}
-void W_Electro_ExplodeCombo(void)
+void W_Electro_ExplodeCombo()
{SELFPARAM();
W_Electro_TriggerCombo(self.origin, WEP_CVAR(electro, combo_comboradius), self.realowner);
remove(self);
}
-void W_Electro_Explode(void)
+void W_Electro_Explode()
{SELFPARAM();
if(other.takedamage == DAMAGE_AIM)
if(IS_PLAYER(other))
remove(self);
}
-void W_Electro_TouchExplode(void)
+void W_Electro_TouchExplode()
{
PROJECTILE_TOUCH;
W_Electro_Explode();
}
-void W_Electro_Bolt_Think(void)
+void W_Electro_Bolt_Think()
{SELFPARAM();
if(time >= self.ltime)
{
Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- proj = spawn();
- proj.classname = "electro_bolt";
+ proj = new(electro_bolt);
proj.owner = proj.realowner = self;
proj.bot_dodge = true;
proj.bot_dodgerating = WEP_CVAR_PRI(electro, damage);
MUTATOR_CALLHOOK(EditProjectile, self, proj);
}
-void W_Electro_Orb_Touch(void)
+void W_Electro_Orb_Touch()
{SELFPARAM();
PROJECTILE_TOUCH;
if(other.takedamage == DAMAGE_AIM)
Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- entity proj = spawn();
- proj.classname = "electro_orb";
+ entity proj = new(electro_orb);
proj.owner = proj.realowner = self;
proj.use = W_Electro_Explode;
proj.think = adaptor_think2use_hittype_splash;
MUTATOR_CALLHOOK(EditProjectile, self, proj);
}
-void W_Electro_CheckAttack(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Electro_CheckAttack(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{SELFPARAM();
if(self.electro_count > 1)
if(self.BUTTON_ATCK2)
- if(weapon_prepareattack(thiswep, actor, true, -1))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, -1))
{
W_Electro_Attack_Orb(WEP_ELECTRO);
self.electro_count -= 1;
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
return;
}
// WEAPONTODO: when the player releases the button, cut down the length of refire2?
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
}
.float bot_secondary_electromooth;
}
}
}
- METHOD(Electro, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Electro, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(autocvar_g_balance_electro_reload_ammo) // forced reload // WEAPONTODO
{
}
}
- if(fire1)
+ if(fire & 1)
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire)))
{
W_Electro_Attack_Bolt(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready);
}
}
- else if(fire2)
+ else if(fire & 2)
{
if(time >= actor.electro_secondarytime)
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(electro, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(electro, refire)))
{
W_Electro_Attack_Orb(thiswep);
actor.electro_count = WEP_CVAR_SEC(electro, count);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack);
actor.electro_secondarytime = time + WEP_CVAR_SEC(electro, refire2) * W_WeaponRateFactor();
}
}
org2 = w_org + w_backoff * 6;
if(w_deathtype & HITTYPE_SECONDARY)
{
- pointparticles(particleeffectnum(EFFECT_ELECTRO_BALLEXPLODE), org2, '0 0 0', 1);
+ pointparticles(EFFECT_ELECTRO_BALLEXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
}
if(w_deathtype & HITTYPE_BOUNCE)
{
// this is sent as "primary (w_deathtype & HITTYPE_BOUNCE)" to distinguish it from (w_deathtype & HITTYPE_SECONDARY) bounced balls
- pointparticles(particleeffectnum(EFFECT_ELECTRO_COMBO), org2, '0 0 0', 1);
+ pointparticles(EFFECT_ELECTRO_COMBO, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_ELECTRO_IMPACT_COMBO, VOL_BASE, ATTEN_NORM);
}
else
{
- pointparticles(particleeffectnum(EFFECT_ELECTRO_IMPACT), org2, '0 0 0', 1);
+ pointparticles(EFFECT_ELECTRO_IMPACT, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
}
/* crosshair */ //ATTRIB(Fireball, w_crosshair_size, float, 0.65);
/* wepimg */ ATTRIB(Fireball, model2, string, "weaponfireball");
/* refname */ ATTRIB(Fireball, netname, string, "fireball");
-/* wepname */ ATTRIB(Fireball, message, string, _("Fireball"));
+/* wepname */ ATTRIB(Fireball, m_name, string, _("Fireball"));
ENDCLASS(Fireball)
REGISTER_WEAPON(FIREBALL, NEW(Fireball));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_fireball) { weapon_defaultspawnfunc(WEP_FIREBALL.m_id); }
+spawnfunc(weapon_fireball) { weapon_defaultspawnfunc(this, WEP_FIREBALL); }
-void W_Fireball_Explode(void)
+void W_Fireball_Explode()
{SELFPARAM();
entity e;
float dist;
remove(self);
}
-void W_Fireball_TouchExplode(void)
+void W_Fireball_TouchExplode()
{
PROJECTILE_TOUCH;
W_Fireball_Explode();
}
}
-void W_Fireball_Think(void)
+void W_Fireball_Think()
{SELFPARAM();
if(time > self.pushltime)
{
}
}
-void W_Fireball_Attack1(void)
+void W_Fireball_Attack1()
{SELFPARAM();
entity proj;
Send_Effect(EFFECT_FIREBALL_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- proj = spawn();
- proj.classname = "plasma_prim";
+ proj = new(plasma_prim);
proj.owner = proj.realowner = self;
proj.bot_dodge = true;
proj.bot_dodgerating = WEP_CVAR_PRI(fireball, damage);
Send_Effect(EFFECT_FIREBALL_PRE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
}
-void W_Fireball_Attack1_Frame4(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Fireball_Attack1_Frame4(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
W_Fireball_Attack1();
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready);
}
-void W_Fireball_Attack1_Frame3(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Fireball_Attack1_Frame3(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
W_Fireball_AttackEffect(0, '+1.25 +3.75 0');
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4);
}
-void W_Fireball_Attack1_Frame2(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Fireball_Attack1_Frame2(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
W_Fireball_AttackEffect(0, '-1.25 +3.75 0');
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3);
}
-void W_Fireball_Attack1_Frame1(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Fireball_Attack1_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
W_Fireball_AttackEffect(1, '+1.25 -3.75 0');
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2);
}
-void W_Fireball_Attack1_Frame0(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Fireball_Attack1_Frame0(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{SELFPARAM();
W_Fireball_AttackEffect(0, '-1.25 -3.75 0');
sound(self, CH_WEAPON_SINGLE, SND_FIREBALL_PREFIRE2, VOL_BASE, ATTEN_NORM);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1);
}
-void W_Fireball_Firemine_Think(void)
+void W_Fireball_Firemine_Think()
{SELFPARAM();
if(time > self.pushltime)
{
self.nextthink = time + 0.1;
}
-void W_Fireball_Firemine_Touch(void)
+void W_Fireball_Firemine_Touch()
{SELFPARAM();
PROJECTILE_TOUCH;
if(other.takedamage == DAMAGE_AIM)
self.projectiledeathtype |= HITTYPE_BOUNCE;
}
-void W_Fireball_Attack2(void)
+void W_Fireball_Attack2()
{SELFPARAM();
entity proj;
vector f_diff;
Send_Effect(EFFECT_FIREBALL_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- proj = spawn();
+ proj = new(grenade);
proj.owner = proj.realowner = self;
- proj.classname = "grenade";
proj.bot_dodge = true;
proj.bot_dodgerating = WEP_CVAR_SEC(fireball, damage);
proj.movetype = MOVETYPE_BOUNCE;
}
}
}
- METHOD(Fireball, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Fireball, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
- if(fire1)
+ if(fire & 1)
{
if(time >= actor.fireball_primarytime)
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(fireball, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(fireball, refire)))
{
- W_Fireball_Attack1_Frame0(thiswep, actor, fire1, fire2);
+ W_Fireball_Attack1_Frame0(thiswep, actor, weaponentity, fire);
actor.fireball_primarytime = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor();
}
}
- else if(fire2)
+ else if(fire & 2)
{
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(fireball, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(fireball, refire)))
{
W_Fireball_Attack2();
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready);
}
}
}
else
{
org2 = w_org + w_backoff * 16;
- pointparticles(particleeffectnum(EFFECT_FIREBALL_EXPLODE), org2, '0 0 0', 1);
+ pointparticles(EFFECT_FIREBALL_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_FIREBALL_IMPACT2, VOL_BASE, ATTEN_NORM * 0.25); // long range boom
}
/* crosshair */ ATTRIB(Hagar, w_crosshair_size, float, 0.8);
/* wepimg */ ATTRIB(Hagar, model2, string, "weaponhagar");
/* refname */ ATTRIB(Hagar, netname, string, "hagar");
-/* wepname */ ATTRIB(Hagar, message, string, _("Hagar"));
+/* wepname */ ATTRIB(Hagar, m_name, string, _("Hagar"));
ENDCLASS(Hagar)
REGISTER_WEAPON(HAGAR, NEW(Hagar));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(WEP_HAGAR.m_id); }
+spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(this, WEP_HAGAR); }
// NO bounce protection, as bounces are limited!
-void W_Hagar_Explode(void)
+void W_Hagar_Explode()
{SELFPARAM();
self.event_damage = func_null;
RadiusDamage(self, self.realowner, WEP_CVAR_PRI(hagar, damage), WEP_CVAR_PRI(hagar, edgedamage), WEP_CVAR_PRI(hagar, radius), world, world, WEP_CVAR_PRI(hagar, force), self.projectiledeathtype, other);
remove(self);
}
-void W_Hagar_Explode2(void)
+void W_Hagar_Explode2()
{SELFPARAM();
self.event_damage = func_null;
RadiusDamage(self, self.realowner, WEP_CVAR_SEC(hagar, damage), WEP_CVAR_SEC(hagar, edgedamage), WEP_CVAR_SEC(hagar, radius), world, world, WEP_CVAR_SEC(hagar, force), self.projectiledeathtype, other);
W_PrepareExplosionByDamage(attacker, self.think);
}
-void W_Hagar_Touch(void)
+void W_Hagar_Touch()
{SELFPARAM();
PROJECTILE_TOUCH;
self.use();
}
-void W_Hagar_Touch2(void)
+void W_Hagar_Touch2()
{SELFPARAM();
PROJECTILE_TOUCH;
Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- missile = spawn();
+ missile = new(missile);
missile.owner = missile.realowner = self;
- missile.classname = "missile";
missile.bot_dodge = true;
missile.bot_dodgerating = WEP_CVAR_PRI(hagar, damage);
Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- missile = spawn();
+ missile = new(missile);
missile.owner = missile.realowner = self;
- missile.classname = "missile";
missile.bot_dodge = true;
missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage);
}
.float hagar_loadstep, hagar_loadblock, hagar_loadbeep, hagar_warning;
-void W_Hagar_Attack2_Load_Release(void)
+void W_Hagar_Attack2_Load_Release(.entity weaponentity)
{SELFPARAM();
// time to release the rockets we've loaded
if(!self.hagar_load)
return;
- weapon_prepareattack_do(self, true, WEP_CVAR_SEC(hagar, refire));
+ weapon_prepareattack_do(self, weaponentity, true, WEP_CVAR_SEC(hagar, refire));
W_SetupShot(self, false, 2, SND(HAGAR_FIRE), CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
missile = world;
for(counter = 0; counter < shots; ++counter)
{
- missile = spawn();
+ missile = new(missile);
missile.owner = missile.realowner = self;
- missile.classname = "missile";
missile.bot_dodge = true;
missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage);
MUTATOR_CALLHOOK(EditProjectile, self, missile);
}
- weapon_thinkf(self, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready);
+ weapon_thinkf(self, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready);
self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, refire) * W_WeaponRateFactor();
self.hagar_load = 0;
}
-void W_Hagar_Attack2_Load(Weapon thiswep)
+void W_Hagar_Attack2_Load(Weapon thiswep, .entity weaponentity)
{SELFPARAM();
// loadable hagar secondary attack, must always run each frame
if(self.hagar_load)
{
// if we pressed primary fire while loading, unload all rockets and abort
- self.weaponentity.state = WS_READY;
+ self.(weaponentity).state = WS_READY;
W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo
self.hagar_load = 0;
sound(self, CH_WEAPON_A, SND_HAGAR_BEEP, VOL_BASE, ATTN_NORM);
if(!self.hagar_loadblock && self.hagar_loadstep < time)
{
W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo));
- self.weaponentity.state = WS_INUSE;
+ self.(weaponentity).state = WS_INUSE;
self.hagar_load += 1;
sound(self, CH_WEAPON_B, SND_HAGAR_LOAD, VOL_BASE * 0.8, ATTN_NORM); // sound is too loud according to most
// release if player let go of button or if they've held it in too long
if(!self.BUTTON_ATCK2 || (stopped && self.hagar_loadstep < time && WEP_CVAR_SEC(hagar, load_hold) >= 0))
{
- self.weaponentity.state = WS_READY;
- W_Hagar_Attack2_Load_Release();
+ self.(weaponentity).state = WS_READY;
+ W_Hagar_Attack2_Load_Release(weaponentity);
}
}
else
else // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming
self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false);
}
- METHOD(Hagar, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Hagar, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
float loadable_secondary;
loadable_secondary = (WEP_CVAR_SEC(hagar, load) && WEP_CVAR(hagar, secondary));
if(loadable_secondary)
- W_Hagar_Attack2_Load(thiswep); // must always run each frame
+ W_Hagar_Attack2_Load(thiswep, weaponentity); // must always run each frame
if(autocvar_g_balance_hagar_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo))) { // forced reload
Weapon w = get_weaponinfo(actor.weapon);
w.wr_reload(w);
- } else if(fire1 && !actor.hagar_load && !actor.hagar_loadblock) // not while secondary is loaded or awaiting reset
+ } else if((fire & 1) && !actor.hagar_load && !actor.hagar_loadblock) // not while secondary is loaded or awaiting reset
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(hagar, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(hagar, refire)))
{
W_Hagar_Attack(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hagar, refire), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hagar, refire), w_ready);
}
}
- else if(fire2 && !loadable_secondary && WEP_CVAR(hagar, secondary))
+ else if((fire & 2) && !loadable_secondary && WEP_CVAR(hagar, secondary))
{
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(hagar, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hagar, refire)))
{
W_Hagar_Attack2(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready);
}
}
}
// we lost the weapon and want to prepare switching away
if(self.hagar_load)
{
- self.weaponentity.state = WS_READY;
- W_Hagar_Attack2_Load_Release();
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+ self.(weaponentity).state = WS_READY;
+ W_Hagar_Attack2_Load_Release(weaponentity);
}
}
METHOD(Hagar, wr_init, void(entity thiswep))
}
METHOD(Hagar, wr_playerdeath, void(entity thiswep))
{
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
// if we have any rockets loaded when we die, release them
if(self.hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath))
- W_Hagar_Attack2_Load_Release();
+ W_Hagar_Attack2_Load_Release(weaponentity);
}
METHOD(Hagar, wr_reload, void(entity thiswep))
{
{
vector org2;
org2 = w_org + w_backoff * 6;
- pointparticles(particleeffectnum(EFFECT_HAGAR_EXPLODE), org2, '0 0 0', 1);
+ pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
{
if(w_random<0.15)
/* crosshair */ ATTRIB(HLAC, w_crosshair_size, float, 0.6);
/* wepimg */ ATTRIB(HLAC, model2, string, "weaponhlac");
/* refname */ ATTRIB(HLAC, netname, string, "hlac");
-/* wepname */ ATTRIB(HLAC, message, string, _("Heavy Laser Assault Cannon"));
+/* wepname */ ATTRIB(HLAC, m_name, string, _("Heavy Laser Assault Cannon"));
ENDCLASS(HLAC)
REGISTER_WEAPON(HLAC, NEW(HLAC));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_hlac) { weapon_defaultspawnfunc(WEP_HLAC.m_id); }
+spawnfunc(weapon_hlac) { weapon_defaultspawnfunc(this, WEP_HLAC); }
-void W_HLAC_Touch(void)
+void W_HLAC_Touch()
{SELFPARAM();
float isprimary;
self.punchangle_y = random() - 0.5;
}
- missile = spawn();
+ missile = new(hlacbolt);
missile.owner = missile.realowner = self;
- missile.classname = "hlacbolt";
missile.bot_dodge = true;
missile.bot_dodgerating = WEP_CVAR_PRI(hlac, damage);
MUTATOR_CALLHOOK(EditProjectile, self, missile);
}
-void W_HLAC_Attack2(void)
+void W_HLAC_Attack2()
{SELFPARAM();
entity missile;
float spread;
W_SetupShot(self, false, 3, SND(LASERGUN_FIRE), CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage));
Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- missile = spawn();
+ missile = new(hlacbolt);
missile.owner = missile.realowner = self;
- missile.classname = "hlacbolt";
missile.bot_dodge = true;
missile.bot_dodgerating = WEP_CVAR_SEC(hlac, damage);
}
// weapon frames
-void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
if(actor.weapon != actor.switchweapon) // abort immediately if switching
{
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
return;
}
if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
{
W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
return;
}
- ATTACK_FINISHED(actor) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor();
+ int slot = weaponslot(weaponentity);
+ ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor();
W_HLAC_Attack(WEP_HLAC);
actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
}
else
{
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, animtime), w_ready);
}
}
{
self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(hlac, speed), 0, WEP_CVAR_PRI(hlac, lifetime), false);
}
- METHOD(HLAC, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(HLAC, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(autocvar_g_balance_hlac_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo))) { // forced reload
Weapon w = get_weaponinfo(actor.weapon);
w.wr_reload(w);
- } else if(fire1)
+ } else if(fire & 1)
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(hlac, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(hlac, refire)))
{
actor.misc_bulletcounter = 0;
W_HLAC_Attack(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame);
}
}
- else if(fire2 && WEP_CVAR(hlac, secondary))
+ else if((fire & 2) && WEP_CVAR(hlac, secondary))
{
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(hlac, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hlac, refire)))
{
W_HLAC_Attack2_Frame(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready);
}
}
}
{
vector org2;
org2 = w_org + w_backoff * 6;
- pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1);
+ pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM);
}
+++ /dev/null
-#ifndef IMPLEMENTATION
-CLASS(HeavyMachineGun, Weapon)
-/* ammotype */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails)
-/* impulse */ ATTRIB(HeavyMachineGun, impulse, int, 3)
-/* flags */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON);
-/* rating */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
-/* color */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0');
-/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg");
-#ifndef MENUQC
-/* model */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM);
-#endif
-/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi");
-/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6);
-/* wepimg */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg");
-/* refname */ ATTRIB(HeavyMachineGun, netname, string, "hmg");
-/* wepname */ ATTRIB(HeavyMachineGun, message, string, _("Heavy Machine Gun"));
-ENDCLASS(HeavyMachineGun)
-REGISTER_WEAPON(HMG, NEW(HeavyMachineGun));
-
-#define HMG_SETTINGS(w_cvar,w_prop) HMG_SETTINGS_LIST(w_cvar, w_prop, HMG, hmg)
-#define HMG_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
- w_cvar(id, sn, NONE, spread_min) \
- w_cvar(id, sn, NONE, spread_max) \
- w_cvar(id, sn, NONE, spread_add) \
- w_cvar(id, sn, NONE, solidpenetration) \
- w_cvar(id, sn, NONE, damage) \
- w_cvar(id, sn, NONE, force) \
- w_cvar(id, sn, NONE, refire) \
- w_cvar(id, sn, NONE, ammo) \
- w_prop(id, sn, float, reloading_ammo, reload_ammo) \
- w_prop(id, sn, float, reloading_time, reload_time) \
- w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \
- w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \
- w_prop(id, sn, string, weaponreplace, weaponreplace) \
- w_prop(id, sn, float, weaponstart, weaponstart) \
- w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \
- w_prop(id, sn, float, weaponthrowable, weaponthrowable)
-
-#ifdef SVQC
-HMG_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
-#endif
-#endif
-#ifdef IMPLEMENTATION
-#ifdef SVQC
-
-spawnfunc(weapon_hmg) { weapon_defaultspawnfunc(WEP_HMG.m_id); }
-
-void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, bool fire1, bool fire2)
-{
- if (!actor.BUTTON_ATCK)
- {
- w_ready(thiswep, actor, fire1, fire2);
- return;
- }
-
- Weapon w = get_weaponinfo(actor.weapon);
- if(!w.wr_checkammo1(w))
- if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
- {
- W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
- w_ready(thiswep, actor, fire1, fire2);
- return;
- }
-
- W_DecreaseAmmo(WEP_HMG, self, WEP_CVAR(hmg, ammo));
-
- W_SetupShot (actor, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(hmg, damage));
-
- if(!autocvar_g_norecoil)
- {
- actor.punchangle_x = random () - 0.5;
- actor.punchangle_y = random () - 0.5;
- }
-
- float hmg_spread = bound(WEP_CVAR(hmg, spread_min), WEP_CVAR(hmg, spread_min) + (WEP_CVAR(hmg, spread_add) * actor.misc_bulletcounter), WEP_CVAR(hmg, spread_max));
- fireBullet(w_shotorg, w_shotdir, hmg_spread, WEP_CVAR(hmg, solidpenetration), WEP_CVAR(hmg, damage), WEP_CVAR(hmg, force), WEP_HMG.m_id, 0);
-
- actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
-
- Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
-
- W_MachineGun_MuzzleFlash();
- W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0');
-
- if (autocvar_g_casings >= 2) // casing code
- SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
-
- ATTACK_FINISHED(actor) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor();
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(hmg, refire), W_HeavyMachineGun_Attack_Auto);
-}
-
- METHOD(HeavyMachineGun, wr_aim, void(entity thiswep))
- {
- if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200)
- self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false);
- else
- self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false);
- }
- METHOD(HeavyMachineGun, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
- {
- if(WEP_CVAR(hmg, reload_ammo) && actor.clip_load < WEP_CVAR(hmg, ammo)) { // forced reload
- Weapon w = get_weaponinfo(actor.weapon);
- w.wr_reload(w);
- } else
- {
- if (fire1)
- if (weapon_prepareattack(thiswep, actor, false, 0))
- {
- actor.misc_bulletcounter = 0;
- W_HeavyMachineGun_Attack_Auto(thiswep, actor, fire1, fire2);
- }
- }
- }
- METHOD(HeavyMachineGun, wr_init, void(entity thiswep))
- {
- HMG_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
- }
- METHOD(HeavyMachineGun, wr_checkammo1, bool(entity thiswep))
- {
- float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo);
-
- if(autocvar_g_balance_hmg_reload_ammo)
- ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
-
- return ammo_amount;
- }
- METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep))
- {
- float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo);
-
- if(autocvar_g_balance_hmg_reload_ammo)
- ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
-
- return ammo_amount;
- }
- METHOD(HeavyMachineGun, wr_config, void(entity thiswep))
- {
- HMG_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
- }
- METHOD(HeavyMachineGun, wr_reload, void(entity thiswep))
- {
- W_Reload(self, WEP_CVAR(hmg, ammo), SND(RELOAD));
- }
- METHOD(HeavyMachineGun, wr_suicidemessage, int(entity thiswep))
- {
- return WEAPON_THINKING_WITH_PORTALS;
- }
- METHOD(HeavyMachineGun, wr_killmessage, int(entity thiswep))
- {
- if(w_deathtype & HITTYPE_SECONDARY)
- return WEAPON_HMG_MURDER_SNIPE;
- else
- return WEAPON_HMG_MURDER_SPRAY;
- }
-
-#endif
-#ifdef CSQC
-
- METHOD(HeavyMachineGun, wr_impacteffect, void(entity thiswep))
- {
- vector org2;
- org2 = w_org + w_backoff * 2;
- pointparticles(particleeffectnum(EFFECT_MACHINEGUN_IMPACT), org2, w_backoff * 1000, 1);
- if(!w_issilent)
- if(w_random < 0.05)
- sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTEN_NORM);
- else if(w_random < 0.1)
- sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTEN_NORM);
- else if(w_random < 0.2)
- sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTEN_NORM);
- }
-
-#endif
-#endif
/* crosshair */ ATTRIB(Hook, w_crosshair_size, float, 0.5);
/* wepimg */ ATTRIB(Hook, model2, string, "weaponhook");
/* refname */ ATTRIB(Hook, netname, string, "hook");
-/* wepname */ ATTRIB(Hook, message, string, _("Grappling Hook"));
+/* wepname */ ATTRIB(Hook, m_name, string, _("Grappling Hook"));
ATTRIB(Hook, ammo_factor, float, 1)
ENDCLASS(Hook)
REGISTER_WEAPON(HOOK, NEW(Hook));
CLASS(OffhandHook, OffhandWeapon)
+#ifdef SVQC
METHOD(OffhandHook, offhand_think, void(OffhandHook this, entity actor, bool key_pressed))
{
Weapon wep = WEP_HOOK;
- wep.wr_think(wep, actor, key_pressed, false);
+ .entity weaponentity = weaponentities[1];
+ wep.wr_think(wep, actor, weaponentity, key_pressed ? 1 : 0);
}
+#endif
ENDCLASS(OffhandHook)
OffhandHook OFFHAND_HOOK; STATIC_INIT(OFFHAND_HOOK) { OFFHAND_HOOK = NEW(OffhandHook); }
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_hook) { weapon_defaultspawnfunc(WEP_HOOK.m_id); }
+spawnfunc(weapon_hook) { weapon_defaultspawnfunc(this, WEP_HOOK); }
-void W_Hook_ExplodeThink(void)
+void W_Hook_ExplodeThink()
{SELFPARAM();
float dt, dmg_remaining_next, f;
remove(self);
}
-void W_Hook_Explode2(void)
+void W_Hook_Explode2()
{SELFPARAM();
self.event_damage = func_null;
self.touch = func_null;
W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2);
}
-void W_Hook_Touch2(void)
+void W_Hook_Touch2()
{SELFPARAM();
PROJECTILE_TOUCH;
self.use();
MUTATOR_CALLHOOK(EditProjectile, actor, gren);
}
- METHOD(Hook, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Hook, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
- if (fire1) {
+ if (fire & 1) {
if(!actor.hook)
if(!(actor.hook_state & HOOK_WAITING_FOR_RELEASE))
if(time > actor.hook_refire)
- if(weapon_prepareattack(thiswep, actor, false, -1))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, -1))
{
W_DecreaseAmmo(thiswep, actor, thiswep.ammo_factor * WEP_CVAR_PRI(hook, ammo));
actor.hook_state |= HOOK_FIRING;
actor.hook_state |= HOOK_WAITING_FOR_RELEASE;
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready);
}
} else {
actor.hook_state |= HOOK_REMOVING;
actor.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
}
- if(fire2)
+ if(fire & 2)
{
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(hook, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hook, refire)))
{
W_Hook_Attack2(thiswep, actor);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready);
}
}
{
vector org2;
org2 = w_org + w_backoff * 2;
- pointparticles(particleeffectnum(EFFECT_HOOK_EXPLODE), org2, '0 0 0', 1);
+ pointparticles(EFFECT_HOOK_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_HOOKBOMB_IMPACT, VOL_BASE, ATTN_NORM);
}
/* crosshair */ ATTRIB(MachineGun, w_crosshair_size, float, 0.6);
/* wepimg */ ATTRIB(MachineGun, model2, string, "weaponuzi");
/* refname */ ATTRIB(MachineGun, netname, string, "machinegun");
-/* wepname */ ATTRIB(MachineGun, message, string, _("MachineGun"));
+/* wepname */ ATTRIB(MachineGun, m_name, string, _("MachineGun"));
ENDCLASS(MachineGun)
REGISTER_WEAPON(MACHINEGUN, NEW(MachineGun));
if(autocvar_sv_q3acompat_machineshotgunswap)
if(self.classname != "droppedweapon")
{
- weapon_defaultspawnfunc(WEP_SHOCKWAVE.m_id);
+ weapon_defaultspawnfunc(this, WEP_SHOCKWAVE);
return;
}
- weapon_defaultspawnfunc(WEP_MACHINEGUN.m_id);
+ weapon_defaultspawnfunc(this, WEP_MACHINEGUN);
}
spawnfunc(weapon_uzi) { spawnfunc_weapon_machinegun(this); }
-void W_MachineGun_MuzzleFlash_Think(void)
+void W_MachineGun_MuzzleFlash_Think()
{SELFPARAM();
self.frame = self.frame + 2;
self.scale = self.scale * 0.5;
}
-void W_MachineGun_MuzzleFlash(void)
+void W_MachineGun_MuzzleFlash()
{SELFPARAM();
if(self.muzzle_flash == world)
self.muzzle_flash = spawn();
self.muzzle_flash.owner = self.muzzle_flash.realowner = self;
}
-void W_MachineGun_Attack(Weapon thiswep, int deathtype)
+void W_MachineGun_Attack(Weapon thiswep, int deathtype, .entity weaponentity)
{SELFPARAM();
W_SetupShot(self, true, 0, SND(UZI_FIRE), CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage)));
if(!autocvar_g_norecoil)
self.punchangle_x = random() - 0.5;
self.punchangle_y = random() - 0.5;
}
-
+ int slot = weaponslot(weaponentity);
// this attack_finished just enforces a cooldown at the end of a burst
- ATTACK_FINISHED(self) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor();
+ ATTACK_FINISHED(self, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor();
if(self.misc_bulletcounter == 1)
fireBullet(w_shotorg, w_shotdir, WEP_CVAR(machinegun, first_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, first_damage), WEP_CVAR(machinegun, first_force), deathtype, 0);
}
// weapon frames
-void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
if(actor.weapon != actor.switchweapon) // abort immediately if switching
{
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
return;
}
if(actor.BUTTON_ATCK)
if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
{
W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
return;
}
actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
- W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
+ W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, weaponentity);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
}
else
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready);
}
-void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
float machinegun_spread;
- if(!fire1)
+ if(!(fire & 1))
{
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
return;
}
if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
{
W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
return;
}
if(autocvar_g_casings >= 2) // casing code
SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor);
- ATTACK_FINISHED(actor) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor();
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto);
+ int slot = weaponslot(weaponentity);
+ ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor();
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto);
}
-void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
W_SetupShot(actor, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage));
if(!autocvar_g_norecoil)
actor.misc_bulletcounter = actor.misc_bulletcounter + 1;
if(actor.misc_bulletcounter == 0)
{
- ATTACK_FINISHED(actor) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor();
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready);
+ int slot = weaponslot(weaponentity);
+ ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor();
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready);
}
else
{
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst);
}
}
else
self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false);
}
- METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(WEP_CVAR(machinegun, reload_ammo) && actor.clip_load < min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo))) { // forced reload
Weapon w = get_weaponinfo(actor.weapon);
} else
if(WEP_CVAR(machinegun, mode) == 1)
{
- if(fire1)
- if(weapon_prepareattack(thiswep, actor, false, 0))
+ if(fire & 1)
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
{
actor.misc_bulletcounter = 0;
- W_MachineGun_Attack_Auto(thiswep, actor, fire1, fire2);
+ W_MachineGun_Attack_Auto(thiswep, actor, weaponentity, fire);
}
- if(fire2)
- if(weapon_prepareattack(thiswep, actor, true, 0))
+ if(fire & 2)
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
{
Weapon w = get_weaponinfo(actor.weapon);
if(!w.wr_checkammo2(w))
if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
{
W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
return;
}
W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, burst_ammo));
actor.misc_bulletcounter = WEP_CVAR(machinegun, burst) * -1;
- W_MachineGun_Attack_Burst(thiswep, actor, fire1, fire2);
+ W_MachineGun_Attack_Burst(thiswep, actor, weaponentity, fire);
}
}
else
{
- if(fire1)
- if(weapon_prepareattack(thiswep, actor, false, 0))
+ if(fire & 1)
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
{
actor.misc_bulletcounter = 1;
- W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id); // sets attack_finished
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
+ W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, weaponentity); // sets attack_finished
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame);
}
- if(fire2 && WEP_CVAR(machinegun, first))
- if(weapon_prepareattack(thiswep, actor, true, 0))
+ if((fire & 2) && WEP_CVAR(machinegun, first))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0))
{
actor.misc_bulletcounter = 1;
- W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id | HITTYPE_SECONDARY); // sets attack_finished
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready);
+ W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id | HITTYPE_SECONDARY, weaponentity); // sets attack_finished
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready);
}
}
}
{
vector org2;
org2 = w_org + w_backoff * 2;
- pointparticles(particleeffectnum(EFFECT_MACHINEGUN_IMPACT), org2, w_backoff * 1000, 1);
+ pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1);
if(!w_issilent)
- if(w_random < 0.05)
- sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTN_NORM);
- else if(w_random < 0.1)
- sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTN_NORM);
- else if(w_random < 0.2)
- sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTN_NORM);
+ sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM);
}
#endif
/* crosshair */ ATTRIB(MineLayer, w_crosshair_size, float, 0.9);
/* wepimg */ ATTRIB(MineLayer, model2, string, "weaponminelayer");
/* refname */ ATTRIB(MineLayer, netname, string, "minelayer");
-/* wepname */ ATTRIB(MineLayer, message, string, _("Mine Layer"));
+/* wepname */ ATTRIB(MineLayer, m_name, string, _("Mine Layer"));
ENDCLASS(MineLayer)
REGISTER_WEAPON(MINE_LAYER, NEW(MineLayer));
#ifdef SVQC
MINELAYER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
-void W_MineLayer_Think(void);
+void W_MineLayer_Think();
.float minelayer_detonate, mine_explodeanyway;
.float mine_time;
.vector mine_orientation;
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_minelayer) { weapon_defaultspawnfunc(WEP_MINE_LAYER.m_id); }
+spawnfunc(weapon_minelayer) { weapon_defaultspawnfunc(this, WEP_MINE_LAYER); }
void W_MineLayer_Stick(entity to)
{SELFPARAM();
// in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile
- entity newmine;
- newmine = spawn();
+ entity newmine = spawn();
newmine.classname = self.classname;
newmine.bot_dodge = self.bot_dodge;
SetMovetypeFollow(self, to);
}
-void W_MineLayer_Explode(void)
+void W_MineLayer_Explode()
{SELFPARAM();
if(other.takedamage == DAMAGE_AIM)
if(IS_PLAYER(other))
if(!w.wr_checkammo1(w))
{
self.cnt = WEP_MINE_LAYER.m_id;
- ATTACK_FINISHED(self) = time;
+ int slot = 0; // TODO: unhardcode
+ ATTACK_FINISHED(self, slot) = time;
self.switchweapon = w_getbestweapon(self);
}
setself(this);
remove(self);
}
-void W_MineLayer_DoRemoteExplode(void)
+void W_MineLayer_DoRemoteExplode()
{SELFPARAM();
self.event_damage = func_null;
self.takedamage = DAMAGE_NO;
if(!w.wr_checkammo1(w))
{
self.cnt = WEP_MINE_LAYER.m_id;
- ATTACK_FINISHED(self) = time;
+ int slot = 0; // TODO: unhardcode
+ ATTACK_FINISHED(self, slot) = time;
self.switchweapon = w_getbestweapon(self);
}
setself(this);
remove(self);
}
-void W_MineLayer_RemoteExplode(void)
+void W_MineLayer_RemoteExplode()
{SELFPARAM();
if(self.realowner.deadflag == DEAD_NO)
if((self.spawnshieldtime >= 0)
}
}
-void W_MineLayer_ProximityExplode(void)
+void W_MineLayer_ProximityExplode()
{SELFPARAM();
// make sure no friend is in the mine's radius. If there is any, explosion is delayed until he's at a safe distance
if(WEP_CVAR(minelayer, protection) && self.mine_explodeanyway == 0)
return minecount;
}
-void W_MineLayer_Think(void)
+void W_MineLayer_Think()
{SELFPARAM();
entity head;
W_MineLayer_RemoteExplode();
}
-void W_MineLayer_Touch(void)
+void W_MineLayer_Touch()
{SELFPARAM();
if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW)
return; // we're already a stuck mine, why do we get called? TODO does this even happen?
if(self.BUTTON_ATCK2 == true) self.BUTTON_ATCK = false;
}
}
- METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(autocvar_g_balance_minelayer_reload_ammo && actor.clip_load < WEP_CVAR(minelayer, ammo)) // forced reload
{
w.wr_reload(w);
}
}
- else if(fire1)
+ else if(fire & 1)
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(minelayer, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(minelayer, refire)))
{
W_MineLayer_Attack(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready);
}
}
- if(fire2)
+ if(fire & 2)
{
if(W_MineLayer_PlacedMines(true))
sound(actor, CH_WEAPON_B, SND_MINE_DET, VOL_BASE, ATTN_NORM);
}
METHOD(MineLayer, wr_checkammo1, bool(entity thiswep))
{
+ int slot = 0; // TODO: unhardcode
// don't switch while placing a mine
- if(ATTACK_FINISHED(self) <= time || self.weapon != WEP_MINE_LAYER.m_id)
+ if(ATTACK_FINISHED(self, slot) <= time || self.weapon != WEP_MINE_LAYER.m_id)
{
float ammo_amount = self.WEP_AMMO(MINE_LAYER) >= WEP_CVAR(minelayer, ammo);
ammo_amount += self.(weapon_load[WEP_MINE_LAYER.m_id]) >= WEP_CVAR(minelayer, ammo);
{
vector org2;
org2 = w_org + w_backoff * 12;
- pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), org2, '0 0 0', 1);
+ pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_MINE_EXP, VOL_BASE, ATTN_NORM);
}
/* crosshair */ ATTRIB(Mortar, w_crosshair_size, float, 0.7);
/* wepimg */ ATTRIB(Mortar, model2, string, "weapongrenadelauncher");
/* refname */ ATTRIB(Mortar, netname, string, "mortar");
-/* wepname */ ATTRIB(Mortar, message, string, _("Mortar"));
+/* wepname */ ATTRIB(Mortar, m_name, string, _("Mortar"));
ENDCLASS(Mortar)
REGISTER_WEAPON(MORTAR, NEW(Mortar));
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_mortar) { weapon_defaultspawnfunc(WEP_MORTAR.m_id); }
+spawnfunc(weapon_mortar) { weapon_defaultspawnfunc(this, WEP_MORTAR); }
spawnfunc(weapon_grenadelauncher) { spawnfunc_weapon_mortar(this); }
-void W_Mortar_Grenade_Explode(void)
+void W_Mortar_Grenade_Explode()
{SELFPARAM();
if(other.takedamage == DAMAGE_AIM)
if(IS_PLAYER(other))
remove(self);
}
-void W_Mortar_Grenade_Explode2(void)
+void W_Mortar_Grenade_Explode2()
{SELFPARAM();
if(other.takedamage == DAMAGE_AIM)
if(IS_PLAYER(other))
W_PrepareExplosionByDamage(attacker, self.use);
}
-void W_Mortar_Grenade_Think1(void)
+void W_Mortar_Grenade_Think1()
{SELFPARAM();
self.nextthink = time;
if(time > self.cnt)
W_Mortar_Grenade_Explode();
}
-void W_Mortar_Grenade_Touch1(void)
+void W_Mortar_Grenade_Touch1()
{SELFPARAM();
PROJECTILE_TOUCH;
if(other.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
}
}
-void W_Mortar_Grenade_Touch2(void)
+void W_Mortar_Grenade_Touch2()
{SELFPARAM();
PROJECTILE_TOUCH;
if(other.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- gren = spawn();
+ gren = new(grenade);
gren.owner = gren.realowner = self;
- gren.classname = "grenade";
gren.bot_dodge = true;
gren.bot_dodgerating = WEP_CVAR_PRI(mortar, damage);
gren.movetype = MOVETYPE_BOUNCE;
Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- gren = spawn();
+ gren = new(grenade);
gren.owner = gren.realowner = self;
- gren.classname = "grenade";
gren.bot_dodge = true;
gren.bot_dodgerating = WEP_CVAR_SEC(mortar, damage);
gren.movetype = MOVETYPE_BOUNCE;
wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime))));
wepinfo_ter_dps = 0;
*/
- METHOD(Mortar, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Mortar, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(autocvar_g_balance_mortar_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo))) { // forced reload
Weapon w = get_weaponinfo(actor.weapon);
w.wr_reload(w);
- } else if(fire1)
+ } else if(fire & 1)
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(mortar, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(mortar, refire)))
{
W_Mortar_Attack(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready);
}
}
- else if(fire2)
+ else if(fire & 2)
{
if(WEP_CVAR_SEC(mortar, remote_detonateprimary))
{
if(nadefound)
sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM);
}
- else if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(mortar, refire)))
+ else if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(mortar, refire)))
{
W_Mortar_Attack2(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready);
}
}
}
{
vector org2;
org2 = w_org + w_backoff * 12;
- pointparticles(particleeffectnum(EFFECT_GRENADE_EXPLODE), org2, '0 0 0', 1);
+ pointparticles(EFFECT_GRENADE_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_GRENADE_IMPACT, VOL_BASE, ATTN_NORM);
}
/* crosshair */ ATTRIB(PortoLaunch, w_crosshair_size, float, 0.6);
/* wepimg */ ATTRIB(PortoLaunch, model2, string, "weaponporto");
/* refname */ ATTRIB(PortoLaunch, netname, string, "porto");
-/* wepname */ ATTRIB(PortoLaunch, message, string, _("Port-O-Launch"));
+/* wepname */ ATTRIB(PortoLaunch, m_name, string, _("Port-O-Launch"));
ENDCLASS(PortoLaunch)
REGISTER_WEAPON(PORTO, NEW(PortoLaunch));
#ifdef SVQC
#include "../../triggers/trigger/jumppads.qh"
-spawnfunc(weapon_porto) { weapon_defaultspawnfunc(WEP_PORTO.m_id); }
+spawnfunc(weapon_porto) { weapon_defaultspawnfunc(this, WEP_PORTO); }
REGISTER_MUTATOR(porto_ticker, true);
MUTATOR_HOOKFUNCTION(porto_ticker, SV_StartFrame) {
e.porto_forbidden = max(0, e.porto_forbidden - 1);
}
-void W_Porto_Success(void)
+void W_Porto_Success()
{SELFPARAM();
if(self.realowner == world)
{
}
}
-void W_Porto_Think(void)
+void W_Porto_Think()
{SELFPARAM();
trace_plane_normal = '0 0 0';
if(self.realowner.playerid != self.playerid)
W_Porto_Fail(0);
}
-void W_Porto_Touch(void)
+void W_Porto_Touch()
{SELFPARAM();
vector norm;
//Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- gren = spawn();
+ gren = new(porto);
gren.cnt = type;
gren.owner = gren.realowner = self;
gren.playerid = self.playerid;
- gren.classname = "porto";
gren.bot_dodge = true;
gren.bot_dodgerating = 200;
gren.movetype = MOVETYPE_BOUNCEMISSILE;
{
PORTO_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
}
- METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(WEP_CVAR(porto, secondary))
{
- if(fire1)
+ if(fire & 1)
if(!actor.porto_current)
if(!actor.porto_forbidden)
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(porto, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(porto, refire)))
{
W_Porto_Attack(0);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
}
- if(fire2)
+ if(fire & 2)
if(!actor.porto_current)
if(!actor.porto_forbidden)
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(porto, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(porto, refire)))
{
W_Porto_Attack(1);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready);
}
}
else
{
if(actor.porto_v_angle_held)
{
- if(!fire2)
+ if(!(fire & 2))
{
actor.porto_v_angle_held = 0;
}
else
{
- if(fire2)
+ if(fire & 2)
{
actor.porto_v_angle = actor.v_angle;
actor.porto_v_angle_held = 1;
if(actor.porto_v_angle_held)
makevectors(actor.porto_v_angle); // override the previously set angles
- if(fire1)
+ if(fire & 1)
if(!actor.porto_current)
if(!actor.porto_forbidden)
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(porto, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(porto, refire)))
{
W_Porto_Attack(-1);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
}
}
}
/* crosshair */ ATTRIB(Rifle, w_crosshair_size, float, 0.6);
/* wepimg */ ATTRIB(Rifle, model2, string, "weaponrifle");
/* refname */ ATTRIB(Rifle, netname, string, "rifle");
-/* wepname */ ATTRIB(Rifle, message, string, _("Rifle"));
+/* wepname */ ATTRIB(Rifle, m_name, string, _("Rifle"));
ENDCLASS(Rifle)
REGISTER_WEAPON(RIFLE, NEW(Rifle));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_rifle) { weapon_defaultspawnfunc(WEP_RIFLE.m_id); }
+spawnfunc(weapon_rifle) { weapon_defaultspawnfunc(this, WEP_RIFLE); }
spawnfunc(weapon_campingrifle) { spawnfunc_weapon_rifle(this); }
spawnfunc(weapon_sniperrifle) { spawnfunc_weapon_rifle(this); }
SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
}
-void W_Rifle_Attack(void)
+void W_Rifle_Attack()
{
W_Rifle_FireBullet(WEP_RIFLE, WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE.m_id, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), SND(CAMPINGRIFLE_FIRE));
}
-void W_Rifle_Attack2(void)
+void W_Rifle_Attack2()
{
W_Rifle_FireBullet(WEP_RIFLE, WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), SND(CAMPINGRIFLE_FIRE2));
}
-.void(void) rifle_bullethail_attackfunc;
+.void() rifle_bullethail_attackfunc;
.float rifle_bullethail_frame;
.float rifle_bullethail_animtime;
.float rifle_bullethail_refire;
-void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
float r, sw, af;
sw = actor.switchweapon; // make it not detect weapon changes as reason to abort firing
- af = ATTACK_FINISHED(actor);
+ int slot = weaponslot(weaponentity);
+ af = ATTACK_FINISHED(actor, slot);
actor.switchweapon = actor.weapon;
- ATTACK_FINISHED(actor) = time;
- LOG_INFO(ftos(actor.WEP_AMMO(RIFLE)), "\n");
- r = weapon_prepareattack(thiswep, actor, actor.rifle_bullethail_frame == WFRAME_FIRE2, actor.rifle_bullethail_refire);
+ ATTACK_FINISHED(actor, slot) = time;
+ r = weapon_prepareattack(thiswep, actor, weaponentity, actor.rifle_bullethail_frame == WFRAME_FIRE2, actor.rifle_bullethail_refire);
if(actor.switchweapon == actor.weapon)
actor.switchweapon = sw;
if(r)
{
actor.rifle_bullethail_attackfunc();
- weapon_thinkf(actor, actor.rifle_bullethail_frame, actor.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue);
- LOG_INFO("thinkf set\n");
+ weapon_thinkf(actor, weaponentity, actor.rifle_bullethail_frame, actor.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue);
}
else
{
- ATTACK_FINISHED(actor) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
- LOG_INFO("out of ammo... ", ftos(actor.weaponentity.state), "\n");
+ ATTACK_FINISHED(actor, slot) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
}
}
-void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
+void W_Rifle_BulletHail(.entity weaponentity, float mode, void() AttackFunc, float fr, float animtime, float refire)
{SELFPARAM();
// if we get here, we have at least one bullet to fire
AttackFunc();
self.rifle_bullethail_frame = fr;
self.rifle_bullethail_animtime = animtime;
self.rifle_bullethail_refire = refire;
- weapon_thinkf(self, fr, animtime, W_Rifle_BulletHail_Continue);
+ weapon_thinkf(self, weaponentity, fr, animtime, W_Rifle_BulletHail_Continue);
}
else
{
// just one shot
- weapon_thinkf(self, fr, animtime, w_ready);
+ weapon_thinkf(self, weaponentity, fr, animtime, w_ready);
}
}
}
}
}
- METHOD(Rifle, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Rifle, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(autocvar_g_balance_rifle_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo))) { // forced reload
Weapon w = get_weaponinfo(actor.weapon);
} else
{
actor.rifle_accumulator = bound(time - WEP_CVAR(rifle, bursttime), actor.rifle_accumulator, time);
- if(fire1)
- if(weapon_prepareattack_check(thiswep, actor, false, WEP_CVAR_PRI(rifle, refire)))
+ if(fire & 1)
+ if(weapon_prepareattack_check(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(rifle, refire)))
if(time >= actor.rifle_accumulator + WEP_CVAR_PRI(rifle, burstcost))
{
- weapon_prepareattack_do(actor, false, WEP_CVAR_PRI(rifle, refire));
- W_Rifle_BulletHail(WEP_CVAR_PRI(rifle, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(rifle, animtime), WEP_CVAR_PRI(rifle, refire));
+ weapon_prepareattack_do(actor, weaponentity, false, WEP_CVAR_PRI(rifle, refire));
+ W_Rifle_BulletHail(weaponentity, WEP_CVAR_PRI(rifle, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(rifle, animtime), WEP_CVAR_PRI(rifle, refire));
actor.rifle_accumulator += WEP_CVAR_PRI(rifle, burstcost);
}
- if(fire2)
+ if(fire & 2)
{
if(WEP_CVAR(rifle, secondary))
{
w.wr_reload(w);
} else
{
- if(weapon_prepareattack_check(thiswep, actor, true, WEP_CVAR_SEC(rifle, refire)))
+ if(weapon_prepareattack_check(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(rifle, refire)))
if(time >= actor.rifle_accumulator + WEP_CVAR_SEC(rifle, burstcost))
{
- weapon_prepareattack_do(actor, true, WEP_CVAR_SEC(rifle, refire));
- W_Rifle_BulletHail(WEP_CVAR_SEC(rifle, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(rifle, animtime), WEP_CVAR_PRI(rifle, refire));
+ weapon_prepareattack_do(actor, weaponentity, true, WEP_CVAR_SEC(rifle, refire));
+ W_Rifle_BulletHail(weaponentity, WEP_CVAR_SEC(rifle, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(rifle, animtime), WEP_CVAR_PRI(rifle, refire));
actor.rifle_accumulator += WEP_CVAR_SEC(rifle, burstcost);
}
}
{
vector org2;
org2 = w_org + w_backoff * 2;
- pointparticles(particleeffectnum(EFFECT_RIFLE_IMPACT), org2, w_backoff * 1000, 1);
+ pointparticles(EFFECT_RIFLE_IMPACT, org2, w_backoff * 1000, 1);
if(!w_issilent)
{
- if(w_random < 0.2)
- sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTN_NORM);
- else if(w_random < 0.4)
- sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTN_NORM);
- else if(w_random < 0.5)
- sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTN_NORM);
+ sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM);
}
}
METHOD(Rifle, wr_init, void(entity thiswep))
+++ /dev/null
-#ifndef IMPLEMENTATION
-CLASS(RocketPropelledChainsaw, Weapon)
-/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets)
-/* impulse */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7)
-/* flags */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON);
-/* rating */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH);
-/* color */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0');
-/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl");
-#ifndef MENUQC
-/* model */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM);
-#endif
-/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher");
-/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6);
-/* wepimg */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc");
-/* refname */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc");
-/* wepname */ ATTRIB(RocketPropelledChainsaw, message, string, _("Rocket Propelled Chainsaw"));
-ENDCLASS(RocketPropelledChainsaw)
-REGISTER_WEAPON(RPC, NEW(RocketPropelledChainsaw));
-
-#define RPC_SETTINGS(w_cvar,w_prop) RPC_SETTINGS_LIST(w_cvar, w_prop, RPC, rpc)
-#define RPC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
- w_cvar(id, sn, NONE, ammo) \
- w_cvar(id, sn, NONE, animtime) \
- w_cvar(id, sn, NONE, damage) \
- w_cvar(id, sn, NONE, damage2) \
- w_cvar(id, sn, NONE, damageforcescale) \
- w_cvar(id, sn, NONE, edgedamage) \
- w_cvar(id, sn, NONE, force) \
- w_cvar(id, sn, NONE, health) \
- w_cvar(id, sn, NONE, lifetime) \
- w_cvar(id, sn, NONE, radius) \
- w_cvar(id, sn, NONE, refire) \
- w_cvar(id, sn, NONE, speed) \
- w_cvar(id, sn, NONE, speedaccel) \
- w_prop(id, sn, float, reloading_ammo, reload_ammo) \
- w_prop(id, sn, float, reloading_time, reload_time) \
- w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \
- w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \
- w_prop(id, sn, string, weaponreplace, weaponreplace) \
- w_prop(id, sn, float, weaponstart, weaponstart) \
- w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \
- w_prop(id, sn, float, weaponthrowable, weaponthrowable)
-
-#ifdef SVQC
-RPC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
-#endif
-#endif
-#ifdef IMPLEMENTATION
-#ifdef SVQC
-spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(WEP_RPC.m_id); }
-
-void W_RocketPropelledChainsaw_Explode()
-{SELFPARAM();
- self.event_damage = func_null;
- self.takedamage = DAMAGE_NO;
-
- RadiusDamage (self, self.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), world, world, WEP_CVAR(rpc, force), self.projectiledeathtype, other);
-
- remove (self);
-}
-
-void W_RocketPropelledChainsaw_Touch (void)
-{SELFPARAM();
- if(WarpZone_Projectile_Touch())
- if(wasfreed(self))
- return;
-
- W_RocketPropelledChainsaw_Explode();
-}
-
-void W_RocketPropelledChainsaw_Damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{SELFPARAM();
- if (self.health <= 0)
- return;
-
- if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
- return; // g_projectiles_damage says to halt
-
- self.health = self.health - damage;
-
- if (self.health <= 0)
- W_PrepareExplosionByDamage(attacker, W_RocketPropelledChainsaw_Explode);
-}
-
-void W_RocketPropelledChainsaw_Think()
-{SELFPARAM();
- if(self.cnt <= time)
- {
- remove(self);
- return;
- }
-
- self.cnt = vlen(self.velocity);
- self.wait = self.cnt * sys_frametime;
- self.pos1 = normalize(self.velocity);
-
- tracebox(self.origin, self.mins, self.maxs, self.origin + self.pos1 * (2 * self.wait), MOVE_NORMAL, self);
- if(IS_PLAYER(trace_ent))
- Damage (trace_ent, self, self.realowner, WEP_CVAR(rpc, damage2), self.projectiledeathtype, self.origin, normalize(self.origin - other.origin) * WEP_CVAR(rpc, force));
-
- self.velocity = self.pos1 * (self.cnt + (WEP_CVAR(rpc, speedaccel) * sys_frametime));
-
- UpdateCSQCProjectile(self);
- self.nextthink = time;
-}
-
-void W_RocketPropelledChainsaw_Attack (Weapon thiswep)
-{SELFPARAM();
- entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(self);
- entity flash = spawn ();
-
- W_DecreaseAmmo(thiswep, self, WEP_CVAR(rpc, ammo));
- W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', false, 5, SND(ROCKET_FIRE), CH_WEAPON_A, WEP_CVAR(rpc, damage));
- Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- PROJECTILE_MAKETRIGGER(missile);
-
- missile.owner = missile.realowner = self;
- missile.bot_dodge = true;
- missile.bot_dodgerating = WEP_CVAR(rpc, damage) * 2;
-
- missile.takedamage = DAMAGE_YES;
- missile.damageforcescale = WEP_CVAR(rpc, damageforcescale);
- missile.health = WEP_CVAR(rpc, health);
- missile.event_damage = W_RocketPropelledChainsaw_Damage;
- missile.damagedbycontents = true;
- missile.movetype = MOVETYPE_FLY;
-
- missile.projectiledeathtype = WEP_RPC.m_id;
- setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
-
- setorigin (missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point
- W_SetupProjVelocity_Basic(missile, WEP_CVAR(rpc, speed), 0);
-
- missile.touch = W_RocketPropelledChainsaw_Touch;
-
- missile.think = W_RocketPropelledChainsaw_Think;
- missile.cnt = time + WEP_CVAR(rpc, lifetime);
- missile.nextthink = time;
- missile.flags = FL_PROJECTILE;
-
- CSQCProjectile(missile, true, PROJECTILE_RPC, false);
-
- setmodel(flash, MDL_RPC_MUZZLEFLASH); // precision set below
- SUB_SetFade (flash, time, 0.1);
- flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
- W_AttachToShotorg(self, flash, '5 0 0');
- missile.pos1 = missile.velocity;
-
- MUTATOR_CALLHOOK(EditProjectile, self, missile);
-}
-
- METHOD(RocketPropelledChainsaw, wr_aim, void(entity thiswep))
- {
- self.BUTTON_ATCK = bot_aim(WEP_CVAR(rpc, speed), 0, WEP_CVAR(rpc, lifetime), false);
- }
- METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
- {
- if(WEP_CVAR(rpc, reload_ammo) && actor.clip_load < WEP_CVAR(rpc, ammo)) {
- Weapon w = get_weaponinfo(actor.weapon);
- w.wr_reload(w);
- } else
- {
- if (fire1)
- {
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(rpc, refire)))
- {
- W_RocketPropelledChainsaw_Attack(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready);
- }
- }
-
- if (fire2)
- {
- // to-do
- }
- }
- }
- METHOD(RocketPropelledChainsaw, wr_init, void(entity thiswep))
- {
- RPC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
- }
- METHOD(RocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep))
- {
- float ammo_amount = self.WEP_AMMO(RPC) >= WEP_CVAR(rpc, ammo);
- ammo_amount += self.(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR(rpc, ammo);
- return ammo_amount;
- }
- METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep))
- {
- return false;
- }
- METHOD(RocketPropelledChainsaw, wr_config, void(entity thiswep))
- {
- RPC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
- }
- METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep))
- {
- W_Reload(self, WEP_CVAR(rpc, ammo), SND(RELOAD));
- }
- METHOD(RocketPropelledChainsaw, wr_suicidemessage, int(entity thiswep))
- {
- if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
- return WEAPON_RPC_SUICIDE_SPLASH;
- else
- return WEAPON_RPC_SUICIDE_DIRECT;
- }
- METHOD(RocketPropelledChainsaw, wr_killmessage, int(entity thiswep))
- {
- if(w_deathtype & HITTYPE_SECONDARY)
- return WEAPON_BLASTER_MURDER;
- else if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
- return WEAPON_RPC_MURDER_SPLASH;
- else
- return WEAPON_RPC_MURDER_DIRECT;
- }
-
-#endif
-
-#ifdef CSQC
-
- METHOD(RocketPropelledChainsaw, wr_impacteffect, void(entity thiswep))
- {
- vector org2;
- org2 = w_org + w_backoff * 12;
- pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), org2, '0 0 0', 1);
- if(!w_issilent)
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
- }
-
-#endif
-#endif
/* crosshair */ ATTRIB(Seeker, w_crosshair_size, float, 0.8);
/* wepimg */ ATTRIB(Seeker, model2, string, "weaponseeker");
/* refname */ ATTRIB(Seeker, netname, string, "seeker");
-/* wepname */ ATTRIB(Seeker, message, string, _("T.A.G. Seeker"));
+/* wepname */ ATTRIB(Seeker, m_name, string, _("T.A.G. Seeker"));
ENDCLASS(Seeker)
REGISTER_WEAPON(SEEKER, NEW(Seeker));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_seeker) { weapon_defaultspawnfunc(WEP_SEEKER.m_id); }
+spawnfunc(weapon_seeker) { weapon_defaultspawnfunc(this, WEP_SEEKER); }
// ============================
// Begin: Missile functions, these are general functions to be manipulated by other code
// ============================
-void W_Seeker_Missile_Explode(void)
+void W_Seeker_Missile_Explode()
{SELFPARAM();
self.event_damage = func_null;
RadiusDamage(self, self.realowner, WEP_CVAR(seeker, missile_damage), WEP_CVAR(seeker, missile_edgedamage), WEP_CVAR(seeker, missile_radius), world, world, WEP_CVAR(seeker, missile_force), self.projectiledeathtype, other);
remove(self);
}
-void W_Seeker_Missile_Touch(void)
+void W_Seeker_Missile_Touch()
{
PROJECTILE_TOUCH;
W_Seeker_Missile_Explode();
}
-void W_Seeker_Missile_Think(void)
+void W_Seeker_Missile_Think()
{SELFPARAM();
entity e;
vector desireddir, olddir, newdir, eorg;
}
/*
-void W_Seeker_Missile_Animate(void)
+void W_Seeker_Missile_Animate()
{
self.frame = self.frame +1;
self.nextthink = time + 0.05;
//self.detornator = false;
- missile = spawn();
+ missile = new(seeker_missile);
missile.owner = missile.realowner = self;
- missile.classname = "seeker_missile";
missile.bot_dodge = true;
missile.bot_dodgerating = WEP_CVAR(seeker, missile_damage);
// ============================
// Begin: FLAC, close range attack meant for defeating rockets which are coming at you.
// ============================
-void W_Seeker_Flac_Explode(void)
+void W_Seeker_Flac_Explode()
{SELFPARAM();
self.event_damage = func_null;
remove(self);
}
-void W_Seeker_Flac_Touch(void)
+void W_Seeker_Flac_Touch()
{
PROJECTILE_TOUCH;
Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
- missile = spawn();
+ missile = new(missile);
missile.owner = missile.realowner = self;
- missile.classname = "missile";
missile.bot_dodge = true;
missile.bot_dodgerating = WEP_CVAR(seeker, flac_damage);
missile.touch = W_Seeker_Flac_Explode;
return world;
}
-void W_Seeker_Attack(void)
+void W_Seeker_Attack()
{SELFPARAM();
entity tracker, closest_target;
W_Seeker_Fire_Missile(WEP_SEEKER, '0 0 0', closest_target);
}
-void W_Seeker_Vollycontroller_Think(void) // TODO: Merge this with W_Seeker_Attack
+void W_Seeker_Vollycontroller_Think() // TODO: Merge this with W_Seeker_Attack
{SELFPARAM();
float c;
entity oldenemy;
setself(this);
}
-void W_Seeker_Tracker_Think(void)
+void W_Seeker_Tracker_Think()
{SELFPARAM();
// commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up
if((self.realowner.deadflag != DEAD_NO) || (self.tag_target.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER.m_id)
// ============================
// Begin: Tag projectile
// ============================
-void W_Seeker_Tag_Explode(void)
+void W_Seeker_Tag_Explode()
{SELFPARAM();
//if(other==self.realowner)
// return;
W_Seeker_Tag_Explode();
}
-void W_Seeker_Tag_Touch(void)
+void W_Seeker_Tag_Touch()
{SELFPARAM();
vector dir;
vector org2;
else
{
//sprint(self.realowner, strcat("You just tagged ^2", other.netname, "^7 with a tracking device!\n"));
- e = spawn();
+ e = new(tag_tracker);
e.cnt = WEP_CVAR(seeker, missile_count);
- e.classname = "tag_tracker";
e.owner = self.owner;
e.realowner = self.realowner;
W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, SND(TAG_FIRE), CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count));
- missile = spawn();
+ missile = new(seeker_tag);
missile.owner = missile.realowner = self;
- missile.classname = "seeker_tag";
missile.bot_dodge = true;
missile.bot_dodgerating = 50;
missile.touch = W_Seeker_Tag_Touch;
else
self.BUTTON_ATCK = bot_aim(WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), false);
}
- METHOD(Seeker, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(autocvar_g_balance_seeker_reload_ammo && actor.clip_load < min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo))) { // forced reload
Weapon w = get_weaponinfo(actor.weapon);
w.wr_reload(w);
- } else if(fire1)
+ } else if(fire & 1)
{
if(WEP_CVAR(seeker, type) == 1)
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(seeker, missile_refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, missile_refire)))
{
W_Seeker_Attack();
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready);
}
}
else
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(seeker, tag_refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire)))
{
W_Seeker_Fire_Tag(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
}
}
}
- else if(fire2)
+ else if(fire & 2)
{
if(WEP_CVAR(seeker, type) == 1)
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(seeker, tag_refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire)))
{
W_Seeker_Fire_Tag(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
}
}
else
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(seeker, flac_refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, flac_refire)))
{
W_Seeker_Fire_Flac(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready);
}
}
}
}
else
{
- pointparticles(particleeffectnum(EFFECT_HAGAR_EXPLODE), org2, '0 0 0', 1);
+ pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
{
if(w_random<0.15)
}
else
{
- pointparticles(particleeffectnum(EFFECT_HAGAR_EXPLODE), org2, '0 0 0', 1);
+ pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
if(!w_issilent)
{
if(w_random<0.15)
/* crosshair */ ATTRIB(Shockwave, w_crosshair_size, float, 0.7);
/* wepimg */ ATTRIB(Shockwave, model2, string, "weaponshotgun");
/* refname */ ATTRIB(Shockwave, netname, string, "shockwave");
-/* wepname */ ATTRIB(Shockwave, message, string, _("Shockwave"));
+/* wepname */ ATTRIB(Shockwave, m_name, string, _("Shockwave"));
ENDCLASS(Shockwave)
REGISTER_WEAPON(SHOCKWAVE, NEW(Shockwave));
SHOCKWAVE_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
#endif
#ifdef CSQC
-void Net_ReadShockwaveParticle(void);
+void Net_ReadShockwaveParticle();
.vector sw_shotorg;
.vector sw_shotdir;
.float sw_distance;
#endif
#endif
#ifdef IMPLEMENTATION
+
+REGISTER_NET_TEMP(TE_CSQC_SHOCKWAVEPARTICLE)
+
#ifdef SVQC
spawnfunc(weapon_shockwave)
{
if(autocvar_sv_q3acompat_machineshotgunswap)
if(self.classname != "droppedweapon")
{
- weapon_defaultspawnfunc(WEP_MACHINEGUN.m_id);
+ weapon_defaultspawnfunc(this, WEP_MACHINEGUN);
return;
}
- weapon_defaultspawnfunc(WEP_SHOCKWAVE.m_id);
+ weapon_defaultspawnfunc(this, WEP_SHOCKWAVE);
}
const float MAX_SHOCKWAVE_HITS = 10;
vector shockwave_hit_force[MAX_SHOCKWAVE_HITS];
// MELEE ATTACK MODE
-void W_Shockwave_Melee_Think(void)
+void W_Shockwave_Melee_Think()
{SELFPARAM();
// declarations
float i, f, swing, swing_factor, swing_damage, meleetime, is_player;
}
}
-void W_Shockwave_Melee(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Shockwave_Melee(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
sound(actor, CH_WEAPON_A, SND_SHOTGUN_MELEE, VOL_BASE, ATTN_NORM);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(shockwave, melee_animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(shockwave, melee_animtime), w_ready);
- entity meleetemp;
- meleetemp = spawn();
+ entity meleetemp = new(meleetemp);
+ make_pure(meleetemp);
meleetemp.owner = meleetemp.realowner = actor;
meleetemp.think = W_Shockwave_Melee_Think;
meleetemp.nextthink = time + WEP_CVAR(shockwave, melee_delay) * W_WeaponRateFactor();
return true;
}
-void W_Shockwave_Send(void)
+void W_Shockwave_Send()
{SELFPARAM();
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_SHOCKWAVEPARTICLE);
+ WriteHeader(MSG_BROADCAST, TE_CSQC_SHOCKWAVEPARTICLE);
WriteCoord(MSG_BROADCAST, w_shotorg.x);
WriteCoord(MSG_BROADCAST, w_shotorg.y);
WriteCoord(MSG_BROADCAST, w_shotorg.z);
WriteByte(MSG_BROADCAST, num_for_edict(self));
}
-void W_Shockwave_Attack(void)
+void W_Shockwave_Attack()
{SELFPARAM();
// declarations
float multiplier, multiplier_from_accuracy, multiplier_from_distance;
else
{ self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); }
}
- METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
- if(fire1)
+ if(fire & 1)
{
if(time >= actor.shockwave_blasttime) // handle refire separately so the secondary can be fired straight after a primary
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(shockwave, blast_animtime)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(shockwave, blast_animtime)))
{
W_Shockwave_Attack();
actor.shockwave_blasttime = time + WEP_CVAR(shockwave, blast_refire) * W_WeaponRateFactor();
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready);
}
}
}
- else if(fire2)
+ else if(fire & 2)
{
//if(actor.clip_load >= 0) // we are not currently reloading
if(!actor.crouch) // no crouchmelee please
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR(shockwave, melee_refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(shockwave, melee_refire)))
{
// attempt forcing playback of the anim by switching to another anim (that we never play) here...
- weapon_thinkf(actor, WFRAME_FIRE1, 0, W_Shockwave_Melee);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, W_Shockwave_Melee);
}
}
}
}
}
-void Net_ReadShockwaveParticle(void)
+NET_HANDLE(TE_CSQC_SHOCKWAVEPARTICLE, bool isNew)
+{
+ Net_ReadShockwaveParticle();
+ return true;
+}
+
+void Net_ReadShockwaveParticle()
{
entity shockwave;
shockwave = spawn();
// handled by Net_ReadShockwaveParticle
//vector org2;
//org2 = w_org + w_backoff * 2;
- //pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1);
+ //pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1);
}
#endif
/* crosshair */ ATTRIB(Shotgun, w_crosshair_size, float, 0.65);
/* wepimg */ ATTRIB(Shotgun, model2, string, "weaponshotgun");
/* refname */ ATTRIB(Shotgun, netname, string, "shotgun");
-/* wepname */ ATTRIB(Shotgun, message, string, _("Shotgun"));
+/* wepname */ ATTRIB(Shotgun, m_name, string, _("Shotgun"));
ENDCLASS(Shotgun)
REGISTER_WEAPON(SHOTGUN, NEW(Shotgun));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(WEP_SHOTGUN.m_id); }
+spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(this, WEP_SHOTGUN); }
void W_Shotgun_Attack(Weapon thiswep, float isprimary)
{SELFPARAM();
.float swing_prev;
.entity swing_alreadyhit;
-void W_Shotgun_Melee_Think(void)
+void W_Shotgun_Melee_Think()
{SELFPARAM();
// declarations
float i, f, swing, swing_factor, swing_damage, meleetime, is_player;
}
}
-void W_Shotgun_Attack2(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Shotgun_Attack2(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
sound(actor, CH_WEAPON_A, SND_SHOTGUN_MELEE, VOL_BASE, ATTEN_NORM);
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(shotgun, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(shotgun, animtime), w_ready);
- entity meleetemp;
- meleetemp = spawn();
+ entity meleetemp = new(meleetemp);
+ make_pure(meleetemp);
meleetemp.realowner = actor;
meleetemp.think = W_Shotgun_Melee_Think;
meleetemp.nextthink = time + WEP_CVAR_SEC(shotgun, melee_delay) * W_WeaponRateFactor();
}
// alternate secondary weapon frames
-void W_Shotgun_Attack3_Frame2(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Shotgun_Attack3_Frame2(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
Weapon w = get_weaponinfo(actor.weapon);
if (!w.wr_checkammo2(w))
if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
{
W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
return;
}
sound(actor, CH_WEAPON_SINGLE, SND_Null, VOL_BASE, ATTN_NORM); // kill previous sound
W_Shotgun_Attack(WEP_SHOTGUN, true); // actually is secondary, but we trick the last shot into playing full reload sound
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready);
}
-void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
Weapon w = get_weaponinfo(actor.weapon);
if (!w.wr_checkammo2(w))
if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
{
W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
- w_ready(thiswep, actor, fire1, fire2);
+ w_ready(thiswep, actor, weaponentity, fire);
return;
}
W_Shotgun_Attack(WEP_SHOTGUN, false);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2);
}
.float shotgun_primarytime;
else
self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false);
}
- METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(WEP_CVAR(shotgun, reload_ammo) && actor.clip_load < WEP_CVAR_PRI(shotgun, ammo)) // forced reload
{
}
else
{
- if(fire1)
+ if(fire & 1)
{
if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(shotgun, animtime)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(shotgun, animtime)))
{
W_Shotgun_Attack(thiswep, true);
actor.shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor();
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready);
}
}
}
- else if(fire2 && WEP_CVAR(shotgun, secondary) == 2)
+ else if((fire & 2) && WEP_CVAR(shotgun, secondary) == 2)
{
if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_SEC(shotgun, alt_animtime)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(shotgun, alt_animtime)))
{
W_Shotgun_Attack(thiswep, false);
actor.shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor();
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1);
}
}
}
if(actor.clip_load >= 0) // we are not currently reloading
if(!actor.crouch) // no crouchmelee please
if(WEP_CVAR(shotgun, secondary) == 1)
- if((fire1 && actor.WEP_AMMO(SHOTGUN) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || fire2)
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(shotgun, refire)))
+ if(((fire & 1) && actor.WEP_AMMO(SHOTGUN) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (fire & 2))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(shotgun, refire)))
{
// attempt forcing playback of the anim by switching to another anim (that we never play) here...
- weapon_thinkf(actor, WFRAME_FIRE1, 0, W_Shotgun_Attack2);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, W_Shotgun_Attack2);
}
}
METHOD(Shotgun, wr_init, void(entity thiswep))
METHOD(Shotgun, wr_impacteffect, void(entity thiswep))
{
vector org2 = w_org + w_backoff * 2;
- pointparticles(particleeffectnum(EFFECT_SHOTGUN_IMPACT), org2, w_backoff * 1000, 1);
+ pointparticles(EFFECT_SHOTGUN_IMPACT, org2, w_backoff * 1000, 1);
if(!w_issilent && time - self.prevric > 0.25)
{
- if(w_random < 0.0165)
- sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTEN_NORM);
- else if(w_random < 0.033)
- sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTEN_NORM);
- else if(w_random < 0.05)
- sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTEN_NORM);
+ if(w_random < 0.05)
+ sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM);
self.prevric = time;
}
}
/* wepimg */ ATTRIB(Tuba, model2, string, "weapontuba");
/* refname */ ATTRIB(Tuba, netname, string, "tuba");
/* xgettext:no-c-format */
-/* wepname */ ATTRIB(Tuba, message, string, _("@!#%'n Tuba"));
+/* wepname */ ATTRIB(Tuba, m_name, string, _("@!#%'n Tuba"));
ENDCLASS(Tuba)
REGISTER_WEAPON(TUBA, NEW(Tuba));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_tuba) { weapon_defaultspawnfunc(WEP_TUBA.m_id); }
+spawnfunc(weapon_tuba) { weapon_defaultspawnfunc(this, WEP_TUBA); }
bool W_Tuba_HasPlayed(entity pl, string melody, int instrument, bool ignorepitch, float mintempo, float maxtempo)
{
return true;
}
-void W_Tuba_NoteOff(void)
+void W_Tuba_NoteOff()
{SELFPARAM();
// we have a note:
// on: self.spawnshieldtime
if(!sound_allowed(MSG_ONE, self.realowner))
return false;
- WriteByte(MSG_ENTITY, ENT_CLIENT_TUBANOTE);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_TUBANOTE);
WriteByte(MSG_ENTITY, sf);
if(sf & 1)
{
return true;
}
-void W_Tuba_NoteThink(void)
+void W_Tuba_NoteThink()
{SELFPARAM();
float dist_mult;
float vol0, vol1;
self.BUTTON_ATCK2 = 1;
}
}
- METHOD(Tuba, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Tuba, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
- if(fire1)
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(tuba, refire)))
+ if(fire & 1)
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(tuba, refire)))
{
W_Tuba_NoteOn(0);
//weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready);
- weapon_thinkf(actor, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
}
- if(fire2)
- if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR(tuba, refire)))
+ if(fire & 2)
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(tuba, refire)))
{
W_Tuba_NoteOn(HITTYPE_SECONDARY);
//weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready);
- weapon_thinkf(actor, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
}
if(actor.tuba_note)
{
- if(!fire1 && !fire2)
+ if(!(fire & 1) && !(fire & 2))
{
WITH(entity, self, actor.tuba_note, W_Tuba_NoteOff());
}
}
METHOD(Tuba, wr_reload, void(entity thiswep))
{
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
// switch to alternate instruments :)
- if(self.weaponentity.state == WS_READY)
+ if(self.(weaponentity).state == WS_READY)
{
switch(self.tuba_instrument)
{
}
W_SetupShot(self, false, 0, "", 0, 0);
Send_Effect(EFFECT_TELEPORT, w_shotorg, '0 0 0', 1);
- self.weaponentity.state = WS_INUSE;
- weapon_thinkf(self, WFRAME_RELOAD, 0.5, w_ready);
+ self.(weaponentity).state = WS_INUSE;
+ weapon_thinkf(self, weaponentity, WFRAME_RELOAD, 0.5, w_ready);
}
}
METHOD(Tuba, wr_checkammo1, bool(entity thiswep))
/* crosshair */ ATTRIB(Vaporizer, w_crosshair_size, float, 0.6);
/* wepimg */ ATTRIB(Vaporizer, model2, string, "weaponminstanex");
/* refname */ ATTRIB(Vaporizer, netname, string, "vaporizer");
-/* wepname */ ATTRIB(Vaporizer, message, string, _("Vaporizer"));
+/* wepname */ ATTRIB(Vaporizer, m_name, string, _("Vaporizer"));
ENDCLASS(Vaporizer)
REGISTER_WEAPON(VAPORIZER, NEW(Vaporizer));
#endif
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_vaporizer) { weapon_defaultspawnfunc(WEP_VAPORIZER.m_id); }
+spawnfunc(weapon_vaporizer) { weapon_defaultspawnfunc(this, WEP_VAPORIZER); }
spawnfunc(weapon_minstanex) { spawnfunc_weapon_vaporizer(this); }
void W_RocketMinsta_Explosion(vector loc)
W_DecreaseAmmo(thiswep, self, ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)));
}
-void W_RocketMinsta_Laser_Explode (void)
+void W_RocketMinsta_Laser_Explode ()
{SELFPARAM();
if(other.takedamage == DAMAGE_AIM)
if(IS_PLAYER(other))
remove(self);
}
-void W_RocketMinsta_Laser_Touch (void)
+void W_RocketMinsta_Laser_Touch ()
{SELFPARAM();
PROJECTILE_TOUCH;
//W_RocketMinsta_Laser_Explode ();
remove(self);
}
-void W_RocketMinsta_Attack2(void)
+void W_RocketMinsta_Attack2()
{SELFPARAM();
makevectors(self.v_angle);
while(counter < total)
{
- proj = spawn ();
- proj.classname = "plasma_prim";
+ proj = new(plasma_prim);
proj.owner = proj.realowner = self;
proj.bot_dodge = true;
proj.bot_dodgerating = autocvar_g_rm_laser_damage;
}
}
-void W_RocketMinsta_Attack3 (void)
+void W_RocketMinsta_Attack3 ()
{SELFPARAM();
makevectors(self.v_angle);
while(counter < total)
{
- proj = spawn ();
- proj.classname = "plasma_prim";
+ proj = new(plasma_prim);
proj.owner = proj.realowner = self;
proj.bot_dodge = true;
proj.bot_dodgerating = autocvar_g_rm_laser_damage;
else
self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(vaporizer, speed), 0, WEP_CVAR_SEC(vaporizer, lifetime), false); // WEAPONTODO: replace with proper vaporizer cvars
}
- METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
// if the laser uses load, we also consider its ammo for reloading
Weapon w = get_weaponinfo(actor.weapon);
w.wr_reload(w);
}
- if(fire1 && (actor.ammo_cells || !autocvar_g_rm) && !forbidWeaponUse(actor))
+ if((fire & 1) && (actor.ammo_cells || !autocvar_g_rm) && !forbidWeaponUse(actor))
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(vaporizer, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vaporizer, refire)))
{
W_Vaporizer_Attack(thiswep);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready);
}
}
- if(fire2 || (fire1 && !actor.ammo_cells && autocvar_g_rm))
+ if((fire & 2) || ((fire & 1) && !actor.ammo_cells && autocvar_g_rm))
{
if((autocvar_g_rm && autocvar_g_rm_laser) || autocvar_g_rm_laser == 2)
{
actor.weapon = oldwep;
// now do normal refire
- weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready);
}
}
else
vector org2 = w_org + w_backoff * 6;
if(w_deathtype & HITTYPE_SECONDARY)
{
- pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1);
+ pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1);
if(!w_issilent) { sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); }
}
else
{
- pointparticles(particleeffectnum(EFFECT_VORTEX_IMPACT), org2, '0 0 0', 1);
+ pointparticles(EFFECT_VORTEX_IMPACT, org2, '0 0 0', 1);
if(!w_issilent) { sound(self, CH_SHOTS, SND_NEXIMPACT, VOL_BASE, ATTN_NORM); }
}
}
/* crosshair */ ATTRIB(Vortex, w_crosshair_size, float, 0.65);
/* wepimg */ ATTRIB(Vortex, model2, string, "weaponnex");
/* refname */ ATTRIB(Vortex, netname, string, "vortex");
-/* wepname */ ATTRIB(Vortex, message, string, _("Vortex"));
+/* wepname */ ATTRIB(Vortex, m_name, string, _("Vortex"));
ENDCLASS(Vortex)
REGISTER_WEAPON(VORTEX, NEW(Vortex));
#endif
#endif
#ifdef IMPLEMENTATION
-#ifdef SVQC
-spawnfunc(weapon_vortex) { weapon_defaultspawnfunc(WEP_VORTEX.m_id); }
-spawnfunc(weapon_nex) { spawnfunc_weapon_vortex(this); }
+REGISTER_NET_TEMP(TE_CSQC_VORTEXBEAMPARTICLE)
+
+#if defined(SVQC)
void SendCSQCVortexBeamParticle(float charge) {
vector v;
v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_VORTEXBEAMPARTICLE);
+ WriteHeader(MSG_BROADCAST, TE_CSQC_VORTEXBEAMPARTICLE);
WriteCoord(MSG_BROADCAST, w_shotorg.x);
WriteCoord(MSG_BROADCAST, w_shotorg.y);
WriteCoord(MSG_BROADCAST, w_shotorg.z);
WriteCoord(MSG_BROADCAST, v.z);
WriteByte(MSG_BROADCAST, bound(0, 255 * charge, 255));
}
+#elif defined(CSQC)
+NET_HANDLE(TE_CSQC_VORTEXBEAMPARTICLE, bool isNew)
+{
+ vector shotorg, endpos;
+ float charge;
+ shotorg.x = ReadCoord(); shotorg.y = ReadCoord(); shotorg.z = ReadCoord();
+ endpos.x = ReadCoord(); endpos.y = ReadCoord(); endpos.z = ReadCoord();
+ charge = ReadByte() / 255.0;
+
+ pointparticles(EFFECT_VORTEX_MUZZLEFLASH, shotorg, normalize(endpos - shotorg) * 1000, 1);
+
+ //draw either the old v2.3 beam or the new beam
+ charge = sqrt(charge); // divide evenly among trail spacing and alpha
+ particles_alphamin = particles_alphamax = particles_fade = charge;
+
+ if (autocvar_cl_particles_oldvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo()))
+ WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM_OLD), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
+ else
+ WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
+ return true;
+}
+#endif
+
+#ifdef SVQC
+spawnfunc(weapon_vortex) { weapon_defaultspawnfunc(this, WEP_VORTEX); }
+spawnfunc(weapon_nex) { spawnfunc_weapon_vortex(this); }
void W_Vortex_Attack(Weapon thiswep, float issecondary)
{SELFPARAM();
self.BUTTON_ATCK2 = true;
}
}
- METHOD(Vortex, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2))
+ METHOD(Vortex, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(WEP_CVAR(vortex, charge) && actor.vortex_charge < WEP_CVAR(vortex, charge_limit))
actor.vortex_charge = min(1, actor.vortex_charge + WEP_CVAR(vortex, charge_rate) * frametime / W_TICSPERFRAME);
w.wr_reload(w);
} else
{
- if(fire1)
+ if(fire & 1)
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(vortex, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vortex, refire)))
{
W_Vortex_Attack(thiswep, 0);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(vortex, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vortex, animtime), w_ready);
}
}
- if((WEP_CVAR(vortex, charge) && !WEP_CVAR(vortex, secondary)) ? (actor.BUTTON_ZOOM | actor.BUTTON_ZOOMSCRIPT) : fire2)
+ if((WEP_CVAR(vortex, charge) && !WEP_CVAR(vortex, secondary)) ? (actor.BUTTON_ZOOM | actor.BUTTON_ZOOMSCRIPT) : (fire & 2))
{
if(WEP_CVAR(vortex, charge))
{
else if(WEP_CVAR_SEC(vortex, ammo))
{
- if(fire2) // only eat ammo when the button is pressed
+ if(fire & 2) // only eat ammo when the button is pressed
{
dt = min(dt, (1 - actor.vortex_charge) / WEP_CVAR(vortex, charge_rate));
if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
}
else if(WEP_CVAR(vortex, secondary))
{
- if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_SEC(vortex, refire)))
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(vortex, refire)))
{
W_Vortex_Attack(thiswep, 1);
- weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(vortex, animtime), w_ready);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(vortex, animtime), w_ready);
}
}
}
METHOD(Vortex, wr_impacteffect, void(entity thiswep))
{
vector org2 = w_org + w_backoff * 6;
- pointparticles(particleeffectnum(EFFECT_VORTEX_IMPACT), org2, '0 0 0', 1);
+ pointparticles(EFFECT_VORTEX_IMPACT, org2, '0 0 0', 1);
if(!w_issilent)
sound(self, CH_SHOTS, SND_NEXIMPACT, VOL_BASE, ATTN_NORM);
}
#define spawn _spawn
#define particleeffectnum _particleeffectnum
+#define trailparticles __trailparticles
+#define pointparticles __pointparticles
#define setmodel _setmodel
#include "upstream/csprogsdefs.qc"
#undef spawn
#undef particleeffectnum
+#undef trailparticles
+#undef pointparticles
#undef setmodel
#pragma noref 0
#pragma noref 1
#define particleeffectnum __particleeffectnum
+#define trailparticles __trailparticles
+#define pointparticles __pointparticles
#include "upstream/dpextensions.qc"
#undef particleeffectnum
+#undef trailparticles
+#undef pointparticles
int(entity ent, string tagname) _gettagindex = #451;
#define gettagindex _gettagindex
#include "lazy.qh"
#include "linkedlist.qh"
#include "log.qh"
+#include "map.qc"
#include "math.qh"
#include "misc.qh"
#include "net.qh"
#include "progname.qh"
#include "random.qc"
#include "registry.qh"
+#include "registry_net.qh"
#include "replicate.qh"
#include "self.qh"
#include "sortlist.qc"
#include "sort.qh"
#include "spawnfunc.qh"
#include "static.qh"
+#include "stats.qh"
#include "string.qh"
#include "struct.qh"
#include "test.qc"
#endif
// used for simplifying ACCUMULATE_FUNCTIONs
-#define SET_FIRST_OR_LAST(input, first, count) if (!input) { input = (first + count); }
-#define SET_FIELD_COUNT(field, first, count) if (!field) { field = (first + count); ++count; }
-#define CHECK_MAX_COUNT(name, max, count, type) if (count > max) { error(strcat("Maximum ", type, " hit: ", #name, ": ", ftos(count), ".\n")); }
+#define SET_FIRST_OR_LAST(input, first, count) \
+ if (!input) { input = (first + count); }
+#define SET_FIELD_COUNT(field, first, count) \
+ if (!field) { field = (first + count); ++count; }
+#define CHECK_MAX_COUNT(name, max, count, type) \
+ if (count > max) { error(strcat("Maximum ", type, " hit: ", #name, ": ", ftos(count), ".\n")); }
#endif
#ifndef BITS_H
#define BITS_H
+#include "log.qh"
#define BIT(n) (1 << (n))
#define BITS(n) (BIT(n) - 1)
return f;
}
+int randombit(int bits)
+{
+ if (!(bits & (bits - 1))) // this ONLY holds for powers of two!
+ return bits;
+
+ int r = random();
+ int b = 0;
+ int n = 0;
+
+ for (int f = 1; f <= bits; f *= 2)
+ {
+ if (bits & f)
+ {
+ ++n;
+ r *= n;
+ if (r <= 1) b = f;
+ else r = (r - 1) / (n - 1);
+ }
+ }
+ return b;
+}
+
+int randombits(int bits, int k, bool error_return)
+{
+ int r = 0;
+ while (k > 0 && bits != r)
+ {
+ r += randombit(bits - r);
+ --k;
+ }
+ if (error_return)
+ if (k > 0) return -1;
+ // all
+ return r;
+}
+
+void randombit_test(int bits, int iter)
+{
+ while (iter > 0)
+ {
+ LOG_INFO(ftos(randombit(bits)), "\n");
+ --iter;
+ }
+}
+
+enum {
+ OP_SET,
+ OP_MIN,
+ OP_MAX,
+ OP_PLUS,
+ OP_MINUS
+};
+
+bool GiveBit(entity e, .int fld, int bit, int op, int val)
+{
+ int v0 = (e.(fld) & bit);
+ switch (op)
+ {
+ case OP_SET:
+ if (val > 0) e.(fld) |= bit;
+ else e.(fld) &= ~bit;
+ break;
+ case OP_MIN:
+ case OP_PLUS:
+ if (val > 0) e.(fld) |= bit;
+ break;
+ case OP_MAX:
+ if (val <= 0) e.(fld) &= ~bit;
+ break;
+ case OP_MINUS:
+ if (val > 0) e.(fld) &= ~bit;
+ break;
+ }
+ int v1 = (e.(fld) & bit);
+ return v0 != v1;
+}
+
+bool GiveValue(entity e, .int fld, int op, int val)
+{
+ int v0 = e.(fld);
+ switch (op)
+ {
+ case OP_SET:
+ e.(fld) = val;
+ break;
+ case OP_MIN:
+ e.(fld) = max(e.(fld), val); // min 100 cells = at least 100 cells
+ break;
+ case OP_MAX:
+ e.(fld) = min(e.(fld), val);
+ break;
+ case OP_PLUS:
+ e.(fld) += val;
+ break;
+ case OP_MINUS:
+ e.(fld) -= val;
+ break;
+ }
+ int v1 = e.(fld);
+ return v0 != v1;
+}
+
#endif
[[deprecated("use true")]][[alias("true")]] const bool TRUE;
[[deprecated("use false")]][[alias("false")]] const bool FALSE;
+#define boolean(value) ((value) != 0)
+
// get true/false value of a string with multiple different inputs
float InterpretBoolean(string input)
{
case "off":
return false;
- default: return stof(input);
+ default: return boolean(stof(input));
}
}
-#define boolean(value) ((value) != 0)
-
#endif
#ifndef COLOR_H
#define COLOR_H
+#include "string.qh"
+
#define colormapPaletteColor(c, isPants) colormapPaletteColor_(c, isPants, time)
vector colormapPaletteColor_(int c, bool isPants, float t)
{
}
}
+float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)
+{
+ if (mi == ma)
+ {
+ return 0;
+ }
+ else if (ma == rgb.x)
+ {
+ if (rgb.y >= rgb.z) return (rgb.y - rgb.z) / (ma - mi);
+ else return (rgb.y - rgb.z) / (ma - mi) + 6;
+ }
+ else if (ma == rgb.y)
+ {
+ return (rgb.z - rgb.x) / (ma - mi) + 2;
+ }
+ else // if(ma == rgb_z)
+ {
+ return (rgb.x - rgb.y) / (ma - mi) + 4;
+ }
+}
+
+vector hue_mi_ma_to_rgb(float hue, float mi, float ma)
+{
+ vector rgb;
+
+ hue -= 6 * floor(hue / 6);
+
+ // else if(ma == rgb_x)
+ // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
+ if (hue <= 1)
+ {
+ rgb.x = ma;
+ rgb.y = hue * (ma - mi) + mi;
+ rgb.z = mi;
+ }
+ // else if(ma == rgb_y)
+ // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120;
+ else if (hue <= 2)
+ {
+ rgb.x = (2 - hue) * (ma - mi) + mi;
+ rgb.y = ma;
+ rgb.z = mi;
+ }
+ else if (hue <= 3)
+ {
+ rgb.x = mi;
+ rgb.y = ma;
+ rgb.z = (hue - 2) * (ma - mi) + mi;
+ }
+ // else // if(ma == rgb_z)
+ // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240;
+ else if (hue <= 4)
+ {
+ rgb.x = mi;
+ rgb.y = (4 - hue) * (ma - mi) + mi;
+ rgb.z = ma;
+ }
+ else if (hue <= 5)
+ {
+ rgb.x = (hue - 4) * (ma - mi) + mi;
+ rgb.y = mi;
+ rgb.z = ma;
+ }
+ // else if(ma == rgb_x)
+ // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
+ else // if(hue <= 6)
+ {
+ rgb.x = ma;
+ rgb.y = mi;
+ rgb.z = (6 - hue) * (ma - mi) + mi;
+ }
+
+ return rgb;
+}
+
+vector rgb_to_hsv(vector rgb)
+{
+ float mi, ma;
+ vector hsv;
+
+ mi = min(rgb.x, rgb.y, rgb.z);
+ ma = max(rgb.x, rgb.y, rgb.z);
+
+ hsv.x = rgb_mi_ma_to_hue(rgb, mi, ma);
+ hsv.z = ma;
+
+ if (ma == 0) hsv.y = 0;
+ else hsv.y = 1 - mi / ma;
+
+ return hsv;
+}
+
+vector hsv_to_rgb(vector hsv)
+{
+ return hue_mi_ma_to_rgb(hsv.x, hsv.z * (1 - hsv.y), hsv.z);
+}
+
+vector rgb_to_hsl(vector rgb)
+{
+ float mi, ma;
+ vector hsl;
+
+ mi = min(rgb.x, rgb.y, rgb.z);
+ ma = max(rgb.x, rgb.y, rgb.z);
+
+ hsl.x = rgb_mi_ma_to_hue(rgb, mi, ma);
+
+ hsl.z = 0.5 * (mi + ma);
+ if (mi == ma) hsl.y = 0;
+ else if (hsl.z <= 0.5) hsl.y = (ma - mi) / (2 * hsl.z);
+ else // if(hsl_z > 0.5)
+ hsl.y = (ma - mi) / (2 - 2 * hsl.z);
+
+ return hsl;
+}
+
+vector hsl_to_rgb(vector hsl)
+{
+ float mi, ma, maminusmi;
+
+ if (hsl.z <= 0.5) maminusmi = hsl.y * 2 * hsl.z;
+ else maminusmi = hsl.y * (2 - 2 * hsl.z);
+
+ // hsl_z = 0.5 * mi + 0.5 * ma
+ // maminusmi = - mi + ma
+ mi = hsl.z - 0.5 * maminusmi;
+ ma = hsl.z + 0.5 * maminusmi;
+
+ return hue_mi_ma_to_rgb(hsl.x, mi, ma);
+}
+
+string rgb_to_hexcolor(vector rgb)
+{
+ return strcat(
+ "^x",
+ DEC_TO_HEXDIGIT(floor(rgb.x * 15 + 0.5)),
+ DEC_TO_HEXDIGIT(floor(rgb.y * 15 + 0.5)),
+ DEC_TO_HEXDIGIT(floor(rgb.z * 15 + 0.5))
+ );
+}
+
#endif
self.csqcmodel_teleported = 0;
}
-void CSQCModel_Read(bool isnew)
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_MODEL, bool isnew)
+{
int sf = ReadInt24_t();
// some nice flags for CSQCMODEL_IF and the hooks
// draw it
self.drawmask = MASK_NORMAL;
self.predraw = CSQCModel_Draw;
+ return true;
}
entity CSQCModel_server2csqc(float pl)
#include "common.qh"
-void CSQCModel_Read(bool isnew);
-
#define CSQCMODEL_IF(cond)
#define CSQCMODEL_ENDIF
#define CSQCMODEL_PROPERTY(flag,t,r,w,f) \
#endif
}
-void CSQCPlayer_Physics(void)
+void CSQCPlayer_Physics()
{
switch(autocvar_cl_movement)
{
unused_float = islocalplayer;
unused_float = isnolocalplayer;
- WriteByte(MSG_ENTITY, ENT_CLIENT_MODEL);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_MODEL);
WriteInt24_t(MSG_ENTITY, sf);
#define CSQCMODEL_IF(cond) if(cond) {
return true;
}
-#ifdef CSQCPLAYER_FORCE_UPDATES
+#if CSQCPLAYER_FORCE_UPDATES
.float csqcmodel_nextforcedupdate;
#endif
void CSQCModel_CheckUpdate(entity e)
unused_float = islocalplayer;
unused_float = isnolocalplayer;
-#ifdef CSQCPLAYER_FORCE_UPDATES
+#if CSQCPLAYER_FORCE_UPDATES
if(isplayer && time > e.csqcmodel_nextforcedupdate)
{
e.SendFlags |= CSQCMODEL_PROPERTY_ORIGIN;
#include "progname.qh"
#include "static.qh"
-void RegisterCvars(void(string name, string def, string desc, bool archive, string file)f) {}
+void RegisterCvars(void(string name, string def, string desc, bool archive, string file) f) {}
+
+bool cvar_value_issafe(string s)
+{
+ if (strstrofs(s, "\"", 0) >= 0) return false;
+ if (strstrofs(s, "\\", 0) >= 0) return false;
+ if (strstrofs(s, ";", 0) >= 0) return false;
+ if (strstrofs(s, "$", 0) >= 0) return false;
+ if (strstrofs(s, "\r", 0) >= 0) return false;
+ if (strstrofs(s, "\n", 0) >= 0) return false;
+ return true;
+}
/** escape the string to make it safe for consoles */
string MakeConsoleSafe(string input)
#define repr_cvar_vector(x) (sprintf("%v", x))
#define __AUTOCVAR(file, archive, var, type, desc, default) \
- [[accumulate]] void RegisterCvars(void(string, string, string, bool, string)f) \
+ [[accumulate]] void RegisterCvars(void(string, string, string, bool, string) f) \
{ \
f( #var, repr_cvar_##type(default), desc, archive, file); \
} \
void defer(float fdelay, void() func)
{
SELFPARAM();
- entity e;
- e = spawn();
- e.owner = self;
+ entity e = new(deferred);
+ make_pure(e);
+ e.owner = this;
e.use = func;
e.think = defer_think;
e.nextthink = time + fdelay;
do \
{ \
for (int i = start; i < end; ++i) \
- { \
- const noref entity it = arr[i]; \
- if (cond) { body } \
- } \
+ { \
+ const noref entity it = arr[i]; \
+ if (cond) { body } \
+ } \
} \
while (0)
#define FOREACH_LIST(list, next, cond, body) \
do \
- { \
- noref int i = 0; \
+ { \
+ int i = 0; \
for (entity it = list##_first; it; (it = it.next, ++i)) \
- { \
- if (cond) { body } \
- } \
+ { \
+ if (cond) { body } \
+ } \
+ } \
+ while (0)
+
+#define FOREACH_WORD(words, cond, body) \
+ do \
+ { \
+ string _words = words; \
+ int i = 0; \
+ for (string _it; (_it = car(_words)); (_words = cdr(_words), ++i)) \
+ { \
+ const noref string it = _it; \
+ if (cond) { body } \
+ } \
} \
while (0)
{
LinkedListNode n = NEW(LinkedListNode);
n.ll_data = e;
- n.ll_prev = this.ll_tail;
- LinkedListNode tail = this.ll_tail;
- if (tail) tail.ll_next = n;
- else this.ll_head = this.ll_tail = n;
+ LinkedListNode tail = n.ll_prev = this.ll_tail;
+ this.ll_tail = (tail) ? tail.ll_next = n : this.ll_head = n;
return e;
}
}
#define LL_EACH(list, cond, body) \
- do \
- { \
- noref int i = 0; \
- for (entity _it = list.ll_head; _it; (_it = _it.ll_next, ++i)) \
- { \
- noref entity it = _it.ll_data; \
- if (cond) { body } \
- } \
- } \
+ do \
+ { \
+ noref int i = 0; \
+ for (entity _it = list.ll_head; _it; (_it = _it.ll_next, ++i)) \
+ { \
+ noref entity it = _it.ll_data; \
+ if (cond) { body } \
+ } \
+ } \
while (0)
#endif
--- /dev/null
+#ifndef MAP_H
+#define MAP_H
+
+// Databases (hash tables)
+const float DB_BUCKETS = 8192;
+void db_save(float db, string pFilename)
+{
+ int fh = fopen(pFilename, FILE_WRITE);
+ if (fh < 0)
+ {
+ LOG_INFO(strcat("^1Can't write DB to ", pFilename));
+ return;
+ }
+ int n = buf_getsize(db);
+ fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
+ for (int i = 0; i < n; ++i)
+ fputs(fh, strcat(bufstr_get(db, i), "\n"));
+ fclose(fh);
+}
+
+int db_create()
+{
+ return buf_create();
+}
+
+void db_put(float db, string pKey, string pValue);
+
+int db_load(string pFilename)
+{
+ int db = buf_create();
+ if (db < 0) return -1;
+ int fh = fopen(pFilename, FILE_READ);
+ if (fh < 0) return db;
+ string l = fgets(fh);
+ if (stof(l) == DB_BUCKETS)
+ {
+ for (int i = 0; (l = fgets(fh)); ++i)
+ {
+ if (l != "") bufstr_set(db, i, l);
+ }
+ }
+ else
+ {
+ // different count of buckets, or a dump?
+ // need to reorganize the database then (SLOW)
+ //
+ // note: we also parse the first line (l) in case the DB file is
+ // missing the bucket count
+ do
+ {
+ int n = tokenizebyseparator(l, "\\");
+ for (int j = 2; j < n; j += 2)
+ db_put(db, argv(j - 1), uri_unescape(argv(j)));
+ }
+ while ((l = fgets(fh)));
+ }
+ fclose(fh);
+ return db;
+}
+
+void db_dump(float db, string pFilename)
+{
+ int fh = fopen(pFilename, FILE_WRITE);
+ if (fh < 0) error(strcat("Can't dump DB to ", pFilename));
+ int n = buf_getsize(db);
+ fputs(fh, "0\n");
+ for (int i = 0; i < n; ++i)
+ {
+ int m = tokenizebyseparator(bufstr_get(db, i), "\\");
+ for (int j = 2; j < m; j += 2)
+ fputs(fh, strcat("\\", argv(j - 1), "\\", argv(j), "\n"));
+ }
+ fclose(fh);
+}
+
+void db_close(float db)
+{
+ buf_del(db);
+}
+
+string db_get(float db, string pKey)
+{
+ int h = crc16(false, pKey) % DB_BUCKETS;
+ return uri_unescape(infoget(bufstr_get(db, h), pKey));
+}
+
+void db_put(float db, string pKey, string pValue)
+{
+ int h = crc16(false, pKey) % DB_BUCKETS;
+ bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));
+}
+
+void db_test()
+{
+ LOG_INFO("LOAD...\n");
+ int db = db_load("foo.db");
+ LOG_INFO("LOADED. FILL...\n");
+ for (int i = 0; i < DB_BUCKETS; ++i)
+ db_put(db, ftos(random()), "X");
+ LOG_INFO("FILLED. SAVE...\n");
+ db_save(db, "foo.db");
+ LOG_INFO("SAVED. CLOSE...\n");
+ db_close(db);
+ LOG_INFO("CLOSED.\n");
+}
+
+#endif
#define MEAN_EVALUATE(prefix) mean_evaluate(self, prefix##_accumulator, prefix##_count, prefix##_mean)
#define MEAN_DECLARE(prefix, m) float prefix##_mean = m; .float prefix##_count, prefix##_accumulator
-/*
-==================
-crandom
-
-Returns a random number between -1.0 and 1.0
-==================
-*/
-float crandom()
-{
- return 2 * (random() - 0.5);
-}
+/** Returns a random number between -1.0 and 1.0 */
+#define crandom() (2 * (random() - 0.5))
/*
return b == median(a - eps, b, c + eps);
}
+float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
+{
+ if (halflifedist > 0) return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
+ else if (halflifedist < 0) return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
+ else return 1;
+}
+
float power2of(float e)
{
return pow(2, e);
else return 0;
}
+/** ax^2 + bx + c = 0 */
+vector solve_quadratic(float a, float b, float c)
+{
+ vector v;
+ float D;
+ v = '0 0 0';
+ if (a == 0)
+ {
+ if (b != 0)
+ {
+ v.x = v.y = -c / b;
+ v.z = 1;
+ }
+ else
+ {
+ if (c == 0)
+ {
+ // actually, every number solves the equation!
+ v.z = 1;
+ }
+ }
+ }
+ else
+ {
+ D = b * b - 4 * a * c;
+ if (D >= 0)
+ {
+ D = sqrt(D);
+ if (a > 0) // put the smaller solution first
+ {
+ v.x = ((-b) - D) / (2 * a);
+ v.y = ((-b) + D) / (2 * a);
+ }
+ else
+ {
+ v.x = (-b + D) / (2 * a);
+ v.y = (-b - D) / (2 * a);
+ }
+ v.z = 1;
+ }
+ else
+ {
+ // complex solutions!
+ D = sqrt(-D);
+ v.x = -b / (2 * a);
+ if (a > 0) v.y = D / (2 * a);
+ else v.y = -D / (2 * a);
+ v.z = 0;
+ }
+ }
+ return v;
+}
#endif
#define OVERLOAD(F, ...) OVERLOAD_(F,##__VA_ARGS__)(__VA_ARGS__)
#endif
+#if defined(CSQC)
+ #define etof(e) num_for_edict(e)
+ #define ftoe(i) entitybyindex(i)
+#elif defined(SVQC)
+ #define etof(e) num_for_edict(e)
+ #define ftoe(i) edict_num(i)
+#elif defined(MENUQC)
+ // already defined
+#endif
+
+#undef etof
+// avoid bounds checks
+#define etof(e) stof(sprintf("%i", e))
+
#define GET(name) name##get
#define GETTER(type, name) type GET(name)() { return name; }
-
+#define PROPERTY(type, name) type name; GETTER(type, name)
#define LAMBDA(...) { __VA_ARGS__; }
-// Can't wrap with do-while as block may contain continue or break
+// With block may not contain continue or break
#define WITH(type, name, value, block) \
+ do \
{ \
type __with_save = (name); \
name = (value); \
LAMBDA(block) \
name = __with_save; \
- } do \
- { \
} \
while (0)
#ifdef SVQC
.int Version; // deprecated, use SendFlags
.int SendFlags;
- .bool(entity to, int sendflags)SendEntity;
- .bool(entity this, entity to, int sendflags)SendEntity3;
+ .bool(entity to, int sendflags) SendEntity;
+ .bool(entity this, entity to, int sendflags) SendEntity3;
bool SendEntity_self(entity to, int sendflags) { return self.SendEntity3(self, to, sendflags); }
- void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags)sendfunc)
+ void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
{
if (e.classname == "") e.classname = "net_linked";
.void() uncustomizeentityforclient;
.float uncustomizeentityforclient_set;
- void SetCustomizer(entity e, float(void)customizer, void(void)uncustomizer)
+ void SetCustomizer(entity e, float() customizer, void() uncustomizer)
{
e.customizeentityforclient = customizer;
e.uncustomizeentityforclient = uncustomizer;
.string netname;
.int m_id;
-.void(entity this, bool isNew)m_read;
+.bool(entity this, bool isNew) m_read;
#ifdef CSQC
- #define Net_Accept() \
+ #define Net_Accept(classname) \
do \
{ \
- if (!this) this = spawn(); \
+ if (!this) this = new(classname); \
} \
while (0)
#define Net_Reject() \
if (this) remove(this); \
} \
while (0)
+ #define NET_HANDLE(id, param) \
+ bool Net_Handle_##id(entity this, param)
#else
#define WriteHeader(to, id) \
do \
#endif
#ifdef CSQC
- #define REGISTER_NET_LINKED(id, param) \
- void Ent_Read##id(entity this, param) \
+ #define REGISTER_NET_LINKED(id) \
+ [[accumulate]] NET_HANDLE(id, bool) \
{ \
this = self; \
+ this.sourceLocFile = __FILE__; \
+ this.sourceLocLine = __LINE__; \
} \
- REGISTER(RegisterLinkedEntities, NET, LinkedEntities, id, m_id, spawn()) \
+ REGISTER(RegisterLinkedEntities, NET, LinkedEntities, id, m_id, new(net_linked_packet)) \
{ \
+ make_pure(this); \
this.netname = #id; \
- this.m_read = Ent_Read##id; \
- } \
- [[accumulate]] void Ent_Read##id(entity this, param)
+ this.m_read = Net_Handle_##id; \
+ }
#else
- #define REGISTER_NET_LINKED(id, param) \
+ #define REGISTER_NET_LINKED(id) \
const bool NET_##id##_istemp = false; \
- REGISTER(RegisterLinkedEntities, NET, LinkedEntities, id, m_id, spawn()) \
+ REGISTER(RegisterLinkedEntities, NET, LinkedEntities, id, m_id, new(net_linked_packet)) \
{ \
+ make_pure(this); \
this.netname = #id; \
}
#endif
-REGISTRY(LinkedEntities, BIT(0))
+REGISTRY(LinkedEntities, BITS(8) - 1)
+#define LinkedEntities_from(i) _LinkedEntities_from(i, NULL)
REGISTER_REGISTRY(RegisterLinkedEntities)
-REGISTRY_SORT(LinkedEntities, netname, 0)
+REGISTRY_SORT(LinkedEntities, 0)
+REGISTRY_CHECK(LinkedEntities)
STATIC_INIT(RegisterLinkedEntities_renumber)
{
for (int i = 0; i < LinkedEntities_COUNT; ++i)
- LinkedEntities[i].m_id = 100 + i;
+ LinkedEntities_from(i).m_id = 1 + i;
}
#ifdef CSQC
- #define REGISTER_NET_TEMP(id, param) \
- void Net_Read##id(entity this, param); \
- REGISTER(RegisterTempEntities, NET, TempEntities, id, m_id, spawn()) \
+ #define REGISTER_NET_TEMP(id) \
+ NET_HANDLE(id, bool); \
+ REGISTER(RegisterTempEntities, NET, TempEntities, id, m_id, new(net_temp_packet)) \
{ \
+ make_pure(this); \
this.netname = #id; \
- this.m_read = Net_Read##id; \
- } \
- void Net_Read##id(entity this, param)
+ this.m_read = Net_Handle_##id; \
+ }
#else
- #define REGISTER_NET_TEMP(id, param) \
+ #define REGISTER_NET_TEMP(id) \
const bool NET_##id##_istemp = true; \
- REGISTER(RegisterTempEntities, NET, TempEntities, id, m_id, spawn()) \
+ REGISTER(RegisterTempEntities, NET, TempEntities, id, m_id, new(net_temp_packet)) \
{ \
+ make_pure(this); \
this.netname = #id; \
}
#endif
-REGISTRY(TempEntities, BIT(0))
+REGISTRY(TempEntities, BITS(8) - 80)
+#define TempEntities_from(i) _TempEntities_from(i, NULL)
REGISTER_REGISTRY(RegisterTempEntities)
-REGISTRY_SORT(TempEntities, netname, 0)
+REGISTRY_SORT(TempEntities, 0)
+REGISTRY_CHECK(TempEntities)
STATIC_INIT(RegisterTempEntities_renumber)
{
for (int i = 0; i < TempEntities_COUNT; ++i)
- TempEntities[i].m_id = 115 + i;
+ TempEntities_from(i).m_id = 80 + i;
}
#ifndef MENUQC
+ const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05;
+ #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
+ #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
+
#ifdef CSQC
+ entity ReadCSQCEntity()
+ {
+ int f = ReadShort();
+ if (f == 0) return world;
+ return findfloat(world, entnum, f);
+ }
int ReadInt24_t()
{
int v = ReadShort() << 8; // note: this is signed
v.z = ReadInt24_t();
return v;
}
+
+ float ReadApproxPastTime()
+ {
+ float dt = ReadByte();
+
+ // map from range...PPROXPASTTIME_MAX / 256
+ dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt));
+
+ return servertime - dt;
+ }
+
#else
+ const int MSG_ENTITY = 5;
+
void WriteInt24_t(float dst, float val)
{
float v;
WriteInt24_t(dst, val.y);
WriteInt24_t(dst, val.z);
}
+
+ // this will use the value:
+ // 128
+ // accuracy near zero is APPROXPASTTIME_MAX/(256*255)
+ // accuracy at x is 1/derivative, i.e.
+ // APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536
+ void WriteApproxPastTime(float dst, float t)
+ {
+ float dt = time - t;
+
+ // warning: this is approximate; do not resend when you don't have to!
+ // be careful with sendflags here!
+ // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75
+
+ // map to range...
+ dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt));
+
+ // round...
+ dt = rint(bound(0, dt, 255));
+
+ WriteByte(dst, dt);
+ }
+
+ // allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example)
+ #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname, statement) \
+ entity varname; varname = msg_entity; \
+ FOR_EACH_REALCLIENT(msg_entity) \
+ if (msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) \
+ statement msg_entity = varname
+ #define WRITESPECTATABLE_MSG_ONE(statement) \
+ do \
+ { \
+ WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement); \
+ } \
+ while (0)
+ #define WRITESPECTATABLE(msg, statement) \
+ if (msg == MSG_ONE) WRITESPECTATABLE_MSG_ONE(statement); \
+ else \
+ statement float WRITESPECTATABLE_workaround = 0
#endif
#endif
#define string_null nil
#else
// the NULL function
- var void func_null(void);
+ var void func_null();
string string_null;
#endif
#include "misc.qh"
#include "nil.qh"
+#include "static.qh"
#ifdef MENUQC
#define NULL (null_entity)
#define NULL (world)
#endif
+.vector origin;
+.bool pure_data;
+#define make_pure(e) \
+ do \
+ { \
+ (e).pure_data = true; \
+ } \
+ while (0)
+#define is_pure(e) ((e).pure_data)
+
.string classname;
/** Location entity was spawned from in source */
.string sourceLocFile;
.int sourceLocLine;
entity _spawn();
-entity __spawn(string _classname, string _sourceFile, int _sourceLine)
+entity __spawn(string _classname, string _sourceFile, int _sourceLine, bool pure)
{
entity this = _spawn();
this.classname = _classname;
this.sourceLocFile = _sourceFile;
this.sourceLocLine = _sourceLine;
+ if (pure) make_pure(this);
return this;
}
#ifndef QCC_SUPPORT_ENTITYCLASS
#define entityclass_2(name, base) typedef entity name
#define class(name)
- #define new(class) __spawn( #class, __FILE__, __LINE__)
+ #define new(class) __spawn( #class, __FILE__, __LINE__, false)
#else
#define entityclass_2(name, base) entityclass name : base {}
#define class(name) [[class(name)]]
- #define new(class) ((class) __spawn( #class, __FILE__, __LINE__))
+ #define new(class) ((class) __spawn( #class, __FILE__, __LINE__, false))
#endif
-#define spawn() new(entity)
+#define spawn() __spawn("entity", __FILE__, __LINE__, false)
+
+entity _clearentity_ent;
+STATIC_INIT(clearentity)
+{
+ _clearentity_ent = new(clearentity);
+}
+void clearentity(entity e)
+{
+#ifdef CSQC
+ int n = e.entnum;
+#endif
+ copyentity(_clearentity_ent, e);
+#ifdef CSQC
+ e.entnum = n;
+#endif
+}
// Classes have a `spawn##cname(entity)` constructor
// The parameter is used across [[accumulate]] functions
void cname##_vtbl_init() \
{ \
cname e = new(vtbl); \
+ make_pure(e); \
spawn##cname##_static(e); \
e.vtblname = #cname; \
/* Top level objects refer to themselves */ \
class(cname).type name[cnt];
#define ENDCLASS(cname) \
- [[last]] INIT(cname) \
+ INIT(cname) \
{ \
return this; \
}
}
return s;
}
- METHOD(Object, display, void(entity this, void(string name, string icon)returns))
+ METHOD(Object, display, void(entity this, void(string name, string icon) returns))
{
returns(sprintf("entity %i", this), "nopreview_map");
}
}
}
+float DistributeEvenly_amount;
+float DistributeEvenly_totalweight;
+
+void DistributeEvenly_Init(float amount, float totalweight)
+{
+ if (DistributeEvenly_amount)
+ {
+ LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
+ LOG_TRACE(ftos(DistributeEvenly_totalweight), " left!)\n");
+ }
+ if (totalweight == 0) DistributeEvenly_amount = 0;
+ else DistributeEvenly_amount = amount;
+ DistributeEvenly_totalweight = totalweight;
+}
+
+float DistributeEvenly_Get(float weight)
+{
+ float f;
+ if (weight <= 0) return 0;
+ f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
+ DistributeEvenly_totalweight -= weight;
+ DistributeEvenly_amount -= f;
+ return f;
+}
+
+float DistributeEvenly_GetRandomized(float weight)
+{
+ float f;
+ if (weight <= 0) return 0;
+ f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
+ DistributeEvenly_totalweight -= weight;
+ DistributeEvenly_amount -= f;
+ return f;
+}
+
+// from the GNU Scientific Library
+float gsl_ran_gaussian_lastvalue;
+float gsl_ran_gaussian_lastvalue_set;
+float gsl_ran_gaussian(float sigma)
+{
+ if (gsl_ran_gaussian_lastvalue_set)
+ {
+ gsl_ran_gaussian_lastvalue_set = 0;
+ return sigma * gsl_ran_gaussian_lastvalue;
+ }
+ else
+ {
+ float a = random() * 2 * M_PI;
+ float b = sqrt(-2 * log(random()));
+ gsl_ran_gaussian_lastvalue = cos(a) * b;
+ gsl_ran_gaussian_lastvalue_set = 1;
+ return sigma * sin(a) * b;
+ }
+}
// prandom - PREDICTABLE random number generator (not seeded yet)
#include "oo.qh"
+#define REGISTER_REGISTRY(func) ACCUMULATE_FUNCTION(__static_init, func)
+
#define REGISTER_INIT(ns, id) [[accumulate]] void Register_##ns##_##id##_init(entity this)
#define REGISTER_INIT_POST(ns, id) [[accumulate]] void Register_##ns##_##id##_init_post(entity this)
#define REGISTRY(id, max) \
void Register##id() {} \
const int id##_MAX = max; \
- noref entity id[id##_MAX], id##_first, id##_last; \
- int id##_COUNT;
+ noref entity _##id[id##_MAX], id##_first, id##_last; \
+ int id##_COUNT; \
+ entity _##id##_from(int i, entity null) { if (i >= 0 && i < id##_COUNT) { entity e = _##id[i]; if (e) return e; } return null; }
+
+/** registered item identifier */
+.string registered_id;
/**
* Register a new entity with a global constructor.
* @param fld The field to store the current count into
* @param inst An expression to create a new instance, invoked for every registration
*/
-#define REGISTER(initfunc, ns, array, id, fld, inst) \
- entity ns##_##id; \
- REGISTER_INIT(ns, id) {} \
- REGISTER_INIT_POST(ns, id) {} \
+#define REGISTER(initfunc, ns, array, id, fld, inst) \
+ entity ns##_##id; \
+ REGISTER_INIT(ns, id) {} \
+ REGISTER_INIT_POST(ns, id) {} \
void Register_##ns##_##id() \
- { \
+ { \
if (array##_COUNT >= array##_MAX) LOG_FATALF("Registry capacity exceeded (%s)", ftos(array##_MAX)); \
- entity this = inst; \
- ns##_##id = this; \
- this.fld = array##_COUNT; \
- array[array##_COUNT++] = this; \
- if (!array##_first) array##_first = this; \
- if (array##_last) array##_last.REGISTRY_NEXT = this; \
- array##_last = this; \
- Register_##ns##_##id##_init(this); \
- Register_##ns##_##id##_init_post(this); \
- } \
- ACCUMULATE_FUNCTION(initfunc, Register_##ns##_##id) \
+ entity this = inst; \
+ ns##_##id = this; \
+ this.registered_id = #id; \
+ this.fld = array##_COUNT; \
+ _##array[array##_COUNT++] = this; \
+ if (!array##_first) array##_first = this; \
+ if (array##_last) array##_last.REGISTRY_NEXT = this; \
+ array##_last = this; \
+ Register_##ns##_##id##_init(this); \
+ Register_##ns##_##id##_init_post(this); \
+ } \
+ ACCUMULATE_FUNCTION(initfunc, Register_##ns##_##id) \
REGISTER_INIT(ns, id)
/** internal next pointer */
#define REGISTRY_NEXT enemy
.entity REGISTRY_NEXT;
-#define REGISTRY_SORT(id, field, skip) \
+#define REGISTRY_SORT(id, skip) \
void _REGISTRY_SWAP_##id(int i, int j, entity pass) \
- { \
- i += skip; j += skip; \
- \
- entity a = id[i], b = id[j]; \
- id[i] = b; \
- id[j] = a; \
- \
- entity a_next = a.REGISTRY_NEXT, b_next = b.REGISTRY_NEXT; \
- a.REGISTRY_NEXT = b_next; \
- b.REGISTRY_NEXT = a_next; \
- \
- if (i == 0) id##_first = b; \
- else id[i - 1].REGISTRY_NEXT = b; \
- \
- if (j == 0) id##_first = a; \
- else id[j - 1].REGISTRY_NEXT = a; \
- } \
- float _REGISTRY_CMP_##id(int i, int j, entity pass) \
- { \
- i += skip; j += skip; \
- string a = id[i].field; \
- string b = id[j].field; \
- return strcasecmp(a, b); \
- } \
+ { \
+ i += skip; j += skip; \
+ \
+ entity a = _##id[i], b = _##id[j]; \
+ _##id[i] = b; \
+ _##id[j] = a; \
+ \
+ entity a_next = a.REGISTRY_NEXT, b_next = b.REGISTRY_NEXT; \
+ a.REGISTRY_NEXT = b_next; \
+ b.REGISTRY_NEXT = a_next; \
+ \
+ if (i == 0) id##_first = b; \
+ else _##id[i - 1].REGISTRY_NEXT = b; \
+ \
+ if (j == 0) id##_first = a; \
+ else _##id[j - 1].REGISTRY_NEXT = a; \
+ } \
+ int _REGISTRY_CMP_##id(int i, int j, entity pass) \
+ { \
+ i += skip; j += skip; \
+ string a = _##id[i].registered_id; \
+ string b = _##id[j].registered_id; \
+ return strcmp(a, b); \
+ } \
STATIC_INIT(Registry_sort_##id) \
- { \
+ { \
heapsort(id##_COUNT - (skip), _REGISTRY_SWAP_##id, _REGISTRY_CMP_##id, NULL); \
}
+#define REGISTRY_HASH(id) Registry_hash_##id
+
+[[accumulate]] void Registry_check(string r, string server) { }
+[[accumulate]] void Registry_send_all() { }
+
+#ifdef SVQC
+void Registry_send(string id, string hash);
+#else
+#define Registry_send(id, hash)
+#endif
+
+#define REGISTRY_CHECK(id) \
+ string REGISTRY_HASH(id); \
+ STATIC_INIT(Registry_check_##id) \
+ { \
+ string algo = "SHA256"; \
+ string join = ":"; \
+ string s = ""; \
+ FOREACH(id, true, LAMBDA(s = strcat(s, join, it.registered_id))); \
+ s = substring(s, strlen(join), -1); \
+ string h = REGISTRY_HASH(id) = strzone(digest_hex(algo, s)); \
+ LOG_TRACEF(#id ": %s\n[%s]\n", h, s); \
+ } \
+ [[accumulate]] void Registry_check(string r, string sv) \
+ { \
+ if (r == #id) \
+ { \
+ string cl = REGISTRY_HASH(id); \
+ if (cl != sv) \
+ { \
+ LOG_FATALF("client/server mismatch (%s).\nCL: %s\nSV: %s\n", r, cl, sv); \
+ } \
+ } \
+ } \
+ [[accumulate]] void Registry_send_all() { Registry_send(#id, REGISTRY_HASH(id)); } \
+
#endif
--- /dev/null
+#ifndef REGISTRY_NET_H
+#define REGISTRY_NET_H
+
+#include "net.qh"
+
+REGISTER_NET_TEMP(registry)
+
+#ifdef CSQC
+NET_HANDLE(registry, bool isnew)
+{
+ string k = ReadString();
+ string v = ReadString();
+ Registry_check(k, v);
+ return true;
+}
+#endif
+
+#ifdef SVQC
+void Registry_send(string id, string hash)
+{
+ int channel = MSG_ONE;
+ WriteHeader(channel, registry);
+ WriteString(channel, id);
+ WriteString(channel, hash);
+}
+#endif
+
+#endif
#define SORT_H
/** is only ever called for i1 < i2 */
-typedef void (float i1, float i2, entity pass) swapfunc_t;
+typedef void (int i1, int i2, entity pass) swapfunc_t;
/** <0 for <, ==0 for ==, >0 for > (like strcmp) */
-typedef float (float i1, float i2, entity pass) comparefunc_t;
+typedef int (int i1, int i2, entity pass) comparefunc_t;
-void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass)
+void heapsort(int n, swapfunc_t swap, comparefunc_t cmp, entity pass)
{
- int root, child;
+ #define heapify(_count) \
+ do \
+ { \
+ for (int start = floor(((_count) - 2) / 2); start >= 0; --start) \
+ { \
+ siftdown(start, (_count) - 1); \
+ } \
+ } \
+ while (0)
- // heapify
- int start = floor((n - 2) / 2);
- while (start >= 0)
- {
- // siftdown(start, n - 1);
- root = start;
- while (root * 2 + 1 <= n - 1)
- {
- child = root * 2 + 1;
- if (child < n - 1 && cmp(child, child + 1, pass) < 0) child += 1;
- if (cmp(root, child, pass) < 0)
- {
- swap(root, child, pass);
- root = child;
- }
- else
- {
- break;
- }
- }
- // end of siftdown
- --start;
- }
+ #define siftdown(_start, _end) \
+ do \
+ { \
+ for (int root = (_start); root * 2 + 1 <= (_end); ) \
+ { \
+ int child = root * 2 + 1; \
+ if (child < (_end) && cmp(child, child + 1, pass) < 0) child += 1; \
+ if (cmp(root, child, pass) >= 0) break; \
+ swap(root, child, pass); \
+ root = child; \
+ } \
+ } \
+ while (0)
- // extract
+ heapify(n);
int end = n - 1;
while (end > 0)
{
swap(0, end, pass);
end -= 1;
- // siftdown(0, end);
- root = 0;
- while (root * 2 + 1 <= end)
- {
- child = root * 2 + 1;
- if (child < end && cmp(child, child + 1, pass) < 0) child += 1;
- if (cmp(root, child, pass) < 0)
- {
- swap(root, child, pass);
- root = child;
- }
- else
- {
- break;
- }
- }
- // end of siftdown
+ siftdown(0, end);
}
}
entity Sort_Spawn()
{
- entity sort;
- sort = spawn();
+ entity sort = new(sortlist);
+ make_pure(sort);
sort.sort_next = NULL;
sort.chain = sort;
return sort;
void spawnfunc_##id(entity this) \
{ \
this = self; \
+ if (!this.sourceLocFile) \
+ { \
+ this.sourceLocFile = __FILE__; \
+ this.sourceLocLine = __LINE__; \
+ } \
if (!this.spawnfunc_checked) \
{ \
for (int i = 0, n = numentityfields(); i < n; ++i) \
/**/
#define FIELDS_UNION(fld) \
+ FIELD_SCALAR(fld, sourceLocFile) \
+ FIELD_SCALAR(fld, sourceLocLine) \
FIELD_SCALAR(fld, Version) \
FIELD_SCALAR(fld, ammo_cells) \
FIELD_SCALAR(fld, ammo_nails) \
#define static_init() CALL_ACCUMULATED_FUNCTION(__static_init)
void __static_init_late() {}
#define static_init_late() CALL_ACCUMULATED_FUNCTION(__static_init_late)
-
-#define REGISTER_REGISTRY(func) ACCUMULATE_FUNCTION(__static_init, func)
+void __static_init_precache() {}
+#define static_init_precache() CALL_ACCUMULATED_FUNCTION(__static_init_precache)
#define _STATIC_INIT(where, func) \
void _static_##func(); \
ACCUMULATE_FUNCTION(where, _static_##func) \
void _static_##func()
-#define STATIC_INIT(func) _STATIC_INIT(__static_init, func)
-#define STATIC_INIT_LATE(func) _STATIC_INIT(__static_init_late, func##_late)
+#define STATIC_INIT(func) _STATIC_INIT(__static_init, func)
+#define STATIC_INIT_LATE(func) _STATIC_INIT(__static_init_late, func##_late)
+#define PRECACHE(func) _STATIC_INIT(__static_init_precache, func##_precache)
#endif
--- /dev/null
+#ifndef LIB_STATS_H
+#define LIB_STATS_H
+
+#include "registry.qh"
+#include "sort.qh"
+
+.int m_id;
+
+#if defined(CSQC)
+ /** Get all stats and store them as globals, access with `STAT(ID)` */
+ void stats_get() {}
+ #define STAT(...) EVAL(OVERLOAD(STAT, __VA_ARGS__))
+ #define STAT_1(id) STAT_2(id, NULL)
+ #define STAT_2(id, cl) (0, _STAT(id))
+
+ #define getstat_int(id) getstati(id, 0, 24)
+ #define getstat_bool(id) boolean(getstati(id))
+ #define getstat_float(id) getstatf(id)
+
+ #define _STAT(id) g_stat_##id
+ #define REGISTER_STAT(id, type) \
+ type _STAT(id); \
+ REGISTER(RegisterStats, STAT, Stats, id, m_id, new(stat)) \
+ { \
+ make_pure(this); \
+ } \
+ [[accumulate]] void stats_get() \
+ { \
+ _STAT(id) = getstat_##type(STAT_##id.m_id); \
+ }
+#elif defined(SVQC)
+ /** Add all registered stats, access with `STAT(ID, player)` or `.type stat = _STAT(ID); player.stat` */
+ void stats_add() {}
+ #define STAT(id, cl) (cl._STAT(id))
+
+ #define addstat_int(id, fld) addstat(id, AS_INT, fld)
+ #define addstat_bool(id, fld) addstat(id, AS_INT, fld)
+ #define addstat_float(id, fld) addstat(id, AS_FLOAT, fld)
+ const int AS_STRING = 1;
+ const int AS_INT = 2;
+ const int AS_FLOAT = 8;
+
+ #define _STAT(id) stat_##id
+ #define REGISTER_STAT(id, type) \
+ .type _STAT(id); \
+ REGISTER(RegisterStats, STAT, Stats, id, m_id, new(stat)) \
+ { \
+ make_pure(this); \
+ } \
+ [[accumulate]] void stats_add() \
+ { \
+ addstat_##type(STAT_##id.m_id, _STAT(id)); \
+ }
+#else
+ #define REGISTER_STAT(id, type)
+#endif
+
+const int STATS_ENGINE_RESERVE = 32 + (8 * 3); // Not sure how to handle vector stats yet, reserve them too
+
+REGISTRY(Stats, 220 - STATS_ENGINE_RESERVE)
+REGISTER_REGISTRY(RegisterStats)
+REGISTRY_SORT(Stats, 0)
+REGISTRY_CHECK(Stats)
+STATIC_INIT(RegisterStats_renumber)
+{
+ FOREACH(Stats, true, LAMBDA(it.m_id = STATS_ENGINE_RESERVE + i));
+}
+#ifdef SVQC
+STATIC_INIT(stats_add) { stats_add(); }
+#endif
+
+#endif
#ifndef STRING_H
#define STRING_H
+#include "nil.qh"
+#include "sort.qh"
+#include "oo.qh"
+
#ifndef SVQC
float stringwidth_colors(string s, vector theSize)
{
}
#endif
-// Timer (#5)
-//
// TODO: macro
string seconds_tostring(float sec)
{
return sprintf("%d:%02d", minutes, sec);
}
+string format_time(float seconds)
+{
+ seconds = floor(seconds + 0.5);
+ float days = floor(seconds / 864000);
+ seconds -= days * 864000;
+ float hours = floor(seconds / 36000);
+ seconds -= hours * 36000;
+ float minutes = floor(seconds / 600);
+ seconds -= minutes * 600;
+ if (days > 0) return sprintf(_("%d days, %02d:%02d:%02d"), days, hours, minutes, seconds);
+ else return sprintf(_("%02d:%02d:%02d"), hours, minutes, seconds);
+}
+
+string mmsss(float tenths)
+{
+ tenths = floor(tenths + 0.5);
+ float minutes = floor(tenths / 600);
+ tenths -= minutes * 600;
+ string s = ftos(1000 + tenths);
+ return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1));
+}
+
+string mmssss(float hundredths)
+{
+ hundredths = floor(hundredths + 0.5);
+ float minutes = floor(hundredths / 6000);
+ hundredths -= minutes * 6000;
+ string s = ftos(10000 + hundredths);
+ return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2));
+}
+
int ColorTranslateMode;
string ColorTranslateRGB(string s)
return input;
}
-bool startsWith(string haystack, string needle)
-{
- return substring(haystack, 0, strlen(needle)) == needle;
-}
+#define startsWith(haystack, needle) (strstrofs(haystack, needle, 0) == 0)
bool startsWithNocase(string haystack, string needle)
{
return sc;
}
+/** returns first word */
string car(string s)
{
int o = strstrofs(s, " ", 0);
return substring(s, 0, o);
}
+/** returns all but first word */
string cdr(string s)
{
int o = strstrofs(s, " ", 0);
return substring(s, 0, endpos);
}
-bool strhasword(string s, string w)
-{
- return strstrofs(strcat(" ", s, " "), strcat(" ", w, " "), 0) >= 0;
-}
+#define strhasword(s, w) (strstrofs(strcat(" ", s, " "), strcat(" ", w, " "), 0) >= 0)
int u8_strsize(string s)
{
return l;
}
+bool isInvisibleString(string s)
+{
+ s = strdecolorize(s);
+ bool utf8 = cvar("utf8_enable");
+ for (int i = 0, n = strlen(s); i < n; ++i)
+ {
+ int c = str2chr(s, i);
+ switch (c)
+ {
+ case 0:
+ case 32: // space
+ break;
+ case 192: // charmap space
+ if (!utf8) break;
+ return false;
+ case 160: // space in unicode fonts
+ case 0xE000 + 192: // utf8 charmap space
+ if (utf8) break;
+ default:
+ return false;
+ }
+ }
+ return true;
+}
+
+// Multiline text file buffers
+
+int buf_load(string pFilename)
+{
+ int buf = buf_create();
+ if (buf < 0) return -1;
+ int fh = fopen(pFilename, FILE_READ);
+ if (fh < 0)
+ {
+ buf_del(buf);
+ return -1;
+ }
+ string l;
+ for (int i = 0; (l = fgets(fh)); ++i)
+ bufstr_set(buf, i, l);
+ fclose(fh);
+ return buf;
+}
+
+void buf_save(float buf, string pFilename)
+{
+ int fh = fopen(pFilename, FILE_WRITE);
+ if (fh < 0) error(strcat("Can't write buf to ", pFilename));
+ int n = buf_getsize(buf);
+ for (int i = 0; i < n; ++i)
+ fputs(fh, strcat(bufstr_get(buf, i), "\n"));
+ fclose(fh);
+}
+
+/**
+ * converts a number to a string with the indicated number of decimals
+ * works for up to 10 decimals!
+ */
+string ftos_decimals(float number, int decimals)
+{
+ // inhibit stupid negative zero
+ if (number == 0) number = 0;
+ // we have sprintf...
+ return sprintf("%.*f", decimals, number);
+}
+
+int vercmp_recursive(string v1, string v2)
+{
+ int dot1 = strstrofs(v1, ".", 0);
+ int dot2 = strstrofs(v2, ".", 0);
+ string s1 = (dot1 == -1) ? v1 : substring(v1, 0, dot1);
+ string s2 = (dot2 == -1) ? v2 : substring(v2, 0, dot2);
+
+ float r;
+ r = stof(s1) - stof(s2);
+ if (r != 0) return r;
+
+ r = strcasecmp(s1, s2);
+ if (r != 0) return r;
+
+ if (dot1 == -1) return (dot2 == -1) ? 0 : -1;
+ else return (dot2 == -1) ? 1 : vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999));
+}
+
+int vercmp(string v1, string v2)
+{
+ if (strcasecmp(v1, v2) == 0) return 0; // early out check
+
+ // "git" beats all
+ if (v1 == "git") return 1;
+ if (v2 == "git") return -1;
+
+ return vercmp_recursive(v1, v2);
+}
+
+const string HEXDIGITS = "0123456789ABCDEF0123456789abcdef";
+#define HEXDIGIT_TO_DEC_RAW(d) (strstrofs(HEXDIGITS, (d), 0))
+#define HEXDIGIT_TO_DEC(d) ((HEXDIGIT_TO_DEC_RAW(d) | 0x10) - 0x10)
+#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS, (d), 1))
+
#endif
// attempts to close will result in a reading handle
// create a writing end that does nothing yet
- e = spawn();
- e.classname = "url_single_fopen_file";
+ e = new(url_single_fopen_file);
+ make_pure(e);
e.url_url = strzone(url);
e.url_fh = URL_FH_CURL;
e.url_wbuf = buf_create();
// Make a dummy handle object (no buffers at
// all). Wait for data to come from the
// server, then call the callback
- e = spawn();
- e.classname = "url_single_fopen_file";
+ e = new(url_single_fopen_file);
+ make_pure(e);
e.url_url = strzone(url);
e.url_fh = URL_FH_CURL;
e.url_rbuf = -1;
{
case FILE_WRITE:
case FILE_APPEND:
- e = spawn();
- e.classname = "url_single_fopen_stdout";
+ e = new(url_single_fopen_stdout);
+ make_pure(e);
e.url_fh = URL_FH_STDOUT;
e.url_ready = rdy;
e.url_ready_pass = pass;
}
else
{
- e = spawn();
- e.classname = "url_single_fopen_file";
+ e = new(url_single_fopen_file);
+ make_pure(e);
e.url_fh = fh;
e.url_ready = rdy;
e.url_ready_pass = pass;
return;
}
- entity me;
- me = spawn();
- me.classname = "url_multi";
+ entity me = new(url_multi);
+ make_pure(me);
me.url_url = strzone(url);
me.url_attempt = 0;
me.url_mode = mode;
return v;
}
+vector rotate(vector v, float a)
+{
+ float a_sin = sin(a), a_cos = cos(a);
+ vector r = '0 0 0';
+ r.x = v.x * a_cos + v.y * a_sin;
+ r.y = -1 * v.x * a_sin + v.y * a_cos;
+ return r;
+}
+
+vector yinvert(vector v)
+{
+ v.y = 1 - v.y;
+ return v;
+}
+
#ifndef MENUQC
vector get_corner_position(entity box, int corner)
{
self.drawmask = MASK_NORMAL;
}
-void WarpZone_Read(float isnew)
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew)
+{
warpzone_warpzones_exist = 1;
if (!self.enemy)
{
- self.enemy = spawn();
- self.enemy.classname = "warpzone_from";
+ self.enemy = new(warpzone_from);
}
self.classname = "trigger_warpzone";
// how to draw
// engine currently wants this
self.predraw = WarpZone_Fade_PreDraw;
+ return true;
}
-void WarpZone_Camera_Read(float isnew)
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_WARPZONE_CAMERA, bool isnew)
+{
warpzone_cameras_exist = 1;
self.classname = "func_warpzone_camera";
// how to draw
// engine currently wants this
self.predraw = WarpZone_Fade_PreDraw;
+ return true;
}
void CL_RotateMoves(vector ang) = #638;
-void WarpZone_Teleported_Read(float isnew)
-{SELFPARAM();
- vector v;
+NET_HANDLE(ENT_CLIENT_WARPZONE_TELEPORTED, bool isnew)
+{
self.classname = "warpzone_teleported";
+ vector v;
v.x = ReadCoord();
v.y = ReadCoord();
v.z = ReadCoord();
- if(!isnew)
- return;
+ return = true;
+ if (!isnew) return;
self.warpzone_transform = v;
setproperty(VF_CL_VIEWANGLES, WarpZone_TransformVAngles(self, getpropertyvec(VF_CL_VIEWANGLES)));
if(checkextension("DP_CSQC_ROTATEMOVES"))
setproperty(VF_ORIGIN, org + o);
}
-void WarpZone_Init()
-{
-}
-
void WarpZone_Shutdown()
{
WarpZone_View_Outside();
#ifndef LIB_WARPZONE_CLIENT_H
#define LIB_WARPZONE_CLIENT_H
-void WarpZone_Read(float bIsNewEntity);
-void WarpZone_Camera_Read(float bIsNewEntity);
-void WarpZone_Teleported_Read(float bIsNewEntity);
-
void WarpZone_FixPMove();
void WarpZone_FixView();
-void WarpZone_Init();
void WarpZone_Shutdown();
vector warpzone_save_view_origin;
{
if(!WarpZone_trace_transform)
{
- WarpZone_trace_transform = spawn();
- WarpZone_trace_transform.classname = "warpzone_trace_transform";
+ WarpZone_trace_transform = new(warpzone_trace_transform);
+ make_pure(WarpZone_trace_transform);
}
WarpZone_Accumulator_Clear(WarpZone_trace_transform);
}
float WarpZone_TrailParticles_trace_callback_eff;
void WarpZone_TrailParticles_trace_callback(vector from, vector endpos, vector to)
{
- trailparticles(WarpZone_TrailParticles_trace_callback_own, WarpZone_TrailParticles_trace_callback_eff, from, endpos);
+ __trailparticles(WarpZone_TrailParticles_trace_callback_own, WarpZone_TrailParticles_trace_callback_eff, from, endpos);
}
void WarpZone_TrailParticles(entity own, float eff, vector org, vector end)
bool WarpZoneLib_BadEntity(entity e)
{
- string myclassname = e.classname;
- if (e.instanceOfObject) return true;
- switch(myclassname)
+ string s = e.classname;
+ if (is_pure(e)) return true;
+ switch (s)
{
- case "deathtype":
- case "weaponentity":
- case "exteriorweaponentity":
- case "csqc_score_team":
- case "pingplreport":
- case "ent_client_scoreinfo":
- case "saved_cvar_value":
- case "accuracy":
case "entcs_sender":
case "entcs_receiver":
- case "clientinit":
- case "sprite_waypoint":
- case "waypoint":
- case "gibsplash":
- //case "net_linked": // actually some real entities are linked without classname, fail
+ // case "net_linked": // actually some real entities are linked without classname, fail
case "":
return true;
}
- if(startsWith(myclassname, "msg_"))
- return true;
-
- if(startsWith(myclassname, "target_"))
- return true;
+ if (startsWith(s, "target_")) return true;
- if(startsWith(myclassname, "info_"))
- return true;
+ if (startsWith(s, "info_")) return true;
return false;
}
{
if(me.WarpZone_refsys.owner != me)
{
- me.WarpZone_refsys = spawn();
- me.WarpZone_refsys.classname = "warpzone_refsys";
+ me.WarpZone_refsys = new(warpzone_refsys);
me.WarpZone_refsys.owner = me;
me.WarpZone_refsys.think = WarpZone_RefSys_GC;
me.WarpZone_refsys.nextthink = time + 1;
bool WarpZone_Teleported_Send(entity to, int sf)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE_TELEPORTED);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_WARPZONE_TELEPORTED);
WriteCoord(MSG_ENTITY, self.angles.x);
WriteCoord(MSG_ENTITY, self.angles.y);
WriteCoord(MSG_ENTITY, self.angles.z);
// instead of fixangle, send the transform to the client for smoother operation
player.fixangle = false;
- entity ts = spawn();
+ entity ts = new(warpzone_teleported);
setmodel(ts, MDL_Null);
ts.SendEntity = WarpZone_Teleported_Send;
ts.SendFlags = 0xFFFFFF;
ts.owner = player;
ts.enemy = wz;
ts.effects = EF_NODEPTHTEST;
- ts.classname = "warpzone_teleported";
ts.angles = wz.warpzone_transform;
}
#endif
return 1;
}
-void WarpZone_Touch (void)
+void WarpZone_Touch ()
{SELFPARAM();
if(other.classname == "trigger_warpzone")
return;
bool WarpZone_Send(entity to, int sendflags)
{SELFPARAM();
- WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_WARPZONE);
// we must send this flag for clientside to match properly too
int f = 0;
bool WarpZone_Camera_Send(entity to, int sendflags)
{SELFPARAM();
int f = 0;
- WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE_CAMERA);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_WARPZONE_CAMERA);
if(self.warpzone_fadestart)
BITSET_ASSIGN(f, 2);
self.enemy.aiment = self;
}
-void WarpZoneCamera_Think(void)
+void WarpZoneCamera_Think()
{SELFPARAM();
if(self.warpzone_save_origin != self.origin
|| self.warpzone_save_angles != self.angles
spawnfunc_trigger_warpzone_reconnect(this); // both names make sense here :(
}
-void WarpZone_PlayerPhysics_FixVAngle(void)
+void WarpZone_PlayerPhysics_FixVAngle()
{SELFPARAM();
#ifndef WARPZONE_DONT_FIX_VANGLE
if(IS_REAL_CLIENT(self))
//const float ENT_CLIENT_WARPZONE;
//const float ENT_CLIENT_WARPZONE_CAMERA;
-void WarpZone_PlayerPhysics_FixVAngle(void);
+void WarpZone_PlayerPhysics_FixVAngle();
-void WarpZone_PostInitialize_Callback(void);
+void WarpZone_PostInitialize_Callback();
#endif
--- /dev/null
+#ifndef CLASSES_H
+#define CLASSES_H
+
+#include "classes.inc"
+#define IMPLEMENTATION
+#include "classes.inc"
+#undef IMPLEMENTATION
+
+#endif
#include "menu_cmd.qh"
#include "../menu.qh"
-#include "../oo/classes.qc"
+#include "../classes.qc"
+
+#include "../mutators/events.qh"
#include "../../common/command/generic.qh"
void GameCommand(string theCommand)
{
- float argc;
- argc = tokenize_console(theCommand);
+ int argc = tokenize_console(theCommand);
+ string ss = strtolower(argv(0));
if (argv(0) == "help" || argc == 0)
{
return;
}
+ if(MUTATOR_CALLHOOK(Menu_ConsoleCommand, ss, argc, theCommand)) // handled by a mutator
+ return;
+
LOG_INFO(_("Invalid command. For a list of supported commands, try menu_cmd help.\n"));
}
draw_endBoldFont();
}
-void draw_beginBoldFont()
-{
- drawfont = FONT_USER+3;
-}
-
-void draw_endBoldFont()
-{
- drawfont = FONT_USER+0;
-}
-
vector globalToBox(vector v, vector theOrigin, vector theScale)
{
v -= theOrigin;
float draw_alpha;
void draw_reset(float cw, float ch, float ox, float oy);
-void draw_beginBoldFont();
-void draw_endBoldFont();
+#define draw_beginBoldFont() do { drawfont = FONT_USER + 3; } while (0)
+#define draw_endBoldFont() do { drawfont = FONT_USER + 0; } while (0)
void draw_setMousePointer(string pic, vector theSize, vector theOffset);
void draw_drawMousePointer(vector where);
#include "xonotic/tab.qc"
-REGISTRY(Settings, BIT(3))
+REGISTRY(Settings, BITS(3))
+#define Settings_from(i) _Settings_from(i, NULL)
REGISTER_REGISTRY(RegisterSettings)
#define REGISTER_SETTINGS(id, impl) \
LAZY_NEW(id, impl) \
#include "menu.qh"
-#include "oo/classes.qc"
+#include "classes.qc"
#include "xonotic/util.qh"
#include "../common/items/all.qh"
// needs to be done so early because of the constants they create
static_init();
static_init_late();
+ static_init_precache();
RegisterSLCategories();
--- /dev/null
+#ifndef MENU_MUTATORS_EVENTS_H
+#define MENU_MUTATORS_EVENTS_H
+
+#include "../../common/mutators/base.qh"
+
+// globals
+
+string cmd_name;
+int cmd_argc;
+string cmd_string;
+
+/**
+ * Called when a menu command is parsed
+ * NOTE: hooks MUST start with if (MUTATOR_RETURNVALUE) return false;
+ * NOTE: return true if you handled the command, return false to continue handling
+ * NOTE: THESE HOOKS MUST NEVER EVER CALL tokenize()
+ * // example:
+ * MUTATOR_HOOKFUNCTION(foo, Menu_ConsoleCommand) {
+ * if (MUTATOR_RETURNVALUE) return false; // command was already handled
+ * if (cmd_name == "echocvar" && cmd_argc >= 2) {
+ * print(cvar_string(argv(1)), "\n");
+ * return true;
+ * }
+ * if (cmd_name == "echostring" && cmd_argc >= 2) {
+ * print(substring(cmd_string, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), "\n");
+ * return true;
+ * }
+ * return false;
+ * }
+ */
+#define EV_Menu_ConsoleCommand(i, o) \
+ /** command name */ i(string, cmd_name) \
+ /** also, argv() can be used */ i(int, cmd_argc) \
+ /** whole command, use only if you really have to */ i(string, cmd_string) \
+ /**/
+MUTATOR_HOOKABLE(Menu_ConsoleCommand, EV_Menu_ConsoleCommand);
+#endif
+++ /dev/null
-#ifndef CLASSES_H
-#define CLASSES_H
-
-#include "../classes.inc"
-#define IMPLEMENTATION
-#include "../classes.inc"
-#undef IMPLEMENTATION
-
-#endif
#define world NULL
-#include "oo/classes.qc"
+#include "classes.qc"
#include "draw.qc"
#include "menu.qc"
{
e = get_weaponinfo(j);
if(argv(i) == e.netname)
- s = strcat(s, " & ", e.message);
+ s = strcat(s, " & ", e.m_name);
}
}
s = sprintf(_("%s Arena"), substring(s, 3, strlen(s) - 3));
if((j & 1) == 0)
me.TR(me);
me.TDempty(me, 0.2);
- me.TD(me, 1, 1.8, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.message)));
+ me.TD(me, 1, 1.8, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.m_name)));
setDependentWeird(e, checkCompatibility_weaponarena_weapon);
++j;
}
CLASS(SettingSource, DataSource)
METHOD(SettingSource, getEntry, entity(entity this, int i, void(string name, string icon) returns))
{
- Lazy l = Settings[i];
+ Lazy l = Settings_from(i);
entity it = l.m_get();
if (returns) returns(it.title, string_null);
return it;
}
METHOD(SettingSource, getEntryTooltip, entity(entity this, int i, void(string theTooltip) returns))
{
- Lazy l = Settings[i];
+ Lazy l = Settings_from(i);
entity it = l.m_get();
if (returns) returns(it.tooltip);
return it;
#ifdef IMPLEMENTATION
-entity makeXonoticGametypeList(void)
+entity makeXonoticGametypeList()
{
entity me;
me = NEW(XonoticGametypeList);
continue;
bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_ID, argv(0));
bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_NAME, argv(1));
- float k = strstrofs(argv(2), "(", 0);
- if(k > 0)
- if(substring(argv(2), strlen(argv(2)) - 1, 1) == ")")
- {
- string percent = substring(argv(2), k + 1, -2);
- if(percent != "100%")
- bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_PERCENTAGE, percent);
- }
- bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_NAME_LOCALIZED, (k < 0) ? argv(2) : substring(argv(2), 0, k - 1));
+ bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_NAME_LOCALIZED, argv(2));
+ string percent = argv(3);
+ if(percent && percent != "100%")
+ bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_PERCENTAGE, percent);
++i;
}
fclose(fh);
#define SLIST_CATEGORY(name,enoverride,dioverride,str) \
SET_FIELD_COUNT(name, CATEGORY_FIRST, category_ent_count) \
CHECK_MAX_COUNT(name, MAX_CATEGORIES, category_ent_count, "SLIST_CATEGORY") \
- cat = spawn(); \
- categories[name - 1] = cat; \
- cat.classname = "slist_category"; \
+ cat = categories[name - 1] = new(slist_category); \
cat.cat_name = strzone(#name); \
cat.cat_enoverride_string = strzone(SLIST_CATEGORY_AUTOCVAR(name)); \
cat.cat_dioverride_string = strzone(dioverride); \
string _Nex_ExtResponseSystem_Packs;
float _Nex_ExtResponseSystem_PacksStep;
+/** engine callback */
void URI_Get_Callback(float id, float status, string data)
{
if(url_URI_Get_Callback(id, status, data))
void UpdateNotification_URI_Get_Callback(float id, float status, string data);
-void URI_Get_Callback(float id, float status, string data);
-
// game type list box stuff (does not NEED to contain all game types, other
// types stay available via console)
int GameType_GetID(int cnt);
for(i = 0; i < n; ++i)
{
e = get_weaponinfo(stof(argv(i)));
- s = strcat(s, e.message, ", ");
+ s = strcat(s, e.m_name, ", ");
}
return substring(s, 0, strlen(s) - 2);
}
draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_FOCUSED, me.focusedItemAlpha);
}
e = get_weaponinfo(stof(argv(i)));
- string msg = e.message;
+ string msg = e.m_name;
if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
msg = strcat(msg, "*");
#ifndef SERVER_ALL_H
#define SERVER_ALL_H
-#include "autocvars.qh"
-#include "constants.qh"
-#include "defs.qh"
-#include "miscfunctions.qh"
+int maxclients;
+
+const string STR_PLAYER = "player";
+const string STR_SPECTATOR = "spectator";
+const string STR_OBSERVER = "observer";
+
+#define IS_PLAYER(v) ((v).classname == STR_PLAYER)
+#define IS_SPEC(v) ((v).classname == STR_SPECTATOR)
+#define IS_OBSERVER(v) ((v).classname == STR_OBSERVER)
+
+#define IS_CLIENT(v) (v.flags & FL_CLIENT)
+#define IS_BOT_CLIENT(v) (clienttype(v) == CLIENTTYPE_BOT)
+#define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL)
+#define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT)
+
+#define IS_MONSTER(v) (v.flags & FL_MONSTER)
+#define IS_VEHICLE(v) (v.vehicle_flags & VHF_ISVEHICLE)
+#define IS_TURRET(v) (v.turret_flags & TUR_FLAG_ISTURRET)
+#define FOR_EACH_CLIENTSLOT(v) for (v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
+#define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if (IS_CLIENT(v))
+#define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if (IS_REAL_CLIENT(v))
+
+#define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if (IS_PLAYER(v))
+#define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if (IS_SPEC(v))
+#define FOR_EACH_OBSERVER(v) FOR_EACH_CLIENT(v) if (IS_OBSERVER(v))
+#define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if (IS_PLAYER(v))
+
+#define FOR_EACH_MONSTER(v) for (v = world; (v = findflags(v, flags, FL_MONSTER)) != world; )
#include "../common/effects/all.qh"
#include "../common/models/all.qh"
#include "../common/sounds/all.qh"
+#include "autocvars.qh"
+#include "constants.qh"
+#include "defs.qh"
+#include "miscfunctions.qh"
+
#endif
bool autocvar_bot_nofire;
#define autocvar_bot_number cvar("bot_number")
#define autocvar_bot_prefix cvar_string("bot_prefix")
-bool autocvar_bot_sound_monopoly;
#define autocvar_bot_suffix cvar_string("bot_suffix")
bool autocvar_bot_usemodelnames;
int autocvar_bot_vs_human;
float autocvar_g_balance_armor_rotlinear;
int autocvar_g_balance_armor_rotstable;
int autocvar_g_balance_armor_start;
-float autocvar_g_balance_cloaked_alpha;
float autocvar_g_balance_contents_damagerate;
float autocvar_g_balance_contents_drowndelay;
int autocvar_g_balance_contents_playerdamage_drowning;
float autocvar_g_balance_health_rotstable;
float autocvar_g_balance_kill_delay;
float autocvar_g_balance_kill_antispam;
-int autocvar_g_balance_nix_ammo_cells;
-int autocvar_g_balance_nix_ammo_plasma;
-int autocvar_g_balance_nix_ammo_fuel;
-int autocvar_g_balance_nix_ammo_nails;
-int autocvar_g_balance_nix_ammo_rockets;
-int autocvar_g_balance_nix_ammo_shells;
-int autocvar_g_balance_nix_ammoincr_cells;
-int autocvar_g_balance_nix_ammoincr_plasma;
-int autocvar_g_balance_nix_ammoincr_fuel;
-int autocvar_g_balance_nix_ammoincr_nails;
-int autocvar_g_balance_nix_ammoincr_rockets;
-int autocvar_g_balance_nix_ammoincr_shells;
-float autocvar_g_balance_nix_incrtime;
-float autocvar_g_balance_nix_roundtime;
float autocvar_g_balance_pause_armor_rot;
float autocvar_g_balance_pause_armor_rot_spawn;
float autocvar_g_balance_pause_fuel_regen;
float autocvar_g_maxplayers_spectator_blocktime;
float autocvar_g_maxpushtime;
float autocvar_g_maxspeed;
-float autocvar_g_midair_shieldtime;
#define autocvar_g_instagib cvar("g_instagib")
-int autocvar_g_instagib_ammo_drop;
-int autocvar_g_instagib_extralives;
-float autocvar_g_instagib_speed_highspeed;
-float autocvar_g_instagib_invis_alpha;
bool autocvar_g_instagib_damagedbycontents = true;
bool autocvar_g_instagib_blaster_keepdamage = false;
bool autocvar_g_instagib_blaster_keepforce = false;
#define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual")
float autocvar_g_movement_highspeed = 1;
-int autocvar_g_multijump;
-float autocvar_g_multijump_add;
-float autocvar_g_multijump_speed;
-float autocvar_g_multijump_maxspeed;
-float autocvar_g_multijump_dodging = 1;
string autocvar_g_mutatormsg;
//float autocvar_g_nick_flood_penalty;
int autocvar_g_nick_flood_penalty_red;
int autocvar_g_nick_flood_penalty_yellow;
//float autocvar_g_nick_flood_timeout;
-bool autocvar_g_nix_with_healtharmor;
-bool autocvar_g_nix_with_blaster;
-bool autocvar_g_nix_with_powerups;
bool autocvar_g_nodepthtestitems;
bool autocvar_g_nodepthtestplayers;
bool autocvar_g_norecoil;
float autocvar_g_items_mindist;
float autocvar_g_items_maxdist;
-int autocvar_g_pickup_cells_max;
-int autocvar_g_pickup_plasma_max;
-int autocvar_g_pickup_fuel_max;
int autocvar_g_pickup_items;
-int autocvar_g_pickup_nails_max;
-int autocvar_g_pickup_rockets_max;
-int autocvar_g_pickup_shells_max;
float autocvar_g_player_alpha;
float autocvar_g_player_brightness;
bool autocvar_g_playerclip_collisions;
float autocvar_g_respawn_ghosts_maxtime;
float autocvar_g_respawn_ghosts_speed;
int autocvar_g_respawn_waves;
-bool autocvar_g_running_guns;
bool autocvar_g_shootfromcenter;
bool autocvar_g_shootfromeye;
string autocvar_g_shootfromfixedorigin;
string autocvar_sv_defaultplayermodel_red;
string autocvar_sv_defaultplayermodel_yellow;
int autocvar_sv_defaultplayerskin;
-float autocvar_sv_dodging_delay;
-float autocvar_sv_dodging_height_threshold;
-float autocvar_sv_dodging_horiz_speed;
-float autocvar_sv_dodging_horiz_speed_frozen;
-float autocvar_sv_dodging_ramp_time;
-bool autocvar_sv_dodging_sound;
-float autocvar_sv_dodging_up_speed;
-float autocvar_sv_dodging_wall_distance_threshold;
-bool autocvar_sv_dodging_wall_dodging;
bool autocvar_sv_dodging_frozen;
-bool autocvar_sv_dodging_frozen_doubletap;
bool autocvar_sv_doublejump;
bool autocvar_sv_eventlog;
bool autocvar_sv_eventlog_console;
float autocvar_g_trueaim_minrange;
bool autocvar_g_debug_defaultsounds;
float autocvar_g_grab_range;
-int autocvar_g_sandbox_info;
-bool autocvar_g_sandbox_readonly;
-string autocvar_g_sandbox_storage_name;
-float autocvar_g_sandbox_storage_autosave;
-bool autocvar_g_sandbox_storage_autoload;
-float autocvar_g_sandbox_editor_flood;
-int autocvar_g_sandbox_editor_maxobjects;
-int autocvar_g_sandbox_editor_free;
-float autocvar_g_sandbox_editor_distance_spawn;
-float autocvar_g_sandbox_editor_distance_edit;
-float autocvar_g_sandbox_object_scale_min;
-float autocvar_g_sandbox_object_scale_max;
-float autocvar_g_sandbox_object_material_velocity_min;
-float autocvar_g_sandbox_object_material_velocity_factor;
int autocvar_g_max_info_autoscreenshot;
bool autocvar_physics_ode;
-int autocvar_g_physical_items;
-float autocvar_g_physical_items_damageforcescale;
-float autocvar_g_physical_items_reset;
float autocvar_g_monsters;
bool autocvar_g_monsters_edit;
bool autocvar_g_monsters_sounds;
float autocvar_g_monsters_miniboss_healthboost;
float autocvar_g_monsters_drop_time;
float autocvar_g_monsters_spawnshieldtime;
+bool autocvar_g_monsters_quake_resize = true;
bool autocvar_g_monsters_teams;
float autocvar_g_monsters_respawn_delay;
bool autocvar_g_monsters_respawn;
float autocvar_g_monsters_armor_blockpercent;
float autocvar_g_monsters_healthbars;
float autocvar_g_monsters_lineofsight;
-float autocvar_g_touchexplode_radius;
-float autocvar_g_touchexplode_damage;
-float autocvar_g_touchexplode_edgedamage;
-float autocvar_g_touchexplode_force;
#define autocvar_g_bloodloss cvar("g_bloodloss")
-float autocvar_g_random_gravity_negative_chance;
-float autocvar_g_random_gravity_min;
-float autocvar_g_random_gravity_max;
-float autocvar_g_random_gravity_positive;
-float autocvar_g_random_gravity_negative;
-float autocvar_g_random_gravity_delay;
bool autocvar_g_nades;
bool autocvar_g_nades_override_dropweapon = true;
vector autocvar_g_nades_throw_offset;
float autocvar_g_nades_heal_foe;
string autocvar_g_nades_pokenade_monster_type;
float autocvar_g_nades_pokenade_monster_lifetime;
-float autocvar_g_campcheck_damage;
-float autocvar_g_campcheck_distance;
-float autocvar_g_campcheck_interval;
bool autocvar_g_jump_grunt;
-bool autocvar_g_overkill_powerups_replace;
-float autocvar_g_overkill_superguns_respawn_time;
-bool autocvar_g_overkill_100h_anyway;
-bool autocvar_g_overkill_100a_anyway;
-bool autocvar_g_overkill_ammo_charge;
-float autocvar_g_overkill_ammo_charge_notice;
-float autocvar_g_overkill_ammo_charge_limit;
-float autocvar_g_spawn_near_teammate_distance;
-int autocvar_g_spawn_near_teammate_ignore_spawnpoint;
-float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
-float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
-int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
-bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
bool autocvar_g_physics_clientselect;
string autocvar_g_physics_clientselect_options;
string autocvar_g_physics_clientselect_default;
-bool autocvar_g_buffs_effects;
-float autocvar_g_buffs_waypoint_distance;
-bool autocvar_g_buffs_randomize;
-float autocvar_g_buffs_random_lifetime;
-bool autocvar_g_buffs_random_location;
-int autocvar_g_buffs_random_location_attempts;
-int autocvar_g_buffs_spawn_count;
-bool autocvar_g_buffs_replace_powerups;
-float autocvar_g_buffs_cooldown_activate;
-float autocvar_g_buffs_cooldown_respawn;
-float autocvar_g_buffs_resistance_blockpercent;
-float autocvar_g_buffs_medic_survive_chance;
-float autocvar_g_buffs_medic_survive_health;
-float autocvar_g_buffs_medic_rot;
-float autocvar_g_buffs_medic_max;
-float autocvar_g_buffs_medic_regen;
-float autocvar_g_buffs_vengeance_damage_multiplier;
-float autocvar_g_buffs_bash_force;
-float autocvar_g_buffs_bash_force_self;
-float autocvar_g_buffs_disability_slowtime;
-float autocvar_g_buffs_disability_speed;
-float autocvar_g_buffs_disability_rate;
-float autocvar_g_buffs_disability_weaponspeed;
-float autocvar_g_buffs_speed_speed;
-float autocvar_g_buffs_speed_rate;
-float autocvar_g_buffs_speed_weaponspeed;
-float autocvar_g_buffs_speed_damage_take;
-float autocvar_g_buffs_speed_regen;
-float autocvar_g_buffs_vampire_damage_steal;
-float autocvar_g_buffs_invisible_alpha;
-float autocvar_g_buffs_flight_gravity;
-float autocvar_g_buffs_jump_height;
bool autocvar_sv_minigames;
bool autocvar_sv_minigames_observer;
-float autocvar_g_buffs_inferno_burntime_factor;
-float autocvar_g_buffs_inferno_burntime_min_time;
-float autocvar_g_buffs_inferno_burntime_target_damage;
-float autocvar_g_buffs_inferno_burntime_target_time;
-float autocvar_g_buffs_inferno_damagemultiplier;
-float autocvar_g_buffs_swapper_range;
-float autocvar_g_buffs_magnet_range_item;
float autocvar_sv_player_scale;
float autocvar_g_rm;
float autocvar_g_rm_damage;
if (targ.solid < SOLID_BBOX) // SOLID_NOT and SOLID_TRIGGER
return false; // could never hit it
if (!tracetossent)
- tracetossent = spawn();
+ tracetossent = new(tracetossent);
tracetossent.owner = ignore;
setsize(tracetossent, m1, m2);
savesolid = targ.solid;
}
if (!tracetossfaketarget)
- tracetossfaketarget = spawn();
+ tracetossfaketarget = new(tracetossfaketarget);
tracetossfaketarget.solid = savesolid;
tracetossfaketarget.movetype = targ.movetype;
_setmodel(tracetossfaketarget, targ.model); // no low precision
head.totalfrags_lastcheck = head.totalfrags;
}
-void bot_calculate_stepheightvec(void)
+void bot_calculate_stepheightvec()
{
stepheightvec = autocvar_sv_stepheight * '0 0 1';
jumpstepheightvec = stepheightvec +
void() havocbot_setupbot;
-void bot_calculate_stepheightvec(void);
+void bot_calculate_stepheightvec();
#endif
// Should it do a weapon combo?
float af, ct, combo_time, combo;
- af = ATTACK_FINISHED(self);
+ af = ATTACK_FINISHED(self, 0);
ct = autocvar_bot_ai_weapon_combo_threshold;
// Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos
.entity tuba_note;
float bot_cmd_debug_assert_canfire()
{SELFPARAM();
- float f;
- f = bot_cmd.bot_cmd_parm_float;
+ float f = bot_cmd.bot_cmd_parm_float;
- if(self.weaponentity.state != WS_READY)
+ int slot = 0;
+ .entity weaponentity = weaponentities[slot];
+ if(self.(weaponentity).state != WS_READY)
{
if(f)
{
LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by weaponentity state\n");
}
}
- else if(ATTACK_FINISHED(self) > time)
+ else if(ATTACK_FINISHED(self, slot) > time)
{
if(f)
{
self.colormod = '8 0 8';
- LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self) - time), " seconds left)\n");
+ LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self, slot) - time), " seconds left)\n");
}
}
else if(self.tuba_note)
if(!f)
{
self.colormod = '8 8 0';
- LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(self) - time), " seconds left\n");
+ LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(self, slot) - time), " seconds left\n");
}
}
if(!self.bot_cmd_current)
{
- self.bot_cmd_current = spawn();
- self.bot_cmd_current.classname = "bot_cmd";
- self.bot_cmd_current.is_bot_cmd = 1;
+ self.bot_cmd_current = new(bot_cmd);
+ self.bot_cmd_current.is_bot_cmd = true;
}
bot_cmd = self.bot_cmd_current;
w = find(w, classname, "waypoint");
}
- w = spawn();
+ w = new(waypoint);
+ make_pure(w);
w.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
- w.classname = "waypoint";
w.wpflags = f;
setorigin(w, (m1 + m2) * 0.5);
setsize(w, m1 - w.origin, m2 - w.origin);
// shared with regular waypoint init, so this is not a cheat by itself
if(!self.personal)
{
- self.personal = spawn();
- self.personal.classname = "personal_wp";
+ self.personal = new(personal_wp);
}
self.personal.origin = self.origin;
self.personal.v_angle = self.v_angle;
effectnum = _particleeffectnum(argv(1));
W_SetupShot(self, false, false, "", CH_WEAPON_A, 0);
traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, self);
- trailparticles(self, effectnum, w_shotorg, trace_endpos);
+ __trailparticles(self, effectnum, w_shotorg, trace_endpos);
DID_CHEAT();
break;
}
break;
case "dragbox_spawn": {
IS_CHEAT(0, argc, 0);
- entity e = spawn();
- e.classname = "dragbox_box";
+ entity e = new(dragbox_box);
e.think = DragBox_Think;
e.nextthink = time;
e.solid = -1; // black
else
e.cnt = max(0, drag_lastcnt);
- e.aiment = spawn();
- e.aiment.classname = "dragbox_corner_1";
+ e.aiment = new(dragbox_corner_1);
e.aiment.owner = e;
setmodel(e.aiment, MDL_MARKER);
e.aiment.skin = 0;
setorigin(e.aiment, trace_endpos);
}
- e.enemy = spawn();
- e.enemy.classname = "dragbox_corner_2";
+ e.enemy = new(dragbox_corner_2);
e.enemy.owner = e;
setmodel(e.enemy, MDL_MARKER);
e.enemy.skin = 1;
else
setorigin(e.enemy, e.aiment.origin + 32 * end);
- e.killindicator = spawn();
- e.killindicator.classname = "drag_digit";
+ e.killindicator = new(drag_digit);
e.killindicator.owner = e;
setattachment(e.killindicator, e, "");
setorigin(e.killindicator, '0 0 -8');
- e.killindicator.killindicator = spawn();
- e.killindicator.killindicator.classname = "drag_digit";
+ e.killindicator.killindicator = new(drag_digit);
e.killindicator.killindicator.owner = e;
setattachment(e.killindicator.killindicator, e, "");
setorigin(e.killindicator.killindicator, '0 0 8');
}
case "dragpoint_spawn": {
IS_CHEAT(0, argc, 0);
- entity e = spawn();
- e.classname = "dragpoint";
+ entity e = new(dragpoint);
e.think = DragBox_Think;
e.nextthink = time;
e.solid = 0; // nothing special
move_out_of_solid(e);
}
- e.killindicator = spawn();
- e.killindicator.classname = "drag_digit";
+ e.killindicator = new(drag_digit);
e.killindicator.owner = e;
setattachment(e.killindicator, e, "");
setorigin(e.killindicator, '0 0 40');
- e.killindicator.killindicator = spawn();
- e.killindicator.killindicator.classname = "drag_digit";
+ e.killindicator.killindicator = new(drag_digit);
e.killindicator.killindicator.owner = e;
setattachment(e.killindicator.killindicator, e, "");
setorigin(e.killindicator.killindicator, '0 0 56');
break;
case "teleporttotarget":
IS_CHEAT(0, argc, 0);
- setself(spawn());
+ setself(new(cheattriggerteleport));
setorigin(self, self.origin);
- self.classname = "cheattriggerteleport";
self.target = argv(1);
teleport_findtarget();
if(!wasfreed(self))
draggee.ltime = max(servertime + serverframetime, draggee.ltime); // fixes func_train breakage
vector vecs = '0 0 0';
- if(dragger.weaponentity.movedir_x > 0)
- vecs = dragger.weaponentity.movedir;
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+ if(dragger.(weaponentity).movedir.x > 0)
+ vecs = dragger.(weaponentity).movedir;
vector dv = v_right * -vecs_y + v_up * vecs_z;
void send_CSQC_teamnagger() {
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
+ WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
}
bool ClientData_Send(entity this, entity to, int sf)
if(e.porto_v_angle_held)
sf |= 8; // angles held
- WriteByte(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
WriteByte(MSG_ENTITY, sf);
if(sf & 2)
void ClientData_Attach()
{SELFPARAM();
- Net_LinkEntity(self.clientdata = spawn(), false, 0, ClientData_Send);
- self.clientdata.drawonlytoclient = self;
- self.clientdata.owner = self;
+ Net_LinkEntity(this.clientdata = new(clientdata), false, 0, ClientData_Send);
+ make_pure(this.clientdata);
+ self.clientdata.drawonlytoclient = this;
+ self.clientdata.owner = this;
}
void ClientData_Detach()
precache_model(modelname);
_setmodel(e, modelname);
player_setupanimsformodel();
- UpdatePlayerSounds();
+ UpdatePlayerSounds(e);
}
/*
self.weaponname = "";
self.switchingweapon = 0;
self.weaponmodel = "";
- self.weaponentity = world;
+ for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ self.weaponentities[slot] = NULL;
+ }
self.exteriorweaponentity = world;
self.killcount = FRAGS_SPECTATOR;
self.velocity = '0 0 0';
self.event_damage = func_null;
}
+int player_getspecies(entity this)
+{
+ get_model_parameters(this.model, this.skin);
+ int s = get_model_parameters_species;
+ get_model_parameters(string_null, 0);
+ if (s < 0) return SPECIES_HUMAN;
+ return s;
+}
+
.float model_randomizer;
void FixPlayermodel(entity player)
{
if(chmdl || oldskin != player.skin) // model or skin has changed
{
- player.species = player_getspecies(); // update species
- UpdatePlayerSounds(); // update skin sounds
+ player.species = player_getspecies(player); // update species
+ UpdatePlayerSounds(player); // update skin sounds
}
if(!teamplay)
this.revival_time = 0;
this.air_finished = time + 12;
- entity spawnevent = spawn();
+ entity spawnevent = new(spawnevent);
+ make_pure(spawnevent);
spawnevent.owner = this;
Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send);
this.killcount = 0;
}
- CL_SpawnWeaponentity(this);
+ for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ CL_SpawnWeaponentity(this, weaponentities[slot]);
+ }
this.alpha = default_player_alpha;
this.colormod = '1 1 1' * autocvar_g_player_brightness;
this.exteriorweaponentity.alpha = default_weapon_alpha;
// changes and just have a console command to update this?
bool ClientInit_SendEntity(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_INIT);
- WriteByte(MSG_ENTITY, g_nexball_meter_period * 32);
- WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[0]));
- WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[1]));
- WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[2]));
- WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[3]));
- WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[0]));
- WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[1]));
- WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[2]));
- WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[3]));
+ WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT);
+ return = true;
+ msg_entity = to;
+ Registry_send_all();
+ int channel = MSG_ONE;
+ WriteHeader(channel, ENT_CLIENT_INIT);
+ WriteByte(channel, g_nexball_meter_period * 32);
+ WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0]));
+ WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1]));
+ WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2]));
+ WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3]));
+ WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0]));
+ WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1]));
+ WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2]));
+ WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3]));
if(sv_foginterval && world.fog != "")
- WriteString(MSG_ENTITY, world.fog);
+ WriteString(channel, world.fog);
else
- WriteString(MSG_ENTITY, "");
- WriteByte(MSG_ENTITY, self.count * 255.0); // g_balance_armor_blockpercent
- WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO
- WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_mortar_bouncestop
- WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_mortar_bouncefactor
- WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_mortar_bouncestop
- WriteByte(MSG_ENTITY, WEP_CVAR(vortex, secondary)); // client has to know if it should zoom or not // WEAPONTODO
- WriteByte(MSG_ENTITY, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO
- WriteByte(MSG_ENTITY, serverflags); // client has to know if it should zoom or not
- WriteByte(MSG_ENTITY, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO
- WriteByte(MSG_ENTITY, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO
- WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange);
- WriteByte(MSG_ENTITY, WEP_CVAR(porto, secondary)); // WEAPONTODO
- return true;
+ WriteString(channel, "");
+ WriteByte(channel, self.count * 255.0); // g_balance_armor_blockpercent
+ WriteCoord(channel, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO
+ WriteCoord(channel, self.bouncestop); // g_balance_mortar_bouncestop
+ WriteCoord(channel, self.ebouncefactor); // g_balance_mortar_bouncefactor
+ WriteCoord(channel, self.ebouncestop); // g_balance_mortar_bouncestop
+ WriteByte(channel, WEP_CVAR(vortex, secondary)); // client has to know if it should zoom or not // WEAPONTODO
+ WriteByte(channel, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO
+ WriteByte(channel, serverflags); // client has to know if it should zoom or not
+ WriteByte(channel, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO
+ WriteByte(channel, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO
+ WriteCoord(channel, autocvar_g_trueaim_minrange);
+ WriteByte(channel, WEP_CVAR(porto, secondary)); // WEAPONTODO
+
+ MUTATOR_CALLHOOK(Ent_Init);
}
void ClientInit_CheckUpdate()
void ClientInit_Spawn()
{SELFPARAM();
- entity e = spawn();
- e.classname = "clientinit";
+
+ entity e = new(clientinit);
+ make_pure(e);
e.think = ClientInit_CheckUpdate;
Net_LinkEntity(e, false, 0, ClientInit_SendEntity);
SetNewParms
=============
*/
-void SetNewParms (void)
+void SetNewParms ()
{
// initialize parms for a new player
parm1 = -(86400 * 366);
SetChangeParms
=============
*/
-void SetChangeParms (void)
+void SetChangeParms ()
{SELFPARAM();
// save parms for level change
parm1 = self.parm_idlesince - time;
DecodeLevelParms
=============
*/
-void DecodeLevelParms (void)
+void DecodeLevelParms ()
{SELFPARAM();
// load parms
self.parm_idlesince = parm1;
}
-void ClientKill (void)
+void ClientKill ()
{SELFPARAM();
if(gameover) return;
if(self.player_blocked) return;
Called once (not at each match start) when a client begins a connection to the server
=============
*/
-void ClientPreConnect (void)
+void ClientPreConnect ()
{SELFPARAM();
if(autocvar_sv_eventlog)
{
Called when a client connects to the server
=============
*/
-void DecodeLevelParms (void);
-void ClientConnect (void)
+void DecodeLevelParms ();
+void ClientConnect ()
{SELFPARAM();
float t;
*/
.entity chatbubbleentity;
void ReadyCount();
-void ClientDisconnect (void)
+void ClientDisconnect ()
{SELFPARAM();
if(self.vehicle)
vehicles_exit(VHEF_RELEASE);
if(self.weaponorder_byimpulse)
strunzone(self.weaponorder_byimpulse);
- ClearPlayerSounds();
+ ClearPlayerSounds(self);
if(self.personal)
remove(self.personal);
// spawn a chatbubble entity if needed
if (!self.chatbubbleentity)
{
- self.chatbubbleentity = spawn();
+ self.chatbubbleentity = new(chatbubbleentity);
self.chatbubbleentity.owner = self;
self.chatbubbleentity.exteriormodeltoclient = self;
self.chatbubbleentity.think = ChatBubbleThink;
else self.colormod = '1 1 1';
}*/
-void respawn(void)
+void respawn()
{SELFPARAM();
if(self.alpha >= 0 && autocvar_g_respawn_ghosts)
{
_sound (self, CH_INFO, samp, VOL_BASE, ATTEN_NORM);
}
-void player_powerups (void)
+void player_powerups ()
{SELFPARAM();
// add a way to see what the items were BEFORE all of these checks for the mutator hook
int items_prev = self.items;
return current;
}
-void player_regen (void)
+void player_regen ()
{SELFPARAM();
float max_mod, regen_mod, rot_mod, limit_mod;
max_mod = regen_mod = rot_mod = limit_mod = 1;
}
void GetPressedKeys()
-{SELFPARAM();
+{
+ SELFPARAM();
MUTATOR_CALLHOOK(GetPressedKeys);
- #define X(var,bit,flag) (flag ? var |= bit : var &= ~bit)
- X(self.pressedkeys, KEY_FORWARD, self.movement_x > 0);
- X(self.pressedkeys, KEY_BACKWARD, self.movement_x < 0);
- X(self.pressedkeys, KEY_RIGHT, self.movement_y > 0);
- X(self.pressedkeys, KEY_LEFT, self.movement_y < 0);
-
- X(self.pressedkeys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(self));
- X(self.pressedkeys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(self));
- X(self.pressedkeys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(self));
- X(self.pressedkeys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(self));
- #undef X
+ int keys = this.pressedkeys;
+ keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0);
+ keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0);
+ keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0);
+ keys = BITSET(keys, KEY_LEFT, this.movement.y < 0);
+
+ keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this));
+ keys = BITSET(keys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(this));
+ keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this));
+ keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this));
+ this.pressedkeys = keys;
}
/*
if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
{
self.classname = STR_PLAYER;
- nades_RemoveBonus(self);
if(autocvar_g_campaign || autocvar_g_balance_teams)
{ JoinBestTeam(self, false, true); }
{
if(self.BUTTON_INFO) // BUTTON_INFO hides initial MOTD
self.motd_actived_time = -2; // wait until BUTTON_INFO gets released
- else if(self.motd_actived_time == -2 || IS_PLAYER(self))
+ else if(self.motd_actived_time == -2 || IS_PLAYER(self) || IS_SPEC(self))
{
// instanctly hide MOTD
self.motd_actived_time = 0;
MUTATOR_CALLHOOK(PlayerUseKey);
}
-float isInvisibleString(string s)
-{
- float i, n, c;
- s = strdecolorize(s);
- for((i = 0), (n = strlen(s)); i < n; ++i)
- {
- c = str2chr(s, i);
- switch(c)
- {
- case 0:
- case 32: // space
- break;
- case 192: // charmap space
- if (!autocvar_utf8_enable)
- break;
- return false;
- case 160: // space in unicode fonts
- case 0xE000 + 192: // utf8 charmap space
- if (autocvar_utf8_enable)
- break;
- default:
- return false;
- }
- }
- return true;
-}
/*
=============
void() nexball_setstatus;
.float last_vehiclecheck;
.int items_added;
-void PlayerPreThink (void)
+void PlayerPreThink ()
{SELFPARAM();
WarpZone_PlayerPhysics_FixVAngle();
// WEAPONTODO: THIS SHIT NEEDS TO GO EVENTUALLY
// It cannot be predicted by the engine!
- if((self.weapon == WEP_SHOCKWAVE.m_id || self.weapon == WEP_SHOTGUN.m_id) && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+ if((self.weapon == WEP_SHOCKWAVE.m_id || self.weapon == WEP_SHOTGUN.m_id) && self.(weaponentity).wframe == WFRAME_FIRE2 && time < self.(weaponentity).weapon_nextthink)
do_crouch = 0;
if (do_crouch)
=============
*/
.float idlekick_lasttimeleft;
-void PlayerPostThink (void)
+void PlayerPostThink ()
{SELFPARAM();
if(sv_maxidle > 0 && frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero).
if(IS_REAL_CLIENT(self))
* 230 to 253: individual weapons (up to 24)
*/
-void ImpulseCommands (void)
+void ImpulseCommands ()
{SELFPARAM();
int imp;
vector org;
break;
}
}
- else
- self.impulse = imp; // retry in next frame
+ //else
+ //self.impulse = imp; // retry in next frame
}
else if(imp == 21)
{
m = (imp - (210 + i)); // <0 for prev, =0 for best, >0 for next
W_CycleWeapon(self.(cvar_cl_weaponpriorities[i]), m);
}
- else
- self.impulse = imp; // retry in next frame
+ //else
+ //self.impulse = imp; // retry in next frame
}
else if(imp >= WEP_IMPULSE_BEGIN && imp <= WEP_IMPULSE_END)
{
if(!self.vehicle)
if(self.deadflag == DEAD_NO)
W_SwitchWeapon (imp - WEP_IMPULSE_BEGIN + WEP_FIRST);
- else
- self.impulse = imp; // retry in next frame
+ //else
+ //self.impulse = imp; // retry in next frame
}
// deploy waypoints
else if (imp >= 30 && imp <= 49)
* 230 to 253: individual weapons (up to 24)
*/
-void ImpulseCommands (void);
+void ImpulseCommands ();
#endif
#include "cheats.qh"
#include "g_damage.qh"
#include "g_subs.qh"
-#include "g_violence.qh"
#include "miscfunctions.qh"
#include "portals.qh"
#include "teamplay.qh"
MUTATOR_CALLHOOK(DropSpecialItems, player);
}
-void CopyBody_Think(void)
+void CopyBody_Think()
{SELFPARAM();
if(self.CopyBody_nextthink && time > self.CopyBody_nextthink)
{
{SELFPARAM();
if (self.effects & EF_NODRAW)
return;
- setself(spawn());
+ setself(new(body));
self.enemy = this;
self.lip = this.lip;
self.colormap = this.colormap;
self.angles = this.angles;
self.v_angle = this.v_angle;
self.avelocity = this.avelocity;
- self.classname = "body";
self.damageforcescale = this.damageforcescale;
self.effects = this.effects;
self.glowmod = this.glowmod;
setself(this);
}
-float player_getspecies()
-{SELFPARAM();
- float s;
- get_model_parameters(self.model, self.skin);
- s = get_model_parameters_species;
- get_model_parameters(string_null, 0);
- if(s < 0)
- return SPECIES_HUMAN;
- return s;
-}
-
void player_setupanimsformodel()
{SELFPARAM();
// load animation info
animdecide_setstate(self, 0, false);
}
-void player_anim (void)
+void player_anim ()
{SELFPARAM();
int deadbits = (self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2));
if(self.deadflag) {
animdecide_setstate(self, animbits, false);
animdecide_setimplicitstate(self, (self.flags & FL_ONGROUND));
- if (self.weaponentity)
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
{
- updateanim(self.weaponentity);
- if (!self.weaponentity.animstate_override)
- setanim(self.weaponentity, self.weaponentity.anim_idle, true, false, false);
+ if (self.(weaponentity))
+ {
+ updateanim(self.(weaponentity));
+ if (!self.(weaponentity).animstate_override)
+ setanim(self.(weaponentity), self.(weaponentity).anim_idle, true, false, false);
+ }
}
}
PlayerScore_Add(self, SP_DMGTAKEN, realdmg);
}
}
-
+
bool abot = (IS_BOT_CLIENT(attacker));
bool vbot = (IS_BOT_CLIENT(self));
{
Weapon w = get_weaponinfo(j);
w.wr_resetplayer(w);
- ATTACK_FINISHED_FOR(self, j) = 0;
+ for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ ATTACK_FINISHED_FOR(self, j, slot) = 0;
+ }
}
}
}
-float Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol)
-// message "": do not say, just test flood control
-// return value:
-// 1 = accept
-// 0 = reject
-// -1 = fake accept
+void MoveToTeam(entity client, int team_colour, int type)
+{
+ int lockteams_backup = lockteams; // backup any team lock
+ lockteams = 0; // disable locked teams
+ TeamchangeFrags(client); // move the players frags
+ SetPlayerColors(client, team_colour - 1); // set the players colour
+ Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0'); // kill the player
+ lockteams = lockteams_backup; // restore the team lock
+ LogTeamchange(client.playerid, client.team, type);
+}
+
+/**
+ * message "": do not say, just test flood control
+ * return value:
+ * 1 = accept
+ * 0 = reject
+ * -1 = fake accept
+ */
+int Say(entity source, float teamsay, entity privatesay, string msgin, bool floodcontrol)
{
string msgstr, colorstr, cmsgstr, namestr, fullmsgstr, sourcemsgstr, fullcmsgstr, sourcecmsgstr, colorprefix;
float flood;
return ret;
}
-float GetVoiceMessageVoiceType(string type)
+int GetVoiceMessageVoiceType(string type)
{
- if(type == "taunt")
- return VOICETYPE_TAUNT;
- if(type == "teamshoot")
- return VOICETYPE_LASTATTACKER;
+ if (type == "taunt") return VOICETYPE_TAUNT;
+ if (type == "teamshoot") return VOICETYPE_LASTATTACKER;
return VOICETYPE_TEAMRADIO;
}
.string GetVoiceMessageSampleField(string type)
{
- GetPlayerSoundSampleField_notFound = 0;
- switch(type)
+ GetPlayerSoundSampleField_notFound = false;
+ switch (type)
{
-#define _VOICEMSG(m) case #m: return playersound_##m;
- ALLVOICEMSGS
-#undef _VOICEMSG
+#define X(m) case #m: return playersound_##m;
+ ALLVOICEMSGS(X)
+#undef X
}
- GetPlayerSoundSampleField_notFound = 1;
+ GetPlayerSoundSampleField_notFound = true;
return playersound_taunt;
}
.string GetPlayerSoundSampleField(string type)
{
- GetPlayerSoundSampleField_notFound = 0;
- switch(type)
+ GetPlayerSoundSampleField_notFound = false;
+ switch (type)
{
-#define _VOICEMSG(m) case #m: return playersound_##m;
- ALLPLAYERSOUNDS
-#undef _VOICEMSG
+#define X(m) case #m: return playersound_##m;
+ ALLPLAYERSOUNDS(X)
+#undef X
}
- GetPlayerSoundSampleField_notFound = 1;
+ GetPlayerSoundSampleField_notFound = true;
return playersound_taunt;
}
-void PrecacheGlobalSound(string samplestring)
+void PrecacheGlobalSound(string sample)
{
- float n, i;
- tokenize_console(samplestring);
- n = stof(argv(1));
- if(n > 0)
+ int n;
+ {
+ string s = cdr(sample);
+ if (s) n = stof(s);
+ else n = 0;
+ }
+ sample = car(sample);
+ if (n > 0)
{
- for(i = 1; i <= n; ++i)
- precache_sound(strcat(argv(0), ftos(i), ".wav"));
+ for (int i = 1; i <= n; ++i)
+ precache_sound(sprintf("%s%d.wav", sample, i));
}
else
{
- precache_sound(strcat(argv(0), ".wav"));
+ precache_sound(sprintf("%s.wav", sample));
}
}
+string allvoicesamples;
+
void PrecachePlayerSounds(string f)
{
int fh = fopen(f, FILE_READ);
if (fh < 0)
+ {
+ LOG_WARNINGF("Player sound file not found: %s\n", f);
return;
+ }
for (string s; (s = fgets(fh)); )
{
int n = tokenize_console(s);
if (n != 3)
{
- if (n != 0) LOG_TRACEF("Invalid sound info line: %s\n", s);
+ if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s);
continue;
}
- PrecacheGlobalSound(strcat(argv(1), " ", argv(2)));
+ string file = argv(1);
+ string variants = argv(2);
+ PrecacheGlobalSound(strcat(file, " ", variants));
}
fclose(fh);
if (!allvoicesamples)
{
-#define _VOICEMSG(m) allvoicesamples = strcat(allvoicesamples, " ", #m);
- ALLVOICEMSGS
-#undef _VOICEMSG
- allvoicesamples = strzone(substring(allvoicesamples, 1, strlen(allvoicesamples) - 1));
+#define X(m) allvoicesamples = strcat(allvoicesamples, " ", #m);
+ ALLVOICEMSGS(X)
+#undef X
+ allvoicesamples = strzone(substring(allvoicesamples, 1, -1));
}
}
-void ClearPlayerSounds()
-{SELFPARAM();
-#define _VOICEMSG(m) if(self.playersound_##m) { strunzone(self.playersound_##m); self.playersound_##m = string_null; }
- ALLPLAYERSOUNDS
- ALLVOICEMSGS
-#undef _VOICEMSG
+void ClearPlayerSounds(entity this)
+{
+#define X(m) if (this.playersound_##m) { strunzone(this.playersound_##m); this.playersound_##m = string_null; }
+ ALLPLAYERSOUNDS(X)
+ ALLVOICEMSGS(X)
+#undef X
}
-float LoadPlayerSounds(string f, float first)
-{SELFPARAM();
- float fh;
- string s;
- var .string field;
- fh = fopen(f, FILE_READ);
- if(fh < 0)
+bool LoadPlayerSounds(string f, bool strict)
+{
+ SELFPARAM();
+ int fh = fopen(f, FILE_READ);
+ if (fh < 0)
{
- LOG_TRACE("Player sound file not found: ", f, "\n");
- return 0;
+ if (strict) LOG_WARNINGF("Player sound file not found: %s\n", f);
+ return false;
}
- while((s = fgets(fh)))
+ for (string s; (s = fgets(fh)); )
{
- if(tokenize_console(s) != 3)
+ int n = tokenize_console(s);
+ if (n != 3)
+ {
+ if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s);
continue;
- field = GetPlayerSoundSampleField(argv(0));
- if(GetPlayerSoundSampleField_notFound)
- field = GetVoiceMessageSampleField(argv(0));
- if(GetPlayerSoundSampleField_notFound)
+ }
+ string key = argv(0);
+ var .string field = GetPlayerSoundSampleField(key);
+ if (GetPlayerSoundSampleField_notFound) field = GetVoiceMessageSampleField(key);
+ if (GetPlayerSoundSampleField_notFound)
+ {
+ LOG_TRACEF("Invalid sound info field: %s\n", key);
continue;
- if (self.(field))
- strunzone(self.(field));
- self.(field) = strzone(strcat(argv(1), " ", argv(2)));
+ }
+ string file = argv(1);
+ string variants = argv(2);
+ if (self.(field)) strunzone(self.(field));
+ self.(field) = strzone(strcat(file, " ", variants));
}
fclose(fh);
- return 1;
-}
-
-void UpdatePlayerSounds()
-{SELFPARAM();
- if(self.modelindex == self.modelindex_for_playersound)
- if(self.skin == self.skin_for_playersound)
- return;
- self.modelindex_for_playersound = self.modelindex;
- self.skin_for_playersound = self.skin;
- ClearPlayerSounds();
- LoadPlayerSounds("sound/player/default.sounds", 1);
- if(!autocvar_g_debug_defaultsounds)
- if(!LoadPlayerSounds(get_model_datafilename(self.model, self.skin, "sounds"), 0))
- LoadPlayerSounds(get_model_datafilename(self.model, 0, "sounds"), 0);
+ return true;
}
-void FakeGlobalSound(string sample, float chan, float voicetype)
-{SELFPARAM();
- float n;
- float tauntrand;
-
- if(sample == "")
- return;
+.int modelindex_for_playersound;
+.int skin_for_playersound;
- tokenize_console(sample);
- n = stof(argv(1));
- if(n > 0)
- sample = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
- else
- sample = strcat(argv(0), ".wav"); // randomization
+void UpdatePlayerSounds(entity this)
+{
+ if (this.modelindex == this.modelindex_for_playersound && this.skin == this.skin_for_playersound) return;
+ this.modelindex_for_playersound = this.modelindex;
+ this.skin_for_playersound = this.skin;
+ ClearPlayerSounds(this);
+ LoadPlayerSounds("sound/player/default.sounds", true);
+ if (autocvar_g_debug_defaultsounds) return;
+ if (!LoadPlayerSounds(get_model_datafilename(this.model, this.skin, "sounds"), false))
+ LoadPlayerSounds(get_model_datafilename(this.model, 0, "sounds"), true);
+}
- switch(voicetype)
+void _GlobalSound(string sample, int chan, int voicetype, bool fake)
+{
+ SELFPARAM();
+ if (sample == "") return;
+ int n;
+ {
+ string s = cdr(sample);
+ if (s) n = stof(s);
+ else n = 0;
+ }
+ sample = car(sample);
+ if (n > 0) sample = sprintf("%s%d.wav", sample, floor(random() * n + 1)); // randomization
+ else sample = sprintf("%s.wav", sample);
+ switch (voicetype)
{
case VOICETYPE_LASTATTACKER_ONLY:
- break;
case VOICETYPE_LASTATTACKER:
- if(self.pusher)
+ {
+ if (!fake)
{
- msg_entity = self;
- if(IS_REAL_CLIENT(msg_entity))
- soundto(MSG_ONE, self, chan, sample, VOL_BASE, ATTEN_NONE);
+ if (!this.pusher) break;
+ msg_entity = this.pusher;
+ if (IS_REAL_CLIENT(msg_entity))
+ {
+ float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE;
+ soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten);
+ }
}
+ if (voicetype == VOICETYPE_LASTATTACKER_ONLY) break;
+ msg_entity = this;
+ if (IS_REAL_CLIENT(msg_entity)) soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NONE);
break;
+ }
case VOICETYPE_TEAMRADIO:
- msg_entity = self;
- if(msg_entity.cvar_cl_voice_directional == 1)
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_MIN);
+ {
+ #define X() \
+ do \
+ { \
+ float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; \
+ soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \
+ } \
+ while (0)
+
+ if (fake) { msg_entity = this; X(); }
else
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE);
- break;
- case VOICETYPE_AUTOTAUNT:
- if(!sv_autotaunt)
- break;
- if(!sv_taunt)
- break;
- if(autocvar_sv_gentle)
- break;
- tauntrand = random();
- msg_entity = self;
- if (tauntrand < msg_entity.cvar_cl_autotaunt)
{
- if (msg_entity.cvar_cl_voice_directional >= 1)
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX));
- else
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE);
+ FOR_EACH_REALCLIENT(msg_entity)
+ {
+ if (!teamplay || msg_entity.team == this.team) X();
+ }
}
+ #undef X
break;
+ }
+ case VOICETYPE_AUTOTAUNT:
case VOICETYPE_TAUNT:
- if(IS_PLAYER(self))
- if(self.deadflag == DEAD_NO)
- animdecide_setaction(self, ANIMACTION_TAUNT, true);
- if(!sv_taunt)
- break;
- if(autocvar_sv_gentle)
- break;
- msg_entity = self;
- if (msg_entity.cvar_cl_voice_directional >= 1)
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX));
+ {
+ if (voicetype == VOICETYPE_AUTOTAUNT) if (!sv_autotaunt) { break; }else {}
+ else if (IS_PLAYER(this) && this.deadflag == DEAD_NO) animdecide_setaction(this, ANIMACTION_TAUNT, true);
+ if (!sv_taunt) break;
+ if (autocvar_sv_gentle) break;
+ float tauntrand = 0;
+ if (voicetype == VOICETYPE_AUTOTAUNT) tauntrand = random();
+ #define X() \
+ do \
+ { \
+ if (voicetype != VOICETYPE_AUTOTAUNT || tauntrand < msg_entity.cvar_cl_autotaunt) \
+ { \
+ float atten = (msg_entity.cvar_cl_voice_directional >= 1) \
+ ? bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX) \
+ : ATTEN_NONE; \
+ soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \
+ } \
+ } \
+ while (0)
+ if (fake)
+ {
+ msg_entity = this;
+ X();
+ }
else
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE);
- break;
- case VOICETYPE_PLAYERSOUND:
- msg_entity = self;
- soundto(MSG_ONE, self, chan, sample, VOL_BASE, ATTEN_NORM);
- break;
- default:
- backtrace("Invalid voice type!");
- break;
- }
-}
-
-void GlobalSound(string sample, float chan, float voicetype)
-{SELFPARAM();
- float n;
- float tauntrand;
-
- if(sample == "")
- return;
-
- tokenize_console(sample);
- n = stof(argv(1));
- if(n > 0)
- sample = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization
- else
- sample = strcat(argv(0), ".wav"); // randomization
-
- switch(voicetype)
- {
- case VOICETYPE_LASTATTACKER_ONLY:
- if(self.pusher)
{
- msg_entity = self.pusher;
- if(IS_REAL_CLIENT(msg_entity))
+ FOR_EACH_REALCLIENT(msg_entity)
{
- if(msg_entity.cvar_cl_voice_directional == 1)
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_MIN);
- else
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE);
+ X();
}
}
+ #undef X
break;
- case VOICETYPE_LASTATTACKER:
- if(self.pusher)
+ }
+ case VOICETYPE_PLAYERSOUND:
+ {
+ if (fake)
{
- msg_entity = self.pusher;
- if(IS_REAL_CLIENT(msg_entity))
- {
- if(msg_entity.cvar_cl_voice_directional == 1)
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_MIN);
- else
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE);
- }
- msg_entity = self;
- if(IS_REAL_CLIENT(msg_entity))
- soundto(MSG_ONE, self, chan, sample, VOL_BASE, ATTEN_NONE);
+ msg_entity = this;
+ soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NORM);
}
- break;
- case VOICETYPE_TEAMRADIO:
- FOR_EACH_REALCLIENT(msg_entity)
- if(!teamplay || msg_entity.team == self.team)
- {
- if(msg_entity.cvar_cl_voice_directional == 1)
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_MIN);
- else
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE);
- }
- break;
- case VOICETYPE_AUTOTAUNT:
- if(!sv_autotaunt)
- break;
- if(!sv_taunt)
- break;
- if(autocvar_sv_gentle)
- break;
- tauntrand = random();
- FOR_EACH_REALCLIENT(msg_entity)
- if (tauntrand < msg_entity.cvar_cl_autotaunt)
- {
- if (msg_entity.cvar_cl_voice_directional >= 1)
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX));
- else
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE);
- }
- break;
- case VOICETYPE_TAUNT:
- if(IS_PLAYER(self))
- if(self.deadflag == DEAD_NO)
- animdecide_setaction(self, ANIMACTION_TAUNT, true);
- if(!sv_taunt)
- break;
- if(autocvar_sv_gentle)
- break;
- FOR_EACH_REALCLIENT(msg_entity)
+ else
{
- if (msg_entity.cvar_cl_voice_directional >= 1)
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX));
- else
- soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE);
+ _sound(this, chan, sample, VOL_BASE, ATTEN_NORM);
}
break;
- case VOICETYPE_PLAYERSOUND:
- _sound(self, chan, sample, VOL_BASE, ATTEN_NORM);
- break;
+ }
default:
+ {
backtrace("Invalid voice type!");
break;
+ }
}
}
-void PlayerSound(.string samplefield, float chan, float voicetype)
-{SELFPARAM();
- GlobalSound(self.(samplefield), chan, voicetype);
+void PlayerSound(.string samplefield, int chan, float voicetype)
+{
+ SELFPARAM();
+ _GlobalSound(this.(samplefield), chan, voicetype, false);
}
void VoiceMessage(string type, string msg)
-{SELFPARAM();
- float voicetype, ownteam;
- float flood;
+{
+ SELFPARAM();
var .string sample = GetVoiceMessageSampleField(type);
-
- if(GetPlayerSoundSampleField_notFound)
+ if (GetPlayerSoundSampleField_notFound)
{
- sprint(self, strcat("Invalid voice. Use one of: ", allvoicesamples, "\n"));
+ sprint(this, sprintf("Invalid voice. Use one of: %s\n", allvoicesamples));
return;
}
-
- voicetype = GetVoiceMessageVoiceType(type);
- ownteam = (voicetype == VOICETYPE_TEAMRADIO);
-
- flood = Say(self, ownteam, world, msg, 1);
-
- if (IS_SPEC(self) || IS_OBSERVER(self) || flood < 0)
- FakeGlobalSound(self.(sample), CH_VOICE, voicetype);
- else if (flood > 0)
- GlobalSound(self.(sample), CH_VOICE, voicetype);
-}
-
-void MoveToTeam(entity client, float team_colour, float type)
-{
- float lockteams_backup;
-
- lockteams_backup = lockteams; // backup any team lock
-
- lockteams = 0; // disable locked teams
-
- TeamchangeFrags(client); // move the players frags
- SetPlayerColors(client, team_colour - 1); // set the players colour
- Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0'); // kill the player
-
- lockteams = lockteams_backup; // restore the team lock
-
- LogTeamchange(client.playerid, client.team, type);
+ int voicetype = GetVoiceMessageVoiceType(type);
+ bool ownteam = (voicetype == VOICETYPE_TEAMRADIO);
+ int flood = Say(this, ownteam, world, msg, true);
+ bool fake;
+ if (IS_SPEC(this) || IS_OBSERVER(this) || flood < 0) fake = true;
+ else if (flood > 0) fake = false;
+ else return;
+ _GlobalSound(this.(sample), CH_VOICE, voicetype, fake);
}
.float istypefrag;
.float CopyBody_nextthink;
-.void(void) CopyBody_think;
-void CopyBody_Think(void);
+.void() CopyBody_think;
+void CopyBody_Think();
void CopyBody(float keepvelocity);
-float player_getspecies();
-
void player_setupanimsformodel();
-void player_anim (void);
+void player_anim();
-void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
+void PlayerCorpseDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
// g_<gametype>_str:
// If 0, default is used.
// For consistency, negative values there are mapped to zero too.
#define GAMETYPE_DEFAULTED_SETTING(str) \
((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \
- (gametype_setting_tmp < 0) ? 0 : \
- (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) : \
- gametype_setting_tmp)
-
+ (gametype_setting_tmp < 0) ? 0 \
+ : (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) \
+ : gametype_setting_tmp)
void calculate_player_respawn_time();
void ClientKill_Now_TeamChange();
-void PlayerDamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
-
-.float muted; // to be used by prvm_edictset server playernumber muted 1
-float Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol);
-// message "": do not say, just test flood control
-// return value:
-// 1 = accept
-// 0 = reject
-// -1 = fake accept
+void MoveToTeam(entity client, float team_colour, float type);
+void PlayerDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
+
+/** to be used by `prvm_edictset server playernumber muted 1` */
+.float muted;
+int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol);
+
+// player sounds, voice messages
+// TODO implemented fall and falling
+#define ALLPLAYERSOUNDS(X) \
+ X(death) \
+ X(drown) \
+ X(fall) \
+ X(falling) \
+ X(gasp) \
+ X(jump) \
+ X(pain100) \
+ X(pain25) \
+ X(pain50) \
+ X(pain75)
+
+#define ALLVOICEMSGS(X) \
+ X(attack) \
+ X(attackinfive) \
+ X(coverme) \
+ X(defend) \
+ X(freelance) \
+ X(incoming) \
+ X(meet) \
+ X(needhelp) \
+ X(seenflag) \
+ X(taunt) \
+ X(teamshoot)
+
+// reserved sound names for the future (some models lack sounds for them):
+// _VOICEMSG(flagcarriertakingdamage)
+// _VOICEMSG(getflag)
+// reserved sound names for the future (ALL models lack sounds for them):
+// _VOICEMSG(affirmative)
+// _VOICEMSG(attacking)
+// _VOICEMSG(defending)
+// _VOICEMSG(roaming)
+// _VOICEMSG(onmyway)
+// _VOICEMSG(droppedflag)
+// _VOICEMSG(negative)
+// _VOICEMSG(seenenemy)
+
+#define X(m) .string playersound_##m;
+ALLPLAYERSOUNDS(X)
+ALLVOICEMSGS(X)
+#undef X
+
+bool GetPlayerSoundSampleField_notFound;
float GetVoiceMessageVoiceType(string type);
-
-string allvoicesamples;
.string GetVoiceMessageSampleField(string type);
-
.string GetPlayerSoundSampleField(string type);
-
void PrecacheGlobalSound(string samplestring);
-
void PrecachePlayerSounds(string f);
-
-void ClearPlayerSounds();
-
-float LoadPlayerSounds(string f, float first);
-
-.int modelindex_for_playersound;
-.int skin_for_playersound;
-void UpdatePlayerSounds();
-
-void FakeGlobalSound(string sample, float chan, float voicetype);
-
-void GlobalSound(string sample, float chan, float voicetype);
-
+void ClearPlayerSounds(entity this);
+float LoadPlayerSounds(string f, bool strict);
+void UpdatePlayerSounds(entity this);
+#define FakeGlobalSound(sample, chan, voicetype) _GlobalSound(sample, chan, voicetype, true)
+void _GlobalSound(string sample, float chan, float voicetype, bool fake);
+#define GlobalSound(def, chan, voicetype) _GlobalSound((def).m_globalsoundstr, chan, voicetype, false)
void PlayerSound(.string samplefield, float chan, float voicetype);
-
void VoiceMessage(string type, string msg);
-void MoveToTeam(entity client, float team_colour, float type);
+.string m_globalsoundstr;
+REGISTRY(GlobalSounds, BITS(8) - 1)
+#define GlobalSounds_from(i) _GlobalSounds_from(i, NULL)
+#define REGISTER_GLOBALSOUND(id, str) \
+ REGISTER(RegisterGlobalSounds, GS, GlobalSounds, id, m_id, new(GlobalSound)) \
+ { \
+ make_pure(this); \
+ this.m_globalsoundstr = str; \
+ }
+REGISTER_REGISTRY(RegisterGlobalSounds)
+REGISTRY_SORT(GlobalSounds, 0)
+REGISTRY_CHECK(GlobalSounds)
+PRECACHE(GlobalSounds)
+{
+ FOREACH(GlobalSounds, true, LAMBDA(PrecacheGlobalSound(it.m_globalsoundstr)));
+}
+
+REGISTER_GLOBALSOUND(STEP, "misc/footstep0 6")
+REGISTER_GLOBALSOUND(STEP_METAL, "misc/metalfootstep0 6")
+REGISTER_GLOBALSOUND(FALL, "misc/hitground 4")
+REGISTER_GLOBALSOUND(FALL_METAL, "misc/metalhitground 4")
+
#endif
// allow functions to be used in other code like g_world.qc and race.qc
string getrecords(float page);
-string getrankings(void);
-string getladder(void);
-string getmaplist(void);
-string getlsmaps(void);
-string getmonsterlist(void);
+string getrankings();
+string getladder();
+string getmaplist();
+string getlsmaps();
+string getmonsterlist();
#endif
if (!radarmapper)
{
- radarmapper = spawn();
- radarmapper.classname = "radarmapper";
+ radarmapper = new(radarmapper);
radarmapper.think = RadarMap_Think;
radarmapper.nextthink = time;
radarmapper.count = 8; // default to the --trace method, as it is faster now
#include "../../common/monsters/sv_monsters.qh"
-void PutObserverInServer(void);
+void PutObserverInServer();
// =====================================================
// Server side game commands code, reworked by Samual
tmp_entity = spawn();
if (argv(1) == "w")
{
- _setmodel(tmp_entity, (nextent(world)).weaponentity.model);
+ .entity weaponentity = weaponentities[0];
+ _setmodel(tmp_entity, (nextent(world)).(weaponentity).model);
}
else
{
tmp_entity = spawn();
if (argv(1) == "w")
{
- _setmodel(tmp_entity, (nextent(world)).weaponentity.model);
+ .entity weaponentity = weaponentities[0];
+ _setmodel(tmp_entity, (nextent(world)).(weaponentity).model);
}
else
{
{
entity tmp_entity;
- tmp_entity = spawn();
- tmp_entity.classname = "make_mapinfo";
+ tmp_entity = new(make_mapinfo);
tmp_entity.think = make_mapinfo_Think;
tmp_entity.nextthink = time;
MapInfo_Enumerate();
vv = stov(argv(2));
dv = stov(argv(3));
traceline(vv, dv, MOVE_NORMAL, world);
- trailparticles(world, particleeffectnum(EFFECT_TR_NEXUIZPLASMA), vv, trace_endpos);
- trailparticles(world, particleeffectnum(EFFECT_TR_CRYLINKPLASMA), trace_endpos, dv);
+ __trailparticles(world, particleeffectnum(EFFECT_TR_NEXUIZPLASMA), vv, trace_endpos);
+ __trailparticles(world, particleeffectnum(EFFECT_TR_CRYLINKPLASMA), trace_endpos, dv);
return;
}
}
{
int nags, i, f, b;
entity e;
- WriteByte(MSG_ENTITY, ENT_CLIENT_NAGGER);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_NAGGER);
// bits:
// 1 = ready
void Nagger_Init()
{
- Net_LinkEntity(nagger = spawn(), false, 0, Nagger_SendEntity);
+ Net_LinkEntity(nagger = new(nagger), false, 0, Nagger_SendEntity);
+ make_pure(nagger);
}
void Nagger_VoteChanged()
const int FL_SPAWNING = BIT(18);
const int FL_PICKUPITEMS = BIT(19);
-const int SVC_SOUND = 6;
-const int SVC_STOPSOUND = 16;
const int SVC_SETVIEW = 5;
const int RESPAWN_FORCE = 1;
#define EFMASK_CHEAP (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NODRAW | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT)
-const int MSG_ENTITY = 5; // csqc
-
const int NUM_PLAYERSKINS_TEAMPLAY = 3;
const int ASSAULT_VALUE_INACTIVE = 1000;
+++ /dev/null
-#include "controlpoint.qh"
-
-#include "command/common.qh"
-
-.bool iscaptured;
-
-bool cpicon_send(entity this, entity to, int sf)
-{
- WriteByte(MSG_ENTITY, ENT_CLIENT_CONTROLPOINT_ICON);
- WriteByte(MSG_ENTITY, sf);
- if(sf & CPSF_SETUP)
- {
- WriteCoord(MSG_ENTITY, self.origin_x);
- WriteCoord(MSG_ENTITY, self.origin_y);
- WriteCoord(MSG_ENTITY, self.origin_z);
-
- WriteByte(MSG_ENTITY, self.health);
- WriteByte(MSG_ENTITY, self.max_health);
- WriteByte(MSG_ENTITY, self.count);
- WriteByte(MSG_ENTITY, self.team);
- WriteByte(MSG_ENTITY, self.owner.iscaptured);
- }
-
- if(sf & CPSF_STATUS)
- {
- WriteByte(MSG_ENTITY, self.team);
-
- if(self.health <= 0)
- WriteByte(MSG_ENTITY, 0);
- else
- WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255));
- }
-
- return true;
-}
-
-void onslaught_controlpoint_icon_link(entity e, void() spawnproc)
-{
- Net_LinkEntity(e, true, 0, cpicon_send);
- e.think = spawnproc;
- e.nextthink = time * sys_frametime;
-}
+++ /dev/null
-#ifndef CONTROLPOINT_H
-#define CONTROLPOINT_H
-
-const vector CPICON_MIN = '-32 -32 -9';
-const vector CPICON_MAX = '32 32 25';
-
-const int CPSF_STATUS = 4;
-const int CPSF_SETUP = 8;
-
-#endif
+++ /dev/null
-#if defined(CSQC)
-#elif defined(MENUQC)
-#elif defined(SVQC)
- #include "../common/constants.qh"
-#endif
-
-void te_csqc_lightningarc(vector from,vector to)
-{
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_ARC);
-
- WriteCoord(MSG_BROADCAST, from.x);
- WriteCoord(MSG_BROADCAST, from.y);
- WriteCoord(MSG_BROADCAST, from.z);
- WriteCoord(MSG_BROADCAST, to.x);
- WriteCoord(MSG_BROADCAST, to.y);
- WriteCoord(MSG_BROADCAST, to.z);
-}
-
+++ /dev/null
-#ifndef CSQCEFFECTS_H
-#define CSQCEFFECTS_H
-void te_csqc_lightningarc(vector from,vector to);
-#endif
#define SERVER_DEFS_H
#include "../common/weapons/all.qh"
+#include "../common/stats.qh"
-#define INDEPENDENT_ATTACK_FINISHED
+#define INDEPENDENT_ATTACK_FINISHED 1
#define BUTTON_ATCK button0
#define BUTTON_JUMP button2
// Globals
-float g_cloaked, g_footsteps, g_grappling_hook, g_instagib;
+float g_footsteps, g_grappling_hook, g_instagib;
float g_warmup_limit;
float g_warmup_allguns;
float g_warmup_allow_timeout;
float warmup_stage;
-float g_pickup_respawntime_weapon;
-float g_pickup_respawntime_superweapon;
-float g_pickup_respawntime_ammo;
-float g_pickup_respawntime_short;
-float g_pickup_respawntime_medium;
-float g_pickup_respawntime_long;
-float g_pickup_respawntime_powerup;
-float g_pickup_respawntimejitter_weapon;
-float g_pickup_respawntimejitter_superweapon;
-float g_pickup_respawntimejitter_ammo;
-float g_pickup_respawntimejitter_short;
-float g_pickup_respawntimejitter_medium;
-float g_pickup_respawntimejitter_long;
-float g_pickup_respawntimejitter_powerup;
+PROPERTY(float, g_pickup_respawntime_weapon)
+PROPERTY(float, g_pickup_respawntime_superweapon)
+PROPERTY(float, g_pickup_respawntime_ammo)
+PROPERTY(float, g_pickup_respawntime_short)
+PROPERTY(float, g_pickup_respawntime_medium)
+PROPERTY(float, g_pickup_respawntime_long)
+PROPERTY(float, g_pickup_respawntime_powerup)
+PROPERTY(float, g_pickup_respawntimejitter_weapon)
+PROPERTY(float, g_pickup_respawntimejitter_superweapon)
+PROPERTY(float, g_pickup_respawntimejitter_ammo)
+PROPERTY(float, g_pickup_respawntimejitter_short)
+PROPERTY(float, g_pickup_respawntimejitter_medium)
+PROPERTY(float, g_pickup_respawntimejitter_long)
+PROPERTY(float, g_pickup_respawntimejitter_powerup)
float g_jetpack;
float sv_clones;
float team1_score, team2_score, team3_score, team4_score;
-float maxclients;
-
// flag set on worldspawn so that the code knows if it is dedicated or not
float server_is_dedicated;
.float pain_frame; //"
.float crouch; // Crouching or not?
-.float strength_finished;
-.float invincible_finished;
+.float strength_finished = _STAT(STRENGTH_FINISHED);
+.float invincible_finished = _STAT(INVINCIBLE_FINISHED);
.float superweapons_finished;
.float cnt; // used in too many places
// string overrides entity
.string item_pickupsound;
.entity item_pickupsound_ent;
+.entity item_model_ent;
// definitions for weaponsystem
// more WEAPONTODO: move these to their proper files
-.entity weaponentity;
.entity exteriorweaponentity;
.vector weaponentity_glowmod;
//.int weapon; // current weapon
-.int switchweapon; // weapon requested to switch to
+.int switchweapon = _STAT(SWITCHWEAPON);
.int switchingweapon; // weapon currently being switched to (is copied from switchweapon once switch is possible)
.string weaponname; // name of .weapon
// WEAPONTODO
.float autoswitch;
float client_hasweapon(entity cl, float wpn, float andammo, float complain);
-void w_clear(Weapon thiswep, entity actor, bool fire1, bool fire2);
-void w_ready(Weapon thiswep, entity actor, bool fire1, bool fire2);
+void w_clear(Weapon thiswep, entity actor, .entity weaponentity, int fire);
+void w_ready(Weapon thiswep, entity actor, .entity weaponentity, int fire);
// VorteX: standalone think for weapons, so normal think on weaponentity can be reserved by weaponflashes (which needs update even player dies)
.float weapon_nextthink;
-.void(Weapon thiswep, entity actor, bool fire1, bool fire2) weapon_think;
+.void(Weapon thiswep, entity actor, .entity weaponentity, int fire) weapon_think;
// weapon states (self.weaponentity.state)
// there is 2 weapon tics that can run in one server frame
const int W_TICSPERFRAME = 2;
-void weapon_defaultspawnfunc(float wpn);
+void weapon_defaultspawnfunc(entity this, Weapon e);
float gameover;
float intermission_running;
float bot_waypoints_for_items;
-.float attack_finished_for[Weapons_MAX];
-.float attack_finished_single;
-#ifdef INDEPENDENT_ATTACK_FINISHED
-#define ATTACK_FINISHED_FOR(ent,w) ((ent).(attack_finished_for[(w) - WEP_FIRST]))
+.float attack_finished_for[Weapons_MAX * MAX_WEAPONSLOTS];
+.float attack_finished_single[MAX_WEAPONSLOTS];
+#if INDEPENDENT_ATTACK_FINISHED
+#define ATTACK_FINISHED_FOR(ent, w, slot) ((ent).(attack_finished_for[((w) - WEP_FIRST) * MAX_WEAPONSLOTS + (slot)]))
#else
-#define ATTACK_FINISHED_FOR(ent,w) ((ent).attack_finished_single)
+#define ATTACK_FINISHED_FOR(ent, w, slot) ((ent).attack_finished_single[slot])
#endif
-#define ATTACK_FINISHED(ent) ATTACK_FINISHED_FOR(ent,(ent).weapon)
+#define ATTACK_FINISHED(ent, slot) ATTACK_FINISHED_FOR(ent, (ent).weapon, slot)
// assault game mode: Which team is attacking in this round?
float assault_attacker_team;
float next_pingtime;
-// player sounds, voice messages
-// TODO implemented fall and falling
-#define ALLPLAYERSOUNDS \
- _VOICEMSG(death) \
- _VOICEMSG(drown) \
- _VOICEMSG(fall) \
- _VOICEMSG(falling) \
- _VOICEMSG(gasp) \
- _VOICEMSG(jump) \
- _VOICEMSG(pain100) \
- _VOICEMSG(pain25) \
- _VOICEMSG(pain50) \
- _VOICEMSG(pain75)
-
-#define ALLVOICEMSGS \
- _VOICEMSG(attack) \
- _VOICEMSG(attackinfive) \
- _VOICEMSG(coverme) \
- _VOICEMSG(defend) \
- _VOICEMSG(freelance) \
- _VOICEMSG(incoming) \
- _VOICEMSG(meet) \
- _VOICEMSG(needhelp) \
- _VOICEMSG(seenflag) \
- _VOICEMSG(taunt) \
- _VOICEMSG(teamshoot)
-
-#define _VOICEMSG(m) .string playersound_##m;
-ALLPLAYERSOUNDS
-ALLVOICEMSGS
-#undef _VOICEMSG
-
-// reserved sound names for the future (some models lack sounds for them):
-// _VOICEMSG(flagcarriertakingdamage) \
-// _VOICEMSG(getflag) \
-// reserved sound names for the future (ALL models lack sounds for them):
-// _VOICEMSG(affirmative) \
-// _VOICEMSG(attacking) \
-// _VOICEMSG(defending) \
-// _VOICEMSG(roaming) \
-// _VOICEMSG(onmyway) \
-// _VOICEMSG(droppedflag) \
-// _VOICEMSG(negative) \
-// _VOICEMSG(seenenemy) \
-// /**/
-
-string globalsound_fall;
-string globalsound_metalfall;
-string globalsound_step;
-string globalsound_metalstep;
-
const float VOICETYPE_PLAYERSOUND = 10;
const float VOICETYPE_TEAMRADIO = 11;
const float VOICETYPE_LASTATTACKER = 12;
const float VOICETYPE_AUTOTAUNT = 14;
const float VOICETYPE_TAUNT = 15;
-void PrecachePlayerSounds(string f);
-void PrecacheGlobalSound(string samplestring);
-void UpdatePlayerSounds();
-void ClearPlayerSounds();
-void PlayerSound(.string samplefield, float channel, float voicetype);
-void GlobalSound(string samplestring, float channel, float voicetype);
-void FakeGlobalSound(string samplestring, float channel, float voicetype);
-void VoiceMessage(string type, string message);
-float GetPlayerSoundSampleField_notFound;
-.string GetVoiceMessageSampleField(string type);
-
// autotaunt system
.float cvar_cl_autotaunt;
.float cvar_cl_voice_directional;
float game_starttime; //point in time when the countdown to game start is over
float round_starttime; //point in time when the countdown to round start is over
-.float stat_game_starttime;
+.float stat_game_starttime = _STAT(GAMESTARTTIME);
.float stat_round_starttime;
void W_Porto_Remove (entity p);
bool entcs_send(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_ENTCS);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_ENTCS);
WriteByte(MSG_ENTITY, sf);
if(sf & BIT(0))
WriteByte(MSG_ENTITY, num_for_edict(self.owner) - 1);
entity attach_entcs(entity e)
{
entity ent = e.entcs = new(entcs_sender);
+ make_pure(ent);
ent.owner = e;
ent.think = entcs_think;
ent.nextthink = time;
#include "../lib/csqcmodel/sv_model.qh"
#include "../lib/warpzone/common.qh"
-bool Damage_DamageInfo_SendEntity(entity this, entity to, int sf)
-{
- WriteByte(MSG_ENTITY, ENT_CLIENT_DAMAGEINFO);
- WriteShort(MSG_ENTITY, self.projectiledeathtype);
- WriteCoord(MSG_ENTITY, floor(self.origin.x));
- WriteCoord(MSG_ENTITY, floor(self.origin.y));
- WriteCoord(MSG_ENTITY, floor(self.origin.z));
- WriteByte(MSG_ENTITY, bound(1, self.dmg, 255));
- WriteByte(MSG_ENTITY, bound(0, self.dmg_radius, 255));
- WriteByte(MSG_ENTITY, bound(1, self.dmg_edge, 255));
- WriteShort(MSG_ENTITY, self.oldorigin.x);
- WriteByte(MSG_ENTITY, self.species);
- return true;
-}
-
-void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner)
-{
- // TODO maybe call this from non-edgedamage too?
- // TODO maybe make the client do the particle effects for the weapons and the impact sounds using this info?
-
- entity e;
-
- if(!sound_allowed(MSG_BROADCAST, dmgowner))
- deathtype |= 0x8000;
-
- e = spawn();
- setorigin(e, org);
- e.projectiledeathtype = deathtype;
- e.dmg = coredamage;
- e.dmg_edge = edgedamage;
- e.dmg_radius = rad;
- e.dmg_force = vlen(force);
- e.velocity = force;
- e.oldorigin_x = compressShortVector(e.velocity);
- e.species = bloodtype;
-
- Net_LinkEntity(e, false, 0.2, Damage_DamageInfo_SendEntity);
-}
-
void UpdateFrags(entity player, float f)
{
PlayerTeamScore_AddScore(player, f);
{
if(!GiveFrags_randomweapons)
{
- GiveFrags_randomweapons = spawn();
- GiveFrags_randomweapons.classname = "GiveFrags_randomweapons";
+ GiveFrags_randomweapons = new(GiveFrags_randomweapons);
}
if(warmup_stage)
{
if(DEATH_ISSPECIAL(deathtype))
{
- entity deathent = Deathtypes[deathtype - DT_FIRST];
+ entity deathent = Deathtypes_from(deathtype - DT_FIRST);
if (!deathent) { backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n"); return; }
if(murder)
targ.revive_speed = freeze_time;
self.bot_attack = false;
- entity ice, head;
- ice = spawn();
+ entity ice = new(ice);
ice.owner = targ;
- ice.classname = "ice";
ice.scale = targ.scale;
ice.think = Ice_Think;
ice.nextthink = time;
RemoveGrapplingHook(targ);
+ entity head;
FOR_EACH_PLAYER(head)
if(head.hook.aiment == targ)
RemoveGrapplingHook(head);
vector farce = damage_explosion_calcpush(self.damageforcescale * force, self.velocity, autocvar_g_balance_damagepush_speedfactor);
if(self.movetype == MOVETYPE_PHYSICS)
{
- entity farcent;
- farcent = spawn();
- farcent.classname = "farce";
+ entity farcent = new(farce);
farcent.enemy = self;
farcent.movedir = farce * 10;
if(self.mass)
if(!e.fire_burner)
{
// print("adding a fire burner to ", e.classname, "\n");
- e.fire_burner = spawn();
- e.fire_burner.classname = "fireburner";
+ e.fire_burner = new(fireburner);
e.fire_burner.think = fireburner_think;
e.fire_burner.nextthink = time;
e.fire_burner.owner = e;
//pl.disableclientprediction = false;
}
-void GrapplingHookReset(void)
+void GrapplingHookReset()
{SELFPARAM();
if(self.realowner.hook == self)
RemoveGrapplingHook(self.owner);
.vector hook_start, hook_end;
bool GrapplingHookSend(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_HOOK);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_HOOK);
sf = sf & 0x7F;
if(sound_allowed(MSG_BROADCAST, self.realowner))
sf |= 0x80;
}
}
-void GrapplingHookTouch (void)
+void GrapplingHookTouch ()
{SELFPARAM();
if(other.movetype == MOVETYPE_FOLLOW)
return;
}
}
-void FireGrapplingHook (void)
+void FireGrapplingHook ()
{SELFPARAM();
entity missile;
vector org;
.float modelscale;
-void g_model_setcolormaptoactivator (void)
+void g_model_setcolormaptoactivator ()
{SELFPARAM();
if(teamplay)
{
self.colormap |= BIT(10); // RENDER_COLORMAPPED
}
-void g_clientmodel_setcolormaptoactivator (void)
+void g_clientmodel_setcolormaptoactivator ()
{SELFPARAM();
g_model_setcolormaptoactivator();
self.SendFlags |= (BIT(3) | BIT(0));
}
-void g_clientmodel_use(void)
+void g_clientmodel_use()
{SELFPARAM();
if (self.antiwall_flag == 1)
{
if(self.lodmodelindex1)
sf |= 0x80;
- WriteByte(MSG_ENTITY, ENT_CLIENT_WALL);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_WALL);
WriteByte(MSG_ENTITY, sf);
- if(sf & 1)
+ if(sf & BIT(0))
{
if(sf & 0x40)
WriteShort(MSG_ENTITY, self.colormap);
}
- if(sf & 2)
+ if(sf & BIT(1))
{
WriteCoord(MSG_ENTITY, self.origin.x);
WriteCoord(MSG_ENTITY, self.origin.y);
WriteCoord(MSG_ENTITY, self.origin.z);
}
- if(sf & 4)
+ if(sf & BIT(2))
{
if(sf & 0x10)
{
}
}
- if(sf & 8)
+ if(sf & BIT(3))
{
if(sf & 0x80)
{
unused but required by the engine
==================
*/
-void main (void)
+void main ()
{
}
#ifndef G_SUBS_H
#define G_SUBS_H
-void SUB_NullThink(void);
+void SUB_NullThink();
void() SUB_CalcMoveDone;
void() SUB_CalcAngleMoveDone;
Remove self
==================
*/
-void SUB_Remove (void);
+void SUB_Remove ();
/*
==================
==================
*/
.float friction;
-void SUB_Friction (void);
+void SUB_Friction ();
/*
==================
*/
void SUB_VanishOrRemove (entity ent);
-void SUB_SetFade_Think (void);
+void SUB_SetFade_Think ();
/*
==================
self.origin traveling at speed
===============
*/
-void SUB_CalcMoveDone (void);
+void SUB_CalcMoveDone ();
.float platmovetype_turn;
-void SUB_CalcMove_controller_think (void);
+void SUB_CalcMove_controller_think ();
void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest);
The calling function should make sure self.think is valid
===============
*/
-void SUB_CalcAngleMoveDone (void);
+void SUB_CalcAngleMoveDone ();
// FIXME: I fixed this function only for rotation around the main axes
void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func);
unused but required by the engine
==================
*/
-void main (void);
+void main ();
// Misc
*/
vector findbetterlocation (vector org, float mindist);
-/*
-==================
-crandom
-
-Returns a random number between -1.0 and 1.0
-==================
-*/
-float crandom (void);
-
/*
==================
Angc used for animations
+++ /dev/null
-#include "g_violence.qh"
-
-.int state;
-
-bool Violence_GibSplash_SendEntity(entity this, entity to, int sf)
-{
- WriteByte(MSG_ENTITY, ENT_CLIENT_GIBSPLASH);
- WriteByte(MSG_ENTITY, self.state); // actually type
- WriteByte(MSG_ENTITY, bound(1, self.cnt * 16, 255)); // gibbage amount multiplier
- WriteShort(MSG_ENTITY, floor(self.origin.x / 4)); // not using a coord here, as gibs don't need this accuracy
- WriteShort(MSG_ENTITY, floor(self.origin.y / 4)); // not using a coord here, as gibs don't need this accuracy
- WriteShort(MSG_ENTITY, floor(self.origin.z / 4)); // not using a coord here, as gibs don't need this accuracy
- WriteShort(MSG_ENTITY, self.oldorigin.x); // acrually compressed velocity
- return true;
-}
-
-// TODO maybe convert this to a TE?
-void Violence_GibSplash_At(vector org, vector dir, float type, float amount, entity gibowner, entity attacker)
-{SELFPARAM();
- if(g_cts) // no gibs in CTS
- return;
-
- entity e;
-
- e = spawn();
- e.classname = "gibsplash";
- e.cnt = amount;
- e.state = type; // should stay smaller than 15
- if(!sound_allowed(MSG_BROADCAST, gibowner) || !sound_allowed(MSG_BROADCAST, attacker))
- e.state |= 0x40; // "silence" bit
- e.state |= 8 * self.species; // gib type, ranges from 0 to 15
-
- // if this is a copied dead body, send the num of its player instead
- // TODO: remove this field, read from model txt files
- if(self.classname == "body")
- e.team = num_for_edict(self.enemy);
- else
- e.team = num_for_edict(self);
-
- setorigin(e, org);
- e.velocity = dir;
-
- e.oldorigin_x = compressShortVector(e.velocity);
-
- Net_LinkEntity(e, false, 0.2, Violence_GibSplash_SendEntity);
-}
-
-void Violence_GibSplash(entity source, float type, float amount, entity attacker)
-{
- Violence_GibSplash_At(source.origin + source.view_ofs, source.velocity, type, amount, source, attacker);
-}
+++ /dev/null
-#ifndef G_VIOLENCE_H
-#define G_VIOLENCE_H
-
-bool Violence_GibSplash_SendEntity(entity this, entity to, int sf);
-
-// TODO maybe convert this to a TE?
-void Violence_GibSplash_At(vector org, vector dir, float type, float amount, entity gibowner, entity attacker);
-
-void Violence_GibSplash(entity source, float type, float amount, entity attacker);
-#endif
e = edict_num(self.cnt + 1);
if(IS_REAL_CLIENT(e))
{
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
+ WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
WriteByte(MSG_BROADCAST, self.cnt);
WriteShort(MSG_BROADCAST, max(1, e.ping));
WriteByte(MSG_BROADCAST, ceil(e.ping_packetloss * 255));
}
else
{
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
+ WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
WriteByte(MSG_BROADCAST, self.cnt);
WriteShort(MSG_BROADCAST, 0);
WriteByte(MSG_BROADCAST, 0);
}
void PingPLReport_Spawn()
{
- pingplreport = spawn();
- pingplreport.classname = "pingplreport";
+ pingplreport = new(pingplreport);
+ make_pure(pingplreport);
pingplreport.think = PingPLReport_Think;
pingplreport.nextthink = time;
}
void SetDefaultAlpha()
{
- if(autocvar_g_running_guns)
- {
- default_player_alpha = -1;
- default_weapon_alpha = +1;
- }
- else if(g_cloaked)
- {
- default_player_alpha = autocvar_g_balance_cloaked_alpha;
- default_weapon_alpha = default_player_alpha;
- }
- else
+ if (!MUTATOR_CALLHOOK(SetDefaultAlpha))
{
default_player_alpha = autocvar_g_player_alpha;
if(default_player_alpha == 0)
entity randomseed;
bool RandomSeed_Send(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_RANDOMSEED);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_RANDOMSEED);
WriteShort(MSG_ENTITY, self.cnt);
return true;
}
}
void RandomSeed_Spawn()
{SELFPARAM();
- randomseed = spawn();
+ randomseed = new(randomseed);
+ make_pure(randomseed);
randomseed.think = RandomSeed_Think;
Net_LinkEntity(randomseed, false, 0, RandomSeed_Send);
remove = remove_unsafely;
- entity e;
- e = spawn();
+ entity e = spawn();
e.think = GotoFirstMap;
e.nextthink = time; // this is usually 1 at this point
- e = spawn();
- e.classname = "info_player_deathmatch"; // safeguard against player joining
+ e = new(info_player_deathmatch); // safeguard against player joining
self.classname = "worldspawn"; // safeguard against various stuff ;)
// needs to be done so early because of the constants they create
static_init();
static_init_late();
+ static_init_precache();
MapInfo_Enumerate();
MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
InitGameplayMode();
static_init_late();
+ static_init_precache();
readlevelcvars();
GrappleHookInit();
WepSet_AddStat();
WepSet_AddStat_InMap();
- addstat(STAT_SWITCHWEAPON, AS_INT, switchweapon);
addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon);
- addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime);
addstat(STAT_ROUNDSTARTTIME, AS_FLOAT, stat_round_starttime);
addstat(STAT_ALLOW_OLDVORTEXBEAM, AS_INT, stat_allow_oldvortexbeam);
Nagger_Init();
- addstat(STAT_STRENGTH_FINISHED, AS_FLOAT, strength_finished);
- addstat(STAT_INVINCIBLE_FINISHED, AS_FLOAT, invincible_finished);
addstat(STAT_SUPERWEAPONS_FINISHED, AS_FLOAT, superweapons_finished);
addstat(STAT_PRESSED_KEYS, AS_FLOAT, pressedkeys);
addstat(STAT_FUEL, AS_INT, ammo_fuel);
addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load);
- addstat(STAT_ARC_HEAT, AS_FLOAT, arc_heat_percent);
-
// freeze attacks
addstat(STAT_FROZEN, AS_INT, frozen);
addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, revive_progress);
void FixIntermissionClient(entity e)
{
- string s;
if(!e.autoscreenshot) // initial call
{
e.autoscreenshot = time + 0.8; // used for autoscreenshot
e.solid = SOLID_NOT;
e.movetype = MOVETYPE_NONE;
e.takedamage = DAMAGE_NO;
- if(e.weaponentity)
+ for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
{
- e.weaponentity.effects = EF_NODRAW;
- if (e.weaponentity.weaponentity)
- e.weaponentity.weaponentity.effects = EF_NODRAW;
+ .entity weaponentity = weaponentities[slot];
+ if(e.(weaponentity))
+ {
+ e.(weaponentity).effects = EF_NODRAW;
+ if (e.(weaponentity).(weaponentity))
+ e.(weaponentity).(weaponentity).effects = EF_NODRAW;
+ }
}
if(IS_REAL_CLIENT(e))
{
stuffcmd(e, "\nscr_printspeed 1000000\n");
- s = autocvar_sv_intermission_cdtrack;
- if(s != "")
- stuffcmd(e, strcat("\ncd loop ", s, "\n"));
+ string list = autocvar_sv_intermission_cdtrack;
+ for(string it; (it = car(list)); list = cdr(list))
+ RandomSelection_Add(world, 0, it, 1, 1);
+ if(RandomSelection_chosen_string && RandomSelection_chosen_string != "")
+ stuffcmd(e, strcat("\ncd loop ", RandomSelection_chosen_string, "\n"));
msg_entity = e;
WriteByte(MSG_ONE, SVC_INTERMISSION);
}
}
// clear the .winning flags
-void ClearWinners(void)
+void ClearWinners()
{
entity head;
FOR_EACH_PLAYER(head)
+++ /dev/null
-#include "generator.qh"
-
-bool generator_send(entity this, entity to, int sf)
-{
- WriteByte(MSG_ENTITY, ENT_CLIENT_GENERATOR);
- WriteByte(MSG_ENTITY, sf);
- if(sf & GSF_SETUP)
- {
- WriteCoord(MSG_ENTITY, self.origin_x);
- WriteCoord(MSG_ENTITY, self.origin_y);
- WriteCoord(MSG_ENTITY, self.origin_z);
-
- WriteByte(MSG_ENTITY, self.health);
- WriteByte(MSG_ENTITY, self.max_health);
- WriteByte(MSG_ENTITY, self.count);
- WriteByte(MSG_ENTITY, self.team);
- }
-
- if(sf & GSF_STATUS)
- {
- WriteByte(MSG_ENTITY, self.team);
-
- if(self.health <= 0)
- WriteByte(MSG_ENTITY, 0);
- else
- WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255));
- }
-
- return true;
-}
-
-void generator_link(void() spawnproc)
-{SELFPARAM();
- Net_LinkEntity(self, true, 0, generator_send);
- self.think = spawnproc;
- self.nextthink = time;
-}
+++ /dev/null
-#ifndef GENERATOR_H
-#define GENERATOR_H
-const vector GENERATOR_MIN = '-52 -52 -14';
-const vector GENERATOR_MAX = '52 52 75';
-
-const int GSF_STATUS = 4;
-const int GSF_SETUP = 8;
-
-bool generator_send(entity this, entity to, int sf);
-#endif
}
}
- entity e;
- e = spawn();
- e.classname = "bansyncer";
+ entity e = new(bansyncer);
e.think = OnlineBanList_Think;
e.nextthink = time + 1;
}
self.message = strzone(strcat("You've picked up the ", self.netname, "!"));
if (self.noise == "")
- self.noise = SND(ITEMPICKUP);
+ self.noise = strzone(SND(ITEMPICKUP));
// save the name for later
item_keys_names[lowestbit(self.itemkeys)] = self.netname;
void MapVote_SendPicture(float id)
{SELFPARAM();
msg_entity = self;
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_PICTURE);
+ WriteHeader(MSG_ONE, TE_CSQC_PICTURE);
WriteByte(MSG_ONE, id);
WritePicture(MSG_ONE, strcat(mapvote_screenshot_dirs[mapvote_maps_screenshot_dir[id]], "/", mapvote_maps[id]), 3072);
}
if(sf & 1)
sf &= ~2; // if we send 1, we don't need to also send 2
- WriteByte(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
WriteByte(MSG_ENTITY, sf);
if(sf & 1)
}
-string admin_name(void)
+string admin_name()
{
if(autocvar_sv_adminnick != "")
return autocvar_sv_adminnick;
return "SERVER ADMIN";
}
-void DistributeEvenly_Init(float amount, float totalweight)
-{
- if (DistributeEvenly_amount)
- {
- LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
- LOG_TRACE(ftos(DistributeEvenly_totalweight), " left!)\n");
- }
- if (totalweight == 0)
- DistributeEvenly_amount = 0;
- else
- DistributeEvenly_amount = amount;
- DistributeEvenly_totalweight = totalweight;
-}
-float DistributeEvenly_Get(float weight)
-{
- float f;
- if (weight <= 0)
- return 0;
- f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
- DistributeEvenly_totalweight -= weight;
- DistributeEvenly_amount -= f;
- return f;
-}
-float DistributeEvenly_GetRandomized(float weight)
-{
- float f;
- if (weight <= 0)
- return 0;
- f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
- DistributeEvenly_totalweight -= weight;
- DistributeEvenly_amount -= f;
- return f;
-}
-
void GameLogEcho(string s)
{
case "l": replacement = NearestLocation(self.origin); break;
case "y": replacement = NearestLocation(cursor); break;
case "d": replacement = NearestLocation(self.death_origin); break;
- case "w": replacement = WEP_NAME((!self.weapon) ? (!self.switchweapon ? self.cnt : self.switchweapon) : self.weapon); break;
+ case "w": replacement = WEP_NAME(((!self.weapon) ? (!self.switchweapon ? self.cnt : self.switchweapon) : self.weapon)); break;
case "W": replacement = ammoitems; break;
case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break;
case "s": replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1')); break;
if (e.netname == s)
{
g_weaponarena_weapons |= WepSet_FromWeapon(j);
- g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
+ g_weaponarena_list = strcat(g_weaponarena_list, e.m_name, " & ");
break;
}
}
warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
}
-float sound_allowed(float destin, entity e)
-{
- // sounds from world may always pass
- for (;;)
- {
- if (e.classname == "body")
- e = e.enemy;
- else if (e.realowner && e.realowner != e)
- e = e.realowner;
- else if (e.owner && e.owner != e)
- e = e.owner;
- else
- break;
- }
- // sounds to self may always pass
- if (destin == MSG_ONE)
- if (e == msg_entity)
- return true;
- // sounds by players can be removed
- if (autocvar_bot_sound_monopoly)
- if (IS_REAL_CLIENT(e))
- return false;
- // anything else may pass
- return true;
-}
-
-void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
-{
- float entno, idx;
-
- if (!sound_allowed(_dest, e))
- return;
-
- entno = num_for_edict(e);
- idx = precache_sound_index(samp);
-
- int sflags;
- sflags = 0;
-
- attenu = floor(attenu * 64);
- vol = floor(vol * 255);
-
- if (vol != 255)
- sflags |= SND_VOLUME;
- if (attenu != 64)
- sflags |= SND_ATTENUATION;
- if (entno >= 8192 || chan < 0 || chan > 7)
- sflags |= SND_LARGEENTITY;
- if (idx >= 256)
- sflags |= SND_LARGESOUND;
-
- WriteByte(_dest, SVC_SOUND);
- WriteByte(_dest, sflags);
- if (sflags & SND_VOLUME)
- WriteByte(_dest, vol);
- if (sflags & SND_ATTENUATION)
- WriteByte(_dest, attenu);
- if (sflags & SND_LARGEENTITY)
- {
- WriteShort(_dest, entno);
- WriteByte(_dest, chan);
- }
- else
- {
- WriteShort(_dest, entno * 8 + chan);
- }
- if (sflags & SND_LARGESOUND)
- WriteShort(_dest, idx);
- else
- WriteByte(_dest, idx);
-
- WriteCoord(_dest, o.x);
- WriteCoord(_dest, o.y);
- WriteCoord(_dest, o.z);
-}
-void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
-{
- vector o;
-
- if (!sound_allowed(_dest, e))
- return;
-
- o = e.origin + 0.5 * (e.mins + e.maxs);
- soundtoat(_dest, e, o, chan, samp, vol, _atten);
-}
-void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
-{
- soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
-}
-void stopsoundto(float _dest, entity e, float chan)
-{
- float entno;
-
- if (!sound_allowed(_dest, e))
- return;
-
- entno = num_for_edict(e);
-
- if (entno >= 8192 || chan < 0 || chan > 7)
- {
- float idx, sflags;
- idx = precache_sound_index(SND(Null));
- sflags = SND_LARGEENTITY;
- if (idx >= 256)
- sflags |= SND_LARGESOUND;
- WriteByte(_dest, SVC_SOUND);
- WriteByte(_dest, sflags);
- WriteShort(_dest, entno);
- WriteByte(_dest, chan);
- if (sflags & SND_LARGESOUND)
- WriteShort(_dest, idx);
- else
- WriteByte(_dest, idx);
- WriteCoord(_dest, e.origin.x);
- WriteCoord(_dest, e.origin.y);
- WriteCoord(_dest, e.origin.z);
- }
- else
- {
- WriteByte(_dest, SVC_STOPSOUND);
- WriteShort(_dest, entno * 8 + chan);
- }
-}
-void stopsound(entity e, float chan)
-{
- if (!sound_allowed(MSG_BROADCAST, e))
- return;
-
- stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
- stopsoundto(MSG_ALL, e, chan); // in case of packet loss
-}
-
-void play2(entity e, string filename)
-{
- //stuffcmd(e, strcat("play2 ", filename, "\n"));
- msg_entity = e;
- soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
-}
-
-// use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
-.float spamtime;
-float spamsound(entity e, float chan, string samp, float vol, float _atten)
-{
- if (!sound_allowed(MSG_BROADCAST, e))
- return false;
-
- if (time > e.spamtime)
- {
- e.spamtime = time;
- _sound(e, chan, samp, vol, _atten);
- return true;
- }
- return false;
-}
-
-void play2team(float t, string filename)
-{
- entity head;
-
- if (autocvar_bot_sound_monopoly)
- return;
-
- FOR_EACH_REALPLAYER(head)
- {
- if (head.team == t)
- play2(head, filename);
- }
-}
-
-void play2all(string samp)
-{
- if (autocvar_bot_sound_monopoly)
- return;
-
- _sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
-}
-
void PrecachePlayerSounds(string f);
void precache_playermodel(string m)
{
precache_playermodels(autocvar_sv_defaultplayermodel);
}
- if (g_footsteps)
- {
- PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
- PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
- }
-
- // gore and miscellaneous sounds
- PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
- PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
-
#if 0
// Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
builtin_remove(e);
}
-void InitializeEntity(entity e, void(void) func, float order)
+void InitializeEntity(entity e, void() func, float order)
{
entity prev, cur;
{
float i, f, b;
entity e;
- WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
WriteByte(MSG_ENTITY, sendflags);
if(sendflags & 1)
return false;
}
-
+/** engine callback */
void URI_Get_Callback(float id, float status, string data)
{
if(url_URI_Get_Callback(id, status, data))
return gettaginfo(gettaginfo_relative_ent, tag);
}
-.float scale2;
-
-bool modeleffect_SendEntity(entity this, entity to, int sf)
-{
- float f;
- WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
-
- f = 0;
- if(self.velocity != '0 0 0')
- f |= 1;
- if(self.angles != '0 0 0')
- f |= 2;
- if(self.avelocity != '0 0 0')
- f |= 4;
-
- WriteByte(MSG_ENTITY, f);
- WriteShort(MSG_ENTITY, self.modelindex);
- WriteByte(MSG_ENTITY, self.skin);
- WriteByte(MSG_ENTITY, self.frame);
- WriteCoord(MSG_ENTITY, self.origin.x);
- WriteCoord(MSG_ENTITY, self.origin.y);
- WriteCoord(MSG_ENTITY, self.origin.z);
- if(f & 1)
- {
- WriteCoord(MSG_ENTITY, self.velocity.x);
- WriteCoord(MSG_ENTITY, self.velocity.y);
- WriteCoord(MSG_ENTITY, self.velocity.z);
- }
- if(f & 2)
- {
- WriteCoord(MSG_ENTITY, self.angles.x);
- WriteCoord(MSG_ENTITY, self.angles.y);
- WriteCoord(MSG_ENTITY, self.angles.z);
- }
- if(f & 4)
- {
- WriteCoord(MSG_ENTITY, self.avelocity.x);
- WriteCoord(MSG_ENTITY, self.avelocity.y);
- WriteCoord(MSG_ENTITY, self.avelocity.z);
- }
- WriteShort(MSG_ENTITY, self.scale * 256.0);
- WriteShort(MSG_ENTITY, self.scale2 * 256.0);
- WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
- WriteByte(MSG_ENTITY, self.fade_time * 100.0);
- WriteByte(MSG_ENTITY, self.alpha * 255.0);
-
- return true;
-}
-
-void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2)
-{
- entity e;
- float sz;
- e = spawn();
- e.classname = "modeleffect";
- _setmodel(e, m);
- e.frame = f;
- setorigin(e, o);
- e.velocity = v;
- e.angles = ang;
- e.avelocity = angv;
- e.alpha = a;
- e.teleport_time = t1;
- e.fade_time = t2;
- e.skin = s;
- if(s0 >= 0)
- e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
- else
- e.scale = -s0;
- if(s2 >= 0)
- e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
- else
- e.scale2 = -s2;
- sz = max(e.scale, e.scale2);
- setsize(e, e.mins * sz, e.maxs * sz);
- Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
-}
-
-void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
-{
- return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
-}
-
-float randombit(float bits)
-{
- if(!(bits & (bits-1))) // this ONLY holds for powers of two!
- return bits;
-
- float n, f, b, r;
-
- r = random();
- b = 0;
- n = 0;
-
- for(f = 1; f <= bits; f *= 2)
- {
- if(bits & f)
- {
- ++n;
- r *= n;
- if(r <= 1)
- b = f;
- else
- r = (r - 1) / (n - 1);
- }
- }
-
- return b;
-}
-
-float randombits(float bits, float k, float error_return)
-{
- float r;
- r = 0;
- while(k > 0 && bits != r)
- {
- r += randombit(bits - r);
- --k;
- }
- if(error_return)
- if(k > 0)
- return -1; // all
- return r;
-}
-
-void randombit_test(float bits, float iter)
-{
- while(iter > 0)
- {
- LOG_INFO(ftos(randombit(bits)), "\n");
- --iter;
- }
-}
-
-float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
-{
- if(halflifedist > 0)
- return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
- else if(halflifedist < 0)
- return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
- else
- return 1;
-}
-
-
.string aiment_classname;
.float aiment_deadflag;
void SetMovetypeFollow(entity ent, entity e)
entity eliminatedPlayers;
void EliminatedPlayers_Init(float(entity) isEliminated_func);
-string admin_name(void);
+string admin_name();
void write_recordmarker(entity pl, float tstart, float dt);
void play2all(string samp);
-void DistributeEvenly_Init(float amount, float totalweight);
-float DistributeEvenly_Get(float weight);
-
-void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2);
-
-void shockwave_spawn(string m, vector org, float sz, float t1, float t2);
-
void play2team(float t, string filename);
void GetCvars_handleFloat(string thisname, float f, .float field, string name);
void stopsoundto(float _dest, entity e, float chan);
void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten);
-float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d);
-float DistributeEvenly_amount;
-float DistributeEvenly_totalweight;
void objerror(string s);
void droptofloor();
void() SUB_Remove;
#define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
-const string STR_PLAYER = "player";
-const string STR_SPECTATOR = "spectator";
-const string STR_OBSERVER = "observer";
-
-#define IS_PLAYER(v) ((v).classname == STR_PLAYER)
-#define IS_SPEC(v) ((v).classname == STR_SPECTATOR)
-#define IS_OBSERVER(v) ((v).classname == STR_OBSERVER)
-#define IS_CLIENT(v) (v.flags & FL_CLIENT)
-#define IS_BOT_CLIENT(v) (clienttype(v) == CLIENTTYPE_BOT)
-#define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL)
-#define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT)
-
-#define IS_MONSTER(v) (v.flags & FL_MONSTER)
-#define IS_VEHICLE(v) (v.vehicle_flags & VHF_ISVEHICLE)
-#define IS_TURRET(v) (v.turret_flags & TUR_FLAG_ISTURRET)
-
-#define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
-#define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(IS_CLIENT(v))
-#define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(IS_REAL_CLIENT(v))
-
-#define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(IS_PLAYER(v))
-#define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if (!IS_PLAYER(v)) // Samual: shouldn't this be IS_SPEC(v)? and rather create a separate macro to include observers too
-#define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(IS_PLAYER(v))
-
-#define FOR_EACH_MONSTER(v) for(v = world; (v = findflags(v, flags, FL_MONSTER)) != world; )
-
#define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
// copies a string to a tempstring (so one can strunzone it)
float sv_taunt;
string GetGametype(); // g_world.qc
-void readlevelcvars(void)
+void readlevelcvars()
{
if(cvar("sv_allow_fullbright"))
serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
sv_clones = cvar("sv_clones");
sv_foginterval = cvar("sv_foginterval");
- g_cloaked = cvar("g_cloaked");
g_footsteps = cvar("g_footsteps");
g_jetpack = cvar("g_jetpack");
sv_maxidle = cvar("sv_maxidle");
//#NO AUTOCVARS END
-
-// Sound functions
-//string precache_sound (string s) = #19;
-// hack
-float precache_sound_index (string s) = #19;
-
-const float SND_VOLUME = BIT(0);
-const float SND_ATTENUATION = BIT(1);
-const float SND_LARGEENTITY = BIT(3);
-const float SND_LARGESOUND = BIT(4);
-
const float INITPRIO_FIRST = 0;
const float INITPRIO_GAMETYPE = 0;
const float INITPRIO_GAMETYPE_FALLBACK = 1;
const float INITPRIO_LINKDOORS = 91;
const float INITPRIO_LAST = 99;
-.void(void) initialize_entity;
+.void() initialize_entity;
.float initialize_entity_order;
.entity initialize_entity_next;
entity initialize_entity_first;
float sound_allowed(float dest, entity e);
-void InitializeEntity(entity e, void(void) func, float order);
-void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer);
+void InitializeEntity(entity e, void() func, float order);
+void SetCustomizer(entity e, float() customizer, void() uncustomizer);
#endif
#include "mutator/gamemode_keepaway.qc"
#include "mutator/gamemode_keyhunt.qc"
#include "mutator/gamemode_lms.qc"
-#include "mutator/gamemode_onslaught.qc"
#include "mutator/gamemode_race.qc"
#include "mutator/gamemode_tdm.qc"
-
-#include "mutator/mutator_bloodloss.qc"
-#include "mutator/mutator_breakablehook.qc"
-#include "mutator/mutator_buffs.qc"
-#include "mutator/mutator_campcheck.qc"
-#include "mutator/mutator_dodging.qc"
-#include "mutator/mutator_hook.qc"
-#include "mutator/mutator_invincibleproj.qc"
-#include "mutator/mutator_melee_only.qc"
-#include "mutator/mutator_midair.qc"
-#include "mutator/mutator_multijump.qc"
-#include "mutator/mutator_nades.qc"
-#include "mutator/mutator_new_toys.qc"
-#include "mutator/mutator_nix.qc"
-#include "mutator/mutator_overkill.qc"
-#include "mutator/mutator_physical_items.qc"
-#include "mutator/mutator_pinata.qc"
-#include "mutator/mutator_random_gravity.qc"
-#include "mutator/mutator_rocketflying.qc"
-#include "mutator/mutator_rocketminsta.qc"
-#include "mutator/mutator_spawn_near_teammate.qc"
-#include "mutator/mutator_superspec.qc"
-#include "mutator/mutator_touchexplode.qc"
-#include "mutator/mutator_vampirehook.qc"
-#include "mutator/mutator_vampire.qc"
-#include "mutator/mutator_weaponarena_random.qc"
-
-#include "mutator/sandbox.qc"
/** returns true if dropping the current weapon shall not be allowed at any time including death */
MUTATOR_HOOKABLE(ForbidDropCurrentWeapon, EV_NO_ARGS);
+/** */
+MUTATOR_HOOKABLE(SetDefaultAlpha, EV_NO_ARGS);
+
/** allows changing attack rate */
#define EV_WeaponRateFactor(i, o) \
/**/ i(float, weapon_rate) \
* checks if the current item may be spawned (self.items and self.weapons may be read and written to, as well as the ammo_ fields)
* return error to request removal
*/
-MUTATOR_HOOKABLE(FilterItem, EV_NO_ARGS);
+#define EV_FilterItem(i, o) \
+ /** the current item */ i(entity, __self) \
+ /**/
+MUTATOR_HOOKABLE(FilterItem, EV_FilterItem);
/** return error to request removal */
#define EV_TurretSpawn(i, o) \
* Called when a player is damaged
*/
#define EV_PlayerDamaged(i, o) \
- /** attacker */ i(entity, mutator_argv_entity_0) \
- /** target */ i(entity, mutator_argv_entity_1) \
- /** health */ i(int, mutator_argv_int_0) \
- /** armor */ i(int, mutator_argv_int_1) \
- /** location */ i(vector, mutator_argv_vector_0) \
- /** deathtype */ i(int, mutator_argv_int_2) \
+ /** attacker */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** target */ i(entity, MUTATOR_ARGV_1_entity) \
+ /** health */ i(int, MUTATOR_ARGV_0_int) \
+ /** armor */ i(int, MUTATOR_ARGV_1_int) \
+ /** location */ i(vector, MUTATOR_ARGV_0_vector) \
+ /** deathtype */ i(int, MUTATOR_ARGV_2_int) \
/**/
MUTATOR_HOOKABLE(PlayerDamaged, EV_PlayerDamaged);
+/**
+ * Called by W_DecreaseAmmo
+ */
+#define EV_W_DecreaseAmmo(i, o) \
+ /** actor */ i(entity, MUTATOR_ARGV_0_entity) \
+ /**/
+MUTATOR_HOOKABLE(W_DecreaseAmmo, EV_W_DecreaseAmmo);
+
+/**
+ * Called by W_Reload
+ */
+#define EV_W_Reload(i, o) \
+ /** actor */ i(entity, MUTATOR_ARGV_0_entity) \
+ /**/
+MUTATOR_HOOKABLE(W_Reload, EV_W_Reload);
+
/** called at the end of player_powerups() in cl_client.qc, used for manipulating the values which are set by powerup items. */
#define EV_PlayerPowerups(i, o) \
/**/ i(entity, __self) \
/**/ i(entity, __self) \
/**/
MUTATOR_HOOKABLE(TurretThink, EV_TurretThink);
+
+MUTATOR_HOOKABLE(Ent_Init, EV_NO_ARGS);
+
+/** */
+#define EV_PrepareExplosionByDamage(i, o) \
+ /**/ i(entity, __self) \
+ /**/ i(entity, frag_attacker) \
+ /**/
+MUTATOR_HOOKABLE(PrepareExplosionByDamage, EV_PrepareExplosionByDamage);
+
+/** called when a monster model is about to be set, allows custom paths etc. */
+#define EV_MonsterModel(i, o) \
+ /**/ i(string, monster_model) \
+ /**/ i(string, monster_model_output) \
+ /**/ o(string, monster_model_output) \
+ /**/
+string monster_model;
+string monster_model_output;
+MUTATOR_HOOKABLE(MonsterModel, EV_MonsterModel);
#endif
REGISTER_MUTATOR(ca, false)
{
- ActivateTeamplay();
- SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, -1, -1);
-
- if (autocvar_g_ca_team_spawns)
- have_team_spawns = -1; // request team spawns
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
ca_Initialize();
+
+ ActivateTeamplay();
+ SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, -1, -1);
+
+ if (autocvar_g_ca_team_spawns)
+ have_team_spawns = -1; // request team spawns
}
MUTATOR_ONREMOVE
REGISTER_MUTATOR(ctf, false)
{
- ActivateTeamplay();
- SetLimits(autocvar_capturelimit_override, -1, autocvar_captureleadlimit_override, -1);
- have_team_spawns = -1; // request team spawns
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
ctf_Initialize();
+
+ ActivateTeamplay();
+ SetLimits(autocvar_capturelimit_override, autocvar_captureleadlimit_override, -1, -1);
+ have_team_spawns = -1; // request team spawns
}
MUTATOR_ONROLLBACK_OR_REMOVE
const int SP_CTF_FCKILLS = 8;
const int SP_CTF_RETURNS = 9;
+CLASS(Flag, Pickup)
+ ATTRIB(Flag, m_mins, vector, PL_MIN_CONST + '0 0 -13')
+ ATTRIB(Flag, m_maxs, vector, PL_MAX_CONST + '0 0 -13')
+ENDCLASS(Flag)
+Flag CTF_FLAG; STATIC_INIT(Flag) { CTF_FLAG = NEW(Flag); }
+void ctf_FlagTouch() { SELFPARAM(); ITEM_HANDLE(Pickup, CTF_FLAG, this, other); }
+
// flag constants // for most of these, there is just one question to be asked: WHYYYYY?
-#define FLAG_MIN (PL_MIN_CONST + '0 0 -13')
-#define FLAG_MAX (PL_MAX_CONST + '0 0 -13')
const float FLAG_SCALE = 0.6;
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
bool ctf_stalemate; // indicates that a stalemate is active
void ctf_CaptureShield_Spawn(entity flag)
{SELFPARAM();
- entity shield = spawn();
+ entity shield = new(ctf_captureshield);
shield.enemy = self;
shield.team = self.team;
shield.touch = ctf_CaptureShield_Touch;
shield.customizeentityforclient = ctf_CaptureShield_Customize;
- shield.classname = "ctf_captureshield";
shield.effects = EF_ADDITIVE;
shield.movetype = MOVETYPE_NOCLIP;
shield.solid = SOLID_TRIGGER;
ctf_CaptureShield_Update(player, 0); // shield player from picking up flag
}
+void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
+{
+ return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
+}
// ==============
// Event Handlers
return true;
}
-void ctf_CheckStalemate(void)
+void ctf_CheckStalemate()
{
// declarations
int stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0;
ctf_CaptureShield_Update(tmp_entity, 1); // release shield only
// sanity checks
- if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX) { // reset the flag boundaries in case it got squished
+ if(self.mins != CTF_FLAG.m_mins || self.maxs != CTF_FLAG.m_maxs) { // reset the flag boundaries in case it got squished
LOG_TRACE("wtf the flag got squashed?\n");
- tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);
+ tracebox(self.origin, CTF_FLAG.m_mins, CTF_FLAG.m_maxs, self.origin, MOVE_NOMONSTERS, self);
if(!trace_startsolid || self.noalign) // can we resize it without getting stuck?
- setsize(self, FLAG_MIN, FLAG_MAX); }
+ setsize(self, CTF_FLAG.m_mins, CTF_FLAG.m_maxs); }
switch(self.ctf_status) // reset flag angles in case warpzones adjust it
{
}
}
-void ctf_FlagTouch()
-{SELFPARAM();
+METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher))
+{
+ return = false;
if(gameover) { return; }
if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; }
- entity toucher = other, tmp_entity;
- bool is_not_monster = (!IS_MONSTER(toucher)), num_perteam = 0;
+ bool is_not_monster = (!IS_MONSTER(toucher));
// automatically kill the flag and return it if it touched lava/slime/nodrop surfaces
if(ITEM_TOUCH_NEEDKILL())
{
if(!autocvar_g_ctf_flag_return_damage_delay)
{
- self.health = 0;
- ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
+ flag.health = 0;
+ ctf_CheckFlagReturn(flag, RETURN_NEEDKILL);
}
- if(!self.ctf_flagdamaged) { return; }
+ if(!flag.ctf_flagdamaged) { return; }
}
- FOR_EACH_PLAYER(tmp_entity) if(SAME_TEAM(toucher, tmp_entity)) { ++num_perteam; }
+ int num_perteam = 0;
+ entity tmp_entity; FOR_EACH_PLAYER(tmp_entity) if(SAME_TEAM(toucher, tmp_entity)) { ++num_perteam; }
// special touch behaviors
if(toucher.frozen) { return; }
}
else if (!IS_PLAYER(toucher)) // The flag just touched an object, most likely the world
{
- if(time > self.wait) // if we haven't in a while, play a sound/effect
+ if(time > flag.wait) // if we haven't in a while, play a sound/effect
{
- Send_Effect_(self.toucheffect, self.origin, '0 0 0', 1);
- _sound(self, CH_TRIGGER, self.snd_flag_touch, VOL_BASE, ATTEN_NORM);
- self.wait = time + FLAG_TOUCHRATE;
+ Send_Effect_(flag.toucheffect, flag.origin, '0 0 0', 1);
+ _sound(flag, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTEN_NORM);
+ flag.wait = time + FLAG_TOUCHRATE;
}
return;
}
else if(toucher.deadflag != DEAD_NO) { return; }
- switch(self.ctf_status)
+ switch(flag.ctf_status)
{
case FLAG_BASE:
{
if(ctf_oneflag)
{
- if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster)
- ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base
- else if(!self.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
- ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the neutral flag
+ if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster)
+ ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base
+ else if(!flag.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
+ ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the neutral flag
}
- else if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && is_not_monster)
- ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base
- else if(CTF_DIFFTEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
- ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the enemies flag
+ else if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, flag) && is_not_monster)
+ ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base
+ else if(CTF_DIFFTEAM(toucher, flag) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster)
+ ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the enemies flag
break;
}
case FLAG_DROPPED:
{
- if(CTF_SAMETEAM(toucher, self) && (autocvar_g_ctf_flag_return || num_perteam <= 1) && self.team) // automatically return if there's only 1 player on the team
- ctf_Handle_Return(self, toucher); // toucher just returned his own flag
- else if(is_not_monster && (!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay)))
- ctf_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag
+ if(CTF_SAMETEAM(toucher, flag) && (autocvar_g_ctf_flag_return || num_perteam <= 1) && flag.team) // automatically return if there's only 1 player on the team
+ ctf_Handle_Return(flag, toucher); // toucher just returned his own flag
+ else if(is_not_monster && (!toucher.flagcarried) && ((toucher != flag.ctf_dropper) || (time > flag.ctf_droptime + autocvar_g_ctf_flag_collect_delay)))
+ ctf_Handle_Pickup(flag, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag
break;
}
case FLAG_PASSING:
{
- if((IS_PLAYER(toucher)) && (toucher.deadflag == DEAD_NO) && (toucher != self.pass_sender))
+ if((IS_PLAYER(toucher)) && (toucher.deadflag == DEAD_NO) && (toucher != flag.pass_sender))
{
- if(DIFF_TEAM(toucher, self.pass_sender))
- ctf_Handle_Return(self, toucher);
+ if(DIFF_TEAM(toucher, flag.pass_sender))
+ ctf_Handle_Return(flag, toucher);
else
- ctf_Handle_Retrieve(self, toucher);
+ ctf_Handle_Retrieve(flag, toucher);
}
break;
}
ctf_RespawnFlag(self);
}
-void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf_FlagSetup()
+void ctf_DelayedFlagSetup() // called after a flag is placed on a map by ctf_FlagSetup()
{SELFPARAM();
// bot waypoints
waypoint_spawnforitem_force(self, self.origin);
ctf_CaptureShield_Spawn(self);
}
-void set_flag_string(entity flag, .string field, string value, string teamname)
-{
- if(flag.(field) == "")
- flag.(field) = strzone(sprintf(value,teamname));
-}
-
void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc
{SELFPARAM();
// declarations
if(!flag.scale) { flag.scale = FLAG_SCALE; }
if(flag.skin == 0) { flag.skin = cvar(sprintf("g_ctf_flag_%s_skin", teamname)); }
if(flag.model == "") { flag.model = cvar_string(sprintf("g_ctf_flag_%s_model", teamname)); }
- set_flag_string(flag, toucheffect, "%sflag_touch", teamname);
- set_flag_string(flag, passeffect, "%s_pass", teamname);
- set_flag_string(flag, capeffect, "%s_cap", teamname);
+ if (flag.toucheffect == "") { flag.toucheffect = EFFECT_FLAG_TOUCH(teamnumber).eent_eff_name; }
+ if (flag.passeffect == "") { flag.passeffect = EFFECT_PASS(teamnumber).eent_eff_name; }
+ if (flag.capeffect == "") { flag.capeffect = EFFECT_CAP(teamnumber).eent_eff_name; }
// sounds
- flag.snd_flag_taken = SND(CTF_TAKEN(teamnumber));
- flag.snd_flag_returned = SND(CTF_RETURNED(teamnumber));
- flag.snd_flag_capture = SND(CTF_CAPTURE(teamnumber));
- flag.snd_flag_dropped = SND(CTF_DROPPED(teamnumber));
- if (flag.snd_flag_respawn == "") flag.snd_flag_respawn = SND(CTF_RESPAWN); // if there is ever a team-based sound for this, update the code to match.
+ flag.snd_flag_taken = strzone(SND(CTF_TAKEN(teamnumber)));
+ flag.snd_flag_returned = strzone(SND(CTF_RETURNED(teamnumber)));
+ flag.snd_flag_capture = strzone(SND(CTF_CAPTURE(teamnumber)));
+ flag.snd_flag_dropped = strzone(SND(CTF_DROPPED(teamnumber)));
+ if (flag.snd_flag_respawn == "") flag.snd_flag_respawn = strzone(SND(CTF_RESPAWN)); // if there is ever a team-based sound for this, update the code to match.
precache_sound(flag.snd_flag_respawn);
- if (flag.snd_flag_touch == "") flag.snd_flag_touch = SND(CTF_TOUCH); // again has no team-based sound
+ if (flag.snd_flag_touch == "") flag.snd_flag_touch = strzone(SND(CTF_TOUCH)); // again has no team-based sound
precache_sound(flag.snd_flag_touch);
- if (flag.snd_flag_pass == "") flag.snd_flag_pass = SND(CTF_PASS); // same story here
+ if (flag.snd_flag_pass == "") flag.snd_flag_pass = strzone(SND(CTF_PASS)); // same story here
precache_sound(flag.snd_flag_pass);
// precache
// appearence
_setmodel(flag, flag.model); // precision set below
- setsize(flag, FLAG_MIN, FLAG_MAX);
+ setsize(flag, CTF_FLAG.m_mins, CTF_FLAG.m_maxs);
setorigin(flag, (flag.origin + FLAG_SPAWN_OFFSET));
if(autocvar_g_ctf_flag_glowtrails)
REGISTER_MUTATOR(cts, false)
{
- g_race_qualifying = true;
- independent_players = 1;
- SetLimits(0, 0, 0, -1);
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
cts_Initialize();
+
+ g_race_qualifying = true;
+ independent_players = 1;
+ SetLimits(0, 0, 0, -1);
}
MUTATOR_ONROLLBACK_OR_REMOVE
return true; // in CTS, you don't lose score by observing
}
-MUTATOR_HOOKFUNCTION(cts, SetModname)
-{
- g_cloaked = 1; // always enable cloak in CTS
-
- return false;
-}
-
MUTATOR_HOOKFUNCTION(cts, GetRecords)
{
for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i)
REGISTER_MUTATOR(dom, false)
{
- int fraglimit_override = autocvar_g_domination_point_limit;
- if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit)
- fraglimit_override = autocvar_g_domination_roundbased_point_limit;
-
- ActivateTeamplay();
- SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, -1, -1);
- have_team_spawns = -1; // request team spawns
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
dom_Initialize();
+
+ int fraglimit_override = autocvar_g_domination_point_limit;
+ if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit)
+ fraglimit_override = autocvar_g_domination_roundbased_point_limit;
+
+ ActivateTeamplay();
+ SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, -1, -1);
+ have_team_spawns = -1; // request team spawns
}
MUTATOR_ONREMOVE
REGISTER_MUTATOR(ft, false)
{
- ActivateTeamplay();
- SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, -1, -1);
-
- if (autocvar_g_freezetag_team_spawns)
- have_team_spawns = -1; // request team spawns
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
freezetag_Initialize();
+
+ ActivateTeamplay();
+ SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, -1, -1);
+
+ if (autocvar_g_freezetag_team_spawns)
+ have_team_spawns = -1; // request team spawns
}
MUTATOR_ONROLLBACK_OR_REMOVE
float freezetag_teams;
.float reviving; // temp var
-#endif
+float autocvar_g_freezetag_revive_extra_size;
+float autocvar_g_freezetag_revive_speed;
+bool autocvar_g_freezetag_revive_nade;
+float autocvar_g_freezetag_revive_nade_health;
+
+#endif
#ifdef IMPLEMENTATION
float autocvar_g_freezetag_frozen_maxtime;
-bool autocvar_g_freezetag_revive_nade;
-float autocvar_g_freezetag_revive_nade_health;
-float autocvar_g_freezetag_revive_extra_size;
-float autocvar_g_freezetag_revive_speed;
float autocvar_g_freezetag_revive_clearspeed;
float autocvar_g_freezetag_round_timelimit;
int autocvar_g_freezetag_teams;
REGISTER_MUTATOR(inv, false)
{
- SetLimits(autocvar_g_invasion_point_limit, -1, -1, -1);
- if (autocvar_g_invasion_teams >= 2)
- {
- ActivateTeamplay();
- if (autocvar_g_invasion_team_spawns)
- have_team_spawns = -1; // request team spawns
- }
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
invasion_Initialize();
cvar_settemp("g_monsters", "1");
+
+ SetLimits(autocvar_g_invasion_point_limit, -1, -1, -1);
+ if (autocvar_g_invasion_teams >= 2)
+ {
+ ActivateTeamplay();
+ if (autocvar_g_invasion_team_spawns)
+ have_team_spawns = -1; // request team spawns
+ }
}
MUTATOR_ONROLLBACK_OR_REMOVE
void ka_SpawnBall() // loads various values for the ball, runs only once at start of match
{
- entity e;
- e = spawn();
+ entity e = new(keepawayball);
e.model = "models/orbs/orbblue.md3";
precache_model(e.model);
_setmodel(e, e.model);
setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
- e.classname = "keepawayball";
e.damageforcescale = autocvar_g_keepawayball_damageforcescale;
e.takedamage = DAMAGE_YES;
e.solid = SOLID_TRIGGER;
REGISTER_MUTATOR(kh, false)
{
- ActivateTeamplay();
- SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, -1, -1);
- if (autocvar_g_keyhunt_team_spawns)
- have_team_spawns = -1; // request team spawns
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
kh_Initialize();
+
+ ActivateTeamplay();
+ SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, -1, -1);
+ if (autocvar_g_keyhunt_team_spawns)
+ have_team_spawns = -1; // request team spawns
}
MUTATOR_ONROLLBACK_OR_REMOVE
.entity kh_next;
float kh_Key_AllOwnedByWhichTeam();
-typedef void(void) kh_Think_t;
+typedef void() kh_Think_t;
void kh_StartRound();
void kh_Controller_SetThink(float t, kh_Think_t func);
// bits 5- 9: team of key 2, or 0 for no such key, or 30 for dropped, or 31 for self
// bits 10-14: team of key 3, or 0 for no such key, or 30 for dropped, or 31 for self
// bits 15-19: team of key 4, or 0 for no such key, or 30 for dropped, or 31 for self
-.float kh_state;
+.float kh_state = _STAT(KH_KEYS);
.float siren_time; // time delay the siren
//.float stuff_time; // time delay to stuffcmd a cvar
}
const string STR_ITEM_KH_KEY = "item_kh_key";
-void kh_Key_Spawn(entity initial_owner, float angle, float i) // runs every time a new flag is created, ie after all the keys have been collected
+void kh_Key_Spawn(entity initial_owner, float _angle, float i) // runs every time a new flag is created, ie after all the keys have been collected
{
- entity key;
- key = spawn();
+ entity key = spawn();
key.count = i;
key.classname = STR_ITEM_KH_KEY;
key.touch = kh_Key_Touch;
key.think = kh_Key_Think;
key.nextthink = time;
key.items = IT_KEY1 | IT_KEY2;
- key.cnt = angle;
+ key.cnt = _angle;
key.angles = '0 360 0' * random();
key.event_damage = kh_Key_Damage;
key.takedamage = DAMAGE_YES;
kh_controller.model = "";
kh_controller.modelindex = 0;
- addstat(STAT_KH_KEYS, AS_INT, kh_state);
-
kh_ScoreRules(kh_teams);
}
REGISTER_MUTATOR(lms, false)
{
- SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, -1, -1);
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
lms_Initialize();
+
+ SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, -1, -1);
}
MUTATOR_ONROLLBACK_OR_REMOVE
+++ /dev/null
-#ifndef GAMEMODE_ONSLAUGHT_H
-#define GAMEMODE_ONSLAUGHT_H
-
-float autocvar_g_onslaught_point_limit;
-void ons_Initialize();
-
-REGISTER_MUTATOR(ons, false)
-{
- ActivateTeamplay();
- SetLimits(autocvar_g_onslaught_point_limit, -1, -1, -1);
- have_team_spawns = -1; // request team spawns
-
- MUTATOR_ONADD
- {
- if (time > 1) // game loads at time 1
- error("This is a game type and it cannot be added at runtime.");
- ons_Initialize();
- }
-
- MUTATOR_ONROLLBACK_OR_REMOVE
- {
- // we actually cannot roll back ons_Initialize here
- // BUT: we don't need to! If this gets called, adding always
- // succeeds.
- }
-
- MUTATOR_ONREMOVE
- {
- LOG_INFO("This is a game type and it cannot be removed at runtime.");
- return -1;
- }
-
- return false;
-}
-
-#ifdef SVQC
-
-.entity ons_toucher; // player who touched the control point
-
-// control point / generator constants
-const float ONS_CP_THINKRATE = 0.2;
-const float GEN_THINKRATE = 1;
-#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13))
-const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128');
-const vector CPICON_OFFSET = ('0 0 96');
-
-// list of generators on the map
-entity ons_worldgeneratorlist;
-.entity ons_worldgeneratornext;
-.entity ons_stalegeneratornext;
-
-// list of control points on the map
-entity ons_worldcplist;
-.entity ons_worldcpnext;
-.entity ons_stalecpnext;
-
-// list of links on the map
-entity ons_worldlinklist;
-.entity ons_worldlinknext;
-.entity ons_stalelinknext;
-
-// definitions
-.entity sprite;
-.string target2;
-.int iscaptured;
-.int islinked;
-.int isshielded;
-.float lasthealth;
-.int lastteam;
-.int lastshielded;
-.int lastcaptured;
-
-.bool waslinked;
-
-bool ons_stalemate;
-
-.float teleport_antispam;
-
-.bool ons_roundlost;
-
-// waypoint sprites
-.entity bot_basewaypoint; // generator waypointsprite
-
-.bool isgenneighbor[17];
-.bool iscpneighbor[17];
-float ons_notification_time[17];
-
-.float ons_overtime_damagedelay;
-
-.vector ons_deathloc;
-
-.entity ons_spawn_by;
-
-// declarations for functions used outside gamemode_onslaught.qc
-void ons_Generator_UpdateSprite(entity e);
-void ons_ControlPoint_UpdateSprite(entity e);
-bool ons_ControlPoint_Attackable(entity cp, int teamnumber);
-
-// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet
-float ons_captureshield_force; // push force of the shield
-
-// bot player logic
-const int HAVOCBOT_ONS_ROLE_NONE = 0;
-const int HAVOCBOT_ONS_ROLE_DEFENSE = 2;
-const int HAVOCBOT_ONS_ROLE_ASSISTANT = 4;
-const int HAVOCBOT_ONS_ROLE_OFFENSE = 8;
-
-.entity havocbot_ons_target;
-
-.int havocbot_role_flags;
-.float havocbot_attack_time;
-
-void havocbot_role_ons_defense();
-void havocbot_role_ons_offense();
-void havocbot_role_ons_assistant();
-
-void havocbot_ons_reset_role(entity bot);
-void havocbot_goalrating_items(float ratingscale, vector org, float sradius);
-void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius);
-
-// score rule declarations
-const int ST_ONS_CAPS = 1;
-const int SP_ONS_CAPS = 4;
-const int SP_ONS_TAKES = 6;
-
-#endif
-#endif
-
-#ifdef IMPLEMENTATION
-
-#include "../../controlpoint.qh"
-#include "../../generator.qh"
-
-bool g_onslaught;
-
-float autocvar_g_onslaught_debug;
-float autocvar_g_onslaught_teleport_wait;
-bool autocvar_g_onslaught_spawn_at_controlpoints;
-bool autocvar_g_onslaught_spawn_at_generator;
-float autocvar_g_onslaught_cp_proxydecap;
-float autocvar_g_onslaught_cp_proxydecap_distance = 512;
-float autocvar_g_onslaught_cp_proxydecap_dps = 100;
-float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5;
-float autocvar_g_onslaught_spawn_at_controlpoints_random;
-float autocvar_g_onslaught_spawn_at_generator_chance;
-float autocvar_g_onslaught_spawn_at_generator_random;
-float autocvar_g_onslaught_cp_buildhealth;
-float autocvar_g_onslaught_cp_buildtime;
-float autocvar_g_onslaught_cp_health;
-float autocvar_g_onslaught_cp_regen;
-float autocvar_g_onslaught_gen_health;
-float autocvar_g_onslaught_shield_force = 100;
-float autocvar_g_onslaught_allow_vehicle_touch;
-float autocvar_g_onslaught_round_timelimit;
-float autocvar_g_onslaught_warmup;
-float autocvar_g_onslaught_teleport_radius;
-float autocvar_g_onslaught_spawn_choose;
-float autocvar_g_onslaught_click_radius;
-
-void FixSize(entity e);
-
-// =======================
-// CaptureShield Functions
-// =======================
-
-bool ons_CaptureShield_Customize()
-{SELFPARAM();
- entity e = WaypointSprite_getviewentity(other);
-
- if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, e.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return false; }
- if(SAME_TEAM(self, e)) { return false; }
-
- return true;
-}
-
-void ons_CaptureShield_Touch()
-{SELFPARAM();
- if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, other.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return; }
- if(!IS_PLAYER(other)) { return; }
- if(SAME_TEAM(other, self)) { return; }
-
- vector mymid = (self.absmin + self.absmax) * 0.5;
- vector othermid = (other.absmin + other.absmax) * 0.5;
-
- Damage(other, self, self, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(othermid - mymid) * ons_captureshield_force);
-
- if(IS_REAL_CLIENT(other))
- {
- play2(other, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
-
- if(self.enemy.classname == "onslaught_generator")
- Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED);
- else
- Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED);
- }
-}
-
-void ons_CaptureShield_Reset()
-{SELFPARAM();
- self.colormap = self.enemy.colormap;
- self.team = self.enemy.team;
-}
-
-void ons_CaptureShield_Spawn(entity generator, bool is_generator)
-{
- entity shield = spawn();
-
- shield.enemy = generator;
- shield.team = generator.team;
- shield.colormap = generator.colormap;
- shield.reset = ons_CaptureShield_Reset;
- shield.touch = ons_CaptureShield_Touch;
- shield.customizeentityforclient = ons_CaptureShield_Customize;
- shield.classname = "ons_captureshield";
- shield.effects = EF_ADDITIVE;
- shield.movetype = MOVETYPE_NOCLIP;
- shield.solid = SOLID_TRIGGER;
- shield.avelocity = '7 0 11';
- shield.scale = 1;
- shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3");
-
- precache_model(shield.model);
- setorigin(shield, generator.origin);
- _setmodel(shield, shield.model);
- setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
-}
-
-
-// ==========
-// Junk Pile
-// ==========
-
-void ons_debug(string input)
-{
- switch(autocvar_g_onslaught_debug)
- {
- case 1: LOG_TRACE(input); break;
- case 2: LOG_INFO(input); break;
- }
-}
-
-void setmodel_fixsize(entity e, Model m)
-{
- setmodel(e, m);
- FixSize(e);
-}
-
-void onslaught_updatelinks()
-{
- entity l;
- // first check if the game has ended
- ons_debug("--- updatelinks ---\n");
- // mark generators as being shielded and networked
- for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
- {
- if (l.iscaptured)
- ons_debug(strcat(etos(l), " (generator) belongs to team ", ftos(l.team), "\n"));
- else
- ons_debug(strcat(etos(l), " (generator) is destroyed\n"));
- l.islinked = l.iscaptured;
- l.isshielded = l.iscaptured;
- l.sprite.SendFlags |= 16;
- }
- // mark points as shielded and not networked
- for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
- {
- l.islinked = false;
- l.isshielded = true;
- int i;
- for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; }
- ons_debug(strcat(etos(l), " (point) belongs to team ", ftos(l.team), "\n"));
- l.sprite.SendFlags |= 16;
- }
- // flow power outward from the generators through the network
- bool stop = false;
- while (!stop)
- {
- stop = true;
- for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
- {
- // if both points are captured by the same team, and only one of
- // them is powered, mark the other one as powered as well
- if (l.enemy.iscaptured && l.goalentity.iscaptured)
- if (l.enemy.islinked != l.goalentity.islinked)
- if(SAME_TEAM(l.enemy, l.goalentity))
- {
- if (!l.goalentity.islinked)
- {
- stop = false;
- l.goalentity.islinked = true;
- ons_debug(strcat(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n"));
- }
- else if (!l.enemy.islinked)
- {
- stop = false;
- l.enemy.islinked = true;
- ons_debug(strcat(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n"));
- }
- }
- }
- }
- // now that we know which points are powered we can mark their neighbors
- // as unshielded if team differs
- for(l = ons_worldlinklist; l; l = l.ons_worldlinknext)
- {
- if (l.goalentity.islinked)
- {
- if(DIFF_TEAM(l.goalentity, l.enemy))
- {
- ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n"));
- l.enemy.isshielded = false;
- }
- if(l.goalentity.classname == "onslaught_generator")
- l.enemy.isgenneighbor[l.goalentity.team] = true;
- else
- l.enemy.iscpneighbor[l.goalentity.team] = true;
- }
- if (l.enemy.islinked)
- {
- if(DIFF_TEAM(l.goalentity, l.enemy))
- {
- ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n"));
- l.goalentity.isshielded = false;
- }
- if(l.enemy.classname == "onslaught_generator")
- l.goalentity.isgenneighbor[l.enemy.team] = true;
- else
- l.goalentity.iscpneighbor[l.enemy.team] = true;
- }
- }
- // now update the generators
- for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
- {
- if (l.isshielded)
- {
- ons_debug(strcat(etos(l), " (generator) is shielded\n"));
- l.takedamage = DAMAGE_NO;
- l.bot_attack = false;
- }
- else
- {
- ons_debug(strcat(etos(l), " (generator) is not shielded\n"));
- l.takedamage = DAMAGE_AIM;
- l.bot_attack = true;
- }
-
- ons_Generator_UpdateSprite(l);
- }
- // now update the takedamage and alpha variables on control point icons
- for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
- {
- if (l.isshielded)
- {
- ons_debug(strcat(etos(l), " (point) is shielded\n"));
- if (l.goalentity)
- {
- l.goalentity.takedamage = DAMAGE_NO;
- l.goalentity.bot_attack = false;
- }
- }
- else
- {
- ons_debug(strcat(etos(l), " (point) is not shielded\n"));
- if (l.goalentity)
- {
- l.goalentity.takedamage = DAMAGE_AIM;
- l.goalentity.bot_attack = true;
- }
- }
- ons_ControlPoint_UpdateSprite(l);
- }
- l = findchain(classname, "ons_captureshield");
- while(l)
- {
- l.team = l.enemy.team;
- l.colormap = l.enemy.colormap;
- l = l.chain;
- }
-}
-
-
-// ===================
-// Main Link Functions
-// ===================
-
-bool ons_Link_Send(entity this, entity to, int sendflags)
-{
- WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
- WriteByte(MSG_ENTITY, sendflags);
- if(sendflags & 1)
- {
- WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
- WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
- WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
- }
- if(sendflags & 2)
- {
- WriteCoord(MSG_ENTITY, self.enemy.origin_x);
- WriteCoord(MSG_ENTITY, self.enemy.origin_y);
- WriteCoord(MSG_ENTITY, self.enemy.origin_z);
- }
- if(sendflags & 4)
- {
- WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
- }
- return true;
-}
-
-void ons_Link_CheckUpdate()
-{SELFPARAM();
- // TODO check if the two sides have moved (currently they won't move anyway)
- float cc = 0, cc1 = 0, cc2 = 0;
-
- if(self.goalentity.islinked || self.goalentity.iscaptured) { cc1 = (self.goalentity.team - 1) * 0x01; }
- if(self.enemy.islinked || self.enemy.iscaptured) { cc2 = (self.enemy.team - 1) * 0x10; }
-
- cc = cc1 + cc2;
-
- if(cc != self.clientcolors)
- {
- self.clientcolors = cc;
- self.SendFlags |= 4;
- }
-
- self.nextthink = time;
-}
-
-void ons_DelayedLinkSetup()
-{SELFPARAM();
- self.goalentity = find(world, targetname, self.target);
- self.enemy = find(world, targetname, self.target2);
- if(!self.goalentity) { objerror("can not find target\n"); }
- if(!self.enemy) { objerror("can not find target2\n"); }
-
- ons_debug(strcat(etos(self.goalentity), " linked with ", etos(self.enemy), "\n"));
- self.SendFlags |= 3;
- self.think = ons_Link_CheckUpdate;
- self.nextthink = time;
-}
-
-
-// =============================
-// Main Control Point Functions
-// =============================
-
-int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber)
-{
- if(cp.isgenneighbor[teamnumber]) { return 2; }
- if(cp.iscpneighbor[teamnumber]) { return 1; }
-
- return 0;
-}
-
-int ons_ControlPoint_Attackable(entity cp, int teamnumber)
- // -2: SAME TEAM, attackable by enemy!
- // -1: SAME TEAM!
- // 0: off limits
- // 1: attack it
- // 2: touch it
- // 3: attack it (HIGH PRIO)
- // 4: touch it (HIGH PRIO)
-{
- int a;
-
- if(cp.isshielded)
- {
- return 0;
- }
- else if(cp.goalentity)
- {
- // if there's already an icon built, nothing happens
- if(cp.team == teamnumber)
- {
- a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
- if(a) // attackable by enemy?
- return -2; // EMERGENCY!
- return -1;
- }
- // we know it can be linked, so no need to check
- // but...
- a = ons_ControlPoint_CanBeLinked(cp, teamnumber);
- if(a == 2) // near our generator?
- return 3; // EMERGENCY!
- return 1;
- }
- else
- {
- // free point
- if(ons_ControlPoint_CanBeLinked(cp, teamnumber))
- {
- a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t
- if(a == 2)
- return 4; // GET THIS ONE NOW!
- else
- return 2; // TOUCH ME
- }
- }
- return 0;
-}
-
-void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{SELFPARAM();
- if(damage <= 0) { return; }
-
- if (self.owner.isshielded)
- {
- // this is protected by a shield, so ignore the damage
- if (time > self.pain_finished)
- if (IS_PLAYER(attacker))
- {
- play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
- self.pain_finished = time + 1;
- attacker.typehitsound += 1; // play both sounds (shield is way too quiet)
- }
-
- return;
- }
-
- if(IS_PLAYER(attacker))
- if(time - ons_notification_time[self.team] > 10)
- {
- play2team(self.team, SND(ONS_CONTROLPOINT_UNDERATTACK));
- ons_notification_time[self.team] = time;
- }
-
- self.health = self.health - damage;
- if(self.owner.iscaptured)
- WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
- else
- WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / ONS_CP_THINKRATE));
- self.pain_finished = time + 1;
- // particles on every hit
- pointparticles(particleeffectnum(EFFECT_SPARKS), hitloc, force*-1, 1);
- //sound on every hit
- if (random() < 0.5)
- sound(self, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM);
- else
- sound(self, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM);
-
- if (self.health < 0)
- {
- sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
- pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), self.origin, '0 0 0', 1);
- Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_CPDESTROYED_), self.owner.message, attacker.netname);
-
- PlayerScore_Add(attacker, SP_ONS_TAKES, 1);
- PlayerScore_Add(attacker, SP_SCORE, 10);
-
- self.owner.goalentity = world;
- self.owner.islinked = false;
- self.owner.iscaptured = false;
- self.owner.team = 0;
- self.owner.colormap = 1024;
-
- WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
-
- onslaught_updatelinks();
-
- // Use targets now (somebody make sure this is in the right place..)
- setself(self.owner);
- activator = self;
- SUB_UseTargets ();
- setself(this);
-
- self.owner.waslinked = self.owner.islinked;
- if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
- setmodel_fixsize(self.owner, MDL_ONS_CP_PAD1);
- //setsize(self, '-32 -32 0', '32 32 8');
-
- remove(self);
- }
-
- self.SendFlags |= CPSF_STATUS;
-}
-
-void ons_ControlPoint_Icon_Think()
-{SELFPARAM();
- self.nextthink = time + ONS_CP_THINKRATE;
-
- if(autocvar_g_onslaught_cp_proxydecap)
- {
- int _enemy_count = 0;
- int _friendly_count = 0;
- float _dist;
- entity _player;
-
- FOR_EACH_PLAYER(_player)
- {
- if(!_player.deadflag)
- {
- _dist = vlen(_player.origin - self.origin);
- if(_dist < autocvar_g_onslaught_cp_proxydecap_distance)
- {
- if(SAME_TEAM(_player, self))
- ++_friendly_count;
- else
- ++_enemy_count;
- }
- }
- }
-
- _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
- _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
-
- self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health);
- self.SendFlags |= CPSF_STATUS;
- if(self.health <= 0)
- {
- ons_ControlPoint_Icon_Damage(self, self, 1, 0, self.origin, '0 0 0');
- return;
- }
- }
-
- if (time > self.pain_finished + 5)
- {
- if(self.health < self.max_health)
- {
- self.health = self.health + self.count;
- if (self.health >= self.max_health)
- self.health = self.max_health;
- WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
- }
- }
-
- if(self.owner.islinked != self.owner.waslinked)
- {
- // unteam the spawnpoint if needed
- int t = self.owner.team;
- if(!self.owner.islinked)
- self.owner.team = 0;
-
- setself(self.owner);
- activator = self;
- SUB_UseTargets ();
- setself(this);
-
- self.owner.team = t;
-
- self.owner.waslinked = self.owner.islinked;
- }
-
- // damaged fx
- if(random() < 0.6 - self.health / self.max_health)
- {
- Send_Effect(EFFECT_ELECTRIC_SPARKS, self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
-
- if(random() > 0.8)
- sound(self, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM);
- else if (random() > 0.5)
- sound(self, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM);
- }
-}
-
-void ons_ControlPoint_Icon_BuildThink()
-{SELFPARAM();
- int a;
-
- self.nextthink = time + ONS_CP_THINKRATE;
-
- // only do this if there is power
- a = ons_ControlPoint_CanBeLinked(self.owner, self.owner.team);
- if(!a)
- return;
-
- self.health = self.health + self.count;
-
- self.SendFlags |= CPSF_STATUS;
-
- if (self.health >= self.max_health)
- {
- self.health = self.max_health;
- self.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on
- self.think = ons_ControlPoint_Icon_Think;
- sound(self, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM);
- self.owner.iscaptured = true;
- self.solid = SOLID_BBOX;
-
- Send_Effect(EFFECT_CAP(self.owner.team), self.owner.origin, '0 0 0', 1);
-
- WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
- WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
-
- if(IS_PLAYER(self.owner.ons_toucher))
- {
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, self.owner.ons_toucher.netname, self.owner.message);
- Send_Notification(NOTIF_ALL_EXCEPT, self.owner.ons_toucher, MSG_CENTER, APP_TEAM_ENT_4(self.owner.ons_toucher, CENTER_ONS_CAPTURE_), self.owner.message);
- Send_Notification(NOTIF_ONE, self.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, self.owner.message);
- PlayerScore_Add(self.owner.ons_toucher, SP_ONS_CAPS, 1);
- PlayerTeamScore_AddScore(self.owner.ons_toucher, 10);
- }
-
- self.owner.ons_toucher = world;
-
- onslaught_updatelinks();
-
- // Use targets now (somebody make sure this is in the right place..)
- setself(self.owner);
- activator = self;
- SUB_UseTargets ();
- setself(this);
-
- self.SendFlags |= CPSF_SETUP;
- }
- if(self.owner.model != MDL_ONS_CP_PAD2.model_str())
- setmodel_fixsize(self.owner, MDL_ONS_CP_PAD2);
-
- if(random() < 0.9 - self.health / self.max_health)
- Send_Effect(EFFECT_RAGE, self.origin + 10 * randomvec(), '0 0 -1', 1);
-}
-
-void onslaught_controlpoint_icon_link(entity e, void() spawnproc);
-
-void ons_ControlPoint_Icon_Spawn(entity cp, entity player)
-{
- entity e = spawn();
-
- setsize(e, CPICON_MIN, CPICON_MAX);
- setorigin(e, cp.origin + CPICON_OFFSET);
-
- e.classname = "onslaught_controlpoint_icon";
- e.owner = cp;
- e.max_health = autocvar_g_onslaught_cp_health;
- e.health = autocvar_g_onslaught_cp_buildhealth;
- e.solid = SOLID_NOT;
- e.takedamage = DAMAGE_AIM;
- e.bot_attack = true;
- e.event_damage = ons_ControlPoint_Icon_Damage;
- e.team = player.team;
- e.colormap = 1024 + (e.team - 1) * 17;
- e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
-
- sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM);
-
- cp.goalentity = e;
- cp.team = e.team;
- cp.colormap = e.colormap;
-
- Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1);
-
- WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE));
- WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY);
- cp.sprite.SendFlags |= 16;
-
- onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink);
-}
-
-entity ons_ControlPoint_Waypoint(entity e)
-{
- if(e.team)
- {
- int a = ons_ControlPoint_Attackable(e, e.team);
-
- if(a == -2) { return WP_OnsCPDefend; } // defend now
- if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch
- if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack
- }
- else
- return WP_OnsCP;
-
- return WP_Null;
-}
-
-void ons_ControlPoint_UpdateSprite(entity e)
-{
- entity s1 = ons_ControlPoint_Waypoint(e);
- WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
-
- bool sh;
- sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4));
-
- if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
- {
- if(e.iscaptured) // don't mess up build bars!
- {
- if(sh)
- {
- WaypointSprite_UpdateMaxHealth(e.sprite, 0);
- }
- else
- {
- WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);
- WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);
- }
- }
- if(e.lastshielded)
- {
- if(e.team)
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false));
- else
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');
- }
- else
- {
- if(e.team)
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false));
- else
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
- }
- WaypointSprite_Ping(e.sprite);
-
- e.lastteam = e.team + 2;
- e.lastshielded = sh;
- e.lastcaptured = e.iscaptured;
- }
-}
-
-void ons_ControlPoint_Touch()
-{SELFPARAM();
- entity toucher = other;
- int attackable;
-
- if(IS_VEHICLE(toucher) && toucher.owner)
- if(autocvar_g_onslaught_allow_vehicle_touch)
- toucher = toucher.owner;
- else
- return;
-
- if(!IS_PLAYER(toucher)) { return; }
- if(toucher.frozen) { return; }
- if(toucher.deadflag != DEAD_NO) { return; }
-
- if ( SAME_TEAM(self,toucher) )
- if ( self.iscaptured )
- {
- if(time <= toucher.teleport_antispam)
- Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time));
- else
- Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT);
- }
-
- attackable = ons_ControlPoint_Attackable(self, toucher.team);
- if(attackable != 2 && attackable != 4)
- return;
- // we've verified that this player has a legitimate claim to this point,
- // so start building the captured point icon (which only captures this
- // point if it successfully builds without being destroyed first)
- ons_ControlPoint_Icon_Spawn(self, toucher);
-
- self.ons_toucher = toucher;
-
- onslaught_updatelinks();
-}
-
-void ons_ControlPoint_Think()
-{SELFPARAM();
- self.nextthink = time + ONS_CP_THINKRATE;
- CSQCMODEL_AUTOUPDATE(self);
-}
-
-void ons_ControlPoint_Reset()
-{SELFPARAM();
- if(self.goalentity)
- remove(self.goalentity);
-
- self.goalentity = world;
- self.team = 0;
- self.colormap = 1024;
- self.iscaptured = false;
- self.islinked = false;
- self.isshielded = true;
- self.think = ons_ControlPoint_Think;
- self.ons_toucher = world;
- self.nextthink = time + ONS_CP_THINKRATE;
- setmodel_fixsize(self, MDL_ONS_CP_PAD1);
-
- WaypointSprite_UpdateMaxHealth(self.sprite, 0);
- WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY);
-
- onslaught_updatelinks();
-
- activator = self;
- SUB_UseTargets(); // to reset the structures, playerspawns etc.
-
- CSQCMODEL_AUTOUPDATE(self);
-}
-
-void ons_DelayedControlPoint_Setup(void)
-{SELFPARAM();
- onslaught_updatelinks();
-
- // captureshield setup
- ons_CaptureShield_Spawn(self, false);
-
- CSQCMODEL_AUTOINIT(self);
-}
-
-void ons_ControlPoint_Setup(entity cp)
-{SELFPARAM();
- // declarations
- setself(cp); // for later usage with droptofloor()
-
- // main setup
- cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist
- ons_worldcplist = cp;
-
- cp.netname = "Control point";
- cp.team = 0;
- cp.solid = SOLID_BBOX;
- cp.movetype = MOVETYPE_NONE;
- cp.touch = ons_ControlPoint_Touch;
- cp.think = ons_ControlPoint_Think;
- cp.nextthink = time + ONS_CP_THINKRATE;
- cp.reset = ons_ControlPoint_Reset;
- cp.colormap = 1024;
- cp.iscaptured = false;
- cp.islinked = false;
- cp.isshielded = true;
-
- if(cp.message == "") { cp.message = "a"; }
-
- // appearence
- setmodel_fixsize(cp, MDL_ONS_CP_PAD1);
-
- // control point placement
- if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location
- {
- cp.noalign = true;
- cp.movetype = MOVETYPE_NONE;
- }
- else // drop to floor, automatically find a platform and set that as spawn origin
- {
- setorigin(cp, cp.origin + '0 0 20');
- cp.noalign = false;
- setself(cp);
- droptofloor();
- cp.movetype = MOVETYPE_TOSS;
- }
-
- // waypointsprites
- WaypointSprite_SpawnFixed(WP_Null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE);
- WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY);
-
- InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION);
-}
-
-
-// =========================
-// Main Generator Functions
-// =========================
-
-entity ons_Generator_Waypoint(entity e)
-{
- if (e.isshielded)
- return WP_OnsGenShielded;
- return WP_OnsGen;
-}
-
-void ons_Generator_UpdateSprite(entity e)
-{
- entity s1 = ons_Generator_Waypoint(e);
- WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1);
-
- if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
- {
- e.lastteam = e.team + 2;
- e.lastshielded = e.isshielded;
- if(e.lastshielded)
- {
- if(e.team)
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false));
- else
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
- }
- else
- {
- if(e.team)
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false));
- else
- WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
- }
- WaypointSprite_Ping(e.sprite);
- }
-}
-
-void ons_GeneratorDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{SELFPARAM();
- if(damage <= 0) { return; }
- if(warmup_stage || gameover) { return; }
- if(!round_handler_IsRoundStarted()) { return; }
-
- if (attacker != self)
- {
- if (self.isshielded)
- {
- // this is protected by a shield, so ignore the damage
- if (time > self.pain_finished)
- if (IS_PLAYER(attacker))
- {
- play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD));
- attacker.typehitsound += 1;
- self.pain_finished = time + 1;
- }
- return;
- }
- if (time > self.pain_finished)
- {
- self.pain_finished = time + 10;
- entity head;
- FOR_EACH_REALPLAYER(head) if(SAME_TEAM(head, self)) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK); }
- play2team(self.team, SND(ONS_GENERATOR_UNDERATTACK));
- }
- }
- self.health = self.health - damage;
- WaypointSprite_UpdateHealth(self.sprite, self.health);
- // choose an animation frame based on health
- self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
- // see if the generator is still functional, or dying
- if (self.health > 0)
- {
- self.lasthealth = self.health;
- }
- else
- {
- if (attacker == self)
- Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_));
- else
- {
- Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_));
- PlayerScore_Add(attacker, SP_SCORE, 100);
- }
- self.iscaptured = false;
- self.islinked = false;
- self.isshielded = false;
- self.takedamage = DAMAGE_NO; // can't be hurt anymore
- self.event_damage = func_null; // won't do anything if hurt
- self.count = 0; // reset counter
- self.think = func_null;
- self.nextthink = 0;
- //self.think(); // do the first explosion now
-
- WaypointSprite_UpdateMaxHealth(self.sprite, 0);
- WaypointSprite_Ping(self.sprite);
- //WaypointSprite_Kill(self.sprite); // can't do this yet, code too poor
-
- onslaught_updatelinks();
- }
-
- // Throw some flaming gibs on damage, more damage = more chance for gib
- if(random() < damage/220)
- {
- sound(self, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
- }
- else
- {
- // particles on every hit
- Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1);
-
- //sound on every hit
- if (random() < 0.5)
- sound(self, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM);
- else
- sound(self, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM);
- }
-
- self.SendFlags |= GSF_STATUS;
-}
-
-void ons_GeneratorThink()
-{SELFPARAM();
- entity e;
- self.nextthink = time + GEN_THINKRATE;
- if (!gameover)
- {
- if(!self.isshielded && self.wait < time)
- {
- self.wait = time + 5;
- FOR_EACH_REALPLAYER(e)
- {
- if(SAME_TEAM(e, self))
- {
- Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM);
- soundto(MSG_ONE, e, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound?
- }
- else
- Send_Notification(NOTIF_ONE, e, MSG_CENTER, APP_TEAM_NUM_4(self.team, CENTER_ONS_NOTSHIELDED_));
- }
- }
- }
-}
-
-void ons_GeneratorReset()
-{SELFPARAM();
- self.team = self.team_saved;
- self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
- self.takedamage = DAMAGE_AIM;
- self.bot_attack = true;
- self.iscaptured = true;
- self.islinked = true;
- self.isshielded = true;
- self.event_damage = ons_GeneratorDamage;
- self.think = ons_GeneratorThink;
- self.nextthink = time + GEN_THINKRATE;
-
- Net_LinkEntity(self, false, 0, generator_send);
-
- self.SendFlags = GSF_SETUP; // just incase
- self.SendFlags |= GSF_STATUS;
-
- WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
- WaypointSprite_UpdateHealth(self.sprite, self.health);
- WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY);
-
- onslaught_updatelinks();
-}
-
-void ons_DelayedGeneratorSetup()
-{SELFPARAM();
- // bot waypoints
- waypoint_spawnforitem_force(self, self.origin);
- self.nearestwaypointtimeout = 0; // activate waypointing again
- self.bot_basewaypoint = self.nearestwaypoint;
-
- // captureshield setup
- ons_CaptureShield_Spawn(self, true);
-
- onslaught_updatelinks();
-
- Net_LinkEntity(self, false, 0, generator_send);
-}
-
-
-void onslaught_generator_touch()
-{SELFPARAM();
- if ( IS_PLAYER(other) )
- if ( SAME_TEAM(self,other) )
- if ( self.iscaptured )
- {
- Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT);
- }
-}
-
-void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc
-{SELFPARAM();
- // declarations
- int teamnumber = gen.team;
- setself(gen); // for later usage with droptofloor()
-
- // main setup
- gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist
- ons_worldgeneratorlist = gen;
-
- gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber));
- gen.classname = "onslaught_generator";
- gen.solid = SOLID_BBOX;
- gen.team_saved = teamnumber;
- gen.movetype = MOVETYPE_NONE;
- gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health;
- gen.takedamage = DAMAGE_AIM;
- gen.bot_attack = true;
- gen.event_damage = ons_GeneratorDamage;
- gen.reset = ons_GeneratorReset;
- gen.think = ons_GeneratorThink;
- gen.nextthink = time + GEN_THINKRATE;
- gen.iscaptured = true;
- gen.islinked = true;
- gen.isshielded = true;
- gen.touch = onslaught_generator_touch;
-
- // appearence
- // model handled by CSQC
- setsize(gen, GENERATOR_MIN, GENERATOR_MAX);
- setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET));
- gen.colormap = 1024 + (teamnumber - 1) * 17;
-
- // generator placement
- setself(gen);
- droptofloor();
-
- // waypointsprites
- WaypointSprite_SpawnFixed(WP_Null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE);
- WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY);
- WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
- WaypointSprite_UpdateHealth(self.sprite, self.health);
-
- InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION);
-}
-
-
-// ===============
-// Round Handler
-// ===============
-
-int total_generators;
-void Onslaught_count_generators()
-{
- entity e;
- total_generators = redowned = blueowned = yellowowned = pinkowned = 0;
- for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext)
- {
- ++total_generators;
- redowned += (e.team == NUM_TEAM_1 && e.health > 0);
- blueowned += (e.team == NUM_TEAM_2 && e.health > 0);
- yellowowned += (e.team == NUM_TEAM_3 && e.health > 0);
- pinkowned += (e.team == NUM_TEAM_4 && e.health > 0);
- }
-}
-
-int Onslaught_GetWinnerTeam()
-{
- int winner_team = 0;
- if(redowned > 0)
- winner_team = NUM_TEAM_1;
- if(blueowned > 0)
- {
- if(winner_team) return 0;
- winner_team = NUM_TEAM_2;
- }
- if(yellowowned > 0)
- {
- if(winner_team) return 0;
- winner_team = NUM_TEAM_3;
- }
- if(pinkowned > 0)
- {
- if(winner_team) return 0;
- winner_team = NUM_TEAM_4;
- }
- if(winner_team)
- return winner_team;
- return -1; // no generators left?
-}
-
-#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0))
-#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1)
-bool Onslaught_CheckWinner()
-{
- entity e;
-
- if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0))
- {
- ons_stalemate = true;
-
- if (!wpforenemy_announced)
- {
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT);
- sound(world, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE);
-
- wpforenemy_announced = true;
- }
-
- entity tmp_entity; // temporary entity
- float d;
- for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay)
- {
- // tmp_entity.max_health / 300 gives 5 minutes of overtime.
- // control points reduce the overtime duration.
- d = 1;
- for(e = ons_worldcplist; e; e = e.ons_worldcpnext)
- {
- if(DIFF_TEAM(e, tmp_entity))
- if(e.islinked)
- d = d + 1;
- }
-
- if(autocvar_g_campaign && autocvar__campaign_testrun)
- d = d * tmp_entity.max_health;
- else
- d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
-
- Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0');
-
- tmp_entity.sprite.SendFlags |= 16;
-
- tmp_entity.ons_overtime_damagedelay = time + 1;
- }
- }
- else { wpforenemy_announced = false; ons_stalemate = false; }
-
- Onslaught_count_generators();
-
- if(ONS_OWNED_GENERATORS_OK())
- return 0;
-
- int winner_team = Onslaught_GetWinnerTeam();
-
- if(winner_team > 0)
- {
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
- Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
- TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1);
- }
- else if(winner_team == -1)
- {
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
- }
-
- ons_stalemate = false;
-
- play2all(SND(CTF_CAPTURE(winner_team)));
-
- round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
-
- FOR_EACH_PLAYER(e)
- {
- e.ons_roundlost = true;
- e.player_blocked = true;
-
- nades_Clear(e);
- }
-
- return 1;
-}
-
-bool Onslaught_CheckPlayers()
-{
- return 1;
-}
-
-void Onslaught_RoundStart()
-{
- entity tmp_entity;
- FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = false; }
-
- for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
- tmp_entity.sprite.SendFlags |= 16;
-
- for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
- tmp_entity.sprite.SendFlags |= 16;
-}
-
-
-// ================
-// Bot player logic
-// ================
-
-// NOTE: LEGACY CODE, needs to be re-written!
-
-void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius)
-{SELFPARAM();
- entity head;
- float t, c;
- int i;
- bool needarmor = false, needweapons = false;
-
- // Needs armor/health?
- if(self.health<100)
- needarmor = true;
-
- // Needs weapons?
- c = 0;
- for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
- {
- // Find weapon
- if(self.weapons & WepSet_FromWeapon(i))
- if(++c>=4)
- break;
- }
-
- if(c<4)
- needweapons = true;
-
- if(!needweapons && !needarmor)
- return;
-
- ons_debug(strcat(self.netname, " needs weapons ", ftos(needweapons) , "\n"));
- ons_debug(strcat(self.netname, " needs armor ", ftos(needarmor) , "\n"));
-
- // See what is around
- head = findchainfloat(bot_pickup, true);
- while (head)
- {
- // gather health and armor only
- if (head.solid)
- if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) )
- if (vlen(head.origin - org) < sradius)
- {
- t = head.bot_pickupevalfunc(self, head);
- if (t > 0)
- navigation_routerating(head, t * ratingscale, 500);
- }
- head = head.chain;
- }
-}
-
-void havocbot_role_ons_setrole(entity bot, int role)
-{
- ons_debug(strcat(bot.netname," switched to "));
- switch(role)
- {
- case HAVOCBOT_ONS_ROLE_DEFENSE:
- ons_debug("defense");
- bot.havocbot_role = havocbot_role_ons_defense;
- bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE;
- bot.havocbot_role_timeout = 0;
- break;
- case HAVOCBOT_ONS_ROLE_ASSISTANT:
- ons_debug("assistant");
- bot.havocbot_role = havocbot_role_ons_assistant;
- bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT;
- bot.havocbot_role_timeout = 0;
- break;
- case HAVOCBOT_ONS_ROLE_OFFENSE:
- ons_debug("offense");
- bot.havocbot_role = havocbot_role_ons_offense;
- bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE;
- bot.havocbot_role_timeout = 0;
- break;
- }
- ons_debug("\n");
-}
-
-int havocbot_ons_teamcount(entity bot, int role)
-{SELFPARAM();
- int c = 0;
- entity head;
-
- FOR_EACH_PLAYER(head)
- if(SAME_TEAM(head, self))
- if(head.havocbot_role_flags & role)
- ++c;
-
- return c;
-}
-
-void havocbot_goalrating_ons_controlpoints_attack(float ratingscale)
-{SELFPARAM();
- entity cp, cp1, cp2, best, pl, wp;
- float radius, bestvalue;
- int c;
- bool found;
-
- // Filter control points
- for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext)
- {
- cp2.wpcost = c = 0;
- cp2.wpconsidered = false;
-
- if(cp2.isshielded)
- continue;
-
- // Ignore owned controlpoints
- if(!(cp2.isgenneighbor[self.team] || cp2.iscpneighbor[self.team]))
- continue;
-
- // Count team mates interested in this control point
- // (easier and cleaner than keeping counters per cp and teams)
- FOR_EACH_PLAYER(pl)
- if(SAME_TEAM(pl, self))
- if(pl.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE)
- if(pl.havocbot_ons_target==cp2)
- ++c;
-
- // NOTE: probably decrease the cost of attackable control points
- cp2.wpcost = c;
- cp2.wpconsidered = true;
- }
-
- // We'll consider only the best case
- bestvalue = 99999999999;
- cp = world;
- for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext)
- {
- if (!cp1.wpconsidered)
- continue;
-
- if(cp1.wpcost<bestvalue)
- {
- bestvalue = cp1.wpcost;
- cp = cp1;
- self.havocbot_ons_target = cp1;
- }
- }
-
- if (!cp)
- return;
-
- ons_debug(strcat(self.netname, " chose cp ranked ", ftos(bestvalue), "\n"));
-
- if(cp.goalentity)
- {
- // Should be attacked
- // Rate waypoints near it
- found = false;
- best = world;
- bestvalue = 99999999999;
- for(radius=0; radius<1000 && !found; radius+=500)
- {
- for(wp=findradius(cp.origin,radius); wp; wp=wp.chain)
- {
- if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
- if(wp.classname=="waypoint")
- if(checkpvs(wp.origin,cp))
- {
- found = true;
- if(wp.cnt<bestvalue)
- {
- best = wp;
- bestvalue = wp.cnt;
- }
- }
- }
- }
-
- if(best)
- {
- navigation_routerating(best, ratingscale, 10000);
- best.cnt += 1;
-
- self.havocbot_attack_time = 0;
- if(checkpvs(self.view_ofs,cp))
- if(checkpvs(self.view_ofs,best))
- self.havocbot_attack_time = time + 2;
- }
- else
- {
- navigation_routerating(cp, ratingscale, 10000);
- }
- ons_debug(strcat(self.netname, " found an attackable controlpoint at ", vtos(cp.origin) ,"\n"));
- }
- else
- {
- // Should be touched
- ons_debug(strcat(self.netname, " found a touchable controlpoint at ", vtos(cp.origin) ,"\n"));
- found = false;
-
- // Look for auto generated waypoint
- if (!bot_waypoints_for_items)
- for (wp = findradius(cp.origin,100); wp; wp = wp.chain)
- {
- if(wp.classname=="waypoint")
- {
- navigation_routerating(wp, ratingscale, 10000);
- found = true;
- }
- }
-
- // Nothing found, rate the controlpoint itself
- if (!found)
- navigation_routerating(cp, ratingscale, 10000);
- }
-}
-
-bool havocbot_goalrating_ons_generator_attack(float ratingscale)
-{SELFPARAM();
- entity g, wp, bestwp;
- bool found;
- int best;
-
- for(g = ons_worldgeneratorlist; g; g = g.ons_worldgeneratornext)
- {
- if(SAME_TEAM(g, self) || g.isshielded)
- continue;
-
- // Should be attacked
- // Rate waypoints near it
- found = false;
- bestwp = world;
- best = 99999999999;
-
- for(wp=findradius(g.origin,400); wp; wp=wp.chain)
- {
- if(wp.classname=="waypoint")
- if(checkpvs(wp.origin,g))
- {
- found = true;
- if(wp.cnt<best)
- {
- bestwp = wp;
- best = wp.cnt;
- }
- }
- }
-
- if(bestwp)
- {
- ons_debug("waypoints found around generator\n");
- navigation_routerating(bestwp, ratingscale, 10000);
- bestwp.cnt += 1;
-
- self.havocbot_attack_time = 0;
- if(checkpvs(self.view_ofs,g))
- if(checkpvs(self.view_ofs,bestwp))
- self.havocbot_attack_time = time + 5;
-
- return true;
- }
- else
- {
- ons_debug("generator found without waypoints around\n");
- // if there aren't waypoints near the generator go straight to it
- navigation_routerating(g, ratingscale, 10000);
- self.havocbot_attack_time = 0;
- return true;
- }
- }
- return false;
-}
-
-void havocbot_role_ons_offense()
-{SELFPARAM();
- if(self.deadflag != DEAD_NO)
- {
- self.havocbot_attack_time = 0;
- havocbot_ons_reset_role(self);
- return;
- }
-
- // Set the role timeout if necessary
- if (!self.havocbot_role_timeout)
- self.havocbot_role_timeout = time + 120;
-
- if (time > self.havocbot_role_timeout)
- {
- havocbot_ons_reset_role(self);
- return;
- }
-
- if(self.havocbot_attack_time>time)
- return;
-
- if (self.bot_strategytime < time)
- {
- navigation_goalrating_start();
- havocbot_goalrating_enemyplayers(20000, self.origin, 650);
- if(!havocbot_goalrating_ons_generator_attack(20000))
- havocbot_goalrating_ons_controlpoints_attack(20000);
- havocbot_goalrating_ons_offenseitems(10000, self.origin, 10000);
- navigation_goalrating_end();
-
- self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
- }
-}
-
-void havocbot_role_ons_assistant()
-{SELFPARAM();
- havocbot_ons_reset_role(self);
-}
-
-void havocbot_role_ons_defense()
-{SELFPARAM();
- havocbot_ons_reset_role(self);
-}
-
-void havocbot_ons_reset_role(entity bot)
-{SELFPARAM();
- entity head;
- int c = 0;
-
- if(self.deadflag != DEAD_NO)
- return;
-
- bot.havocbot_ons_target = world;
-
- // TODO: Defend control points or generator if necessary
-
- // if there is only me on the team switch to offense
- c = 0;
- FOR_EACH_PLAYER(head)
- if(SAME_TEAM(head, self))
- ++c;
-
- if(c==1)
- {
- havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
- return;
- }
-
- havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
-}
-
-
-/*
- * Find control point or generator owned by the same team self which is nearest to pos
- * if max_dist is positive, only control points within this range will be considered
- */
-entity ons_Nearest_ControlPoint(vector pos, float max_dist)
-{SELFPARAM();
- entity tmp_entity, closest_target = world;
- tmp_entity = findchain(classname, "onslaught_controlpoint");
- while(tmp_entity)
- {
- if(SAME_TEAM(tmp_entity, self))
- if(tmp_entity.iscaptured)
- if(max_dist <= 0 || vlen(tmp_entity.origin - pos) <= max_dist)
- if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world)
- closest_target = tmp_entity;
- tmp_entity = tmp_entity.chain;
- }
- tmp_entity = findchain(classname, "onslaught_generator");
- while(tmp_entity)
- {
- if(SAME_TEAM(tmp_entity, self))
- if(max_dist <= 0 || vlen(tmp_entity.origin - pos) < max_dist)
- if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world)
- closest_target = tmp_entity;
- tmp_entity = tmp_entity.chain;
- }
-
- return closest_target;
-}
-
-/*
- * Find control point or generator owned by the same team self which is nearest to pos
- * if max_dist is positive, only control points within this range will be considered
- * This function only check distances on the XY plane, disregarding Z
- */
-entity ons_Nearest_ControlPoint_2D(vector pos, float max_dist)
-{SELFPARAM();
- entity tmp_entity, closest_target = world;
- vector delta;
- float smallest_distance = 0, distance;
-
- tmp_entity = findchain(classname, "onslaught_controlpoint");
- while(tmp_entity)
- {
- delta = tmp_entity.origin - pos;
- delta_z = 0;
- distance = vlen(delta);
-
- if(SAME_TEAM(tmp_entity, self))
- if(tmp_entity.iscaptured)
- if(max_dist <= 0 || distance <= max_dist)
- if(closest_target == world || distance <= smallest_distance )
- {
- closest_target = tmp_entity;
- smallest_distance = distance;
- }
-
- tmp_entity = tmp_entity.chain;
- }
- tmp_entity = findchain(classname, "onslaught_generator");
- while(tmp_entity)
- {
- delta = tmp_entity.origin - pos;
- delta_z = 0;
- distance = vlen(delta);
-
- if(SAME_TEAM(tmp_entity, self))
- if(max_dist <= 0 || distance <= max_dist)
- if(closest_target == world || distance <= smallest_distance )
- {
- closest_target = tmp_entity;
- smallest_distance = distance;
- }
-
- tmp_entity = tmp_entity.chain;
- }
-
- return closest_target;
-}
-/**
- * find the number of control points and generators in the same team as self
- */
-int ons_Count_SelfControlPoints()
-{SELFPARAM();
- entity tmp_entity;
- tmp_entity = findchain(classname, "onslaught_controlpoint");
- int n = 0;
- while(tmp_entity)
- {
- if(SAME_TEAM(tmp_entity, self))
- if(tmp_entity.iscaptured)
- n++;
- tmp_entity = tmp_entity.chain;
- }
- tmp_entity = findchain(classname, "onslaught_generator");
- while(tmp_entity)
- {
- if(SAME_TEAM(tmp_entity, self))
- n++;
- tmp_entity = tmp_entity.chain;
- }
- return n;
-}
-
-/**
- * Teleport player to a random position near tele_target
- * if tele_effects is true, teleport sound+particles are created
- * return false on failure
- */
-bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects)
-{
- if ( !tele_target )
- return false;
-
- int i;
- vector loc;
- float theta;
- // narrow the range for each iteration to increase chances that a spawnpoint
- // can be found even if there's little room around the control point
- float iteration_scale = 1;
- for(i = 0; i < 16; ++i)
- {
- iteration_scale -= i / 16;
- theta = random() * 2 * M_PI;
- loc_y = sin(theta);
- loc_x = cos(theta);
- loc_z = 0;
- loc *= random() * range * iteration_scale;
-
- loc += tele_target.origin + '0 0 128' * iteration_scale;
-
- tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, player);
- if(trace_fraction == 1.0 && !trace_startsolid)
- {
- traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the world
- if(trace_fraction == 1.0 && !trace_startsolid)
- {
- if ( tele_effects )
- {
- Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1);
- sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM);
- }
- setorigin(player, loc);
- player.angles = '0 1 0' * ( theta * RAD2DEG + 180 );
- makevectors(player.angles);
- player.fixangle = true;
- player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait;
-
- if ( tele_effects )
- Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1);
- return true;
- }
- }
- }
-
- return false;
-}
-
-// ==============
-// Hook Functions
-// ==============
-
-MUTATOR_HOOKFUNCTION(ons, reset_map_global)
-{SELFPARAM();
- entity e;
- FOR_EACH_PLAYER(e)
- {
- e.ons_roundlost = false;
- e.ons_deathloc = '0 0 0';
- WITH(entity, self, e, PutClientInServer());
- }
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, ClientDisconnect)
-{SELFPARAM();
- self.ons_deathloc = '0 0 0';
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver)
-{SELFPARAM();
- self.ons_deathloc = '0 0 0';
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, PlayerSpawn)
-{SELFPARAM();
- if(!round_handler_IsRoundStarted())
- {
- self.player_blocked = true;
- return false;
- }
-
- entity l;
- for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
- {
- l.sprite.SendFlags |= 16;
- }
- for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
- {
- l.sprite.SendFlags |= 16;
- }
-
- if(ons_stalemate) { Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); }
-
- if ( autocvar_g_onslaught_spawn_choose )
- if ( self.ons_spawn_by )
- if ( ons_Teleport(self,self.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) )
- {
- self.ons_spawn_by = world;
- return false;
- }
-
- if(autocvar_g_onslaught_spawn_at_controlpoints)
- if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance)
- {
- float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random;
- entity tmp_entity, closest_target = world;
- vector spawn_loc = self.ons_deathloc;
-
- // new joining player or round reset, don't bother checking
- if(spawn_loc == '0 0 0') { return false; }
-
- if(random_target) { RandomSelection_Init(); }
-
- for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext)
- {
- if(SAME_TEAM(tmp_entity, self))
- if(random_target)
- RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
- else if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world)
- closest_target = tmp_entity;
- }
-
- if(random_target) { closest_target = RandomSelection_chosen_ent; }
-
- if(closest_target)
- {
- float i;
- vector loc;
- float iteration_scale = 1;
- for(i = 0; i < 10; ++i)
- {
- iteration_scale -= i / 10;
- loc = closest_target.origin + '0 0 96' * iteration_scale;
- loc += ('0 1 0' * random()) * 128 * iteration_scale;
- tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self);
- if(trace_fraction == 1.0 && !trace_startsolid)
- {
- traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world
- if(trace_fraction == 1.0 && !trace_startsolid)
- {
- setorigin(self, loc);
- self.angles = normalize(loc - closest_target.origin) * RAD2DEG;
- return false;
- }
- }
- }
- }
- }
-
- if(autocvar_g_onslaught_spawn_at_generator)
- if(random() <= autocvar_g_onslaught_spawn_at_generator_chance)
- {
- float random_target = autocvar_g_onslaught_spawn_at_generator_random;
- entity tmp_entity, closest_target = world;
- vector spawn_loc = self.ons_deathloc;
-
- // new joining player or round reset, don't bother checking
- if(spawn_loc == '0 0 0') { return false; }
-
- if(random_target) { RandomSelection_Init(); }
-
- for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
- {
- if(random_target)
- RandomSelection_Add(tmp_entity, 0, string_null, 1, 1);
- else
- {
- if(SAME_TEAM(tmp_entity, self))
- if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world)
- closest_target = tmp_entity;
- }
- }
-
- if(random_target) { closest_target = RandomSelection_chosen_ent; }
-
- if(closest_target)
- {
- float i;
- vector loc;
- float iteration_scale = 1;
- for(i = 0; i < 10; ++i)
- {
- iteration_scale -= i / 10;
- loc = closest_target.origin + '0 0 128' * iteration_scale;
- loc += ('0 1 0' * random()) * 256 * iteration_scale;
- tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self);
- if(trace_fraction == 1.0 && !trace_startsolid)
- {
- traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world
- if(trace_fraction == 1.0 && !trace_startsolid)
- {
- setorigin(self, loc);
- self.angles = normalize(loc - closest_target.origin) * RAD2DEG;
- return false;
- }
- }
- }
- }
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, PlayerDies)
-{SELFPARAM();
- frag_target.ons_deathloc = frag_target.origin;
- entity l;
- for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext)
- {
- l.sprite.SendFlags |= 16;
- }
- for(l = ons_worldcplist; l; l = l.ons_worldcpnext)
- {
- l.sprite.SendFlags |= 16;
- }
-
- if ( autocvar_g_onslaught_spawn_choose )
- if ( ons_Count_SelfControlPoints() > 1 )
- stuffcmd(self, "qc_cmd_cl hud clickradar\n");
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, MonsterMove)
-{SELFPARAM();
- entity e = find(world, targetname, self.target);
- if (e != world)
- self.team = e.team;
-
- return false;
-}
-
-void ons_MonsterSpawn_Delayed()
-{SELFPARAM();
- entity e, own = self.owner;
-
- if(!own) { remove(self); return; }
-
- if(own.targetname)
- {
- e = find(world, target, own.targetname);
- if(e != world)
- {
- own.team = e.team;
-
- activator = e;
- own.use();
- }
- }
-
- remove(self);
-}
-
-MUTATOR_HOOKFUNCTION(ons, MonsterSpawn)
-{SELFPARAM();
- entity e = spawn();
- e.owner = self;
- InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET);
-
- return false;
-}
-
-void ons_TurretSpawn_Delayed()
-{SELFPARAM();
- entity e, own = self.owner;
-
- if(!own) { remove(self); return; }
-
- if(own.targetname)
- {
- e = find(world, target, own.targetname);
- if(e != world)
- {
- own.team = e.team;
- own.active = ACTIVE_NOT;
-
- activator = e;
- own.use();
- }
- }
-
- remove(self);
-}
-
-MUTATOR_HOOKFUNCTION(ons, TurretSpawn)
-{SELFPARAM();
- entity e = spawn();
- e.owner = self;
- InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET);
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole)
-{SELFPARAM();
- havocbot_ons_reset_role(self);
- return true;
-}
-
-MUTATOR_HOOKFUNCTION(ons, GetTeamCount)
-{
- // onslaught is special
- for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext)
- {
- switch(tmp_entity.team)
- {
- case NUM_TEAM_1: c1 = 0; break;
- case NUM_TEAM_2: c2 = 0; break;
- case NUM_TEAM_3: c3 = 0; break;
- case NUM_TEAM_4: c4 = 0; break;
- }
- }
-
- return true;
-}
-
-MUTATOR_HOOKFUNCTION(ons, SpectateCopy)
-{SELFPARAM();
- self.ons_roundlost = other.ons_roundlost; // make spectators see it too
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand)
-{SELFPARAM();
- if(MUTATOR_RETURNVALUE) // command was already handled?
- return false;
-
- if ( cmd_name == "ons_spawn" )
- {
- vector pos = self.origin;
- if(cmd_argc > 1)
- pos_x = stof(argv(1));
- if(cmd_argc > 2)
- pos_y = stof(argv(2));
- if(cmd_argc > 3)
- pos_z = stof(argv(3));
-
- if ( IS_PLAYER(self) )
- {
- if ( !self.frozen )
- {
- entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius);
-
- if ( !source_point && self.health > 0 )
- {
- sprint(self, "\nYou need to be next to a control point\n");
- return 1;
- }
-
-
- entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius);
-
- if ( closest_target == world )
- {
- sprint(self, "\nNo control point found\n");
- return 1;
- }
-
- if ( self.health <= 0 )
- {
- self.ons_spawn_by = closest_target;
- self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
- }
- else
- {
- if ( source_point == closest_target )
- {
- sprint(self, "\nTeleporting to the same point\n");
- return 1;
- }
-
- if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,true) )
- sprint(self, "\nUnable to teleport there\n");
- }
-
- return 1;
- }
-
- sprint(self, "\nNo teleportation for you\n");
- }
-
- return 1;
- }
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(ons, PlayerUseKey)
-{SELFPARAM();
- if(MUTATOR_RETURNVALUE || gameover) { return false; }
-
- if((time > self.teleport_antispam) && (self.deadflag == DEAD_NO) && !self.vehicle)
- {
- entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius);
- if ( source_point )
- {
- stuffcmd(self, "qc_cmd_cl hud clickradar\n");
- return true;
- }
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, PlayHitsound)
-{
- return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded)
- || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded);
-}
-
-MUTATOR_HOOKFUNCTION(ons, SendWaypoint)
-{
- if(wp_sendflags & 16)
- {
- if(self.owner.classname == "onslaught_controlpoint")
- {
- entity wp_owner = self.owner;
- entity e = WaypointSprite_getviewentity(wp_sendto);
- if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; }
- if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; }
- }
- if(self.owner.classname == "onslaught_generator")
- {
- entity wp_owner = self.owner;
- if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; }
- if(wp_owner.health <= 0) { wp_flag |= 2; }
- }
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget)
-{
- if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job!
- {
- ret_float = -3;
- return true;
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ons, TurretThink)
-{
- // ONS uses somewhat backwards linking.
- if(self.target)
- {
- entity e = find(world, targetname, self.target);
- if (e != world)
- self.team = e.team;
- }
-
- if(self.team != self.tur_head.team)
- turret_respawn();
-
- return false;
-}
-
-
-// ==========
-// Spawnfuncs
-// ==========
-
-/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
- Link between control points.
-
- This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.
-
-keys:
-"target" - first control point.
-"target2" - second control point.
- */
-spawnfunc(onslaught_link)
-{
- if(!g_onslaught) { remove(self); return; }
-
- if (self.target == "" || self.target2 == "")
- objerror("target and target2 must be set\n");
-
- self.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist
- ons_worldlinklist = self;
-
- InitializeEntity(self, ons_DelayedLinkSetup, INITPRIO_FINDTARGET);
- Net_LinkEntity(self, false, 0, ons_Link_Send);
-}
-
-/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
- Control point. Be sure to give this enough clearance so that the shootable part has room to exist
-
- This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
-
-keys:
-"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
-"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
-"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
- */
-
-spawnfunc(onslaught_controlpoint)
-{
- if(!g_onslaught) { remove(self); return; }
-
- ons_ControlPoint_Setup(self);
-}
-
-/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
- Base generator.
-
- spawnfunc_onslaught_link entities can target this.
-
-keys:
-"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
-"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
- */
-spawnfunc(onslaught_generator)
-{
- if(!g_onslaught) { remove(self); return; }
- if(!self.team) { objerror("team must be set"); }
-
- ons_GeneratorSetup(self);
-}
-
-// scoreboard setup
-void ons_ScoreRules()
-{
- CheckAllowedTeams(world);
- ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true);
- ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY);
- ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY);
- ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0);
- ScoreRules_basics_end();
-}
-
-void ons_DelayedInit() // Do this check with a delay so we can wait for teams to be set up
-{
- ons_ScoreRules();
-
- round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart);
- round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
-}
-
-void ons_Initialize()
-{
- g_onslaught = true;
- ons_captureshield_force = autocvar_g_onslaught_shield_force;
-
- addstat(STAT_ROUNDLOST, AS_INT, ons_roundlost);
-
- InitializeEntity(world, ons_DelayedInit, INITPRIO_GAMETYPE);
-}
-
-#endif
REGISTER_MUTATOR(rc, false)
{
- rc_SetLimits();
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
race_Initialize();
+
+ rc_SetLimits();
}
MUTATOR_ONROLLBACK_OR_REMOVE
REGISTER_MUTATOR(tdm, false)
{
- ActivateTeamplay();
- SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, -1, -1);
- if (autocvar_g_tdm_team_spawns)
- have_team_spawns = -1; // request team spawns
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
InitializeEntity(world, tdm_DelayedInit, INITPRIO_GAMETYPE);
+
+ ActivateTeamplay();
+ SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, -1, -1);
+ if (autocvar_g_tdm_team_spawns)
+ have_team_spawns = -1; // request team spawns
}
MUTATOR_ONROLLBACK_OR_REMOVE
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss"));
-
-.float bloodloss_timer;
-
-MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink)
-{SELFPARAM();
- if(IS_PLAYER(self))
- if(self.health <= autocvar_g_bloodloss && self.deadflag == DEAD_NO)
- {
- self.BUTTON_CROUCH = true;
-
- if(time >= self.bloodloss_timer)
- {
- if(self.vehicle)
- vehicles_exit(VHEF_RELEASE);
- if(self.event_damage)
- self.event_damage(self, self, 1, DEATH_ROT.m_id, self.origin, '0 0 0');
- self.bloodloss_timer = time + 0.5 + random() * 0.5;
- }
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump)
-{SELFPARAM();
- if(self.health <= autocvar_g_bloodloss)
- return true;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":bloodloss");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Blood loss");
- return false;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-#include "../../../common/deathtypes/all.qh"
-#include "../../g_hook.qh"
-
-REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook"));
-
-bool autocvar_g_breakablehook; // allow toggling mid match?
-bool autocvar_g_breakablehook_owner;
-
-MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate)
-{
- if(frag_target.classname == "grapplinghook")
- {
- if((!autocvar_g_breakablehook)
- || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner)
- ) { frag_damage = 0; }
-
- // hurt the owner of the hook
- if(DIFF_TEAM(frag_attacker, frag_target.realowner))
- {
- Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0');
- RemoveGrapplingHook(frag_target.realowner);
- return false; // dead
- }
- }
-
- return false;
-}
-#endif
+++ /dev/null
-#ifndef MUTATOR_BUFFS_H
-#define MUTATOR_BUFFS_H
-
-// ammo
-.float buff_ammo_prev_infitems;
-.int buff_ammo_prev_clipload;
-// invisible
-.float buff_invisible_prev_alpha;
-// flight
-.float buff_flight_prev_gravity;
-// disability
-.float buff_disability_time;
-.float buff_disability_effect_time;
-// common buff variables
-.float buff_effect_delay;
-
-// buff definitions
-.float buff_active;
-.float buff_activetime;
-.float buff_activetime_updated;
-.entity buff_waypoint;
-.int oldbuffs; // for updating effects
-.entity buff_model; // controls effects (TODO: make csqc)
-
-const vector BUFF_MIN = ('-16 -16 -20');
-const vector BUFF_MAX = ('16 16 20');
-
-// client side options
-.float cvar_cl_buffs_autoreplace;
-#endif
-
-#ifdef IMPLEMENTATION
-
-#include "../../../common/triggers/target/music.qh"
-#include "../../../common/gamemodes/all.qh"
-#include "../../../common/buffs/all.qh"
-
-.float buff_time;
-void buffs_DelayedInit();
-
-REGISTER_MUTATOR(buffs, cvar("g_buffs"))
-{
- MUTATOR_ONADD
- {
- addstat(STAT_BUFFS, AS_INT, buffs);
- addstat(STAT_BUFF_TIME, AS_FLOAT, buff_time);
-
- InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET);
- }
-}
-
-entity buff_FirstFromFlags(int _buffs)
-{
- if (flags)
- {
- FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it));
- }
- return BUFF_Null;
-}
-
-bool buffs_BuffModel_Customize()
-{SELFPARAM();
- entity player, myowner;
- bool same_team;
-
- player = WaypointSprite_getviewentity(other);
- myowner = self.owner;
- same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner));
-
- if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0)
- return false;
-
- if(MUTATOR_CALLHOOK(BuffModel_Customize, self, player))
- return false;
-
- if(player == myowner || (IS_SPEC(other) && other.enemy == myowner))
- {
- // somewhat hide the model, but keep the glow
- self.effects = 0;
- self.alpha = -1;
- }
- else
- {
- self.effects = EF_FULLBRIGHT | EF_LOWPRECISION;
- self.alpha = 1;
- }
- return true;
-}
-
-void buffs_BuffModel_Spawn(entity player)
-{
- player.buff_model = spawn();
- setmodel(player.buff_model, MDL_BUFF);
- setsize(player.buff_model, '0 0 -40', '0 0 40');
- setattachment(player.buff_model, player, "");
- setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1));
- player.buff_model.owner = player;
- player.buff_model.scale = 0.7;
- player.buff_model.pflags = PFLAGS_FULLDYNAMIC;
- player.buff_model.light_lev = 200;
- player.buff_model.customizeentityforclient = buffs_BuffModel_Customize;
-}
-
-vector buff_GlowColor(entity buff)
-{
- //if(buff.team) { return Team_ColorRGB(buff.team); }
- return buff.m_color;
-}
-
-void buff_Effect(entity player, string eff)
-{SELFPARAM();
- if(!autocvar_g_buffs_effects) { return; }
-
- if(time >= self.buff_effect_delay)
- {
- Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1);
- self.buff_effect_delay = time + 0.05; // prevent spam
- }
-}
-
-// buff item
-float buff_Waypoint_visible_for_player(entity plr)
-{SELFPARAM();
- if(!self.owner.buff_active && !self.owner.buff_activetime)
- return false;
-
- if (plr.buffs)
- {
- return plr.cvar_cl_buffs_autoreplace == false || plr.buffs != self.owner.buffs;
- }
-
- return WaypointSprite_visible_for_player(plr);
-}
-
-void buff_Waypoint_Spawn(entity e)
-{
- entity buff = buff_FirstFromFlags(e.buffs);
- entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, world, e.team, e, buff_waypoint, true, RADARICON_Buff);
- wp.wp_extra = buff.m_id;
- WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod);
- e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player;
-}
-
-void buff_SetCooldown(float cd)
-{SELFPARAM();
- cd = max(0, cd);
-
- if(!self.buff_waypoint)
- buff_Waypoint_Spawn(self);
-
- WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + cd);
- self.buff_activetime = cd;
- self.buff_active = !cd;
-}
-
-void buff_Respawn(entity ent)
-{SELFPARAM();
- if(gameover) { return; }
-
- vector oldbufforigin = ent.origin;
-
- if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256))
- {
- entity spot = SelectSpawnPoint(true);
- setorigin(ent, ((spot.origin + '0 0 200') + (randomvec() * 300)));
- ent.angles = spot.angles;
- }
-
- tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent);
-
- setorigin(ent, trace_endpos); // attempt to unstick
-
- ent.movetype = MOVETYPE_TOSS;
-
- makevectors(ent.angles);
- ent.velocity = '0 0 200';
- ent.angles = '0 0 0';
- if(autocvar_g_buffs_random_lifetime > 0)
- ent.lifetime = time + autocvar_g_buffs_random_lifetime;
-
- Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1);
- Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(ent), '0 0 0', 1);
-
- WaypointSprite_Ping(ent.buff_waypoint);
-
- sound(ent, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
-}
-
-void buff_Touch()
-{SELFPARAM();
- if(gameover) { return; }
-
- if(ITEM_TOUCH_NEEDKILL())
- {
- buff_Respawn(self);
- return;
- }
-
- if((self.team && DIFF_TEAM(other, self))
- || (other.frozen)
- || (other.vehicle)
- || (!self.buff_active)
- )
- {
- // can't touch this
- return;
- }
-
- if(MUTATOR_CALLHOOK(BuffTouch, self, other))
- return;
-
- if(!IS_PLAYER(other))
- return; // incase mutator changed other
-
- if (other.buffs)
- {
- if (other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs)
- {
- int buffid = buff_FirstFromFlags(other.buffs).m_id;
- //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs);
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, buffid);
-
- other.buffs = 0;
- //sound(other, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
- }
- else { return; } // do nothing
- }
-
- self.owner = other;
- self.buff_active = false;
- self.lifetime = 0;
- int buffid = buff_FirstFromFlags(self.buffs).m_id;
- Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, buffid);
- Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, buffid);
-
- Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
- sound(other, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM);
- other.buffs |= (self.buffs);
-}
-
-float buff_Available(entity buff)
-{
- if (buff == BUFF_Null)
- return false;
- if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only"))))
- return false;
- if (buff == BUFF_VAMPIRE && cvar("g_vampire"))
- return false;
- return cvar(strcat("g_buffs_", buff.m_name));
-}
-
-.int buff_seencount;
-
-void buff_NewType(entity ent, float cb)
-{
- RandomSelection_Init();
- FOREACH(Buffs, buff_Available(it), LAMBDA(
- it.buff_seencount += 1;
- // if it's already been chosen, give it a lower priority
- RandomSelection_Add(world, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount));
- ));
- ent.buffs = RandomSelection_chosen_float;
-}
-
-void buff_Think()
-{SELFPARAM();
- if(self.buffs != self.oldbuffs)
- {
- entity buff = buff_FirstFromFlags(self.buffs);
- self.color = buff.m_color;
- self.glowmod = buff_GlowColor(buff);
- self.skin = buff.m_skin;
-
- setmodel(self, MDL_BUFF);
-
- if(self.buff_waypoint)
- {
- //WaypointSprite_Disown(self.buff_waypoint, 1);
- WaypointSprite_Kill(self.buff_waypoint);
- buff_Waypoint_Spawn(self);
- if(self.buff_activetime)
- WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + self.buff_activetime - frametime);
- }
-
- self.oldbuffs = self.buffs;
- }
-
- if(!gameover)
- if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
- if(!self.buff_activetime_updated)
- {
- buff_SetCooldown(self.buff_activetime);
- self.buff_activetime_updated = true;
- }
-
- if(!self.buff_active && !self.buff_activetime)
- if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
- {
- buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime);
- self.owner = world;
- if(autocvar_g_buffs_randomize)
- buff_NewType(self, self.buffs);
-
- if(autocvar_g_buffs_random_location || (self.spawnflags & 64))
- buff_Respawn(self);
- }
-
- if(self.buff_activetime)
- if(!gameover)
- if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
- {
- self.buff_activetime = max(0, self.buff_activetime - frametime);
-
- if(!self.buff_activetime)
- {
- self.buff_active = true;
- sound(self, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM);
- Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
- }
- }
-
- if(self.buff_active)
- {
- if(self.team && !self.buff_waypoint)
- buff_Waypoint_Spawn(self);
-
- if(self.lifetime)
- if(time >= self.lifetime)
- buff_Respawn(self);
- }
-
- self.nextthink = time;
- //self.angles_y = time * 110.1;
-}
-
-void buff_Waypoint_Reset()
-{SELFPARAM();
- WaypointSprite_Kill(self.buff_waypoint);
-
- if(self.buff_activetime) { buff_Waypoint_Spawn(self); }
-}
-
-void buff_Reset()
-{SELFPARAM();
- if(autocvar_g_buffs_randomize)
- buff_NewType(self, self.buffs);
- self.owner = world;
- buff_SetCooldown(autocvar_g_buffs_cooldown_activate);
- buff_Waypoint_Reset();
- self.buff_activetime_updated = false;
-
- if(autocvar_g_buffs_random_location || (self.spawnflags & 64))
- buff_Respawn(self);
-}
-
-float buff_Customize()
-{SELFPARAM();
- entity player = WaypointSprite_getviewentity(other);
- if(!self.buff_active || (self.team && DIFF_TEAM(player, self)))
- {
- self.alpha = 0.3;
- if(self.effects & EF_FULLBRIGHT) { self.effects &= ~(EF_FULLBRIGHT); }
- self.pflags = 0;
- }
- else
- {
- self.alpha = 1;
- if(!(self.effects & EF_FULLBRIGHT)) { self.effects |= EF_FULLBRIGHT; }
- self.light_lev = 220 + 36 * sin(time);
- self.pflags = PFLAGS_FULLDYNAMIC;
- }
- return true;
-}
-
-void buff_Init(entity ent)
-{SELFPARAM();
- if(!cvar("g_buffs")) { remove(ent); return; }
-
- if(!teamplay && ent.team) { ent.team = 0; }
-
- entity buff = buff_FirstFromFlags(self.buffs);
-
- setself(ent);
- if(!self.buffs || buff_Available(buff))
- buff_NewType(self, 0);
-
- self.classname = "item_buff";
- self.solid = SOLID_TRIGGER;
- self.flags = FL_ITEM;
- self.think = buff_Think;
- self.touch = buff_Touch;
- self.reset = buff_Reset;
- self.nextthink = time + 0.1;
- self.gravity = 1;
- self.movetype = MOVETYPE_TOSS;
- self.scale = 1;
- self.skin = buff.m_skin;
- self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW;
- self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
- self.customizeentityforclient = buff_Customize;
- //self.gravity = 100;
- self.color = buff.m_color;
- self.glowmod = buff_GlowColor(self);
- buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime);
- self.buff_active = !self.buff_activetime;
- self.pflags = PFLAGS_FULLDYNAMIC;
-
- if(self.spawnflags & 1)
- self.noalign = true;
-
- if(self.noalign)
- self.movetype = MOVETYPE_NONE; // reset by random location
-
- setmodel(self, MDL_BUFF);
- setsize(self, BUFF_MIN, BUFF_MAX);
-
- if(cvar("g_buffs_random_location") || (self.spawnflags & 64))
- buff_Respawn(self);
-
- setself(this);
-}
-
-void buff_Init_Compat(entity ent, entity replacement)
-{
- if (ent.spawnflags & 2)
- ent.team = NUM_TEAM_1;
- else if (ent.spawnflags & 4)
- ent.team = NUM_TEAM_2;
-
- ent.buffs = replacement.m_itemid;
-
- buff_Init(ent);
-}
-
-void buff_SpawnReplacement(entity ent, entity old)
-{
- setorigin(ent, old.origin);
- ent.angles = old.angles;
- ent.noalign = (old.noalign || (old.spawnflags & 1));
-
- buff_Init(ent);
-}
-
-void buff_Vengeance_DelayedDamage()
-{SELFPARAM();
- if(self.enemy)
- Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF.m_id, self.enemy.origin, '0 0 0');
-
- remove(self);
- return;
-}
-
-float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base)
-{
- return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base);
-}
-
-// mutator hooks
-MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor)
-{
- if(frag_deathtype == DEATH_BUFF.m_id) { return false; }
-
- if(frag_target.buffs & BUFF_RESISTANCE.m_itemid)
- {
- vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage);
- damage_take = v.x;
- damage_save = v.y;
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate)
-{
- if(frag_deathtype == DEATH_BUFF.m_id) { return false; }
-
- if(frag_target.buffs & BUFF_SPEED.m_itemid)
- if(frag_target != frag_attacker)
- frag_damage *= autocvar_g_buffs_speed_damage_take;
-
- if(frag_target.buffs & BUFF_MEDIC.m_itemid)
- if((frag_target.health - frag_damage) <= 0)
- if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
- if(frag_attacker)
- if(random() <= autocvar_g_buffs_medic_survive_chance)
- frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health);
-
- if(frag_target.buffs & BUFF_JUMP.m_itemid)
- if(frag_deathtype == DEATH_FALL.m_id)
- frag_damage = 0;
-
- if(frag_target.buffs & BUFF_VENGEANCE.m_itemid)
- if(frag_attacker)
- if(frag_attacker != frag_target)
- if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
- {
- entity dmgent = spawn();
-
- dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier;
- dmgent.enemy = frag_attacker;
- dmgent.owner = frag_target;
- dmgent.think = buff_Vengeance_DelayedDamage;
- dmgent.nextthink = time + 0.1;
- }
-
- if(frag_target.buffs & BUFF_BASH.m_itemid)
- if(frag_attacker != frag_target)
- if(vlen(frag_force))
- frag_force = '0 0 0';
-
- if(frag_attacker.buffs & BUFF_BASH.m_itemid)
- if(vlen(frag_force))
- if(frag_attacker == frag_target)
- frag_force *= autocvar_g_buffs_bash_force_self;
- else
- frag_force *= autocvar_g_buffs_bash_force;
-
- if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid)
- if(frag_target != frag_attacker)
- frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime;
-
- if(frag_attacker.buffs & BUFF_MEDIC.m_itemid)
- if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC)
- if(SAME_TEAM(frag_attacker, frag_target))
- if(frag_attacker != frag_target)
- {
- frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage);
- frag_damage = 0;
- }
-
- if(frag_attacker.buffs & BUFF_INFERNO.m_itemid)
- if(frag_target != frag_attacker) {
- float time = buff_Inferno_CalculateTime(
- frag_damage,
- 0,
- autocvar_g_buffs_inferno_burntime_min_time,
- autocvar_g_buffs_inferno_burntime_target_damage,
- autocvar_g_buffs_inferno_burntime_target_time,
- autocvar_g_buffs_inferno_burntime_factor
- );
- Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier) * time, time, DEATH_BUFF.m_id);
- }
-
- // this... is ridiculous (TODO: fix!)
- if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid)
- if(!frag_target.vehicle)
- if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC)
- if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
- if(frag_target.deadflag == DEAD_NO)
- if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target))
- if(frag_attacker != frag_target)
- if(!frag_target.frozen)
- if(frag_target.takedamage)
- if(DIFF_TEAM(frag_attacker, frag_target))
- {
- frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max);
- if(frag_target.armorvalue)
- frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max);
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs,PlayerSpawn)
-{SELFPARAM();
- self.buffs = 0;
- // reset timers here to prevent them continuing after re-spawn
- self.buff_disability_time = 0;
- self.buff_disability_effect_time = 0;
- return false;
-}
-
-.float stat_sv_maxspeed;
-.float stat_sv_airspeedlimit_nonqw;
-.float stat_sv_jumpvelocity;
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics)
-{SELFPARAM();
- if(self.buffs & BUFF_SPEED.m_itemid)
- {
- self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed;
- self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed;
- }
-
- if(time < self.buff_disability_time)
- {
- self.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed;
- self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed;
- }
-
- if(self.buffs & BUFF_JUMP.m_itemid)
- {
- // automatically reset, no need to worry
- self.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height;
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerJump)
-{SELFPARAM();
- if(self.buffs & BUFF_JUMP.m_itemid)
- player_jumpheight = autocvar_g_buffs_jump_height;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
-{SELFPARAM();
- if(time < self.buff_disability_time)
- {
- monster_speed_walk *= autocvar_g_buffs_disability_speed;
- monster_speed_run *= autocvar_g_buffs_disability_speed;
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerDies)
-{SELFPARAM();
- if(self.buffs)
- {
- int buffid = buff_FirstFromFlags(self.buffs).m_id;
- Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid);
- self.buffs = 0;
-
- if(self.buff_model)
- {
- remove(self.buff_model);
- self.buff_model = world;
- }
- }
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST)
-{SELFPARAM();
- if(MUTATOR_RETURNVALUE || gameover) { return false; }
- if(self.buffs)
- {
- int buffid = buff_FirstFromFlags(self.buffs).m_id;
- Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid);
- Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid);
-
- self.buffs = 0;
- sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
- return true;
- }
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon)
-{SELFPARAM();
- if(MUTATOR_RETURNVALUE || gameover) { return false; }
-
- if(self.buffs & BUFF_SWAPPER.m_itemid)
- {
- float best_distance = autocvar_g_buffs_swapper_range;
- entity closest = world;
- entity player;
- FOR_EACH_PLAYER(player)
- if(DIFF_TEAM(self, player))
- if(player.deadflag == DEAD_NO && !player.frozen && !player.vehicle)
- if(vlen(self.origin - player.origin) <= best_distance)
- {
- best_distance = vlen(self.origin - player.origin);
- closest = player;
- }
-
- if(closest)
- {
- vector my_org, my_vel, my_ang, their_org, their_vel, their_ang;
-
- my_org = self.origin;
- my_vel = self.velocity;
- my_ang = self.angles;
- their_org = closest.origin;
- their_vel = closest.velocity;
- their_ang = closest.angles;
-
- Drop_Special_Items(closest);
-
- MUTATOR_CALLHOOK(PortalTeleport, self); // initiate flag dropper
-
- setorigin(self, their_org);
- setorigin(closest, my_org);
-
- closest.velocity = my_vel;
- closest.angles = my_ang;
- closest.fixangle = true;
- closest.oldorigin = my_org;
- closest.oldvelocity = my_vel;
- self.velocity = their_vel;
- self.angles = their_ang;
- self.fixangle = true;
- self.oldorigin = their_org;
- self.oldvelocity = their_vel;
-
- // set pusher so self gets the kill if they fall into void
- closest.pusher = self;
- closest.pushltime = time + autocvar_g_maxpushtime;
- closest.istypefrag = closest.BUTTON_CHAT;
-
- Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1);
- Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1);
-
- sound(self, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM);
- sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM);
-
- // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam
- self.buffs = 0;
- return true;
- }
- }
- return false;
-}
-
-bool buffs_RemovePlayer(entity player)
-{
- if(player.buff_model)
- {
- remove(player.buff_model);
- player.buff_model = world;
- }
-
- // also reset timers here to prevent them continuing after spectating
- player.buff_disability_time = 0;
- player.buff_disability_effect_time = 0;
-
- return false;
-}
-MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { return buffs_RemovePlayer(self); }
-MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { return buffs_RemovePlayer(self); }
-
-MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint)
-{SELFPARAM();
- entity e = WaypointSprite_getviewentity(other);
-
- // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
- // but only apply this to real players, not to spectators
- if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == other))
- if(DIFF_TEAM(self.owner, e))
- return true;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST)
-{SELFPARAM();
- if(autocvar_g_buffs_replace_powerups)
- switch(self.classname)
- {
- case "item_strength":
- case "item_invincible":
- {
- entity e = spawn();
- buff_SpawnReplacement(e, self);
- return true;
- }
- }
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor)
-{SELFPARAM();
- if(self.buffs & BUFF_SPEED.m_itemid)
- weapon_rate *= autocvar_g_buffs_speed_rate;
-
- if(time < self.buff_disability_time)
- weapon_rate *= autocvar_g_buffs_disability_rate;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor)
-{SELFPARAM();
- if(self.buffs & BUFF_SPEED.m_itemid)
- ret_float *= autocvar_g_buffs_speed_weaponspeed;
-
- if(time < self.buff_disability_time)
- ret_float *= autocvar_g_buffs_disability_weaponspeed;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
-{SELFPARAM();
- if(gameover || self.deadflag != DEAD_NO) { return false; }
-
- if(time < self.buff_disability_time)
- if(time >= self.buff_disability_effect_time)
- {
- Send_Effect(EFFECT_SMOKING, self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1);
- self.buff_disability_effect_time = time + 0.5;
- }
-
- // handle buff lost status
- // 1: notify everyone else
- // 2: notify carrier as well
- int buff_lost = 0;
-
- if(self.buff_time)
- if(time >= self.buff_time)
- buff_lost = 2;
-
- if(self.frozen) { buff_lost = 1; }
-
- if(buff_lost)
- {
- if(self.buffs)
- {
- int buffid = buff_FirstFromFlags(self.buffs).m_id;
- Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid);
- if(buff_lost >= 2)
- {
- Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message?
- sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
- }
- self.buffs = 0;
- }
- }
-
- if(self.buffs & BUFF_MAGNET.m_itemid)
- {
- vector pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item;
- for(other = world; (other = findflags(other, flags, FL_ITEM)); )
- if(boxesoverlap(self.absmin - pickup_size, self.absmax + pickup_size, other.absmin, other.absmax))
- {
- setself(other);
- other = this;
- if(self.touch)
- self.touch();
- other = self;
- setself(this);
- }
- }
-
- if(self.buffs & BUFF_AMMO.m_itemid)
- if(self.clip_size)
- self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size;
-
- if((self.buffs & BUFF_INVISIBLE.m_itemid) && (self.oldbuffs & BUFF_INVISIBLE.m_itemid))
- if(self.alpha != autocvar_g_buffs_invisible_alpha)
- self.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO)
-
-#define BUFF_ONADD(b) if ( (self.buffs & (b).m_itemid) && !(self.oldbuffs & (b).m_itemid))
-#define BUFF_ONREM(b) if (!(self.buffs & (b).m_itemid) && (self.oldbuffs & (b).m_itemid))
-
- if(self.buffs != self.oldbuffs)
- {
- entity buff = buff_FirstFromFlags(self.buffs);
- float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0;
- self.buff_time = (bufftime) ? time + bufftime : 0;
-
- BUFF_ONADD(BUFF_AMMO)
- {
- self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO);
- self.items |= IT_UNLIMITED_WEAPON_AMMO;
-
- if(self.clip_load)
- self.buff_ammo_prev_clipload = self.clip_load;
- self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size;
- }
-
- BUFF_ONREM(BUFF_AMMO)
- {
- if(self.buff_ammo_prev_infitems)
- self.items |= IT_UNLIMITED_WEAPON_AMMO;
- else
- self.items &= ~IT_UNLIMITED_WEAPON_AMMO;
-
- if(self.buff_ammo_prev_clipload)
- self.clip_load = self.buff_ammo_prev_clipload;
- }
-
- BUFF_ONADD(BUFF_INVISIBLE)
- {
- if(time < self.strength_finished && g_instagib)
- self.alpha = autocvar_g_instagib_invis_alpha;
- else
- self.alpha = self.buff_invisible_prev_alpha;
- self.alpha = autocvar_g_buffs_invisible_alpha;
- }
-
- BUFF_ONREM(BUFF_INVISIBLE)
- self.alpha = self.buff_invisible_prev_alpha;
-
- BUFF_ONADD(BUFF_FLIGHT)
- {
- self.buff_flight_prev_gravity = self.gravity;
- self.gravity = autocvar_g_buffs_flight_gravity;
- }
-
- BUFF_ONREM(BUFF_FLIGHT)
- self.gravity = self.buff_flight_prev_gravity;
-
- self.oldbuffs = self.buffs;
- if(self.buffs)
- {
- if(!self.buff_model)
- buffs_BuffModel_Spawn(self);
-
- self.buff_model.color = buff.m_color;
- self.buff_model.glowmod = buff_GlowColor(self.buff_model);
- self.buff_model.skin = buff.m_skin;
-
- self.effects |= EF_NOSHADOW;
- }
- else
- {
- remove(self.buff_model);
- self.buff_model = world;
-
- self.effects &= ~(EF_NOSHADOW);
- }
- }
-
- if(self.buff_model)
- {
- self.buff_model.effects = self.effects;
- self.buff_model.effects |= EF_LOWPRECISION;
- self.buff_model.effects = self.buff_model.effects & EFMASK_CHEAP; // eat performance
-
- self.buff_model.alpha = self.alpha;
- }
-
-#undef BUFF_ONADD
-#undef BUFF_ONREM
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, SpectateCopy)
-{SELFPARAM();
- self.buffs = other.buffs;
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, VehicleEnter)
-{
- vh_vehicle.buffs = vh_player.buffs;
- vh_player.buffs = 0;
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, VehicleExit)
-{
- vh_player.buffs = vh_vehicle.buffs;
- vh_vehicle.buffs = 0;
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerRegen)
-{SELFPARAM();
- if(self.buffs & BUFF_MEDIC.m_itemid)
- {
- regen_mod_rot = autocvar_g_buffs_medic_rot;
- regen_mod_limit = regen_mod_max = autocvar_g_buffs_medic_max;
- regen_mod_regen = autocvar_g_buffs_medic_regen;
- }
-
- if(self.buffs & BUFF_SPEED.m_itemid)
- regen_mod_regen = autocvar_g_buffs_speed_regen;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, GetCvars)
-{
- GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":Buffs");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Buffs");
- return false;
-}
-
-void buffs_DelayedInit()
-{
- if(autocvar_g_buffs_spawn_count > 0)
- if(find(world, classname, "item_buff") == world)
- {
- float i;
- for(i = 0; i < autocvar_g_buffs_spawn_count; ++i)
- {
- entity e = spawn();
- e.spawnflags |= 64; // always randomize
- e.velocity = randomvec() * 250; // this gets reset anyway if random location works
- buff_Init(e);
- }
- }
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(campcheck, cvar("g_campcheck"));
-
-.float campcheck_nextcheck;
-.float campcheck_traveled_distance;
-
-MUTATOR_HOOKFUNCTION(campcheck, PlayerDies)
-{SELFPARAM();
- Kill_Notification(NOTIF_ONE, self, MSG_CENTER_CPID, CPID_CAMPCHECK);
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate)
-{
- if(IS_PLAYER(frag_target))
- if(IS_PLAYER(frag_attacker))
- if(frag_attacker != frag_target)
- {
- frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance;
- frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance;
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink)
-{SELFPARAM();
- if(!gameover)
- if(!warmup_stage) // don't consider it camping during warmup?
- if(time >= game_starttime)
- if(IS_PLAYER(self))
- if(IS_REAL_CLIENT(self)) // bots may camp, but that's no reason to constantly kill them
- if(self.deadflag == DEAD_NO)
- if(!self.frozen)
- if(!self.BUTTON_CHAT)
- if(autocvar_g_campcheck_interval)
- {
- vector dist;
-
- // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
- dist = self.prevorigin - self.origin;
- dist.z = 0;
- self.campcheck_traveled_distance += fabs(vlen(dist));
-
- if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
- {
- self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2;
- self.campcheck_traveled_distance = 0;
- }
-
- if(time > self.campcheck_nextcheck)
- {
- if(self.campcheck_traveled_distance < autocvar_g_campcheck_distance)
- {
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_CAMPCHECK);
- if(self.vehicle)
- Damage(self.vehicle, self, self, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, self.vehicle.origin, '0 0 0');
- else
- Damage(self, self, self, bound(0, autocvar_g_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, self.origin, '0 0 0');
- }
- self.campcheck_nextcheck = time + autocvar_g_campcheck_interval;
- self.campcheck_traveled_distance = 0;
- }
-
- return false;
- }
-
- self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn)
-{SELFPARAM();
- self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2;
- self.campcheck_traveled_distance = 0;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":CampCheck");
- return false;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-
-#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
-
-float g_dodging;
-
-// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
-.float dodging_action;
-
-// the jump part of the dodge cannot be ramped
-.float dodging_single_action;
-
-#include "../../../common/animdecide.qh"
-#include "../../../common/physics.qh"
-
-.float cvar_cl_dodging_timeout;
-
-.float stat_dodging;
-.float stat_dodging_delay;
-.float stat_dodging_horiz_speed_frozen;
-.float stat_dodging_frozen_nodoubletap;
-.float stat_dodging_frozen;
-.float stat_dodging_horiz_speed;
-.float stat_dodging_height_threshold;
-.float stat_dodging_distance_threshold;
-.float stat_dodging_ramp_time;
-.float stat_dodging_up_speed;
-.float stat_dodging_wall;
-
-REGISTER_MUTATOR(dodging, cvar("g_dodging"))
-{
- // this just turns on the cvar.
- MUTATOR_ONADD
- {
- g_dodging = cvar("g_dodging");
- addstat(STAT_DODGING, AS_INT, stat_dodging);
- addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay);
- addstat(STAT_DODGING_TIMEOUT, AS_FLOAT, cvar_cl_dodging_timeout); // we stat this, so it is updated on the client when updated on server (otherwise, chaos)
- addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap);
- addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen);
- addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen);
- addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed);
- addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold);
- addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold);
- addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time);
- addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed);
- addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall);
- }
-
- // this just turns off the cvar.
- MUTATOR_ONROLLBACK_OR_REMOVE
- {
- g_dodging = 0;
- }
-
- return false;
-}
-
-#endif
-
-// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
-.float dodging_action;
-
-// the jump part of the dodge cannot be ramped
-.float dodging_single_action;
-
-
-// these are used to store the last key press time for each of the keys..
-.float last_FORWARD_KEY_time;
-.float last_BACKWARD_KEY_time;
-.float last_LEFT_KEY_time;
-.float last_RIGHT_KEY_time;
-
-// these store the movement direction at the time of the dodge action happening.
-.vector dodging_direction;
-
-// this indicates the last time a dodge was executed. used to check if another one is allowed
-// and to ramp up the dodge acceleration in the physics hook.
-.float last_dodging_time;
-
-// This is the velocity gain to be added over the ramp time.
-// It will decrease from frame to frame during dodging_action = 1
-// until it's 0.
-.float dodging_velocity_gain;
-
-#ifdef CSQC
-.int pressedkeys;
-
-#elif defined(SVQC)
-
-void dodging_UpdateStats()
-{SELFPARAM();
- self.stat_dodging = PHYS_DODGING;
- self.stat_dodging_delay = PHYS_DODGING_DELAY;
- self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN;
- self.stat_dodging_frozen = PHYS_DODGING_FROZEN;
- self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP;
- self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD;
- self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD;
- self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME;
- self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED;
- self.stat_dodging_wall = PHYS_DODGING_WALL;
-}
-
-#endif
-
-// returns 1 if the player is close to a wall
-bool check_close_to_wall(float threshold)
-{SELFPARAM();
- if (PHYS_DODGING_WALL == 0) { return false; }
-
- #define X(OFFSET) \
- tracebox(self.origin, self.mins, self.maxs, self.origin + OFFSET, true, self); \
- if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold) \
- return true;
- X(1000*v_right);
- X(-1000*v_right);
- X(1000*v_forward);
- X(-1000*v_forward);
- #undef X
-
- return false;
-}
-
-bool check_close_to_ground(float threshold)
-{SELFPARAM();
- return IS_ONGROUND(self) ? true : false;
-}
-
-float PM_dodging_checkpressedkeys()
-{SELFPARAM();
- if(!PHYS_DODGING)
- return false;
-
- float frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN);
- float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
-
- // first check if the last dodge is far enough back in time so we can dodge again
- if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY)
- return false;
-
- makevectors(self.angles);
-
- if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1
- && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1)
- return true;
-
- float tap_direction_x = 0;
- float tap_direction_y = 0;
- float dodge_detected = 0;
-
- #define X(COND,BTN,RESULT) \
- if (self.movement_##COND) \
- /* is this a state change? */ \
- if(!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_##BTN) || frozen_no_doubletap) { \
- tap_direction_##RESULT; \
- if ((time - self.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(self)) \
- dodge_detected = 1; \
- self.last_##BTN##_KEY_time = time; \
- }
- X(x < 0, BACKWARD, x--);
- X(x > 0, FORWARD, x++);
- X(y < 0, LEFT, y--);
- X(y > 0, RIGHT, y++);
- #undef X
-
- if (dodge_detected == 1)
- {
- self.last_dodging_time = time;
-
- self.dodging_action = 1;
- self.dodging_single_action = 1;
-
- self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
-
- self.dodging_direction_x = tap_direction_x;
- self.dodging_direction_y = tap_direction_y;
-
- // normalize the dodging_direction vector.. (unlike UT99) XD
- float length = self.dodging_direction_x * self.dodging_direction_x
- + self.dodging_direction_y * self.dodging_direction_y;
- length = sqrt(length);
-
- self.dodging_direction_x = self.dodging_direction_x * 1.0 / length;
- self.dodging_direction_y = self.dodging_direction_y * 1.0 / length;
- return true;
- }
- return false;
-}
-
-void PM_dodging()
-{SELFPARAM();
- if (!PHYS_DODGING)
- return;
-
-#ifdef SVQC
- dodging_UpdateStats();
-#endif
-
- if (PHYS_DEAD(self))
- return;
-
- // when swimming, no dodging allowed..
- if (self.waterlevel >= WATERLEVEL_SWIMMING)
- {
- self.dodging_action = 0;
- self.dodging_direction_x = 0;
- self.dodging_direction_y = 0;
- return;
- }
-
- // make sure v_up, v_right and v_forward are sane
- makevectors(self.angles);
-
- // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
- // will be called ramp_time/frametime times = 2 times. so, we need to
- // add 0.5 * the total speed each frame until the dodge action is done..
- float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
-
- // if ramp time is smaller than frametime we get problems ;D
- common_factor = min(common_factor, 1);
-
- float horiz_speed = PHYS_FROZEN(self) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
- float new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed);
- new_velocity_gain = max(0, new_velocity_gain);
-
- float velocity_difference = self.dodging_velocity_gain - new_velocity_gain;
-
- // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
- if (self.dodging_action == 1)
- {
- //disable jump key during dodge accel phase
- if(self.movement_z > 0) { self.movement_z = 0; }
-
- self.velocity += ((self.dodging_direction_y * velocity_difference) * v_right)
- + ((self.dodging_direction_x * velocity_difference) * v_forward);
-
- self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference;
- }
-
- // the up part of the dodge is a single shot action
- if (self.dodging_single_action == 1)
- {
- UNSET_ONGROUND(self);
-
- self.velocity += PHYS_DODGING_UP_SPEED * v_up;
-
-#ifdef SVQC
- if (autocvar_sv_dodging_sound)
- PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND);
-
- animdecide_setaction(self, ANIMACTION_JUMP, true);
-#endif
-
- self.dodging_single_action = 0;
- }
-
- // are we done with the dodging ramp yet?
- if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
- {
- // reset state so next dodge can be done correctly
- self.dodging_action = 0;
- self.dodging_direction_x = 0;
- self.dodging_direction_y = 0;
- }
-}
-
-#ifdef SVQC
-
-MUTATOR_HOOKFUNCTION(dodging, GetCvars)
-{
- GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics)
-{
- // print("dodging_PlayerPhysics\n");
- PM_dodging();
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys)
-{
- PM_dodging_checkpressedkeys();
-
- return false;
-}
-
-#endif
-
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up"));
-#ifdef SVQC
-REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) {
- MUTATOR_ONADD {
- g_grappling_hook = true;
- WEP_HOOK.ammo_factor = 0;
- }
- MUTATOR_ONROLLBACK_OR_REMOVE {
- g_grappling_hook = false;
- WEP_HOOK.ammo_factor = 1;
- }
-}
-
-MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":grappling_hook");
-}
-
-MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Hook");
-}
-
-MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString)
-{
- ret_string = strcat(ret_string, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
-}
-
-MUTATOR_HOOKFUNCTION(hook, PlayerSpawn)
-{
- SELFPARAM();
- self.offhand = OFFHAND_HOOK;
-}
-
-MUTATOR_HOOKFUNCTION(hook, FilterItem)
-{
- return self.weapon == WEP_HOOK.m_id;
-}
-
-#endif
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles"));
-
-MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile)
-{
- if(other.health)
- {
- // disable health which in effect disables damage calculations
- other.health = 0;
- }
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":InvincibleProjectiles");
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Invincible Projectiles");
- return 0;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball);
-
-MUTATOR_HOOKFUNCTION(melee_only, SetStartItems)
-{
- start_ammo_shells = warmup_start_ammo_shells = 0;
- start_weapons = warmup_start_weapons = WEPSET(SHOTGUN);
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon)
-{
- return true;
-}
-
-MUTATOR_HOOKFUNCTION(melee_only, FilterItem)
-{SELFPARAM();
- switch (self.items)
- {
- case ITEM_HealthSmall.m_itemid:
- case ITEM_ArmorSmall.m_itemid:
- return false;
- }
-
- return true;
-}
-
-MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":MeleeOnly");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Melee Only Arena");
- return false;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(midair, cvar("g_midair"));
-
-.float midair_shieldtime;
-
-MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate)
-{SELFPARAM();
- if(IS_PLAYER(frag_attacker))
- if(IS_PLAYER(frag_target))
- if(time < self.midair_shieldtime)
- frag_damage = false;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(midair, PlayerPowerups)
-{SELFPARAM();
- if(time >= game_starttime)
- if(self.flags & FL_ONGROUND)
- {
- self.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
- self.midair_shieldtime = max(self.midair_shieldtime, time + autocvar_g_midair_shieldtime);
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(midair, PlayerSpawn)
-{SELFPARAM();
- if(IS_BOT_CLIENT(self))
- self.bot_moveskill = 0; // disable bunnyhopping
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":midair");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Midair");
- return false;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-#ifdef SVQC
- #include "../../antilag.qh"
-#endif
-#include "../../../common/physics.qh"
-
-.int multijump_count;
-.bool multijump_ready;
-.bool cvar_cl_multijump;
-
-#ifdef CSQC
-
-#define PHYS_MULTIJUMP getstati(STAT_MULTIJUMP)
-#define PHYS_MULTIJUMP_SPEED getstatf(STAT_MULTIJUMP_SPEED)
-#define PHYS_MULTIJUMP_ADD getstati(STAT_MULTIJUMP_ADD)
-#define PHYS_MULTIJUMP_MAXSPEED getstatf(STAT_MULTIJUMP_MAXSPEED)
-#define PHYS_MULTIJUMP_DODGING getstati(STAT_MULTIJUMP_DODGING)
-
-#elif defined(SVQC)
-
-#define PHYS_MULTIJUMP autocvar_g_multijump
-#define PHYS_MULTIJUMP_SPEED autocvar_g_multijump_speed
-#define PHYS_MULTIJUMP_ADD autocvar_g_multijump_add
-#define PHYS_MULTIJUMP_MAXSPEED autocvar_g_multijump_maxspeed
-#define PHYS_MULTIJUMP_DODGING autocvar_g_multijump_dodging
-
-
-.float stat_multijump;
-.float stat_multijump_speed;
-.float stat_multijump_add;
-.float stat_multijump_maxspeed;
-.float stat_multijump_dodging;
-
-void multijump_UpdateStats()
-{SELFPARAM();
- self.stat_multijump = PHYS_MULTIJUMP;
- self.stat_multijump_speed = PHYS_MULTIJUMP_SPEED;
- self.stat_multijump_add = PHYS_MULTIJUMP_ADD;
- self.stat_multijump_maxspeed = PHYS_MULTIJUMP_MAXSPEED;
- self.stat_multijump_dodging = PHYS_MULTIJUMP_DODGING;
-}
-
-void multijump_AddStats()
-{
- addstat(STAT_MULTIJUMP, AS_INT, stat_multijump);
- addstat(STAT_MULTIJUMP_SPEED, AS_FLOAT, stat_multijump_speed);
- addstat(STAT_MULTIJUMP_ADD, AS_INT, stat_multijump_add);
- addstat(STAT_MULTIJUMP_MAXSPEED, AS_FLOAT, stat_multijump_maxspeed);
- addstat(STAT_MULTIJUMP_DODGING, AS_INT, stat_multijump_dodging);
-}
-
-#endif
-
-void PM_multijump()
-{SELFPARAM();
- if(!PHYS_MULTIJUMP) { return; }
-
- if(IS_ONGROUND(self))
- {
- self.multijump_count = 0;
- }
-}
-
-bool PM_multijump_checkjump()
-{SELFPARAM();
- if(!PHYS_MULTIJUMP) { return false; }
-
-#ifdef SVQC
- bool client_multijump = self.cvar_cl_multijump;
-#elif defined(CSQC)
- bool client_multijump = cvar("cl_multijump");
-
- if(cvar("cl_multijump") > 1)
- return false; // nope
-#endif
-
- if (!IS_JUMP_HELD(self) && !IS_ONGROUND(self) && client_multijump) // jump button pressed this frame and we are in midair
- self.multijump_ready = true; // this is necessary to check that we released the jump button and pressed it again
- else
- self.multijump_ready = false;
-
- int phys_multijump = PHYS_MULTIJUMP;
-
-#ifdef CSQC
- phys_multijump = (PHYS_MULTIJUMP) ? -1 : 0;
-#endif
-
- if(!player_multijump && self.multijump_ready && (self.multijump_count < phys_multijump || phys_multijump == -1) && self.velocity_z > PHYS_MULTIJUMP_SPEED && (!PHYS_MULTIJUMP_MAXSPEED || vlen(self.velocity) <= PHYS_MULTIJUMP_MAXSPEED))
- {
- if (PHYS_MULTIJUMP)
- {
- if (!PHYS_MULTIJUMP_ADD) // in this case we make the z velocity == jumpvelocity
- {
- if (self.velocity_z < PHYS_JUMPVELOCITY)
- {
- player_multijump = true;
- self.velocity_z = 0;
- }
- }
- else
- player_multijump = true;
-
- if(player_multijump)
- {
- if(PHYS_MULTIJUMP_DODGING)
- if(self.movement_x != 0 || self.movement_y != 0) // don't remove all speed if player isnt pressing any movement keys
- {
- float curspeed;
- vector wishvel, wishdir;
-
-/*#ifdef SVQC
- curspeed = max(
- vlen(vec2(self.velocity)), // current xy speed
- vlen(vec2(antilag_takebackavgvelocity(self, max(self.lastteleporttime + sys_frametime, time - 0.25), time))) // average xy topspeed over the last 0.25 secs
- );
-#elif defined(CSQC)*/
- curspeed = vlen(vec2(self.velocity));
-//#endif
-
- makevectors(self.v_angle_y * '0 1 0');
- wishvel = v_forward * self.movement_x + v_right * self.movement_y;
- wishdir = normalize(wishvel);
-
- self.velocity_x = wishdir_x * curspeed; // allow "dodging" at a multijump
- self.velocity_y = wishdir_y * curspeed;
- // keep velocity_z unchanged!
- }
- if (PHYS_MULTIJUMP > 0)
- {
- self.multijump_count += 1;
- }
- }
- }
- self.multijump_ready = false; // require releasing and pressing the jump button again for the next jump
- }
-
- return false;
-}
-
-#ifdef SVQC
-REGISTER_MUTATOR(multijump, cvar("g_multijump"))
-{
- MUTATOR_ONADD
- {
- multijump_AddStats();
- }
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(multijump, PlayerPhysics)
-{
- multijump_UpdateStats();
- PM_multijump();
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(multijump, PlayerJump)
-{
- return PM_multijump_checkjump();
-}
-
-MUTATOR_HOOKFUNCTION(multijump, GetCvars)
-{
- GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_multijump, "cl_multijump");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":multijump");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Multi jump");
- return false;
-}
-
-#endif
-#endif
+++ /dev/null
-#ifndef MUTATOR_NADES_H
-#define MUTATOR_NADES_H
-
-.entity nade;
-.entity fake_nade;
-.float nade_timer;
-.float nade_refire;
-.float bonus_nades;
-.float nade_special_time;
-.float bonus_nade_score;
-.float nade_type;
-.string pokenade_type;
-.entity nade_damage_target;
-.float cvar_cl_nade_type;
-.string cvar_cl_pokenade_type;
-.float toss_time;
-.float stat_healing_orb;
-.float stat_healing_orb_alpha;
-.float nade_show_particles;
-
-// Remove nades that are being thrown
-void(entity player) nades_Clear;
-
-// Give a bonus grenade to a player
-void(entity player, float score) nades_GiveBonus;
-// Remove all bonus nades from a player
-void(entity player) nades_RemoveBonus;
-
-#endif
-#ifdef IMPLEMENTATION
-
-#include "../../../common/nades/all.qh"
-#include "../../../common/gamemodes/all.qh"
-#include "../../../common/monsters/spawn.qh"
-#include "../../../common/monsters/sv_monsters.qh"
-#include "../../g_subs.qh"
-
-REGISTER_MUTATOR(nades, cvar("g_nades"))
-{
- MUTATOR_ONADD
- {
- addstat(STAT_NADE_TIMER, AS_FLOAT, nade_timer);
- addstat(STAT_NADE_BONUS, AS_FLOAT, bonus_nades);
- addstat(STAT_NADE_BONUS_TYPE, AS_INT, nade_type);
- addstat(STAT_NADE_BONUS_SCORE, AS_FLOAT, bonus_nade_score);
- addstat(STAT_HEALING_ORB, AS_FLOAT, stat_healing_orb);
- addstat(STAT_HEALING_ORB_ALPHA, AS_FLOAT, stat_healing_orb_alpha);
- }
-
- return false;
-}
-
-.float nade_time_primed;
-
-.entity nade_spawnloc;
-
-void nade_timer_think()
-{SELFPARAM();
- self.skin = 8 - (self.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10);
- self.nextthink = time;
- if(!self.owner || wasfreed(self.owner))
- remove(self);
-}
-
-void nade_burn_spawn(entity _nade)
-{
- CSQCProjectile(_nade, true, Nades[_nade.nade_type].m_projectile[true], true);
-}
-
-void nade_spawn(entity _nade)
-{
- entity timer = spawn();
- setmodel(timer, MDL_NADE_TIMER);
- setattachment(timer, _nade, "");
- timer.classname = "nade_timer";
- timer.colormap = _nade.colormap;
- timer.glowmod = _nade.glowmod;
- timer.think = nade_timer_think;
- timer.nextthink = time;
- timer.wait = _nade.wait;
- timer.owner = _nade;
- timer.skin = 10;
-
- _nade.effects |= EF_LOWPRECISION;
-
- CSQCProjectile(_nade, true, Nades[_nade.nade_type].m_projectile[false], true);
-}
-
-void napalm_damage(float dist, float damage, float edgedamage, float burntime)
-{SELFPARAM();
- entity e;
- float d;
- vector p;
-
- if ( damage < 0 )
- return;
-
- RandomSelection_Init();
- for(e = WarpZone_FindRadius(self.origin, dist, true); e; e = e.chain)
- if(e.takedamage == DAMAGE_AIM)
- if(self.realowner != e || autocvar_g_nades_napalm_selfdamage)
- if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self))
- if(!e.frozen)
- {
- p = e.origin;
- p.x += e.mins.x + random() * (e.maxs.x - e.mins.x);
- p.y += e.mins.y + random() * (e.maxs.y - e.mins.y);
- p.z += e.mins.z + random() * (e.maxs.z - e.mins.z);
- d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p);
- if(d < dist)
- {
- e.fireball_impactvec = p;
- RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
- }
- }
- if(RandomSelection_chosen_ent)
- {
- d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
- d = damage + (edgedamage - damage) * (d / dist);
- Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
- //trailparticles(self, particleeffectnum(EFFECT_FIREBALL_LASER), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
- Send_Effect(EFFECT_FIREBALL_LASER, self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
- }
-}
-
-
-void napalm_ball_think()
-{SELFPARAM();
- if(round_handler_IsActive())
- if(!round_handler_IsRoundStarted())
- {
- remove(self);
- return;
- }
-
- if(time > self.pushltime)
- {
- remove(self);
- return;
- }
-
- 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; }
- }
-
- self.angles = vectoangles(self.velocity);
-
- napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage,
- autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime);
-
- self.nextthink = time + 0.1;
-}
-
-
-void nade_napalm_ball()
-{SELFPARAM();
- entity proj;
- vector kick;
-
- spamsound(self, CH_SHOTS, SND(FIREBALL_FIRE), VOL_BASE, ATTEN_NORM);
-
- proj = spawn ();
- proj.owner = self.owner;
- proj.realowner = self.realowner;
- proj.team = self.owner.team;
- proj.classname = "grenade";
- proj.bot_dodge = true;
- proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage;
- proj.movetype = MOVETYPE_BOUNCE;
- proj.projectiledeathtype = DEATH_NADE_NAPALM.m_id;
- PROJECTILE_MAKETRIGGER(proj);
- setmodel(proj, MDL_Null);
- proj.scale = 1;//0.5;
- setsize(proj, '-4 -4 -4', '4 4 4');
- setorigin(proj, self.origin);
- proj.think = napalm_ball_think;
- proj.nextthink = time;
- proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale;
- proj.effects = EF_LOWPRECISION | EF_FLAME;
-
- kick.x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
- kick.y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
- kick.z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread;
- proj.velocity = kick;
-
- proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime;
-
- proj.angles = vectoangles(proj.velocity);
- proj.flags = FL_PROJECTILE;
- proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
-
- //CSQCProjectile(proj, true, PROJECTILE_NAPALM_FIRE, true);
-}
-
-
-void napalm_fountain_think()
-{SELFPARAM();
-
- if(round_handler_IsActive())
- if(!round_handler_IsRoundStarted())
- {
- remove(self);
- return;
- }
-
- if(time >= self.ltime)
- {
- remove(self);
- return;
- }
-
- 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; }
-
- UpdateCSQCProjectile(self);
- }
-
- napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage,
- autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime);
-
- self.nextthink = time + 0.1;
- if(time >= self.nade_special_time)
- {
- self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay;
- nade_napalm_ball();
- }
-}
-
-void nade_napalm_boom()
-{SELFPARAM();
- entity fountain;
- int c;
- for (c = 0; c < autocvar_g_nades_napalm_ball_count; c++)
- nade_napalm_ball();
-
-
- fountain = spawn();
- fountain.owner = self.owner;
- fountain.realowner = self.realowner;
- fountain.origin = self.origin;
- setorigin(fountain, fountain.origin);
- fountain.think = napalm_fountain_think;
- fountain.nextthink = time;
- fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime;
- fountain.pushltime = fountain.ltime;
- fountain.team = self.team;
- fountain.movetype = MOVETYPE_TOSS;
- fountain.projectiledeathtype = DEATH_NADE_NAPALM.m_id;
- fountain.bot_dodge = true;
- fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage;
- fountain.nade_special_time = time;
- setsize(fountain, '-16 -16 -16', '16 16 16');
- CSQCProjectile(fountain, true, PROJECTILE_NAPALM_FOUNTAIN, true);
-}
-
-void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time)
-{
- frost_target.frozen_by = freezefield.realowner;
- Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1);
- Freeze(frost_target, 1/freeze_time, 3, false);
-
- Drop_Special_Items(frost_target);
-}
-
-void nade_ice_think()
-{SELFPARAM();
-
- if(round_handler_IsActive())
- if(!round_handler_IsRoundStarted())
- {
- remove(self);
- return;
- }
-
- if(time >= self.ltime)
- {
- if ( autocvar_g_nades_ice_explode )
- {
- entity expef = EFFECT_NADE_EXPLODE(self.realowner.team);
- Send_Effect(expef, self.origin + '0 0 1', '0 0 0', 1);
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
-
- RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
- autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
- Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
- autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
- }
- remove(self);
- return;
- }
-
-
- self.nextthink = time+0.1;
-
- // gaussian
- float randomr;
- randomr = random();
- randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius;
- float randomw;
- randomw = random()*M_PI*2;
- vector randomp;
- randomp.x = randomr*cos(randomw);
- randomp.y = randomr*sin(randomw);
- randomp.z = 1;
- Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, self.origin + randomp, '0 0 0', 1);
-
- if(time >= self.nade_special_time)
- {
- self.nade_special_time = time+0.7;
-
- Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1);
- Send_Effect(EFFECT_ICEFIELD, self.origin, '0 0 0', 1);
- }
-
-
- float current_freeze_time = self.ltime - time - 0.1;
-
- entity e;
- for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain)
- if(e != self)
- if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner))
- if(e.takedamage && e.deadflag == DEAD_NO)
- if(e.health > 0)
- if(!e.revival_time || ((time - e.revival_time) >= 1.5))
- if(!e.frozen)
- if(current_freeze_time > 0)
- nade_ice_freeze(self, e, current_freeze_time);
-}
-
-void nade_ice_boom()
-{SELFPARAM();
- entity fountain;
- fountain = spawn();
- fountain.owner = self.owner;
- fountain.realowner = self.realowner;
- fountain.origin = self.origin;
- setorigin(fountain, fountain.origin);
- fountain.think = nade_ice_think;
- fountain.nextthink = time;
- fountain.ltime = time + autocvar_g_nades_ice_freeze_time;
- fountain.pushltime = fountain.wait = fountain.ltime;
- fountain.team = self.team;
- fountain.movetype = MOVETYPE_TOSS;
- fountain.projectiledeathtype = DEATH_NADE_ICE.m_id;
- fountain.bot_dodge = false;
- setsize(fountain, '-16 -16 -16', '16 16 16');
- fountain.nade_special_time = time+0.3;
- fountain.angles = self.angles;
-
- if ( autocvar_g_nades_ice_explode )
- {
- setmodel(fountain, MDL_PROJECTILE_GRENADE);
- entity timer = spawn();
- setmodel(timer, MDL_NADE_TIMER);
- setattachment(timer, fountain, "");
- timer.classname = "nade_timer";
- timer.colormap = self.colormap;
- timer.glowmod = self.glowmod;
- timer.think = nade_timer_think;
- timer.nextthink = time;
- timer.wait = fountain.ltime;
- timer.owner = fountain;
- timer.skin = 10;
- }
- else
- setmodel(fountain, MDL_Null);
-}
-
-void nade_translocate_boom()
-{SELFPARAM();
- if(self.realowner.vehicle)
- return;
-
- vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins.z - 24);
- tracebox(locout, self.realowner.mins, self.realowner.maxs, locout, MOVE_NOMONSTERS, self.realowner);
- locout = trace_endpos;
-
- makevectors(self.realowner.angles);
-
- MUTATOR_CALLHOOK(PortalTeleport, self.realowner);
-
- TeleportPlayer(self, self.realowner, locout, self.realowner.angles, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
-}
-
-void nade_spawn_boom()
-{SELFPARAM();
- entity spawnloc = spawn();
- setorigin(spawnloc, self.origin);
- setsize(spawnloc, self.realowner.mins, self.realowner.maxs);
- spawnloc.movetype = MOVETYPE_NONE;
- spawnloc.solid = SOLID_NOT;
- spawnloc.drawonlytoclient = self.realowner;
- spawnloc.effects = EF_STARDUST;
- spawnloc.cnt = autocvar_g_nades_spawn_count;
-
- if(self.realowner.nade_spawnloc)
- {
- remove(self.realowner.nade_spawnloc);
- self.realowner.nade_spawnloc = world;
- }
-
- self.realowner.nade_spawnloc = spawnloc;
-}
-
-void nade_heal_think()
-{SELFPARAM();
- if(time >= self.ltime)
- {
- remove(self);
- return;
- }
-
- self.nextthink = time;
-
- if(time >= self.nade_special_time)
- {
- self.nade_special_time = time+0.25;
- self.nade_show_particles = 1;
- }
- else
- self.nade_show_particles = 0;
-}
-
-void nade_heal_touch()
-{SELFPARAM();
- float maxhealth;
- float health_factor;
- if(IS_PLAYER(other) || IS_MONSTER(other))
- if(other.deadflag == DEAD_NO)
- if(!other.frozen)
- {
- health_factor = autocvar_g_nades_heal_rate*frametime/2;
- if ( other != self.realowner )
- {
- if ( SAME_TEAM(other,self) )
- health_factor *= autocvar_g_nades_heal_friend;
- else
- health_factor *= autocvar_g_nades_heal_foe;
- }
- if ( health_factor > 0 )
- {
- maxhealth = (IS_MONSTER(other)) ? other.max_health : g_pickup_healthmega_max;
- if ( other.health < maxhealth )
- {
- if ( self.nade_show_particles )
- Send_Effect(EFFECT_HEALING, other.origin, '0 0 0', 1);
- other.health = min(other.health+health_factor, maxhealth);
- }
- other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
- }
- else if ( health_factor < 0 )
- {
- Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL.m_id,other.origin,'0 0 0');
- }
-
- }
-
- if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) )
- {
- entity show_red = (IS_VEHICLE(other)) ? other.owner : other;
- show_red.stat_healing_orb = time+0.1;
- show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime;
- }
-}
-
-void nade_heal_boom()
-{SELFPARAM();
- entity healer;
- healer = spawn();
- healer.owner = self.owner;
- healer.realowner = self.realowner;
- setorigin(healer, self.origin);
- healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar
- healer.ltime = time + healer.healer_lifetime;
- healer.team = self.realowner.team;
- healer.bot_dodge = false;
- healer.solid = SOLID_TRIGGER;
- healer.touch = nade_heal_touch;
-
- setmodel(healer, MDL_NADE_HEAL);
- healer.healer_radius = autocvar_g_nades_nade_radius;
- vector size = '1 1 1' * healer.healer_radius / 2;
- setsize(healer,-size,size);
-
- Net_LinkEntity(healer, true, 0, healer_send);
-
- healer.think = nade_heal_think;
- healer.nextthink = time;
- healer.SendFlags |= 1;
-}
-
-void nade_monster_boom()
-{SELFPARAM();
- entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, false, false, 1);
-
- if(autocvar_g_nades_pokenade_monster_lifetime > 0)
- e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime;
- e.monster_skill = MONSTER_SKILL_INSANE;
-}
-
-void nade_boom()
-{SELFPARAM();
- entity expef = NULL;
- bool nade_blast = true;
-
- switch ( Nades[self.nade_type] )
- {
- case NADE_TYPE_NAPALM:
- nade_blast = autocvar_g_nades_napalm_blast;
- expef = EFFECT_EXPLOSION_MEDIUM;
- break;
- case NADE_TYPE_ICE:
- nade_blast = false;
- expef = EFFECT_ELECTRO_COMBO; // hookbomb_explode electro_combo bigplasma_impact
- break;
- case NADE_TYPE_TRANSLOCATE:
- nade_blast = false;
- break;
- case NADE_TYPE_MONSTER:
- case NADE_TYPE_SPAWN:
- nade_blast = false;
- switch(self.realowner.team)
- {
- case NUM_TEAM_1: expef = EFFECT_SPAWN_RED; break;
- case NUM_TEAM_2: expef = EFFECT_SPAWN_BLUE; break;
- case NUM_TEAM_3: expef = EFFECT_SPAWN_YELLOW; break;
- case NUM_TEAM_4: expef = EFFECT_SPAWN_PINK; break;
- default: expef = EFFECT_SPAWN_NEUTRAL; break;
- }
- break;
- case NADE_TYPE_HEAL:
- nade_blast = false;
- expef = EFFECT_SPAWN_RED;
- break;
-
- default:
- case NADE_TYPE_NORMAL:
- expef = EFFECT_NADE_EXPLODE(self.realowner.team);
- break;
- }
-
- if(expef)
- Send_Effect(expef, findbetterlocation(self.origin, 8), '0 0 0', 1);
-
- sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
- sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
-
- self.event_damage = func_null; // prevent somehow calling damage in the next call
-
- if(nade_blast)
- {
- RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
- autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
- Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
- }
-
- if(self.takedamage)
- switch ( Nades[self.nade_type] )
- {
- case NADE_TYPE_NAPALM: nade_napalm_boom(); break;
- case NADE_TYPE_ICE: nade_ice_boom(); break;
- case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break;
- case NADE_TYPE_SPAWN: nade_spawn_boom(); break;
- case NADE_TYPE_HEAL: nade_heal_boom(); break;
- case NADE_TYPE_MONSTER: nade_monster_boom(); break;
- }
-
- entity head;
- for(head = world; (head = find(head, classname, "grapplinghook")); )
- if(head.aiment == self)
- RemoveGrapplingHook(head.realowner);
-
- remove(self);
-}
-
-void nade_touch()
-{SELFPARAM();
- /*float is_weapclip = 0;
- if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
- if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID))
- if (!(trace_dphitcontents & DPCONTENTS_OPAQUE))
- is_weapclip = 1;*/
- if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip)
- {
- entity head;
- for(head = world; (head = find(head, classname, "grapplinghook")); )
- if(head.aiment == self)
- RemoveGrapplingHook(head.realowner);
- remove(self);
- return;
- }
-
- PROJECTILE_TOUCH;
-
- //setsize(self, '-2 -2 -2', '2 2 2');
- //UpdateCSQCProjectile(self);
- if(self.health == self.max_health)
- {
- spamsound(self, CH_SHOTS, SND(GRENADE_BOUNCE_RANDOM()), VOL_BASE, ATTEN_NORM);
- return;
- }
-
- self.enemy = other;
- nade_boom();
-}
-
-void nade_beep()
-{SELFPARAM();
- sound(self, CH_SHOTS_SINGLE, SND_NADE_BEEP, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX));
- self.think = nade_boom;
- self.nextthink = max(self.wait, time);
-}
-
-void nade_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{SELFPARAM();
- if(ITEM_DAMAGE_NEEDKILL(deathtype))
- {
- self.takedamage = DAMAGE_NO;
- nade_boom();
- return;
- }
-
- if(self.nade_type == NADE_TYPE_TRANSLOCATE.m_id || self.nade_type == NADE_TYPE_SPAWN.m_id)
- return;
-
- if(DEATH_ISWEAPON(deathtype, WEP_BLASTER))
- {
- force *= 1.5;
- damage = 0;
- }
-
- if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER) && (deathtype & HITTYPE_SECONDARY))
- {
- force *= 0.5; // too much
- frag_damage = 0;
- }
-
- if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER))
- {
- force *= 6;
- damage = self.max_health * 0.55;
- }
-
- if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN) || DEATH_ISWEAPON(deathtype, WEP_HMG))
- damage = self.max_health * 0.1;
-
- if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) // WEAPONTODO
- if(deathtype & HITTYPE_SECONDARY)
- {
- damage = self.max_health * 0.1;
- force *= 10;
- }
- else
- damage = self.max_health * 1.15;
-
- self.velocity += force;
- UpdateCSQCProjectile(self);
-
- if(damage <= 0 || ((self.flags & FL_ONGROUND) && IS_PLAYER(attacker)))
- return;
-
- if(self.health == self.max_health)
- {
- sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX));
- self.nextthink = max(time + autocvar_g_nades_nade_lifetime, time);
- self.think = nade_beep;
- }
-
- self.health -= damage;
-
- if ( self.nade_type != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker) )
- self.realowner = attacker;
-
- if(self.health <= 0)
- W_PrepareExplosionByDamage(attacker, nade_boom);
- else
- nade_burn_spawn(self);
-}
-
-void toss_nade(entity e, vector _velocity, float _time)
-{SELFPARAM();
- if(e.nade == world)
- return;
-
- entity _nade = e.nade;
- e.nade = world;
-
- remove(e.fake_nade);
- e.fake_nade = world;
-
- makevectors(e.v_angle);
-
- W_SetupShot(e, false, false, "", CH_WEAPON_A, 0);
-
- Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES);
-
- vector offset = (v_forward * autocvar_g_nades_throw_offset.x)
- + (v_right * autocvar_g_nades_throw_offset.y)
- + (v_up * autocvar_g_nades_throw_offset.z);
- if(autocvar_g_nades_throw_offset == '0 0 0')
- offset = '0 0 0';
-
- setorigin(_nade, w_shotorg + offset + (v_right * 25) * -1);
- //setmodel(_nade, MDL_PROJECTILE_NADE);
- //setattachment(_nade, world, "");
- PROJECTILE_MAKETRIGGER(_nade);
- setsize(_nade, '-16 -16 -16', '16 16 16');
- _nade.movetype = MOVETYPE_BOUNCE;
-
- tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, false, _nade);
- if (trace_startsolid)
- setorigin(_nade, e.origin);
-
- if(self.v_angle.x >= 70 && self.v_angle.x <= 110 && self.BUTTON_CROUCH)
- _nade.velocity = '0 0 100';
- else if(autocvar_g_nades_nade_newton_style == 1)
- _nade.velocity = e.velocity + _velocity;
- else if(autocvar_g_nades_nade_newton_style == 2)
- _nade.velocity = _velocity;
- else
- _nade.velocity = W_CalculateProjectileVelocity(e.velocity, _velocity, true);
-
- _nade.touch = nade_touch;
- _nade.health = autocvar_g_nades_nade_health;
- _nade.max_health = _nade.health;
- _nade.takedamage = DAMAGE_AIM;
- _nade.event_damage = nade_damage;
- _nade.customizeentityforclient = func_null;
- _nade.exteriormodeltoclient = world;
- _nade.traileffectnum = 0;
- _nade.teleportable = true;
- _nade.pushable = true;
- _nade.gravity = 1;
- _nade.missile_flags = MIF_SPLASH | MIF_ARC;
- _nade.damagedbycontents = true;
- _nade.angles = vectoangles(_nade.velocity);
- _nade.flags = FL_PROJECTILE;
- _nade.projectiledeathtype = DEATH_NADE.m_id;
- _nade.toss_time = time;
- _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX);
-
- if(_nade.nade_type == NADE_TYPE_TRANSLOCATE.m_id || _nade.nade_type == NADE_TYPE_SPAWN.m_id)
- _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
- else
- _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
-
- nade_spawn(_nade);
-
- if(_time)
- {
- _nade.think = nade_boom;
- _nade.nextthink = _time;
- }
-
- e.nade_refire = time + autocvar_g_nades_nade_refire;
- e.nade_timer = 0;
-}
-
-void nades_GiveBonus(entity player, float score)
-{
- if (autocvar_g_nades)
- if (autocvar_g_nades_bonus)
- if (IS_REAL_CLIENT(player))
- if (IS_PLAYER(player) && player.bonus_nades < autocvar_g_nades_bonus_max)
- if (player.frozen == 0)
- if (player.deadflag == DEAD_NO)
- {
- if ( player.bonus_nade_score < 1 )
- player.bonus_nade_score += score/autocvar_g_nades_bonus_score_max;
-
- if ( player.bonus_nade_score >= 1 )
- {
- Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS);
- play2(player, SND(KH_ALARM));
- player.bonus_nades++;
- player.bonus_nade_score -= 1;
- }
- }
-}
-
-void nades_RemoveBonus(entity player)
-{
- player.bonus_nades = player.bonus_nade_score = 0;
-}
-
-float nade_customize()
-{SELFPARAM();
- //if(IS_SPEC(other)) { return false; }
- if(other == self.realowner || (IS_SPEC(other) && other.enemy == self.realowner))
- {
- // somewhat hide the model, but keep the glow
- //self.effects = 0;
- if(self.traileffectnum)
- self.traileffectnum = 0;
- self.alpha = -1;
- }
- else
- {
- //self.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
- if(!self.traileffectnum)
- self.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades[self.nade_type].m_projectile[false], self.team).eent_eff_name);
- self.alpha = 1;
- }
-
- return true;
-}
-
-void nade_prime()
-{SELFPARAM();
- if(autocvar_g_nades_bonus_only)
- if(!self.bonus_nades)
- return; // only allow bonus nades
-
- if(self.nade)
- remove(self.nade);
-
- if(self.fake_nade)
- remove(self.fake_nade);
-
- entity n = spawn(), fn = spawn();
-
- n.classname = "nade";
- fn.classname = "fake_nade";
-
- if(self.items & ITEM_Strength.m_itemid && autocvar_g_nades_bonus_onstrength)
- n.nade_type = self.nade_type;
- else if (self.bonus_nades >= 1)
- {
- n.nade_type = self.nade_type;
- n.pokenade_type = self.pokenade_type;
- self.bonus_nades -= 1;
- }
- else
- {
- n.nade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_nade_type : autocvar_g_nades_nade_type);
- n.pokenade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type);
- }
-
- n.nade_type = bound(1, n.nade_type, Nades_COUNT);
-
- setmodel(n, MDL_PROJECTILE_NADE);
- //setattachment(n, self, "bip01 l hand");
- n.exteriormodeltoclient = self;
- n.customizeentityforclient = nade_customize;
- n.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades[n.nade_type].m_projectile[false], self.team).eent_eff_name);
- n.colormod = Nades[n.nade_type].m_color;
- n.realowner = self;
- n.colormap = self.colormap;
- n.glowmod = self.glowmod;
- n.wait = time + autocvar_g_nades_nade_lifetime;
- n.nade_time_primed = time;
- n.think = nade_beep;
- n.nextthink = max(n.wait - 3, time);
- n.projectiledeathtype = DEATH_NADE.m_id;
-
- setmodel(fn, MDL_NADE_VIEW);
- setattachment(fn, self.weaponentity, "");
- fn.realowner = fn.owner = self;
- fn.colormod = Nades[n.nade_type].m_color;
- fn.colormap = self.colormap;
- fn.glowmod = self.glowmod;
- fn.think = SUB_Remove;
- fn.nextthink = n.wait;
-
- self.nade = n;
- self.fake_nade = fn;
-}
-
-float CanThrowNade()
-{SELFPARAM();
- if(self.vehicle)
- return false;
-
- if(gameover)
- return false;
-
- if(self.deadflag != DEAD_NO)
- return false;
-
- if (!autocvar_g_nades)
- return false; // allow turning them off mid match
-
- if(forbidWeaponUse(self))
- return false;
-
- if (!IS_PLAYER(self))
- return false;
-
- return true;
-}
-
-.bool nade_altbutton;
-
-void nades_CheckThrow()
-{SELFPARAM();
- if(!CanThrowNade())
- return;
-
- entity held_nade = self.nade;
- if (!held_nade)
- {
- self.nade_altbutton = true;
- if(time > self.nade_refire)
- {
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_NADE_THROW);
- nade_prime();
- self.nade_refire = time + autocvar_g_nades_nade_refire;
- }
- }
- else
- {
- self.nade_altbutton = false;
- if (time >= held_nade.nade_time_primed + 1) {
- makevectors(self.v_angle);
- float _force = time - held_nade.nade_time_primed;
- _force /= autocvar_g_nades_nade_lifetime;
- _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce));
- toss_nade(self, (v_forward * 0.75 + v_up * 0.2 + v_right * 0.05) * _force, 0);
- }
- }
-}
-
-void nades_Clear(entity player)
-{
- if(player.nade)
- remove(player.nade);
- if(player.fake_nade)
- remove(player.fake_nade);
-
- player.nade = player.fake_nade = world;
- player.nade_timer = 0;
-}
-
-MUTATOR_HOOKFUNCTION(nades, VehicleEnter)
-{
- if(vh_player.nade)
- toss_nade(vh_player, '0 0 100', max(vh_player.nade.wait, time + 0.05));
-
- return false;
-}
-
-CLASS(NadeOffhand, OffhandWeapon)
- METHOD(NadeOffhand, offhand_think, void(NadeOffhand this, entity player, bool key_pressed))
- {
- entity held_nade = player.nade;
- if (held_nade)
- {
- player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / autocvar_g_nades_nade_lifetime, 1);
- // LOG_TRACEF("%d %d\n", player.nade_timer, time - held_nade.nade_time_primed);
- makevectors(player.angles);
- held_nade.velocity = player.velocity;
- setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0);
- held_nade.angles_y = player.angles.y;
-
- if (time + 0.1 >= held_nade.wait)
- toss_nade(player, '0 0 0', time + 0.05);
- }
-
- if (!CanThrowNade()) return;
- if (!(time > player.nade_refire)) return;
- if (key_pressed) {
- if (!held_nade) {
- nade_prime();
- held_nade = player.nade;
- }
- } else if (time >= held_nade.nade_time_primed + 1) {
- if (held_nade) {
- makevectors(player.v_angle);
- float _force = time - held_nade.nade_time_primed;
- _force /= autocvar_g_nades_nade_lifetime;
- _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce));
- toss_nade(player, (v_forward * 0.7 + v_up * 0.2 + v_right * 0.1) * _force, 0);
- }
- }
- }
-ENDCLASS(NadeOffhand)
-NadeOffhand OFFHAND_NADE; STATIC_INIT(OFFHAND_NADE) { OFFHAND_NADE = NEW(NadeOffhand); }
-
-MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST)
-{
- if (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)) || autocvar_g_nades_override_dropweapon) {
- nades_CheckThrow();
- return true;
- }
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, PlayerPreThink)
-{SELFPARAM();
- if (!IS_PLAYER(self)) { return false; }
-
- if (self.nade && (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, self, self.nade_altbutton);
-
- if(IS_PLAYER(self))
- {
- if ( autocvar_g_nades_bonus && autocvar_g_nades )
- {
- entity key;
- float key_count = 0;
- FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; }
-
- float time_score;
- if(self.flagcarried || self.ballcarried) // this player is important
- time_score = autocvar_g_nades_bonus_score_time_flagcarrier;
- else
- time_score = autocvar_g_nades_bonus_score_time;
-
- if(key_count)
- time_score = autocvar_g_nades_bonus_score_time_flagcarrier * key_count; // multiply by the number of keys the player is holding
-
- if(autocvar_g_nades_bonus_client_select)
- {
- self.nade_type = self.cvar_cl_nade_type;
- self.pokenade_type = self.cvar_cl_pokenade_type;
- }
- else
- {
- self.nade_type = autocvar_g_nades_bonus_type;
- self.pokenade_type = autocvar_g_nades_pokenade_monster_type;
- }
-
- self.nade_type = bound(1, self.nade_type, Nades_COUNT);
-
- if(self.bonus_nade_score >= 0 && autocvar_g_nades_bonus_score_max)
- nades_GiveBonus(self, time_score / autocvar_g_nades_bonus_score_max);
- }
- else
- {
- self.bonus_nades = self.bonus_nade_score = 0;
- }
- }
-
- float n = 0;
- entity o = world;
- if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
- n = -1;
- else
- {
- vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
- n = 0;
- FOR_EACH_PLAYER(other) if(self != other)
- {
- if(other.deadflag == DEAD_NO)
- if(other.frozen == 0)
- if(SAME_TEAM(other, self))
- if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
- {
- if(!o)
- o = other;
- if(self.frozen == 1)
- other.reviving = true;
- ++n;
- }
- }
- }
-
- if(n && self.frozen == 3) // OK, there is at least one teammate reviving us
- {
- self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
- self.health = max(1, self.revive_progress * start_health);
-
- if(self.revive_progress >= 1)
- {
- Unfreeze(self);
-
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname);
- Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname);
- }
-
- FOR_EACH_PLAYER(other) if(other.reviving)
- {
- other.revive_progress = self.revive_progress;
- other.reviving = false;
- }
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, PlayerSpawn)
-{SELFPARAM();
- if(autocvar_g_nades_spawn)
- self.nade_refire = time + autocvar_g_spawnshieldtime;
- else
- self.nade_refire = time + autocvar_g_nades_nade_refire;
-
- if(autocvar_g_nades_bonus_client_select)
- self.nade_type = self.cvar_cl_nade_type;
-
- self.nade_timer = 0;
-
- if (!self.offhand) self.offhand = OFFHAND_NADE;
-
- if(self.nade_spawnloc)
- {
- setorigin(self, self.nade_spawnloc.origin);
- self.nade_spawnloc.cnt -= 1;
-
- if(self.nade_spawnloc.cnt <= 0)
- {
- remove(self.nade_spawnloc);
- self.nade_spawnloc = world;
- }
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, PlayerDies, CBC_ORDER_LAST)
-{
- if(frag_target.nade)
- if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade)
- toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05));
-
- float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor);
-
- if(IS_PLAYER(frag_attacker))
- {
- if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target)
- nades_RemoveBonus(frag_attacker);
- else if(frag_target.flagcarried)
- nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium);
- else if(autocvar_g_nades_bonus_score_spree && frag_attacker.killcount > 1)
- {
- #define SPREE_ITEM(counta,countb,center,normal,gentle) \
- case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; }
- switch(frag_attacker.killcount)
- {
- KILL_SPREE_LIST
- default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break;
- }
- #undef SPREE_ITEM
- }
- else
- nades_GiveBonus(frag_attacker, killcount_bonus);
- }
-
- nades_RemoveBonus(frag_target);
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, PlayerDamage_Calculate)
-{
- if(frag_target.frozen)
- if(autocvar_g_freezetag_revive_nade)
- if(frag_attacker == frag_target)
- if(frag_deathtype == DEATH_NADE.m_id)
- if(time - frag_inflictor.toss_time <= 0.1)
- {
- Unfreeze(frag_target);
- frag_target.health = autocvar_g_freezetag_revive_nade_health;
- Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3);
- frag_damage = 0;
- frag_force = '0 0 0';
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname);
- Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, MonsterDies)
-{SELFPARAM();
- if(IS_PLAYER(frag_attacker))
- if(DIFF_TEAM(frag_attacker, self))
- if(!(self.spawnflags & MONSTERFLAG_SPAWNED))
- nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor);
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, DropSpecialItems)
-{
- if(frag_target.nade)
- toss_nade(frag_target, '0 0 0', time + 0.05);
-
- return false;
-}
-
-bool nades_RemovePlayer()
-{SELFPARAM();
- nades_Clear(self);
- nades_RemoveBonus(self);
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, MakePlayerObserver) { nades_RemovePlayer(); }
-MUTATOR_HOOKFUNCTION(nades, ClientDisconnect) { nades_RemovePlayer(); }
-MUTATOR_HOOKFUNCTION(nades, reset_map_global) { nades_RemovePlayer(); }
-
-MUTATOR_HOOKFUNCTION(nades, SpectateCopy)
-{SELFPARAM();
- self.nade_timer = other.nade_timer;
- self.nade_type = other.nade_type;
- self.pokenade_type = other.pokenade_type;
- self.bonus_nades = other.bonus_nades;
- self.bonus_nade_score = other.bonus_nade_score;
- self.stat_healing_orb = other.stat_healing_orb;
- self.stat_healing_orb_alpha = other.stat_healing_orb_alpha;
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, GetCvars)
-{
- GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nade_type, "cl_nade_type");
- GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_pokenade_type, "cl_pokenade_type");
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":Nades");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Nades");
- return false;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-/*
-
-CORE laser vortex lg rl cry gl elec hagar fireb hook
- vaporizer porto
- tuba
-
-NEW rifle hlac minel seeker
-IDEAS OPEN flak OPEN FUN FUN FUN FUN
-
-
-
-How this mutator works:
- =======================
-
-When a gun tries to spawn, this mutator is called. It will provide alternate
-weaponreplace lists.
-
-Entity:
-
-{
-"classname" "weapon_vortex"
-"new_toys" "rifle"
-}
--> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise.
-
-{
-"classname" "weapon_vortext"
-"new_toys" "vortex rifle"
-}
--> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise.
-
-{
-"classname" "weapon_vortex"
-"new_toys" "vortex"
-}
--> This is always a Vortex.
-
-If the map specifies no "new_toys" argument
-
-There will be two default replacements selectable: "replace all" and "replace random".
-In "replace all" mode, e.g. Vortex will have the default replacement "rifle".
-In "replace random" mode, Vortex will have the default replacement "vortex rifle".
-
-This mutator's replacements run BEFORE regular weaponreplace!
-
-The New Toys guns do NOT get a spawn function, so they can only ever be spawned
-when this mutator is active.
-
-Likewise, warmup, give all, give ALL and impulse 99 will not give them unless
-this mutator is active.
-
-Outside this mutator, they still can be spawned by:
-- setting their start weapon cvar to 1
-- give weaponname
-- weaponreplace
-- weaponarena (but all and most weapons arena again won't include them)
-
-This mutator performs the default replacements on the DEFAULTS of the
-start weapon selection.
-
-These weapons appear in the menu's priority list, BUT get a suffix
-"(Mutator weapon)".
-
-Picking up a "new toys" weapon will not play standard weapon pickup sound, but
-roflsound "New toys, new toys!" sound.
-
-*/
-
-bool nt_IsNewToy(int w);
-
-REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill"))
-{
- MUTATOR_ONADD
- {
- if(time > 1) // game loads at time 1
- error("This cannot be added at runtime\n");
-
- // mark the guns as ok to use by e.g. impulse 99
- for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
- if(nt_IsNewToy(i))
- get_weaponinfo(i).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
- }
-
- MUTATOR_ONROLLBACK_OR_REMOVE
- {
- for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
- if(nt_IsNewToy(i))
- get_weaponinfo(i).spawnflags |= WEP_FLAG_MUTATORBLOCKED;
- }
-
- MUTATOR_ONREMOVE
- {
- LOG_INFO("This cannot be removed at runtime\n");
- return -1;
- }
-
- return 0;
-}
-
-.string new_toys;
-
-float autocvar_g_new_toys_autoreplace;
-bool autocvar_g_new_toys_use_pickupsound = true;
-const float NT_AUTOREPLACE_NEVER = 0;
-const float NT_AUTOREPLACE_ALWAYS = 1;
-const float NT_AUTOREPLACE_RANDOM = 2;
-
-MUTATOR_HOOKFUNCTION(nt, SetModname)
-{
- modname = "NewToys";
- return 0;
-}
-
-bool nt_IsNewToy(int w)
-{
- switch(w)
- {
- case WEP_SEEKER.m_id:
- case WEP_MINE_LAYER.m_id:
- case WEP_HLAC.m_id:
- case WEP_RIFLE.m_id:
- case WEP_SHOCKWAVE.m_id:
- return true;
- default:
- return false;
- }
-}
-
-string nt_GetFullReplacement(string w)
-{
- switch(w)
- {
- case "hagar": return "seeker";
- case "devastator": return "minelayer";
- case "machinegun": return "hlac";
- case "vortex": return "rifle";
- //case "shotgun": return "shockwave";
- default: return string_null;
- }
-}
-
-string nt_GetReplacement(string w, float m)
-{
- if(m == NT_AUTOREPLACE_NEVER)
- return w;
- string s = nt_GetFullReplacement(w);
- if (!s)
- return w;
- if(m == NT_AUTOREPLACE_RANDOM)
- s = strcat(w, " ", s);
- return s;
-}
-
-MUTATOR_HOOKFUNCTION(nt, SetStartItems)
-{
- // rearrange start_weapon_default
- // apply those bits that are set by start_weapon_defaultmask
- // same for warmup
-
- float i, j, k, n;
-
- WepSet newdefault;
- WepSet warmup_newdefault;
-
- newdefault = '0 0 0';
- warmup_newdefault = '0 0 0';
-
- for(i = WEP_FIRST; i <= WEP_LAST; ++i)
- {
- entity e = get_weaponinfo(i);
- if(!e.weapon)
- continue;
-
- n = tokenize_console(nt_GetReplacement(e.netname, autocvar_g_new_toys_autoreplace));
-
- for(j = 0; j < n; ++j)
- for(k = WEP_FIRST; k <= WEP_LAST; ++k)
- if(get_weaponinfo(k).netname == argv(j))
- {
- if(start_weapons & WepSet_FromWeapon(i))
- newdefault |= WepSet_FromWeapon(k);
- if(warmup_start_weapons & WepSet_FromWeapon(i))
- warmup_newdefault |= WepSet_FromWeapon(k);
- }
- }
-
- newdefault &= start_weapons_defaultmask;
- start_weapons &= ~start_weapons_defaultmask;
- start_weapons |= newdefault;
-
- warmup_newdefault &= warmup_start_weapons_defaultmask;
- warmup_start_weapons &= ~warmup_start_weapons_defaultmask;
- warmup_start_weapons |= warmup_newdefault;
-
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace)
-{SELFPARAM();
- // otherwise, we do replace
- if(self.new_toys)
- {
- // map defined replacement:
- ret_string = self.new_toys;
- }
- else
- {
- // auto replacement:
- ret_string = nt_GetReplacement(other.netname, autocvar_g_new_toys_autoreplace);
- }
-
- // apply regular weaponreplace
- ret_string = W_Apply_Weaponreplace(ret_string);
-
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(nt, FilterItem)
-{SELFPARAM();
- if(nt_IsNewToy(self.weapon) && autocvar_g_new_toys_use_pickupsound) {
- self.item_pickupsound = string_null;
- self.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS;
- }
- return 0;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-float g_nix_with_blaster;
-// WEAPONTODO
-int nix_weapon;
-float nix_nextchange;
-float nix_nextweapon;
-.float nix_lastchange_id;
-.float nix_lastinfotime;
-.float nix_nextincr;
-
-bool NIX_CanChooseWeapon(int wpn);
-
-REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill"))
-{
- MUTATOR_ONADD
- {
- g_nix_with_blaster = autocvar_g_nix_with_blaster;
-
- nix_nextchange = 0;
- nix_nextweapon = 0;
-
- for (int i = WEP_FIRST; i <= WEP_LAST; ++i)
- if (NIX_CanChooseWeapon(i)) {
- Weapon w = get_weaponinfo(i);
- w.wr_init(w);
- }
- }
-
- MUTATOR_ONROLLBACK_OR_REMOVE
- {
- // nothing to roll back
- }
-
- MUTATOR_ONREMOVE
- {
- // as the PlayerSpawn hook will no longer run, NIX is turned off by this!
- entity e;
- FOR_EACH_PLAYER(e) if(e.deadflag == DEAD_NO)
- {
- e.ammo_cells = start_ammo_cells;
- e.ammo_plasma = start_ammo_plasma;
- e.ammo_shells = start_ammo_shells;
- e.ammo_nails = start_ammo_nails;
- e.ammo_rockets = start_ammo_rockets;
- e.ammo_fuel = start_ammo_fuel;
- e.weapons = start_weapons;
- if(!client_hasweapon(e, e.weapon, true, false))
- e.switchweapon = w_getbestweapon(self);
- }
- }
-
- return 0;
-}
-
-bool NIX_CanChooseWeapon(int wpn)
-{
- entity e = get_weaponinfo(wpn);
- if(!e.weapon) // skip dummies
- return false;
- if(g_weaponarena)
- {
- if(!(g_weaponarena_weapons & WepSet_FromWeapon(wpn)))
- return false;
- }
- else
- {
- if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster)
- return false;
- if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
- return false;
- if (!(e.spawnflags & WEP_FLAG_NORMAL))
- return false;
- }
- return true;
-}
-void NIX_ChooseNextWeapon()
-{
- float j;
- RandomSelection_Init();
- for(j = WEP_FIRST; j <= WEP_LAST; ++j)
- if(NIX_CanChooseWeapon(j))
- RandomSelection_Add(world, j, string_null, 1, (j != nix_weapon));
- nix_nextweapon = RandomSelection_chosen_float;
-}
-
-void NIX_GiveCurrentWeapon()
-{SELFPARAM();
- float dt;
-
- if(!nix_nextweapon)
- NIX_ChooseNextWeapon();
-
- dt = ceil(nix_nextchange - time);
-
- if(dt <= 0)
- {
- nix_weapon = nix_nextweapon;
- nix_nextweapon = 0;
- if (!nix_nextchange) // no round played yet?
- nix_nextchange = time; // start the first round now!
- else
- nix_nextchange = time + autocvar_g_balance_nix_roundtime;
- // Weapon w = get_weaponinfo(nix_weapon);
- // w.wr_init(w); // forget it, too slow
- }
-
- // get weapon info
- entity e = get_weaponinfo(nix_weapon);
-
- if(nix_nextchange != self.nix_lastchange_id) // this shall only be called once per round!
- {
- self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = self.ammo_plasma = self.ammo_fuel = 0;
-
- if(self.items & IT_UNLIMITED_WEAPON_AMMO)
- {
- switch(e.ammo_field)
- {
- case ammo_shells: self.ammo_shells = autocvar_g_pickup_shells_max; break;
- case ammo_nails: self.ammo_nails = autocvar_g_pickup_nails_max; break;
- case ammo_rockets: self.ammo_rockets = autocvar_g_pickup_rockets_max; break;
- case ammo_cells: self.ammo_cells = autocvar_g_pickup_cells_max; break;
- case ammo_plasma: self.ammo_plasma = autocvar_g_pickup_plasma_max; break;
- case ammo_fuel: self.ammo_fuel = autocvar_g_pickup_fuel_max; break;
- }
- }
- else
- {
- switch(e.ammo_field)
- {
- case ammo_shells: self.ammo_shells = autocvar_g_balance_nix_ammo_shells; break;
- case ammo_nails: self.ammo_nails = autocvar_g_balance_nix_ammo_nails; break;
- case ammo_rockets: self.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break;
- case ammo_cells: self.ammo_cells = autocvar_g_balance_nix_ammo_cells; break;
- case ammo_plasma: self.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break;
- case ammo_fuel: self.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break;
- }
- }
-
- self.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
- if(dt >= 1 && dt <= 5)
- self.nix_lastinfotime = -42;
- else
- Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon);
-
- Weapon w = get_weaponinfo(nix_weapon);
- w.wr_resetplayer(w);
-
- // all weapons must be fully loaded when we spawn
- if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
- self.(weapon_load[nix_weapon]) = e.reloading_ammo;
-
- // vortex too
- if(WEP_CVAR(vortex, charge))
- {
- if(WEP_CVAR_SEC(vortex, chargepool))
- self.vortex_chargepool_ammo = 1;
- self.vortex_charge = WEP_CVAR(vortex, charge_start);
- }
-
- // set last change info
- self.nix_lastchange_id = nix_nextchange;
- }
- if(self.nix_lastinfotime != dt)
- {
- self.nix_lastinfotime = dt; // initial value 0 should count as "not seen"
- if(dt >= 1 && dt <= 5)
- Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt);
- }
-
- if(!(self.items & IT_UNLIMITED_WEAPON_AMMO) && time > self.nix_nextincr)
- {
- switch(e.ammo_field)
- {
- case ammo_shells: self.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break;
- case ammo_nails: self.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break;
- case ammo_rockets: self.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break;
- case ammo_cells: self.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break;
- case ammo_plasma: self.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break;
- case ammo_fuel: self.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break;
- }
-
- self.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
- }
-
- self.weapons = '0 0 0';
- if(g_nix_with_blaster)
- self.weapons |= WEPSET(BLASTER);
- self.weapons |= WepSet_FromWeapon(nix_weapon);
-
- if(self.switchweapon != nix_weapon)
- if(!client_hasweapon(self, self.switchweapon, true, false))
- if(client_hasweapon(self, nix_weapon, true, false))
- W_SwitchWeapon(nix_weapon);
-}
-
-MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon)
-{
- return 1; // no throwing in NIX
-}
-
-MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":NIX");
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", NIX");
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(nix, FilterItem)
-{SELFPARAM();
- switch (self.items)
- {
- case ITEM_HealthSmall.m_itemid:
- case ITEM_HealthMedium.m_itemid:
- case ITEM_HealthLarge.m_itemid:
- case ITEM_HealthMega.m_itemid:
- case ITEM_ArmorSmall.m_itemid:
- case ITEM_ArmorMedium.m_itemid:
- case ITEM_ArmorLarge.m_itemid:
- case ITEM_ArmorMega.m_itemid:
- if (autocvar_g_nix_with_healtharmor)
- return 0;
- break;
- case ITEM_Strength.m_itemid:
- case ITEM_Shield.m_itemid:
- if (autocvar_g_nix_with_powerups)
- return 0;
- break;
- }
-
- return 1; // delete all other items
-}
-
-MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn)
-{SELFPARAM();
- if(self.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo)
- return 1;
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(nix, PlayerPreThink)
-{SELFPARAM();
- if(!intermission_running)
- if(self.deadflag == DEAD_NO)
- if(IS_PLAYER(self))
- NIX_GiveCurrentWeapon();
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(nix, PlayerSpawn)
-{SELFPARAM();
- self.nix_lastchange_id = -1;
- NIX_GiveCurrentWeapon(); // overrides the weapons you got when spawning
- self.items |= IT_UNLIMITED_SUPERWEAPONS;
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST)
-{
- modname = "NIX";
- return 0;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-.vector ok_deathloc;
-.float ok_spawnsys_timer;
-.float ok_lastwep;
-.float ok_item;
-
-.float ok_notice_time;
-.float ammo_charge[Weapons_MAX];
-.float ok_use_ammocharge;
-.float ok_ammo_charge;
-
-.float ok_pauseregen_finished;
-
-void(entity ent, float wep) ok_DecreaseCharge;
-
-void ok_Initialize();
-
-REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
-{
- MUTATOR_ONADD
- {
- ok_Initialize();
- }
-
- MUTATOR_ONREMOVE
- {
- WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
- WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
- }
-
- return false;
-}
-
-void W_Blaster_Attack(entity, float, float, float, float, float, float, float, float, float, float);
-spawnfunc(weapon_hmg);
-spawnfunc(weapon_rpc);
-
-void ok_DecreaseCharge(entity ent, int wep)
-{
- if(!ent.ok_use_ammocharge) return;
-
- entity wepent = get_weaponinfo(wep);
-
- if(wepent.weapon == 0)
- return; // dummy
-
- ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
-}
-
-void ok_IncreaseCharge(entity ent, int wep)
-{
- entity wepent = get_weaponinfo(wep);
-
- if(wepent.weapon == 0)
- return; // dummy
-
- if(ent.ok_use_ammocharge)
- if(!ent.BUTTON_ATCK) // not while attacking?
- ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME);
-}
-
-float ok_CheckWeaponCharge(entity ent, int wep)
-{
- if(!ent.ok_use_ammocharge) return true;
-
- entity wepent = get_weaponinfo(wep);
-
- if(wepent.weapon == 0)
- return 0; // dummy
-
- return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname)));
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST)
-{
- if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target))
- if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
- {
- frag_damage = 0;
-
- if(frag_attacker != frag_target)
- if(frag_target.health > 0)
- if(frag_target.frozen == 0)
- if(frag_target.deadflag == DEAD_NO)
- {
- Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE);
- frag_force = '0 0 0';
- }
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerDamage_SplitHealthArmor)
-{SELFPARAM();
- if(damage_take)
- self.ok_pauseregen_finished = max(self.ok_pauseregen_finished, time + 2);
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerDies)
-{SELFPARAM();
- entity targ = ((frag_attacker) ? frag_attacker : frag_target);
-
- if(IS_MONSTER(self))
- {
- remove(other); // remove default item
- other = world;
- }
-
- setself(spawn());
- self.ok_item = true;
- self.noalign = true;
- self.pickup_anyway = true;
- spawnfunc_item_armor_small(this);
- self.movetype = MOVETYPE_TOSS;
- self.gravity = 1;
- self.reset = SUB_Remove;
- setorigin(self, frag_target.origin + '0 0 32');
- self.velocity = '0 0 200' + normalize(targ.origin - self.origin) * 500;
- self.classname = "droppedweapon"; // hax
- SUB_SetFade(self, time + 5, 1);
- setself(this);
-
- self.ok_lastwep = self.switchweapon;
-
- return false;
-}
-MUTATOR_HOOKFUNCTION(ok, MonsterDropItem) { ok_PlayerDies(); }
-
-MUTATOR_HOOKFUNCTION(ok, PlayerRegen)
-{SELFPARAM();
- // overkill's values are different, so use custom regen
- if(!self.frozen)
- {
- self.armorvalue = CalcRotRegen(self.armorvalue, autocvar_g_balance_armor_regenstable, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, 1 * frametime * (time > self.ok_pauseregen_finished), 0, 0, 1, 1 * frametime * (time > self.pauserotarmor_finished), autocvar_g_balance_armor_limit);
- self.health = CalcRotRegen(self.health, autocvar_g_balance_health_regenstable, 0, 100, 1 * frametime * (time > self.ok_pauseregen_finished), 200, 0, autocvar_g_balance_health_rotlinear, 1 * frametime * (time > self.pauserothealth_finished), autocvar_g_balance_health_limit);
-
- float minf, maxf, limitf;
-
- maxf = autocvar_g_balance_fuel_rotstable;
- minf = autocvar_g_balance_fuel_regenstable;
- limitf = autocvar_g_balance_fuel_limit;
-
- self.ammo_fuel = CalcRotRegen(self.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > self.pauseregen_finished) * ((self.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > self.pauserotfuel_finished), limitf);
- }
- return true; // return true anyway, as frozen uses no regen
-}
-
-MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon)
-{
- return true;
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
-{SELFPARAM();
- if(intermission_running || gameover)
- return false;
-
- if(self.deadflag != DEAD_NO || !IS_PLAYER(self) || self.frozen)
- return false;
-
- if(self.ok_lastwep)
- {
- self.switchweapon = self.ok_lastwep;
- self.ok_lastwep = 0;
- }
-
- ok_IncreaseCharge(self, self.weapon);
-
- if(self.BUTTON_ATCK2)
- if(!forbidWeaponUse(self) || self.weapon_blocked) // allow if weapon is blocked
- if(time >= self.jump_interval)
- {
- self.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor();
- makevectors(self.v_angle);
-
- int oldwep = self.weapon;
- self.weapon = WEP_BLASTER.m_id;
- W_Blaster_Attack(
- self,
- WEP_BLASTER.m_id | HITTYPE_SECONDARY,
- WEP_CVAR_SEC(vaporizer, shotangle),
- WEP_CVAR_SEC(vaporizer, damage),
- WEP_CVAR_SEC(vaporizer, edgedamage),
- WEP_CVAR_SEC(vaporizer, radius),
- WEP_CVAR_SEC(vaporizer, force),
- WEP_CVAR_SEC(vaporizer, speed),
- WEP_CVAR_SEC(vaporizer, spread),
- WEP_CVAR_SEC(vaporizer, delay),
- WEP_CVAR_SEC(vaporizer, lifetime)
- );
- self.weapon = oldwep;
- }
-
- self.weapon_blocked = false;
-
- self.ok_ammo_charge = self.ammo_charge[self.weapon];
-
- if(self.ok_use_ammocharge)
- if(!ok_CheckWeaponCharge(self, self.weapon))
- {
- if(autocvar_g_overkill_ammo_charge_notice && time > self.ok_notice_time && self.BUTTON_ATCK && IS_REAL_CLIENT(self) && self.weapon == self.switchweapon)
- {
- //Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERKILL_CHARGE);
- self.ok_notice_time = time + 2;
- play2(self, SND(DRYFIRE));
- }
- Weapon wpn = get_weaponinfo(self.weapon);
- if(self.weaponentity.state != WS_CLEAR)
- w_ready(wpn, self, self.BUTTON_ATCK, self.BUTTON_ATCK2);
-
- self.weapon_blocked = true;
- }
-
- self.BUTTON_ATCK2 = 0;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ok, PlayerSpawn)
-{SELFPARAM();
- if(autocvar_g_overkill_ammo_charge)
- {
- for(int i = WEP_FIRST; i <= WEP_LAST; ++i)
- self.ammo_charge[i] = autocvar_g_overkill_ammo_charge_limit;
-
- self.ok_use_ammocharge = 1;
- self.ok_notice_time = time;
- }
- else
- self.ok_use_ammocharge = 0;
-
- self.ok_pauseregen_finished = time + 2;
-
- return false;
-}
-
-void _spawnfunc_weapon_hmg() { SELFPARAM(); spawnfunc_weapon_hmg(this); }
-void _spawnfunc_weapon_rpc() { SELFPARAM(); spawnfunc_weapon_rpc(this); }
-
-MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn)
-{SELFPARAM();
- if(autocvar_g_powerups)
- if(autocvar_g_overkill_powerups_replace)
- {
- if(self.classname == "item_strength")
- {
- entity wep = spawn();
- setorigin(wep, self.origin);
- setmodel(wep, MDL_OK_HMG);
- wep.classname = "weapon_hmg";
- wep.ok_item = true;
- wep.noalign = self.noalign;
- wep.cnt = self.cnt;
- wep.team = self.team;
- wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
- wep.pickup_anyway = true;
- wep.think = _spawnfunc_weapon_hmg;
- wep.nextthink = time + 0.1;
- return true;
- }
-
- if(self.classname == "item_invincible")
- {
- entity wep = spawn();
- setorigin(wep, self.origin);
- setmodel(wep, MDL_OK_RPC);
- wep.classname = "weapon_rpc";
- wep.ok_item = true;
- wep.noalign = self.noalign;
- wep.cnt = self.cnt;
- wep.team = self.team;
- wep.respawntime = autocvar_g_overkill_superguns_respawn_time;
- wep.pickup_anyway = true;
- wep.think = _spawnfunc_weapon_rpc;
- wep.nextthink = time + 0.1;
- return true;
- }
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ok, FilterItem)
-{SELFPARAM();
- if(self.ok_item)
- return false;
-
- switch(self.items)
- {
- case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway);
- case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway);
- }
-
- return true;
-}
-
-MUTATOR_HOOKFUNCTION(ok, SpectateCopy)
-{SELFPARAM();
- self.ammo_charge[self.weapon] = other.ammo_charge[other.weapon];
- self.ok_use_ammocharge = other.ok_use_ammocharge;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ok, SetStartItems)
-{
- WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN));
-
- if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); }
- if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); }
-
- start_items |= IT_UNLIMITED_WEAPON_AMMO;
- start_weapons = warmup_start_weapons = ok_start_items;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":OK");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Overkill");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(ok, SetModname)
-{
- modname = "Overkill";
- return true;
-}
-
-void ok_SetCvars()
-{
- // hack to force overkill playermodels
- cvar_settemp("sv_defaultcharacter", "1");
- cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
- cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm");
- cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm");
-}
-
-void ok_Initialize()
-{
- ok_SetCvars();
-
- precache_all_playermodels("models/ok_player/*.dpm");
-
- addstat(STAT_OK_AMMO_CHARGE, AS_FLOAT, ok_use_ammocharge);
- addstat(STAT_OK_AMMO_CHARGEPOOL, AS_FLOAT, ok_ammo_charge);
-
- WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
- WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
-
- WEP_SHOTGUN.mdl = "ok_shotgun";
- WEP_MACHINEGUN.mdl = "ok_mg";
- WEP_VORTEX.mdl = "ok_sniper";
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(physical_items, cvar("g_physical_items"))
-{
- // check if we have a physics engine
- MUTATOR_ONADD
- {
- if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")))
- {
- LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n");
- return -1;
- }
- }
-
- MUTATOR_ONROLLBACK_OR_REMOVE
- {
- // nothing to roll back
- }
-
- MUTATOR_ONREMOVE
- {
- LOG_INFO("This cannot be removed at runtime\n");
- return -1;
- }
-
- return 0;
-}
-
-.vector spawn_origin, spawn_angles;
-
-void physical_item_think()
-{SELFPARAM();
- self.nextthink = time;
-
- self.alpha = self.owner.alpha; // apply fading and ghosting
-
- if(!self.cnt) // map item, not dropped
- {
- // copy ghost item properties
- self.colormap = self.owner.colormap;
- self.colormod = self.owner.colormod;
- self.glowmod = self.owner.glowmod;
-
- // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there
- if(autocvar_g_physical_items_reset)
- {
- if(self.owner.wait > time) // awaiting respawn
- {
- setorigin(self, self.spawn_origin);
- self.angles = self.spawn_angles;
- self.solid = SOLID_NOT;
- self.alpha = -1;
- self.movetype = MOVETYPE_NONE;
- }
- else
- {
- self.alpha = 1;
- self.solid = SOLID_CORPSE;
- self.movetype = MOVETYPE_PHYSICS;
- }
- }
- }
-
- if(!self.owner.modelindex)
- remove(self); // the real item is gone, remove this
-}
-
-void physical_item_touch()
-{SELFPARAM();
- if(!self.cnt) // not for dropped items
- if (ITEM_TOUCH_NEEDKILL())
- {
- setorigin(self, self.spawn_origin);
- self.angles = self.spawn_angles;
- }
-}
-
-void physical_item_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{SELFPARAM();
- if(!self.cnt) // not for dropped items
- if(ITEM_DAMAGE_NEEDKILL(deathtype))
- {
- setorigin(self, self.spawn_origin);
- self.angles = self.spawn_angles;
- }
-}
-
-MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn)
-{SELFPARAM();
- if(self.owner == world && autocvar_g_physical_items <= 1)
- return false;
- if (self.spawnflags & 1) // floating item
- return false;
-
- // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics.
- // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed.
- entity wep;
- wep = spawn();
- _setmodel(wep, self.model);
- setsize(wep, self.mins, self.maxs);
- setorigin(wep, self.origin);
- wep.angles = self.angles;
- wep.velocity = self.velocity;
-
- wep.owner = self;
- wep.solid = SOLID_CORPSE;
- wep.movetype = MOVETYPE_PHYSICS;
- wep.takedamage = DAMAGE_AIM;
- wep.effects |= EF_NOMODELFLAGS; // disable the spinning
- wep.colormap = self.owner.colormap;
- wep.glowmod = self.owner.glowmod;
- wep.damageforcescale = autocvar_g_physical_items_damageforcescale;
- wep.dphitcontentsmask = self.dphitcontentsmask;
- wep.cnt = (self.owner != world);
-
- wep.think = physical_item_think;
- wep.nextthink = time;
- wep.touch = physical_item_touch;
- wep.event_damage = physical_item_damage;
-
- if(!wep.cnt)
- {
- // fix the spawn origin
- setorigin(wep, wep.origin + '0 0 1');
- entity oldself;
- oldself = self;
- WITH(entity, self, wep, builtin_droptofloor());
- }
-
- wep.spawn_origin = wep.origin;
- wep.spawn_angles = self.angles;
-
- self.effects |= EF_NODRAW; // hide the original weapon
- self.movetype = MOVETYPE_FOLLOW;
- self.aiment = wep; // attach the original weapon
- self.SendEntity = func_null;
-
- return false;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill"));
-
-MUTATOR_HOOKFUNCTION(pinata, PlayerDies)
-{SELFPARAM();
- for(int j = WEP_FIRST; j <= WEP_LAST; ++j)
- if(self.weapons & WepSet_FromWeapon(j))
- if(self.switchweapon != j)
- if(W_IsWeaponThrowable(j))
- W_ThrowNewWeapon(self, j, false, self.origin + (self.mins + self.maxs) * 0.5, randomvec() * 175 + '0 0 325');
-
- return true;
-}
-
-MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":Pinata");
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Piñata");
- return false;
-}
-
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-// Random Gravity
-//
-// Mutator by Mario
-// Inspired by Player 2
-
-REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity"))
-{
- MUTATOR_ONADD
- {
- cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end
- }
-
- return false;
-}
-
-float gravity_delay;
-
-MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame)
-{
- if(gameover || !cvar("g_random_gravity")) return false;
- if(time < gravity_delay) return false;
- if(time < game_starttime) return false;
- if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false;
-
- if(random() >= autocvar_g_random_gravity_negative_chance)
- cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max)));
- else
- cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max)));
-
- gravity_delay = time + autocvar_g_random_gravity_delay;
-
- LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity), "\n");
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":RandomGravity");
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Random gravity");
- return 0;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying"));
-
-MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile)
-{
- if(other.classname == "rocket" || other.classname == "mine")
- {
- // kill detonate delay of rockets
- other.spawnshieldtime = time;
- }
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":RocketFlying");
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Rocket Flying");
- return 0;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-#include "../../../common/deathtypes/all.qh"
-#include "../../round_handler.qh"
-
-REGISTER_MUTATOR(rm, cvar("g_instagib"));
-
-MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate)
-{
- // we do it this way, so rm can be toggled during the match
- if(!autocvar_g_rm) { return false; }
-
- if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR))
- if(frag_attacker == frag_target || frag_target.classname == "nade")
- frag_damage = 0;
-
- if(autocvar_g_rm_laser)
- if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO))
- if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
- frag_damage = 0;
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(rm, PlayerDies)
-{
- // we do it this way, so rm can be toggled during the match
- if(!autocvar_g_rm) { return false; }
-
- if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO))
- frag_damage = 1000; // always gib if it was a vaporizer death
-
- return false;
-}
-
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate") && teamplay);
-
-.entity msnt_lookat;
-
-.float msnt_timer;
-.vector msnt_deathloc;
-
-.float cvar_cl_spawn_near_teammate;
-
-MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score)
-{SELFPARAM();
- if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate))
- return 0;
-
- entity p;
-
- spawn_spot.msnt_lookat = world;
-
- if(!teamplay)
- return 0;
-
- RandomSelection_Init();
- FOR_EACH_PLAYER(p) if(p != self) if(p.team == self.team) if(!p.deadflag)
- {
- float l = vlen(spawn_spot.origin - p.origin);
- if(l > autocvar_g_spawn_near_teammate_distance)
- continue;
- if(l < 48)
- continue;
- if(!checkpvs(spawn_spot.origin, p))
- continue;
- RandomSelection_Add(p, 0, string_null, 1, 1);
- }
-
- if(RandomSelection_chosen_ent)
- {
- spawn_spot.msnt_lookat = RandomSelection_chosen_ent;
- spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
- }
- else if(self.team == spawn_spot.team)
- spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
-
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
-{SELFPARAM();
- // Note: when entering this, fixangle is already set.
- if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate))
- {
- if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death)
- self.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
-
- entity team_mate, best_mate = world;
- vector best_spot = '0 0 0';
- float pc = 0, best_dist = 0, dist = 0;
- FOR_EACH_PLAYER(team_mate)
- {
- if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && team_mate.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0)
- if(team_mate.deadflag == DEAD_NO)
- if(team_mate.msnt_timer < time)
- if(SAME_TEAM(self, team_mate))
- if(time > team_mate.spawnshieldtime) // spawn shielding
- if(team_mate.frozen == 0)
- if(team_mate != self)
- {
- tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate);
- if(trace_fraction != 1.0)
- if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY))
- {
- pc = pointcontents(trace_endpos + '0 0 1');
- if(pc == CONTENT_EMPTY)
- {
- if(vlen(team_mate.velocity) > 5)
- fixedmakevectors(vectoangles(team_mate.velocity));
- else
- fixedmakevectors(team_mate.angles);
-
- for(pc = 0; pc != 5; ++pc) // test 5 diffrent spots close to mate
- {
- switch(pc)
- {
- case 0:
- tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 128, MOVE_NORMAL, team_mate);
- break;
- case 1:
- tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 128 , MOVE_NORMAL, team_mate);
- break;
- case 2:
- tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate);
- break;
- case 3:
- tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate);
- break;
- case 4:
- tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_forward * 128, MOVE_NORMAL, team_mate);
- break;
- }
-
- if(trace_fraction == 1.0)
- {
- traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NORMAL, team_mate);
- if(trace_fraction != 1.0)
- {
- if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath)
- {
- dist = vlen(trace_endpos - self.msnt_deathloc);
- if(dist < best_dist || best_dist == 0)
- {
- best_dist = dist;
- best_spot = trace_endpos;
- best_mate = team_mate;
- }
- }
- else
- {
- setorigin(self, trace_endpos);
- self.angles = team_mate.angles;
- self.angles_z = 0; // never spawn tilted even if the spot says to
- team_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
- return 0;
- }
- }
- }
- }
- }
- }
- }
- }
-
- if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath)
- if(best_dist)
- {
- setorigin(self, best_spot);
- self.angles = best_mate.angles;
- self.angles_z = 0; // never spawn tilted even if the spot says to
- best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
- }
- }
- else if(spawn_spot.msnt_lookat)
- {
- self.angles = vectoangles(spawn_spot.msnt_lookat.origin - self.origin);
- self.angles_x = -self.angles.x;
- self.angles_z = 0; // never spawn tilted even if the spot says to
- /*
- sprint(self, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n");
- sprint(self, "distance: ", vtos(spawn_spot.msnt_lookat.origin - self.origin), "\n");
- sprint(self, "angles: ", vtos(self.angles), "\n");
- */
- }
-
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies)
-{SELFPARAM();
- self.msnt_deathloc = self.origin;
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(spawn_near_teammate, GetCvars)
-{
- GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_spawn_near_teammate, "cl_spawn_near_teammate");
- return false;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(superspec, cvar("g_superspectate"));
-
-#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1"
-#define _ISLOCAL ((edict_num(1) == self) ? true : false)
-
-const float ASF_STRENGTH = BIT(0);
-const float ASF_SHIELD = BIT(1);
-const float ASF_MEGA_AR = BIT(2);
-const float ASF_MEGA_HP = BIT(3);
-const float ASF_FLAG_GRAB = BIT(4);
-const float ASF_OBSERVER_ONLY = BIT(5);
-const float ASF_SHOWWHAT = BIT(6);
-const float ASF_SSIM = BIT(7);
-const float ASF_FOLLOWKILLER = BIT(8);
-const float ASF_ALL = 0xFFFFFF;
-.float autospec_flags;
-
-const float SSF_SILENT = 1;
-const float SSF_VERBOSE = 2;
-const float SSF_ITEMMSG = 4;
-.float superspec_flags;
-
-.string superspec_itemfilter; //"classname1 classname2 ..."
-
-bool superspec_Spectate(entity _player)
-{SELFPARAM();
- if(Spectate(_player) == 1)
- self.classname = STR_SPECTATOR;
-
- return true;
-}
-
-void superspec_save_client_conf()
-{SELFPARAM();
- string fn = "superspec-local.options";
- float fh;
-
- if (!_ISLOCAL)
- {
- if(self.crypto_idfp == "")
- return;
-
- fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp));
- }
-
- fh = fopen(fn, FILE_WRITE);
- if(fh < 0)
- {
- LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n");
- }
- else
- {
- fputs(fh, _SSMAGIX);
- fputs(fh, "\n");
- fputs(fh, ftos(self.autospec_flags));
- fputs(fh, "\n");
- fputs(fh, ftos(self.superspec_flags));
- fputs(fh, "\n");
- fputs(fh, self.superspec_itemfilter);
- fputs(fh, "\n");
- fclose(fh);
- }
-}
-
-void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel)
-{
- sprint(_to, strcat(_con_title, _msg));
-
- if(_to.superspec_flags & SSF_SILENT)
- return;
-
- if(_spamlevel > 1)
- if (!(_to.superspec_flags & SSF_VERBOSE))
- return;
-
- centerprint(_to, strcat(_center_title, _msg));
-}
-
-float superspec_filteritem(entity _for, entity _item)
-{
- float i;
-
- if(_for.superspec_itemfilter == "")
- return true;
-
- if(_for.superspec_itemfilter == "")
- return true;
-
- float l = tokenize_console(_for.superspec_itemfilter);
- for(i = 0; i < l; ++i)
- {
- if(argv(i) == _item.classname)
- return true;
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(superspec, ItemTouch)
-{SELFPARAM();
- entity _item = self;
-
- entity e;
- FOR_EACH_SPEC(e)
- {
- setself(e);
- if(self.superspec_flags & SSF_ITEMMSG)
- if(superspec_filteritem(self, _item))
- {
- if(self.superspec_flags & SSF_VERBOSE)
- superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n", other.netname, _item.netname), 1);
- else
- superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", other.netname, _item.netname, _item.classname), 1);
- if((self.autospec_flags & ASF_SSIM) && self.enemy != other)
- {
- superspec_Spectate(other);
-
- setself(this);
- return MUT_ITEMTOUCH_CONTINUE;
- }
- }
-
- if((self.autospec_flags & ASF_SHIELD && _item.invincible_finished) ||
- (self.autospec_flags & ASF_STRENGTH && _item.strength_finished) ||
- (self.autospec_flags & ASF_MEGA_AR && _item.itemdef == ITEM_ArmorMega) ||
- (self.autospec_flags & ASF_MEGA_HP && _item.itemdef == ITEM_HealthMega) ||
- (self.autospec_flags & ASF_FLAG_GRAB && _item.classname == "item_flag_team"))
- {
-
- if((self.enemy != other) || IS_OBSERVER(self))
- {
- if(self.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(self))
- {
- if(self.superspec_flags & SSF_VERBOSE)
- superspec_msg("", "", self, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", other.netname, _item.netname), 2);
- }
- else
- {
- if(self.autospec_flags & ASF_SHOWWHAT)
- superspec_msg("", "", self, sprintf("^7Following %s^7 due to picking up %s\n", other.netname, _item.netname), 2);
-
- superspec_Spectate(other);
- }
- }
- }
- }
-
- setself(this);
-
- return MUT_ITEMTOUCH_CONTINUE;
-}
-
-MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand)
-{SELFPARAM();
-#define OPTIONINFO(flag,var,test,text,long,short) \
- var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \
- var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n")
-
- if(MUTATOR_RETURNVALUE) // command was already handled?
- return false;
-
- if(IS_PLAYER(self))
- return false;
-
- if(cmd_name == "superspec_itemfilter")
- {
- if(argv(1) == "help")
- {
- string _aspeco;
- _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n";
- _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n");
- _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n");
- superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", self, _aspeco, 1);
- }
- else if(argv(1) == "clear")
- {
- if(self.superspec_itemfilter != "")
- strunzone(self.superspec_itemfilter);
-
- self.superspec_itemfilter = "";
- }
- else if(argv(1) == "show" || argv(1) == "")
- {
- if(self.superspec_itemfilter == "")
- {
- superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", self, "", 1);
- return true;
- }
- float i;
- float l = tokenize_console(self.superspec_itemfilter);
- string _msg = "";
- for(i = 0; i < l; ++i)
- _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n");
- //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i));
-
- _msg = strcat(_msg,"\n");
-
- superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", self, _msg, 1);
- }
- else
- {
- if(self.superspec_itemfilter != "")
- strunzone(self.superspec_itemfilter);
-
- self.superspec_itemfilter = strzone(argv(1));
- }
-
- return true;
- }
-
- if(cmd_name == "superspec")
- {
- string _aspeco;
-
- if(cmd_argc > 1)
- {
- float i, _bits = 0, _start = 1;
- if(argv(1) == "help")
- {
- _aspeco = "use cmd superspec [option] [on|off] to set options\n\n";
- _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n");
- _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n");
- _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n");
- _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n");
- superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", self, _aspeco, 1);
- return true;
- }
-
- if(argv(1) == "clear")
- {
- self.superspec_flags = 0;
- _start = 2;
- }
-
- for(i = _start; i < cmd_argc; ++i)
- {
- if(argv(i) == "on" || argv(i) == "1")
- {
- self.superspec_flags |= _bits;
- _bits = 0;
- }
- else if(argv(i) == "off" || argv(i) == "0")
- {
- if(_start == 1)
- self.superspec_flags &= ~_bits;
-
- _bits = 0;
- }
- else
- {
- if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ;
- if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE;
- if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG;
- }
- }
- }
-
- _aspeco = "";
- OPTIONINFO(self.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si");
- OPTIONINFO(self.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve");
- OPTIONINFO(self.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im");
-
- superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", self, _aspeco, 1);
-
- return true;
- }
-
-/////////////////////
-
- if(cmd_name == "autospec")
- {
- string _aspeco;
- if(cmd_argc > 1)
- {
- if(argv(1) == "help")
- {
- _aspeco = "use cmd autospec [option] [on|off] to set options\n\n";
- _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n");
- _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n");
- _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n");
- _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n");
- _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n");
- _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n");
- _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n");
- _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n");
- _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n");
- _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n");
- superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", self, _aspeco, 1);
- return true;
- }
-
- float i, _bits = 0, _start = 1;
- if(argv(1) == "clear")
- {
- self.autospec_flags = 0;
- _start = 2;
- }
-
- for(i = _start; i < cmd_argc; ++i)
- {
- if(argv(i) == "on" || argv(i) == "1")
- {
- self.autospec_flags |= _bits;
- _bits = 0;
- }
- else if(argv(i) == "off" || argv(i) == "0")
- {
- if(_start == 1)
- self.autospec_flags &= ~_bits;
-
- _bits = 0;
- }
- else
- {
- if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH;
- if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD;
- if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP;
- if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR;
- if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB;
- if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY;
- if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT;
- if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM;
- if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER;
- if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL;
- }
- }
- }
-
- _aspeco = "";
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st");
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh");
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh");
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma");
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg");
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo");
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw");
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im");
- OPTIONINFO(self.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk");
-
- superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", self, _aspeco, 1);
- return true;
- }
-
- if(cmd_name == "followpowerup")
- {
- entity _player;
- FOR_EACH_PLAYER(_player)
- {
- if(_player.strength_finished > time || _player.invincible_finished > time)
- return superspec_Spectate(_player);
- }
-
- superspec_msg("", "", self, "No active powerup\n", 1);
- return true;
- }
-
- if(cmd_name == "followstrength")
- {
- entity _player;
- FOR_EACH_PLAYER(_player)
- {
- if(_player.strength_finished > time)
- return superspec_Spectate(_player);
- }
-
- superspec_msg("", "", self, "No active Strength\n", 1);
- return true;
- }
-
- if(cmd_name == "followshield")
- {
- entity _player;
- FOR_EACH_PLAYER(_player)
- {
- if(_player.invincible_finished > time)
- return superspec_Spectate(_player);
- }
-
- superspec_msg("", "", self, "No active Shield\n", 1);
- return true;
- }
-
- return false;
-#undef OPTIONINFO
-}
-
-MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":SS");
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Super Spectators");
- return 0;
-}
-
-void superspec_hello()
-{SELFPARAM();
- if(self.enemy.crypto_idfp == "")
- Send_Notification(NOTIF_ONE_ONLY, self.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID);
-
- remove(self);
-}
-
-MUTATOR_HOOKFUNCTION(superspec, ClientConnect)
-{SELFPARAM();
- if(!IS_REAL_CLIENT(self))
- return false;
-
- string fn = "superspec-local.options";
- float fh;
-
- self.superspec_flags = SSF_VERBOSE;
- self.superspec_itemfilter = "";
-
- entity _hello = spawn();
- _hello.enemy = self;
- _hello.think = superspec_hello;
- _hello.nextthink = time + 5;
-
- if (!_ISLOCAL)
- {
- if(self.crypto_idfp == "")
- return false;
-
- fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp));
- }
-
- fh = fopen(fn, FILE_READ);
- if(fh < 0)
- {
- LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n");
- }
- else
- {
- string _magic = fgets(fh);
- if(_magic != _SSMAGIX)
- {
- LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic\n");
- }
- else
- {
- self.autospec_flags = stof(fgets(fh));
- self.superspec_flags = stof(fgets(fh));
- self.superspec_itemfilter = strzone(fgets(fh));
- }
- fclose(fh);
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(superspec, PlayerDies)
-{SELFPARAM();
- entity e;
- FOR_EACH_SPEC(e)
- {
- setself(e);
- if(self.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && self.enemy == this)
- {
- if(self.autospec_flags & ASF_SHOWWHAT)
- superspec_msg("", "", self, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2);
-
- superspec_Spectate(frag_attacker);
- }
- }
-
- setself(this);
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect)
-{
- superspec_save_client_conf();
- return false;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode"));
-
-.float touchexplode_time;
-
-void PlayerTouchExplode(entity p1, entity p2)
-{SELFPARAM();
- vector org = (p1.origin + p2.origin) * 0.5;
- org.z += (p1.mins.z + p2.mins.z) * 0.5;
-
- sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM);
- Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1);
-
- entity e = spawn();
- setorigin(e, org);
- RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, world);
- remove(e);
-}
-
-MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink)
-{SELFPARAM();
- if(time > self.touchexplode_time)
- if(!gameover)
- if(!self.frozen)
- if(IS_PLAYER(self))
- if(self.deadflag == DEAD_NO)
- if (!IS_INDEPENDENT_PLAYER(self))
- FOR_EACH_PLAYER(other) if(self != other)
- {
- if(time > other.touchexplode_time)
- if(!other.frozen)
- if(other.deadflag == DEAD_NO)
- if (!IS_INDEPENDENT_PLAYER(other))
- if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax))
- {
- PlayerTouchExplode(self, other);
- self.touchexplode_time = other.touchexplode_time = time + 0.2;
- }
- }
-
- return false;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib"));
-
-MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor)
-{
- if(time >= frag_target.spawnshieldtime)
- if(frag_target != frag_attacker)
- if(frag_target.deadflag == DEAD_NO)
- {
- frag_attacker.health += bound(0, damage_take, frag_target.health);
- frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit);
- }
-
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString)
-{
- ret_string = strcat(ret_string, ":Vampire");
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString)
-{
- ret_string = strcat(ret_string, ", Vampire");
- return 0;
-}
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-REGISTER_MUTATOR(vh, cvar("g_vampirehook"));
-
-bool autocvar_g_vampirehook_teamheal;
-float autocvar_g_vampirehook_damage;
-float autocvar_g_vampirehook_damagerate;
-float autocvar_g_vampirehook_health_steal;
-
-.float last_dmg;
-
-MUTATOR_HOOKFUNCTION(vh, GrappleHookThink)
-{SELFPARAM();
- entity dmgent = ((SAME_TEAM(self.owner, self.aiment) && autocvar_g_vampirehook_teamheal) ? self.owner : self.aiment);
-
- if(IS_PLAYER(self.aiment))
- if(self.last_dmg < time)
- if(!self.aiment.frozen)
- if(time >= game_starttime)
- if(DIFF_TEAM(self.owner, self.aiment) || autocvar_g_vampirehook_teamheal)
- if(self.aiment.health > 0)
- if(autocvar_g_vampirehook_damage)
- {
- self.last_dmg = time + autocvar_g_vampirehook_damagerate;
- self.owner.damage_dealt += autocvar_g_vampirehook_damage;
- Damage(dmgent, self, self.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, self.origin, '0 0 0');
- if(SAME_TEAM(self.owner, self.aiment))
- self.aiment.health = min(self.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
- else
- self.owner.health = min(self.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max);
-
- if(dmgent == self.owner)
- dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?!
- }
-
- return false;
-}
-
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-// WEAPONTODO: rename the cvars
-REGISTER_MUTATOR(weaponarena_random, true);
-
-MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn) {
- SELFPARAM();
- if (!g_weaponarena_random) return;
- if (g_weaponarena_random_with_blaster) this.weapons &= ~WEPSET(BLASTER);
- W_RandomWeapons(this, g_weaponarena_random);
- if (g_weaponarena_random_with_blaster) this.weapons |= WEPSET(BLASTER);
-}
-
-#endif
+++ /dev/null
-#ifdef IMPLEMENTATION
-float autosave_time;
-void sandbox_Database_Load();
-
-REGISTER_MUTATOR(sandbox, cvar("g_sandbox"))
-{
- MUTATOR_ONADD
- {
- autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame
- if(autocvar_g_sandbox_storage_autoload)
- sandbox_Database_Load();
- }
-
- MUTATOR_ONROLLBACK_OR_REMOVE
- {
- // nothing to roll back
- }
-
- MUTATOR_ONREMOVE
- {
- // nothing to remove
- }
-
- return false;
-}
-
-const float MAX_STORAGE_ATTACHMENTS = 16;
-float object_count;
-.float object_flood;
-.entity object_attach;
-.string material;
-
-.float touch_timer;
-void sandbox_ObjectFunction_Touch()
-{SELFPARAM();
- // apply material impact effects
-
- if(!self.material)
- return;
- if(self.touch_timer > time)
- return; // don't execute each frame
- self.touch_timer = time + 0.1;
-
- // make particle count and sound volume depend on impact speed
- float intensity;
- intensity = vlen(self.velocity) + vlen(other.velocity);
- if(intensity) // avoid divisions by 0
- intensity /= 2; // average the two velocities
- if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min))
- return; // impact not strong enough to do anything
- // now offset intensity and apply it to the effects
- intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity
- intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1);
-
- _sound(self, CH_TRIGGER, strcat("object/impact_", self.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM);
- Send_Effect_(strcat("impact_", self.material), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10
-}
-
-void sandbox_ObjectFunction_Think()
-{SELFPARAM();
- entity e;
-
- // decide if and how this object can be grabbed
- if(autocvar_g_sandbox_readonly)
- self.grab = 0; // no grabbing
- else if(autocvar_g_sandbox_editor_free < 2 && self.crypto_idfp)
- self.grab = 1; // owner only
- else
- self.grab = 3; // anyone
-
- // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server).
- // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this,
- // since if the owning player disconnects, the object's owner should also be reset.
- FOR_EACH_REALPLAYER(e) // bots can't have objects
- {
- if(self.crypto_idfp == e.crypto_idfp)
- {
- self.realowner = e;
- break;
- }
- self.realowner = world;
- }
-
- self.nextthink = time;
-
- CSQCMODEL_AUTOUPDATE(self);
-}
-
-.float old_solid, old_movetype;
-entity sandbox_ObjectEdit_Get(float permissions)
-{SELFPARAM();
- // Returns the traced entity if the player can edit it, and world if not.
- // If permissions if false, the object is returned regardless of editing rights.
- // Attached objects are SOLID_NOT and do not get traced.
-
- crosshair_trace_plusvisibletriggers(self);
- if(vlen(self.origin - trace_ent.origin) > autocvar_g_sandbox_editor_distance_edit)
- return world; // out of trace range
- if(trace_ent.classname != "object")
- return world; // entity is not an object
- if(!permissions)
- return trace_ent; // don't check permissions, anyone can edit this object
- if(trace_ent.crypto_idfp == "")
- return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it
- if (!(trace_ent.realowner != self && autocvar_g_sandbox_editor_free < 2))
- return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server
- return world;
-}
-
-void sandbox_ObjectEdit_Scale(entity e, float f)
-{
- e.scale = f;
- if(e.scale)
- {
- e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max);
- _setmodel(e, e.model); // reset mins and maxs based on mesh
- setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size
- }
-}
-
-void sandbox_ObjectAttach_Remove(entity e);
-void sandbox_ObjectAttach_Set(entity e, entity parent, string s)
-{
- // attaches e to parent on string s
-
- // we can't attach to an attachment, for obvious reasons
- sandbox_ObjectAttach_Remove(e);
-
- e.old_solid = e.solid; // persist solidity
- e.old_movetype = e.movetype; // persist physics
- e.movetype = MOVETYPE_FOLLOW;
- e.solid = SOLID_NOT;
- e.takedamage = DAMAGE_NO;
-
- setattachment(e, parent, s);
- e.owner = parent;
-}
-
-void sandbox_ObjectAttach_Remove(entity e)
-{
- // detaches any object attached to e
-
- entity head;
- for(head = world; (head = find(head, classname, "object")); )
- {
- if(head.owner == e)
- {
- vector org;
- org = gettaginfo(head, 0);
- setattachment(head, world, "");
- head.owner = world;
-
- // objects change origin and angles when detached, so apply previous position
- setorigin(head, org);
- head.angles = e.angles; // don't allow detached objects to spin or roll
-
- head.solid = head.old_solid; // restore persisted solidity
- head.movetype = head.old_movetype; // restore persisted physics
- head.takedamage = DAMAGE_AIM;
- }
- }
-}
-
-entity sandbox_ObjectSpawn(float database)
-{SELFPARAM();
- // spawn a new object with default properties
-
- entity e = spawn();
- e.classname = "object";
- e.takedamage = DAMAGE_AIM;
- e.damageforcescale = 1;
- e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly
- e.movetype = MOVETYPE_TOSS;
- e.frame = 0;
- e.skin = 0;
- e.material = string_null;
- e.touch = sandbox_ObjectFunction_Touch;
- e.think = sandbox_ObjectFunction_Think;
- e.nextthink = time;
- //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects?
-
- if(!database)
- {
- // set the object's owner via player UID
- // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone
- if(self.crypto_idfp != "")
- e.crypto_idfp = strzone(self.crypto_idfp);
- else
- print_to(self, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!");
-
- // set public object information
- e.netname = strzone(self.netname); // name of the owner
- e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time
- e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time
-
- // set origin and direction based on player position and view angle
- makevectors(self.v_angle);
- WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, self);
- setorigin(e, trace_endpos);
- e.angles_y = self.v_angle.y;
- }
-
- WITH(entity, self, e, CSQCMODEL_AUTOINIT(e));
-
- object_count += 1;
- return e;
-}
-
-void sandbox_ObjectRemove(entity e)
-{
- sandbox_ObjectAttach_Remove(e); // detach child objects
-
- // if the object being removed has been selected for attachment by a player, unset it
- entity head;
- FOR_EACH_REALPLAYER(head) // bots can't have objects
- {
- if(head.object_attach == e)
- head.object_attach = world;
- }
-
- if(e.material) { strunzone(e.material); e.material = string_null; }
- if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; }
- if(e.netname) { strunzone(e.netname); e.netname = string_null; }
- if(e.message) { strunzone(e.message); e.message = string_null; }
- if(e.message2) { strunzone(e.message2); e.message2 = string_null; }
- remove(e);
- e = world;
-
- object_count -= 1;
-}
-
-string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global
-
-string sandbox_ObjectPort_Save(entity e, float database)
-{
- // save object properties, and return them as a string
- float i = 0;
- string s;
- entity head;
-
- for(head = world; (head = find(head, classname, "object")); )
- {
- // the main object needs to be first in the array [0] with attached objects following
- float slot, physics, solidity;
- if(head == e) // this is the main object, place it first
- {
- slot = 0;
- solidity = head.solid; // applied solidity is normal solidity for children
- physics = head.movetype; // applied physics are normal physics for parents
- }
- else if(head.owner == e) // child object, list them in order
- {
- i += 1; // children start from 1
- slot = i;
- solidity = head.old_solid; // persisted solidity is normal solidity for children
- physics = head.old_movetype; // persisted physics are normal physics for children
- gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below
- }
- else
- continue;
-
- // ---------------- OBJECT PROPERTY STORAGE: SAVE ----------------
- if(slot)
- {
- // properties stored only for child objects
- if(gettaginfo_name) port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none
- }
- else
- {
- // properties stored only for parent objects
- if(database)
- {
- port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " ");
- port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " ");
- }
- }
- // properties stored for all objects
- port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" ");
- port_string[slot] = strcat(port_string[slot], ftos(head.skin), " ");
- port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " ");
- port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " ");
- port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " ");
- port_string[slot] = strcat(port_string[slot], ftos(head.frame), " ");
- port_string[slot] = strcat(port_string[slot], ftos(head.scale), " ");
- port_string[slot] = strcat(port_string[slot], ftos(solidity), " ");
- port_string[slot] = strcat(port_string[slot], ftos(physics), " ");
- port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " ");
- if(head.material) port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none
- if(database)
- {
- // properties stored only for the database
- if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none
- port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" ");
- port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" ");
- port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" ");
- }
- }
-
- // now apply the array to a simple string, with the ; symbol separating objects
- s = "";
- for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
- {
- if(port_string[i])
- s = strcat(s, port_string[i], "; ");
- port_string[i] = string_null; // fully clear the string
- }
-
- return s;
-}
-
-entity sandbox_ObjectPort_Load(string s, float database)
-{
- // load object properties, and spawn a new object with them
- float n, i;
- entity e = world, parent = world;
-
- // separate objects between the ; symbols
- n = tokenizebyseparator(s, "; ");
- for(i = 0; i < n; ++i)
- port_string[i] = argv(i);
-
- // now separate and apply the properties of each object
- for(i = 0; i < n; ++i)
- {
- float argv_num;
- string tagname = string_null;
- argv_num = 0;
- tokenize_console(port_string[i]);
- e = sandbox_ObjectSpawn(database);
-
- // ---------------- OBJECT PROPERTY STORAGE: LOAD ----------------
- if(i)
- {
- // properties stored only for child objects
- if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num;
- }
- else
- {
- // properties stored only for parent objects
- if(database)
- {
- setorigin(e, stov(argv(argv_num))); ++argv_num;
- e.angles = stov(argv(argv_num)); ++argv_num;
- }
- parent = e; // mark parent objects as such
- }
- // properties stored for all objects
- _setmodel(e, argv(argv_num)); ++argv_num;
- e.skin = stof(argv(argv_num)); ++argv_num;
- e.alpha = stof(argv(argv_num)); ++argv_num;
- e.colormod = stov(argv(argv_num)); ++argv_num;
- e.glowmod = stov(argv(argv_num)); ++argv_num;
- e.frame = stof(argv(argv_num)); ++argv_num;
- sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num;
- e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num;
- e.movetype = e.old_movetype = stof(argv(argv_num)); ++argv_num;
- e.damageforcescale = stof(argv(argv_num)); ++argv_num;
- if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num;
- if(database)
- {
- // properties stored only for the database
- if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num;
- if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num;
- if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num;
- if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num;
- }
-
- // attach last
- if(i)
- sandbox_ObjectAttach_Set(e, parent, tagname);
- }
-
- for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
- port_string[i] = string_null; // fully clear the string
-
- return e;
-}
-
-void sandbox_Database_Save()
-{
- // saves all objects to the database file
- entity head;
- string file_name;
- float file_get;
-
- file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
- file_get = fopen(file_name, FILE_WRITE);
- fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S")));
- fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n"));
-
- for(head = world; (head = find(head, classname, "object")); )
- {
- // attached objects are persisted separately, ignore them here
- if(head.owner != world)
- continue;
-
- // use a line of text for each object, listing all properties
- fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n"));
- }
- fclose(file_get);
-}
-
-void sandbox_Database_Load()
-{
- // loads all objects from the database file
- string file_read, file_name;
- float file_get, i;
-
- file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
- file_get = fopen(file_name, FILE_READ);
- if(file_get < 0)
- {
- if(autocvar_g_sandbox_info > 0)
- LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n"));
- }
- else
- {
- for (;;)
- {
- file_read = fgets(file_get);
- if(file_read == "")
- break;
- if(substring(file_read, 0, 2) == "//")
- continue;
- if(substring(file_read, 0, 1) == "#")
- continue;
-
- entity e;
- e = sandbox_ObjectPort_Load(file_read, true);
-
- if(e.material)
- {
- // since objects are being loaded for the first time, precache material sounds for each
- for (i = 1; i <= 5; i++) // 5 sounds in total
- precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav"));
- }
- }
- if(autocvar_g_sandbox_info > 0)
- LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n"));
- }
- fclose(file_get);
-}
-
-MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand)
-{SELFPARAM();
- if(MUTATOR_RETURNVALUE) // command was already handled?
- return false;
- if(cmd_name == "g_sandbox")
- {
- if(autocvar_g_sandbox_readonly)
- {
- print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
- return true;
- }
- if(cmd_argc < 2)
- {
- print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
- return true;
- }
-
- switch(argv(1))
- {
- entity e;
- float i;
- string s;
-
- // ---------------- COMMAND: HELP ----------------
- case "help":
- print_to(self, "You can use the following sandbox commands:");
- print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model");
- print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
- print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
- print_to(self, "^3copy value ^7- copies the properties of the object to the specified client cvar");
- print_to(self, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\"");
- print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
- print_to(self, "^3get ^7- selects the object you are facing as the object to be attached");
- print_to(self, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
- print_to(self, "^3remove ^7- detaches all objects from the object you are facing");
- print_to(self, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
- print_to(self, "^3skin value ^7- changes the skin of the object");
- print_to(self, "^3alpha value ^7- sets object transparency");
- print_to(self, "^3colormod \"value_x value_y value_z\" ^7- main object color");
- print_to(self, "^3glowmod \"value_x value_y value_z\" ^7- glow object color");
- print_to(self, "^3frame value ^7- object animation frame, for self-animated models");
- print_to(self, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size");
- print_to(self, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid");
- print_to(self, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical");
- print_to(self, "^3force value ^7- amount of force applied to objects that are shot");
- print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
- print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it");
- print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object");
- print_to(self, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
- print_to(self, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
- print_to(self, "^3attachments ^7- prints information about the object's attachments");
- print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
- return true;
-
- // ---------------- COMMAND: OBJECT, SPAWN ----------------
- case "object_spawn":
- if(time < self.object_flood)
- {
- print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object"));
- return true;
- }
- self.object_flood = time + autocvar_g_sandbox_editor_flood;
- if(object_count >= autocvar_g_sandbox_editor_maxobjects)
- {
- print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
- return true;
- }
- if(cmd_argc < 3)
- {
- print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command");
- return true;
- }
- if (!(fexists(argv(2))))
- {
- print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct");
- return true;
- }
-
- e = sandbox_ObjectSpawn(false);
- _setmodel(e, argv(2));
-
- if(autocvar_g_sandbox_info > 0)
- LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " spawned an object at origin ^3", vtos(e.origin), "\n"));
- return true;
-
- // ---------------- COMMAND: OBJECT, REMOVE ----------------
- case "object_remove":
- e = sandbox_ObjectEdit_Get(true);
- if(e != world)
- {
- if(autocvar_g_sandbox_info > 0)
- LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " removed an object at origin ^3", vtos(e.origin), "\n"));
- sandbox_ObjectRemove(e);
- return true;
- }
-
- print_to(self, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over");
- return true;
-
- // ---------------- COMMAND: OBJECT, DUPLICATE ----------------
- case "object_duplicate":
- switch(argv(2))
- {
- case "copy":
- // copies customizable properties of the selected object to the clipboard cvar
- e = sandbox_ObjectEdit_Get(autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
- if(e != world)
- {
- s = sandbox_ObjectPort_Save(e, false);
- s = strreplace("\"", "\\\"", s);
- stuffcmd(self, strcat("set ", argv(3), " \"", s, "\""));
-
- print_to(self, "^2SANDBOX - INFO: ^7Object copied to clipboard");
- return true;
- }
- print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over");
- return true;
-
- case "paste":
- // spawns a new object using the properties in the player's clipboard cvar
- if(time < self.object_flood)
- {
- print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object"));
- return true;
- }
- self.object_flood = time + autocvar_g_sandbox_editor_flood;
- if(argv(3) == "") // no object in clipboard
- {
- print_to(self, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
- return true;
- }
- if(object_count >= autocvar_g_sandbox_editor_maxobjects)
- {
- print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
- return true;
- }
- e = sandbox_ObjectPort_Load(argv(3), false);
-
- print_to(self, "^2SANDBOX - INFO: ^7Object pasted successfully");
- if(autocvar_g_sandbox_info > 0)
- LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " pasted an object at origin ^3", vtos(e.origin), "\n"));
- return true;
- }
- return true;
-
- // ---------------- COMMAND: OBJECT, ATTACH ----------------
- case "object_attach":
- switch(argv(2))
- {
- case "get":
- // select e as the object as meant to be attached
- e = sandbox_ObjectEdit_Get(true);
- if(e != world)
- {
- self.object_attach = e;
- print_to(self, "^2SANDBOX - INFO: ^7Object selected for attachment");
- return true;
- }
- print_to(self, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over");
- return true;
- case "set":
- if(self.object_attach == world)
- {
- print_to(self, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first.");
- return true;
- }
-
- // attaches the previously selected object to e
- e = sandbox_ObjectEdit_Get(true);
- if(e != world)
- {
- sandbox_ObjectAttach_Set(self.object_attach, e, argv(3));
- self.object_attach = world; // object was attached, no longer keep it scheduled for attachment
- print_to(self, "^2SANDBOX - INFO: ^7Object attached successfully");
- if(autocvar_g_sandbox_info > 1)
- LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " attached objects at origin ^3", vtos(e.origin), "\n"));
- return true;
- }
- print_to(self, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over");
- return true;
- case "remove":
- // removes e if it was attached
- e = sandbox_ObjectEdit_Get(true);
- if(e != world)
- {
- sandbox_ObjectAttach_Remove(e);
- print_to(self, "^2SANDBOX - INFO: ^7Child objects detached successfully");
- if(autocvar_g_sandbox_info > 1)
- LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " detached objects at origin ^3", vtos(e.origin), "\n"));
- return true;
- }
- print_to(self, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over");
- return true;
- }
- return true;
-
- // ---------------- COMMAND: OBJECT, EDIT ----------------
- case "object_edit":
- if(argv(2) == "")
- {
- print_to(self, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit");
- return true;
- }
-
- e = sandbox_ObjectEdit_Get(true);
- if(e != world)
- {
- switch(argv(2))
- {
- case "skin":
- e.skin = stof(argv(3));
- break;
- case "alpha":
- e.alpha = stof(argv(3));
- break;
- case "color_main":
- e.colormod = stov(argv(3));
- break;
- case "color_glow":
- e.glowmod = stov(argv(3));
- break;
- case "frame":
- e.frame = stof(argv(3));
- break;
- case "scale":
- sandbox_ObjectEdit_Scale(e, stof(argv(3)));
- break;
- case "solidity":
- switch(argv(3))
- {
- case "0": // non-solid
- e.solid = SOLID_TRIGGER;
- break;
- case "1": // solid
- e.solid = SOLID_BBOX;
- break;
- default:
- break;
- }
- case "physics":
- switch(argv(3))
- {
- case "0": // static
- e.movetype = MOVETYPE_NONE;
- break;
- case "1": // movable
- e.movetype = MOVETYPE_TOSS;
- break;
- case "2": // physical
- e.movetype = MOVETYPE_PHYSICS;
- break;
- default:
- break;
- }
- break;
- case "force":
- e.damageforcescale = stof(argv(3));
- break;
- case "material":
- if(e.material) strunzone(e.material);
- if(argv(3))
- {
- for (i = 1; i <= 5; i++) // precache material sounds, 5 in total
- precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".wav"));
- e.material = strzone(argv(3));
- }
- else
- e.material = string_null; // no material
- break;
- default:
- print_to(self, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'");
- return true;
- }
-
- // update last editing time
- if(e.message2) strunzone(e.message2);
- e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S"));
-
- if(autocvar_g_sandbox_info > 1)
- LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n"));
- return true;
- }
-
- print_to(self, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over");
- return true;
-
- // ---------------- COMMAND: OBJECT, CLAIM ----------------
- case "object_claim":
- // if the player can edit an object but is not its owner, this can be used to claim that object
- if(self.crypto_idfp == "")
- {
- print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
- return true;
- }
- e = sandbox_ObjectEdit_Get(true);
- if(e != world)
- {
- // update the owner's name
- // Do this before checking if you're already the owner and skipping if such, so we
- // also update the player's nickname if he changed it (but has the same player UID)
- if(e.netname != self.netname)
- {
- if(e.netname) strunzone(e.netname);
- e.netname = strzone(self.netname);
- print_to(self, "^2SANDBOX - INFO: ^7Object owner name updated");
- }
-
- if(e.crypto_idfp == self.crypto_idfp)
- {
- print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
- return true;
- }
-
- if(e.crypto_idfp) strunzone(e.crypto_idfp);
- e.crypto_idfp = strzone(self.crypto_idfp);
-
- print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully");
- }
- print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
- return true;
-
- // ---------------- COMMAND: OBJECT, INFO ----------------
- case "object_info":
- // prints public information about the object to the player
- e = sandbox_ObjectEdit_Get(false);
- if(e != world)
- {
- switch(argv(2))
- {
- case "object":
- print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\""));
- return true;
- case "mesh":
- s = "";
- FOR_EACH_TAG(e)
- s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
- print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s));
- return true;
- case "attachments":
- // this should show the same info as 'mesh' but for attachments
- s = "";
- entity head;
- i = 0;
- for(head = world; (head = find(head, classname, "object")); )
- {
- if(head.owner == e)
- {
- ++i; // start from 1
- gettaginfo(e, head.tag_index);
- s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame));
- s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
- }
- }
- if(i) // object contains attachments
- print_to(self, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s));
- else
- print_to(self, "^2SANDBOX - INFO: ^7Object contains no attachments");
- return true;
- }
- }
- print_to(self, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
- return true;
-
- // ---------------- COMMAND: DEFAULT ----------------
- default:
- print_to(self, "Invalid command. For usage information, type 'sandbox help'");
- return true;
- }
- }
- return false;
-}
-
-MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame)
-{
- if(!autocvar_g_sandbox_storage_autosave)
- return false;
- if(time < autosave_time)
- return false;
- autosave_time = time + autocvar_g_sandbox_storage_autosave;
-
- sandbox_Database_Save();
-
- return true;
-}
-#endif
{
LOG_TRACE("AStar: Goal found on first node!\n");
- open = spawn();
+ open = new(path_end);
open.owner = open;
- open.classname = "path_end";
setorigin(open,path.origin);
pathlib_cleanup();
if(!CheckWireframeBox(own, org - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward))
return world;
- portal = spawn();
- portal.classname = "portal";
+ portal = new(portal);
portal.aiment = own;
setorigin(portal, org);
portal.mangle = ang;
#include "../lib/_all.inc"
#include "_all.qh"
+#include "../common/effects/qc/all.qc"
+
#include "anticheat.qc"
#include "antilag.qc"
#include "campaign.qc"
#include "cl_client.qc"
#include "cl_impulse.qc"
#include "cl_player.qc"
-#include "controlpoint.qc"
-#include "csqceffects.qc"
#include "ent_cs.qc"
#include "g_damage.qc"
#include "g_hook.qc"
// #include "g_lights.qc" // TODO: was never used
#include "g_models.qc"
#include "g_subs.qc"
-#include "g_violence.qc"
#include "g_world.qc"
-#include "generator.qc"
#include "ipban.qc"
#include "item_key.qc"
#include "mapvoting.qc"
#include "command/all.qc"
-#include "mutators/all.qc"
-
#include "pathlib/_all.inc"
#include "weapons/accuracy.qc"
#include "../common/campaign_setup.qc"
#include "../common/effects/effectinfo.qc"
#include "../common/mapinfo.qc"
-#include "../common/monsters/spawn.qc"
-#include "../common/monsters/sv_monsters.qc"
#include "../common/minigames/minigames.qc"
#include "../common/minigames/sv_minigames.qc"
+#include "../common/monsters/spawn.qc"
+#include "../common/monsters/sv_monsters.qc"
#include "../common/movetypes/include.qc"
#include "../common/net_notice.qc"
#include "../common/notifications.qc"
#include "../common/physics.qc"
#include "../common/playerstats.qc"
-#include "../common/viewloc.qc"
#include "../common/triggers/include.qc"
#include "../common/util.qc"
+#include "../common/viewloc.qc"
#include "../common/deathtypes/all.qc"
#include "../common/buffs/all.qc"
#include "../common/gamemodes/all.qc"
#include "../common/items/all.qc"
#include "../common/monsters/all.qc"
-#include "../common/mutators/all.qc"
#include "../common/nades/all.qc"
#include "../common/turrets/all.qc"
#include "../common/vehicles/all.qc"
#include "../common/weapons/all.qc"
+#include "../common/mutators/all.qc"
+#include "mutators/all.qc"
#include "../common/turrets/sv_turrets.qc"
#include "../common/turrets/config.qc"
if(!spec)
msg_entity = e;
WRITESPECTATABLE_MSG_ONE({
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_RACE);
+ WriteHeader(MSG_ONE, TE_CSQC_RACE);
if(spec)
{
WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING);
void race_send_recordtime(float msg)
{
// send the server best time
- WriteByte(msg, SVC_TEMPENTITY);
- WriteByte(msg, TE_CSQC_RACE);
+ WriteHeader(msg, TE_CSQC_RACE);
WriteByte(msg, RACE_NET_SERVER_RECORD);
WriteInt24_t(msg, race_readTime(GetMapname(), 1));
}
void race_send_speedaward(float msg)
{
// send the best speed of the round
- WriteByte(msg, SVC_TEMPENTITY);
- WriteByte(msg, TE_CSQC_RACE);
+ WriteHeader(msg, TE_CSQC_RACE);
WriteByte(msg, RACE_NET_SPEED_AWARD);
WriteInt24_t(msg, floor(speedaward_speed+0.5));
WriteString(msg, speedaward_holder);
void race_send_speedaward_alltimebest(float msg)
{
// send the best speed
- WriteByte(msg, SVC_TEMPENTITY);
- WriteByte(msg, TE_CSQC_RACE);
+ WriteHeader(msg, TE_CSQC_RACE);
WriteByte(msg, RACE_NET_SPEED_AWARD_BEST);
WriteInt24_t(msg, floor(speedaward_alltimebest+0.5));
WriteString(msg, speedaward_alltimebest_holder);
void race_SendRankings(float pos, float prevpos, float del, float msg)
{
- WriteByte(msg, SVC_TEMPENTITY);
- WriteByte(msg, TE_CSQC_RACE);
+ WriteHeader(msg, TE_CSQC_RACE);
WriteByte(msg, RACE_NET_SERVER_RANKINGS);
WriteShort(msg, pos);
WriteShort(msg, prevpos);
msg = MSG_ALL;
msg_entity = e;
WRITESPECTATABLE_MSG_ONE_VARNAME(dummy3, {
- WriteByte(msg, SVC_TEMPENTITY);
- WriteByte(msg, TE_CSQC_RACE);
+ WriteHeader(msg, TE_CSQC_RACE);
WriteByte(msg, RACE_NET_SERVER_STATUS);
WriteShort(msg, id);
WriteString(msg, e.netname);
if(g_race_qualifying)
{
WRITESPECTATABLE_MSG_ONE_VARNAME(dummy1, {
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_RACE);
+ WriteHeader(MSG_ONE, TE_CSQC_RACE);
WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING);
WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
WriteInt24_t(MSG_ONE, t); // time to that intermediate
{
msg_entity = e;
WRITESPECTATABLE_MSG_ONE_VARNAME(dummy2, {
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_RACE);
+ WriteHeader(MSG_ONE, TE_CSQC_RACE);
WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE);
WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
if(e == oth)
{
msg_entity = oth;
WRITESPECTATABLE_MSG_ONE_VARNAME(dummy3, {
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_RACE);
+ WriteHeader(MSG_ONE, TE_CSQC_RACE);
WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT);
WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at
if(e == oth)
msg_entity = e;
WRITESPECTATABLE_MSG_ONE({
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_RACE);
+ WriteHeader(MSG_ONE, TE_CSQC_RACE);
WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_CLEAR); // next
});
}
{
msg_entity = pl;
WRITESPECTATABLE_MSG_ONE({
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_RACE);
+ WriteHeader(MSG_ONE, TE_CSQC_RACE);
WriteByte(MSG_ONE, RACE_NET_PENALTY_QUALIFYING);
WriteShort(MSG_ONE, TIME_ENCODE(penalty));
WriteString(MSG_ONE, reason);
{
msg_entity = pl;
WRITESPECTATABLE_MSG_ONE_VARNAME(dummy, {
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_RACE);
+ WriteHeader(MSG_ONE, TE_CSQC_RACE);
WriteByte(MSG_ONE, RACE_NET_PENALTY_RACE);
WriteShort(MSG_ONE, TIME_ENCODE(penalty));
WriteString(MSG_ONE, reason);
#include "../common/util.qh"
void round_handler_Think()
-{SELFPARAM();
- float f;
+{
+ SELFPARAM();
- if(time < game_starttime)
+ if (time < game_starttime)
{
round_handler_Reset(game_starttime);
return;
}
- if(gameover)
+ if (gameover)
{
round_handler_Reset(0);
round_handler_Remove();
return;
}
- if(self.wait)
+ if (this.wait)
{
- self.wait = false;
- self.cnt = self.count + 1; // init countdown
- round_starttime = time + self.count;
+ this.wait = false;
+ this.cnt = this.count + 1; // init countdown
+ round_starttime = time + this.count;
reset_map(true);
}
- if(self.cnt > 0) // countdown running
+ if (this.cnt > 0) // countdown running
{
- if(self.canRoundStart())
+ if (this.canRoundStart())
{
- if(self.cnt == self.count + 1)
- round_starttime = time + self.count;
- f = self.cnt - 1;
- if(f == 0)
+ if (this.cnt == this.count + 1) round_starttime = time + this.count;
+ int f = this.cnt - 1;
+ if (f == 0)
{
- self.cnt = 0;
- self.round_endtime = (self.round_timelimit) ? time + self.round_timelimit : 0;
- self.nextthink = time;
- if(self.roundStart)
- self.roundStart();
+ this.cnt = 0;
+ this.round_endtime = (this.round_timelimit) ? time + this.round_timelimit : 0;
+ this.nextthink = time;
+ if (this.roundStart) this.roundStart();
return;
}
- self.cnt = self.cnt - 1;
+ this.cnt = this.cnt - 1;
}
else
{
round_handler_Reset(0);
}
- self.nextthink = time + 1; // canRoundStart every second
+ this.nextthink = time + 1; // canRoundStart every second
}
else
{
- if(self.canRoundEnd())
+ if (this.canRoundEnd())
{
// schedule a new round
- self.wait = true;
- self.nextthink = time + self.delay;
+ this.wait = true;
+ this.nextthink = time + this.delay;
}
else
{
- self.nextthink = time; // canRoundEnd every frame
+ this.nextthink = time; // canRoundEnd every frame
}
}
}
void round_handler_Init(float the_delay, float the_count, float the_round_timelimit)
{
- round_handler.delay = (the_delay > 0) ? the_delay : 0;
- round_handler.count = fabs(floor(the_count));
- round_handler.cnt = round_handler.count + 1;
- round_handler.round_timelimit = (the_round_timelimit > 0) ? the_round_timelimit : 0;
+ entity this = round_handler;
+ this.delay = (the_delay > 0) ? the_delay : 0;
+ this.count = fabs(floor(the_count));
+ this.cnt = this.count + 1;
+ this.round_timelimit = (the_round_timelimit > 0) ? the_round_timelimit : 0;
}
// NOTE: this is only needed because if round_handler spawns at time 1
// gamestarttime isn't initialized yet
void round_handler_FirstThink()
{
- round_starttime = max(time, game_starttime) + round_handler.count;
- round_handler.think = round_handler_Think;
- round_handler.nextthink = max(time, game_starttime);
+ SELFPARAM();
+ round_starttime = max(time, game_starttime) + this.count;
+ this.think = round_handler_Think;
+ this.nextthink = max(time, game_starttime);
}
void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func)
{
- if(round_handler)
+ if (round_handler)
{
backtrace("Can't spawn round_handler again!");
return;
}
- round_handler = spawn();
- round_handler.classname = "round_handler";
+ entity this = round_handler = new(round_handler);
- round_handler.think = round_handler_FirstThink;
- round_handler.canRoundStart = canRoundStart_func;
- round_handler.canRoundEnd = canRoundEnd_func;
- round_handler.roundStart = roundStart_func;
- round_handler.wait = false;
+ this.think = round_handler_FirstThink;
+ this.canRoundStart = canRoundStart_func;
+ this.canRoundEnd = canRoundEnd_func;
+ this.roundStart = roundStart_func;
+ this.wait = false;
round_handler_Init(5, 5, 180);
- round_handler.nextthink = time;
+ this.nextthink = time;
}
void round_handler_Reset(float next_think)
{
- round_handler.wait = false;
- if(round_handler.count)
- if(round_handler.cnt < round_handler.count + 1)
- round_handler.cnt = round_handler.count + 1;
- round_handler.nextthink = next_think;
- round_starttime = (next_think) ? (next_think + round_handler.count) : -1;
+ entity this = round_handler;
+ this.wait = false;
+ if (this.count)
+ if (this.cnt < this.count + 1) this.cnt = this.count + 1;
+ this.nextthink = next_think;
+ round_starttime = (next_think) ? (next_think + this.count) : -1;
}
void round_handler_Remove()
remove(round_handler);
round_handler = world;
}
-
{
float i, p, longflags;
- WriteByte(MSG_ENTITY, ENT_CLIENT_TEAMSCORES);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_TEAMSCORES);
WriteByte(MSG_ENTITY, self.team - 1);
longflags = 0;
void TeamScore_Spawn(float t, string name)
{
- entity ts;
- ts = spawn();
- ts.classname = "csqc_score_team";
+ entity ts = new(csqc_score_team);
+ make_pure(ts);
ts.netname = name; // not used yet, FIXME
ts.team = t;
Net_LinkEntity(ts, false, 0, TeamScore_SendEntity);
bool ScoreInfo_SendEntity(entity this, entity to, int sf)
{
float i;
- WriteByte(MSG_ENTITY, ENT_CLIENT_SCORES_INFO);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES_INFO);
WriteInt24_t(MSG_ENTITY, MapInfo_LoadedGametype);
for(i = 0; i < MAX_SCORE; ++i)
{
}
else
{
- scores_initialized = spawn();
- scores_initialized.classname = "ent_client_scoreinfo";
+ scores_initialized = new(ent_client_scoreinfo);
+ make_pure(scores_initialized);
Net_LinkEntity(scores_initialized, false, 0, ScoreInfo_SendEntity);
}
if(teams >= 1)
{
float i, p, longflags;
- WriteByte(MSG_ENTITY, ENT_CLIENT_SCORES);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES);
WriteByte(MSG_ENTITY, num_for_edict(self.owner));
longflags = 0;
void PlayerScore_Attach(entity player)
{
- entity sk;
if(player.scorekeeper)
error("player already has a scorekeeper");
- sk = spawn();
+ entity sk = new(scorekeeper);
+ make_pure(sk);
sk.owner = player;
Net_LinkEntity(sk, false, 0, PlayerScore_SendEntity);
player.scorekeeper = sk;
FOR_EACH_CLIENT(p)
{
+ string s = "";
if(fullstatus)
{
s = GetPlayerScoreString(p, 1);
PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_SCOREBOARD, scores_label[i]), s.(scores[i]));
}
-void PlayerScore_TeamStats(void)
+void PlayerScore_TeamStats()
{
entity sk;
float t, i;
bool SpawnPoint_Send(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_SPAWNPOINT);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_SPAWNPOINT);
WriteByte(MSG_ENTITY, self.team);
WriteShort(MSG_ENTITY, self.origin.x);
{
float send;
- WriteByte(MSG_ENTITY, ENT_CLIENT_SPAWNEVENT);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_SPAWNEVENT);
if(autocvar_g_spawn_alloweffects)
{
{
// show where spawnpoints point at too
makevectors(self.angles);
- entity e;
- e = spawn();
- e.classname = "info_player_foo";
+ entity e = new(info_player_foo);
setorigin(e, self.origin + v_forward * 24);
setsize(e, '-8 -8 -8', '8 8 8');
e.solid = SOLID_TRIGGER;
void spawn_flocker()
{SELFPARAM();
- entity flocker;
-
- flocker = spawn ();
+ entity flocker = new(flocker);
setorigin(flocker, self.origin + '0 0 32');
setmodel (flocker, MDL_FLOCKER);
setsize (flocker, '-3 -3 -3', '3 3 3');
flocker.flock_id = self.flock_id;
- flocker.classname = "flocker";
flocker.owner = self;
flocker.think = flocker_think;
flocker.nextthink = time + random() * 5;
self.think = flockerspawn_think;
self.nextthink = time + 0.25;
- self.enemy = spawn();
+ self.enemy = new(FLock Hunter);
setmodel(self.enemy, MDL_FLOCKER);
setorigin(self.enemy,self.origin + '0 0 768' + (randomvec() * 128));
- self.enemy.classname = "FLock Hunter";
self.enemy.scale = 3;
self.enemy.effects = EF_LOWPRECISION;
self.enemy.movetype = MOVETYPE_BOUNCEMISSILE;
#include "../common/constants.qh"
#include "../common/deathtypes/all.qh"
+#include "../common/debug.qh"
#include "../common/mapinfo.qh"
#include "../common/util.qh"
.float lastground;
.int state;
-void CreatureFrame (void)
+void CreatureFrame ()
{SELFPARAM();
float dm;
if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS))
{
if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
- GlobalSound(globalsound_metalstep, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+ GlobalSound(GS_STEP_METAL, CH_PLAYER, VOICETYPE_PLAYERSOUND);
else
- GlobalSound(globalsound_step, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+ GlobalSound(GS_STEP, CH_PLAYER, VOICETYPE_PLAYERSOUND);
}
}
}
}
}
-void WarpZone_PostInitialize_Callback(void)
+void WarpZone_PostInitialize_Callback()
{
// create waypoint links for warpzones
entity e;
#include "../lib/warpzone/util_server.qh"
#endif
+REGISTER_NET_LINKED(ENT_CLIENT_ITEM)
+
#ifdef CSQC
void ItemDraw(entity self)
{
self.drawmask = MASK_NORMAL;
}
-void ItemRead(float _IsNew)
-{SELFPARAM();
+NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
+{
int sf = ReadByte();
if(sf & ISF_LOCATION)
if(self.ItemStatus & ITS_ANIMATE2)
self.move_avelocity = '0 -90 0';
}
+ return true;
}
#endif
else
sf &= ~ISF_DROP;
- WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_ITEM);
WriteByte(MSG_ENTITY, sf);
//WriteByte(MSG_ENTITY, self.cnt);
if(sf & ISF_SIZE)
{
- WriteByte(MSG_ENTITY, ((self.flags & FL_POWERUP) || self.health || self.armorvalue));
+ Pickup p = this.itemdef;
+ WriteByte(MSG_ENTITY, p.instanceOfPowerup || p.instanceOfHealth || p.instanceOfArmor);
}
if(sf & ISF_STATUS)
item.SendFlags |= ISF_LOCATION;
}
-float have_pickup_item(void)
-{SELFPARAM();
- if(self.flags & FL_POWERUP)
+bool have_pickup_item(entity this)
+{
+ if(this.itemdef.instanceOfPowerup)
{
if(autocvar_g_powerups > 0)
return true;
if(autocvar_g_pickup_items == 0)
return false;
if(g_weaponarena)
- if(self.weapons || (self.items & IT_AMMO)) // no item or ammo pickups in weaponarena
+ if(this.weapons || (this.items & IT_AMMO)) // no item or ammo pickups in weaponarena
return false;
}
return true;
e.spawnshieldtime = 1;
e.ItemStatus &= ~ITS_AVAILABLE;
}
- else if((e.flags & FL_WEAPON) && !(e.flags & FL_NO_WEAPON_STAY) && g_weapon_stay)
+ else {
+ entity def = e.itemdef;
+ bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.weapons & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons
+ || e.team // weapon stay isn't supported for teamed weapons
+ ;
+ if(def.instanceOfWeaponPickup && !nostay && g_weapon_stay)
{
// make the item translucent and not touchable
e.model = e.mdl;
e.glowmod = e.colormod;
e.spawnshieldtime = 1;
e.ItemStatus &= ~ITS_AVAILABLE;
- }
+ }}
if (e.items & ITEM_Strength.m_itemid || e.items & ITEM_Shield.m_itemid)
e.ItemStatus |= ITS_POWERUP;
void Item_ItemsTime_SetTime(entity e, float t);
void Item_ItemsTime_SetTimesForAllPlayers();
-void Item_Respawn (void)
+void Item_Respawn ()
{SELFPARAM();
Item_Show(self, 1);
// this is ugly...
Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
}
-void Item_RespawnCountdown (void)
+void Item_RespawnCountdown ()
{SELFPARAM();
if(self.count >= ITEM_RESPAWN_TICKS)
{
pickedup |= Item_GiveAmmoTo(item, player, health, item.max_health, ITEM_MODE_HEALTH);
pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue, ITEM_MODE_ARMOR);
- if (item.flags & FL_WEAPON)
+ if (item.itemdef.instanceOfWeaponPickup)
{
WepSet it;
it = item.weapons;
return 0;
// crude hack to enforce switching weapons
- if(g_cts && (item.flags & FL_WEAPON))
+ if(g_cts && item.itemdef.instanceOfWeaponPickup)
{
W_SwitchWeapon_Force(player, item.weapon);
return 1;
return 1;
}
-void Item_Touch (void)
-{SELFPARAM();
- entity e, head;
+void Item_Touch()
+{
+ SELFPARAM();
// remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
- if(self.classname == "droppedweapon")
+ if (this.classname == "droppedweapon")
{
if (ITEM_TOUCH_NEEDKILL())
{
- remove(self);
+ remove(this);
return;
}
}
if(!(other.flags & FL_PICKUPITEMS)
|| other.frozen
|| other.deadflag
- || (self.solid != SOLID_TRIGGER)
- || (self.owner == other)
- || (time < self.item_spawnshieldtime)
- ) { return;}
+ || (this.solid != SOLID_TRIGGER)
+ || (this.owner == other)
+ || (time < this.item_spawnshieldtime)
+ ) { return; }
- switch(MUTATOR_CALLHOOK(ItemTouch, self, other))
+ switch (MUTATOR_CALLHOOK(ItemTouch, this, other))
{
case MUT_ITEMTOUCH_RETURN: { return; }
case MUT_ITEMTOUCH_PICKUP: { goto pickup; }
}
- if (self.classname == "droppedweapon")
+ if (this.classname == "droppedweapon")
{
- self.strength_finished = max(0, self.strength_finished - time);
- self.invincible_finished = max(0, self.invincible_finished - time);
- self.superweapons_finished = max(0, self.superweapons_finished - time);
+ this.strength_finished = max(0, this.strength_finished - time);
+ this.invincible_finished = max(0, this.invincible_finished - time);
+ this.superweapons_finished = max(0, this.superweapons_finished - time);
}
- entity it = self.itemdef;
- bool gave = (it && it.instanceOfPickup) ? ITEM_HANDLE(Pickup, it, self, other) : Item_GiveTo(self, other);
+ entity it = this.itemdef;
+ bool gave = ITEM_HANDLE(Pickup, it, this, other);
if (!gave)
{
- if (self.classname == "droppedweapon")
+ if (this.classname == "droppedweapon")
{
// undo what we did above
- self.strength_finished += time;
- self.invincible_finished += time;
- self.superweapons_finished += time;
+ this.strength_finished += time;
+ this.invincible_finished += time;
+ this.superweapons_finished += time;
}
return;
}
other.last_pickup = time;
- Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
- _sound (other, CH_TRIGGER, (self.item_pickupsound ? self.item_pickupsound : self.item_pickupsound_ent.sound_str()), VOL_BASE, ATTEN_NORM);
+ Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
+ _sound (other, CH_TRIGGER, (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM);
- if (self.classname == "droppedweapon")
- remove (self);
- else if (self.spawnshieldtime)
+ if (this.classname == "droppedweapon")
+ remove (this);
+ else if (this.spawnshieldtime)
{
- if(self.team)
+ entity e;
+ if(this.team)
{
RandomSelection_Init();
- for(head = world; (head = findfloat(head, team, self.team)); )
+ for(entity head = world; (head = findfloat(head, team, this.team)); )
{
if(head.flags & FL_ITEM)
if(head.classname != "item_flag_team" && head.classname != "item_key_team")
}
else
- e = self;
+ e = this;
Item_ScheduleRespawn(e);
}
}
-void Item_Reset()
-{SELFPARAM();
- Item_Show(self, !self.state);
- setorigin (self, self.origin);
+void Item_Reset(entity this)
+{
+ Item_Show(this, !this.state);
+ setorigin(this, this.origin);
- if(self.classname != "droppedweapon")
+ if (this.classname != "droppedweapon")
{
- self.think = Item_Think;
- self.nextthink = time;
+ this.think = Item_Think;
+ this.nextthink = time;
- if(self.waypointsprite_attached)
- WaypointSprite_Kill(self.waypointsprite_attached);
+ if (this.waypointsprite_attached)
+ WaypointSprite_Kill(this.waypointsprite_attached);
- if((self.flags & FL_POWERUP) || (self.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
- Item_ScheduleInitialRespawn(self);
+ if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
+ Item_ScheduleInitialRespawn(this);
}
}
+void Item_Reset_self() { SELFPARAM(); Item_Reset(this); }
void Item_FindTeam()
{SELFPARAM();
head.effects &= ~EF_NODRAW;
}
- Item_Reset();
+ Item_Reset(self);
}
}
// Savage: used for item garbage-collection
// TODO: perhaps nice special effect?
-void RemoveItem(void)
+void RemoveItem()
{SELFPARAM();
if(wasfreed(self) || !self) { return; }
Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
RemoveItem();
}
-void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
-{SELFPARAM();
- startitem_failed = false;
-
- if(self.model == "")
- self.model = itemmodel;
+void _StartItem(entity this, entity def, float defaultrespawntime, float defaultrespawntimejitter)
+{
+ string itemname = def.m_name;
+ Model itemmodel = def.m_model;
+ Sound pickupsound = def.m_sound;
+ float(entity player, entity item) pickupevalfunc = def.m_pickupevalfunc;
+ float pickupbasevalue = def.m_botvalue;
+ int itemflags = def.m_itemflags;
- if(self.model == "")
- {
- error(strcat("^1Tried to spawn ", itemname, " with no model!\n"));
- return;
- }
+ startitem_failed = false;
- if(self.item_pickupsound == "")
- self.item_pickupsound = pickupsound;
+ this.item_model_ent = itemmodel;
+ this.item_pickupsound_ent = pickupsound;
- if(!self.respawntime) // both need to be set
+ if(!this.respawntime) // both need to be set
{
- self.respawntime = defaultrespawntime;
- self.respawntimejitter = defaultrespawntimejitter;
+ this.respawntime = defaultrespawntime;
+ this.respawntimejitter = defaultrespawntimejitter;
}
- self.items = itemid;
- self.weapon = weaponid;
+ int itemid = def.m_itemid;
+ this.items = itemid;
+ int weaponid = def.instanceOfWeaponPickup ? def.m_weapon.m_id : 0;
+ this.weapon = weaponid;
- if(!self.fade_end)
+ if(!this.fade_end)
{
- self.fade_start = autocvar_g_items_mindist;
- self.fade_end = autocvar_g_items_maxdist;
+ this.fade_start = autocvar_g_items_mindist;
+ this.fade_end = autocvar_g_items_maxdist;
}
if(weaponid)
- self.weapons = WepSet_FromWeapon(weaponid);
+ this.weapons = WepSet_FromWeapon(weaponid);
- self.flags = FL_ITEM | itemflags;
+ this.flags = FL_ITEM | itemflags;
- if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item
+ if(MUTATOR_CALLHOOK(FilterItem, this)) // error means we do not want the item
{
startitem_failed = true;
- remove(self);
+ remove(this);
return;
}
// is it a dropped weapon?
- if (self.classname == "droppedweapon")
+ if (this.classname == "droppedweapon")
{
- self.reset = SUB_Remove;
+ this.reset = SUB_Remove;
// it's a dropped weapon
- self.movetype = MOVETYPE_TOSS;
+ this.movetype = MOVETYPE_TOSS;
// Savage: remove thrown items after a certain period of time ("garbage collection")
- self.think = RemoveItem;
- self.nextthink = time + 20;
+ this.think = RemoveItem;
+ this.nextthink = time + 20;
- self.takedamage = DAMAGE_YES;
- self.event_damage = Item_Damage;
+ this.takedamage = DAMAGE_YES;
+ this.event_damage = Item_Damage;
- if(self.strength_finished || self.invincible_finished || self.superweapons_finished)
- /*
- if(self.items == 0)
- if(!(self.weapons & ~WEPSET_SUPERWEAPONS)) // only superweapons
- if(self.ammo_nails == 0)
- if(self.ammo_cells == 0)
- if(self.ammo_rockets == 0)
- if(self.ammo_shells == 0)
- if(self.ammo_fuel == 0)
- if(self.health == 0)
- if(self.armorvalue == 0)
- */
+ if(this.strength_finished || this.invincible_finished || this.superweapons_finished)
{
// if item is worthless after a timer, have it expire then
- self.nextthink = max(self.strength_finished, self.invincible_finished, self.superweapons_finished);
+ this.nextthink = max(this.strength_finished, this.invincible_finished, this.superweapons_finished);
}
// don't drop if in a NODROP zone (such as lava)
- traceline(self.origin, self.origin, MOVE_NORMAL, self);
+ traceline(this.origin, this.origin, MOVE_NORMAL, this);
if (trace_dpstartcontents & DPCONTENTS_NODROP)
{
startitem_failed = true;
- remove(self);
+ remove(this);
return;
}
}
else
{
- if(!have_pickup_item())
+ if(!have_pickup_item(this))
{
startitem_failed = true;
- remove (self);
+ remove (this);
return;
}
- if(self.angles != '0 0 0')
- self.SendFlags |= ISF_ANGLES;
+ if(this.angles != '0 0 0')
+ this.SendFlags |= ISF_ANGLES;
- self.reset = Item_Reset;
+ this.reset = Item_Reset_self;
// it's a level item
- if(self.spawnflags & 1)
- self.noalign = 1;
- if (self.noalign > 0)
- self.movetype = MOVETYPE_NONE;
+ if(this.spawnflags & 1)
+ this.noalign = 1;
+ if (this.noalign > 0)
+ this.movetype = MOVETYPE_NONE;
else
- self.movetype = MOVETYPE_TOSS;
+ this.movetype = MOVETYPE_TOSS;
// do item filtering according to game mode and other things
- if (self.noalign <= 0)
+ if (this.noalign <= 0)
{
// first nudge it off the floor a little bit to avoid math errors
- setorigin(self, self.origin + '0 0 1');
+ setorigin(this, this.origin + '0 0 1');
// set item size before we spawn a spawnfunc_waypoint
- if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
- setsize (self, '-16 -16 0', '16 16 48');
- else
- setsize (self, '-16 -16 0', '16 16 32');
- self.SendFlags |= ISF_SIZE;
+ setsize(this, def.m_mins, def.m_maxs);
+ this.SendFlags |= ISF_SIZE;
// note droptofloor returns false if stuck/or would fall too far
- if(!self.noalign)
- droptofloor();
- waypoint_spawnforitem(self);
+ if (!this.noalign)
+ WITH(entity, self, this, droptofloor());
+ waypoint_spawnforitem(this);
}
/*
* can't do it that way, as it would break maps
* TODO make a target_give like entity another way, that perhaps has
* the weapon name in a key
- if(self.targetname)
+ if(this.targetname)
{
// target_give not yet supported; maybe later
- print("removed targeted ", self.classname, "\n");
+ print("removed targeted ", this.classname, "\n");
startitem_failed = true;
- remove (self);
+ remove (this);
return;
}
*/
if(autocvar_spawn_debug >= 2)
{
- entity otheritem;
- for(otheritem = findradius(self.origin, 3); otheritem; otheritem = otheritem.chain)
+ for(entity otheritem = findradius(this.origin, 3); otheritem; otheritem = otheritem.chain)
{
// why not flags & fl_item?
if(otheritem.is_item)
{
- LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(self.origin));
+ LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
LOG_TRACE(" vs ", otheritem.netname, vtos(otheritem.origin), "\n");
error("Mapper sucks.");
}
}
- self.is_item = true;
+ this.is_item = true;
}
weaponsInMap |= WepSet_FromWeapon(weaponid);
- precache_model (self.model);
- precache_sound (self.item_pickupsound);
+ precache_model(this.model);
+ precache_sound(this.item_pickupsound);
- if((itemflags & (FL_POWERUP | FL_WEAPON)) || (itemid & (IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2)))
- self.target = "###item###"; // for finding the nearest item using find()
+ if ( def.instanceOfPowerup
+ || def.instanceOfWeaponPickup
+ || (def.instanceOfHealth && def != ITEM_HealthSmall)
+ || (def.instanceOfArmor && def != ITEM_ArmorSmall)
+ || (itemid & (IT_KEY1 | IT_KEY2))
+ ) this.target = "###item###"; // for finding the nearest item using find()
- Item_ItemsTime_SetTime(self, 0);
+ Item_ItemsTime_SetTime(this, 0);
}
- self.bot_pickup = true;
- self.bot_pickupevalfunc = pickupevalfunc;
- self.bot_pickupbasevalue = pickupbasevalue;
- self.mdl = self.model;
- self.netname = itemname;
- self.touch = Item_Touch;
- setmodel(self, MDL_Null); // precision set below
- //self.effects |= EF_LOWPRECISION;
-
- if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
- {
- self.pos1 = '-16 -16 0';
- self.pos2 = '16 16 48';
- }
- else
- {
- self.pos1 = '-16 -16 0';
- self.pos2 = '16 16 32';
- }
- setsize (self, self.pos1, self.pos2);
+ this.bot_pickup = true;
+ this.bot_pickupevalfunc = pickupevalfunc;
+ this.bot_pickupbasevalue = pickupbasevalue;
+ this.mdl = this.model ? this.model : strzone(this.item_model_ent.model_str());
+ this.netname = itemname;
+ this.touch = Item_Touch;
+ setmodel(this, MDL_Null); // precision set below
+ //this.effects |= EF_LOWPRECISION;
- self.SendFlags |= ISF_SIZE;
+ setsize (this, this.pos1 = def.m_mins, this.pos2 = def.m_maxs);
- if(!(self.spawnflags & 1024))
- {
- if(itemflags & FL_POWERUP)
- self.ItemStatus |= ITS_ANIMATE1;
+ this.SendFlags |= ISF_SIZE;
- if(self.armorvalue || self.health)
- self.ItemStatus |= ITS_ANIMATE2;
+ if (!(this.spawnflags & 1024)) {
+ if(def.instanceOfPowerup)
+ this.ItemStatus |= ITS_ANIMATE1;
+
+ if(this.armorvalue || this.health)
+ this.ItemStatus |= ITS_ANIMATE2;
}
- if(itemflags & FL_WEAPON)
+ if(def.instanceOfWeaponPickup)
{
- if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely
- self.colormap = 1024; // color shirt=0 pants=0 grey
+ if (this.classname != "droppedweapon") // if dropped, colormap is already set up nicely
+ this.colormap = 1024; // color shirt=0 pants=0 grey
else
- self.gravity = 1;
-
- if(!(self.spawnflags & 1024))
- self.ItemStatus |= ITS_ANIMATE1;
- self.ItemStatus |= ISF_COLORMAP;
+ this.gravity = 1;
+ if (!(this.spawnflags & 1024))
+ this.ItemStatus |= ITS_ANIMATE1;
+ this.ItemStatus |= ISF_COLORMAP;
}
- self.state = 0;
- if(self.team) // broken, no idea why.
+ this.state = 0;
+ if(this.team) // broken, no idea why.
{
- if(!self.cnt)
- self.cnt = 1; // item probability weight
+ if(!this.cnt)
+ this.cnt = 1; // item probability weight
- self.effects |= EF_NODRAW; // marker for item team search
- InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET);
+ this.effects |= EF_NODRAW; // marker for item team search
+ InitializeEntity(this, Item_FindTeam, INITPRIO_FINDTARGET);
}
else
- Item_Reset();
+ Item_Reset(this);
- Net_LinkEntity(self, !((itemflags & FL_POWERUP) || self.health || self.armorvalue), 0, ItemSend);
+ Net_LinkEntity(this, !(def.instanceOfPowerup || def.instanceOfHealth || def.instanceOfArmor), 0, ItemSend);
// call this hook after everything else has been done
- if(MUTATOR_CALLHOOK(Item_Spawn, self))
+ if (MUTATOR_CALLHOOK(Item_Spawn, this))
{
startitem_failed = true;
- remove(self);
+ remove(this);
return;
}
}
-void StartItemA (entity a)
-{SELFPARAM();
- self.itemdef = a;
- StartItem(strzone(a.m_model.model_str()), a.m_sound, a.m_respawntime(), a.m_respawntimejitter(), a.m_name, a.m_itemid, 0, a.m_itemflags, a.m_pickupevalfunc, a.m_botvalue);
+void StartItem(entity this, GameItem def)
+{
+ _StartItem(
+ this,
+ this.itemdef = def,
+ def.m_respawntime(), // defaultrespawntime
+ def.m_respawntimejitter() // defaultrespawntimejitter
+ );
}
spawnfunc(item_rockets)
self.ammo_rockets = g_pickup_rockets;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItemA (ITEM_Rockets);
+ StartItem(this, ITEM_Rockets);
}
spawnfunc(item_bullets)
self.ammo_nails = g_pickup_nails;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItemA (ITEM_Bullets);
+ StartItem(this, ITEM_Bullets);
}
spawnfunc(item_cells)
self.ammo_cells = g_pickup_cells;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItemA (ITEM_Cells);
+ StartItem(this, ITEM_Cells);
}
spawnfunc(item_plasma)
self.ammo_plasma = g_pickup_plasma;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItemA (ITEM_Plasma);
+ StartItem(this, ITEM_Plasma);
}
spawnfunc(item_shells)
self.ammo_shells = g_pickup_shells;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItemA (ITEM_Shells);
+ StartItem(this, ITEM_Shells);
}
spawnfunc(item_armor_small)
self.max_armorvalue = g_pickup_armorsmall_max;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_armorsmall_anyway;
- StartItemA (ITEM_ArmorSmall);
+ StartItem(this, ITEM_ArmorSmall);
}
spawnfunc(item_armor_medium)
self.max_armorvalue = g_pickup_armormedium_max;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_armormedium_anyway;
- StartItemA (ITEM_ArmorMedium);
+ StartItem(this, ITEM_ArmorMedium);
}
spawnfunc(item_armor_big)
self.max_armorvalue = g_pickup_armorbig_max;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_armorbig_anyway;
- StartItemA (ITEM_ArmorLarge);
+ StartItem(this, ITEM_ArmorLarge);
}
spawnfunc(item_armor_large)
self.max_armorvalue = g_pickup_armorlarge_max;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_armorlarge_anyway;
- StartItemA (ITEM_ArmorMega);
+ StartItem(this, ITEM_ArmorMega);
}
spawnfunc(item_health_small)
self.health = g_pickup_healthsmall;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_healthsmall_anyway;
- StartItemA (ITEM_HealthSmall);
+ StartItem(this, ITEM_HealthSmall);
}
spawnfunc(item_health_medium)
self.health = g_pickup_healthmedium;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_healthmedium_anyway;
- StartItemA (ITEM_HealthMedium);
+ StartItem(this, ITEM_HealthMedium);
}
spawnfunc(item_health_large)
self.health = g_pickup_healthlarge;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_healthlarge_anyway;
- StartItemA (ITEM_HealthLarge);
+ StartItem(this, ITEM_HealthLarge);
}
spawnfunc(item_health_mega)
self.health = g_pickup_healthmega;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_healthmega_anyway;
- StartItemA (ITEM_HealthMega);
+ StartItem(this, ITEM_HealthMega);
}
// support old misnamed entities
{
if(!self.strength_finished)
self.strength_finished = autocvar_g_balance_powerup_strength_time;
- StartItemA (ITEM_Strength);
+ StartItem(this, ITEM_Strength);
}
spawnfunc(item_invincible)
{
if(!self.invincible_finished)
self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
- StartItemA (ITEM_Shield);
+ StartItem(this, ITEM_Shield);
}
// compatibility:
self.ammo_fuel = g_pickup_fuel;
if(!self.pickup_anyway)
self.pickup_anyway = g_pickup_ammo_anyway;
- StartItemA (ITEM_JetpackFuel);
+ StartItem(this, ITEM_JetpackFuel);
}
spawnfunc(item_fuel_regen)
spawnfunc_item_fuel(this);
return;
}
- StartItemA (ITEM_JetpackRegen);
+ StartItem(this, ITEM_JetpackRegen);
}
spawnfunc(item_jetpack)
spawnfunc_item_fuel(this);
return;
}
- StartItemA (ITEM_Jetpack);
+ StartItem(this, ITEM_Jetpack);
}
float GiveWeapon(entity e, float wpn, float op, float val)
return (v0 != v1);
}
-float GiveBit(entity e, .float fld, float bit, float op, float val)
-{
- float v0, v1;
- v0 = (e.(fld) & bit);
- switch(op)
- {
- case OP_SET:
- if(val > 0)
- e.(fld) |= bit;
- else
- e.(fld) &= ~bit;
- break;
- case OP_MIN:
- case OP_PLUS:
- if(val > 0)
- e.(fld) |= bit;
- break;
- case OP_MAX:
- if(val <= 0)
- e.(fld) &= ~bit;
- break;
- case OP_MINUS:
- if(val > 0)
- e.(fld) &= ~bit;
- break;
- }
- v1 = (e.(fld) & bit);
- return (v0 != v1);
-}
-
-float GiveValue(entity e, .float fld, float op, float val)
-{
- float v0, v1;
- v0 = e.(fld);
- switch(op)
- {
- case OP_SET:
- e.(fld) = val;
- break;
- case OP_MIN:
- e.(fld) = max(e.(fld), val); // min 100 cells = at least 100 cells
- break;
- case OP_MAX:
- e.(fld) = min(e.(fld), val);
- break;
- case OP_PLUS:
- e.(fld) += val;
- break;
- case OP_MINUS:
- e.(fld) -= val;
- break;
- }
- v1 = e.(fld);
- return (v0 != v1);
-}
-
void GiveSound(entity e, float v0, float v1, float t, string snd_incr, string snd_decr)
{
if(v1 == v0)
.float fade_end;
#ifdef SVQC
-void StartItemA (entity a);
+void StartItem(entity this, entity a);
#endif
#ifdef CSQC
void ItemDraw(entity this);
void ItemDrawSimple(entity this);
-void ItemRead(float _IsNew);
-
#endif
#ifdef SVQC
spawnfunc(item_strength);
bool ItemSend(entity this, entity to, int sf);
-float have_pickup_item(void);
+bool have_pickup_item(entity this);
const float ITEM_RESPAWN_TICKS = 10;
void Item_Show (entity e, float mode);
-void Item_Respawn (void);
+void Item_Respawn ();
-void Item_RespawnCountdown (void);
+void Item_RespawnCountdown ();
void Item_ScheduleRespawnIn(entity e, float t);
void Item_ScheduleRespawn(entity e);
float Item_GiveTo(entity item, entity player);
-void Item_Touch (void);
+void Item_Touch();
-void Item_Reset();
+void Item_Reset(entity this);
void Item_FindTeam();
// Savage: used for item garbage-collection
.float is_item;
.entity itemdef;
-void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue);
-
+void _StartItem(entity this, entity def, float defaultrespawntime, float defaultrespawntimejitter);
-void target_items_use (void);
-const float OP_SET = 0;
-const float OP_MIN = 1;
-const float OP_MAX = 2;
-const float OP_PLUS = 3;
-const float OP_MINUS = 4;
+void target_items_use ();
float GiveWeapon(entity e, float wpn, float op, float val);
return versionmsg;
}
-string getwelcomemessage(void)
+string getwelcomemessage()
{
string s, modifications, motd;
modifications = strcat(modifications, ", No start weapons");
if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
modifications = strcat(modifications, ", Low gravity");
- if(g_cloaked && !g_cts)
- modifications = strcat(modifications, ", Cloaked");
if(g_weapon_stay && !g_cts)
modifications = strcat(modifications, ", Weapons stay");
if(g_jetpack)
string GetClientVersionMessage();
-string getwelcomemessage(void);
+string getwelcomemessage();
void SetPlayerColors(entity pl, float _color);
bool accuracy_send(entity this, entity to, int sf)
{
- WriteByte(MSG_ENTITY, ENT_CLIENT_ACCURACY);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_ACCURACY);
entity a = this.owner;
if (IS_SPEC(a)) a = a.enemy;
void accuracy_init(entity e)
{
entity a = e.accuracy = new(accuracy);
+ make_pure(a);
a.owner = e;
a.drawonlytoclient = e;
Net_LinkEntity(a, false, 0, accuracy_send);
self.realowner = attacker;
}
+ MUTATOR_CALLHOOK(PrepareExplosionByDamage, self, attacker);
+
// do not explode NOW but in the NEXT FRAME!
// because recursive calls to RadiusDamage are not allowed
self.nextthink = time;
if(self.gravity != 0)
sf |= 0x10;
- WriteByte(MSG_ENTITY, ENT_CLIENT_PROJECTILE);
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_PROJECTILE);
WriteByte(MSG_ENTITY, sf);
if(sf & 1)
void Send_WeaponComplain(entity e, float wpn, float type)
{
msg_entity = e;
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN);
+ WriteHeader(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN);
WriteByte(MSG_ONE, wpn);
WriteByte(MSG_ONE, type);
}
}
// previously used if exists and has ammo, (second) best otherwise
-void W_LastWeapon(void)
+void W_LastWeapon()
{SELFPARAM();
if(client_hasweapon(self, self.cnt, true, false))
W_SwitchWeapon(self.cnt);
void W_PreviousWeapon(float list);
// previously used if exists and has ammo, (second) best otherwise
-void W_LastWeapon(void);
+void W_LastWeapon();
#endif
return substring(out, 1, -1);
}
-void weapon_defaultspawnfunc(float wpn)
-{SELFPARAM();
- entity e;
- float t;
- string s;
- float i, j;
- int f;
-
- if(self.classname != "droppedweapon" && self.classname != "replacedweapon")
+void weapon_defaultspawnfunc(entity this, Weapon e)
+{
+ int wpn = e.m_id;
+ if (this.classname != "droppedweapon" && this.classname != "replacedweapon")
{
- e = get_weaponinfo(wpn);
-
- if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
+ if (e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
{
objerror("Attempted to spawn a mutator-blocked weapon rejected");
startitem_failed = true;
return;
}
- s = W_Apply_Weaponreplace(e.netname);
- MUTATOR_CALLHOOK(SetWeaponreplace, self, e, s);
+ string s = W_Apply_Weaponreplace(e.netname);
+ MUTATOR_CALLHOOK(SetWeaponreplace, this, e, s);
s = ret_string;
- if(s == "")
+ if (s == "")
{
- remove(self);
+ remove(this);
startitem_failed = true;
return;
}
- t = tokenize_console(s);
- if(t >= 2)
+ int t = tokenize_console(s);
+ if (t >= 2)
{
- self.team = --internalteam;
- for(i = 1; i < t; ++i)
+ this.team = --internalteam;
+ for (int i = 1; i < t; ++i)
{
s = argv(i);
- for(j = WEP_FIRST; j <= WEP_LAST; ++j)
+ int j;
+ for (j = WEP_FIRST; j <= WEP_LAST; ++j)
{
e = get_weaponinfo(j);
- if(e.netname == s)
+ if (e.netname == s)
{
- setself(spawn());
- copyentity(this, self);
- self.classname = "replacedweapon";
- weapon_defaultspawnfunc(j);
+ entity replacement = spawn();
+ copyentity(this, replacement);
+ replacement.classname = "replacedweapon";
+ weapon_defaultspawnfunc(replacement, e);
break;
}
}
- if(j > WEP_LAST)
+ if (j > WEP_LAST)
{
LOG_INFO("The weapon replace list for ", this.classname, " contains an unknown weapon ", s, ". Skipped.\n");
}
}
- setself(this);
}
- if(t >= 1) // always the case!
+ if (t >= 1) // always the case!
{
s = argv(0);
wpn = 0;
- for(j = WEP_FIRST; j <= WEP_LAST; ++j)
+ int j;
+ for (j = WEP_FIRST; j <= WEP_LAST; ++j)
{
e = get_weaponinfo(j);
- if(e.netname == s)
+ if (e.netname == s)
{
wpn = j;
break;
}
}
- if(j > WEP_LAST)
+ if (j > WEP_LAST)
{
- LOG_INFO("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n");
+ LOG_INFO("The weapon replace list for ", this.classname, " contains an unknown weapon ", s, ". Skipped.\n");
}
}
- if(wpn == 0)
+ if (wpn == 0)
{
- remove(self);
+ remove(this);
startitem_failed = true;
return;
}
e = get_weaponinfo(wpn);
- if(!self.respawntime)
+ if (!this.respawntime)
{
- if(e.weapons & WEPSET_SUPERWEAPONS)
+ if (e.weapons & WEPSET_SUPERWEAPONS)
{
- self.respawntime = g_pickup_respawntime_superweapon;
- self.respawntimejitter = g_pickup_respawntimejitter_superweapon;
+ this.respawntime = g_pickup_respawntime_superweapon;
+ this.respawntimejitter = g_pickup_respawntimejitter_superweapon;
}
else
{
- self.respawntime = g_pickup_respawntime_weapon;
- self.respawntimejitter = g_pickup_respawntimejitter_weapon;
+ this.respawntime = g_pickup_respawntime_weapon;
+ this.respawntimejitter = g_pickup_respawntimejitter_weapon;
}
}
- if(e.weapons & WEPSET_SUPERWEAPONS)
- if(!self.superweapons_finished)
- self.superweapons_finished = autocvar_g_balance_superweapons_time;
+ if (e.weapons & WEPSET_SUPERWEAPONS)
+ if (!this.superweapons_finished)
+ this.superweapons_finished = autocvar_g_balance_superweapons_time;
// if we don't already have ammo, give us some ammo
- if(!self.(e.ammo_field))
+ if (!this.(e.ammo_field))
{
- switch(e.ammo_field)
+ switch (e.ammo_field)
{
- case ammo_shells: self.ammo_shells = cvar("g_pickup_shells_weapon"); break;
- case ammo_nails: self.ammo_nails = cvar("g_pickup_nails_weapon"); break;
- case ammo_rockets: self.ammo_rockets = cvar("g_pickup_rockets_weapon"); break;
- case ammo_cells: self.ammo_cells = cvar("g_pickup_cells_weapon"); break;
- case ammo_plasma: self.ammo_plasma = cvar("g_pickup_plasma_weapon"); break;
- case ammo_fuel: self.ammo_fuel = cvar("g_pickup_fuel_weapon"); break;
+ case ammo_shells: this.ammo_shells = cvar("g_pickup_shells_weapon"); break;
+ case ammo_nails: this.ammo_nails = cvar("g_pickup_nails_weapon"); break;
+ case ammo_rockets: this.ammo_rockets = cvar("g_pickup_rockets_weapon"); break;
+ case ammo_cells: this.ammo_cells = cvar("g_pickup_cells_weapon"); break;
+ case ammo_plasma: this.ammo_plasma = cvar("g_pickup_plasma_weapon"); break;
+ case ammo_fuel: this.ammo_fuel = cvar("g_pickup_fuel_weapon"); break;
}
}
#if 0 // WEAPONTODO
- if(e.items)
+ if (e.items)
{
- for(i = 0, j = 1; i < 24; ++i, j *= 2)
+ for (int i = 0, j = 1; i < 24; ++i, j <<= 1)
{
- if(e.items & j)
+ if (e.items & j)
{
ammotype = Item_CounterField(j);
- if(!self.ammotype)
- self.ammotype = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon"));
+ if (!this.ammotype)
+ this.ammotype = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon"));
}
}
}
#endif
// pickup anyway
- if(g_pickup_weapons_anyway)
- self.pickup_anyway = true;
-
- f = FL_WEAPON;
-
- // no weapon-stay on superweapons
- if(e.weapons & WEPSET_SUPERWEAPONS)
- f |= FL_NO_WEAPON_STAY;
-
- // weapon stay isn't supported for teamed weapons
- if(self.team)
- f |= FL_NO_WEAPON_STAY;
-
- StartItem(strzone(e.m_model.model_str()), string_null, self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue);
- self.item_pickupsound_ent = SND_WEAPONPICKUP;
+ if (g_pickup_weapons_anyway)
+ this.pickup_anyway = true;
+
+ GameItem def = e.m_pickup;
+ _StartItem(
+ this,
+ this.itemdef = def,
+ this.respawntime, // defaultrespawntime
+ this.respawntimejitter // defaultrespawntimejitter
+ );
#if 0 // WEAPONTODO
- if (self.modelindex) { // don't precache if self was removed
- Weapon w = get_weaponinfo(e.weapon);
- w.wr_init(w);
+ if (this.modelindex) { // don't precache if this was removed
+ e.wr_init(e);
}
#endif
}
string W_Apply_Weaponreplace(string in);
-void weapon_defaultspawnfunc(float wpn);
+void weapon_defaultspawnfunc(entity this, Weapon e);
#endif
{SELFPARAM();
float thisammo, i;
string s;
- var .int ammotype = (get_weaponinfo(wpn)).ammo_field;
+ Weapon info = get_weaponinfo(wpn);
+ var .int ammotype = info.ammo_field;
- entity wep = spawn();
+ entity wep = new(droppedweapon);
setorigin(wep, org);
- wep.classname = "droppedweapon";
wep.velocity = velo;
wep.owner = wep.enemy = own;
wep.flags |= FL_TOSSED;
}
}
- WITH(entity, self, wep, weapon_defaultspawnfunc(wpn));
+ weapon_defaultspawnfunc(wep, info);
if(startitem_failed)
return string_null;
wep.glowmod = own.weaponentity_glowmod;
return;
if(!autocvar_g_weapon_throwable)
return;
- if(self.weaponentity.state != WS_READY)
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+ if(self.(weaponentity).state != WS_READY)
return;
if(!W_IsWeaponThrowable(w))
return;
W_HitPlotAnalysis(ent, v_forward, v_right, v_up);
- if(ent.weaponentity.movedir.x > 0)
- vecs = ent.weaponentity.movedir;
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+ vector md = ent.(weaponentity).movedir;
+ if(md.x > 0)
+ vecs = md;
else
vecs = '0 0 0';
float total_damage = 0;
if(tracereffects & EF_RED)
- fireBullet_trace_callback_eff = particleeffectnum(EFFECT_RIFLE);
+ fireBullet_trace_callback_eff = EFFECT_RIFLE;
else if(tracereffects & EF_BLUE)
- fireBullet_trace_callback_eff = particleeffectnum(EFFECT_RIFLE_WEAK);
+ fireBullet_trace_callback_eff = EFFECT_RIFLE_WEAK;
else
- fireBullet_trace_callback_eff = particleeffectnum(EFFECT_BULLET);
+ fireBullet_trace_callback_eff = EFFECT_BULLET;
float lag = ANTILAG_LATENCY(self);
if(lag < 0.001)
.vector railgunforce;
void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, int deathtype);
-float fireBullet_trace_callback_eff;
+entity fireBullet_trace_callback_eff;
entity fireBullet_last_hit;
void fireBullet_trace_callback(vector start, vector hit, vector end);
void fireBullet(vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, int tracereffects);
vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn)
{
- switch(algn)
+ switch (algn)
{
default: case 3: break; // right alignment
- case 4: vecs.y = -vecs.y; break; // left
- case 1: case 2: vecs.y = 0; vecs.z -= 2; break; // center
+ case 4: vecs.y = -vecs.y;
+ break; // left
+ case 1: case 2: vecs.y = 0;
+ vecs.z -= 2;
+ break; // center
}
return vecs;
{
string s;
- if(visual)
+ if (visual)
+ {
vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
- else if(autocvar_g_shootfromeye)
+ }
+ else if (autocvar_g_shootfromeye)
+ {
vecs.y = vecs.z = 0;
- else if(autocvar_g_shootfromcenter)
+ }
+ else if (autocvar_g_shootfromcenter)
{
vecs.y = 0;
vecs.z -= 2;
}
- else if((s = autocvar_g_shootfromfixedorigin) != "")
+ else if ((s = autocvar_g_shootfromfixedorigin) != "")
{
vector v = stov(s);
- if(y_is_right) { v.y = -v.y; }
- if(v.x != 0) { vecs.x = v.x; }
+ if (y_is_right) v.y = -v.y;
+ if (v.x != 0) vecs.x = v.x;
vecs.y = v.y;
vecs.z = v.z;
}
- else // just do the same as top
+ else // just do the same as top
+ {
vecs = shotorg_adjustfromclient(vecs, y_is_right, algn);
+ }
return vecs;
}
}
-void weapon_thinkf(entity actor, float fr, float t, void(Weapon thiswep, entity actor, bool fire1, bool fire2) func);
+void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(Weapon thiswep, entity actor,
+ .entity weaponentity, int fire) func);
-float CL_Weaponentity_CustomizeEntityForClient()
-{SELFPARAM();
- self.viewmodelforclient = self.owner;
- if(IS_SPEC(other))
- if(other.enemy == self.owner)
- self.viewmodelforclient = other;
+bool CL_Weaponentity_CustomizeEntityForClient()
+{
+ SELFPARAM();
+ this.viewmodelforclient = this.owner;
+ if (IS_SPEC(other) && other.enemy == this.owner) this.viewmodelforclient = other;
return true;
}
*/
// writes:
-// self.origin, self.angles
-// self.weaponentity
-// self.movedir, self.view_ofs
+// this.origin, this.angles
+// this.weaponentity
+// this.movedir, this.view_ofs
// attachment stuff
// anim stuff
// to free:
// call again with ""
// remove the ent
-void CL_WeaponEntity_SetModel(string name)
-{SELFPARAM();
- float v_shot_idx;
+void CL_WeaponEntity_SetModel(entity this, .entity weaponentity, string name)
+{
if (name != "")
{
// if there is a child entity, hide it until we're sure we use it
- if (self.weaponentity)
- self.weaponentity.model = "";
- _setmodel(self, W_Model(strcat("v_", name, ".md3")));
- v_shot_idx = gettagindex(self, "shot"); // used later
- if(!v_shot_idx)
- v_shot_idx = gettagindex(self, "tag_shot");
-
- _setmodel(self, W_Model(strcat("h_", name, ".iqm")));
+ if (this.(weaponentity)) this.(weaponentity).model = "";
+ _setmodel(this, W_Model(strcat("v_", name, ".md3")));
+ int v_shot_idx = gettagindex(this, "shot"); // used later
+ if (!v_shot_idx) v_shot_idx = gettagindex(this, "tag_shot");
+
+ _setmodel(this, W_Model(strcat("h_", name, ".iqm")));
// preset some defaults that work great for renamed zym files (which don't need an animinfo)
- self.anim_fire1 = animfixfps(self, '0 1 0.01', '0 0 0');
- self.anim_fire2 = animfixfps(self, '1 1 0.01', '0 0 0');
- self.anim_idle = animfixfps(self, '2 1 0.01', '0 0 0');
- self.anim_reload = animfixfps(self, '3 1 0.01', '0 0 0');
+ this.anim_fire1 = animfixfps(this, '0 1 0.01', '0 0 0');
+ this.anim_fire2 = animfixfps(this, '1 1 0.01', '0 0 0');
+ this.anim_idle = animfixfps(this, '2 1 0.01', '0 0 0');
+ this.anim_reload = animfixfps(this, '3 1 0.01', '0 0 0');
// if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
// if we don't, this is a "real" animated model
- if(gettagindex(self, "weapon"))
+ if (gettagindex(this, "weapon"))
{
- if (!self.weaponentity)
- self.weaponentity = spawn();
- _setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3")));
- setattachment(self.weaponentity, self, "weapon");
+ if (!this.(weaponentity)) this.(weaponentity) = new(weaponentity);
+ _setmodel(this.(weaponentity), W_Model(strcat("v_", name, ".md3")));
+ setattachment(this.(weaponentity), this, "weapon");
}
- else if(gettagindex(self, "tag_weapon"))
+ else if (gettagindex(this, "tag_weapon"))
{
- if (!self.weaponentity)
- self.weaponentity = spawn();
- _setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3")));
- setattachment(self.weaponentity, self, "tag_weapon");
+ if (!this.(weaponentity)) this.(weaponentity) = new(weaponentity);
+ _setmodel(this.(weaponentity), W_Model(strcat("v_", name, ".md3")));
+ setattachment(this.(weaponentity), this, "tag_weapon");
}
else
{
- if(self.weaponentity)
- remove(self.weaponentity);
- self.weaponentity = world;
+ if (this.(weaponentity)) remove(this.(weaponentity));
+ this.(weaponentity) = NULL;
}
- setorigin(self,'0 0 0');
- self.angles = '0 0 0';
- self.frame = 0;
- self.viewmodelforclient = world;
+ setorigin(this, '0 0 0');
+ this.angles = '0 0 0';
+ this.frame = 0;
+ this.viewmodelforclient = NULL;
float idx;
- if(v_shot_idx) // v_ model attached to invisible h_ model
+ if (v_shot_idx) // v_ model attached to invisible h_ model
{
- self.movedir = gettaginfo(self.weaponentity, v_shot_idx);
+ this.movedir = gettaginfo(this.(weaponentity), v_shot_idx);
}
else
{
- idx = gettagindex(self, "shot");
- if(!idx)
- idx = gettagindex(self, "tag_shot");
- if(idx)
- self.movedir = gettaginfo(self, idx);
+ idx = gettagindex(this, "shot");
+ if (!idx) idx = gettagindex(this, "tag_shot");
+ if (idx)
+ {
+ this.movedir = gettaginfo(this, idx);
+ }
else
{
- LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
- self.movedir = '0 0 0';
+ LOG_INFO("WARNING: weapon model ", this.model,
+ " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
+ this.movedir = '0 0 0';
}
}
- if(self.weaponentity) // v_ model attached to invisible h_ model
+ if (this.(weaponentity)) // v_ model attached to invisible h_ model
{
- idx = gettagindex(self.weaponentity, "shell");
- if(!idx)
- idx = gettagindex(self.weaponentity, "tag_shell");
- if(idx)
- self.spawnorigin = gettaginfo(self.weaponentity, idx);
+ idx = gettagindex(this.(weaponentity), "shell");
+ if (!idx) idx = gettagindex(this.(weaponentity), "tag_shell");
+ if (idx) this.spawnorigin = gettaginfo(this.(weaponentity), idx);
}
else
+ {
idx = 0;
- if(!idx)
+ }
+ if (!idx)
{
- idx = gettagindex(self, "shell");
- if(!idx)
- idx = gettagindex(self, "tag_shell");
- if(idx)
- self.spawnorigin = gettaginfo(self, idx);
+ idx = gettagindex(this, "shell");
+ if (!idx) idx = gettagindex(this, "tag_shell");
+ if (idx)
+ {
+ this.spawnorigin = gettaginfo(this, idx);
+ }
else
{
- LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
- self.spawnorigin = self.movedir;
+ LOG_INFO("WARNING: weapon model ", this.model,
+ " does not support the 'shell' tag, will display casings wrong\n");
+ this.spawnorigin = this.movedir;
}
}
- if(v_shot_idx)
+ if (v_shot_idx)
{
- self.oldorigin = '0 0 0'; // use regular attachment
+ this.oldorigin = '0 0 0'; // use regular attachment
}
else
{
- if(self.weaponentity)
+ if (this.(weaponentity))
{
- idx = gettagindex(self, "weapon");
- if(!idx)
- idx = gettagindex(self, "tag_weapon");
+ idx = gettagindex(this, "weapon");
+ if (!idx) idx = gettagindex(this, "tag_weapon");
}
else
{
- idx = gettagindex(self, "handle");
- if(!idx)
- idx = gettagindex(self, "tag_handle");
+ idx = gettagindex(this, "handle");
+ if (!idx) idx = gettagindex(this, "tag_handle");
}
- if(idx)
+ if (idx)
{
- self.oldorigin = self.movedir - gettaginfo(self, idx);
+ this.oldorigin = this.movedir - gettaginfo(this, idx);
}
else
{
- LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
- self.oldorigin = '0 0 0'; // there is no way to recover from this
+ LOG_INFO("WARNING: weapon model ", this.model,
+ " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
+ this.oldorigin = '0 0 0'; // there is no way to recover from this
}
}
- self.viewmodelforclient = self.owner;
+ this.viewmodelforclient = this.owner;
}
else
{
- self.model = "";
- if(self.weaponentity)
- remove(self.weaponentity);
- self.weaponentity = world;
- self.movedir = '0 0 0';
- self.spawnorigin = '0 0 0';
- self.oldorigin = '0 0 0';
- self.anim_fire1 = '0 1 0.01';
- self.anim_fire2 = '0 1 0.01';
- self.anim_idle = '0 1 0.01';
- self.anim_reload = '0 1 0.01';
+ this.model = "";
+ if (this.(weaponentity)) remove(this.(weaponentity));
+ this.(weaponentity) = NULL;
+ this.movedir = '0 0 0';
+ this.spawnorigin = '0 0 0';
+ this.oldorigin = '0 0 0';
+ this.anim_fire1 = '0 1 0.01';
+ this.anim_fire2 = '0 1 0.01';
+ this.anim_idle = '0 1 0.01';
+ this.anim_reload = '0 1 0.01';
}
- self.view_ofs = '0 0 0';
+ this.view_ofs = '0 0 0';
- if(self.movedir.x >= 0)
+ if (this.movedir.x >= 0)
{
- vector v0;
- v0 = self.movedir;
- self.movedir = shotorg_adjust(v0, false, false, self.owner.cvar_cl_gunalign);
- self.view_ofs = shotorg_adjust(v0, false, true, self.owner.cvar_cl_gunalign) - v0;
+ vector v0 = this.movedir;
+ this.movedir = shotorg_adjust(v0, false, false, this.owner.cvar_cl_gunalign);
+ this.view_ofs = shotorg_adjust(v0, false, true, this.owner.cvar_cl_gunalign) - v0;
}
- self.owner.stat_shotorg = compressShotOrigin(self.movedir);
- self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly
+ this.owner.stat_shotorg = compressShotOrigin(this.movedir);
+ this.movedir = decompressShotOrigin(this.owner.stat_shotorg); // make them match perfectly
- self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount
+ this.spawnorigin += this.view_ofs; // offset the casings origin by the same amount
// check if an instant weapon switch occurred
- setorigin(self, self.view_ofs);
+ setorigin(this, this.view_ofs);
// reset animstate now
- self.wframe = WFRAME_IDLE;
- setanim(self, self.anim_idle, true, false, true);
+ this.wframe = WFRAME_IDLE;
+ setanim(this, this.anim_idle, true, false, true);
}
vector CL_Weapon_GetShotOrg(float wpn)
-{SELFPARAM();
- entity wi = get_weaponinfo(wpn);
- setself(spawn());
- CL_WeaponEntity_SetModel(wi.mdl);
- vector ret = self.movedir;
- CL_WeaponEntity_SetModel("");
- remove(self);
- setself(this);
+{
+ entity wi = Weapons_from(wpn);
+ entity e = spawn();
+ .entity weaponentity = weaponentities[0];
+ CL_WeaponEntity_SetModel(e, weaponentity, wi.mdl);
+ vector ret = e.movedir;
+ CL_WeaponEntity_SetModel(e, weaponentity, "");
+ remove(e);
return ret;
}
+..entity weaponentity_fld;
+
void CL_Weaponentity_Think()
-{SELFPARAM();
- int tb;
- self.nextthink = time;
- if (intermission_running)
- self.frame = self.anim_idle.x;
- if (self.owner.weaponentity != self)
+{
+ SELFPARAM();
+ this.nextthink = time;
+ if (intermission_running) this.frame = this.anim_idle.x;
+ .entity weaponentity = this.weaponentity_fld;
+ if (this.owner.(weaponentity) != this)
{
- if (self.weaponentity)
- remove(self.weaponentity);
- remove(self);
+ if (this.(weaponentity)) remove(this.(weaponentity));
+ remove(this);
return;
}
- if (self.owner.deadflag != DEAD_NO)
+ if (this.owner.deadflag != DEAD_NO)
{
- self.model = "";
- if (self.weaponentity)
- self.weaponentity.model = "";
+ this.model = "";
+ if (this.(weaponentity)) this.(weaponentity).model = "";
return;
}
- if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
+ if (this.weaponname != this.owner.weaponname || this.dmg != this.owner.modelindex
+ || this.deadflag != this.owner.deadflag)
{
- self.weaponname = self.owner.weaponname;
- self.dmg = self.owner.modelindex;
- self.deadflag = self.owner.deadflag;
+ this.weaponname = this.owner.weaponname;
+ this.dmg = this.owner.modelindex;
+ this.deadflag = this.owner.deadflag;
- CL_WeaponEntity_SetModel(self.owner.weaponname);
+ CL_WeaponEntity_SetModel(this, weaponentity, this.owner.weaponname);
}
- tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT));
- self.effects = self.owner.effects & EFMASK_CHEAP;
- self.effects &= ~EF_LOWPRECISION;
- self.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it
- self.effects &= ~EF_TELEPORT_BIT;
- self.effects &= ~EF_RESTARTANIM_BIT;
- self.effects |= tb;
-
- if(self.owner.alpha == default_player_alpha)
- self.alpha = default_weapon_alpha;
- else if(self.owner.alpha != 0)
- self.alpha = self.owner.alpha;
- else
- self.alpha = 1;
-
- self.glowmod = self.owner.weaponentity_glowmod;
- self.colormap = self.owner.colormap;
- if (self.weaponentity)
+ int tb = (this.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT));
+ this.effects = this.owner.effects & EFMASK_CHEAP;
+ this.effects &= ~EF_LOWPRECISION;
+ this.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it
+ this.effects &= ~EF_TELEPORT_BIT;
+ this.effects &= ~EF_RESTARTANIM_BIT;
+ this.effects |= tb;
+ if (weaponentity != weaponentities[0]) this.effects |= EF_NODRAW;
+
+ if (this.owner.alpha == default_player_alpha) this.alpha = default_weapon_alpha;
+ else if (this.owner.alpha != 0) this.alpha = this.owner.alpha;
+ else this.alpha = 1;
+
+ this.glowmod = this.owner.weaponentity_glowmod;
+ this.colormap = this.owner.colormap;
+ if (this.(weaponentity))
{
- self.weaponentity.effects = self.effects;
- self.weaponentity.alpha = self.alpha;
- self.weaponentity.colormap = self.colormap;
- self.weaponentity.glowmod = self.glowmod;
+ this.(weaponentity).effects = this.effects;
+ this.(weaponentity).alpha = this.alpha;
+ this.(weaponentity).colormap = this.colormap;
+ this.(weaponentity).glowmod = this.glowmod;
}
- self.angles = '0 0 0';
+ this.angles = '0 0 0';
- float f = (self.owner.weapon_nextthink - time);
- if (self.state == WS_RAISE && !intermission_running)
+ float f = this.weapon_nextthink - time;
+ if (this.state == WS_RAISE && !intermission_running)
{
- entity newwep = get_weaponinfo(self.owner.switchweapon);
+ entity newwep = Weapons_from(this.owner.switchweapon);
f = f * g_weaponratefactor / max(f, newwep.switchdelay_raise);
- self.angles_x = -90 * f * f;
+ this.angles_x = -90 * f * f;
}
- else if (self.state == WS_DROP && !intermission_running)
+ else if (this.state == WS_DROP && !intermission_running)
{
- entity oldwep = get_weaponinfo(self.owner.weapon);
+ entity oldwep = Weapons_from(this.owner.weapon);
f = 1 - f * g_weaponratefactor / max(f, oldwep.switchdelay_drop);
- self.angles_x = -90 * f * f;
+ this.angles_x = -90 * f * f;
}
- else if (self.state == WS_CLEAR)
+ else if (this.state == WS_CLEAR)
{
f = 1;
- self.angles_x = -90 * f * f;
+ this.angles_x = -90 * f * f;
}
}
void CL_ExteriorWeaponentity_Think()
-{SELFPARAM();
- float tag_found;
- self.nextthink = time;
- if (self.owner.exteriorweaponentity != self)
+{
+ SELFPARAM();
+ this.nextthink = time;
+ if (this.owner.exteriorweaponentity != this)
{
- remove(self);
+ remove(this);
return;
}
- if (self.owner.deadflag != DEAD_NO)
+ if (this.owner.deadflag != DEAD_NO)
{
- self.model = "";
+ this.model = "";
return;
}
- if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
+ if (this.weaponname != this.owner.weaponname || this.dmg != this.owner.modelindex
+ || this.deadflag != this.owner.deadflag)
{
- self.weaponname = self.owner.weaponname;
- self.dmg = self.owner.modelindex;
- self.deadflag = self.owner.deadflag;
- if (self.owner.weaponname != "")
- _setmodel(self, W_Model(strcat("v_", self.owner.weaponname, ".md3")));
- else
- self.model = "";
-
- if((tag_found = gettagindex(self.owner, "tag_weapon")))
+ this.weaponname = this.owner.weaponname;
+ this.dmg = this.owner.modelindex;
+ this.deadflag = this.owner.deadflag;
+ if (this.owner.weaponname != "") _setmodel(this, W_Model(strcat("v_", this.owner.weaponname, ".md3")));
+ else this.model = "";
+
+ int tag_found;
+ if ((tag_found = gettagindex(this.owner, "tag_weapon")))
{
- self.tag_index = tag_found;
- self.tag_entity = self.owner;
+ this.tag_index = tag_found;
+ this.tag_entity = this.owner;
}
else
- setattachment(self, self.owner, "bip01 r hand");
+ {
+ setattachment(this, this.owner, "bip01 r hand");
+ }
}
- self.effects = self.owner.effects;
- self.effects |= EF_LOWPRECISION;
- self.effects = self.effects & EFMASK_CHEAP; // eat performance
- if(self.owner.alpha == default_player_alpha)
- self.alpha = default_weapon_alpha;
- else if(self.owner.alpha != 0)
- self.alpha = self.owner.alpha;
- else
- self.alpha = 1;
+ this.effects = this.owner.effects;
+ this.effects |= EF_LOWPRECISION;
+ this.effects = this.effects & EFMASK_CHEAP; // eat performance
+ if (this.owner.alpha == default_player_alpha) this.alpha = default_weapon_alpha;
+ else if (this.owner.alpha != 0) this.alpha = this.owner.alpha;
+ else this.alpha = 1;
- self.glowmod = self.owner.weaponentity_glowmod;
- self.colormap = self.owner.colormap;
+ this.glowmod = this.owner.weaponentity_glowmod;
+ this.colormap = this.owner.colormap;
- CSQCMODEL_AUTOUPDATE(self);
+ CSQCMODEL_AUTOUPDATE(this);
}
// spawning weaponentity for client
-void CL_SpawnWeaponentity(entity e)
+void CL_SpawnWeaponentity(entity e, .entity weaponentity)
{
- entity view = e.weaponentity = spawn();
- view.classname = "weaponentity";
+ entity view = e.(weaponentity) = new(weaponentity);
+ make_pure(view);
view.solid = SOLID_NOT;
view.owner = e;
- setmodel(view, MDL_Null); // precision set when changed
+ setmodel(view, MDL_Null); // precision set when changed
setorigin(view, '0 0 0');
view.angles = '0 0 0';
view.viewmodelforclient = e;
view.flags = 0;
+ view.weaponentity_fld = weaponentity;
view.think = CL_Weaponentity_Think;
view.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
view.nextthink = time;
- entity exterior = e.exteriorweaponentity = spawn();
- exterior.classname = "exteriorweaponentity";
- exterior.solid = SOLID_NOT;
- exterior.exteriorweaponentity = exterior;
- exterior.owner = e;
- setorigin(exterior, '0 0 0');
- exterior.angles = '0 0 0';
- exterior.think = CL_ExteriorWeaponentity_Think;
- exterior.nextthink = time;
-
- CSQCMODEL_AUTOINIT(exterior);
+ if (weaponentity == weaponentities[0])
+ {
+ entity exterior = e.exteriorweaponentity = new(exteriorweaponentity);
+ make_pure(exterior);
+ exterior.solid = SOLID_NOT;
+ exterior.exteriorweaponentity = exterior;
+ exterior.owner = e;
+ setorigin(exterior, '0 0 0');
+ exterior.angles = '0 0 0';
+ exterior.think = CL_ExteriorWeaponentity_Think;
+ exterior.nextthink = time;
+
+ CSQCMODEL_AUTOINIT(exterior);
+ }
}
// Weapon subs
-void w_clear(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void w_clear(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
if (actor.weapon != -1)
{
actor.weapon = 0;
actor.switchingweapon = 0;
}
- if (actor.weaponentity)
+ entity this = actor.(weaponentity);
+ if (this)
{
- actor.weaponentity.state = WS_CLEAR;
- actor.weaponentity.effects = 0;
+ this.state = WS_CLEAR;
+ this.effects = 0;
}
}
-void w_ready(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void w_ready(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
- if (actor.weaponentity)
- actor.weaponentity.state = WS_READY;
- weapon_thinkf(actor, WFRAME_IDLE, 1000000, w_ready);
+ entity this = actor.(weaponentity);
+ if (this) this.state = WS_READY;
+ weapon_thinkf(actor, weaponentity, WFRAME_IDLE, 1000000, w_ready);
}
.float prevdryfire;
.float prevwarntime;
-bool weapon_prepareattack_checkammo(Weapon thiswep, entity actor, float secondary)
+bool weapon_prepareattack_checkammo(Weapon thiswep, entity actor, bool secondary)
{
if ((actor.items & IT_UNLIMITED_WEAPON_AMMO)) return true;
bool ammo = false;
- if (secondary) {
- WITH(entity, self, actor, ammo = thiswep.wr_checkammo2(thiswep));
- } else {
- WITH(entity, self, actor, ammo = thiswep.wr_checkammo1(thiswep));
- }
+ if (secondary) WITH(entity, self, actor, ammo = thiswep.wr_checkammo2(thiswep));
+ else WITH(entity, self, actor, ammo = thiswep.wr_checkammo1(thiswep));
if (ammo) return true;
// always keep the Mine Layer if we placed mines, so that we can detonate them
- entity mine;
- if(actor.weapon == WEP_MINE_LAYER.m_id)
- for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == actor)
- return false;
+ if (thiswep == WEP_MINE_LAYER)
+ for (entity mine; (mine = find(mine, classname, "mine")); )
+ if (mine.owner == actor) return false;
- if(actor.weapon == WEP_SHOTGUN.m_id)
- if(!secondary && WEP_CVAR(shotgun, secondary) == 1)
- return false; // no clicking, just allow
+ if (thiswep == WEP_SHOTGUN)
+ if (!secondary && WEP_CVAR(shotgun, secondary) == 1) return false; // no clicking, just allow
- if(actor.weapon == actor.switchweapon && time - actor.prevdryfire > 1) // only play once BEFORE starting to switch weapons
+ if (thiswep == Weapons_from(actor.switchweapon) && time - actor.prevdryfire > 1) // only play once BEFORE starting to switch weapons
{
- sound (actor, CH_WEAPON_A, SND_DRYFIRE, VOL_BASE, ATTEN_NORM);
+ sound(actor, CH_WEAPON_A, SND_DRYFIRE, VOL_BASE, ATTEN_NORM);
actor.prevdryfire = time;
}
// check if the other firing mode has enough ammo
bool ammo_other = false;
- if (secondary) {
- WITH(entity, self, actor, ammo = thiswep.wr_checkammo1(thiswep));
- } else {
- WITH(entity, self, actor, ammo = thiswep.wr_checkammo2(thiswep));
- }
+ if (secondary) WITH(entity, self, actor, ammo_other = thiswep.wr_checkammo1(thiswep));
+ else WITH(entity, self, actor, ammo_other = thiswep.wr_checkammo2(thiswep));
if (ammo_other)
{
if (time - actor.prevwarntime > 1)
actor,
MSG_MULTI,
ITEM_WEAPON_PRIMORSEC,
- actor.weapon,
+ thiswep.m_id,
secondary,
(1 - secondary)
- );
+ );
}
actor.prevwarntime = time;
}
- else // this weapon is totally unable to fire, switch to another one
+ else // this weapon is totally unable to fire, switch to another one
{
W_SwitchToOtherWeapon(actor);
}
return false;
}
+
.float race_penalty;
-bool weapon_prepareattack_check(Weapon thiswep, entity actor, bool secondary, float attacktime)
+bool weapon_prepareattack_check(Weapon thiswep, entity actor, .entity weaponentity, bool secondary, float attacktime)
{
- if(!weapon_prepareattack_checkammo(thiswep, actor, secondary))
- return false;
+ if (!weapon_prepareattack_checkammo(thiswep, actor, secondary)) return false;
- //if sv_ready_restart_after_countdown is set, don't allow the player to shoot
- //if all players readied up and the countdown is running
- if(time < game_starttime || time < actor.race_penalty) {
- return false;
- }
+ // if sv_ready_restart_after_countdown is set, don't allow the player to shoot
+ // if all players readied up and the countdown is running
+ if (time < game_starttime || time < actor.race_penalty) return false;
- if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused
+ if (timeout_status == TIMEOUT_ACTIVE) // don't allow the player to shoot while game is paused
return false;
// do not even think about shooting if switching
- if(actor.switchweapon != actor.weapon)
- return false;
+ if (actor.switchweapon != actor.weapon) return false;
- if(attacktime >= 0)
+ if (attacktime >= 0)
{
+ int slot = weaponslot(weaponentity);
// don't fire if previous attack is not finished
- if (ATTACK_FINISHED(actor) > time + actor.weapon_frametime * 0.5)
- return false;
+ if (ATTACK_FINISHED(actor, slot) > time + actor.weapon_frametime * 0.5) return false;
+ entity this = actor.(weaponentity);
// don't fire while changing weapon
- if (actor.weaponentity.state != WS_READY)
- return false;
+ if (this.state != WS_READY) return false;
}
return true;
}
-void weapon_prepareattack_do(entity actor, bool secondary, float attacktime)
+
+void weapon_prepareattack_do(entity actor, .entity weaponentity, bool secondary, float attacktime)
{
- actor.weaponentity.state = WS_INUSE;
+ entity this = actor.(weaponentity);
+ this.state = WS_INUSE;
- actor.spawnshieldtime = min(actor.spawnshieldtime, time); // kill spawn shield when you fire
+ actor.spawnshieldtime = min(actor.spawnshieldtime, time); // kill spawn shield when you fire
// if the weapon hasn't been firing continuously, reset the timer
- if(attacktime >= 0)
+ if (attacktime >= 0)
{
- if (ATTACK_FINISHED(actor) < time - actor.weapon_frametime * 1.5)
+ int slot = weaponslot(weaponentity);
+ if (ATTACK_FINISHED(actor, slot) < time - actor.weapon_frametime * 1.5)
{
- ATTACK_FINISHED(actor) = time;
- //dprint("resetting attack finished to ", ftos(time), "\n");
+ ATTACK_FINISHED(actor, slot) = time;
+ // dprint("resetting attack finished to ", ftos(time), "\n");
}
- ATTACK_FINISHED(actor) = ATTACK_FINISHED(actor) + attacktime * W_WeaponRateFactor();
+ ATTACK_FINISHED(actor, slot) = ATTACK_FINISHED(actor, slot) + attacktime * W_WeaponRateFactor();
}
actor.bulletcounter += 1;
- //dprint("attack finished ", ftos(ATTACK_FINISHED(actor)), "\n");
+ // dprint("attack finished ", ftos(ATTACK_FINISHED(actor, slot)), "\n");
}
-bool weapon_prepareattack(Weapon thiswep, entity actor, bool secondary, float attacktime)
+
+bool weapon_prepareattack(Weapon thiswep, entity actor, .entity weaponentity, bool secondary, float attacktime)
{
- if (weapon_prepareattack_check(thiswep, actor, secondary, attacktime)) {
- weapon_prepareattack_do(actor, secondary, attacktime);
+ if (weapon_prepareattack_check(thiswep, actor, weaponentity, secondary, attacktime))
+ {
+ weapon_prepareattack_do(actor, weaponentity, secondary, attacktime);
return true;
}
return false;
}
-void weapon_thinkf(entity actor, float fr, float t, void(Weapon thiswep, entity actor, bool fire1, bool fire2) func)
+void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(Weapon thiswep, entity actor,
+ .entity weaponentity, int fire) func)
{
- vector a;
- vector of, or, ou;
- float restartanim;
-
- if(fr == WFRAME_DONTCHANGE)
+ entity this = actor.(weaponentity);
+ bool restartanim;
+ if (fr == WFRAME_DONTCHANGE)
{
- fr = actor.weaponentity.wframe;
+ fr = this.wframe;
restartanim = false;
}
else if (fr == WFRAME_IDLE)
+ {
restartanim = false;
+ }
else
+ {
restartanim = true;
+ }
- of = v_forward;
- or = v_right;
- ou = v_up;
+ vector of = v_forward;
+ vector or = v_right;
+ vector ou = v_up;
- if (actor.weaponentity)
+ if (this)
{
- actor.weaponentity.wframe = fr;
- a = '0 0 0';
- if (fr == WFRAME_IDLE)
- a = actor.weaponentity.anim_idle;
- else if (fr == WFRAME_FIRE1)
- a = actor.weaponentity.anim_fire1;
- else if (fr == WFRAME_FIRE2)
- a = actor.weaponentity.anim_fire2;
- else // if (fr == WFRAME_RELOAD)
- a = actor.weaponentity.anim_reload;
+ this.wframe = fr;
+ vector a = '0 0 0';
+ if (fr == WFRAME_IDLE) a = this.anim_idle;
+ else if (fr == WFRAME_FIRE1) a = this.anim_fire1;
+ else if (fr == WFRAME_FIRE2) a = this.anim_fire2;
+ else // if (fr == WFRAME_RELOAD)
+ a = this.anim_reload;
a.z *= g_weaponratefactor;
- setanim(actor.weaponentity, a, restartanim == false, restartanim, restartanim);
+ setanim(this, a, restartanim == false, restartanim, restartanim);
}
v_forward = of;
v_right = or;
v_up = ou;
- if(actor.weapon_think == w_ready && func != w_ready && actor.weaponentity.state == WS_RAISE)
- {
- backtrace("Tried to override initial weapon think function - should this really happen?");
- }
+ if (this.weapon_think == w_ready && func != w_ready && this.state == WS_RAISE) backtrace(
+ "Tried to override initial weapon think function - should this really happen?");
t *= W_WeaponRateFactor();
// VorteX: haste can be added here
- if (actor.weapon_think == w_ready)
+ if (this.weapon_think == w_ready)
{
- actor.weapon_nextthink = time;
- //dprint("started firing at ", ftos(time), "\n");
+ this.weapon_nextthink = time;
+ // dprint("started firing at ", ftos(time), "\n");
}
- if (actor.weapon_nextthink < time - actor.weapon_frametime * 1.5 || actor.weapon_nextthink > time + actor.weapon_frametime * 1.5)
+ if (this.weapon_nextthink < time - actor.weapon_frametime * 1.5
+ || this.weapon_nextthink > time + actor.weapon_frametime * 1.5)
{
- actor.weapon_nextthink = time;
- //dprint("reset weapon animation timer at ", ftos(time), "\n");
+ this.weapon_nextthink = time;
+ // dprint("reset weapon animation timer at ", ftos(time), "\n");
}
- actor.weapon_nextthink = actor.weapon_nextthink + t;
- actor.weapon_think = func;
- //dprint("next ", ftos(actor.weapon_nextthink), "\n");
+ this.weapon_nextthink = this.weapon_nextthink + t;
+ this.weapon_think = func;
+ // dprint("next ", ftos(this.weapon_nextthink), "\n");
- if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
+ if ((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
{
- if((actor.weapon == WEP_SHOCKWAVE.m_id || actor.weapon == WEP_SHOTGUN.m_id) && fr == WFRAME_FIRE2)
- animdecide_setaction(actor, ANIMACTION_MELEE, restartanim);
- else
- animdecide_setaction(actor, ANIMACTION_SHOOT, restartanim);
+ if ((actor.weapon == WEP_SHOCKWAVE.m_id || actor.weapon == WEP_SHOTGUN.m_id)
+ && fr == WFRAME_FIRE2) animdecide_setaction(actor, ANIMACTION_MELEE, restartanim);
+ else animdecide_setaction(actor, ANIMACTION_SHOOT, restartanim);
}
else
{
- if(actor.anim_upper_action == ANIMACTION_SHOOT || actor.anim_upper_action == ANIMACTION_MELEE)
- actor.anim_upper_action = 0;
+ if (actor.anim_upper_action == ANIMACTION_SHOOT
+ || actor.anim_upper_action == ANIMACTION_MELEE) actor.anim_upper_action = 0;
}
}
-float forbidWeaponUse(entity player)
+bool forbidWeaponUse(entity player)
{
- if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
- return 1;
- if(round_handler_IsActive() && !round_handler_IsRoundStarted())
- return 1;
- if(player.player_blocked)
- return 1;
- if(player.frozen)
- return 1;
- if(player.weapon_blocked)
- return 1;
- return 0;
+ if (time < game_starttime && !autocvar_sv_ready_restart_after_countdown) return true;
+ if (round_handler_IsActive() && !round_handler_IsRoundStarted()) return true;
+ if (player.player_blocked) return true;
+ if (player.frozen) return true;
+ if (player.weapon_blocked) return true;
+ return false;
}
.bool hook_switchweapon;
void W_WeaponFrame(entity actor)
{
- vector fo, ri, up;
+ .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+ entity this = actor.(weaponentity);
+ if (frametime) actor.weapon_frametime = frametime;
- if (frametime)
- actor.weapon_frametime = frametime;
+ if (!this || actor.health < 1) return; // Dead player can't use weapons and injure impulse commands
- if (!actor.weaponentity || actor.health < 1)
- return; // Dead player can't use weapons and injure impulse commands
- if(forbidWeaponUse(actor))
- if(actor.weaponentity.state != WS_CLEAR)
+ if (forbidWeaponUse(actor))
{
- Weapon wpn = get_weaponinfo(actor.weapon);
- w_ready(wpn, actor, actor.BUTTON_ATCK, actor.BUTTON_ATCK2);
- return;
+ if (actor.(weaponentity).state != WS_CLEAR)
+ {
+ Weapon wpn = Weapons_from(actor.weapon);
+ w_ready(wpn, actor, weaponentity, (actor.BUTTON_ATCK ? 1 : 0) | (actor.BUTTON_ATCK2 ? 2 : 0));
+ return;
+ }
}
- if(!actor.switchweapon)
+ if (actor.switchweapon == 0)
{
actor.weapon = 0;
actor.switchingweapon = 0;
- actor.weaponentity.state = WS_CLEAR;
+ this.state = WS_CLEAR;
actor.weaponname = "";
- //actor.items &= ~IT_AMMO;
+ // actor.items &= ~IT_AMMO;
return;
}
makevectors(actor.v_angle);
- fo = v_forward; // save them in case the weapon think functions change it
- ri = v_right;
- up = v_up;
+ vector fo = v_forward; // save them in case the weapon think functions change it
+ vector ri = v_right;
+ vector up = v_up;
// Change weapon
if (actor.weapon != actor.switchweapon)
{
- if (actor.weaponentity.state == WS_CLEAR)
+ switch (this.state)
{
- // end switching!
- actor.switchingweapon = actor.switchweapon;
- entity newwep = get_weaponinfo(actor.switchweapon);
-
- // the two weapon entities will notice this has changed and update their models
- actor.weapon = actor.switchweapon;
- actor.weaponname = newwep.mdl;
- actor.bulletcounter = 0;
- actor.ammo_field = newwep.ammo_field;
- Weapon w = get_weaponinfo(actor.switchweapon);
- w.wr_setup(w);
- actor.weaponentity.state = WS_RAISE;
-
- // set our clip load to the load of the weapon we switched to, if it's reloadable
- if(newwep.spawnflags & WEP_FLAG_RELOADABLE && newwep.reloading_ammo) // prevent accessing undefined cvars
+ default:
+ LOG_WARNINGF("unhandled weaponentity (%i) state for player (%i): %d\n", this, actor, this.state);
+ break;
+ case WS_INUSE:
+ case WS_RAISE:
+ break;
+ case WS_CLEAR:
{
- actor.clip_load = actor.(weapon_load[actor.switchweapon]);
- actor.clip_size = newwep.reloading_ammo;
- }
- else
- actor.clip_load = actor.clip_size = 0;
-
- weapon_thinkf(actor, WFRAME_IDLE, newwep.switchdelay_raise, w_ready);
- }
- else if (actor.weaponentity.state == WS_DROP)
- {
- // in dropping phase we can switch at any time
- actor.switchingweapon = actor.switchweapon;
- }
- else if (actor.weaponentity.state == WS_READY)
- {
- // start switching!
- actor.switchingweapon = actor.switchweapon;
- entity oldwep = get_weaponinfo(actor.weapon);
+ // end switching!
+ actor.switchingweapon = actor.switchweapon;
+ entity newwep = Weapons_from(actor.switchweapon);
+
+ // the two weapon entities will notice this has changed and update their models
+ actor.weapon = actor.switchweapon;
+ actor.weaponname = newwep.mdl;
+ actor.bulletcounter = 0;
+ actor.ammo_field = newwep.ammo_field;
+ newwep.wr_setup(newwep);
+ this.state = WS_RAISE;
+
+ // set our clip load to the load of the weapon we switched to, if it's reloadable
+ if ((newwep.spawnflags & WEP_FLAG_RELOADABLE) && newwep.reloading_ammo) // prevent accessing undefined cvars
+ {
+ actor.clip_load = actor.(weapon_load[actor.switchweapon]);
+ actor.clip_size = newwep.reloading_ammo;
+ }
+ else
+ {
+ actor.clip_load = actor.clip_size = 0;
+ }
- // set up weapon switch think in the future, and start drop anim
- #ifndef INDEPENDENT_ATTACK_FINISHED
- if(ATTACK_FINISHED(actor) <= time + actor.weapon_frametime * 0.5)
+ weapon_thinkf(actor, weaponentity, WFRAME_IDLE, newwep.switchdelay_raise, w_ready);
+ break;
+ }
+ case WS_DROP:
{
- #endif
- sound(actor, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM);
- actor.weaponentity.state = WS_DROP;
- weapon_thinkf(actor, WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
- #ifndef INDEPENDENT_ATTACK_FINISHED
+ // in dropping phase we can switch at any time
+ actor.switchingweapon = actor.switchweapon;
+ break;
+ }
+ case WS_READY:
+ {
+ // start switching!
+ actor.switchingweapon = actor.switchweapon;
+ entity oldwep = Weapons_from(actor.weapon);
+
+ // set up weapon switch think in the future, and start drop anim
+ if (
+#if INDEPENDENT_ATTACK_FINISHED
+ true
+#else
+ ATTACK_FINISHED(actor, slot) <= time + actor.weapon_frametime * 0.5
+#endif
+ )
+ {
+ sound(actor, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM);
+ this.state = WS_DROP;
+ weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
+ }
+ break;
}
- #endif
}
}
// LordHavoc: network timing test code
- //if (actor.button0)
- // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(actor)), " >= ", ftos(actor.weapon_nextthink), "\n");
+ // if (actor.button0)
+ // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(actor, slot)), " >= ", ftos(this.weapon_nextthink), "\n");
- float w;
- w = actor.weapon;
+ int w = actor.weapon;
// call the think code which may fire the weapon
// and do so multiple times to resolve framerate dependency issues if the
// server framerate is very low and the weapon fire rate very high
- float c;
- c = 0;
- while (c < W_TICSPERFRAME)
+ for (int c = 0; c < W_TICSPERFRAME; ++c)
{
- c = c + 1;
- if(w && !(actor.weapons & WepSet_FromWeapon(w)))
+ if (w && !(actor.weapons & WepSet_FromWeapon(w)))
{
- if(actor.weapon == actor.switchweapon)
- W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
+ if (actor.weapon == actor.switchweapon) W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
w = 0;
}
bool block_weapon = false;
{
bool key_pressed = actor.BUTTON_HOOK && !actor.vehicle;
- Weapon off = actor.offhand;
- if (off && !(actor.weapons & WEPSET(HOOK))) {
- if (off.offhand_think) off.offhand_think(off, actor, key_pressed);
- } else {
- if (key_pressed && actor.switchweapon != WEP_HOOK.m_id && !actor.hook_switchweapon) {
- W_SwitchWeapon(WEP_HOOK.m_id);
- }
+ Weapon off = actor.offhand;
+ if (off && !(actor.weapons & WEPSET(HOOK)))
+ {
+ if (off.offhand_think) off.offhand_think(off, actor, key_pressed);
+ }
+ else
+ {
+ if (key_pressed && actor.switchweapon != WEP_HOOK.m_id && !actor.hook_switchweapon) W_SwitchWeapon(
+ WEP_HOOK.m_id);
actor.hook_switchweapon = key_pressed;
Weapon h = WEP_HOOK;
block_weapon = (actor.weapon == h.m_id && (actor.BUTTON_ATCK || key_pressed));
- h.wr_think(h, actor, block_weapon, false);
- }
- }
+ h.wr_think(h, actor, weaponentity, block_weapon ? 1 : 0);
+ }
+ }
+
+ v_forward = fo;
+ v_right = ri;
+ v_up = up;
if (!block_weapon)
- if (w) {
- Weapon e = get_weaponinfo(actor.weapon);
- e.wr_think(e, actor, actor.BUTTON_ATCK, actor.BUTTON_ATCK2);
- } else {
- Weapon w = get_weaponinfo(actor.weapon);
- w.wr_gonethink(w);
+ {
+ if (w)
+ {
+ Weapon e = Weapons_from(actor.weapon);
+ e.wr_think(e, actor, weaponentity, (actor.BUTTON_ATCK ? 1 : 0) | (actor.BUTTON_ATCK2 ? 2 : 0));
+ }
+ else
+ {
+ Weapon w = Weapons_from(actor.weapon);
+ w.wr_gonethink(w);
+ }
}
- if (time + actor.weapon_frametime * 0.5 >= actor.weapon_nextthink)
+ if (time + actor.weapon_frametime * 0.5 >= this.weapon_nextthink)
{
- if(actor.weapon_think)
+ if (this.weapon_think)
{
v_forward = fo;
v_right = ri;
v_up = up;
- Weapon wpn = get_weaponinfo(actor.weapon);
- actor.weapon_think(wpn, actor, actor.BUTTON_ATCK, actor.BUTTON_ATCK2);
+ Weapon wpn = Weapons_from(actor.weapon);
+ this.weapon_think(wpn, actor, weaponentity,
+ (actor.BUTTON_ATCK ? 1 : 0) | (actor.BUTTON_ATCK2 ? 2 : 0));
}
else
+ {
bprint("\{1}^1ERROR: undefined weapon think function for ", actor.netname, "\n");
+ }
}
}
}
void W_AttachToShotorg(entity actor, entity flash, vector offset)
{
- entity xflash;
+ .entity weaponentity = weaponentities[0];
flash.owner = actor;
flash.angles_z = random() * 360;
- if(gettagindex(actor.weaponentity, "shot"))
- setattachment(flash, actor.weaponentity, "shot");
- else
- setattachment(flash, actor.weaponentity, "tag_shot");
+ entity this = actor.(weaponentity);
+ if (gettagindex(this, "shot")) setattachment(flash, this, "shot");
+ else setattachment(flash, this, "tag_shot");
setorigin(flash, offset);
- xflash = spawn();
+ entity xflash = spawn();
copyentity(flash, xflash);
flash.viewmodelforclient = actor;
- if(actor.weaponentity.oldorigin.x > 0)
+ if (this.oldorigin.x > 0)
{
setattachment(xflash, actor.exteriorweaponentity, "");
- setorigin(xflash, actor.weaponentity.oldorigin + offset);
+ setorigin(xflash, this.oldorigin + offset);
}
else
{
- if(gettagindex(actor.exteriorweaponentity, "shot"))
- setattachment(xflash, actor.exteriorweaponentity, "shot");
- else
- setattachment(xflash, actor.exteriorweaponentity, "tag_shot");
+ if (gettagindex(actor.exteriorweaponentity, "shot")) setattachment(xflash, actor.exteriorweaponentity, "shot");
+ else setattachment(xflash, actor.exteriorweaponentity, "tag_shot");
setorigin(xflash, offset);
}
}
void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use)
{
+ if (MUTATOR_CALLHOOK(W_DecreaseAmmo, actor)) return;
- if(cvar("g_overkill"))
- if(actor.ok_use_ammocharge)
- {
- ok_DecreaseCharge(actor, actor.weapon);
- return; // TODO
- }
-
- if((actor.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo)
- return;
+ if ((actor.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo) return;
// if this weapon is reloadable, decrease its load. Else decrease the player's ammo
- if(wep.reloading_ammo)
+ if (wep.reloading_ammo)
{
actor.clip_load -= ammo_use;
actor.(weapon_load[actor.weapon]) = actor.clip_load;
}
- else if(wep.ammo_field != ammo_none)
+ else if (wep.ammo_field != ammo_none)
{
actor.(wep.ammo_field) -= ammo_use;
- if(actor.(wep.ammo_field) < 0)
+ if (actor.(wep.ammo_field) < 0)
{
backtrace(sprintf(
"W_DecreaseAmmo(%.2f): '%s' subtracted too much %s from '%s', resulting with '%.2f' left... "
GetAmmoPicture(wep.ammo_field),
actor.netname,
actor.(wep.ammo_field)
- ));
+ ));
}
}
}
.float reload_complain;
.string reload_sound;
-void W_ReloadedAndReady(Weapon thiswep, entity actor, bool fire1, bool fire2)
+void W_ReloadedAndReady(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
// finish the reloading process, and do the ammo transfer
- actor.clip_load = actor.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
+ actor.clip_load = actor.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
// if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load
- if(!actor.reload_ammo_min || actor.items & IT_UNLIMITED_WEAPON_AMMO || actor.ammo_field == ammo_none)
+ if (!actor.reload_ammo_min || actor.items & IT_UNLIMITED_WEAPON_AMMO || actor.ammo_field == ammo_none)
+ {
actor.clip_load = actor.reload_ammo_amount;
+ }
else
{
// make sure we don't add more ammo than we have
float load = min(actor.reload_ammo_amount - actor.clip_load, actor.(actor.ammo_field));
- actor.clip_load += load;
- actor.(actor.ammo_field) -= load;
+ actor.clip_load += load;
+ actor.(actor.ammo_field) -= load;
}
actor.(weapon_load[actor.weapon]) = actor.clip_load;
// then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
// so your weapon is disabled for a few seconds without reason
- //ATTACK_FINISHED(actor) -= actor.reload_time - 1;
+ // ATTACK_FINISHED(actor, slot) -= actor.reload_time - 1;
- Weapon wpn = get_weaponinfo(actor.weapon);
- w_ready(wpn, actor, actor.BUTTON_ATCK, actor.BUTTON_ATCK2);
+ Weapon wpn = Weapons_from(actor.weapon);
+ w_ready(wpn, actor, weaponentity, (actor.BUTTON_ATCK ? 1 : 0) | (actor.BUTTON_ATCK2 ? 2 : 0));
}
void W_Reload(entity actor, float sent_ammo_min, string sent_sound)
{
+ .entity weaponentity = weaponentities[0];
// set global values to work with
- entity e;
- e = get_weaponinfo(actor.weapon);
+ entity e = Weapons_from(actor.weapon);
- if(cvar("g_overkill"))
- if(actor.ok_use_ammocharge)
- return; // TODO
+ if (MUTATOR_CALLHOOK(W_Reload, actor)) return;
actor.reload_ammo_min = sent_ammo_min;
actor.reload_ammo_amount = e.reloading_ammo;
// don't reload weapons that don't have the RELOADABLE flag
if (!(e.spawnflags & WEP_FLAG_RELOADABLE))
{
- LOG_TRACE("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
+ LOG_TRACE(
+ "Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
return;
}
// return if reloading is disabled for this weapon
- if(!actor.reload_ammo_amount)
- return;
+ if (!actor.reload_ammo_amount) return;
// our weapon is fully loaded, no need to reload
- if (actor.clip_load >= actor.reload_ammo_amount)
- return;
+ if (actor.clip_load >= actor.reload_ammo_amount) return;
// no ammo, so nothing to load
- if(actor.ammo_field != ammo_none)
- if(!actor.(actor.ammo_field) && actor.reload_ammo_min)
- if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
+ if (actor.ammo_field != ammo_none)
{
- if(IS_REAL_CLIENT(actor) && actor.reload_complain < time)
+ if (!actor.(actor.ammo_field) && actor.reload_ammo_min)
{
- play2(actor, SND(UNAVAILABLE));
- sprint(actor, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(actor.weapon), "\n"));
- actor.reload_complain = time + 1;
- }
- // switch away if the amount of ammo is not enough to keep using this weapon
- Weapon w = get_weaponinfo(actor.weapon);
- if (!(w.wr_checkammo1(w) + w.wr_checkammo2(w)))
- {
- actor.clip_load = -1; // reload later
- W_SwitchToOtherWeapon(actor);
+ if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
+ {
+ if (IS_REAL_CLIENT(actor) && actor.reload_complain < time)
+ {
+ play2(actor, SND(UNAVAILABLE));
+ sprint(actor, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(actor.weapon), "\n"));
+ actor.reload_complain = time + 1;
+ }
+ // switch away if the amount of ammo is not enough to keep using this weapon
+ Weapon w = Weapons_from(actor.weapon);
+ if (!(w.wr_checkammo1(w) + w.wr_checkammo2(w)))
+ {
+ actor.clip_load = -1; // reload later
+ W_SwitchToOtherWeapon(actor);
+ }
+ return;
+ }
}
- return;
}
- if (actor.weaponentity)
+ entity this = actor.(weaponentity);
+ if (this)
{
- if (actor.weaponentity.wframe == WFRAME_RELOAD)
- return;
+ if (this.wframe == WFRAME_RELOAD) return;
// allow switching away while reloading, but this will cause a new reload!
- actor.weaponentity.state = WS_READY;
+ this.state = WS_READY;
}
// now begin the reloading process
// then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
// so your weapon is disabled for a few seconds without reason
- //ATTACK_FINISHED(actor) = max(time, ATTACK_FINISHED(actor)) + actor.reload_time + 1;
+ // ATTACK_FINISHED(actor, slot) = max(time, ATTACK_FINISHED(actor, slot)) + actor.reload_time + 1;
- weapon_thinkf(actor, WFRAME_RELOAD, actor.reload_time, W_ReloadedAndReady);
+ weapon_thinkf(actor, weaponentity, WFRAME_RELOAD, actor.reload_time, W_ReloadedAndReady);
- if(actor.clip_load < 0)
- actor.clip_load = 0;
+ if (actor.clip_load < 0) actor.clip_load = 0;
actor.old_clip_load = actor.clip_load;
actor.clip_load = actor.(weapon_load[actor.weapon]) = -1;
}
void W_DropEvent(.void(Weapon) event, entity player, float weapon_type, entity weapon_item)
-{SELFPARAM();
- setself(player);
+{
+ Weapon w = Weapons_from(weapon_type);
weapon_dropevent_item = weapon_item;
- Weapon w = get_weaponinfo(weapon_type);
- w.event(w);
- setself(this);
+ WITH(entity, self, player, w.event(w));
}
vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn);
-void CL_SpawnWeaponentity(entity e);
+void CL_SpawnWeaponentity(entity e, .entity weaponentity);
vector CL_Weapon_GetShotOrg(float wpn);
float W_WeaponSpeedFactor();
-bool weapon_prepareattack(Weapon thiswep, entity actor, bool secondary, float attacktime);
+bool weapon_prepareattack(Weapon thiswep, entity actor, .entity weaponentity, bool secondary, float attacktime);
-bool weapon_prepareattack_check(Weapon thiswep, entity actor, float secondary, float attacktime);
+bool weapon_prepareattack_check(Weapon thiswep, entity actor, .entity weaponentity, float secondary, float attacktime);
-void weapon_prepareattack_do(entity actor, float secondary, float attacktime);
+void weapon_prepareattack_do(entity actor, .entity weaponentity, float secondary, float attacktime);
-void weapon_thinkf(entity actor, float fr, float t, void(Weapon thiswep, entity actor, bool fire1, bool fire2) func);
+void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(Weapon thiswep, entity actor, .entity weaponentity, int fire) func);
#endif
#
# Try to limit code width to N number of columns
-code_width = 0 # number
+code_width = 120 # number
# Whether to fully split long 'for' statements at semi-colons
# WARNING: Code doesn't seem to use this feature - delete from the config?