private static int CheckArmor(edict_t ent, float[] point, float[] normal, int damage, int te_sparks, int dflags) { gclient_t client; int save; int index; gitem_t armor; if (damage == 0) { return(0); } client = ent.client; if (client == null) { return(0); } if ((dflags & Defines.DAMAGE_NO_ARMOR) != 0) { return(0); } index = GameItems.ArmorIndex(ent); if (index == 0) { return(0); } armor = GameItems.GetItemByIndex(index); var garmor = (gitem_armor_t)armor.info; if (0 != (dflags & Defines.DAMAGE_ENERGY)) { save = (int)Math.Ceiling(garmor.energy_protection * damage); } else { save = (int)Math.Ceiling(garmor.normal_protection * damage); } if (save >= client.pers.inventory[index]) { save = client.pers.inventory[index]; } if (save == 0) { return(0); } client.pers.inventory[index] -= save; GameCombat.SpawnDamage(te_sparks, point, normal, save); return(save); }
/** * T_RadiusDamage. */ public static void T_RadiusDamage(edict_t inflictor, edict_t attacker, float damage, edict_t ignore, float radius, int mod) { float points; EdictIterator edictit = null; float[] v = { 0, 0, 0 }; float[] dir = { 0, 0, 0 }; while ((edictit = GameBase.findradius(edictit, inflictor.s.origin, radius)) != null) { var ent = edictit.o; if (ent == ignore) { continue; } if (ent.takedamage == 0) { continue; } Math3D.VectorAdd(ent.mins, ent.maxs, v); Math3D.VectorMA(ent.s.origin, 0.5f, v, v); Math3D.VectorSubtract(inflictor.s.origin, v, v); points = damage - 0.5f * Math3D.VectorLength(v); if (ent == attacker) { points = points * 0.5f; } if (points > 0) { if (GameCombat.CanDamage(ent, inflictor)) { Math3D.VectorSubtract(ent.s.origin, inflictor.s.origin, dir); GameCombat.T_Damage( ent, inflictor, attacker, dir, inflictor.s.origin, Globals.vec3_origin, (int)points, (int)points, Defines.DAMAGE_RADIUS, mod ); } } } }
/** * Kills all entities that would touch the proposed new positioning of ent. * Ent should be unlinked before calling this! */ public static bool KillBox(edict_t ent) { trace_t tr; while (true) { tr = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, ent.s.origin, null, Defines.MASK_PLAYERSOLID); if (tr.ent == null || tr.ent == GameBase.g_edicts[0]) { break; } // nail it GameCombat.T_Damage( tr.ent, ent, ent, Globals.vec3_origin, ent.s.origin, Globals.vec3_origin, 100000, 0, Defines.DAMAGE_NO_PROTECTION, Defines.MOD_TELEFRAG ); // if we didn't kill it, fail if (tr.ent.solid != 0) { return(false); } } return(true); // all clear }
/** * General effect handling for a player. */ public static void P_WorldEffects() { bool breather; bool envirosuit; int waterlevel, old_waterlevel; if (PlayerView.current_player.movetype == Defines.MOVETYPE_NOCLIP) { PlayerView.current_player.air_finished = GameBase.level.time + 12; // don't // need air return; } waterlevel = PlayerView.current_player.waterlevel; old_waterlevel = PlayerView.current_client.old_waterlevel; PlayerView.current_client.old_waterlevel = waterlevel; breather = PlayerView.current_client.breather_framenum > GameBase.level.framenum; envirosuit = PlayerView.current_client.enviro_framenum > GameBase.level.framenum; // // if just entered a water volume, play a sound // if (old_waterlevel == 0 && waterlevel != 0) { PlayerWeapon.PlayerNoise(PlayerView.current_player, PlayerView.current_player.s.origin, Defines.PNOISE_SELF); if ((PlayerView.current_player.watertype & Defines.CONTENTS_LAVA) != 0) { GameBase.gi.sound(PlayerView.current_player, Defines.CHAN_BODY, GameBase.gi.soundindex("player/lava_in.wav"), 1, Defines.ATTN_NORM, 0); } else if ((PlayerView.current_player.watertype & Defines.CONTENTS_SLIME) != 0) { GameBase.gi.sound(PlayerView.current_player, Defines.CHAN_BODY, GameBase.gi.soundindex("player/watr_in.wav"), 1, Defines.ATTN_NORM, 0); } else if ((PlayerView.current_player.watertype & Defines.CONTENTS_WATER) != 0) { GameBase.gi.sound(PlayerView.current_player, Defines.CHAN_BODY, GameBase.gi.soundindex("player/watr_in.wav"), 1, Defines.ATTN_NORM, 0); } PlayerView.current_player.flags |= Defines.FL_INWATER; // clear damage_debounce, so the pain sound will play immediately PlayerView.current_player.damage_debounce_time = GameBase.level.time - 1; } // // if just completely exited a water volume, play a sound // if (old_waterlevel != 0 && waterlevel == 0) { PlayerWeapon.PlayerNoise(PlayerView.current_player, PlayerView.current_player.s.origin, Defines.PNOISE_SELF); GameBase.gi.sound(PlayerView.current_player, Defines.CHAN_BODY, GameBase.gi.soundindex("player/watr_out.wav"), 1, Defines.ATTN_NORM, 0); PlayerView.current_player.flags &= ~Defines.FL_INWATER; } // // check for head just going under water // if (old_waterlevel != 3 && waterlevel == 3) { GameBase.gi.sound(PlayerView.current_player, Defines.CHAN_BODY, GameBase.gi.soundindex("player/watr_un.wav"), 1, Defines.ATTN_NORM, 0); } // // check for head just coming out of water // if (old_waterlevel == 3 && waterlevel != 3) { if (PlayerView.current_player.air_finished < GameBase.level.time) { // gasp for // air GameBase.gi.sound(PlayerView.current_player, Defines.CHAN_VOICE, GameBase.gi.soundindex("player/gasp1.wav"), 1, Defines.ATTN_NORM, 0); PlayerWeapon.PlayerNoise(PlayerView.current_player, PlayerView.current_player.s.origin, Defines.PNOISE_SELF); } else if (PlayerView.current_player.air_finished < GameBase.level.time + 11) { // just // break // surface GameBase.gi.sound(PlayerView.current_player, Defines.CHAN_VOICE, GameBase.gi.soundindex("player/gasp2.wav"), 1, Defines.ATTN_NORM, 0); } } // // check for drowning // if (waterlevel == 3) { // breather or envirosuit give air if (breather || envirosuit) { PlayerView.current_player.air_finished = GameBase.level.time + 10; if ((int)(PlayerView.current_client.breather_framenum - GameBase.level.framenum) % 25 == 0) { if (PlayerView.current_client.breather_sound == 0) { GameBase.gi.sound( PlayerView.current_player, Defines.CHAN_AUTO, GameBase.gi.soundindex("player/u_breath1.wav"), 1, Defines.ATTN_NORM, 0 ); } else { GameBase.gi.sound( PlayerView.current_player, Defines.CHAN_AUTO, GameBase.gi.soundindex("player/u_breath2.wav"), 1, Defines.ATTN_NORM, 0 ); } PlayerView.current_client.breather_sound ^= 1; PlayerWeapon.PlayerNoise(PlayerView.current_player, PlayerView.current_player.s.origin, Defines.PNOISE_SELF); //FIXME: release a bubble? } } // if out of air, start drowning if (PlayerView.current_player.air_finished < GameBase.level.time) { // drown! if (PlayerView.current_player.client.next_drown_time < GameBase.level.time && PlayerView.current_player.health > 0) { PlayerView.current_player.client.next_drown_time = GameBase.level.time + 1; // take more damage the longer underwater PlayerView.current_player.dmg += 2; if (PlayerView.current_player.dmg > 15) { PlayerView.current_player.dmg = 15; } // play a gurp sound instead of a normal pain sound if (PlayerView.current_player.health <= PlayerView.current_player.dmg) { GameBase.gi.sound( PlayerView.current_player, Defines.CHAN_VOICE, GameBase.gi.soundindex("player/drown1.wav"), 1, Defines.ATTN_NORM, 0 ); } else if ((Lib.rand() & 1) != 0) { GameBase.gi.sound(PlayerView.current_player, Defines.CHAN_VOICE, GameBase.gi.soundindex("*gurp1.wav"), 1, Defines.ATTN_NORM, 0); } else { GameBase.gi.sound(PlayerView.current_player, Defines.CHAN_VOICE, GameBase.gi.soundindex("*gurp2.wav"), 1, Defines.ATTN_NORM, 0); } PlayerView.current_player.pain_debounce_time = GameBase.level.time; GameCombat.T_Damage( PlayerView.current_player, GameBase.g_edicts[0], GameBase.g_edicts[0], Globals.vec3_origin, PlayerView.current_player.s.origin, Globals.vec3_origin, PlayerView.current_player.dmg, 0, Defines.DAMAGE_NO_ARMOR, Defines.MOD_WATER ); } } } else { PlayerView.current_player.air_finished = GameBase.level.time + 12; PlayerView.current_player.dmg = 2; } // // check for sizzle damage // if (waterlevel != 0 && 0 != (PlayerView.current_player.watertype & (Defines.CONTENTS_LAVA | Defines.CONTENTS_SLIME))) { if ((PlayerView.current_player.watertype & Defines.CONTENTS_LAVA) != 0) { if (PlayerView.current_player.health > 0 && PlayerView.current_player.pain_debounce_time <= GameBase.level.time && PlayerView.current_client.invincible_framenum < GameBase.level.framenum) { if ((Lib.rand() & 1) != 0) { GameBase.gi.sound( PlayerView.current_player, Defines.CHAN_VOICE, GameBase.gi.soundindex("player/burn1.wav"), 1, Defines.ATTN_NORM, 0 ); } else { GameBase.gi.sound( PlayerView.current_player, Defines.CHAN_VOICE, GameBase.gi.soundindex("player/burn2.wav"), 1, Defines.ATTN_NORM, 0 ); } PlayerView.current_player.pain_debounce_time = GameBase.level.time + 1; } if (envirosuit) // take 1/3 damage with envirosuit { GameCombat.T_Damage( PlayerView.current_player, GameBase.g_edicts[0], GameBase.g_edicts[0], Globals.vec3_origin, PlayerView.current_player.s.origin, Globals.vec3_origin, 1 * waterlevel, 0, 0, Defines.MOD_LAVA ); } else { GameCombat.T_Damage( PlayerView.current_player, GameBase.g_edicts[0], GameBase.g_edicts[0], Globals.vec3_origin, PlayerView.current_player.s.origin, Globals.vec3_origin, 3 * waterlevel, 0, 0, Defines.MOD_LAVA ); } } if ((PlayerView.current_player.watertype & Defines.CONTENTS_SLIME) != 0) { if (!envirosuit) { // no damage from slime with envirosuit GameCombat.T_Damage( PlayerView.current_player, GameBase.g_edicts[0], GameBase.g_edicts[0], Globals.vec3_origin, PlayerView.current_player.s.origin, Globals.vec3_origin, 1 * waterlevel, 0, 0, Defines.MOD_SLIME ); } } } }
/** * Calculates damage and effect when a player falls down. */ public static void P_FallingDamage(edict_t ent) { float delta; int damage; float[] dir = { 0, 0, 0 }; if (ent.s.modelindex != 255) { return; // not in the player model } if (ent.movetype == Defines.MOVETYPE_NOCLIP) { return; } if (ent.client.oldvelocity[2] < 0 && ent.velocity[2] > ent.client.oldvelocity[2] && null == ent.groundentity) { delta = ent.client.oldvelocity[2]; } else { if (ent.groundentity == null) { return; } delta = ent.velocity[2] - ent.client.oldvelocity[2]; } delta = delta * delta * 0.0001f; // never take falling damage if completely underwater if (ent.waterlevel == 3) { return; } if (ent.waterlevel == 2) { delta *= 0.25f; } if (ent.waterlevel == 1) { delta *= 0.5f; } if (delta < 1) { return; } if (delta < 15) { ent.s.@event = Defines.EV_FOOTSTEP; return; } ent.client.fall_value = delta * 0.5f; if (ent.client.fall_value > 40) { ent.client.fall_value = 40; } ent.client.fall_time = GameBase.level.time + Defines.FALL_TIME; if (delta > 30) { if (ent.health > 0) { if (delta >= 55) { ent.s.@event = Defines.EV_FALLFAR; } else { ent.s.@event = Defines.EV_FALL; } } ent.pain_debounce_time = GameBase.level.time; // no normal pain // sound damage = (int)((delta - 30) / 2); if (damage < 1) { damage = 1; } Math3D.VectorSet(dir, 0, 0, 1); if (GameBase.deathmatch.value == 0 || 0 == ((int)GameBase.dmflags.value & Defines.DF_NO_FALLING)) { GameCombat.T_Damage( ent, GameBase.g_edicts[0], GameBase.g_edicts[0], dir, ent.s.origin, Globals.vec3_origin, damage, 0, 0, Defines.MOD_FALLING ); } } else { ent.s.@event = Defines.EV_FALLSHORT; return; } }
public static void T_Damage( edict_t targ, edict_t inflictor, edict_t attacker, float[] dir, float[] point, float[] normal, int damage, int knockback, int dflags, int mod ) { gclient_t client; int take; int save; int asave; int psave; int te_sparks; if (targ.takedamage == 0) { return; } // friendly fire avoidance // if enabled you can't hurt teammates (but you can hurt yourself) // knockback still occurs if (targ != attacker && ((GameBase.deathmatch.value != 0 && 0 != ((int)GameBase.dmflags.value & (Defines.DF_MODELTEAMS | Defines.DF_SKINTEAMS))) || GameBase.coop.value != 0)) { if (GameUtil.OnSameTeam(targ, attacker)) { if (((int)GameBase.dmflags.value & Defines.DF_NO_FRIENDLY_FIRE) != 0) { damage = 0; } else { mod |= Defines.MOD_FRIENDLY_FIRE; } } } GameBase.meansOfDeath = mod; // easy mode takes half damage if (GameBase.skill.value == 0 && GameBase.deathmatch.value == 0 && targ.client != null) { damage /= 2; if (damage == 0) { damage = 1; } } client = targ.client; if ((dflags & Defines.DAMAGE_BULLET) != 0) { te_sparks = Defines.TE_BULLET_SPARKS; } else { te_sparks = Defines.TE_SPARKS; } Math3D.VectorNormalize(dir); // bonus damage for suprising a monster if (0 == (dflags & Defines.DAMAGE_RADIUS) && (targ.svflags & Defines.SVF_MONSTER) != 0 && attacker.client != null && targ.enemy == null && targ.health > 0) { damage *= 2; } if ((targ.flags & Defines.FL_NO_KNOCKBACK) != 0) { knockback = 0; } // figure momentum add if (0 == (dflags & Defines.DAMAGE_NO_KNOCKBACK)) { if (knockback != 0 && targ.movetype != Defines.MOVETYPE_NONE && targ.movetype != Defines.MOVETYPE_BOUNCE && targ.movetype != Defines.MOVETYPE_PUSH && targ.movetype != Defines.MOVETYPE_STOP) { float[] kvel = { 0, 0, 0 }; float mass; if (targ.mass < 50) { mass = 50; } else { mass = targ.mass; } if (targ.client != null && attacker == targ) { Math3D.VectorScale(dir, 1600.0f * (float)knockback / mass, kvel); } // the rocket jump hack... else { Math3D.VectorScale(dir, 500.0f * (float)knockback / mass, kvel); } Math3D.VectorAdd(targ.velocity, kvel, targ.velocity); } } take = damage; save = 0; // check for godmode if ((targ.flags & Defines.FL_GODMODE) != 0 && 0 == (dflags & Defines.DAMAGE_NO_PROTECTION)) { take = 0; save = damage; GameCombat.SpawnDamage(te_sparks, point, normal, save); } // check for invincibility if (client != null && client.invincible_framenum > GameBase.level.framenum && 0 == (dflags & Defines.DAMAGE_NO_PROTECTION)) { if (targ.pain_debounce_time < GameBase.level.time) { GameBase.gi.sound(targ, Defines.CHAN_ITEM, GameBase.gi.soundindex("items/protect4.wav"), 1, Defines.ATTN_NORM, 0); targ.pain_debounce_time = GameBase.level.time + 2; } take = 0; save = damage; } psave = GameCombat.CheckPowerArmor(targ, point, normal, take, dflags); take -= psave; asave = GameCombat.CheckArmor(targ, point, normal, take, te_sparks, dflags); take -= asave; // treat cheat/powerup savings the same as armor asave += save; // team damage avoidance if (0 == (dflags & Defines.DAMAGE_NO_PROTECTION) && GameCombat.CheckTeamDamage(targ, attacker)) { return; } // do the damage if (take != 0) { if (0 != (targ.svflags & Defines.SVF_MONSTER) || client != null) { GameCombat.SpawnDamage(Defines.TE_BLOOD, point, normal, take); } else { GameCombat.SpawnDamage(te_sparks, point, normal, take); } targ.health = targ.health - take; if (targ.health <= 0) { if ((targ.svflags & Defines.SVF_MONSTER) != 0 || client != null) { targ.flags |= Defines.FL_NO_KNOCKBACK; } GameCombat.Killed(targ, inflictor, attacker, take, point); return; } } if ((targ.svflags & Defines.SVF_MONSTER) != 0) { GameCombat.M_ReactToDamage(targ, attacker); if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED) && take != 0) { targ.pain.pain(targ, attacker, knockback, take); // nightmare mode monsters don't go into pain frames often if (GameBase.skill.value == 3) { targ.pain_debounce_time = GameBase.level.time + 5; } } } else if (client != null) { if ((targ.flags & Defines.FL_GODMODE) == 0 && take != 0) { targ.pain.pain(targ, attacker, knockback, take); } } else if (take != 0) { if (targ.pain != null) { targ.pain.pain(targ, attacker, knockback, take); } } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if (client != null) { client.damage_parmor += psave; client.damage_armor += asave; client.damage_blood += take; client.damage_knockback += knockback; Math3D.VectorCopy(point, client.damage_from); } }
private static int CheckPowerArmor(edict_t ent, float[] point, float[] normal, int damage, int dflags) { gclient_t client; int save; int power_armor_type; var index = 0; int damagePerCell; int pa_te_type; var power = 0; int power_used; if (damage == 0) { return(0); } client = ent.client; if ((dflags & Defines.DAMAGE_NO_ARMOR) != 0) { return(0); } if (client != null) { power_armor_type = GameItems.PowerArmorType(ent); if (power_armor_type != Defines.POWER_ARMOR_NONE) { index = GameItems.ITEM_INDEX(GameItems.FindItem("Cells")); power = client.pers.inventory[index]; } } else if ((ent.svflags & Defines.SVF_MONSTER) != 0) { power_armor_type = ent.monsterinfo.power_armor_type; power = ent.monsterinfo.power_armor_power; } else { return(0); } if (power_armor_type == Defines.POWER_ARMOR_NONE) { return(0); } if (power == 0) { return(0); } if (power_armor_type == Defines.POWER_ARMOR_SCREEN) { float[] vec = { 0, 0, 0 }; float dot; float[] forward = { 0, 0, 0 }; // only works if damage point is in front Math3D.AngleVectors(ent.s.angles, forward, null, null); Math3D.VectorSubtract(point, ent.s.origin, vec); Math3D.VectorNormalize(vec); dot = Math3D.DotProduct(vec, forward); if (dot <= 0.3) { return(0); } damagePerCell = 1; pa_te_type = Defines.TE_SCREEN_SPARKS; damage = damage / 3; } else { damagePerCell = 2; pa_te_type = Defines.TE_SHIELD_SPARKS; damage = 2 * damage / 3; } save = power * damagePerCell; if (save == 0) { return(0); } if (save > damage) { save = damage; } GameCombat.SpawnDamage(pa_te_type, point, normal, save); ent.powerarmor_time = GameBase.level.time + 0.2f; power_used = save / damagePerCell; if (client != null) { client.pers.inventory[index] -= power_used; } else { ent.monsterinfo.power_armor_power -= power_used; } return(save); }