- wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints
- wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache
- make
- - EXPECT=662ab75eeb91d25c2d86ebb81ce8dc02
+ - EXPECT=ed9be8d1b1a544f89bcdd7d36876fede
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
// general gameplay
set g_overkill 1
+
+// hack - eventually, we should be able to choose overkill models in menu like for vanilla
+set sv_defaultcharacter 1
+set 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"
+set sv_defaultplayermodel_red "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm"
+set sv_defaultplayermodel_blue "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"
+
set g_respawn_ghosts 0
set g_nades 1
set g_powerups -1 "if set to 0 the strength and shield (invincibility) will not spawn on the map, if 1 they will spawn in all game modes, -1 is game mode default"
set g_use_ammunition 1 "if set to 0 all weapons have unlimited ammunition"
set g_pickup_items -1 "if set to 0 all items (health, armor, ammo, weapons...) are removed from the map, if 1 they are forced to spawn"
+set g_pickup_respawntime_scaling_reciprocal 0 "Multiply respawn time by `reciprocal / (p + offset) + linear` where `p` is the current number of players, takes effect with 2 or more players present, `reciprocal` (with `offset` and `linear` set to 0) can be used to achieve a constant number of items spawned *per player*"
+set g_pickup_respawntime_scaling_offset 0 "Multiply respawn time by `reciprocal / (p + offset) + linear` where `p` is the current number of players, takes effect with 2 or more players present, `offset` offsets the curve left or right - the results are not intuitive and I recommend plotting the respawn time and the number of items per player to see what's happening"
+set g_pickup_respawntime_scaling_linear 1 "Multiply respawn time by `reciprocal / (p + offset) + linear` where `p` is the current number of players, takes effect with 2 or more players present, `linear` can be used to simply scale the respawn time linearly"
set g_weaponarena "0" "put in a list of weapons to enable a weapon arena mode, or try \"all\" or \"most\""
set g_weaponarena_random "0" "if set to a number, only that weapon count is given on every spawn (randomly)"
set g_weaponarena_random_with_blaster "1" "additionally, always provide the blaster in random weapon arena games"
// =======
set g_nades 0 "enable off-hand grenades"
set g_nades_spread 0.04 "random spread offset of throw direction"
-set g_nades_throw_offset "0 0 0" "nade throwing offset"
+set g_nades_throw_offset "0 -25 0" "nade throwing offset"
set g_nades_spawn 1 "give nades right away when player spawns rather than delaying entire refire"
set g_nades_client_select 0 "allow client side selection of nade type"
set g_nades_pickup 0 "allow picking up thrown nades (not your own)"
if (this.text) strunzone(this.text);
this.text = strzone(s);
- float size_range = autocvar_cl_damagetext_size_max - autocvar_cl_damagetext_size_min;
- float damage_range = autocvar_cl_damagetext_size_max_damage - autocvar_cl_damagetext_size_min_damage;
- float scale_factor = size_range / damage_range;
- this.m_size = bound(
- autocvar_cl_damagetext_size_min,
- (potential - autocvar_cl_damagetext_size_min_damage) * scale_factor + autocvar_cl_damagetext_size_min,
- autocvar_cl_damagetext_size_max);
+ this.m_size = map_bound_ranges(potential,
+ autocvar_cl_damagetext_size_min_damage, autocvar_cl_damagetext_size_max_damage,
+ autocvar_cl_damagetext_size_min, autocvar_cl_damagetext_size_max);
}
CONSTRUCTOR(DamageText, int _group, vector _origin, bool _screen_coords, int _health, int _armor, int _potential_damage, int _deathtype, bool _friendlyfire) {
.int pressedkeys;
#endif
-// returns 1 if the player is close to a wall
+// returns true if the player is close to a wall
bool check_close_to_wall(entity this, float threshold)
{
if (PHYS_DODGING_WALL == 0) { return false; }
-#define X(OFFSET) \
- tracebox(this.origin, this.mins, this.maxs, this.origin + OFFSET, true, this); \
- if(trace_fraction < 1 && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) && vdist(this.origin - trace_endpos, <, threshold)) \
+#define X(dir) \
+ tracebox(this.origin, this.mins, this.maxs, this.origin + threshold * dir, true, this); \
+ if (trace_fraction < 1 && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) \
return true;
- X(1000*v_right);
- X(-1000*v_right);
- X(1000*v_forward);
- X(-1000*v_forward);
+
+ X(v_right);
+ X(-v_right);
+ X(v_forward);
+ X(-v_forward);
#undef X
return false;
delete(e.fake_nade);
e.fake_nade = NULL;
+ Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_NADES);
+
makevectors(e.v_angle);
// NOTE: always throw from first weapon entity?
W_SetupShot(e, _nade.weaponentity_fld, false, false, SND_Null, CH_WEAPON_A, 0);
- Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, 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';
+ + (v_right * autocvar_g_nades_throw_offset.y)
+ + (v_up * autocvar_g_nades_throw_offset.z);
- setorigin(_nade, w_shotorg + offset + (v_right * 25) * -1);
+ setorigin(_nade, w_shotorg + offset);
//setmodel(_nade, MDL_PROJECTILE_NADE);
//setattachment(_nade, NULL, "");
PROJECTILE_MAKETRIGGER(_nade);
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");
WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
}
}
+AUTOCVAR(g_pickup_respawntime_scaling_reciprocal, float, 0.0, "Multiply respawn time by `reciprocal / (p + offset) + linear` where `p` is the current number of players, takes effect with 2 or more players present, `reciprocal` (with `offset` and `linear` set to 0) can be used to achieve a constant number of items spawned *per player*");
+AUTOCVAR(g_pickup_respawntime_scaling_offset, float, 0.0, "Multiply respawn time by `reciprocal / (p + offset) + linear` where `p` is the current number of players, takes effect with 2 or more players present, `offset` offsets the curve left or right - the results are not intuitive and I recommend plotting the respawn time and the number of items per player to see what's happening");
+AUTOCVAR(g_pickup_respawntime_scaling_linear, float, 1.0, "Multiply respawn time by `reciprocal / (p + offset) + linear` where `p` is the current number of players, takes effect with 2 or more players present, `linear` can be used to simply scale the respawn time linearly");
void Item_ScheduleRespawn(entity e)
{
if(e.respawntime > 0)
{
Item_Show(e, 0);
- Item_ScheduleRespawnIn(e, ITEM_RESPAWNTIME(e));
+
+ CheckAllowedTeams(NULL);
+ GetTeamCounts(NULL);
+ int players = 0;
+ if (c1 != -1) players += c1;
+ if (c2 != -1) players += c2;
+ if (c3 != -1) players += c3;
+ if (c4 != -1) players += c4;
+ float adjusted_respawntime;
+ if (players >= 2) {
+ float a = autocvar_g_pickup_respawntime_scaling_reciprocal;
+ float b = autocvar_g_pickup_respawntime_scaling_offset;
+ float c = autocvar_g_pickup_respawntime_scaling_linear;
+ adjusted_respawntime = e.respawntime * (a / (players + b) + c);
+ } else {
+ adjusted_respawntime = e.respawntime;
+ }
+ //LOG_INFOF("item %s will respawn in %f\n", e.classname, adjusted_respawntime);
+ // range: adjusted_respawntime - respawntimejitter .. adjusted_respawntime + respawntimejitter
+ float actual_time = adjusted_respawntime + crandom() * e.respawntimejitter;
+ Item_ScheduleRespawnIn(e, actual_time);
}
else // if respawntime is -1, this item does not respawn
Item_Show(e, -1);
const float ITEM_RESPAWN_TICKS = 10;
-#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))
// range: 10 .. respawntime + respawntimejitter
CLASS(MachineGun, Weapon)
/* ammotype */ ATTRIB(MachineGun, ammo_field, .int, ammo_nails);
/* impulse */ ATTRIB(MachineGun, impulse, int, 3);
-/* flags */ ATTRIB(MachineGun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
+/* flags */ ATTRIB(MachineGun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_PENETRATEWALLS);
/* rating */ ATTRIB(MachineGun, bot_pickupbasevalue, float, 7000);
/* color */ ATTRIB(MachineGun, wpcolor, vector, '1 1 0');
/* modelname */ ATTRIB(MachineGun, mdl, string, "uzi");
}
return v;
}
+
+/// Maps values between the src and dest range: src_min to dest_min, src_max to dest_max, values between them
+/// to the curresponding values between and extrapolates for values outside the range.
+///
+/// src_min and src_max must not be the same or division by zero accurs.
+///
+/// dest_max can be smaller than dest_min if you want the resulting range to be inverted, all values can be negative.
+ERASEABLE
+float map_ranges(float value, float src_min, float src_max, float dest_min, float dest_max) {
+ float src_diff = src_max - src_min;
+ float dest_diff = dest_max - dest_min;
+ float ratio = (value - src_min) / src_diff;
+ return dest_min + dest_diff * ratio;
+}
+
+/// Same as `map_ranges` except that values outside the source range are clamped to min or max.
+ERASEABLE
+float map_bound_ranges(float value, float src_min, float src_max, float dest_min, float dest_max) {
+ if (value <= src_min) return dest_min;
+ if (value >= src_max) return dest_max;
+ return map_ranges(value, src_min, src_max, dest_min, dest_max);
+}