../common/teams.qh
../common/util.qh
+../common/nades.qh
+ ../common/buffs.qh
../common/test.qh
../common/counting.qh
../common/items.qh
../common/monsters/monsters.qc
+../common/nades.qc
+ ../common/buffs.qc
../warpzonelib/anglestransform.qc
../warpzonelib/mathlib.qc
if(SAME_TEAM(targ, ent))
return FALSE; // enemy is on our team
- if (targ.freezetag_frozen)
+ if (targ.frozen)
return FALSE; // ignore frozen
- if(autocvar_g_monsters_target_infront || ent.spawnflags & MONSTERFLAG_INFRONT)
+ if(autocvar_g_monsters_target_infront || (ent.spawnflags & MONSTERFLAG_INFRONT))
if(ent.enemy != targ)
{
float dot;
// enemy is always preferred target
if(self.enemy)
{
- makevectors(self.angles);
+ vector targ_origin = ((self.enemy.absmin + self.enemy.absmax) * 0.5);
+ targ_origin = WarpZone_RefSys_TransformOrigin(self.enemy, self, targ_origin); // origin of target as seen by the monster (us)
+ WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self);
+
+ if((self.enemy == world)
+ || (self.enemy.deadflag != DEAD_NO || self.enemy.health < 1)
- || (self.enemy.freezetag_frozen)
++ || (self.enemy.frozen)
+ || (self.enemy.flags & FL_NOTARGET)
+ || (self.enemy.alpha < 0.5)
+ || (self.enemy.takedamage == DAMAGE_NO)
+ || (vlen(self.origin - targ_origin) > self.target_range)
+ || ((trace_fraction < 1) && (trace_ent != self.enemy)))
+ //|| (time > self.ctf_droptime + autocvar_g_ctf_pass_timelimit)) // TODO: chase timelimit?
+ {
+ self.enemy = world;
+ self.pass_distance = 0;
+ }
+
+ if(self.enemy)
+ {
+ /*WarpZone_TrailParticles(world, particleeffectnum("red_pass"), self.origin, targ_origin);
+ print("Trace origin: ", vtos(targ_origin), "\n");
+ print("Target origin: ", vtos(self.enemy.origin), "\n");
+ print("My origin: ", vtos(self.origin), "\n"); */
+
+ self.monster_movestate = MONSTER_MOVE_ENEMY;
+ self.last_trace = time + 1.2;
+ return targ_origin;
+ }
+
+ /*makevectors(self.angles);
self.monster_movestate = MONSTER_MOVE_ENEMY;
self.last_trace = time + 1.2;
- return self.enemy.origin;
+ return self.enemy.origin; */
}
switch(self.monster_moveflags)
{
self.think = monster_dead_think;
self.nextthink = time;
- self.ltime = time + 5;
+ self.monster_lifetime = time + 5;
+ if(self.frozen)
+ {
+ Unfreeze(self); // remove any icy remains
+ self.health = 0; // reset by Unfreeze
+ }
+
monster_dropitem();
MonsterSound(monstersound_death, 0, FALSE, CH_VOICE);
void monsters_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
+ if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
+ return;
+
+ if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
+ return;
+
if(time < self.pain_finished && deathtype != DEATH_KILL)
return;
--- /dev/null
-// 69 empty?
+ // Full list of all stat constants, icnluded in a single location for easy reference
+ // 255 is the current limit (MAX_CL_STATS - 1), engine will need to be modified if you wish to add more stats
+
+ const float MAX_CL_STATS = 256;
+ const float STAT_HEALTH = 0;
+ // 1 empty?
+ const float STAT_WEAPON = 2;
+ const float STAT_AMMO = 3;
+ const float STAT_ARMOR = 4;
+ const float STAT_WEAPONFRAME = 5;
+ const float STAT_SHELLS = 6;
+ const float STAT_NAILS = 7;
+ const float STAT_ROCKETS = 8;
+ const float STAT_CELLS = 9;
+ const float STAT_ACTIVEWEAPON = 10;
+ const float STAT_TOTALSECRETS = 11;
+ const float STAT_TOTALMONSTERS = 12;
+ const float STAT_SECRETS = 13;
+ const float STAT_MONSTERS = 14;
+ const float STAT_ITEMS = 15;
+ const float STAT_VIEWHEIGHT = 16;
+ // 17 empty?
+ // 18 empty?
+ // 19 empty?
+ // 20 empty?
+ const float STAT_VIEWZOOM = 21;
+ // 22 empty?
+ // 23 empty?
+ // 24 empty?
+ // 25 empty?
+ // 26 empty?
+ // 27 empty?
+ // 28 empty?
+ // 29 empty?
+ // 30 empty?
+ // 31 empty?
+ const float STAT_KH_KEYS = 32;
+ const float STAT_CTF_STATE = 33;
+ // 34 empty?
+ const float STAT_WEAPONS = 35;
+ const float STAT_SWITCHWEAPON = 36;
+ const float STAT_GAMESTARTTIME = 37;
+ const float STAT_STRENGTH_FINISHED = 38;
+ const float STAT_INVINCIBLE_FINISHED = 39;
+ // 40 empty?
+ // 41 empty?
+ const float STAT_PRESSED_KEYS = 42;
+ const float STAT_ALLOW_OLDNEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config
+ const float STAT_FUEL = 44;
+ const float STAT_NB_METERSTART = 45;
+ const float STAT_SHOTORG = 46; // compressShotOrigin
+ const float STAT_LEADLIMIT = 47;
+ const float STAT_WEAPON_CLIPLOAD = 48;
+ const float STAT_WEAPON_CLIPSIZE = 49;
+ const float STAT_NEX_CHARGE = 50;
+ const float STAT_LAST_PICKUP = 51;
+ const float STAT_HUD = 52;
+ const float STAT_NEX_CHARGEPOOL = 53;
+ const float STAT_HIT_TIME = 54;
+ const float STAT_TYPEHIT_TIME = 55;
+ const float STAT_LAYED_MINES = 56;
+ const float STAT_HAGAR_LOAD = 57;
+ const float STAT_SWITCHINGWEAPON = 58;
+ const float STAT_SUPERWEAPONS_FINISHED = 59;
+ const float STAT_VEHICLESTAT_HEALTH = 60;
+ const float STAT_VEHICLESTAT_SHIELD = 61;
+ const float STAT_VEHICLESTAT_ENERGY = 62;
+ const float STAT_VEHICLESTAT_AMMO1 = 63;
+ const float STAT_VEHICLESTAT_RELOAD1 = 64;
+ const float STAT_VEHICLESTAT_AMMO2 = 65;
+ const float STAT_VEHICLESTAT_RELOAD2 = 66;
+ const float STAT_VEHICLESTAT_W2MODE = 67;
+ // 68 empty?
-// 79 empty?
-// 80 empty?
++const float STAT_NADE_TIMER = 69;
+ const float STAT_SECRETS_TOTAL = 70;
+ const float STAT_SECRETS_FOUND = 71;
+ const float STAT_RESPAWN_TIME = 72;
+ const float STAT_ROUNDSTARTTIME = 73;
+ const float STAT_WEAPONS2 = 74;
+ const float STAT_WEAPONS3 = 75;
+ const float STAT_MONSTERS_TOTAL = 76;
+ const float STAT_MONSTERS_KILLED = 77;
+ const float STAT_BUFFS = 78;
++const float STAT_NADE_BONUS = 79;
++const float STAT_NADE_BONUS_TYPE = 80;
++const float STAT_NADE_BONUS_SCORE = 81;
++const float STAT_HEALING_ORB = 82;
++const float STAT_HEALING_ORB_ALPHA = 83;
++// 84 empty?
++// 85 empty?
+ // 86 empty?
+ // 87 empty?
+ // 88 empty?
+ // 89 empty?
+ // 90 empty?
+ // 91 empty?
+ // 92 empty?
+ // 93 empty?
+ // 94 empty?
+ // 95 empty?
+ // 96 empty?
+ // 97 empty?
+ // 98 empty?
+ // 99 empty?
+
+
+ /* The following stats change depending on the gamemode, so can share the same ID */
+ // IDs 100 to 104 reserved for gamemodes
+
+ // freeze tag, clan arena, jailbreak
+ const float STAT_REDALIVE = 100;
+ const float STAT_BLUEALIVE = 101;
+ const float STAT_YELLOWALIVE = 102;
+ const float STAT_PINKALIVE = 103;
+
+ // domination
+ const float STAT_DOM_TOTAL_PPS = 100;
+ const float STAT_DOM_PPS_RED = 101;
+ const float STAT_DOM_PPS_BLUE = 102;
+ const float STAT_DOM_PPS_YELLOW = 103;
+ const float STAT_DOM_PPS_PINK = 104;
+
+ // vip
+ const float STAT_VIP = 100;
+ const float STAT_VIP_RED = 101;
+ const float STAT_VIP_BLUE = 102;
+ const float STAT_VIP_YELLOW = 103;
+ const float STAT_VIP_PINK = 104;
+
+ // key hunt
+ const float STAT_KH_REDKEY_TEAM = 100;
+ const float STAT_KH_BLUEKEY_TEAM = 101;
+ const float STAT_KH_YELLOWKEY_TEAM = 102;
+ const float STAT_KH_PINKKEY_TEAM = 103;
+
+ /* Gamemode-specific stats end here */
+
+
+ const float STAT_FROZEN = 105;
+ const float STAT_REVIVE_PROGRESS = 106;
+ // 107 empty?
+ // 108 empty?
+ // 109 empty?
+ // 110 empty?
+ // 111 empty?
+ // 112 empty?
+ // 113 empty?
+ // 114 empty?
+ // 115 empty?
+ // 116 empty?
+ // 117 empty?
+ // 118 empty?
+ // 119 empty?
+ // 120 empty?
+ // 121 empty?
+ // 122 empty?
+ // 123 empty?
+ // 124 empty?
+ // 125 empty?
+ // 126 empty?
+ // 127 empty?
+ // 128 empty?
+ // 129 empty?
+ // 130 empty?
+ // 131 empty?
+ // 132 empty?
+ // 133 empty?
+ // 134 empty?
+ // 135 empty?
+ // 136 empty?
+ // 137 empty?
+ // 138 empty?
+ // 139 empty?
+ // 140 empty?
+ // 141 empty?
+ // 142 empty?
+ // 143 empty?
+ // 144 empty?
+ // 145 empty?
+ // 146 empty?
+ // 147 empty?
+ // 148 empty?
+ // 149 empty?
+ // 150 empty?
+ // 151 empty?
+ // 152 empty?
+ // 153 empty?
+ // 154 empty?
+ // 155 empty?
+ // 156 empty?
+ // 157 empty?
+ // 158 empty?
+ // 159 empty?
+ // 160 empty?
+ // 161 empty?
+ // 162 empty?
+ // 162 empty?
+ // 163 empty?
+ // 164 empty?
+ // 165 empty?
+ // 166 empty?
+ // 167 empty?
+ // 168 empty?
+ // 169 empty?
+ // 170 empty?
+ // 171 empty?
+ // 172 empty?
+ // 173 empty?
+ // 174 empty?
+ // 175 empty?
+ // 176 empty?
+ // 177 empty?
+ // 178 empty?
+ // 179 empty?
+ // 180 empty?
+ // 181 empty?
+ // 182 empty?
+ // 183 empty?
+ // 184 empty?
+ // 185 empty?
+ // 186 empty?
+ // 187 empty?
+ // 188 empty?
+ // 189 empty?
+ // 190 empty?
+ // 191 empty?
+ // 192 empty?
+ // 193 empty?
+ // 194 empty?
+ // 195 empty?
+ // 196 empty?
+ // 197 empty?
+ // 198 empty?
+ // 199 empty?
+ // 200 empty?
+ // 201 empty?
+ // 202 empty?
+ // 203 empty?
+ // 204 empty?
+ // 205 empty?
+ // 206 empty?
+ // 207 empty?
+ // 208 empty?
+ // 209 empty?
+ // 210 empty?
+ // 211 empty?
+ // 212 empty?
+ // 213 empty?
+ // 214 empty?
+ // 215 empty?
+ // 216 empty?
+ // 217 empty?
+ // 218 empty?
+ // 219 empty?
+ const float STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR = 220;
+ const float STAT_MOVEVARS_AIRCONTROL_PENALTY = 221;
+ const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222;
+ const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223;
+ const float STAT_MOVEVARS_AIRCONTROL_POWER = 224;
+ const float STAT_MOVEFLAGS = 225;
+ const float STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL = 226;
+ const float STAT_MOVEVARS_WARSOWBUNNY_ACCEL = 227;
+ const float STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED = 228;
+ const float STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL = 229;
+ const float STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO = 230;
+ const float STAT_MOVEVARS_AIRSTOPACCELERATE = 231;
+ const float STAT_MOVEVARS_AIRSTRAFEACCELERATE = 232;
+ const float STAT_MOVEVARS_MAXAIRSTRAFESPEED = 233;
+ const float STAT_MOVEVARS_AIRCONTROL = 234;
+ const float STAT_FRAGLIMIT = 235;
+ const float STAT_TIMELIMIT = 236;
+ const float STAT_MOVEVARS_WALLFRICTION = 237;
+ const float STAT_MOVEVARS_FRICTION = 238;
+ const float STAT_MOVEVARS_WATERFRICTION = 239;
+ const float STAT_MOVEVARS_TICRATE = 240;
+ const float STAT_MOVEVARS_TIMESCALE = 241;
+ const float STAT_MOVEVARS_GRAVITY = 242;
+ const float STAT_MOVEVARS_STOPSPEED = 243;
+ const float STAT_MOVEVARS_MAXSPEED = 244;
+ const float STAT_MOVEVARS_SPECTATORMAXSPEED = 245;
+ const float STAT_MOVEVARS_ACCELERATE = 246;
+ const float STAT_MOVEVARS_AIRACCELERATE = 247;
+ const float STAT_MOVEVARS_WATERACCELERATE = 248;
+ const float STAT_MOVEVARS_ENTGRAVITY = 249;
+ const float STAT_MOVEVARS_JUMPVELOCITY = 250;
+ const float STAT_MOVEVARS_EDGEFRICTION = 251;
+ const float STAT_MOVEVARS_MAXAIRSPEED = 252;
+ const float STAT_MOVEVARS_STEPHEIGHT = 253;
+ const float STAT_MOVEVARS_AIRACCEL_QW = 254;
+ const float STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION = 255;
void player_regen (void)
{
+ float max_mod, regen_mod, rot_mod, limit_mod;
+ max_mod = regen_mod = rot_mod = limit_mod = 1;
+ regen_mod_max = max_mod;
+ regen_mod_regen = regen_mod;
+ regen_mod_rot = rot_mod;
+ regen_mod_limit = limit_mod;
if(!MUTATOR_CALLHOOK(PlayerRegen))
+ if(!self.frozen)
{
- float minh, mina, maxh, maxa, limith, limita, max_mod, regen_mod, rot_mod, limit_mod;
+ float minh, mina, maxh, maxa, limith, limita;
maxh = autocvar_g_balance_health_rotstable;
maxa = autocvar_g_balance_armor_rotstable;
minh = autocvar_g_balance_health_regenstable;
*/
void PlayerJump (void)
{
+ if(self.frozen)
+ return; // no jumping in freezetag when frozen
+
+ if(self.player_blocked)
+ return; // no jumping while blocked
+
float doublejump = FALSE;
+ float mjumpheight = autocvar_sv_jumpvelocity;
player_multijump = doublejump;
+ player_jumpheight = mjumpheight;
if(MUTATOR_CALLHOOK(PlayerJump))
return;
else if(MUTATOR_CALLHOOK(AllowMobSpawning)) { sprint(self, "Monster spawning is currently disabled by a mutator.\n"); return; }
else if(!autocvar_g_monsters) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_MONSTERS_DISABLED); return; }
else if(self.vehicle) { sprint(self, "You can't spawn monsters while driving a vehicle.\n"); return; }
- else if(self.freezetag_frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; }
+ else if(self.frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; }
else if(autocvar_g_campaign) { sprint(self, "You can't spawn monsters in campaign mode.\n"); return; }
else if(self.deadflag != DEAD_NO) { sprint(self, "You can't spawn monsters while dead.\n"); return; }
- else if(self.monstercount >= autocvar_g_monsters_max_perplayer) { sprint(self, "You have spawned too many monsters, kill some before trying to spawn any more.\n"); return; }
+ else if(monstercount >= autocvar_g_monsters_max_perplayer) { sprint(self, "You have spawned too many monsters, kill some before trying to spawn any more.\n"); return; }
else if(totalspawned >= autocvar_g_monsters_max) { sprint(self, "The global maximum monster count has been reached, kill some before trying to spawn any more.\n"); return; }
else if(tospawn != "")
{
e.fire_hitsound = TRUE;
if (!IS_INDEPENDENT_PLAYER(e))
- if (!e.frozen)
- if(!e.freezetag_frozen)
++ if(!e.frozen)
FOR_EACH_PLAYER(other) if(e != other)
{
if(IS_PLAYER(other))
}
// special touch behaviors
- if(toucher.vehicle_flags & VHF_ISVEHICLE)
+ if(toucher.frozen) { return; }
+ else if(toucher.vehicle_flags & VHF_ISVEHICLE)
{
- if(autocvar_g_ctf_allow_vehicle_touch)
+ if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner)
toucher = toucher.owner; // the player is actually the vehicle owner, not other
else
return; // do nothing
MUTATOR_HOOK(reset_map_players, freezetag_reset_map_players, CBC_ORDER_ANY);
MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
- MUTATOR_HOOK(HavocBot_ChooseRule, freezetag_BotRoles, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
- MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerJump, freezetag_PlayerJump, CBC_ORDER_ANY);
- MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
- MUTATOR_HOOK(ItemTouch, freezetag_ItemTouch, CBC_ORDER_ANY);
+ MUTATOR_HOOK(HavocBot_ChooseRole, freezetag_BotRoles, CBC_ORDER_ANY);
- MUTATOR_HOOK(SpectateCopy, freezetag_SpectateCopy, CBC_ORDER_ANY);
MUTATOR_HOOK(GetTeamCount, freezetag_GetTeamCount, CBC_ORDER_EXCLUSIVE);
- MUTATOR_HOOK(VehicleTouch, freezetag_VehicleTouch, CBC_ORDER_ANY);
MUTATOR_ONADD
{
--- /dev/null
- || (other.freezetag_frozen)
+ float buffs_BuffModel_Customize()
+ {
+ entity player, myowner;
+ float same_team;
+
+ player = WaypointSprite_getviewentity(other);
+ myowner = self.owner;
+ same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner));
+
+ if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0)
+ return FALSE;
+
+ if(player == myowner || (IS_SPEC(other) && other.enemy == myowner))
+ {
+ // somewhat hide the model, but keep the glow
+ self.effects = 0;
+ self.alpha = -1;
+ }
+ else
+ {
+ self.effects = EF_FULLBRIGHT | EF_LOWPRECISION;
+ self.alpha = 1;
+ }
+ return TRUE;
+ }
+
+ // buff item
+ float buff_Waypoint_visible_for_player(entity plr)
+ {
+ if(!self.owner.buff_active && !self.owner.buff_activetime)
+ return FALSE;
+
+ if(plr.buffs)
+ {
+ if(plr.cvar_cl_buffs_autoreplace)
+ {
+ if(plr.buffs == self.owner.buffs)
+ return FALSE;
+ }
+ else
+ return FALSE;
+ }
+
+ 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;
+ }
+
+ void buff_SetCooldown(float cd)
+ {
+ cd = max(0, cd);
+
+ if(!self.buff_waypoint)
+ buff_Waypoint_Spawn(self);
+
+ WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + cd);
+ self.buff_activetime = cd;
+ self.buff_active = !cd;
+ }
+
+ void buff_Respawn(entity ent)
+ {
+ if(gameover) { return; }
+
+ vector oldbufforigin = ent.origin;
+
+ if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256))
+ {
+ entity spot = SelectSpawnPoint(TRUE);
+ setorigin(ent, ((spot.origin + '0 0 200') + (randomvec() * 300)));
+ ent.angles = spot.angles;
+ }
+
+ tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent);
+
+ setorigin(ent, trace_endpos); // attempt to unstick
+
+ ent.movetype = MOVETYPE_TOSS;
+
+ makevectors(ent.angles);
+ ent.velocity = '0 0 200';
+ ent.angles = '0 0 0';
+ if(autocvar_g_buffs_random_lifetime > 0)
+ ent.lifetime = time + autocvar_g_buffs_random_lifetime;
+
+ pointparticles(particleeffectnum("electro_combo"), oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1);
+ pointparticles(particleeffectnum("electro_combo"), CENTER_OR_VIEWOFS(ent), '0 0 0', 1);
+
+ WaypointSprite_Ping(ent.buff_waypoint);
+
+ sound(ent, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+ }
+
+ void buff_Touch()
+ {
+ if(gameover) { return; }
+
+ if(ITEM_TOUCH_NEEDKILL())
+ {
+ buff_Respawn(self);
+ return;
+ }
+
+ if((self.team && DIFF_TEAM(other, self))
- if(!self.owner || self.owner.freezetag_frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
++ || (other.frozen)
+ || (other.vehicle)
+ || (!IS_PLAYER(other))
+ || (!self.buff_active)
+ )
+ {
+ // can't touch this
+ return;
+ }
+
+ if(other.buffs)
+ {
+ if(other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs)
+ {
+ //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);
+
+ other.buffs = 0;
+ //sound(other, CH_TRIGGER, "relics/relic_effect.wav", VOL_BASE, ATTN_NORM);
+ }
+ else { return; } // do nothing
+ }
+
+ 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);
+
+ 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)
+ {
+ if(buffid == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only"))))
+ return FALSE;
+
+ if(buffid == BUFF_VAMPIRE && cvar("g_vampire"))
+ return FALSE;
+
+ if(!cvar(strcat("g_buffs_", Buff_Name(buffid))))
+ return FALSE;
+
+ return TRUE;
+ }
+
+ 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;
+ }
+ ent.buffs = RandomSelection_chosen_float;
+ }
+
+ 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);
+
+ setmodel(self, "models/relics/relic.md3");
+
+ if(self.buff_waypoint)
+ {
+ //WaypointSprite_Disown(self.buff_waypoint, 1);
+ WaypointSprite_Kill(self.buff_waypoint);
+ buff_Waypoint_Spawn(self);
+ if(self.buff_activetime)
+ WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + self.buff_activetime - frametime);
+ }
+
+ self.oldbuffs = self.buffs;
+ }
+
+ if(!gameover)
+ if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+ if(!self.buff_activetime_updated)
+ {
+ buff_SetCooldown(self.buff_activetime);
+ self.buff_activetime_updated = TRUE;
+ }
+
+ if(!self.buff_active && !self.buff_activetime)
- if(!frag_target.freezetag_frozen)
++ if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
+ {
+ buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime);
+ self.owner = world;
+ if(autocvar_g_buffs_randomize)
+ buff_NewType(self, self.buffs);
+
+ if(autocvar_g_buffs_random_location || (self.spawnflags & 1))
+ buff_Respawn(self);
+ }
+
+ if(self.buff_activetime)
+ if(!gameover)
+ if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+ {
+ self.buff_activetime = max(0, self.buff_activetime - frametime);
+
+ if(!self.buff_activetime)
+ {
+ self.buff_active = TRUE;
+ sound(self, CH_TRIGGER, "misc/strength_respawn.wav", VOL_BASE, ATTN_NORM);
+ pointparticles(particleeffectnum("item_respawn"), CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+ }
+ }
+
+ if(!self.buff_active)
+ {
+ self.alpha = 0.3;
+ self.effects &= ~(EF_FULLBRIGHT);
+ self.pflags = 0;
+ }
+ else
+ {
+ 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);
+
+ if(self.lifetime)
+ if(time >= self.lifetime)
+ buff_Respawn(self);
+ }
+
+ self.nextthink = time;
+ //self.angles_y = time * 110.1;
+ }
+
+ void buff_Waypoint_Reset()
+ {
+ WaypointSprite_Kill(self.buff_waypoint);
+
+ if(self.buff_activetime) { buff_Waypoint_Spawn(self); }
+ }
+
+ void buff_Reset()
+ {
+ if(autocvar_g_buffs_randomize)
+ buff_NewType(self, self.buffs);
+ self.owner = world;
+ buff_SetCooldown(autocvar_g_buffs_cooldown_activate);
+ buff_Waypoint_Reset();
+ self.buff_activetime_updated = FALSE;
+
+ if(autocvar_g_buffs_random_location || (self.spawnflags & 1))
+ buff_Respawn(self);
+ }
+
+ void buff_Init(entity ent)
+ {
+ if(!cvar("g_buffs")) { remove(self); return; }
+
+ if(!teamplay && self.team) { self.team = 0; }
+
+ entity oldself = self;
+ self = ent;
+ if(!self.buffs || buff_Available(self.buffs))
+ buff_NewType(self, 0);
+
+ self.classname = "item_buff";
+ self.solid = SOLID_TRIGGER;
+ self.flags = FL_ITEM;
+ self.think = buff_Think;
+ self.touch = buff_Touch;
+ self.reset = buff_Reset;
+ self.nextthink = time + 0.1;
+ self.gravity = 1;
+ self.movetype = MOVETYPE_TOSS;
+ self.scale = 1;
+ self.skin = Buff_Skin(self.buffs);
+ self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW;
+ self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
+ //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);
+ buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime);
+ self.buff_active = !self.buff_activetime;
+ self.pflags = PFLAGS_FULLDYNAMIC;
+
+ if(self.noalign)
+ self.movetype = MOVETYPE_NONE; // reset by random location
+
+ setmodel(self, "models/relics/relic.md3");
+ setsize(self, BUFF_MIN, BUFF_MAX);
+
+ if(cvar("g_buffs_random_location") || (self.spawnflags & 1))
+ buff_Respawn(self);
+
+ self = oldself;
+ }
+
+ void buff_Init_Compat(entity ent, float replacement)
+ {
+ if(ent.spawnflags & 2)
+ ent.team = NUM_TEAM_1;
+ else if(ent.spawnflags & 4)
+ ent.team = NUM_TEAM_2;
+
+ ent.buffs = replacement;
+
+ buff_Init(ent);
+ }
+
+ void buff_SpawnReplacement(entity ent, entity old)
+ {
+ setorigin(ent, old.origin);
+ ent.angles = old.angles;
+ ent.noalign = old.noalign;
+
+ buff_Init(ent);
+ }
+
+ // mutator hooks
+ MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_SplitHealthArmor)
+ {
+ if(frag_deathtype == DEATH_BUFF_VENGEANCE) { return FALSE; } // oh no you don't
+
+ if(frag_target.buffs & BUFF_RESISTANCE)
+ {
+ vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage);
+ damage_take = v_x;
+ damage_save = v_y;
+ }
+
+ 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_target.buffs & BUFF_SPEED)
+ if(frag_target != frag_attacker)
+ frag_damage *= autocvar_g_buffs_speed_damage_take;
+
+ if(frag_target.buffs & BUFF_MEDIC)
+ 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;
+
+ if(frag_target.buffs & BUFF_VENGEANCE)
+ if(frag_attacker)
+ if(frag_attacker != frag_target)
+ if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+ {
+ entity dmgent = spawn();
+
+ dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier;
+ dmgent.enemy = frag_attacker;
+ dmgent.owner = frag_target;
+ dmgent.think = buff_Vengeance_DelayedDamage;
+ dmgent.nextthink = time + 0.1;
+ }
+
+ if(frag_target.buffs & BUFF_BASH)
+ if(frag_attacker != frag_target)
+ if(vlen(frag_force))
+ frag_force = '0 0 0';
+
+ if(frag_attacker.buffs & BUFF_BASH)
+ 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_target != frag_attacker)
+ frag_target.buff_disability_time = time + autocvar_g_buffs_disability_time;
+
+ if(frag_attacker.buffs & BUFF_MEDIC)
+ if(SAME_TEAM(frag_attacker, frag_target))
+ if(frag_attacker != frag_target)
+ {
+ frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage);
+ frag_damage = 0;
+ }
+
+ // this... is ridiculous (TODO: fix!)
+ if(frag_attacker.buffs & BUFF_VAMPIRE)
+ if(!frag_target.vehicle)
+ if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+ if(frag_target.deadflag == DEAD_NO)
+ if(IS_PLAYER(frag_target) || (frag_target.flags & FL_MONSTER))
+ if(frag_attacker != frag_target)
- if(self.freezetag_frozen)
++ 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);
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerSpawn)
+ {
+ self.buffs = 0;
+ // reset timers here to prevent them continuing after re-spawn
+ self.buff_disability_time = 0;
+ self.buff_disability_effect_time = 0;
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerPhysics)
+ {
+ if(self.buffs & BUFF_SPEED)
+ {
+ self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed;
+ self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed;
+ }
+
+ if(time < self.buff_disability_time)
+ {
+ self.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed;
+ self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed;
+ }
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerJump)
+ {
+ if(self.buffs & BUFF_JUMP)
+ player_jumpheight = autocvar_g_buffs_jump_height;
+ self.stat_jumpheight = player_jumpheight;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_MonsterMove)
+ {
+ if(time < self.buff_disability_time)
+ {
+ monster_speed_walk *= autocvar_g_buffs_disability_speed;
+ monster_speed_run *= autocvar_g_buffs_disability_speed;
+ }
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerDies)
+ {
+ if(self.buffs)
+ {
+ Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs);
+ self.buffs = 0;
+
+ if(self.buff_model)
+ {
+ remove(self.buff_model);
+ self.buff_model = world;
+ }
+ }
+ return FALSE;
+ }
+
+ 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);
+
+ self.buffs = 0;
+ sound(self, CH_TRIGGER, "relics/relic_effect.wav", VOL_BASE, ATTN_NORM);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_RemovePlayer)
+ {
+ if(self.buff_model)
+ {
+ remove(self.buff_model);
+ self.buff_model = world;
+ }
+
+ // also reset timers here to prevent them continuing after spectating
+ self.buff_disability_time = 0;
+ self.buff_disability_effect_time = 0;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_CustomizeWaypoint)
+ {
+ entity e = WaypointSprite_getviewentity(other);
+
+ // 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(DIFF_TEAM(self.owner, e))
+ return TRUE;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_OnEntityPreSpawn)
+ {
+ if(autocvar_g_buffs_replace_powerups)
+ switch(self.classname)
+ {
+ case "item_strength":
+ case "item_invincible":
+ {
+ entity e = spawn();
+ buff_SpawnReplacement(e, self);
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_WeaponRate)
+ {
+ if(self.buffs & BUFF_SPEED)
+ weapon_rate *= autocvar_g_buffs_speed_rate;
+
+ if(time < self.buff_disability_time)
+ weapon_rate *= autocvar_g_buffs_disability_rate;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerThink)
+ {
+ if(gameover || self.deadflag != DEAD_NO) { return FALSE; }
+
+ if(time < self.buff_disability_time)
+ if(time >= self.buff_disability_effect_time)
+ {
+ pointparticles(particleeffectnum("smoking"), self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1);
+ self.buff_disability_effect_time = time + 0.5;
+ }
+
++ if(self.frozen)
+ {
+ if(self.buffs)
+ {
+ Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs);
+ self.buffs = 0;
+ }
+ }
+
+ if((self.buffs & BUFF_INVISIBLE) && (self.oldbuffs & BUFF_INVISIBLE))
+ if(self.alpha != autocvar_g_buffs_invisible_alpha)
+ self.alpha = autocvar_g_buffs_invisible_alpha;
+
+ if(self.buffs != self.oldbuffs)
+ {
+ if(self.oldbuffs & 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.oldbuffs & BUFF_INVISIBLE)
+ {
+ if(time < self.strength_finished && g_minstagib)
+ self.alpha = autocvar_g_minstagib_invis_alpha;
+ else
+ self.alpha = self.buff_invisible_prev_alpha;
+ }
+ else if(self.buffs & BUFF_INVISIBLE)
+ {
+ if(time < self.strength_finished && g_minstagib)
+ 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)
+ {
+ self.buff_flight_prev_gravity = self.gravity;
+ self.gravity = autocvar_g_buffs_flight_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);
+
+ self.effects |= EF_NOSHADOW;
+ }
+ else
+ {
+ remove(self.buff_model);
+ self.buff_model = world;
+
+ self.effects &= ~(EF_NOSHADOW);
+ }
+ }
+
+ if(self.buff_model)
+ {
+ self.buff_model.effects = self.effects;
+ self.buff_model.effects |= EF_LOWPRECISION;
+ self.buff_model.effects = self.buff_model.effects & EFMASK_CHEAP; // eat performance
+
+ self.buff_model.alpha = self.alpha;
+ }
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_SpectateCopy)
+ {
+ self.buffs = other.buffs;
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_VehicleEnter)
+ {
+ vh_vehicle.buffs = vh_player.buffs;
+ vh_player.buffs = 0;
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_VehicleExit)
+ {
+ vh_player.buffs = vh_vehicle.buffs;
+ vh_vehicle.buffs = 0;
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerRegen)
+ {
+ if(self.buffs & BUFF_MEDIC)
+ {
+ 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)
+ regen_mod_regen = autocvar_g_buffs_speed_regen;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_GetCvars)
+ {
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace");
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_BuildMutatorsString)
+ {
+ ret_string = strcat(ret_string, ":Buffs");
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_BuildMutatorsPrettyString)
+ {
+ ret_string = strcat(ret_string, ", Buffs");
+ return FALSE;
+ }
+
+ void buffs_DelayedInit()
+ {
+ if(autocvar_g_buffs_spawn_count > 0)
+ if(find(world, classname, "item_buff") == world)
+ {
+ float i;
+ for(i = 0; i < autocvar_g_buffs_spawn_count; ++i)
+ {
+ entity e = spawn();
+ e.spawnflags |= 1; // always randomize
+ e.velocity = randomvec() * 250; // this gets reset anyway if random location works
+ buff_Init(e);
+ }
+ }
+ }
+
+ void buffs_Initialize()
+ {
+ precache_model("models/relics/relic.md3");
+ precache_sound("misc/strength_respawn.wav");
+ precache_sound("misc/shield_respawn.wav");
+ precache_sound("relics/relic_effect.wav");
+ precache_sound("weapons/rocket_impact.wav");
+ precache_sound("keepaway/respawn.wav");
+
+ addstat(STAT_BUFFS, AS_INT, buffs);
+ addstat(STAT_MOVEVARS_JUMPVELOCITY, AS_FLOAT, stat_jumpheight);
+
+ InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET);
+ }
+
+ MUTATOR_DEFINITION(mutator_buffs)
+ {
+ MUTATOR_HOOK(PlayerDamage_SplitHealthArmor, buffs_PlayerDamage_SplitHealthArmor, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDamage_Calculate, buffs_PlayerDamage_Calculate, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerSpawn, buffs_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPhysics, buffs_PlayerPhysics, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerJump, buffs_PlayerJump, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MonsterMove, buffs_MonsterMove, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SpectateCopy, buffs_SpectateCopy, CBC_ORDER_ANY);
+ MUTATOR_HOOK(VehicleEnter, buffs_VehicleEnter, CBC_ORDER_ANY);
+ 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(MakePlayerObserver, buffs_RemovePlayer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientDisconnect, buffs_RemovePlayer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(OnEntityPreSpawn, buffs_OnEntityPreSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(CustomizeWaypoint, buffs_CustomizeWaypoint, CBC_ORDER_ANY);
+ MUTATOR_HOOK(WeaponRateFactor, buffs_WeaponRate, 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);
+ MUTATOR_HOOK(BuildMutatorsPrettyString, buffs_BuildMutatorsPrettyString, CBC_ORDER_ANY);
+
+ MUTATOR_ONADD
+ {
+ buffs_Initialize();
+ }
+
+ return FALSE;
+ }
timer.owner = _nade;
timer.skin = 10;
- switch(_nade.realowner.team)
+ _nade.effects |= EF_LOWPRECISION;
+
+ CSQCProjectile(_nade, TRUE, Nade_ProjectileFromID(_nade.nade_type, FALSE), TRUE);
+}
+
+void napalm_damage(float dist, float damage, float edgedamage, float burntime)
+{
+ entity e;
+ float d;
+ vector p;
+
+ if ( damage < 0 )
+ return;
+
+ RandomSelection_Init();
+ for(e = WarpZone_FindRadius(self.origin, dist, TRUE); e; e = e.chain)
+ if(e.takedamage == DAMAGE_AIM)
+ if(self.realowner != e || autocvar_g_nades_napalm_selfdamage)
+ if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self))
+ if(!e.frozen)
+ {
+ p = e.origin;
+ p_x += e.mins_x + random() * (e.maxs_x - e.mins_x);
+ p_y += e.mins_y + random() * (e.maxs_y - e.mins_y);
+ p_z += e.mins_z + random() * (e.maxs_z - e.mins_z);
+ d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p);
+ if(d < dist)
+ {
+ e.fireball_impactvec = p;
+ RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
+ }
+ }
+ if(RandomSelection_chosen_ent)
{
- case NUM_TEAM_1: p = PROJECTILE_NADE_RED; break;
- case NUM_TEAM_2: p = PROJECTILE_NADE_BLUE; break;
- case NUM_TEAM_3: p = PROJECTILE_NADE_YELLOW; break;
- case NUM_TEAM_4: p = PROJECTILE_NADE_PINK; break;
- default: p = PROJECTILE_NADE; break;
+ d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
+ d = damage + (edgedamage - damage) * (d / dist);
+ Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
+ //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
+ pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
}
+}
- CSQCProjectile(_nade, TRUE, p, TRUE);
- entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, FALSE, 1);
+void napalm_ball_think()
+{
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ {
+ remove(self);
+ return;
+ }
+
+ if(time > self.pushltime)
+ {
+ remove(self);
+ return;
+ }
+
+ vector midpoint = ((self.absmin + self.absmax) * 0.5);
+ if(pointcontents(midpoint) == CONTENT_WATER)
+ {
+ self.velocity = self.velocity * 0.5;
+
+ if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+ { self.velocity_z = 200; }
+ }
+
+ self.angles = vectoangles(self.velocity);
+
+ napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage,
+ autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime);
+
+ self.nextthink = time + 0.1;
+}
+
+
+void nade_napalm_ball()
+{
+ entity proj;
+ vector kick;
+
+ spamsound(self, CH_SHOTS, "weapons/fireball_fire.wav", VOL_BASE, ATTEN_NORM);
+
+ proj = spawn ();
+ proj.owner = self.owner;
+ proj.realowner = self.realowner;
+ proj.team = self.owner.team;
+ proj.classname = "grenade";
+ proj.bot_dodge = TRUE;
+ proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage;
+ proj.movetype = MOVETYPE_BOUNCE;
+ proj.projectiledeathtype = DEATH_NADE_NAPALM;
+ PROJECTILE_MAKETRIGGER(proj);
+ setmodel(proj, "null");
+ proj.scale = 1;//0.5;
+ setsize(proj, '-4 -4 -4', '4 4 4');
+ setorigin(proj, self.origin);
+ proj.think = napalm_ball_think;
+ proj.nextthink = time;
+ proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale;
+ proj.effects = EF_LOWPRECISION | EF_FLAME;
+
+ kick_x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
+ kick_y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
+ kick_z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread;
+ proj.velocity = kick;
+
+ proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime;
+
+ proj.angles = vectoangles(proj.velocity);
+ proj.flags = FL_PROJECTILE;
+ proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
+
+ //CSQCProjectile(proj, TRUE, PROJECTILE_NAPALM_FIRE, TRUE);
+}
+
+
+void napalm_fountain_think()
+{
+
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ {
+ remove(self);
+ return;
+ }
+
+ if(time >= self.ltime)
+ {
+ remove(self);
+ return;
+ }
+
+ vector midpoint = ((self.absmin + self.absmax) * 0.5);
+ if(pointcontents(midpoint) == CONTENT_WATER)
+ {
+ self.velocity = self.velocity * 0.5;
+
+ if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+ { self.velocity_z = 200; }
+
+ UpdateCSQCProjectile(self);
+ }
+
+ napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage,
+ autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime);
+
+ self.nextthink = time + 0.1;
+ if(time >= self.nade_special_time)
+ {
+ self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay;
+ nade_napalm_ball();
+ }
+}
+
+void nade_napalm_boom()
+{
+ entity fountain;
+ local float c;
+ for (c = 0; c < autocvar_g_nades_napalm_ball_count; c ++)
+ nade_napalm_ball();
+
+
+ fountain = spawn();
+ fountain.owner = self.owner;
+ fountain.realowner = self.realowner;
+ fountain.origin = self.origin;
+ setorigin(fountain, fountain.origin);
+ fountain.think = napalm_fountain_think;
+ fountain.nextthink = time;
+ fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime;
+ fountain.pushltime = fountain.ltime;
+ fountain.team = self.team;
+ fountain.movetype = MOVETYPE_TOSS;
+ fountain.projectiledeathtype = DEATH_NADE_NAPALM;
+ fountain.bot_dodge = TRUE;
+ fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage;
+ fountain.nade_special_time = time;
+ setsize(fountain, '-16 -16 -16', '16 16 16');
+ CSQCProjectile(fountain, TRUE, PROJECTILE_NAPALM_FOUNTAIN, TRUE);
+}
+
+void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time)
+{
+ frost_target.frozen_by = freezefield.realowner;
+ pointparticles(particleeffectnum("electro_impact"), frost_target.origin, '0 0 0', 1);
+ Freeze(frost_target, 1/freeze_time, 3, FALSE);
+ if(frost_target.ballcarried)
+ if(g_keepaway) { ka_DropEvent(frost_target); }
+ else { DropBall(frost_target.ballcarried, frost_target.origin, frost_target.velocity);}
+ if(frost_target.flagcarried) { ctf_Handle_Throw(frost_target, world, DROP_THROW); }
+ if(frost_target.nade) { toss_nade(frost_target, '0 0 0', time + 0.05); }
+
+ kh_Key_DropAll(frost_target, FALSE);
+}
+
+void nade_ice_think()
+{
+
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ {
+ remove(self);
+ return;
+ }
+
+ if(time >= self.ltime)
+ {
+ if ( autocvar_g_nades_ice_explode )
+ {
+ string expef;
+ switch(self.realowner.team)
+ {
+ case NUM_TEAM_1: expef = "nade_red_explode"; break;
+ case NUM_TEAM_2: expef = "nade_blue_explode"; break;
+ case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
+ case NUM_TEAM_4: expef = "nade_pink_explode"; break;
+ default: expef = "nade_neutral_explode"; break;
+ }
+ pointparticles(particleeffectnum(expef), self.origin + '0 0 1', '0 0 0', 1);
+ sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
+
+ RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+ autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+ Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+ autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+ }
+ remove(self);
+ return;
+ }
+
+
+ self.nextthink = time+0.1;
+
+ // gaussian
+ float randomr;
+ randomr = random();
+ randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius;
+ float randomw;
+ randomw = random()*M_PI*2;
+ vector randomp;
+ randomp_x = randomr*cos(randomw);
+ randomp_y = randomr*sin(randomw);
+ randomp_z = 1;
+ pointparticles(particleeffectnum("electro_muzzleflash"), self.origin + randomp, '0 0 0', 1);
+
+ if(time >= self.nade_special_time)
+ {
+ self.nade_special_time = time+0.7;
+
+
+ pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1);
+ pointparticles(particleeffectnum("icefield"), self.origin, '0 0 0', 1);
+ }
+
+
+ float current_freeze_time = self.ltime - time - 0.1;
+
+ entity e;
+ for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain)
+ if(e != self)
+ if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner))
+ if(e.takedamage && e.deadflag == DEAD_NO)
+ if(e.health > 0)
+ if(!e.revival_time || ((time - e.revival_time) >= 1.5))
+ if(!e.frozen)
+ if(current_freeze_time > 0)
+ nade_ice_freeze(self, e, current_freeze_time);
+}
+
+void nade_ice_boom()
+{
+ entity fountain;
+ fountain = spawn();
+ fountain.owner = self.owner;
+ fountain.realowner = self.realowner;
+ fountain.origin = self.origin;
+ setorigin(fountain, fountain.origin);
+ fountain.think = nade_ice_think;
+ fountain.nextthink = time;
+ fountain.ltime = time + autocvar_g_nades_ice_freeze_time;
+ fountain.pushltime = fountain.wait = fountain.ltime;
+ fountain.team = self.team;
+ fountain.movetype = MOVETYPE_TOSS;
+ fountain.projectiledeathtype = DEATH_NADE_ICE;
+ fountain.bot_dodge = FALSE;
+ setsize(fountain, '-16 -16 -16', '16 16 16');
+ fountain.nade_special_time = time+0.3;
+ fountain.angles = self.angles;
+
+ if ( autocvar_g_nades_ice_explode )
+ {
+ setmodel(fountain, "models/grenademodel.md3");
+ entity timer = spawn();
+ setmodel(timer, "models/ok_nade_counter/ok_nade_counter.md3");
+ setattachment(timer, fountain, "");
+ timer.classname = "nade_timer";
+ timer.colormap = self.colormap;
+ timer.glowmod = self.glowmod;
+ timer.think = nade_timer_think;
+ timer.nextthink = time;
+ timer.wait = fountain.ltime;
+ timer.owner = fountain;
+ timer.skin = 10;
+ }
+ else
+ setmodel(fountain, "null");
+}
+
+void nade_translocate_boom()
+{
+ if(self.realowner.vehicle)
+ return;
+
+ vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins_z - 24);
+
+ makevectors(self.realowner.angles);
+
+ entity oldself = self;
+ self = self.realowner;
+ MUTATOR_CALLHOOK(PortalTeleport);
+ self.realowner = self;
+ self = oldself;
+
+ TeleportPlayer(self, self.realowner, locout, self.realowner.mangle, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
+}
+
+void nade_spawn_boom()
+{
+ entity spawnloc = spawn();
+ setorigin(spawnloc, self.origin);
+ setsize(spawnloc, self.realowner.mins, self.realowner.maxs);
+ spawnloc.movetype = MOVETYPE_NONE;
+ spawnloc.solid = SOLID_NOT;
+ spawnloc.drawonlytoclient = self.realowner;
+ spawnloc.effects = EF_STARDUST;
+ spawnloc.cnt = autocvar_g_nades_spawn_count;
+
+ if(self.realowner.nade_spawnloc)
+ {
+ remove(self.realowner.nade_spawnloc);
+ self.realowner.nade_spawnloc = world;
+ }
+
+ self.realowner.nade_spawnloc = spawnloc;
+}
+
+void nade_heal_think()
+{
+ if(time >= self.ltime)
+ {
+ remove(self);
+ return;
+ }
+
+ self.nextthink = time;
+
+ if(time >= self.nade_special_time)
+ {
+ self.nade_special_time = time+0.25;
+ self.nade_show_particles = 1;
+ }
+ else
+ self.nade_show_particles = 0;
+}
+
+void nade_heal_touch()
+{
+ float maxhealth;
+ float health_factor;
+ if(IS_PLAYER(other) || (other.flags & FL_MONSTER))
+ if(other.deadflag == DEAD_NO)
+ if(!other.frozen)
+ {
+ health_factor = autocvar_g_nades_heal_rate*frametime/2;
+ if ( other != self.realowner )
+ {
+ if ( SAME_TEAM(other,self) )
+ health_factor *= autocvar_g_nades_heal_friend;
+ else
+ health_factor *= autocvar_g_nades_heal_foe;
+ }
+ if ( health_factor > 0 )
+ {
+ maxhealth = (other.flags & FL_MONSTER) ? other.max_health : g_pickup_healthmega_max;
+ if ( other.health < maxhealth )
+ {
+ if ( self.nade_show_particles )
+ pointparticles(particleeffectnum("healing_fx"), other.origin, '0 0 0', 1);
+ other.health = min(other.health+health_factor, maxhealth);
+ }
+ other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
+ }
+ else if ( health_factor < 0 )
+ {
+ Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL,other.origin,'0 0 0');
+ }
+
+ }
+
+ if ( IS_REAL_CLIENT(other) || (other.vehicle_flags & VHF_ISVEHICLE) )
+ {
+ entity show_red = (other.vehicle_flags & VHF_ISVEHICLE) ? other.owner : other;
+ show_red.stat_healing_orb = time+0.1;
+ show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime;
+ }
+}
+
+void nade_heal_boom()
+{
+ entity healer;
+ healer = spawn();
+ healer.owner = self.owner;
+ healer.realowner = self.realowner;
+ setorigin(healer, self.origin);
+ healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar
+ healer.ltime = time + healer.healer_lifetime;
+ healer.team = self.realowner.team;
+ healer.bot_dodge = FALSE;
+ healer.solid = SOLID_TRIGGER;
+ healer.touch = nade_heal_touch;
+
+ setmodel(healer, "models/ctf/shield.md3");
+ healer.healer_radius = autocvar_g_nades_nade_radius;
+ vector size = '1 1 1' * healer.healer_radius / 2;
+ setsize(healer,-size,size);
+
+ Net_LinkEntity(healer, TRUE, 0, healer_send);
+
+ healer.think = nade_heal_think;
+ healer.nextthink = time;
+ healer.SendFlags |= 1;
+}
+
+void nade_monster_boom()
+{
++ entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, FALSE, FALSE, 1);
+
+ //e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime;
+ e.monster_skill = MONSTER_SKILL_INSANE;
}
void nade_boom()
../warpzonelib/server.qh
../common/constants.qh
+ ../common/stats.qh
../common/teams.qh
../common/util.qh
+../common/nades.qh
+ ../common/buffs.qh
../common/test.qh
../common/counting.qh
../common/items.qh
../common/items.qc
+../common/nades.qc
+ ../common/buffs.qc
accuracy.qc
// a player's mines shall explode if he disconnects or dies
// TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams?
- if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO)
- if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.freezetag_frozen)
++ if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.frozen)
{
other = world;
self.projectiledeathtype |= HITTYPE_BOUNCE;
head = findradius(self.origin, autocvar_g_balance_minelayer_proximityradius);
while(head)
{
- if(IS_PLAYER(head) && head.deadflag == DEAD_NO)
- if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.freezetag_frozen)
++ if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.frozen)
if(head != self.realowner && DIFF_TEAM(head, self.realowner)) // don't trigger for team mates
if(!self.mine_time)
{