X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fcommon%2Fmutators%2Fmutator%2Fnades%2Fnades.qc;h=a5cbab32bdf06af141309a244ab35763ea46dda4;hb=9c3e1026151c97ff534d76900e80e8b139539b4c;hp=04c61a6d73c5e8cf23c135e780372cb892d6a74e;hpb=d0723d5dc8d2e92262cfe895b9dbfb4241c68d9c;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc index 04c61a6d7..a5cbab32b 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qc +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -11,6 +11,7 @@ float autocvar_g_nades_spread = 0.04; REGISTER_STAT(NADES_SMALL, int, autocvar_g_nades_nade_small) #ifdef GAMEQC + REPLICATE(cvar_cl_nade_type, int, "cl_nade_type"); REPLICATE(cvar_cl_pokenade_type, string, "cl_pokenade_type"); @@ -39,30 +40,34 @@ entity Nade_TrailEffect(int proj, int nade_team) #endif #ifdef CSQC +#include +#include + +bool darkblink; + +void HUD_DarkBlinking() +{ + vector bottomright = vec2(vid_conwidth, vid_conheight); + drawfill('0 0 0', bottomright, NADE_TYPE_DARKNESS.m_color, 0.986, DRAWFLAG_NORMAL); +} + REGISTER_MUTATOR(cl_nades, true); MUTATOR_HOOKFUNCTION(cl_nades, HUD_Draw_overlay) { - // TODO: make a common orb state! - if (STAT(HEALING_ORB) > time) - { - M_ARGV(0, vector) = NADE_TYPE_HEAL.m_color; - M_ARGV(1, float) = STAT(HEALING_ORB_ALPHA); - return true; - } - if (STAT(ENTRAP_ORB) > time) - { - M_ARGV(0, vector) = NADE_TYPE_ENTRAP.m_color; - M_ARGV(1, float) = STAT(ENTRAP_ORB_ALPHA); - return true; - } - if (STAT(VEIL_ORB) > time) + if (STAT(NADE_DARKNESS_TIME) > time) { - M_ARGV(0, vector) = NADE_TYPE_VEIL.m_color; - M_ARGV(1, float) = STAT(VEIL_ORB_ALPHA); + if (!darkblink) + localcmd("play2 sound/misc/blind\n"); + darkblink = true; + M_ARGV(0, vector) = NADE_TYPE_DARKNESS.m_color; + HUD_DarkBlinking(); return true; } + else + darkblink = false; return false; } + MUTATOR_HOOKFUNCTION(cl_nades, Ent_Projectile) { entity proj = M_ARGV(0, entity); @@ -116,6 +121,17 @@ MUTATOR_HOOKFUNCTION(cl_nades, EditProjectile) else proj.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; } + +MUTATOR_HOOKFUNCTION(cl_nades, BuildGameplayTipsString) +{ + if (mut_is_active(MUT_NADES)) + { + string key = getcommandkey(_("drop weapon / throw nade"), "dropweapon"); + M_ARGV(0, string) = strcat(M_ARGV(0, string), + "\n", sprintf(_("^3nades^8 are enabled, press ^3%s^8 to use them"), key), "\n"); + } +} + bool Projectile_isnade(int p) { return Nade_FromProjectile(p) != NADE_TYPE_Null; @@ -162,6 +178,7 @@ void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expan #include #include #include +#include .float nade_time_primed; .float nade_lifetime; @@ -200,6 +217,14 @@ void nade_spawn(entity _nade) CSQCProjectile(_nade, true, REGISTRY_GET(Nades, STAT(NADE_BONUS_TYPE, _nade)).m_projectile[false], true); } +void normal_nade_boom(entity this) +{ + RadiusDamage(this, this.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, + autocvar_g_nades_nade_radius, this, NULL, autocvar_g_nades_nade_force, this.projectiledeathtype, DMG_NOWEP, this.enemy); + Damage_DamageInfo(this.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, + autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, this.projectiledeathtype, 0, this); +} + void napalm_damage(entity this, float dist, float damage, float edgedamage, float burntime) { entity e; @@ -404,33 +429,30 @@ void nade_ice_think(entity this) Send_Effect(expef, this.origin + '0 0 1', '0 0 0', 1); sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - RadiusDamage(this, this.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, this, NULL, autocvar_g_nades_nade_force, this.projectiledeathtype, DMG_NOWEP, this.enemy); - Damage_DamageInfo(this.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, this.projectiledeathtype, 0, this); + normal_nade_boom(this); } delete(this); return; } - this.nextthink = time+0.1; + this.nextthink = time + 0.1; // gaussian float randomr; randomr = random(); - randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius; + randomr = exp(-5 * randomr * randomr) * autocvar_g_nades_nade_radius; float randomw; - randomw = random()*M_PI*2; + randomw = random() * M_PI * 2; vector randomp; - randomp.x = randomr*cos(randomw); - randomp.y = randomr*sin(randomw); + randomp.x = randomr * cos(randomw); + randomp.y = randomr * sin(randomw); randomp.z = 1; Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, this.origin + randomp, '0 0 0', 1); if(time >= this.nade_special_time) { - this.nade_special_time = time+0.7; + this.nade_special_time = time + 0.7; Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1); Send_Effect(EFFECT_ICEFIELD, this.origin, '0 0 0', 1); @@ -439,12 +461,19 @@ void nade_ice_think(entity this) float current_freeze_time = this.ltime - time - 0.1; - FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage && !IS_DEAD(it) && GetResource(it, RES_HEALTH) > 0 && current_freeze_time > 0, + FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage + && !IS_DEAD(it) && GetResource(it, RES_HEALTH) > 0 && current_freeze_time > 0 + && (!it.revival_time || ((time - it.revival_time) >= 1.5)) && !STAT(FROZEN, it), { - if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(it, this.realowner) || it == this.realowner)) - if(!it.revival_time || ((time - it.revival_time) >= 1.5)) - if(!STAT(FROZEN, it)) - nade_ice_freeze(this, it, current_freeze_time); + switch (autocvar_g_nades_ice_teamcheck) + { + case 0: break; // affect everyone + default: + case 2: if(SAME_TEAM(it, this.realowner)) continue; // don't affect teammates + // fall through (check case 1 condition too) + case 1: if(it == this.realowner) continue; // don't affect the player who threw the nade + } + nade_ice_freeze(this, it, current_freeze_time); }); } @@ -464,7 +493,7 @@ void nade_ice_boom(entity this) fountain.projectiledeathtype = DEATH_NADE_ICE.m_id; fountain.bot_dodge = false; setsize(fountain, '-16 -16 -16', '16 16 16'); - fountain.nade_special_time = time+0.3; + fountain.nade_special_time = time + 0.3; fountain.angles = this.angles; if ( autocvar_g_nades_ice_explode ) @@ -490,6 +519,14 @@ void nade_translocate_boom(entity this) if(this.realowner.vehicle) return; + setsize(this, PL_MIN_CONST-'16 16 16', PL_MAX_CONST+'16 16 16'); + + if(!move_out_of_solid(this)) + { + sprint(this.realowner, "^1Couldn't move the translocator out of solid! origin: ", vtos(this.origin), "\n"); + return; + } + vector locout = this.origin + '0 0 1' * (1 - this.realowner.mins.z - 24); tracebox(locout, this.realowner.mins, this.realowner.maxs, locout, MOVE_NOMONSTERS, this.realowner); locout = trace_endpos; @@ -589,15 +626,10 @@ void nade_entrap_touch(entity this, entity toucher) #endif } - if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) || IS_MONSTER(toucher) ) + if ( IS_REAL_CLIENT(toucher) || (IS_VEHICLE(toucher) && toucher.owner) ) { - entity show_tint = (IS_VEHICLE(toucher)) ? toucher.owner : toucher; - STAT(ENTRAP_ORB, show_tint) = time + 0.1; - - float tint_alpha = 0.75; - if(SAME_TEAM(toucher, this.realowner)) - tint_alpha = 0.45; - STAT(ENTRAP_ORB_ALPHA, show_tint) = tint_alpha * (this.ltime - time) / this.orb_lifetime; + entity show_tint = (IS_VEHICLE(toucher) && toucher.owner) ? toucher.owner : toucher; + show_tint.nade_entrap_time = time + 0.1; } } @@ -613,18 +645,15 @@ void nade_heal_touch(entity this, entity toucher) { float maxhealth; float health_factor; - if(IS_PLAYER(toucher) || IS_MONSTER(toucher)) + + if(IS_PLAYER(toucher) || IS_MONSTER(toucher) || IS_VEHICLE(toucher)) if(!IS_DEAD(toucher)) if(!STAT(FROZEN, toucher)) { health_factor = autocvar_g_nades_heal_rate*frametime/2; if ( toucher != this.realowner ) - { - if ( SAME_TEAM(toucher,this) ) - health_factor *= autocvar_g_nades_heal_friend; - else - health_factor *= autocvar_g_nades_heal_foe; - } + health_factor *= (SAME_TEAM(toucher,this)) ? autocvar_g_nades_heal_friend : autocvar_g_nades_heal_foe; + if ( health_factor > 0 ) { maxhealth = (IS_MONSTER(toucher)) ? toucher.max_health : g_pickup_healthmega_max; @@ -632,24 +661,13 @@ void nade_heal_touch(entity this, entity toucher) if (hp < maxhealth) { if (this.nade_show_particles) - { Send_Effect(EFFECT_HEALING, toucher.origin, '0 0 0', 1); - } + GiveResourceWithLimit(toucher, RES_HEALTH, health_factor, maxhealth); } } else if ( health_factor < 0 ) - { Damage(toucher,this,this.realowner,-health_factor,DEATH_NADE_HEAL.m_id,DMG_NOWEP,toucher.origin,'0 0 0'); - } - - } - - if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) ) - { - entity show_red = (IS_VEHICLE(toucher)) ? toucher.owner : toucher; - STAT(HEALING_ORB, show_red) = time+0.1; - STAT(HEALING_ORB_ALPHA, show_red) = 0.75 * (this.ltime - time) / this.orb_lifetime; } } @@ -663,9 +681,13 @@ void nade_heal_boom(entity this) void nade_monster_boom(entity this) { + if(!autocvar_g_monsters) + return; entity e = spawn(); e.noalign = true; // don't drop to floor e = spawnmonster(e, this.pokenade_type, MON_Null, this.realowner, this.realowner, this.origin, false, false, 1); + if(!e) + return; // monster failed to be spawned if(autocvar_g_nades_pokenade_monster_lifetime > 0) e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime; @@ -674,22 +696,21 @@ void nade_monster_boom(entity this) void nade_veil_touch(entity this, entity toucher) { - if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) || IS_MONSTER(toucher) ) + if ( IS_REAL_CLIENT(toucher) || (IS_VEHICLE(toucher) && toucher.owner) ) { - entity show_tint = (IS_VEHICLE(toucher)) ? toucher.owner : toucher; + entity show_tint = (IS_VEHICLE(toucher) && toucher.owner) ? toucher.owner : toucher; float tint_alpha = 0.75; if(SAME_TEAM(toucher, this.realowner)) { tint_alpha = 0.45; - if(!STAT(VEIL_ORB, show_tint)) + if(!show_tint.nade_veil_time) { toucher.nade_veil_prevalpha = toucher.alpha; toucher.alpha = -1; } } - STAT(VEIL_ORB, show_tint) = time + 0.1; - STAT(VEIL_ORB_ALPHA, show_tint) = tint_alpha * (this.ltime - time) / this.orb_lifetime; + show_tint.nade_veil_time = time + 0.1; } } @@ -701,56 +722,208 @@ void nade_veil_boom(entity this) orb.colormod = NADE_TYPE_VEIL.m_color; } +void nade_ammo_touch(entity this, entity toucher) +{ + float maxammo = 999; + float ammo_factor; + float amshells = GetResource(toucher, RES_SHELLS); + float ambullets = GetResource(toucher, RES_BULLETS); + float amrockets = GetResource(toucher, RES_ROCKETS); + float amcells = GetResource(toucher, RES_CELLS); + float amplasma = GetResource(toucher, RES_PLASMA); + if(IS_PLAYER(toucher) || IS_MONSTER(toucher)) + if(!IS_DEAD(toucher)) + if(!STAT(FROZEN, toucher)) + { + ammo_factor = autocvar_g_nades_ammo_rate*frametime/2; + if ( toucher != this.realowner ) + ammo_factor *= (SAME_TEAM(toucher, this)) ? autocvar_g_nades_ammo_friend : autocvar_g_nades_ammo_foe; + +#define CHECK_AMMO_RESOURCE_LIMIT(amresource, res_resource) \ + if (amresource < maxammo) \ + GiveResourceWithLimit(toucher, res_resource, ammo_factor, maxammo); + +#define DROP_AMMO_RESOURCE(amresource, res_resource) \ + if (amresource > 0) \ + SetResource(toucher, res_resource, amresource + ammo_factor); + + if ( ammo_factor > 0 ) + { + CHECK_AMMO_RESOURCE_LIMIT(amshells, RES_SHELLS); + CHECK_AMMO_RESOURCE_LIMIT(ambullets, RES_BULLETS); + CHECK_AMMO_RESOURCE_LIMIT(amrockets, RES_ROCKETS); + CHECK_AMMO_RESOURCE_LIMIT(amcells, RES_CELLS); + CHECK_AMMO_RESOURCE_LIMIT(amplasma, RES_PLASMA); + + if (this.nade_show_particles) + Send_Effect(EFFECT_HEALING, toucher.origin, '0 0 0', 1); + } + else if ( ammo_factor < 0 ) + { + //Foe drops ammo points + DROP_AMMO_RESOURCE(amshells, RES_SHELLS); + DROP_AMMO_RESOURCE(ambullets, RES_BULLETS); + DROP_AMMO_RESOURCE(amrockets, RES_ROCKETS); + DROP_AMMO_RESOURCE(amcells, RES_CELLS); + DROP_AMMO_RESOURCE(amplasma, RES_PLASMA); + + return; + } + } +#undef CHECK_AMMO_RESOURCE_LIMIT +#undef DROP_AMMO_RESOURCE + + if ( IS_REAL_CLIENT(toucher) || (IS_VEHICLE(toucher) && toucher.owner) ) + { + entity show_tint = (IS_VEHICLE(toucher) && toucher.owner) ? toucher.owner : toucher; + show_tint.nade_ammo_time = time + 0.1; + } +} + +void nade_ammo_boom(entity this) +{ + entity orb = nades_spawn_orb(this.owner, this.realowner, this.origin, autocvar_g_nades_ammo_time, autocvar_g_nades_nade_radius); + + settouch(orb, nade_ammo_touch); + orb.colormod = '0.66 0.33 0'; +} + +void nade_darkness_think(entity this) +{ + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) + { + delete(this); + return; + } + + if(time >= this.ltime) + { + if ( autocvar_g_nades_darkness_explode ) + { + entity expef = EFFECT_NADE_EXPLODE(this.realowner.team); + Send_Effect(expef, this.origin + '0 0 1', '0 0 0', 1); + sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + + normal_nade_boom(this); + } + else + Send_Effect(EFFECT_SPAWN_PURPLE, this.origin + '0 0 1', '0 0 0', 1); + + delete(this); + return; + } + + this.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; + Send_Effect(EFFECT_DARKFIELD, this.origin + randomp, '0 0 0', 1); + + if(time >= this.nade_special_time) + { + this.nade_special_time = time + 0.7; + Send_Effect(EFFECT_DARKFIELD, this.origin, '0 0 0', 1); + } + + + float current_dark_time = this.ltime - time - 0.1; + + FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage + && !IS_DEAD(it) && GetResource(it, RES_HEALTH) > 0 && current_dark_time > 0 && IS_REAL_CLIENT(it), + { + switch (autocvar_g_nades_darkness_teamcheck) + { + case 0: break; // affect everyone + default: + case 2: if(SAME_TEAM(it, this.realowner)) continue; // don't affect teammates + // fall through (check case 1 condition too) + case 1: if(it == this.realowner) continue; // don't affect the player who threw the nade + } + STAT(NADE_DARKNESS_TIME, it) = time + 0.1; + }); +} + +void nade_darkness_boom(entity this) +{ + entity fountain = new(nade_darkness_fountain); + fountain.owner = this.owner; + fountain.realowner = this.realowner; + fountain.origin = this.origin; + setorigin(fountain, fountain.origin); + setthink(fountain, nade_darkness_think); + fountain.nextthink = time; + fountain.ltime = time + autocvar_g_nades_darkness_time; + fountain.pushltime = fountain.wait = fountain.ltime; + fountain.team = this.team; + set_movetype(fountain, MOVETYPE_TOSS); + fountain.projectiledeathtype = DEATH_NADE.m_id; + fountain.bot_dodge = false; + setsize(fountain, '-16 -16 -16', '16 16 16'); + fountain.nade_special_time = time + 0.3; + fountain.angles = this.angles; + + if ( autocvar_g_nades_darkness_explode ) + { + setmodel(fountain, MDL_PROJECTILE_GRENADE); + entity timer = new(nade_timer); + setmodel(timer, MDL_NADE_TIMER); + setattachment(timer, fountain, ""); + timer.colormap = this.colormap; + timer.glowmod = this.glowmod; + setthink(timer, nade_timer_think); + timer.nextthink = time; + timer.wait = fountain.ltime; + timer.owner = fountain; + timer.skin = 10; + } + else + setmodel(fountain, MDL_Null); +} + void nade_boom(entity this) { entity expef = NULL; bool nade_blast = true; - switch ( REGISTRY_GET(Nades, STAT(NADE_BONUS_TYPE, this)) ) - { - case NADE_TYPE_NAPALM: - nade_blast = autocvar_g_nades_napalm_blast; - expef = EFFECT_EXPLOSION_MEDIUM; - break; - case NADE_TYPE_ICE: - nade_blast = false; - expef = EFFECT_ELECTRO_COMBO; // hookbomb_explode electro_combo bigplasma_impact - break; - case NADE_TYPE_TRANSLOCATE: - nade_blast = false; - break; - case NADE_TYPE_MONSTER: - case NADE_TYPE_SPAWN: - nade_blast = false; - switch(this.realowner.team) - { - case NUM_TEAM_1: expef = EFFECT_SPAWN_RED; break; - case NUM_TEAM_2: expef = EFFECT_SPAWN_BLUE; break; - case NUM_TEAM_3: expef = EFFECT_SPAWN_YELLOW; break; - case NUM_TEAM_4: expef = EFFECT_SPAWN_PINK; break; - default: expef = EFFECT_SPAWN_NEUTRAL; break; - } - break; - case NADE_TYPE_HEAL: - nade_blast = false; - expef = EFFECT_SPAWN_RED; - break; +#define GET_NADE_TYPE_SPAWN_EFFECT(team_owner) \ + ((team_owner) == NUM_TEAM_1 ? EFFECT_SPAWN_RED : \ + ((team_owner) == NUM_TEAM_2 ? EFFECT_SPAWN_BLUE : \ + ((team_owner) == NUM_TEAM_3 ? EFFECT_SPAWN_YELLOW : \ + ((team_owner) == NUM_TEAM_4 ? EFFECT_SPAWN_PINK : \ + EFFECT_SPAWN_NEUTRAL)))) - case NADE_TYPE_ENTRAP: - nade_blast = false; - expef = EFFECT_SPAWN_YELLOW; - break; - - case NADE_TYPE_VEIL: - nade_blast = false; - expef = EFFECT_SPAWN_NEUTRAL; - break; +#define SET_NADE_EFFECT(nade_type, blast, exp_effect) \ + case nade_type: \ + nade_blast = blast; \ + expef = exp_effect; \ + break - default: - case NADE_TYPE_NORMAL: - expef = EFFECT_NADE_EXPLODE(this.realowner.team); - break; + switch ( REGISTRY_GET(Nades, STAT(NADE_BONUS_TYPE, this)) ) + { + SET_NADE_EFFECT(NADE_TYPE_NAPALM, autocvar_g_nades_napalm_blast, EFFECT_EXPLOSION_MEDIUM); + SET_NADE_EFFECT(NADE_TYPE_ICE, false, EFFECT_ELECTRO_COMBO /* hookbomb_explode electro_combo bigplasma_impact */); + SET_NADE_EFFECT(NADE_TYPE_TRANSLOCATE, false, NULL); + SET_NADE_EFFECT(NADE_TYPE_MONSTER, true, (!autocvar_g_monsters) ? EFFECT_NADE_EXPLODE(this.realowner.team) : NULL); + SET_NADE_EFFECT(NADE_TYPE_SPAWN, false, GET_NADE_TYPE_SPAWN_EFFECT(this.realowner.team)); + SET_NADE_EFFECT(NADE_TYPE_HEAL, false, EFFECT_SPAWN_RED); + SET_NADE_EFFECT(NADE_TYPE_ENTRAP, false, EFFECT_SPAWN_YELLOW); + SET_NADE_EFFECT(NADE_TYPE_VEIL, false, EFFECT_SPAWN_NEUTRAL); + SET_NADE_EFFECT(NADE_TYPE_AMMO, false, EFFECT_SPAWN_BROWN); + SET_NADE_EFFECT(NADE_TYPE_DARKNESS, false, EFFECT_EXPLOSION_MEDIUM); + SET_NADE_EFFECT(NADE_TYPE_NORMAL, true, EFFECT_NADE_EXPLODE(this.realowner.team)); + default: expef = EFFECT_NADE_EXPLODE(this.realowner.team); break; } +#undef GET_NADE_TYPE_SPAWN_EFFECT +#undef SET_NADE_EFFECT if(expef) Send_Effect(expef, findbetterlocation(this.origin, 8), '0 0 0', 1); @@ -761,23 +934,21 @@ void nade_boom(entity this) this.event_damage = func_null; // prevent somehow calling damage in the next call if(nade_blast) - { - RadiusDamage(this, this.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, this, NULL, autocvar_g_nades_nade_force, this.projectiledeathtype, DMG_NOWEP, this.enemy); - Damage_DamageInfo(this.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, this.projectiledeathtype, 0, this); - } + normal_nade_boom(this); if(this.takedamage) switch ( REGISTRY_GET(Nades, STAT(NADE_BONUS_TYPE, this)) ) { - case NADE_TYPE_NAPALM: nade_napalm_boom(this); break; - case NADE_TYPE_ICE: nade_ice_boom(this); break; + case NADE_TYPE_NAPALM: nade_napalm_boom(this); break; + case NADE_TYPE_ICE: nade_ice_boom(this); break; case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(this); break; - case NADE_TYPE_SPAWN: nade_spawn_boom(this); break; - case NADE_TYPE_HEAL: nade_heal_boom(this); break; - case NADE_TYPE_MONSTER: nade_monster_boom(this); break; - case NADE_TYPE_ENTRAP: nade_entrap_boom(this); break; - case NADE_TYPE_VEIL: nade_veil_boom(this); break; + case NADE_TYPE_SPAWN: nade_spawn_boom(this); break; + case NADE_TYPE_HEAL: nade_heal_boom(this); break; + case NADE_TYPE_MONSTER: nade_monster_boom(this); break; + case NADE_TYPE_ENTRAP: nade_entrap_boom(this); break; + case NADE_TYPE_VEIL: nade_veil_boom(this); break; + case NADE_TYPE_AMMO: nade_ammo_boom(this); break; + case NADE_TYPE_DARKNESS: nade_darkness_boom(this); break; } IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this, @@ -918,12 +1089,25 @@ void nade_damage(entity this, entity inflictor, entity attacker, float damage, i hp -= damage; SetResource(this, RES_HEALTH, hp); - - if ( STAT(NADE_BONUS_TYPE, this) != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker) ) + if(STAT(NADE_BONUS_TYPE, this) != NADE_TYPE_TRANSLOCATE.m_id && STAT(NADE_BONUS_TYPE, this) != NADE_TYPE_SPAWN.m_id) + if(STAT(NADE_BONUS_TYPE, this) != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker)) this.realowner = attacker; if(hp <= 0) + { + if(autocvar_g_nades_spawn_destroy_damage > 0 && STAT(NADE_BONUS_TYPE, this) == NADE_TYPE_SPAWN.m_id) + Damage(this.realowner, attacker, attacker, autocvar_g_nades_spawn_destroy_damage, DEATH_TOUCHEXPLODE.m_id, DMG_NOWEP, this.realowner.origin, '0 0 0'); + + if(autocvar_g_nades_translocate_destroy_damage > 0 && STAT(NADE_BONUS_TYPE, this) == NADE_TYPE_TRANSLOCATE.m_id) + { + Damage(this.realowner, attacker, attacker, autocvar_g_nades_translocate_destroy_damage, DEATH_TOUCHEXPLODE.m_id, DMG_NOWEP, this.realowner.origin, '0 0 0'); + W_PrepareExplosionByDamage(this, this.realowner, nade_boom); // Don't change the owner + + return; + } + W_PrepareExplosionByDamage(this, attacker, nade_boom); + } else nade_burn_spawn(this); } @@ -1150,8 +1334,8 @@ void nade_prime(entity this) } else { - ntype = ((autocvar_g_nades_client_select) ? CS_CVAR(this).cvar_cl_nade_type : autocvar_g_nades_nade_type); - pntype = ((autocvar_g_nades_client_select) ? CS_CVAR(this).cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type); + ntype = ((autocvar_g_nades_client_select) ? CS_CVAR(this).cvar_cl_nade_type : autocvar_g_nades_nade_type); + pntype = ((autocvar_g_nades_client_select) ? CS_CVAR(this).cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type); } spawn_held_nade(this, this, autocvar_g_nades_nade_lifetime, ntype, pntype); @@ -1205,6 +1389,29 @@ void nades_Clear(entity player) STAT(NADE_TIMER, player) = 0; } +int nades_CheckTypes(entity player, int cl_ntype) +{ + // TODO check what happens without this patch +#define CL_NADE_TYPE_CHECK(nade_ent, nade_cvar) \ + case nade_ent.m_id: if (nade_cvar) return cl_ntype + + switch (cl_ntype) + { + CL_NADE_TYPE_CHECK(NADE_TYPE_NAPALM, autocvar_g_nades_napalm); + CL_NADE_TYPE_CHECK(NADE_TYPE_ICE, autocvar_g_nades_ice); + CL_NADE_TYPE_CHECK(NADE_TYPE_TRANSLOCATE, autocvar_g_nades_translocate); + CL_NADE_TYPE_CHECK(NADE_TYPE_SPAWN, autocvar_g_nades_spawn); + CL_NADE_TYPE_CHECK(NADE_TYPE_HEAL, autocvar_g_nades_heal); + CL_NADE_TYPE_CHECK(NADE_TYPE_MONSTER, autocvar_g_nades_pokenade); + CL_NADE_TYPE_CHECK(NADE_TYPE_ENTRAP, autocvar_g_nades_entrap); + CL_NADE_TYPE_CHECK(NADE_TYPE_VEIL, autocvar_g_nades_veil); + CL_NADE_TYPE_CHECK(NADE_TYPE_AMMO, autocvar_g_nades_ammo); + CL_NADE_TYPE_CHECK(NADE_TYPE_DARKNESS, autocvar_g_nades_darkness); + } + return NADE_TYPE_NORMAL.m_id; // default to NADE_TYPE_NORMAL for unknown nade types +#undef CL_NADE_TYPE_CHECK +} + MUTATOR_HOOKFUNCTION(nades, VehicleEnter) { entity player = M_ARGV(0, entity); @@ -1312,7 +1519,7 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) if(autocvar_g_nades_bonus_client_select) { - STAT(NADE_BONUS_TYPE, player) = CS_CVAR(player).cvar_cl_nade_type; + STAT(NADE_BONUS_TYPE, player) = nades_CheckTypes(player, CS_CVAR(player).cvar_cl_nade_type); player.pokenade_type = CS_CVAR(player).cvar_cl_pokenade_type; } else @@ -1331,9 +1538,9 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) STAT(NADE_BONUS, player) = STAT(NADE_BONUS_SCORE, player) = 0; } - if(STAT(VEIL_ORB, player) && STAT(VEIL_ORB, player) <= time) + if(player.nade_veil_time && player.nade_veil_time <= time) { - STAT(VEIL_ORB, player) = 0; + player.nade_veil_time = 0; if(player.vehicle) player.vehicle.alpha = player.vehicle.nade_veil_prevalpha; else @@ -1405,7 +1612,7 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPhysics_UpdateStats) entity player = M_ARGV(0, entity); // these automatically reset, no need to worry - if(STAT(ENTRAP_ORB, player) > time) + if(player.nade_entrap_time > time) STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_nades_entrap_speed; } @@ -1413,16 +1620,16 @@ MUTATOR_HOOKFUNCTION(nades, MonsterMove) { entity mon = M_ARGV(0, entity); - if (STAT(ENTRAP_ORB, mon) > time) + if (mon.nade_entrap_time > time) { M_ARGV(1, float) *= autocvar_g_nades_entrap_speed; // run speed M_ARGV(2, float) *= autocvar_g_nades_entrap_speed; // walk speed } - if (STAT(VEIL_ORB, mon) && STAT(VEIL_ORB, mon) <= time) + if (mon.nade_veil_time && mon.nade_veil_time <= time) { mon.alpha = mon.nade_veil_prevalpha; - STAT(VEIL_ORB, mon) = 0; + mon.nade_veil_time = 0; } } @@ -1430,10 +1637,9 @@ MUTATOR_HOOKFUNCTION(nades, PlayerSpawn) { entity player = M_ARGV(0, entity); - if(autocvar_g_nades_spawn) - player.nade_refire = time + autocvar_g_spawnshieldtime; - else - player.nade_refire = time + autocvar_g_nades_nade_refire; + player.nade_refire = (autocvar_g_nades_onspawn) + ? time + autocvar_g_nades_nade_refire + : time + autocvar_g_spawnshieldtime; if(autocvar_g_nades_bonus_client_select) STAT(NADE_BONUS_TYPE, player) = CS_CVAR(player).cvar_cl_nade_type; @@ -1452,6 +1658,9 @@ MUTATOR_HOOKFUNCTION(nades, PlayerSpawn) delete(player.nade_spawnloc); player.nade_spawnloc = NULL; } + + if(autocvar_g_nades_spawn_health_respawn > 0) + SetResource(player, RES_HEALTH, autocvar_g_nades_spawn_health_respawn); } } @@ -1555,22 +1764,16 @@ MUTATOR_HOOKFUNCTION(nades, SpectateCopy) client.pokenade_type = spectatee.pokenade_type; STAT(NADE_BONUS, client) = STAT(NADE_BONUS, spectatee); STAT(NADE_BONUS_SCORE, client) = STAT(NADE_BONUS_SCORE, spectatee); - STAT(HEALING_ORB, client) = STAT(HEALING_ORB, spectatee); - STAT(HEALING_ORB_ALPHA, client) = STAT(HEALING_ORB_ALPHA, spectatee); - STAT(ENTRAP_ORB, client) = STAT(ENTRAP_ORB, spectatee); - STAT(ENTRAP_ORB_ALPHA, client) = STAT(ENTRAP_ORB_ALPHA, spectatee); - STAT(VEIL_ORB, client) = STAT(VEIL_ORB, spectatee); - STAT(VEIL_ORB_ALPHA, client) = STAT(VEIL_ORB_ALPHA, spectatee); } -MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString) +MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString) { - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Nades"); + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Nades"); } -MUTATOR_HOOKFUNCTION(nades, BuildGameplayTipsString) +MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString) { - M_ARGV(0, string) = strcat(M_ARGV(0, string), "\n\n^3nades^8 are enabled, press 'g' (dropweapon) to use them\n"); + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Nades"); } #endif