set g_balance_machinegun_mode 1
set g_balance_machinegun_reload_ammo 30
set g_balance_machinegun_reload_time 1.5
-set g_balance_machinegun_solidpenetration 13.1
+set g_balance_machinegun_solidpenetration 63
set g_balance_machinegun_spread_add 0.012
set g_balance_machinegun_spread_max 0.05
set g_balance_machinegun_spread_min 0
set g_pickup_fuel_jetpack 100
set g_pickup_fuel_max 100
set g_pickup_armorsmall 5
-set g_pickup_armorsmall_max 20
+set g_pickup_armorsmall_max 100
set g_pickup_armorsmall_anyway 1
set g_pickup_armormedium 25
set g_pickup_armormedium_max 100
-// ================
-// Overkill Mode
-// ================
+// ==============
+// Overkill Mod
+// ==============
exec defaultXonotic.cfg
exec balance-overkill.cfg
// general gameplay
set g_overkill 1
+set g_respawn_ghosts 0
+
set g_nades 1
+set g_nades_nade_small 1
+set g_nades_spread 0
+set g_nades_nade_refire 10
+set g_nades_nade_newton_style 2
+
set g_dodging 1
set sv_dodging_wall_dodging 1
+
set g_spawn_near_teammate 1
set g_spawn_near_teammate_ignore_spawnpoint 1
+set g_spawnshieldtime 0.5
+set g_respawn_delay_forced 2
+
+set g_lms_start_armor 100
// waypoint editor enable
set g_waypointeditor 0
set g_waypointeditor_auto 0 "Automatically create waypoints for bots while playing; BEWARE, this currently creates too many of them"
+set g_waypointeditor_symmetrical 0 "Enable symmetrical editing of waypoints, useful in symmetrical CTF maps. NOTE: it assumes that the map is perfectly symmetrical"
+set g_waypointeditor_symmetrical_center "0 0" "Center (x y) for symmetrical editing of waypoints"
set bot_ignore_bots 0 "When set, bots don't shoot at other bots"
set bot_join_empty 0 "When set, bots also play if no player has joined the server"
set bot_vs_human 0 "Bots and humans play in different teams when set. positive values to make an all-bot blue team, set to negative values to make an all-bot red team, the absolute value is the ratio bots vs humans (1 for equal count). Changes will be correctly applied only from the next game"
set g_overkill 0 "enable overkill"
set g_overkill_powerups_replace 1
+set g_overkill_itemwaypoints 1
set g_overkill_filter_healthmega 0
set g_overkill_filter_armormedium 0
set g_overkill_filter_armorbig 0
set g_nades_nade_edgedamage 90
set g_nades_nade_radius 300
set g_nades_nade_force 650
-set g_nades_nade_newton_style 0
+set g_nades_nade_newton_style 0 "0 is absolute, 1 is relative (takes into account player speed), 2 is something in between"
set g_nades_nade_type 1 "Type of the off-hand grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade 8:entrap"
seta cl_nade_timer 1 "show a visual timer for nades, 1 = only circle, 2 = circle with text"
sv_warsowbunny_topspeed 925
sv_warsowbunny_backtosideratio 0.8
sv_friction_on_land 0
-sv_friction_slick 0.5
+sv_friction_slick 0
sv_doublejump 1
sv_jumpspeedcap_min 0
sv_jumpspeedcap_max 0.5
this.colormap = ReadShort();
else
this.colormap = 0;
+ this.skin = ReadByte();
}
if(f & 2)
#include "ent_cs.qh"
+REGISTRY(EntCSProps, BITS(16) - 1)
+#define EntCSProps_from(i) _EntCSProps_from(i, NULL)
+REGISTER_REGISTRY(EntCSProps)
+REGISTRY_SORT(EntCSProps)
+REGISTRY_CHECK(EntCSProps)
+STATIC_INIT(RegisterEntCSProps_renumber) { FOREACH(EntCSProps, true, it.m_id = i); }
+
+.bool m_public;
+.bool(entity ent, entity player) m_check;
+.void(entity ent, entity player) m_set;
+.void(int chan, entity ent) m_send;
+.void(entity ent) m_receive;
+
+#ifdef SVQC
+#define ENTCS_PROP(id, ispublic, checkprop, setprop, svsend, clreceive) \
+ bool id##_check(entity ent, entity player) { return (ent.(checkprop) != player.(checkprop)); } \
+ void id##_set(entity ent, entity player) { setprop(ent.(checkprop), player.(checkprop)); } \
+ void id##_send(int chan, entity ent) { LAMBDA(svsend); } \
+ REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \
+ this.m_public = ispublic; \
+ this.m_check = id##_check; \
+ this.m_set = id##_set; \
+ this.m_send = id##_send; \
+ }
+#elif defined(CSQC)
+#define ENTCS_PROP(id, ispublic, checkprop, setprop, svsend, clreceive) \
+ void id##_receive(entity ent) { LAMBDA(clreceive); } \
+ REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \
+ this.m_public = ispublic; \
+ this.m_receive = id##_receive; \
+ }
+#endif
+
#define ENTCS_SET_NORMAL(var, x) MACRO_BEGIN \
var = x; \
MACRO_END
var = strzone(x); \
MACRO_END
-// #define PROP(public, fld, set, sv, cl)
-#define ENTCS_NETPROPS(ent, PROP) PROP(false, sv_entnum, ENTCS_SET_NORMAL, {}, {}) /* sentinel */ \
- PROP(false, origin, ENTCS_SET_NORMAL, \
- { WriteCoord(chan, ent.origin.x); WriteCoord(chan, ent.origin.y); \
- WriteCoord(chan, ent.origin.z); }, \
- { ent.has_sv_origin = true; vector v; v.x = ReadCoord(); v.y = ReadCoord(); v.z = ReadCoord(); setorigin(ent, v); }) \
- \
- PROP(false, angles_y, ENTCS_SET_NORMAL, \
- { WriteByte(chan, ent.angles.y / 360 * 256); }, \
- { vector v = '0 0 0'; v.y = ReadByte() / 256 * 360; ent.angles = v; }) \
- \
- PROP(false, health, ENTCS_SET_NORMAL, \
- { WriteByte(chan, bound(0, ent.health / 10, 255)); /* FIXME: use a better scale? */ }, \
- { ent.healthvalue = ReadByte() * 10; }) \
- \
- PROP(false, armorvalue, ENTCS_SET_NORMAL, \
- { WriteByte(chan, bound(0, ent.armorvalue / 10, 255)); /* FIXME: use a better scale? */ }, \
- { ent.armorvalue = ReadByte() * 10; }) \
- \
- PROP(true, netname, ENTCS_SET_MUTABLE_STRING, \
- { WriteString(chan, ent.netname); }, \
- { if (ent.netname) strunzone(ent.netname); ent.netname = strzone(ReadString()); }) \
- \
- PROP(true, model, ENTCS_SET_NORMAL, \
- { WriteString(chan, ent.model); }, \
- { if (ent.model) strunzone(ent.model); ent.model = strzone(ReadString()); }) \
- \
- PROP(true, skin, ENTCS_SET_NORMAL, \
- { WriteByte(chan, ent.skin); }, \
- { ent.skin = ReadByte(); }) \
- \
- PROP(true, clientcolors, ENTCS_SET_NORMAL, \
- { WriteByte(chan, ent.clientcolors); }, \
- { ent.colormap = ReadByte(); }) \
- \
- PROP(true, frags, ENTCS_SET_NORMAL, \
- { WriteShort(chan, ent.frags); }, \
- { ent.frags = ReadShort(); }) \
- \
- /**/
+ENTCS_PROP(ENTNUM, false, sv_entnum, ENTCS_SET_NORMAL, {}, {}) /* sentinel */
+
+ENTCS_PROP(ORIGIN, false, origin, ENTCS_SET_NORMAL,
+ { WriteVector(chan, ent.origin); },
+ { ent.has_sv_origin = true; vector v = ReadVector(); setorigin(ent, v); })
+
+ENTCS_PROP(ANGLES, false, angles_y, ENTCS_SET_NORMAL,
+ { WriteByte(chan, ent.angles.y / 360 * 256); },
+ { vector v = '0 0 0'; v.y = ReadByte() / 256 * 360; ent.angles = v; })
+
+ENTCS_PROP(HEALTH, false, health, ENTCS_SET_NORMAL,
+ { WriteByte(chan, bound(0, ent.health / 10, 255)); /* FIXME: use a better scale? */ },
+ { ent.healthvalue = ReadByte() * 10; })
+
+ENTCS_PROP(ARMOR, false, armorvalue, ENTCS_SET_NORMAL,
+ { WriteByte(chan, bound(0, ent.armorvalue / 10, 255)); /* FIXME: use a better scale? */ },
+ { ent.armorvalue = ReadByte() * 10; })
+
+ENTCS_PROP(NAME, true, netname, ENTCS_SET_MUTABLE_STRING,
+ { WriteString(chan, ent.netname); },
+ { if (ent.netname) strunzone(ent.netname); ent.netname = strzone(ReadString()); })
+
+ENTCS_PROP(MODEL, true, model, ENTCS_SET_NORMAL,
+ { WriteString(chan, ent.model); },
+ { if (ent.model) strunzone(ent.model); ent.model = strzone(ReadString()); })
+
+ENTCS_PROP(SKIN, true, skin, ENTCS_SET_NORMAL,
+ { WriteByte(chan, ent.skin); },
+ { ent.skin = ReadByte(); })
+
+ENTCS_PROP(CLIENTCOLORS, true, clientcolors, ENTCS_SET_NORMAL,
+ { WriteByte(chan, ent.clientcolors); },
+ { ent.colormap = ReadByte(); })
+
+ENTCS_PROP(FRAGS, true, frags, ENTCS_SET_NORMAL,
+ { WriteShort(chan, ent.frags); },
+ { ent.frags = ReadShort(); })
#ifdef SVQC
int ENTCS_PUBLICMASK = 0;
STATIC_INIT(ENTCS_PUBLICMASK)
{
- int i = 0;
- #define X(public, fld, set, sv, cl) { \
- if (public) { \
- ENTCS_PUBLICMASK |= BIT(i); \
- } \
- i += 1; \
- }
- ENTCS_NETPROPS(this, X);
- #undef X
- if (i >= BITS(16 - 1)) LOG_FATAL("Exceeded ENTCS_NETPROPS limit");
+ FOREACH(EntCSProps, it.m_public,
+ {
+ ENTCS_PUBLICMASK |= BIT(it.m_id);
+ });
}
bool _entcs_send(entity this, entity to, int sf, int chan)
WriteHeader(chan, CLIENT_ENTCS);
WriteByte(chan, etof(player) - 1);
WriteShort(chan, sf);
- int i = 0;
- #define X(public, fld, set, sv, cl) { \
- if (sf & BIT(i)) { \
- sv; \
- } \
- i += 1; \
- }
- ENTCS_NETPROPS(this, X);
- #undef X
+ FOREACH(EntCSProps, sf & BIT(it.m_id),
+ {
+ it.m_send(chan, this);
+ });
return true;
}
{
this.nextthink = time + 0.033333333333; // TODO: increase this to like 0.15 once the client can do smoothing
entity o = this.owner;
- int i = 0;
- #define X(public, fld, set, sv, cl) { \
- if (o.fld != this.fld) { \
- set(this.fld, o.fld); \
- this.SendFlags |= BIT(i); \
- } \
- i += 1; \
- }
- ENTCS_NETPROPS(this, X);
- #undef X
+ FOREACH(EntCSProps, it.m_check(this, o),
+ {
+ it.m_set(this, o);
+ this.SendFlags |= BIT(it.m_id);
+ });
setorigin(this, this.origin); // relink
}
int sf = ReadShort();
e.has_sv_origin = false;
e.m_entcs_private = boolean(sf & BIT(0));
- int i = 0;
- #define X(public, fld, set, sv, cl) { \
- if (sf & BIT(i)) { \
- cl; \
- } \
- i += 1; \
- }
- ENTCS_NETPROPS(e, X);
- #undef X
+ FOREACH(EntCSProps, sf & BIT(it.m_id),
+ {
+ it.m_receive(e);
+ });
e.iflags |= IFLAG_ORIGIN;
InterpolateOrigin_Note(e);
getthink(e)(e);
player.buff_disability_effect_time = 0;
}
-.float stat_sv_maxspeed;
-.float stat_sv_airspeedlimit_nonqw;
-.float stat_sv_jumpvelocity;
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics)
+MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics_UpdateStats)
{
entity player = M_ARGV(0, entity);
+ // these automatically reset, no need to worry
if(player.buffs & BUFF_SPEED.m_itemid)
- {
- player.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed;
- player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed;
- }
+ STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_buffs_speed_speed;
if(time < player.buff_disability_time)
- {
- player.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed;
- player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed;
- }
-
- if(player.buffs & BUFF_JUMP.m_itemid)
- {
- // automatically reset, no need to worry
- player.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height;
- }
+ STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_buffs_disability_speed;
}
-MUTATOR_HOOKFUNCTION(buffs, PlayerJump)
+MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics)
{
entity player = M_ARGV(0, entity);
+ // these automatically reset, no need to worry
if(player.buffs & BUFF_JUMP.m_itemid)
- M_ARGV(1, float) = autocvar_g_buffs_jump_height;
+ STAT(MOVEVARS_JUMPVELOCITY, player) = autocvar_g_buffs_jump_height;
}
MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
bool autocvar_g_overkill_powerups_replace;
+bool autocvar_g_overkill_itemwaypoints = true;
+
bool autocvar_g_overkill_filter_healthmega;
bool autocvar_g_overkill_filter_armormedium;
bool autocvar_g_overkill_filter_armorbig;
}
}
+bool ok_HandleItemWaypoints(entity e)
+{
+ if(!autocvar_g_overkill_itemwaypoints)
+ return false; // don't handle it
+
+ switch(e.itemdef)
+ {
+ case ITEM_HealthMega: return true;
+ case ITEM_ArmorMedium: return true;
+ case ITEM_ArmorBig: return true;
+ case ITEM_ArmorMega: return true;
+ }
+
+ return false;
+}
+
+MUTATOR_HOOKFUNCTION(ok, Item_RespawnCountdown)
+{
+ entity item = M_ARGV(0, entity);
+ return ok_HandleItemWaypoints(item);
+}
+
+MUTATOR_HOOKFUNCTION(ok, Item_ScheduleRespawn)
+{
+ entity item = M_ARGV(0, entity);
+ return ok_HandleItemWaypoints(item);
+}
+
MUTATOR_HOOKFUNCTION(ok, FilterItem)
{
entity item = M_ARGV(0, entity);
REGISTER_STAT(JETPACK_MAXSPEED_UP, float, autocvar_g_jetpack_maxspeed_up)
REGISTER_STAT(JETPACK_REVERSE_THRUST, float, autocvar_g_jetpack_reverse_thrust)
-REGISTER_STAT(MOVEVARS_HIGHSPEED, float, autocvar_g_movement_highspeed)
+REGISTER_STAT(MOVEVARS_HIGHSPEED, float)
#ifdef SVQC
AUTOCVAR(g_walljump, bool, false, "Enable wall jumping mutator");
this.count += 1;
if(this.count == 1)
{
- MUTATOR_CALLHOOK(Item_RespawnCountdown, string_null, '0 0 0');
do {
{
entity wi = Weapons_from(this.weapon);
}
}
} while (0);
+ bool mutator_returnvalue = MUTATOR_CALLHOOK(Item_RespawnCountdown, this);
if(this.waypointsprite_attached)
{
GameItem def = this.itemdef;
- if (Item_ItemsTime_SpectatorOnly(def))
+ if (Item_ItemsTime_SpectatorOnly(def) && !mutator_returnvalue)
WaypointSprite_UpdateRule(this.waypointsprite_attached, 0, SPRITERULE_SPECTATOR);
WaypointSprite_UpdateBuildFinished(this.waypointsprite_attached, time + ITEM_RESPAWN_TICKS);
}
void Item_ScheduleRespawnIn(entity e, float t)
{
// if the respawn time is longer than 10 seconds, show a waypoint, otherwise, just respawn normally
- if ((Item_ItemsTime_Allow(e.itemdef) || (e.weapons & WEPSET_SUPERWEAPONS)) && (t - ITEM_RESPAWN_TICKS) > 0)
+ if ((Item_ItemsTime_Allow(e.itemdef) || (e.weapons & WEPSET_SUPERWEAPONS) || MUTATOR_CALLHOOK(Item_ScheduleRespawn, e, t)) && (t - ITEM_RESPAWN_TICKS) > 0)
{
setthink(e, Item_RespawnCountdown);
e.nextthink = time + max(0, t - ITEM_RESPAWN_TICKS);
e.scheduledrespawntime = e.nextthink + ITEM_RESPAWN_TICKS;
e.count = 0;
- t = Item_ItemsTime_UpdateTime(e, e.scheduledrespawntime);
- Item_ItemsTime_SetTime(e, t);
- Item_ItemsTime_SetTimesForAllPlayers();
+ if(Item_ItemsTime_Allow(e.itemdef) || (e.weapons & WEPSET_SUPERWEAPONS))
+ {
+ t = Item_ItemsTime_UpdateTime(e, e.scheduledrespawntime);
+ Item_ItemsTime_SetTime(e, t);
+ Item_ItemsTime_SetTimesForAllPlayers();
+ }
}
else
{
const float ITEM_RESPAWN_TICKS = 10;
-#define ITEM_RESPAWNTIME(i) ((i).respawntime + (crandom() * (i).respawntimejitter))
+#define ITEM_RESPAWNTIME(i) ((i).respawntime + crandom() * (i).respawntimejitter)
// range: respawntime - respawntimejitter .. respawntime + respawntimejitter
-#define ITEM_RESPAWNTIME_INITIAL(i) (ITEM_RESPAWN_TICKS + (random() * ((i).respawntime + (i).respawntimejitter - ITEM_RESPAWN_TICKS)))
+#define ITEM_RESPAWNTIME_INITIAL(i) (ITEM_RESPAWN_TICKS + random() * ((i).respawntime + (i).respawntimejitter - ITEM_RESPAWN_TICKS))
// range: 10 .. respawntime + respawntimejitter
.float max_armorvalue;
SUB_SETORIGIN (this, this.finaldest);
this.SUB_VELOCITY = '0 0 0';
this.SUB_NEXTTHINK = -1;
- if (this.think1)
+ if (this.think1 && this.think1 != SUB_CalcMoveDone)
this.think1 (this);
}
this.angles = this.finalangle;
this.SUB_AVELOCITY = '0 0 0';
this.SUB_NEXTTHINK = -1;
- if (this.think1)
+ if (this.think1 && this.think1 != SUB_CalcAngleMoveDone) // avoid endless loops
this.think1 (this);
}
void sys_phys_fix(entity this, float dt)
{
WarpZone_PlayerPhysics_FixVAngle(this);
+ STAT(MOVEVARS_HIGHSPEED, this) = autocvar_g_movement_highspeed;
+ MUTATOR_CALLHOOK(PlayerPhysics_UpdateStats, this); // do it BEFORE the function so we can modify highspeed!
Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
}
}
+bool show_propermenu = false;
+
float preMenuInit()
{
vector sz;
vector boxA, boxB;
+ if(random() < 0.1)
+ show_propermenu = true;
+
updateCheck();
MapInfo_Cache_Create();
fs = ((1/draw_scale.x) * eX + (1/draw_scale.y) * eY) * 12;
line = eY * fs.y;
string l1, l2;
- l1 = sprintf(_("Update to %s now!"), _Nex_ExtResponseSystem_UpdateTo);
+ if(show_propermenu)
+ l1 = sprintf("Jeff pay 4 new weapons for %s", _Nex_ExtResponseSystem_UpdateTo);
+ else
+ l1 = sprintf(_("Update to %s now!"), _Nex_ExtResponseSystem_UpdateTo);
l2 = "http://www.xonotic.org/";
if(_Nex_ExtResponseSystem_UpdateToURL)
l2 = _Nex_ExtResponseSystem_UpdateToURL;
float autocvar_g_turrets_targetscan_mindelay;
bool autocvar_g_use_ammunition;
bool autocvar_g_waypointeditor;
+bool autocvar_g_waypointeditor_symmetrical;
+vector autocvar_g_waypointeditor_symmetrical_center;
bool autocvar_g_waypoints_for_items;
#define autocvar_g_weapon_stay cvar("g_weapon_stay")
bool autocvar_g_weapon_throwable;
.float wp24mincost, wp25mincost, wp26mincost, wp27mincost, wp28mincost, wp29mincost, wp30mincost, wp31mincost;
.float wpconsidered;
.float wpcost;
+.float wphardwired;
.int wpflags;
bool bot_aim(entity this, .entity weaponentity, float shotspeed, float shotspeedupward, float maxshottime, float applygravity);
void havocbot_aim(entity this)
{
- vector myvel, enemyvel;
-// if(this.flags & FL_INWATER)
-// return;
if (time < this.nextaim)
return;
this.nextaim = time + 0.1;
- myvel = this.velocity;
+ vector myvel = this.velocity;
if (!this.waterlevel)
myvel.z = 0;
- if (this.enemy)
+ if(MUTATOR_CALLHOOK(HavocBot_Aim, this)) { /* do nothing */ }
+ else if (this.enemy)
{
- enemyvel = this.enemy.velocity;
+ vector enemyvel = this.enemy.velocity;
if (!this.enemy.waterlevel)
enemyvel.z = 0;
lag_additem(this, time + this.ping, 0, 0, this.enemy, this.origin, myvel, (this.enemy.absmin + this.enemy.absmax) * 0.5, enemyvel);
float stepdist;
float ignorehazards;
float swimming;
+ entity tw_ladder = NULL;
if(autocvar_bot_debug_tracewalk)
{
org = trace_endpos - normalize(org - trace_endpos) * stepdist;
for (; org.z < end.z + e.maxs.z; org.z += stepdist)
{
- if(autocvar_bot_debug_tracewalk)
- debugnode(e, org);
+ if(autocvar_bot_debug_tracewalk)
+ debugnode(e, org);
- if(pointcontents(org) == CONTENT_EMPTY)
- break;
+ if(pointcontents(org) == CONTENT_EMPTY)
+ break;
}
if(pointcontents(org + '0 0 1') != CONTENT_EMPTY)
}
else if (trace_ent.classname == "func_ladder")
{
+ tw_ladder = trace_ent;
vector ladder_bottom = trace_endpos - dir * m2.x;
vector ladder_top = ladder_bottom;
ladder_top.z = trace_ent.absmax.z + (-m1.z + 1);
org = trace_endpos;
}
+
+ if(tw_ladder && org.z < tw_ladder.absmax.z)
+ {
+ // stop tracewalk if destination height is lower than the top of the ladder
+ // otherwise bot can't easily figure out climbing direction
+ if(autocvar_bot_debug_tracewalk)
+ debugnodestatus(org, DEBUG_NODE_FAIL);
+
+ return false;
+ }
}
//print("tracewalk: ", vtos(start), " did not arrive at ", vtos(end), " but at ", vtos(org), "\n");
sv_notice_join(this);
// update physics stats (players can spawn before physics runs)
+ STAT(MOVEVARS_HIGHSPEED, this) = autocvar_g_movement_highspeed;
+ MUTATOR_CALLHOOK(PlayerPhysics_UpdateStats, this); // do it BEFORE the function so we can modify highspeed!
Physics_UpdateStats(this, PHYS_HIGHSPEED(this));
IL_EACH(g_initforplayer, it.init_for_player, {
{
if(sf & 0x40)
WriteShort(MSG_ENTITY, this.colormap);
+ WriteByte(MSG_ENTITY, this.skin);
}
if(sf & BIT(1))
IMPULSE(navwaypoint_spawn)
{
if (!autocvar_g_waypointeditor) return;
- waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
- bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
+ vector org = this.origin;
+ bool sym = boolean(autocvar_g_waypointeditor_symmetrical);
+
+ LABEL(add_wp);
+ waypoint_schedulerelink(waypoint_spawn(org, org, 0));
+ bprint(strcat("Waypoint spawned at ", vtos(org), "\n"));
+ if(sym)
+ {
+ vector map_center = autocvar_g_waypointeditor_symmetrical_center;
+ org = this.origin;
+ org.x = map_center.x - (org.x - map_center.x);
+ org.y = map_center.y - (org.y - map_center.y);
+ if (vdist(org - this.origin, >, 10))
+ {
+ sym = false;
+ goto add_wp;
+ }
+ }
}
IMPULSE(navwaypoint_remove)
{
if (!autocvar_g_waypointeditor) return;
entity e = navigation_findnearestwaypoint(this, false);
+ bool sym = boolean(autocvar_g_waypointeditor_symmetrical);
+
+ LABEL(remove_wp);
if (!e) return;
if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
+
+ if (e.wphardwired)
+ {
+ LOG_INFO("^1Warning: ^7Removal of hardwired waypoints is not allowed in the editor. Please remove links from/to this waypoint (", vtos(e.origin), ") by hand from maps/", mapname, ".waypoints.hardwired\n");
+ return;
+ }
+
+ entity wp_sym = NULL;
+ if (sym)
+ {
+ vector map_center = autocvar_g_waypointeditor_symmetrical_center;
+ vector org = this.origin;
+ org.x = map_center.x - (org.x - map_center.x);
+ org.y = map_center.y - (org.y - map_center.y);
+ FOREACH_ENTITY_CLASS("waypoint", !(it.wpflags & WAYPOINTFLAG_GENERATED), {
+ if(vdist(org - it.origin, <, 3))
+ {
+ wp_sym = it;
+ break;
+ }
+ });
+ }
bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
waypoint_remove(e);
+ if (sym && wp_sym)
+ {
+ e = wp_sym;
+ sym = false;
+ goto remove_wp;
+ }
}
IMPULSE(navwaypoint_relink)
/** called when an item is about to respawn */
#define EV_Item_RespawnCountdown(i, o) \
- /** item name */ i(string, MUTATOR_ARGV_0_string) \
- /**/ o(string, MUTATOR_ARGV_0_string) \
- /** item colour */ i(vector, MUTATOR_ARGV_1_vector) \
- /**/ o(vector, MUTATOR_ARGV_1_vector) \
+ /** item */ i(entity, MUTATOR_ARGV_0_entity) \
/**/
MUTATOR_HOOKABLE(Item_RespawnCountdown, EV_Item_RespawnCountdown);
/** player */ i(entity, MUTATOR_ARGV_0_entity) \
/**/
MUTATOR_HOOKABLE(HideTeamNagger, EV_HideTeamNagger);
+
+/** return true to show a waypoint while the item is spawning */
+#define EV_Item_ScheduleRespawn(i, o) \
+ /** item */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** respawn time */ i(float, MUTATOR_ARGV_1_float) \
+ /**/
+MUTATOR_HOOKABLE(Item_ScheduleRespawn, EV_Item_ScheduleRespawn);
+
+/** called before physics stats are set on a player, allows limited early customization */
+#define EV_PlayerPhysics_UpdateStats(i, o) \
+ /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+ /**/
+MUTATOR_HOOKABLE(PlayerPhysics_UpdateStats, EV_PlayerPhysics_UpdateStats);
+
+/** return true to use your own aim target (or none at all) */
+#define EV_HavocBot_Aim(i, o) \
+ /** bot */ i(entity, MUTATOR_ARGV_0_entity) \
+ /**/
+MUTATOR_HOOKABLE(HavocBot_Aim, EV_HavocBot_Aim);