#endif
#ifdef GAMEQC
-MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
+//MODEL(Invisibility_ITEM, Item_Model("g_strength.md3")); // TODO: new model required
SOUND(Invisibility, Item_Sound("powerup"));
#endif
#ifdef SVQC
.float invisibility_finished;
+bool autocvar_g_powerups_invisibility = 1;
float autocvar_g_balance_powerup_invisibility_alpha = 0.15;
float autocvar_g_balance_powerup_invisibility_time = 30;
void powerup_invisibility_init(Pickup this, entity item)
{
if(!item.invisibility_finished)
- item.invisibility_finished = autocvar_g_balance_powerup_invisibility_time;
+ item.invisibility_finished = (item.count) ? item.count : autocvar_g_balance_powerup_invisibility_time;
}
#endif
REGISTER_ITEM(Invisibility, Powerup) {
this.m_canonical_spawnfunc = "item_invisibility";
+#ifdef SVQC
+ if(autocvar_g_powerups_invisibility)
+ this.spawnflags = ITEM_FLAG_NORMAL;
+ else
+ this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+
+ this.m_iteminit = powerup_invisibility_init;
+#endif
#ifdef GAMEQC
- this.spawnflags = ITEM_FLAG_MUTATORBLOCKED; // TODO: ITEM_FLAG_NORMAL (once it has a model!)
- this.m_model = MDL_Invisibility_ITEM;
+ this.m_itemid = IT_INVISIBILITY;
+ this.m_model = MDL_BUFF; // TODO: MDL_Invisibility_ITEM when new model available
+ this.m_skin = 12;
this.m_sound = SND_Invisibility;
this.m_glow = true;
this.m_respawnsound = SND_STRENGTH_RESPAWN;
this.m_color = '0.5 0.5 1';
this.m_waypoint = _("Invisibility");
this.m_waypointblink = 2;
-#ifdef GAMEQC
- this.m_itemid = IT_INVISIBILITY;
-#endif
-#ifdef SVQC
- this.m_iteminit = powerup_invisibility_init;
-#endif
}
SPAWNFUNC_ITEM(item_invisibility, ITEM_Invisibility)
- // compat
- SPAWNFUNC_ITEM(item_invis, ITEM_Invisibility)
CLASS(Invisibility, Powerups)
ATTRIB(Invisibility, netname, string, "invisibility");
#endif
#ifdef GAMEQC
-MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
+//MODEL(Speed_ITEM, Item_Model("g_invincible.md3")); // TODO: new model required
SOUND(Speed, Item_Sound("powerup_shield"));
#endif
#ifdef SVQC
.float speed_finished;
+bool autocvar_g_powerups_speed = 1;
float autocvar_g_balance_powerup_speed_attackrate = 0.8;
float autocvar_g_balance_powerup_speed_highspeed = 1.5;
float autocvar_g_balance_powerup_speed_time = 30;
void powerup_speed_init(Pickup this, entity item)
{
if(!item.speed_finished)
- item.speed_finished = autocvar_g_balance_powerup_speed_time;
+ item.speed_finished = (item.count) ? item.count : autocvar_g_balance_powerup_speed_time;
}
#endif
REGISTER_ITEM(Speed, Powerup) {
this.m_canonical_spawnfunc = "item_speed";
+#ifdef SVQC
+ if(autocvar_g_powerups_speed)
+ this.spawnflags = ITEM_FLAG_NORMAL;
+ else
+ this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+
+ this.m_iteminit = powerup_speed_init;
+#endif
#ifdef GAMEQC
- this.spawnflags = ITEM_FLAG_MUTATORBLOCKED; // TODO: ITEM_FLAG_NORMAL (once it has a model!)
- this.m_model = MDL_Speed_ITEM;
+ this.m_itemid = IT_SPEED;
+ this.m_model = MDL_BUFF; // TODO: MDL_Speed_ITEM when new model available
+ this.m_skin = 9;
this.m_sound = SND_Speed;
this.m_glow = true;
this.m_respawnsound = SND_SHIELD_RESPAWN;
this.m_color = '0.1 1 0.84';
this.m_waypoint = _("Speed");
this.m_waypointblink = 2;
-#ifdef GAMEQC
- this.m_itemid = IT_SPEED;
-#endif
-#ifdef SVQC
- this.m_iteminit = powerup_speed_init;
-#endif
}
SPAWNFUNC_ITEM(item_speed, ITEM_Speed)
- // compat
- SPAWNFUNC_ITEM(item_haste, ITEM_Speed)
- SPAWNFUNC_ITEM(item_scout, ITEM_Speed)
CLASS(Speed, Powerups)
ATTRIB(Speed, netname, string, "speed");
this.respawn_flags = 0;
this.respawn_time = 0;
STAT(RESPAWN_TIME, this) = 0;
- bool q3dfcompat = autocvar_sv_q3defragcompat && autocvar_sv_q3defragcompat_changehitbox;
- this.scale = ((q3dfcompat) ? 0.9 : autocvar_sv_player_scale);
+ this.scale = ((q3compat && autocvar_sv_q3compat_changehitbox) ? 0.9 : autocvar_sv_player_scale);
this.fade_time = 0;
this.pain_finished = 0;
this.pushltime = 0;
int totalClients = 0;
if(autocvar_sv_maxidle > 0 && autocvar_sv_maxidle_slots > 0)
{
- FOREACH_CLIENT(IS_REAL_CLIENT(it) || autocvar_sv_maxidle_slots_countbots,
+ // maxidle disabled in local matches by not counting clients (totalClients 0)
+ if (server_is_dedicated)
+ {
+ FOREACH_CLIENT(IS_REAL_CLIENT(it) || autocvar_sv_maxidle_slots_countbots,
+ {
+ ++totalClients;
+ });
+ if (maxclients - totalClients > autocvar_sv_maxidle_slots)
+ totalClients = 0;
+ }
+ }
+ else if (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 0)
+ {
+ FOREACH_CLIENT(IS_REAL_CLIENT(it),
{
++totalClients;
});
}
- if (autocvar_sv_maxidle > 0 && autocvar_sv_maxidle_slots > 0 && (maxclients - totalClients) > autocvar_sv_maxidle_slots)
- { /* do nothing */ }
+ if (totalClients < autocvar_sv_maxidle_minplayers)
+ {
+ // idle kick disabled
+ CS(this).parm_idlesince = time;
+ }
else if (time - CS(this).parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10
{
if (CS(this).idlekick_lasttimeleft)
LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "expect a crash just about now");
WriteString(MSG_ENTITY, this.mdl);
+ WriteByte(MSG_ENTITY, bound(0, this.skin, 255));
}
-
if(sf & ISF_COLORMAP)
{
WriteShort(MSG_ENTITY, this.colormap);
if (item.strength_finished)
{
pickedup = true;
- StatusEffects_apply(STATUSEFFECT_Strength, player, max(StatusEffects_gettime(STATUSEFFECT_Strength, player), time) + item.strength_finished, 0);
+ float t = max(StatusEffects_gettime(STATUSEFFECT_Strength, player), time);
+ if (autocvar_g_powerups_stack)
+ t += item.strength_finished;
+ else
+ t = max(t, time + item.strength_finished);
+ StatusEffects_apply(STATUSEFFECT_Strength, player, t, 0);
}
if (item.invincible_finished)
{
pickedup = true;
- StatusEffects_apply(STATUSEFFECT_Shield, player, max(StatusEffects_gettime(STATUSEFFECT_Shield, player), time) + item.invincible_finished, 0);
+ float t = max(StatusEffects_gettime(STATUSEFFECT_Shield, player), time);
+ if (autocvar_g_powerups_stack)
+ t += item.invincible_finished;
+ else
+ t = max(t, time + item.invincible_finished);
+ StatusEffects_apply(STATUSEFFECT_Shield, player, t, 0);
}
if (item.speed_finished)
{
pickedup = true;
- StatusEffects_apply(STATUSEFFECT_Speed, player, max(StatusEffects_gettime(STATUSEFFECT_Speed, player), time) + item.speed_finished, 0);
+ float t = max(StatusEffects_gettime(STATUSEFFECT_Speed, player), time);
+ if (autocvar_g_powerups_stack)
+ t += item.speed_finished;
+ else
+ t = max(t, time + item.speed_finished);
+ StatusEffects_apply(STATUSEFFECT_Speed, player, t, 0);
}
if (item.invisibility_finished)
{
pickedup = true;
- StatusEffects_apply(STATUSEFFECT_Invisibility, player, max(StatusEffects_gettime(STATUSEFFECT_Invisibility, player), time) + item.invisibility_finished, 0);
+ float t = max(StatusEffects_gettime(STATUSEFFECT_Invisibility, player), time);
+ if (autocvar_g_powerups_stack)
+ t += item.invisibility_finished;
+ else
+ t = max(t, time + item.invisibility_finished);
+ StatusEffects_apply(STATUSEFFECT_Invisibility, player, t, 0);
}
if (item.superweapons_finished)
{
if(autocvar_spawn_debug >= 2)
{
- // why not flags & fl_item?
- FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, {
- LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
- LOG_TRACE(" vs ", it.netname, vtos(it.origin));
- error("Mapper sucks.");
- });
+ // why not flags & fl_item?
+ FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, {
+ LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
+ LOG_TRACE(" vs ", it.netname, vtos(it.origin));
+ error("Mapper sucks.");
+ });
this.is_item = true;
}
weaponsInMap |= WepSet_FromWeapon(REGISTRY_GET(Weapons, weaponid));
- if ( def.instanceOfPowerup
- || def.instanceOfWeaponPickup
+ if ( def.instanceOfPowerup
+ || def.instanceOfWeaponPickup
|| (def.instanceOfHealth && def != ITEM_HealthSmall)
|| (def.instanceOfArmor && def != ITEM_ArmorSmall)
|| (itemid & (IT_KEY1 | IT_KEY2))
setmodel(this, MDL_Null); // precision set below
//this.effects |= EF_LOWPRECISION;
+ // support skinned models for powerups
+ this.skin = def.m_skin;
+ this.glowmod = def.m_color;
+
setsize (this, this.pos1 = def.m_mins, this.pos2 = def.m_maxs);
this.SendFlags |= ISF_SIZE;
void StartItem(entity this, GameItem def)
{
- def = def.m_spawnfunc_hookreplace(def, this);
- if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
- {
- delete(this);
- return;
- }
- this.classname = def.m_canonical_spawnfunc;
- _StartItem(
- this,
- this.itemdef = def,
- def.m_respawntime(), // defaultrespawntime
- def.m_respawntimejitter() // defaultrespawntimejitter
+ def = def.m_spawnfunc_hookreplace(def, this);
+
+ if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
+ {
+ delete(this);
+ return;
+ }
+
+ this.classname = def.m_canonical_spawnfunc;
+
+ _StartItem(
+ this,
+ this.itemdef = def,
+ def.m_respawntime(), // defaultrespawntime
+ def.m_respawntimejitter() // defaultrespawntimejitter
);
}
set g_shootfromfixedorigin "" "if set to a string like 0 y z, the gun is moved to the given y and z coordinates. If set to a string like x y z, the whole shot origin is used"
set g_weapon_stay 0 "1: ghost weapons can be picked up but give no ammo, thrown guns have ammo 2: ghost weapons can be picked up and refill ammo to one pickup size, thrown guns have no ammo (to prevent infinite ammo abuse)"
set g_weapon_throwable 1 "if set to 1, weapons can be dropped"
-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_powerups -1 "if set to 0 no powerups will spawn, if 1 they will spawn in all game modes, -1 is game mode default"
+set g_powerups_stack 0 "enables stacking of powerup timers when picking up a powerup you already have; otherwise timer is reset to the time granted by the item, if greater than the time you currently have"
+set g_powerups_strength 1 "allow strength powerups to spawn"
+set g_powerups_shield 1 "allow shield powerups to spawn"
+set g_powerups_speed 1 "allow speed powerups to spawn"
+set g_powerups_invisibility 1 "allow invisibility powerups to spawn"
set g_use_ammunition 1 "if set to 0 all weapons have unlimited ammo"
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 sv_maxidle_alsokickspectators 1 "when sv_maxidle is > 0, kick idle spectators as well as players"
set sv_maxidle_slots 0 "when not 0, only kick idlers when this many or less player slots are available"
set sv_maxidle_slots_countbots 1 "count bots as player slots"
-
+set sv_maxidle_minplayers 2 "check for idlers only when there are at least this many players (only in dedicated servers)"
set sv_maxidle_playertospectator 60 "move players idle for more than this amount of time in seconds to spectators (sv_maxidle timer starts again after sv_maxidle_playertospectator has moved a player to spectators)"
sv_allowdownloads_inarchive 1 // for csprogs.dat
sv_gameplayfix_gravityunaffectedbyticrate 1
sv_gameplayfix_nogravityonground 1
- set sv_q3acompat_machineshotgunswap 0 "shorthand for swapping machinegun and shotgun (for Q3A map compatibility in mapinfo files)"
- set sv_q3defragcompat 0 "toggle for some compatibility hacks (for Q3DF map compatibility)"
+ set sv_q3compat_changehitbox 0 "use Q3 player hitbox dimensions and camera height on Q3 maps (maps with an entry in a .arena or .defi file)
set g_movement_highspeed 1 "multiplier scale for movement speed (applies to sv_maxspeed and sv_maxairspeed, also applies to air acceleration when g_movement_highspeed_q3_compat is set to 0)"
set g_movement_highspeed_q3_compat 0 "apply speed modifiers to air movement in a more Q3-compatible way (only apply speed buffs and g_movement_highspeed to max air speed, not to acceleration)"