From: Mario Date: Fri, 9 Aug 2019 19:46:54 +0000 (+1000) Subject: Some cleanup of the buff code revolving around timers (and supporting them on legacy... X-Git-Tag: xonotic-v0.8.5~1389^2~2 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=f20be079648c06c940d13b62d7c1bbf68139a231 Some cleanup of the buff code revolving around timers (and supporting them on legacy maps) --- diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc index 58663be1ac..97df48106d 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc @@ -180,9 +180,11 @@ void buff_Touch(entity this, entity toucher) { if (CS(toucher).cvar_cl_buffs_autoreplace && STAT(BUFFS, toucher) != STAT(BUFFS, this)) { + // TODO: lost-gained notification for this case int buffid = buff_FirstFromFlags(STAT(BUFFS, toucher)).m_id; - //Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_DROP, STAT(BUFFS, toucher)); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ITEM_BUFF_LOST, toucher.netname, buffid); + Send_Notification(NOTIF_ONE, toucher, MSG_INFO, INFO_ITEM_BUFF_LOST, toucher.netname, buffid); + if(!IS_INDEPENDENT_PLAYER(toucher)) + Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_INFO, INFO_ITEM_BUFF_LOST, toucher.netname, buffid); STAT(BUFFS, toucher) = 0; //sound(toucher, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); @@ -193,13 +195,17 @@ void buff_Touch(entity this, entity toucher) this.owner = toucher; this.buff_active = false; this.lifetime = 0; - int buffid = buff_FirstFromFlags(STAT(BUFFS, this)).m_id; - Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_GOT, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_INFO, INFO_ITEM_BUFF, toucher.netname, buffid); + entity thebuff = buff_FirstFromFlags(STAT(BUFFS, this)); + Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_GOT, thebuff.m_id); + if(!IS_INDEPENDENT_PLAYER(toucher)) + Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_INFO, INFO_ITEM_BUFF, toucher.netname, thebuff.m_id); Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); sound(toucher, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM); STAT(BUFFS, toucher) |= (STAT(BUFFS, this)); + float bufftime = ((this.count) ? this.count : thebuff.m_time(thebuff)); + if(bufftime) + STAT(BUFF_TIME, toucher) = min(time + bufftime, max(STAT(BUFF_TIME, toucher), time) + bufftime); } float buff_Available(entity buff) @@ -339,6 +345,12 @@ bool buff_Customize(entity this, entity client) return true; } +void buff_Delete(entity this) +{ + WaypointSprite_Kill(this.buff_waypoint); + delete_fn(this); +} + void buff_Init(entity this) { if(!cvar("g_buffs")) { delete(this); return; } @@ -374,6 +386,7 @@ void buff_Init(entity this) buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + max(0, game_starttime - time)); this.buff_active = !this.buff_activetime; this.pflags = PFLAGS_FULLDYNAMIC; + this.dtor = buff_Delete; if(this.spawnflags & 1) this.noalign = true; @@ -562,8 +575,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerSpawn) { entity player = M_ARGV(0, entity); - STAT(BUFFS, player) = 0; - STAT(BUFF_TIME, player) = 0; + player.oldbuffs = 0; PS(player).buff_shield = time + 0.5; // prevent picking up buffs immediately // reset timers here to prevent them continuing after re-spawn player.buff_disability_time = 0; @@ -609,8 +621,10 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerDies) if(STAT(BUFFS, frag_target)) { int buffid = buff_FirstFromFlags(STAT(BUFFS, frag_target)).m_id; - Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid); + if(!IS_INDEPENDENT_PLAYER(frag_target)) + Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid); STAT(BUFFS, frag_target) = 0; + STAT(BUFF_TIME, frag_target) = 0; if(frag_target.buff_model) { @@ -630,11 +644,12 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST) { int buffid = buff_FirstFromFlags(STAT(BUFFS, player)).m_id; Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); + if(!IS_INDEPENDENT_PLAYER(player)) + Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); STAT(BUFFS, player) = 0; + STAT(BUFF_TIME, player) = 0; PS(player).buff_shield = time + max(0, autocvar_g_buffs_pickup_delay); - //STAT(BUFF_TIME, player) = 0; // already notified sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); return true; } @@ -846,7 +861,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); } - else + else if(!IS_INDEPENDENT_PLAYER(player)) Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); STAT(BUFFS, player) = 0; PS(player).buff_shield = time + max(0, autocvar_g_buffs_pickup_delay); // always put in a delay, even if small @@ -898,7 +913,8 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) { entity buff = buff_FirstFromFlags(STAT(BUFFS, player)); float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0; - STAT(BUFF_TIME, player) = (bufftime) ? time + bufftime : 0; + if(STAT(BUFF_TIME, player) <= time) // if the player still has a buff countdown, don't reset it! + STAT(BUFF_TIME, player) = (bufftime) ? time + bufftime : 0; BUFF_ONADD(BUFF_AMMO) { diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index 466b383112..6b04c98b06 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -1503,9 +1503,11 @@ spawnfunc(target_items) FOREACH(Buffs, it != BUFF_Null, { s = Buff_UndeprecateName(argv(j)); - if(s == it.m_name) + if(s == it.netname) { STAT(BUFFS, this) |= (it.m_itemid); + if(!STAT(BUFF_TIME, this)) + STAT(BUFF_TIME, this) = it.m_time(it); break; } }); @@ -1565,7 +1567,7 @@ spawnfunc(target_items) if(GetResource(this, RES_FUEL) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_FUEL)), "fuel"); if(GetResource(this, RES_HEALTH) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_HEALTH)), "health"); if(GetResource(this, RES_ARMOR) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResource(this, RES_ARMOR)), "armor"); - FOREACH(Buffs, it != BUFF_Null, this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, !!(STAT(BUFFS, this) & (it.m_itemid)), it.m_name)); + FOREACH(Buffs, it != BUFF_Null && (STAT(BUFFS, this) & it.m_itemid), this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, STAT(BUFF_TIME, this)), it.netname)); FOREACH(Weapons, it != WEP_Null, this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, !!(STAT(WEAPONS, this) & (it.m_wepset)), it.netname)); } this.netname = strzone(this.netname); @@ -1615,28 +1617,28 @@ float GiveWeapon(entity e, float wpn, float op, float val) bool GiveBuff(entity e, Buff thebuff, int op, int val) { bool had_buff = (STAT(BUFFS, e) & thebuff.m_itemid); - switch(op) + switch (op) { case OP_SET: - if(val > 0) - STAT(BUFFS, e) |= thebuff.m_itemid; - else - STAT(BUFFS, e) &= ~thebuff.m_itemid; + STAT(BUFF_TIME, e) = val; break; case OP_MIN: - case OP_PLUS: - if(val > 0) - STAT(BUFFS, e) |= thebuff.m_itemid; + STAT(BUFF_TIME, e) = max(STAT(BUFF_TIME, e), val); break; case OP_MAX: - if(val <= 0) - STAT(BUFFS, e) &= ~thebuff.m_itemid; + STAT(BUFF_TIME, e) = min(STAT(BUFF_TIME, e), val); + break; + case OP_PLUS: + STAT(BUFF_TIME, e) += val; break; case OP_MINUS: - if(val > 0) - STAT(BUFFS, e) &= ~thebuff.m_itemid; + STAT(BUFF_TIME, e) -= val; break; } + if(STAT(BUFF_TIME, e) <= 0) + STAT(BUFFS, e) &= ~thebuff.m_itemid; + else + STAT(BUFFS, e) = thebuff.m_itemid; // NOTE: replaces any existing buffs on the player! bool have_buff = (STAT(BUFFS, e) & thebuff.m_itemid); return (had_buff != have_buff); } @@ -1706,6 +1708,7 @@ float GiveItems(entity e, float beginarg, float endarg) e.strength_finished = max(0, e.strength_finished - time); e.invincible_finished = max(0, e.invincible_finished - time); e.superweapons_finished = max(0, e.superweapons_finished - time); + STAT(BUFF_TIME, e) = max(0, STAT(BUFF_TIME, e) - time); PREGIVE(e, items); PREGIVE_WEAPONS(e); @@ -1820,7 +1823,7 @@ float GiveItems(entity e, float beginarg, float endarg) got += GiveResourceValue(e, RES_FUEL, op, val); break; default: - FOREACH(Buffs, it != BUFF_Null && Buff_UndeprecateName(cmd) == it.m_name, + FOREACH(Buffs, it != BUFF_Null && Buff_UndeprecateName(cmd) == it.netname, { got += GiveBuff(e, it, op, val); break; @@ -1858,7 +1861,7 @@ float GiveItems(entity e, float beginarg, float endarg) POSTGIVE_RES_ROT(e, RES_HEALTH, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_MEGAHEALTH, SND_Null); if(e.superweapons_finished <= 0) - if(!g_weaponarena && STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) + if(!g_weaponarena && (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS)) e.superweapons_finished = autocvar_g_balance_superweapons_time; if(e.strength_finished <= 0) @@ -1873,6 +1876,10 @@ float GiveItems(entity e, float beginarg, float endarg) e.superweapons_finished = 0; else e.superweapons_finished += time; + if(STAT(BUFF_TIME, e) <= 0) + STAT(BUFF_TIME, e) = 0; + else + STAT(BUFF_TIME, e) += time; for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index f43785bbb2..7e2f0cf02a 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -640,6 +640,10 @@ void PutPlayerInServer(entity this) STAT(REVIVE_PROGRESS, this) = 0; this.revival_time = 0; + // TODO: we can't set these in the PlayerSpawn hook since the target code is called before it! + STAT(BUFFS, this) = 0; + STAT(BUFF_TIME, this) = 0; + this.air_finished = time + 12; this.waterlevel = WATERLEVEL_NONE; this.watertype = CONTENT_EMPTY; diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc index 112a7f039d..f96825132f 100644 --- a/qcsrc/server/compat/quake3.qc +++ b/qcsrc/server/compat/quake3.qc @@ -119,7 +119,16 @@ void target_init_use(entity this, entity actor, entity trigger) { actor.strength_finished = 0; actor.invincible_finished = 0; - STAT(BUFFS, actor) = 0; + if(STAT(BUFFS, actor)) // TODO: make a dropbuffs function to handle this + { + int buffid = buff_FirstFromFlags(STAT(BUFFS, actor)).m_id; + Send_Notification(NOTIF_ONE, actor, MSG_MULTI, ITEM_BUFF_DROP, buffid); + sound(actor, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + if(!IS_INDEPENDENT_PLAYER(actor)) + Send_Notification(NOTIF_ALL_EXCEPT, actor, MSG_INFO, INFO_ITEM_BUFF_LOST, actor.netname, buffid); + STAT(BUFFS, actor) = 0; + STAT(BUFF_TIME, actor) = 0; + } } if (!(this.spawnflags & 16)) @@ -169,6 +178,12 @@ void target_give_init(entity this) SetResourceExplicit(this, RES_ARMOR, 100); else if (it.classname == "item_health_mega") SetResourceExplicit(this, RES_HEALTH, 200); + else if (it.classname == "item_buff") { + entity buff = buff_FirstFromFlags(STAT(BUFFS, it)); + this.netname = cons(this.netname, buff.netname); + STAT(BUFF_TIME, this) = it.count; + } + //remove(it); // removing ents in init functions causes havoc, workaround: setthink(it, SUB_Remove); it.nextthink = time; diff --git a/ruleset-XDF.cfg b/ruleset-XDF.cfg index 20740d9a86..16cdd100a8 100644 --- a/ruleset-XDF.cfg +++ b/ruleset-XDF.cfg @@ -21,6 +21,7 @@ set teamplay_mode 2 // friendly fire and self damage set sv_vote_nospectators 1 set timelimit_override 20 set g_buffs_cooldown_respawn 0.1 +set g_buffs_randomize 0 // game mode settings set g_cts_finish_kill_delay 2