- wget -O data/maps/g-23.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.cache
- wget -O data/maps/g-23.waypoints.hardwired https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.hardwired
- make
- - EXPECT=f17c2b4e7a8619ff77983de267669802
+ - EXPECT=0a9ea83e32e148da989cbbadc7421ea0
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
set g_multijump 0 "Number of multiple jumps to allow (jumping again in the air), -1 allows for infinite jumps"
set g_multijump_add 0 "0 = make the current z velocity equal to jumpvelocity, 1 = add jumpvelocity to the current z velocity"
set g_multijump_speed -999999 "Minimum vertical speed a player must have in order to jump again"
+
+
+// ===========
+// wall jump
+// ===========
+set g_walljump 0 "Enable wall jumping mutator"
+set g_walljump_delay 1 "Minimum delay between wall jumps"
+set g_walljump_force 300 "How far to bounce/jump off the wall"
+set g_walljump_velocity_xy_factor 1.15 "How much to slow down along horizontal axis, higher value = higher deceleration, if factor is < 1, you accelerate by wall jumping"
+set g_walljump_velocity_z_factor 0.5 "Upwards velocity factor, multiplied by normal jump velocity"
\ No newline at end of file
// Samual: 31 (just below 32, keeping things smooth without allowing 32qu steps)
// jump duration == 2*sv_jumpvelocity / sv_gravity
-// in this case: 0.6888888888 (thus either 20 or 21 frames)
+// in this case: 0.65 (thus either 19 or 20 frames)
// jump height == sv_jumpvelocity^2 / (2*sv_gravity)
// in this case: 42.25
// player: 24+45 qu
gren.damageforcescale = 0;
gren.event_damage = M_Shambler_Attack_Lightning_Damage;
gren.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, gren);
gren.missile_flags = MIF_SPLASH | MIF_ARC;
W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, false);
IL_PUSH(g_projectiles, proj);
IL_PUSH(g_bot_dodge, proj);
proj.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, proj);
proj.bouncefactor = 0.3;
proj.bouncestop = 0.05;
e.noalign = this.noalign;
e.angles = this.angles;
e.monster_skill = this.monster_skill;
- e = spawnmonster(e, this.spawnmob, 0, this, this, this.origin, false, false, this.monster_moveflags);
+ e = spawnmonster(e, this.spawnmob, 0, this, this, this.origin, false, true, this.monster_moveflags);
}
spawnfunc(monster_spawner)
this.bot_attack = true;
this.iscreature = true;
this.teleportable = true;
+ if(!this.damagedbycontents)
+ IL_PUSH(g_damagedbycontents, this);
this.damagedbycontents = true;
this.monsterid = mon_id;
this.event_damage = Monster_Damage;
#include <server/autocvars.qh>
#include <server/defs.qh>
#endif
-entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby, entity own, vector orig, bool respwn, bool invincible, int moveflag)
+entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby, entity own, vector orig, bool respwn, bool removeifinvalid, int moveflag)
{
e.spawnflags = MONSTERFLAG_SPAWNED;
if(!respwn) { e.spawnflags |= MONSTERFLAG_NORESPAWN; }
- if(invincible) { e.spawnflags |= MONSTERFLAG_INVINCIBLE; }
+ //if(invincible) { e.spawnflags |= MONSTERFLAG_INVINCIBLE; }
setorigin(e, orig);
break;
}
});
- if(!found)
- monster_id = ((monster_id > 0) ? monster_id : MON_FIRST);
+
+ if(!found && !monster_id)
+ {
+ if(removeifinvalid)
+ {
+ delete(e);
+ return NULL; // no good
+ }
+ else
+ monster_id = MON_FIRST;
+ }
}
e.realowner = spawnedby;
#pragma once
-entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby, entity own, vector orig, bool respwn, bool invincible, int moveflag);
+entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby, entity own, vector orig, bool respwn, bool removeifinvalid, int moveflag);
#include <common/mutators/mutator/touchexplode/_mod.inc>
#include <common/mutators/mutator/vampire/_mod.inc>
#include <common/mutators/mutator/vampirehook/_mod.inc>
+#include <common/mutators/mutator/walljump/_mod.inc>
#include <common/mutators/mutator/waypoints/_mod.inc>
#include <common/mutators/mutator/weaponarena_random/_mod.inc>
#include <common/mutators/mutator/touchexplode/_mod.qh>
#include <common/mutators/mutator/vampire/_mod.qh>
#include <common/mutators/mutator/vampirehook/_mod.qh>
+#include <common/mutators/mutator/walljump/_mod.qh>
#include <common/mutators/mutator/waypoints/_mod.qh>
#include <common/mutators/mutator/weaponarena_random/_mod.qh>
#include "damagetext.qh"
+#define DAMAGETEXT_PRECISION_MULTIPLIER 128
+#define DAMAGETEXT_SHORT_LIMIT 256 // the smallest value that we can't send as short - 2^15 (signed short) / DAMAGETEXT_PRECISION_MULTIPLIER
+
REGISTER_MUTATOR(damagetext, true);
#if defined(CSQC) || defined(MENUQC)
// no translatable cvar description please
-AUTOCVAR_SAVE(cl_damagetext, bool, true, "Draw damage dealt where you hit the enemy");
-AUTOCVAR_SAVE(cl_damagetext_format, string, "-{total}", "How to format the damage text. {health}, {armor}, {total}");
+AUTOCVAR_SAVE(cl_damagetext, bool, true, "Draw damage dealt where you hit the enemy");
+AUTOCVAR_SAVE(cl_damagetext_format, string, "-{total}", "How to format the damage text. {health}, {armor}, {total}");
STATIC_INIT(DamageText_LegacyFormat) {
if (strstrofs(autocvar_cl_damagetext_format, "{", 0) < 0) autocvar_cl_damagetext_format = "-{total}";
}
-AUTOCVAR_SAVE(cl_damagetext_color, vector, '1 1 0', "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");
-AUTOCVAR_SAVE(cl_damagetext_friendlyfire, bool, true, "Show damage text for friendlyfire too");
-AUTOCVAR_SAVE(cl_damagetext_friendlyfire_color, vector, '1 0 0', "Damage text color for friendlyfire");
+AUTOCVAR_SAVE(cl_damagetext_color, vector, '1 1 0', "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");
+AUTOCVAR_SAVE(cl_damagetext_accumulate_alpha_rel, float, 0.65, "Only update existing damage text when it's above this much percentage (0 to 1) of the starting alpha");
+AUTOCVAR_SAVE(cl_damagetext_friendlyfire, bool, true, "Show damage text for friendlyfire too");
+AUTOCVAR_SAVE(cl_damagetext_friendlyfire_color, vector, '1 0 0', "Damage text color for friendlyfire");
#endif
#ifdef CSQC
if (w != WEP_Null) rgb = w.wpcolor;
}
string s = autocvar_cl_damagetext_format;
- s = strreplace("{health}", sprintf("%d", this.m_damage), s);
- s = strreplace("{armor}", sprintf("%d", this.m_armordamage), s);
- s = strreplace("{total}", sprintf("%d", this.m_damage + this.m_armordamage), s);
+ s = strreplace("{health}", sprintf("%d", rint(this.m_damage / DAMAGETEXT_PRECISION_MULTIPLIER)), s);
+ s = strreplace("{armor}", sprintf("%d", rint(this.m_armordamage / DAMAGETEXT_PRECISION_MULTIPLIER)), s);
+ s = strreplace("{total}", sprintf("%d", rint((this.m_damage + this.m_armordamage) / DAMAGETEXT_PRECISION_MULTIPLIER)), s);
drawcolorcodedstring2_builtin(pos, s, this.m_size * '1 1 0', rgb, this.alpha, DRAWFLAG_NORMAL);
}
}
this.m_armordamage = _armor;
this.m_deathtype = _deathtype;
setorigin(this, _origin);
- this.alpha = 1;
+ this.alpha = autocvar_cl_damagetext_alpha_start;
}
CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor, int _deathtype, bool _friendlyfire) {
if (SV_DAMAGETEXT_DISABLED()) return;
const entity attacker = M_ARGV(0, entity);
const entity hit = M_ARGV(1, entity); if (hit == attacker) return;
- const int health = M_ARGV(2, int);
- const int armor = M_ARGV(3, int);
+ const float health = M_ARGV(2, float);
+ const float armor = M_ARGV(3, float);
const int deathtype = M_ARGV(5, int);
const vector location = hit.origin;
FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
(SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(it) && it.enemy == attacker) ||
(SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(it))
) {
+ int flags = SAME_TEAM(hit, attacker); // BIT(0)
+ if (health >= DAMAGETEXT_SHORT_LIMIT) flags |= BIT(1);
+ if (armor >= DAMAGETEXT_SHORT_LIMIT) flags |= BIT(2);
+
msg_entity = it;
WriteHeader(MSG_ONE, damagetext);
- WriteShort(MSG_ONE, rint(health));
- WriteShort(MSG_ONE, rint(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);
- WriteByte(MSG_ONE, SAME_TEAM(hit, attacker));
+ WriteByte(MSG_ONE, flags);
+
+ // we need to send a few decimal places to minimize errors when accumulating damage
+ // sending them multiplied saves bandwidth compared to using WriteCoord,
+ // however if the multiplied damage would be too much for (signed) short, we send an int24
+ if (health >= DAMAGETEXT_SHORT_LIMIT) WriteInt24_t(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER);
+ else WriteShort(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER);
+ if (armor >= DAMAGETEXT_SHORT_LIMIT) WriteInt24_t(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
+ else WriteShort(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER);
}
));
}
#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();
- bool friendlyfire = ReadByte();
+ int flags = ReadByte();
+ bool friendlyfire = flags & 1;
+
+ int health, armor;
+ if (flags & BIT(1)) health = ReadInt24_t();
+ else health = ReadShort();
+ if (flags & BIT(2)) armor = ReadInt24_t();
+ else armor = ReadShort();
+
return = true;
if (autocvar_cl_damagetext) {
if (friendlyfire && !autocvar_cl_damagetext_friendlyfire) {
}
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) {
+ if (e.instanceOfDamageText && e.m_group == group && e.alpha > autocvar_cl_damagetext_accumulate_alpha_rel * autocvar_cl_damagetext_alpha_start) {
DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor, deathtype);
return;
}
_nade.gravity = 1;
_nade.missile_flags = MIF_SPLASH | MIF_ARC;
_nade.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, _nade);
_nade.angles = vectoangles(_nade.velocity);
_nade.flags = FL_PROJECTILE;
IL_PUSH(g_projectiles, _nade);
missile.health = WEP_CVAR(rpc, health);
missile.event_damage = W_RocketPropelledChainsaw_Damage;
missile.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, missile);
set_movetype(missile, MOVETYPE_FLY);
missile.projectiledeathtype = WEP_RPC.m_id;
--- /dev/null
+// generated file; do not modify
+#ifdef GAMEQC
+ #include <common/mutators/mutator/walljump/walljump.qc>
+#endif
--- /dev/null
+// generated file; do not modify
+#ifdef GAMEQC
+ #include <common/mutators/mutator/walljump/walljump.qh>
+#endif
--- /dev/null
+#include "walljump.qh"
+
+#ifdef CSQC
+REGISTER_MUTATOR(walljump, true);
+#elif defined(SVQC)
+REGISTER_MUTATOR(walljump, cvar("g_walljump"));
+#endif
+
+#define PHYS_WALLJUMP(s) STAT(WALLJUMP, s)
+#define PHYS_WALLJUMP_VELOCITY_Z_FACTOR(s) STAT(WALLJUMP_VELOCITY_Z_FACTOR, s)
+#define PHYS_WALLJUMP_VELOCITY_XY_FACTOR(s) STAT(WALLJUMP_VELOCITY_XY_FACTOR, s)
+#define PHYS_WALLJUMP_DELAY(s) STAT(WALLJUMP_DELAY, s)
+#define PHYS_WALLJUMP_FORCE(s) STAT(WALLJUMP_FORCE, s)
+
+vector PlayerTouchWall(entity this)
+{
+#define TRACE(newvec) \
+ tracebox (start, this.mins, this.maxs, (newvec), true, this); \
+ if (trace_fraction < 1 && vdist(this.origin - trace_endpos, <, dist) && trace_plane_normal_z < max_normal) \
+ if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) \
+ return trace_plane_normal;
+
+ float dist = 10, max_normal = 0.2, scaler = 100;
+ vector start = this.origin;
+ TRACE(start + v_forward * scaler)
+ TRACE(start - v_forward * scaler)
+ TRACE(start + v_right * scaler)
+ TRACE(start - v_right * scaler)
+#undef TRACE
+ return '0 0 0';
+}
+
+MUTATOR_HOOKFUNCTION(walljump, PlayerJump)
+{
+ entity player = M_ARGV(0, entity);
+
+ if(PHYS_WALLJUMP(player))
+ if(time - STAT(LASTWJ, player) > PHYS_WALLJUMP_DELAY(player)) // can't do this on client, as it's too stupid to obey counters
+ if(!IS_ONGROUND(player))
+ if(player.move_movetype != MOVETYPE_NONE && player.move_movetype != MOVETYPE_FOLLOW && player.move_movetype != MOVETYPE_FLY && player.move_movetype != MOVETYPE_NOCLIP)
+ if(!IS_JUMP_HELD(player))
+ if(!STAT(FROZEN, player))
+ if(!IS_DEAD(player))
+ {
+ vector plane_normal = PlayerTouchWall(player);
+
+ if(plane_normal != '0 0 0')
+ {
+ float wj_force = PHYS_WALLJUMP_FORCE(player);
+ float wj_xy_factor = PHYS_WALLJUMP_VELOCITY_XY_FACTOR(player);
+ float wj_z_factor = PHYS_WALLJUMP_VELOCITY_Z_FACTOR(player);
+ player.velocity_x += plane_normal_x * wj_force;
+ player.velocity_x /= wj_xy_factor;
+ player.velocity_y += plane_normal_y * wj_force;
+ player.velocity_y /= wj_xy_factor;
+ player.velocity_z = PHYS_JUMPVELOCITY(player) * wj_z_factor;
+ if(PHYS_INPUT_BUTTON_CROUCH(player)) player.velocity_z *= -1;
+
+#ifdef SVQC
+ STAT(LASTWJ, player) = time;
+ player.oldvelocity = player.velocity;
+ Send_Effect(EFFECT_SMOKE_RING, trace_endpos, plane_normal, 5);
+ PlayerSound(player, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
+ animdecide_setaction(player, ANIMACTION_JUMP, true);
+#endif
+
+ M_ARGV(2, bool) = true; // multijump
+ }
+ }
+}
--- /dev/null
+#pragma once
REGISTER_STAT(MOVEVARS_HIGHSPEED, float, autocvar_g_movement_highspeed)
+#ifdef SVQC
+AUTOCVAR(g_walljump, bool, false, "Enable wall jumping mutator");
+AUTOCVAR(g_walljump_delay, float, 1, "Minimum delay between wall jumps");
+AUTOCVAR(g_walljump_force, float, 300, "How far to bounce/jump off the wall");
+AUTOCVAR(g_walljump_velocity_xy_factor, float, 1.15, "How much to slow down along horizontal axis, higher value = higher deceleration, if factor is < 1, you accelerate by wall jumping");
+AUTOCVAR(g_walljump_velocity_z_factor, float, 0.5, "Upwards velocity factor, multiplied by normal jump velocity");
+#endif
+REGISTER_STAT(WALLJUMP, int, autocvar_g_walljump)
+REGISTER_STAT(WALLJUMP_VELOCITY_Z_FACTOR, float, autocvar_g_walljump_velocity_z_factor)
+REGISTER_STAT(WALLJUMP_VELOCITY_XY_FACTOR, float, autocvar_g_walljump_velocity_xy_factor)
+REGISTER_STAT(WALLJUMP_DELAY, float, autocvar_g_walljump_delay)
+REGISTER_STAT(WALLJUMP_FORCE, float, autocvar_g_walljump_force)
+REGISTER_STAT(LASTWJ, float)
+
// freeze tag, clan arena
REGISTER_STAT(REDALIVE, int)
REGISTER_STAT(BLUEALIVE, int)
this.origin_z = floorZ;
}
_setmodel(this, this.mdl_dead);
+ ApplyMinMaxScaleAngles(this);
this.effects &= ~EF_NODRAW;
}
void func_breakable_look_restore(entity this)
{
_setmodel(this, this.mdl);
+ ApplyMinMaxScaleAngles(this);
this.effects &= ~EF_NODRAW;
if(this.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow
it.iscreature = true;
it.teleportable = TELEPORT_NORMAL;
it.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, it);
set_movetype(it, MOVETYPE_WALK);
it.solid = SOLID_SLIDEBOX;
it.takedamage = DAMAGE_AIM;
it.iscreature = true;
it.teleportable = TELEPORT_NORMAL;
it.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, it);
it.solid = SOLID_SLIDEBOX;
it.takedamage = DAMAGE_AIM;
if(it.move_movetype != MOVETYPE_WALK)
void vehicles_enter(entity pl, entity veh)
{
- // Remove this when bots know how to use vehicles
+ // Remove this when bots know how to use vehicles
if((IS_BOT_CLIENT(pl) && !autocvar_g_vehicles_allow_bots))
return;
this.iscreature = true;
this.teleportable = false; // no teleporting for vehicles, too buggy
this.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, this);
this.vehicleid = info.vehicleid;
this.PlayerPhysplug = info.PlayerPhysplug;
this.event_damage = func_null;
else
this.nextthink = time + game_starttime;
- if(!MUTATOR_CALLHOOK(VehicleInit, this))
+ if(MUTATOR_CALLHOOK(VehicleInit, this))
return false;
return true;
missile.damageforcescale = WEP_CVAR(arc, bolt_damageforcescale);
missile.event_damage = W_Arc_Bolt_Damage;
missile.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, missile);
settouch(missile, W_Arc_Bolt_Touch);
missile.use = W_Arc_Bolt_Explode_use;
missile.health = WEP_CVAR(devastator, health);
missile.event_damage = W_Devastator_Damage;
missile.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, missile);
set_movetype(missile, MOVETYPE_FLY);
PROJECTILE_MAKETRIGGER(missile);
newproj.event_damage = this.event_damage;
newproj.spawnshieldtime = this.spawnshieldtime;
newproj.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, newproj);
set_movetype(newproj, MOVETYPE_NONE); // lock the orb in place
newproj.projectiledeathtype = this.projectiledeathtype;
IL_PUSH(g_projectiles, proj);
IL_PUSH(g_bot_dodge, proj);
proj.damagedbycontents = (WEP_CVAR_SEC(electro, damagedbycontents));
+ if(proj.damagedbycontents)
+ IL_PUSH(g_damagedbycontents, proj);
proj.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor);
proj.bouncestop = WEP_CVAR_SEC(electro, bouncestop);
missile.damageforcescale = WEP_CVAR_PRI(hagar, damageforcescale);
missile.event_damage = W_Hagar_Damage;
missile.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, missile);
settouch(missile, W_Hagar_Touch);
missile.use = W_Hagar_Explode_use;
missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
missile.event_damage = W_Hagar_Damage;
missile.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, missile);
settouch(missile, W_Hagar_Touch2);
missile.cnt = 0;
missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
missile.event_damage = W_Hagar_Damage;
missile.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, missile);
settouch(missile, W_Hagar_Touch); // not bouncy
missile.use = W_Hagar_Explode2_use;
gren.damageforcescale = WEP_CVAR_SEC(hook, damageforcescale);
gren.event_damage = W_Hook_Damage;
gren.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, gren);
gren.missile_flags = MIF_SPLASH | MIF_ARC;
gren.velocity = '0 0 1' * WEP_CVAR_SEC(hook, speed);
newmine.event_damage = this.event_damage;
newmine.spawnshieldtime = this.spawnshieldtime;
newmine.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, newmine);
set_movetype(newmine, MOVETYPE_NONE); // lock the mine in place
newmine.projectiledeathtype = this.projectiledeathtype;
mine.health = WEP_CVAR(minelayer, health);
mine.event_damage = W_MineLayer_Damage;
mine.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, mine);
set_movetype(mine, MOVETYPE_TOSS);
PROJECTILE_MAKETRIGGER(mine);
gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale);
gren.event_damage = W_Mortar_Grenade_Damage;
gren.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, gren);
gren.missile_flags = MIF_SPLASH | MIF_ARC;
W_SetupProjVelocity_UP_PRI(gren, mortar);
gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale);
gren.event_damage = W_Mortar_Grenade_Damage;
gren.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, gren);
gren.missile_flags = MIF_SPLASH | MIF_ARC;
W_SetupProjVelocity_UP_SEC(gren, mortar);
missile.health = WEP_CVAR(seeker, missile_health);
missile.damageforcescale = WEP_CVAR(seeker, missile_damageforcescale);
missile.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, missile);
//missile.think = W_Seeker_Missile_Animate; // csqc projectiles.
if(missile.enemy != NULL)
}
this.classname = "trigger_warpzone";
+ if(isnew)
+ IL_PUSH(g_warpzones, this);
+
int f = ReadByte();
this.warpzone_isboxy = (f & 1);
if(f & 4)
entity WarpZone_Find(vector mi, vector ma)
{
// if we are near any warpzone planes - MOVE AWAY (work around nearclip)
- entity e;
if(!warpzone_warpzones_exist)
return NULL;
- for(e = NULL; (e = find(e, classname, "trigger_warpzone")); )
- if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, NULL))
- return e;
+ IL_EACH(g_warpzones, WarpZoneLib_BoxTouchesBrush(mi, ma, it, NULL),
+ {
+ return it;
+ });
return NULL;
}
void WarpZone_MakeAllSolid()
{
- entity e;
if(!warpzone_warpzones_exist)
return;
- for(e = NULL; (e = find(e, classname, "trigger_warpzone")); )
- e.solid = SOLID_BSP;
+ IL_EACH(g_warpzones, true,
+ {
+ it.solid = SOLID_BSP;
+ });
}
void WarpZone_MakeAllOther()
{
- entity e;
if(!warpzone_warpzones_exist)
return;
- for(e = NULL; (e = find(e, classname, "trigger_warpzone")); )
- e.solid = SOLID_TRIGGER;
+ IL_EACH(g_warpzones, true,
+ {
+ it.solid = SOLID_TRIGGER;
+ });
}
void WarpZone_Trace_InitTransform()
// uncomment this if your mod uses the roll angle in fixangle
// #define KEEP_ROLL
+IntrusiveList g_warpzones;
+STATIC_INIT(g_warpzones) { g_warpzones = IL_NEW(); }
+
float warpzone_warpzones_exist;
float warpzone_cameras_exist;
this.enemy = NULL;
}
-entity warpzone_first; .entity warpzone_next;
void WarpZone_InitStep_FindTarget(entity this)
{
float i;
BITSET_ASSIGN(this.effects, EF_NODEPTHTEST);
this.warpzone_next = warpzone_first;
warpzone_first = this;
+
+ IL_PUSH(g_warpzones, this);
}
spawnfunc(func_camera)
{
WarpZone_PostInitialize_Callback();
}
- FOREACH_ENTITY_FLOAT(pure_data, false,
+ if(warpzone_warpzones_exist)
{
- if(warpzone_warpzones_exist)
+ IL_EACH(g_projectiles, true,
+ {
WarpZone_StoreProjectileData(it);
+ });
+ }
+
+
+ FOREACH_CLIENT(true,
+ {
+ if(warpzone_warpzones_exist)
+ WarpZone_StoreProjectileData(it); // TODO: not actually needed
if(IS_OBSERVER(it) || it.solid == SOLID_NOT)
if(IS_CLIENT(it)) // we don't care about it being a bot
#pragma once
#ifdef SVQC
+entity warpzone_first; .entity warpzone_next;
+
void WarpZone_StartFrame();
float WarpZone_Projectile_Touch(entity this, entity toucher);
me.TR(me);
me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "cl_forceplayermodels", _("Force player models to mine")));
me.TR(me);
- me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "cl_forceplayercolors", _("Force player colors to mine")));
+ me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Force player colors to mine")));
+ me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_forceplayercolors"));
+ e.addValue(e, _("Never"), "0");
+ e.addValue(e, _("In non teamplay modes only"), "1");
+ e.addValue(e, _("Always"), "2");
+ e.configureXonoticTextSliderValues(e);
me.TR(me);
me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Body fading:")));
me.TD(me, 1, 2, e = makeXonoticSlider(0, 2, 0.2, "cl_deathglow"));
TRANSMUTE(Observer, this);
this.iscreature = false;
this.teleportable = TELEPORT_SIMPLE;
+ if(this.damagedbycontents)
+ IL_REMOVE(g_damagedbycontents, this);
this.damagedbycontents = false;
this.health = FRAGS_SPECTATOR;
SetSpectatee_status(this, etof(this));
this.wasplayer = true;
this.iscreature = true;
this.teleportable = TELEPORT_NORMAL;
+ if(!this.damagedbycontents)
+ IL_PUSH(g_damagedbycontents, this);
this.damagedbycontents = true;
set_movetype(this, MOVETYPE_WALK);
this.solid = SOLID_SLIDEBOX;
if (tmp_moncount >= autocvar_g_monsters_max_perplayer) { print_to(caller, "You can't spawn any more monsters"); return; }
bool found = false;
- for (int i = MON_FIRST; i <= MON_LAST; ++i)
+ FOREACH(Monsters, it != MON_Null && it.netname == arg_lower,
{
- mon = get_monsterinfo(i);
- if (mon.netname == arg_lower) { found = true; break; }
- }
+ found = true;
+ break;
+ });
if (!found && arg_lower != "random") { print_to(caller, "Invalid monster"); return; }
IntrusiveList g_bot_dodge;
STATIC_INIT(g_bot_dodge) { g_bot_dodge = IL_NEW(); }
+
+IntrusiveList g_damagedbycontents;
+STATIC_INIT(g_damagedbycontents) { g_damagedbycontents = IL_NEW(); }
missile.takedamage = DAMAGE_AIM;
missile.damageforcescale = 0;
missile.damagedbycontents = (autocvar_g_balance_grapplehook_damagedbycontents);
+ if(missile.damagedbycontents)
+ IL_PUSH(g_damagedbycontents, missile);
missile.hook_start = missile.hook_end = missile.origin;
#define EV_PlayerDamaged(i, o) \
/** attacker */ i(entity, MUTATOR_ARGV_0_entity) \
/** target */ i(entity, MUTATOR_ARGV_1_entity) \
- /** health */ i(int, MUTATOR_ARGV_2_int) \
- /** armor */ i(int, MUTATOR_ARGV_3_int) \
+ /** health */ i(float, MUTATOR_ARGV_2_float) \
+ /** armor */ i(float, MUTATOR_ARGV_3_float) \
/** location */ i(vector, MUTATOR_ARGV_4_vector) \
/** deathtype */ i(int, MUTATOR_ARGV_5_int) \
/**/
/**
* called when a vehicle initializes
- * return false to remove the vehicle
+ * return true to remove the vehicle
*/
#define EV_VehicleInit(i, o) \
/** vehicle */ i(entity, MUTATOR_ARGV_0_entity) \
// trigger new round
// reset objectives, toggle spawnpoints, reset triggers, ...
-void vehicles_clearreturn(entity veh);
-void vehicles_spawn(entity this);
void assault_new_round(entity this)
{
//bprint("ASSAULT: new round\n");
{
entity veh = M_ARGV(0, entity);
- if(veh.active != ACTIVE_NOT)
- veh.nextthink = time + 0.5;
- return true;
+ veh.nextthink = time + 0.5;
}
MUTATOR_HOOKFUNCTION(as, HavocBot_ChooseRole)
flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP;
flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable;
flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable;
+ if(flag.damagedbycontents)
+ IL_PUSH(g_damagedbycontents, flag);
flag.velocity = '0 0 0';
flag.mangle = flag.angles;
flag.reset = ctf_Reset;
setsize(e, mon.mins, mon.maxs);
if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
- monster = spawnmonster(spawn(), "", mon.m_id, NULL, NULL, e.origin, false, false, 2);
+ monster = spawnmonster(e, "", mon.m_id, NULL, NULL, e.origin, false, false, 2);
else return;
-
- setthink(e, SUB_Remove);
- e.nextthink = time + 0.1;
}
else // if spawnmob field falls through (unset), fallback to mon (relying on spawnmonster for that behaviour)
monster = spawnmonster(spawn(), spawn_point.spawnmob, mon.m_id, spawn_point, spawn_point, spawn_point.origin, false, false, 2);
clone.iscreature = this.iscreature;
clone.teleportable = this.teleportable;
clone.damagedbycontents = this.damagedbycontents;
+ if(clone.damagedbycontents)
+ IL_PUSH(g_damagedbycontents, clone);
clone.angles = this.angles;
clone.v_angle = this.v_angle;
clone.avelocity = this.avelocity;
this.alpha = -1;
this.solid = SOLID_NOT; // restore later
this.takedamage = DAMAGE_NO; // restore later
+ if(this.damagedbycontents)
+ IL_REMOVE(g_damagedbycontents, this);
this.damagedbycontents = false;
}
}
void CreatureFrame_All()
{
- FOREACH_ENTITY_FLOAT(damagedbycontents, true, {
+ IL_EACH(g_damagedbycontents, it.damagedbycontents,
+ {
if (it.move_movetype == MOVETYPE_NOCLIP) continue;
CreatureFrame_Liquids(it);
CreatureFrame_FallDamage(it);
void WarpZone_PostInitialize_Callback()
{
// create waypoint links for warpzones
- entity e;
- for(e = NULL; (e = find(e, classname, "trigger_warpzone")); )
+ //for(entity e = warpzone_first; e; e = e.warpzone_next)
+ for(entity e = NULL; (e = find(e, classname, "trigger_warpzone")); )
{
vector src, dst;
src = (e.absmin + e.absmax) * 0.5;