1#include "sv_instagib.qh" 2 3//int autocvar_g_instagib_ammo_drop; 4bool autocvar_g_instagib_ammo_convert_cells; 5bool autocvar_g_instagib_ammo_convert_rockets; 6bool autocvar_g_instagib_ammo_convert_shells; 7bool autocvar_g_instagib_ammo_convert_bullets; 8int autocvar_g_instagib_extralives; 9float autocvar_g_instagib_speed_highspeed; 10 11#include <server/client.qh> 12 13#include <common/items/_mod.qh> 14 15REGISTER_MUTATOR(mutator_instagib, cvar("g_instagib") && !g_nexball); 16 17spawnfunc(item_minst_cells) 18{ 19 if (!g_instagib) { delete(this); return; } 20 StartItem(this, ITEM_VaporizerCells); 21} 22 23void instagib_invisibility(entity this) 24{ 25 this.strength_finished = autocvar_g_balance_powerup_strength_time; 26 StartItem(this, ITEM_Invisibility); 27} 28 29void instagib_extralife(entity this) 30{ 31 StartItem(this, ITEM_ExtraLife); 32} 33 34void instagib_speed(entity this) 35{ 36 this.invincible_finished = autocvar_g_balance_powerup_invincible_time; 37 StartItem(this, ITEM_Speed); 38} 39 40.float instagib_nextthink; 41.float instagib_needammo; 42void instagib_stop_countdown(entity e) 43{ 44 if (!e.instagib_needammo) 45 return; 46 Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO); 47 e.instagib_needammo = false; 48} 49void instagib_ammocheck(entity this) 50{ 51 if(time < this.instagib_nextthink) 52 return; 53 if(!IS_PLAYER(this)) 54 return; // not a player 55 56 if(IS_DEAD(this) || game_stopped) 57 instagib_stop_countdown(this); 58 else if (this.ammo_cells > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE)) 59 instagib_stop_countdown(this); 60 else if(autocvar_g_rm && autocvar_g_rm_laser) 61 { 62 if(!this.instagib_needammo) 63 { 64 Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE); 65 this.instagib_needammo = true; 66 } 67 } 68 else 69 { 70 this.instagib_needammo = true; 71 if (this.health <= 5) 72 { 73 Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 74 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED); 75 } 76 else if (this.health <= 10) 77 { 78 Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 79 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_1); 80 } 81 else if (this.health <= 20) 82 { 83 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 84 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_2); 85 } 86 else if (this.health <= 30) 87 { 88 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 89 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_3); 90 } 91 else if (this.health <= 40) 92 { 93 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 94 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_4); 95 } 96 else if (this.health <= 50) 97 { 98 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 99 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_5); 100 } 101 else if (this.health <= 60) 102 { 103 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 104 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_6); 105 } 106 else if (this.health <= 70) 107 { 108 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 109 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_7); 110 } 111 else if (this.health <= 80) 112 { 113 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 114 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_8); 115 } 116 else if (this.health <= 90) 117 { 118 Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO); 119 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 120 Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_9); 121 } 122 else 123 { 124 Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO); 125 Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); 126 } 127 } 128 this.instagib_nextthink = time + 1; 129} 130 131MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd) 132{ 133 FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(instagib_stop_countdown(it))); 134} 135 136MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem) 137{ 138 entity item = M_ARGV(1, entity); 139 140 item.monster_loot = ITEM_VaporizerCells; 141} 142 143MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn) 144{ 145 entity mon = M_ARGV(0, entity); 146 147 // always refill ammo 148 if(mon.monsterid == MON_MAGE.monsterid) 149 mon.skin = 1; 150} 151 152MUTATOR_HOOKFUNCTION(mutator_instagib, BotShouldAttack) 153{ 154 entity targ = M_ARGV(1, entity); 155 156 if (targ.items & ITEM_Invisibility.m_itemid) 157 return true; 158} 159 160MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver) 161{ 162 entity player = M_ARGV(0, entity); 163 164 instagib_stop_countdown(player); 165} 166 167MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn) 168{ 169 entity player = M_ARGV(0, entity); 170 171 player.effects |= EF_FULLBRIGHT; 172} 173 174MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPreThink) 175{ 176 entity player = M_ARGV(0, entity); 177 178 instagib_ammocheck(player); 179} 180 181MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen) 182{ 183 // no regeneration in instagib 184 return true; 185} 186 187MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPowerups) 188{ 189 entity player = M_ARGV(0, entity); 190 191 if (!(player.effects & EF_FULLBRIGHT)) 192 player.effects |= EF_FULLBRIGHT; 193 194 if (player.items & ITEM_Invisibility.m_itemid) 195 { 196 play_countdown(player, player.strength_finished, SND_POWEROFF); 197 if (time > player.strength_finished) 198 { 199 player.alpha = default_player_alpha; 200 player.exteriorweaponentity.alpha = default_weapon_alpha; 201 player.items &= ~ITEM_Invisibility.m_itemid; 202 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY); 203 } 204 } 205 else 206 { 207 if (time < player.strength_finished) 208 { 209 player.alpha = autocvar_g_instagib_invis_alpha; 210 player.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha; 211 player.items |= ITEM_Invisibility.m_itemid; 212 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, player.netname); 213 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_INVISIBILITY); 214 } 215 } 216 217 if (player.items & ITEM_Speed.m_itemid) 218 { 219 play_countdown(player, player.invincible_finished, SND_POWEROFF); 220 if (time > player.invincible_finished) 221 { 222 player.items &= ~ITEM_Speed.m_itemid; 223 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_SPEED); 224 } 225 } 226 else 227 { 228 if (time < player.invincible_finished) 229 { 230 player.items |= ITEM_Speed.m_itemid; 231 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, player.netname); 232 Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_SPEED); 233 } 234 } 235} 236 237.float stat_sv_maxspeed; 238 239MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPhysics) 240{ 241 entity player = M_ARGV(0, entity); 242 243 if(player.items & ITEM_Speed.m_itemid) 244 player.stat_sv_maxspeed = player.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed; 245} 246 247MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor) 248{ 249 M_ARGV(4, float) = M_ARGV(7, float); // take = damage 250 M_ARGV(5, float) = 0; // save 251} 252 253MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon) 254{ 255 // weapon dropping on death handled by FilterItem 256 return true; 257} 258 259MUTATOR_HOOKFUNCTION(mutator_instagib, Damage_Calculate) 260{ 261 entity frag_attacker = M_ARGV(1, entity); 262 entity frag_target = M_ARGV(2, entity); 263 float frag_deathtype = M_ARGV(3, float); 264 float frag_damage = M_ARGV(4, float); 265 float frag_mirrordamage = M_ARGV(5, float); 266 vector frag_force = M_ARGV(6, vector); 267 268 if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker)) 269 frag_damage = 0; 270 271 if(IS_PLAYER(frag_target)) 272 { 273 if(frag_deathtype == DEATH_FALL.m_id) 274 frag_damage = 0; // never count fall damage 275 276 if(!autocvar_g_instagib_damagedbycontents) 277 switch(DEATH_ENT(frag_deathtype)) 278 { 279 case DEATH_DROWN: 280 case DEATH_SLIME: 281 case DEATH_LAVA: 282 frag_damage = 0; 283 break; 284 } 285 286 if(IS_PLAYER(frag_attacker)) 287 if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) 288 { 289 if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker)) 290 frag_force = '0 0 0'; 291 292 if(frag_target.armorvalue) 293 { 294 frag_target.armorvalue -= 1; 295 frag_damage = 0; 296 frag_target.damage_dealt += 1; 297 frag_attacker.damage_dealt += 1; 298 Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue); 299 } 300 } 301 302 if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) 303 { 304 if(frag_deathtype & HITTYPE_SECONDARY) 305 { 306 if(!autocvar_g_instagib_blaster_keepdamage || frag_attacker == frag_target) 307 { 308 frag_damage = 0; 309 if(!autocvar_g_instagib_mirrordamage) 310 frag_mirrordamage = 0; // never do mirror damage on enemies 311 } 312 313 if(frag_target != frag_attacker) 314 { 315 if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); } 316 if(!autocvar_g_instagib_blaster_keepforce) 317 frag_force = '0 0 0'; 318 } 319 } 320 } 321 } 322 323 if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring 324 if(IS_PLAYER(frag_attacker)) 325 if(frag_mirrordamage > 0) 326 { 327 // just lose extra LIVES, don't kill the player for mirror damage 328 if(frag_attacker.armorvalue > 0) 329 { 330 frag_attacker.armorvalue -= 1; 331 Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue); 332 frag_attacker.damage_dealt += frag_mirrordamage; 333 } 334 frag_mirrordamage = 0; 335 } 336 337 if(frag_target.alpha && frag_target.alpha < 1) 338 if(IS_PLAYER(frag_target)) 339 yoda = 1; 340 341 M_ARGV(4, float) = frag_damage; 342 M_ARGV(5, float) = frag_mirrordamage; 343 M_ARGV(6, vector) = frag_force; 344} 345 346MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems) 347{ 348 start_health = warmup_start_health = 100; 349 start_armorvalue = warmup_start_armorvalue = 0; 350 351 start_ammo_shells = warmup_start_ammo_shells = 0; 352 start_ammo_nails = warmup_start_ammo_nails = 0; 353 start_ammo_cells = warmup_start_ammo_cells = cvar("g_instagib_ammo_start"); 354 start_ammo_plasma = warmup_start_ammo_plasma = 0; 355 start_ammo_rockets = warmup_start_ammo_rockets = 0; 356 //start_ammo_fuel = warmup_start_ammo_fuel = 0; 357 358 start_weapons = warmup_start_weapons = WEPSET(VAPORIZER); 359 start_items |= IT_UNLIMITED_SUPERWEAPONS; 360} 361 362MUTATOR_HOOKFUNCTION(mutator_instagib, SetWeaponArena) 363{ 364 // turn weapon arena off 365 M_ARGV(0, string) = "off"; 366} 367 368void replace_with_insta_cells(entity item) 369{ 370 entity e = spawn(); 371 setorigin(e, item.origin); 372 e.noalign = item.noalign; 373 e.cnt = item.cnt; 374 e.team = item.team; 375 e.spawnfunc_checked = true; 376 spawnfunc_item_minst_cells(e); 377} 378 379MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem) 380{ 381 entity item = M_ARGV(0, entity); 382 383 if(item.classname == "item_cells") 384 { 385 if(autocvar_g_instagib_ammo_convert_cells) 386 { 387 replace_with_insta_cells(item); 388 } 389 return true; 390 } 391 else if(item.classname == "item_rockets") 392 { 393 if(autocvar_g_instagib_ammo_convert_rockets) 394 { 395 replace_with_insta_cells(item); 396 } 397 return true; 398 } 399 else if(item.classname == "item_shells") 400 { 401 if(autocvar_g_instagib_ammo_convert_shells) 402 { 403 replace_with_insta_cells(item); 404 } 405 return true; 406 } 407 else if(item.classname == "item_bullets") 408 { 409 if(autocvar_g_instagib_ammo_convert_bullets) 410 { 411 replace_with_insta_cells(item); 412 } 413 return true; 414 } 415 416 if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon") 417 { 418 item.ammo_cells = autocvar_g_instagib_ammo_drop; 419 return false; 420 } 421 422 if(item.weapon == WEP_DEVASTATOR.m_id || item.weapon == WEP_VORTEX.m_id) 423 { 424 replace_with_insta_cells(item); 425 return true; 426 } 427 428 if(item.flags & FL_POWERUP) 429 return false; 430 431 if(item.ammo_cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells") 432 item.ammo_cells = autocvar_g_instagib_ammo_drop; 433 434 if(item.ammo_cells && !item.weapon) 435 return false; 436 437 return true; 438} 439 440MUTATOR_HOOKFUNCTION(mutator_instagib, CustomizeWaypoint) 441{ 442 entity wp = M_ARGV(0, entity); 443 entity player = M_ARGV(1, entity); 444 445 entity e = WaypointSprite_getviewentity(player); 446 447 // if you have the invisibility powerup, sprites ALWAYS are restricted to your team 448 // but only apply this to real players, not to spectators 449 if((wp.owner.flags & FL_CLIENT) && (wp.owner.items & ITEM_Invisibility.m_itemid) && (e == player)) 450 if(DIFF_TEAM(wp.owner, e)) 451 return true; 452} 453 454MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies) 455{ 456 float frag_deathtype = M_ARGV(3, float); 457 458 if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) 459 M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death 460} 461 462MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch) 463{ 464 entity item = M_ARGV(0, entity); 465 entity toucher = M_ARGV(1, entity); 466 467 if(item.ammo_cells) 468 { 469 // play some cool sounds ;) 470 if (IS_CLIENT(toucher)) 471 { 472 if(toucher.health <= 5) 473 Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND); 474 else if(toucher.health < 50) 475 Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY); 476 } 477 478 if(toucher.health < 100) 479 toucher.health = 100; 480 481 return MUT_ITEMTOUCH_CONTINUE; 482 } 483 484 if(item.itemdef == ITEM_ExtraLife) 485 { 486 toucher.armorvalue = bound(toucher.armorvalue, 999, toucher.armorvalue + autocvar_g_instagib_extralives); 487 Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES); 488 return MUT_ITEMTOUCH_PICKUP; 489 } 490 491 return MUT_ITEMTOUCH_CONTINUE; 492} 493 494MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn) 495{ 496 if (!autocvar_g_powerups) { return; } 497 entity ent = M_ARGV(0, entity); 498 // Can't use .itemdef here 499 if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega")) 500 return; 501 502 entity e = spawn(); 503 504 float r = random(); 505 if (r < 0.3) 506 setthink(e, instagib_invisibility); 507 else if (r < 0.6) 508 setthink(e, instagib_extralife); 509 else 510 setthink(e, instagib_speed); 511 512 e.nextthink = time + 0.1; 513 e.spawnflags = ent.spawnflags; 514 e.noalign = ent.noalign; 515 setorigin(e, ent.origin); 516 517 return true; 518} 519 520MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString) 521{ 522 M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib"); 523} 524 525MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString) 526{ 527 M_ARGV(0, string) = strcat(M_ARGV(0, string), ", instagib"); 528} 529 530MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname) 531{ 532 M_ARGV(0, string) = "InstaGib"; 533 return true; 534} 535