X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fmutators%2Fmutator_buffs.qc;h=b582e168604c9626b579258e19810c245144bd66;hb=27719a18947959c32a559ed39842f46263749c34;hp=4e8d1d6e3dfc2ee2f3f9f31cb4e5e6a925d4e539;hpb=125d619e9ab2a307b15b7ee1a2ededed32c7e84d;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/mutators/mutator_buffs.qc b/qcsrc/server/mutators/mutator_buffs.qc index 4e8d1d6e3..b582e1686 100644 --- a/qcsrc/server/mutators/mutator_buffs.qc +++ b/qcsrc/server/mutators/mutator_buffs.qc @@ -1,3 +1,4 @@ +#include "../../common/triggers/target/music.qh" #include "mutator_buffs.qh" #include "../_all.qh" @@ -5,7 +6,14 @@ #include "../../common/buffs.qh" -.float lifetime; +entity buff_FirstFromFlags(int _buffs) +{ + if (flags) + { + FOREACH(BUFFS, it.m_itemid & _buffs, LAMBDA(return it)); + } + return BUFF_NULL; +} float buffs_BuffModel_Customize() { @@ -33,31 +41,57 @@ float buffs_BuffModel_Customize() return true; } +void buffs_BuffModel_Spawn(entity player) +{ + player.buff_model = spawn(); + setmodel(player.buff_model, BUFF_MODEL); + setsize(player.buff_model, '0 0 -40', '0 0 40'); + setattachment(player.buff_model, player, ""); + setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1)); + player.buff_model.owner = player; + player.buff_model.scale = 0.7; + player.buff_model.pflags = PFLAGS_FULLDYNAMIC; + player.buff_model.light_lev = 200; + player.buff_model.customizeentityforclient = buffs_BuffModel_Customize; +} + +vector buff_GlowColor(entity buff) +{ + //if(buff.team) { return Team_ColorRGB(buff.team); } + return buff.m_color; +} + +void buff_Effect(entity player, string eff) +{ + if(!autocvar_g_buffs_effects) { return; } + + if(time >= self.buff_effect_delay) + { + pointparticles(particleeffectnum(eff), player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); + self.buff_effect_delay = time + 0.05; // prevent spam + } +} + // buff item float buff_Waypoint_visible_for_player(entity plr) { - if(!self.owner.buff_active && !self.owner.buff_activetime) - return false; + if(!self.owner.buff_active && !self.owner.buff_activetime) + return false; - if(plr.buffs) + if (plr.buffs) { - if(plr.cvar_cl_buffs_autoreplace) - { - if(plr.buffs == self.owner.buffs) - return false; - } - else - return false; + return plr.cvar_cl_buffs_autoreplace == false || plr.buffs != self.owner.buffs; } - return WaypointSprite_visible_for_player(plr); + return WaypointSprite_visible_for_player(plr); } void buff_Waypoint_Spawn(entity e) { - WaypointSprite_Spawn(Buff_Sprite(e.buffs), 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, world, e.team, e, buff_waypoint, true, RADARICON_POWERUP, e.glowmod); - WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_POWERUP, e.glowmod); - e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; + entity buff = buff_FirstFromFlags(e.buffs); + WaypointSprite_Spawn(buff.m_sprite, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, world, e.team, e, buff_waypoint, true, RADARICON_POWERUP, e.glowmod); + WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_POWERUP, e.glowmod); + e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; } void buff_SetCooldown(float cd) @@ -126,12 +160,13 @@ void buff_Touch() return; } - if(other.buffs) + if (other.buffs) { - if(other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs) + if (other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs) { + int buffid = buff_FirstFromFlags(other.buffs).m_id; //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs); - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, other.buffs); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, buffid); other.buffs = 0; //sound(other, CH_TRIGGER, "relics/relic_effect.wav", VOL_BASE, ATTN_NORM); @@ -142,39 +177,36 @@ void buff_Touch() self.owner = other; self.buff_active = false; self.lifetime = 0; - - Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, self.buffs); - Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, self.buffs); + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, buffid); pointparticles(particleeffectnum("item_pickup"), CENTER_OR_VIEWOFS(self), '0 0 0', 1); sound(other, CH_TRIGGER, "misc/shield_respawn.wav", VOL_BASE, ATTN_NORM); other.buffs |= (self.buffs); } -float buff_Available(float buffid) +float buff_Available(entity buff) { - if(buffid == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) + if (buff == BUFF_NULL) return false; - - if(buffid == BUFF_VAMPIRE && cvar("g_vampire")) + if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) return false; - - if(!cvar(strcat("g_buffs_", Buff_Name(buffid)))) + if (buff == BUFF_VAMPIRE && cvar("g_vampire")) return false; - - return true; + return cvar(strcat("g_buffs_", buff.m_name)); } +.int buff_seencount; + void buff_NewType(entity ent, float cb) { - entity e; RandomSelection_Init(); - for(e = Buff_Type_first; e; e = e.enemy) - if(buff_Available(e.items)) - { - RandomSelection_Add(world, e.items, string_null, 1, 1 / e.count); // if it's already been chosen, give it a lower priority - e.count += 1; - } + FOREACH(BUFFS, buff_Available(it), LAMBDA( + it.buff_seencount += 1; + // if it's already been chosen, give it a lower priority + RandomSelection_Add(world, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount)); + )); ent.buffs = RandomSelection_chosen_float; } @@ -182,11 +214,12 @@ void buff_Think() { if(self.buffs != self.oldbuffs) { - self.color = Buff_Color(self.buffs); - self.glowmod = ((self.team) ? Team_ColorRGB(self.team) + '0.1 0.1 0.1' : self.color); - self.skin = Buff_Skin(self.buffs); + entity buff = buff_FirstFromFlags(self.buffs); + self.color = buff.m_color; + self.glowmod = buff_GlowColor(buff); + self.skin = buff.m_skin; - setmodel(self, "models/relics/relic.md3"); + setmodel(self, BUFF_MODEL); if(self.buff_waypoint) { @@ -206,7 +239,7 @@ void buff_Think() { buff_SetCooldown(self.buff_activetime); self.buff_activetime_updated = true; - } + } if(!self.buff_active && !self.buff_activetime) if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs)) @@ -216,7 +249,7 @@ void buff_Think() if(autocvar_g_buffs_randomize) buff_NewType(self, self.buffs); - if(autocvar_g_buffs_random_location || (self.spawnflags & 1)) + if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) buff_Respawn(self); } @@ -234,19 +267,8 @@ void buff_Think() } } - if(!self.buff_active) - { - self.alpha = 0.3; - self.effects &= ~(EF_FULLBRIGHT); - self.pflags = 0; - } - else + if(self.buff_active) { - self.alpha = 1; - self.effects |= EF_FULLBRIGHT; - self.light_lev = 220 + 36 * sin(time); - self.pflags = PFLAGS_FULLDYNAMIC; - if(self.team && !self.buff_waypoint) buff_Waypoint_Spawn(self); @@ -275,19 +297,40 @@ void buff_Reset() buff_Waypoint_Reset(); self.buff_activetime_updated = false; - if(autocvar_g_buffs_random_location || (self.spawnflags & 1)) + if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) buff_Respawn(self); } +float buff_Customize() +{ + entity player = WaypointSprite_getviewentity(other); + if(!self.buff_active || (self.team && DIFF_TEAM(player, self))) + { + self.alpha = 0.3; + if(self.effects & EF_FULLBRIGHT) { self.effects &= ~(EF_FULLBRIGHT); } + self.pflags = 0; + } + else + { + self.alpha = 1; + if(!(self.effects & EF_FULLBRIGHT)) { self.effects |= EF_FULLBRIGHT; } + self.light_lev = 220 + 36 * sin(time); + self.pflags = PFLAGS_FULLDYNAMIC; + } + return true; +} + void buff_Init(entity ent) { - if(!cvar("g_buffs")) { remove(self); return; } + if(!cvar("g_buffs")) { remove(ent); return; } + + if(!teamplay && ent.team) { ent.team = 0; } - if(!teamplay && self.team) { self.team = 0; } + entity buff = buff_FirstFromFlags(self.buffs); entity oldself = self; self = ent; - if(!self.buffs || buff_Available(self.buffs)) + if(!self.buffs || buff_Available(buff)) buff_NewType(self, 0); self.classname = "item_buff"; @@ -300,36 +343,40 @@ void buff_Init(entity ent) self.gravity = 1; self.movetype = MOVETYPE_TOSS; self.scale = 1; - self.skin = Buff_Skin(self.buffs); + self.skin = buff.m_skin; self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW; self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; + self.customizeentityforclient = buff_Customize; //self.gravity = 100; - self.color = Buff_Color(self.buffs); - self.glowmod = ((self.team) ? Team_ColorRGB(self.team) + '0.1 0.1 0.1' : self.color); + self.color = buff.m_color; + self.glowmod = buff_GlowColor(self); buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime); self.buff_active = !self.buff_activetime; self.pflags = PFLAGS_FULLDYNAMIC; + if(self.spawnflags & 1) + self.noalign = true; + if(self.noalign) self.movetype = MOVETYPE_NONE; // reset by random location - setmodel(self, "models/relics/relic.md3"); + setmodel(self, BUFF_MODEL); setsize(self, BUFF_MIN, BUFF_MAX); - if(cvar("g_buffs_random_location") || (self.spawnflags & 1)) + if(cvar("g_buffs_random_location") || (self.spawnflags & 64)) buff_Respawn(self); self = oldself; } -void buff_Init_Compat(entity ent, float replacement) +void buff_Init_Compat(entity ent, entity replacement) { - if(ent.spawnflags & 2) + if (ent.spawnflags & 2) ent.team = NUM_TEAM_1; - else if(ent.spawnflags & 4) + else if (ent.spawnflags & 4) ent.team = NUM_TEAM_2; - ent.buffs = replacement; + ent.buffs = replacement.m_itemid; buff_Init(ent); } @@ -338,17 +385,31 @@ void buff_SpawnReplacement(entity ent, entity old) { setorigin(ent, old.origin); ent.angles = old.angles; - ent.noalign = old.noalign; + ent.noalign = (old.noalign || (old.spawnflags & 1)); buff_Init(ent); } +void buff_Vengeance_DelayedDamage() +{ + if(self.enemy) + Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF, self.enemy.origin, '0 0 0'); + + remove(self); + return; +} + +float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base) +{ + return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base); +} + // mutator hooks MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_SplitHealthArmor) { - if(frag_deathtype == DEATH_BUFF_VENGEANCE) { return false; } // oh no you don't + if(frag_deathtype == DEATH_BUFF) { return false; } - if(frag_target.buffs & BUFF_RESISTANCE) + if(frag_target.buffs & BUFF_RESISTANCE.m_itemid) { vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); damage_take = v.x; @@ -358,32 +419,26 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_SplitHealthArmor) return false; } -void buff_Vengeance_DelayedDamage() -{ - if(self.enemy) - Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF_VENGEANCE, self.enemy.origin, '0 0 0'); - - remove(self); - return; -} - MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_Calculate) { - if(frag_deathtype == DEATH_BUFF_VENGEANCE) { return false; } // oh no you don't + if(frag_deathtype == DEATH_BUFF) { return false; } - if(frag_target.buffs & BUFF_SPEED) + if(frag_target.buffs & BUFF_SPEED.m_itemid) if(frag_target != frag_attacker) frag_damage *= autocvar_g_buffs_speed_damage_take; - if(frag_target.buffs & BUFF_MEDIC) + if(frag_target.buffs & BUFF_MEDIC.m_itemid) if((frag_target.health - frag_damage) <= 0) if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) if(frag_attacker) if(random() <= autocvar_g_buffs_medic_survive_chance) - if(frag_target.health - autocvar_g_buffs_medic_survive_health > 0) // not if the final result would be less than 0, medic must get health - frag_damage = frag_target.health - autocvar_g_buffs_medic_survive_health; + frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health); + + if(frag_target.buffs & BUFF_JUMP.m_itemid) + if(frag_deathtype == DEATH_FALL) + frag_damage = 0; - if(frag_target.buffs & BUFF_VENGEANCE) + if(frag_target.buffs & BUFF_VENGEANCE.m_itemid) if(frag_attacker) if(frag_attacker != frag_target) if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) @@ -397,23 +452,24 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_Calculate) dmgent.nextthink = time + 0.1; } - if(frag_target.buffs & BUFF_BASH) + if(frag_target.buffs & BUFF_BASH.m_itemid) if(frag_attacker != frag_target) if(vlen(frag_force)) frag_force = '0 0 0'; - if(frag_attacker.buffs & BUFF_BASH) + if(frag_attacker.buffs & BUFF_BASH.m_itemid) if(vlen(frag_force)) if(frag_attacker == frag_target) frag_force *= autocvar_g_buffs_bash_force_self; else frag_force *= autocvar_g_buffs_bash_force; - if(frag_attacker.buffs & BUFF_DISABILITY) + if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid) if(frag_target != frag_attacker) - frag_target.buff_disability_time = time + autocvar_g_buffs_disability_time; + frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime; - if(frag_attacker.buffs & BUFF_MEDIC) + if(frag_attacker.buffs & BUFF_MEDIC.m_itemid) + if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) if(SAME_TEAM(frag_attacker, frag_target)) if(frag_attacker != frag_target) { @@ -421,9 +477,23 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_Calculate) frag_damage = 0; } + if(frag_attacker.buffs & BUFF_INFERNO.m_itemid) + if(frag_target != frag_attacker) { + float time = buff_Inferno_CalculateTime( + frag_damage, + 0, + autocvar_g_buffs_inferno_burntime_min_time, + autocvar_g_buffs_inferno_burntime_target_damage, + autocvar_g_buffs_inferno_burntime_target_time, + autocvar_g_buffs_inferno_burntime_factor + ); + Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier) * time, time, DEATH_BUFF); + } + // this... is ridiculous (TODO: fix!) - if(frag_attacker.buffs & BUFF_VAMPIRE) + if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid) if(!frag_target.vehicle) + if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) if(frag_target.deadflag == DEAD_NO) if(IS_PLAYER(frag_target) || (frag_target.flags & FL_MONSTER)) @@ -431,7 +501,11 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_Calculate) if(!frag_target.frozen) if(frag_target.takedamage) if(DIFF_TEAM(frag_attacker, frag_target)) + { frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max); + if(frag_target.armorvalue) + frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max); + } return false; } @@ -447,7 +521,7 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerSpawn) MUTATOR_HOOKFUNCTION(buffs_PlayerPhysics) { - if(self.buffs & BUFF_SPEED) + if(self.buffs & BUFF_SPEED.m_itemid) { self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed; self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed; @@ -459,14 +533,19 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerPhysics) self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; } + if(self.buffs & BUFF_JUMP.m_itemid) + { + // automatically reset, no need to worry + self.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; + } + return false; } MUTATOR_HOOKFUNCTION(buffs_PlayerJump) { - if(self.buffs & BUFF_JUMP) + if(self.buffs & BUFF_JUMP.m_itemid) player_jumpheight = autocvar_g_buffs_jump_height; - self.stat_jumpheight = player_jumpheight; return false; } @@ -486,7 +565,8 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerDies) { if(self.buffs) { - Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs); + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); self.buffs = 0; if(self.buff_model) @@ -503,8 +583,9 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerUseKey) if(MUTATOR_RETURNVALUE || gameover) { return false; } if(self.buffs) { - Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, self.buffs); - Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs); + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); self.buffs = 0; sound(self, CH_TRIGGER, "relics/relic_effect.wav", VOL_BASE, ATTN_NORM); @@ -513,6 +594,76 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerUseKey) return false; } +MUTATOR_HOOKFUNCTION(buffs_PlayerThrowKey) +{ + if(MUTATOR_RETURNVALUE || gameover) { return false; } + + if(self.buffs & BUFF_SWAPPER.m_itemid) + { + float best_distance = autocvar_g_buffs_swapper_range; + entity closest = world; + entity player; + FOR_EACH_PLAYER(player) + if(DIFF_TEAM(self, player)) + if(player.deadflag == DEAD_NO && !player.frozen && !player.vehicle) + if(vlen(self.origin - player.origin) <= best_distance) + { + best_distance = vlen(self.origin - player.origin); + closest = player; + } + + if(closest) + { + vector my_org, my_vel, my_ang, their_org, their_vel, their_ang; + + my_org = self.origin; + my_vel = self.velocity; + my_ang = self.angles; + their_org = closest.origin; + their_vel = closest.velocity; + their_ang = closest.angles; + + if(closest.ballcarried) + if(g_keepaway) { ka_DropEvent(closest); } + else { DropBall(closest.ballcarried, closest.origin, closest.velocity);} + if(closest.flagcarried) { ctf_Handle_Throw(closest, world, DROP_THROW); } + if(closest.nade) { toss_nade(closest, '0 0 0', time + 0.05); } + + MUTATOR_CALLHOOK(PortalTeleport, self); // initiate flag dropper + + setorigin(self, their_org); + setorigin(closest, my_org); + + closest.velocity = my_vel; + closest.angles = my_ang; + closest.fixangle = true; + closest.oldorigin = my_org; + closest.oldvelocity = my_vel; + self.velocity = their_vel; + self.angles = their_ang; + self.fixangle = true; + self.oldorigin = their_org; + self.oldvelocity = their_vel; + + // set pusher so self gets the kill if they fall into void + closest.pusher = self; + closest.pushltime = time + autocvar_g_maxpushtime; + closest.istypefrag = closest.BUTTON_CHAT; + + pointparticles(particleeffectnum("electro_combo"), their_org, '0 0 0', 1); + pointparticles(particleeffectnum("electro_combo"), my_org, '0 0 0', 1); + + sound(self, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTEN_NORM); + sound(closest, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTEN_NORM); + + // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam + self.buffs = 0; + return true; + } + } + return false; +} + MUTATOR_HOOKFUNCTION(buffs_RemovePlayer) { if(self.buff_model) @@ -534,7 +685,7 @@ MUTATOR_HOOKFUNCTION(buffs_CustomizeWaypoint) // if you have the invisibility powerup, sprites ALWAYS are restricted to your team // but only apply this to real players, not to spectators - if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE) && (e == other)) + if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == other)) if(DIFF_TEAM(self.owner, e)) return true; @@ -559,7 +710,7 @@ MUTATOR_HOOKFUNCTION(buffs_OnEntityPreSpawn) MUTATOR_HOOKFUNCTION(buffs_WeaponRate) { - if(self.buffs & BUFF_SPEED) + if(self.buffs & BUFF_SPEED.m_itemid) weapon_rate *= autocvar_g_buffs_speed_rate; if(time < self.buff_disability_time) @@ -568,6 +719,19 @@ MUTATOR_HOOKFUNCTION(buffs_WeaponRate) return false; } +MUTATOR_HOOKFUNCTION(buffs_WeaponSpeed) +{ + if(self.buffs & BUFF_SPEED.m_itemid) + ret_float *= autocvar_g_buffs_speed_weaponspeed; + + if(time < self.buff_disability_time) + ret_float *= autocvar_g_buffs_disability_weaponspeed; + + return false; +} + +.float buff_time; + MUTATOR_HOOKFUNCTION(buffs_PlayerThink) { if(gameover || self.deadflag != DEAD_NO) { return false; } @@ -579,82 +743,116 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerThink) self.buff_disability_effect_time = time + 0.5; } - if(self.frozen) + // handle buff lost status + // 1: notify everyone else + // 2: notify carrier as well + int buff_lost = 0; + + if(self.buff_time) + if(time >= self.buff_time) + buff_lost = 2; + + if(self.frozen) { buff_lost = 1; } + + if(buff_lost) { if(self.buffs) { - Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs); + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); + if(buff_lost >= 2) + { + Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? + sound(self, CH_TRIGGER, "relics/relic_effect.wav", VOL_BASE, ATTN_NORM); + } self.buffs = 0; } } - if((self.buffs & BUFF_INVISIBLE) && (self.oldbuffs & BUFF_INVISIBLE)) + if(self.buffs & BUFF_MAGNET.m_itemid) + { + vector pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item; + for(other = world; (other = findflags(other, flags, FL_ITEM)); ) + if(boxesoverlap(self.absmin - pickup_size, self.absmax + pickup_size, other.absmin, other.absmax)) + { + entity oldself = self; + self = other; + other = oldself; + if(self.touch) + self.touch(); + other = self; + self = oldself; + } + } + + if(self.buffs & BUFF_AMMO.m_itemid) + if(self.clip_size) + self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; + + if((self.buffs & BUFF_INVISIBLE.m_itemid) && (self.oldbuffs & BUFF_INVISIBLE.m_itemid)) if(self.alpha != autocvar_g_buffs_invisible_alpha) - self.alpha = autocvar_g_buffs_invisible_alpha; + self.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO) + +#define BUFF_ONADD(b) if ( (self.buffs & (b).m_itemid) && !(self.oldbuffs & (b).m_itemid)) +#define BUFF_ONREM(b) if (!(self.buffs & (b).m_itemid) && (self.oldbuffs & (b).m_itemid)) if(self.buffs != self.oldbuffs) { - if(self.oldbuffs & BUFF_AMMO) + entity buff = buff_FirstFromFlags(self.buffs); + float bufftime = buff != BUFF_NULL ? buff.m_time(buff) : 0; + self.buff_time = (bufftime) ? time + bufftime : 0; + + BUFF_ONADD(BUFF_AMMO) + { + self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO); + self.items |= IT_UNLIMITED_WEAPON_AMMO; + + if(self.clip_load) + self.buff_ammo_prev_clipload = self.clip_load; + self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; + } + + BUFF_ONREM(BUFF_AMMO) { if(self.buff_ammo_prev_infitems) self.items |= IT_UNLIMITED_WEAPON_AMMO; else self.items &= ~IT_UNLIMITED_WEAPON_AMMO; - } - else if(self.buffs & BUFF_AMMO) - { - self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO); - self.items |= IT_UNLIMITED_WEAPON_AMMO; - if(!self.ammo_shells) { self.ammo_shells = 20; } - if(!self.ammo_cells) { self.ammo_cells = 20; } - if(!self.ammo_rockets) { self.ammo_rockets = 20; } - if(!self.ammo_nails) { self.ammo_nails = 20; } - if(!self.ammo_fuel) { self.ammo_fuel = 20; } + + if(self.buff_ammo_prev_clipload) + self.clip_load = self.buff_ammo_prev_clipload; } - if(self.oldbuffs & BUFF_INVISIBLE) + BUFF_ONADD(BUFF_INVISIBLE) { if(time < self.strength_finished && g_instagib) self.alpha = autocvar_g_instagib_invis_alpha; else self.alpha = self.buff_invisible_prev_alpha; - } - else if(self.buffs & BUFF_INVISIBLE) - { - if(time < self.strength_finished && g_instagib) - self.buff_invisible_prev_alpha = default_player_alpha; - else - self.buff_invisible_prev_alpha = self.alpha; self.alpha = autocvar_g_buffs_invisible_alpha; } - if(self.oldbuffs & BUFF_FLIGHT) - self.gravity = self.buff_flight_prev_gravity; - else if(self.buffs & BUFF_FLIGHT) + BUFF_ONREM(BUFF_INVISIBLE) + self.alpha = self.buff_invisible_prev_alpha; + + BUFF_ONADD(BUFF_FLIGHT) { self.buff_flight_prev_gravity = self.gravity; self.gravity = autocvar_g_buffs_flight_gravity; } + BUFF_ONREM(BUFF_FLIGHT) + self.gravity = self.buff_flight_prev_gravity; + self.oldbuffs = self.buffs; if(self.buffs) { if(!self.buff_model) - { - self.buff_model = spawn(); - setmodel(self.buff_model, "models/relics/relic.md3"); - setsize(self.buff_model, '0 0 -40', '0 0 40'); - setattachment(self.buff_model, self, ""); - setorigin(self.buff_model, '0 0 1' * (self.buff_model.maxs.z * 1)); - self.buff_model.owner = self; - self.buff_model.scale = 0.7; - self.buff_model.pflags = PFLAGS_FULLDYNAMIC; - self.buff_model.light_lev = 200; - self.buff_model.customizeentityforclient = buffs_BuffModel_Customize; - } - self.buff_model.color = Buff_Color(self.buffs); - self.buff_model.glowmod = ((self.buff_model.team) ? Team_ColorRGB(self.buff_model.team) + '0.1 0.1 0.1' : self.buff_model.color); - self.buff_model.skin = Buff_Skin(self.buffs); + buffs_BuffModel_Spawn(self); + + self.buff_model.color = buff.m_color; + self.buff_model.glowmod = buff_GlowColor(self.buff_model); + self.buff_model.skin = buff.m_skin; self.effects |= EF_NOSHADOW; } @@ -676,6 +874,8 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerThink) self.buff_model.alpha = self.alpha; } +#undef BUFF_ONADD +#undef BUFF_ONREM return false; } @@ -701,14 +901,14 @@ MUTATOR_HOOKFUNCTION(buffs_VehicleExit) MUTATOR_HOOKFUNCTION(buffs_PlayerRegen) { - if(self.buffs & BUFF_MEDIC) + if(self.buffs & BUFF_MEDIC.m_itemid) { regen_mod_rot = autocvar_g_buffs_medic_rot; regen_mod_limit = regen_mod_max = autocvar_g_buffs_medic_max; regen_mod_regen = autocvar_g_buffs_medic_regen; } - if(self.buffs & BUFF_SPEED) + if(self.buffs & BUFF_SPEED.m_itemid) regen_mod_regen = autocvar_g_buffs_speed_regen; return false; @@ -741,7 +941,7 @@ void buffs_DelayedInit() for(i = 0; i < autocvar_g_buffs_spawn_count; ++i) { entity e = spawn(); - e.spawnflags |= 1; // always randomize + e.spawnflags |= 64; // always randomize e.velocity = randomvec() * 250; // this gets reset anyway if random location works buff_Init(e); } @@ -750,7 +950,7 @@ void buffs_DelayedInit() void buffs_Initialize() { - precache_model("models/relics/relic.md3"); + precache_model(BUFF_MODEL); precache_sound("misc/strength_respawn.wav"); precache_sound("misc/shield_respawn.wav"); precache_sound("relics/relic_effect.wav"); @@ -758,7 +958,7 @@ void buffs_Initialize() precache_sound("keepaway/respawn.wav"); addstat(STAT_BUFFS, AS_INT, buffs); - addstat(STAT_MOVEVARS_JUMPVELOCITY, AS_FLOAT, stat_jumpheight); + addstat(STAT_BUFF_TIME, AS_FLOAT, buff_time); InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET); } @@ -776,12 +976,14 @@ MUTATOR_DEFINITION(mutator_buffs) MUTATOR_HOOK(VehicleExit, buffs_VehicleExit, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerRegen, buffs_PlayerRegen, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerDies, buffs_PlayerDies, CBC_ORDER_ANY); - MUTATOR_HOOK(PlayerUseKey, buffs_PlayerUseKey, CBC_ORDER_ANY); + MUTATOR_HOOK(PlayerUseKey, buffs_PlayerUseKey, CBC_ORDER_FIRST); + MUTATOR_HOOK(ForbidThrowCurrentWeapon, buffs_PlayerThrowKey, CBC_ORDER_ANY); MUTATOR_HOOK(MakePlayerObserver, buffs_RemovePlayer, CBC_ORDER_ANY); MUTATOR_HOOK(ClientDisconnect, buffs_RemovePlayer, CBC_ORDER_ANY); - MUTATOR_HOOK(OnEntityPreSpawn, buffs_OnEntityPreSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(OnEntityPreSpawn, buffs_OnEntityPreSpawn, CBC_ORDER_LAST); MUTATOR_HOOK(CustomizeWaypoint, buffs_CustomizeWaypoint, CBC_ORDER_ANY); MUTATOR_HOOK(WeaponRateFactor, buffs_WeaponRate, CBC_ORDER_ANY); + MUTATOR_HOOK(WeaponSpeedFactor, buffs_WeaponSpeed, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerPreThink, buffs_PlayerThink, CBC_ORDER_ANY); MUTATOR_HOOK(GetCvars, buffs_GetCvars, CBC_ORDER_ANY); MUTATOR_HOOK(BuildMutatorsString, buffs_BuildMutatorsString, CBC_ORDER_ANY);