1 #include "sv_powerups.qh"
3 MUTATOR_HOOKFUNCTION(powerups, W_PlayStrengthSound)
5 entity player = M_ARGV(0, entity);
7 if(StatusEffects_active(STATUSEFFECT_Strength, player)
8 && ((time > player.prevstrengthsound + autocvar_sv_strengthsound_antispam_time) // prevent insane sound spam
9 || (time > player.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold)))
11 sound(player, CH_TRIGGER, SND_STRENGTH_FIRE, VOL_BASE, ATTEN_NORM);
12 player.prevstrengthsound = time;
14 player.prevstrengthsoundattempt = time;
17 MUTATOR_HOOKFUNCTION(powerups, LogDeath_AppendItemCodes)
19 entity player = M_ARGV(0, entity);
21 if(StatusEffects_active(STATUSEFFECT_Strength, player))
22 M_ARGV(1, string) = strcat(M_ARGV(1, string), "S");
24 if(StatusEffects_active(STATUSEFFECT_Shield, player))
25 M_ARGV(1, string) = strcat(M_ARGV(1, string), "I");
27 // TODO: item codes for other powerups?
30 MUTATOR_HOOKFUNCTION(powerups, Damage_Calculate)
32 entity attacker = M_ARGV(1, entity);
33 entity targ = M_ARGV(2, entity);
35 // apply strength multiplier
36 if(StatusEffects_active(STATUSEFFECT_Strength, attacker))
40 M_ARGV(4, float) = M_ARGV(4, float) * autocvar_g_balance_powerup_strength_selfdamage;
41 M_ARGV(6, vector) = M_ARGV(6, vector) * autocvar_g_balance_powerup_strength_selfforce;
45 M_ARGV(4, float) = M_ARGV(4, float) * autocvar_g_balance_powerup_strength_damage;
46 M_ARGV(6, vector) = M_ARGV(6, vector) * autocvar_g_balance_powerup_strength_force;
50 // apply shield multiplier
51 if(StatusEffects_active(STATUSEFFECT_Shield, targ))
53 M_ARGV(4, float) = M_ARGV(4, float) * autocvar_g_balance_powerup_invincible_takedamage;
56 M_ARGV(6, vector) = M_ARGV(6, vector) * autocvar_g_balance_powerup_invincible_takeforce;
61 MUTATOR_HOOKFUNCTION(powerups, CustomizeWaypoint)
63 entity wp = M_ARGV(0, entity);
64 entity player = M_ARGV(1, entity);
66 entity e = WaypointSprite_getviewentity(player);
68 // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
69 // but only apply this to real players, not to spectators
70 if(IS_CLIENT(wp.owner) && (e == player) && DIFF_TEAM(wp.owner, e) && StatusEffects_active(STATUSEFFECT_Invisibility, wp.owner))
74 MUTATOR_HOOKFUNCTION(powerups, MonsterValidTarget)
76 entity targ = M_ARGV(1, entity);
77 return StatusEffects_active(STATUSEFFECT_Invisibility, targ);
80 void powerups_DropItem_Think(entity this)
82 TakeResource(this, RES_HEALTH, 1);
84 if(GetResource(this, RES_HEALTH) < 1) {
89 // Only needed to update if the timer of the powerup is running
90 if(!GetResource(this, RES_ARMOR))
91 WaypointSprite_UpdateHealth(this.waypointsprite_attached, GetResource(this, RES_HEALTH));
93 this.nextthink = time + 1;
96 void powerups_DropItem(entity this, StatusEffects effect, bool freezeTimer)
98 entity item = Item_DefinitionFromInternalName(effect.netname);
99 float t = StatusEffects_gettime(effect, this);
100 float timeleft = t - time;
103 if(timeleft <= 1 || !item)
107 // If we want the timer to keep running, we enable expiring then use the exact time the powerup will finish at.
108 // If we want the timer to freeze, we disable expiring and we just use the time left of the powerup.
109 // See Item_SetExpiring() below.
110 float finished = (freezeTimer ? timeleft : t);
112 // If the timer is frozen, the item will stay on the floor for 20 secs (same as weapons),
113 // otherwise it'll disappear after the timer runs out.
114 float time_to_live = (freezeTimer ? autocvar_g_items_dropped_lifetime : timeleft);
116 // TODO: items cannot hold their "item field" yet, so we need to list all the powerups here!
119 case ITEM_Strength: e.strength_finished = finished; maxtime = autocvar_g_balance_powerup_strength_time; break;
120 case ITEM_Shield: e.invincible_finished = finished; maxtime = autocvar_g_balance_powerup_invincible_time; break;
121 case ITEM_Invisibility: e.invisibility_finished = finished; maxtime = autocvar_g_balance_powerup_invincible_time; break;
122 case ITEM_Speed: e.speed_finished = finished; maxtime = autocvar_g_balance_powerup_speed_time; break;
124 vector vel = W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false);
125 if (!Item_InitializeLoot(e, item.m_canonical_spawnfunc, this.origin, vel, time_to_live))
128 e.item_spawnshieldtime = time + 0.5;
131 Item_SetExpiring(e, true);
133 // Use health as time left to live
134 SetResourceExplicit(e, RES_HEALTH, time_to_live);
136 // Use armor as timer freezer
138 SetResourceExplicit(e, RES_ARMOR, 1);
140 // Create waypoint displaying time left of the powerup
141 entity wp = WaypointSprite_Spawn(WP_Item, 0, 0, e, '0 0 1' * e.maxs.z, NULL, 0, e, waypointsprite_attached, true, RADARICON_Item);
142 wp.wp_extra = item.m_id;
143 WaypointSprite_UpdateMaxHealth(e.waypointsprite_attached, maxtime);
144 WaypointSprite_UpdateHealth(e.waypointsprite_attached, timeleft);
146 // Start dropping its time to live
147 setthink(e, powerups_DropItem_Think);
148 e.nextthink = time + 1;
151 MUTATOR_HOOKFUNCTION(powerups, ItemTouched)
153 entity e = M_ARGV(0, entity);
154 if(e.waypointsprite_attached)
155 WaypointSprite_Kill(e.waypointsprite_attached);
158 MUTATOR_HOOKFUNCTION(powerups, PlayerDies)
160 if(!autocvar_g_powerups_drop_ondeath)
163 entity frag_target = M_ARGV(2, entity);
165 FOREACH(StatusEffect, it.instanceOfPowerups,
167 if(StatusEffects_active(it, frag_target))
168 powerups_DropItem(frag_target, it, autocvar_g_powerups_drop_ondeath == 2);
172 MUTATOR_HOOKFUNCTION(powerups, PlayerUseKey)
174 if(MUTATOR_RETURNVALUE || game_stopped || !autocvar_g_powerups_drop) return;
176 entity player = M_ARGV(0, entity);
178 FOREACH(StatusEffect, it.instanceOfPowerups,
180 if(StatusEffects_active(it, player)) {
181 powerups_DropItem(player, it, autocvar_g_powerups_drop == 2);
182 StatusEffects_remove(it, player, STATUSEFFECT_REMOVE_NORMAL);
188 MUTATOR_HOOKFUNCTION(powerups, PlayerPhysics_UpdateStats)
190 entity player = M_ARGV(0, entity);
191 // these automatically reset, no need to worry
193 if(StatusEffects_active(STATUSEFFECT_Speed, player))
194 STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_balance_powerup_speed_highspeed;
197 MUTATOR_HOOKFUNCTION(powerups, WeaponRateFactor)
199 entity player = M_ARGV(1, entity);
201 if(StatusEffects_active(STATUSEFFECT_Speed, player))
202 M_ARGV(0, float) *= autocvar_g_balance_powerup_speed_attackrate;
205 MUTATOR_HOOKFUNCTION(powerups, BuildMutatorsPrettyString)
207 if(autocvar_g_powerups == 0)
208 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", No powerups");
209 if(autocvar_g_powerups > 0)
210 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Powerups");
213 MUTATOR_HOOKFUNCTION(powerups, BotShouldAttack)
215 entity targ = M_ARGV(1, entity);
217 if(StatusEffects_active(STATUSEFFECT_Invisibility, targ))
221 MUTATOR_HOOKFUNCTION(powerups, BuildMutatorsString)
223 if(autocvar_g_powerups == 0)
224 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":no_powerups");
225 if(autocvar_g_powerups > 0)
226 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":powerups");