Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
damage.qc File Reference
Include dependency graph for damage.qc:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define SPREE_ITEM(counta, countb, center, normal, gentle)

Functions

string AppendItemcodes (string s, entity player)
void Damage (entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
float Fire_AddDamage (entity e, entity o, float d, float t, float dt)
void Fire_ApplyDamage (entity e)
bool frag_centermessage_override (entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target, string attacker_name)
void GiveFrags (entity attacker, entity targ, float f, int deathtype,.entity weaponentity)
bool Heal (entity targ, entity inflictor, float amount, float limit)
void LogDeath (string mode, int deathtype, entity killer, entity killed)
void Obituary (entity attacker, entity inflictor, entity targ, int deathtype,.entity weaponentity)
void Obituary_SpecialDeath (entity notif_target, float murder, bool msg_from_ent, int deathtype, string s1, string s2, string s3, string s4, float f1, float f2, float f3)
float Obituary_WeaponDeath (entity notif_target, float murder, int deathtype, string s1, string s2, string s3, float f1, float f2)
float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype,.entity weaponentity, entity directhitentity)
float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, vector forcexyzscale, int deathtype,.entity weaponentity, entity directhitentity)

Macro Definition Documentation

◆ SPREE_ITEM

#define SPREE_ITEM ( counta,
countb,
center,
normal,
gentle )
Value:
case counta: \
Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
if (!warmup_stage) \
PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
break;
bool warmup_stage
Definition main.qh:120

Function Documentation

◆ AppendItemcodes()

string AppendItemcodes ( string s,
entity player )

Definition at line 83 of file damage.qc.

84{
85 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
86 {
87 .entity weaponentity = weaponentities[slot];
88 int w = player.(weaponentity).m_weapon.m_id;
89 if(w == 0)
90 w = player.(weaponentity).cnt; // previous weapon
91 if(w != 0 || slot == 0)
92 s = strcat(s, ftos(w));
93 }
94 if(PHYS_INPUT_BUTTON_CHAT(player))
95 s = strcat(s, "T");
96 // TODO: include these codes as a flag on the item itself
97 MUTATOR_CALLHOOK(LogDeath_AppendItemCodes, player, s);
98 s = M_ARGV(1, string);
99 return s;
100}
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
float cnt
Definition powerups.qc:24
#define M_ARGV(x, type)
Definition events.qh:17
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition player.qh:159
string ftos(float f)
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
const int MAX_WEAPONSLOTS
Definition weapon.qh:16
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
Weapon m_weapon
Definition wepent.qh:26

References cnt, entity(), ftos(), M_ARGV, m_weapon, MAX_WEAPONSLOTS, MUTATOR_CALLHOOK, PHYS_INPUT_BUTTON_CHAT, strcat(), and weaponentities.

Referenced by LogDeath().

◆ Damage()

void Damage ( entity targ,
entity inflictor,
entity attacker,
float damage,
int deathtype,
.entity weaponentity,
vector hitloc,
vector force )

Definition at line 503 of file damage.qc.

504{
505 float complainteamdamage = 0;
506 float mirrordamage = 0;
507 float mirrorforce = 0;
508
509 if (game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR))
510 return;
511
512 entity attacker_save = attacker;
513
514 // special rule: gravity bombs and sound-based attacks do not affect teammates (other than for disconnecting the hook)
515 if(DEATH_ISWEAPON(deathtype, WEP_HOOK) || (deathtype & HITTYPE_SOUND))
516 {
517 if(IS_PLAYER(targ) && SAME_TEAM(targ, attacker))
518 {
519 return;
520 }
521 }
522
523 if(deathtype == DEATH_KILL.m_id || deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
524 {
525 // exit the vehicle before killing (fixes a crash)
526 if(IS_PLAYER(targ) && targ.vehicle)
527 vehicles_exit(targ.vehicle, VHEF_RELEASE);
528
529 // These are ALWAYS lethal
530 // No damage modification here
531 // Instead, prepare the victim for their death...
532 if(deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
533 {
535 SetResourceExplicit(targ, RES_HEALTH, 0.9); // this is < 1
536 }
537 StatusEffects_remove(STATUSEFFECT_SpawnShield, targ, STATUSEFFECT_REMOVE_CLEAR);
538 targ.flags -= targ.flags & FL_GODMODE;
539 damage = 100000;
540 }
541 else if(deathtype == DEATH_MIRRORDAMAGE.m_id || deathtype == DEATH_NOAMMO.m_id)
542 {
543 // no processing
544 }
545 else
546 {
547 // nullify damage if teamplay is on
548 if(deathtype != DEATH_TELEFRAG.m_id)
549 if(IS_PLAYER(attacker))
550 {
551 // avoid dealing damage or force to other independent players
552 // and avoid dealing damage or force to things owned by other independent players
553 if((IS_PLAYER(targ) && targ != attacker && (IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(targ))) ||
554 (targ.realowner && IS_INDEPENDENT_PLAYER(targ.realowner) && attacker != targ.realowner))
555 {
556 damage = 0;
557 force = '0 0 0';
558 }
559 else if(!STAT(FROZEN, targ) && SAME_TEAM(attacker, targ))
560 {
562 damage = 0;
563 else if(attacker != targ)
564 {
566 {
567 if(IS_PLAYER(targ) && !IS_DEAD(targ))
568 {
569 attacker.dmg_team = attacker.dmg_team + damage;
570 complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
571 }
572 }
573 else if(autocvar_teamplay_mode == 3)
574 damage = 0;
575 else if(autocvar_teamplay_mode == 4)
576 {
577 if(IS_PLAYER(targ) && !IS_DEAD(targ))
578 {
579 attacker.dmg_team = attacker.dmg_team + damage;
580 complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
581 if(complainteamdamage > 0)
582 mirrordamage = autocvar_g_mirrordamage * complainteamdamage;
583 mirrorforce = autocvar_g_mirrordamage * vlen(force);
584 damage = autocvar_g_friendlyfire * damage;
585 // mirrordamage will be used LATER
586
588 {
590 attacker.dmg_take += v.x;
591 attacker.dmg_save += v.y;
592 attacker.dmg_inflictor = inflictor;
593 mirrordamage = v.z;
594 mirrorforce = 0;
595 }
596
598 {
600 targ.dmg_take += v.x;
601 targ.dmg_save += v.y;
602 targ.dmg_inflictor = inflictor;
603 damage = 0;
605 force = '0 0 0';
606 }
607 }
608 else if(!targ.canteamdamage)
609 damage = 0;
610 }
611 }
612 }
613 }
614
615 if (!DEATH_ISSPECIAL(deathtype))
616 {
618 mirrordamage *= autocvar_g_weapondamagefactor;
619 complainteamdamage *= autocvar_g_weapondamagefactor;
620 force = force * autocvar_g_weaponforcefactor;
621 mirrorforce *= autocvar_g_weaponforcefactor;
622 }
623
624 // should this be changed at all? If so, in what way?
625 MUTATOR_CALLHOOK(Damage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force, attacker.(weaponentity));
626 damage = M_ARGV(4, float);
627 mirrordamage = M_ARGV(5, float);
628 force = M_ARGV(6, vector);
629
630 if(IS_PLAYER(targ) && damage > 0 && attacker)
631 {
632 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
633 {
634 .entity went = weaponentities[slot];
635 if(targ.(went).hook && targ.(went).hook.aiment == attacker)
636 RemoveHook(targ.(went).hook);
637 }
638 }
639
640 if (targ == attacker)
641 damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
642
643 // count the damage
644 if(attacker)
645 if(!IS_DEAD(targ))
646 if(deathtype != DEATH_BUFF.m_id)
647 if(targ.takedamage == DAMAGE_AIM)
648 if(targ != attacker)
649 {
650 entity victim;
651 if(IS_VEHICLE(targ) && targ.owner)
652 victim = targ.owner;
653 else
654 victim = targ;
655
656 // TODO: allow the mutator hook to tell if the hit sound should be team or not
657 if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
658 {
659 if (DIFF_TEAM(victim, attacker))
660 {
661 if(damage > 0)
662 {
663 if(deathtype != DEATH_FIRE.m_id)
664 {
665 if(PHYS_INPUT_BUTTON_CHAT(victim))
666 attacker.typehitsound += 1;
667 else
668 attacker.hitsound_damage_dealt += damage;
669 }
670
671 impressive_hits += 1;
672
673 if (!DEATH_ISSPECIAL(deathtype))
674 {
675 if(IS_PLAYER(targ)) // don't do this for vehicles
676 if(IsFlying(victim))
677 yoda = 1;
678 }
679 }
680 }
681 else if (IS_PLAYER(attacker) && !STAT(FROZEN, victim) && !(deathtype & HITTYPE_SPAM)) // same team
682 {
683 if (deathtype != DEATH_FIRE.m_id)
684 {
685 attacker.typehitsound += 1;
686 }
687 if(complainteamdamage > 0)
688 if(time > CS(attacker).teamkill_complain)
689 {
690 CS(attacker).teamkill_complain = time + 5;
691 CS(attacker).teamkill_soundtime = time + 0.4;
692 CS(attacker).teamkill_soundsource = targ;
693 }
694 }
695 }
696 }
697 }
698
699 // apply push
700 if (targ.damageforcescale)
701 if (force)
702 if (!IS_PLAYER(targ) || !StatusEffects_active(STATUSEFFECT_SpawnShield, targ) || targ == attacker)
703 {
704 vector farce = damage_explosion_calcpush(targ.damageforcescale * force, targ.velocity, autocvar_g_balance_damagepush_speedfactor);
705 if(targ.move_movetype == MOVETYPE_PHYSICS)
706 {
707 entity farcent = new(farce);
708 farcent.enemy = targ;
709 farcent.movedir = farce * 10;
710 if(targ.mass)
711 farcent.movedir = farcent.movedir * targ.mass;
712 farcent.origin = hitloc;
713 farcent.forcetype = FORCETYPE_FORCEATPOS;
714 farcent.nextthink = time + 0.1;
715 setthink(farcent, SUB_Remove);
716 }
717 else if(targ.move_movetype != MOVETYPE_NOCLIP)
718 {
719 targ.velocity = targ.velocity + farce;
720 }
721 UNSET_ONGROUND(targ);
723 }
724 // apply damage
725 if (damage != 0 || (targ.damageforcescale && force))
726 if (targ.event_damage)
727 targ.event_damage (targ, inflictor, attacker, damage, deathtype, weaponentity, hitloc, force);
728
729 // apply mirror damage if any
730 if(!autocvar_g_mirrordamage_onlyweapons || DEATH_WEAPONOF(deathtype) != WEP_Null)
731 if(mirrordamage > 0 || mirrorforce > 0)
732 {
733 attacker = attacker_save;
734
735 force = normalize(attacker.origin + attacker.view_ofs - hitloc) * mirrorforce;
736 Damage(attacker, inflictor, attacker, mirrordamage, DEATH_MIRRORDAMAGE.m_id, weaponentity, attacker.origin, force);
737 }
738}
vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor)
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
bool IsFlying(entity this)
Definition player.qc:836
#define IS_CLIENT(s)
Definition player.qh:242
#define IS_DEAD(s)
Definition player.qh:245
#define IS_PLAYER(s)
Definition player.qh:243
float game_stopped
Definition stats.qh:81
vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage)
Definition util.qc:1313
const int FL_GODMODE
Definition constants.qh:75
const int FRAGS_SPECTATOR
Definition constants.qh:4
float time
void UpdateCSQCProjectile(entity e)
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition damage.qc:503
bool autocvar_g_mirrordamage_virtual
Definition damage.qh:15
bool autocvar_g_mirrordamage_onlyweapons
Definition damage.qh:16
float yoda
Definition damage.qh:48
float autocvar_g_friendlyfire_virtual_force
Definition damage.qh:28
float autocvar_g_balance_armor_blockpercent
Definition damage.qh:21
int impressive_hits
Definition damage.qh:49
float autocvar_g_balance_selfdamagepercent
Definition damage.qh:25
float autocvar_g_friendlyfire_virtual
Definition damage.qh:27
float autocvar_g_teamdamage_threshold
Definition damage.qh:23
float teamkill_complain
Definition damage.qh:54
float autocvar_g_friendlyfire
Definition damage.qh:26
float autocvar_g_balance_damagepush_speedfactor
Definition damage.qh:18
float autocvar_g_mirrordamage
Definition damage.qh:14
#define DEATH_ISWEAPON(t, w)
Definition all.qh:46
#define DEATH_ISSPECIAL(t)
Definition all.qh:39
const int HITTYPE_SPAM
Definition all.qh:34
#define DEATH_WEAPONOF(t)
Definition all.qh:45
const int HITTYPE_SOUND
Definition all.qh:33
void SUB_Remove(entity this)
Remove entity.
Definition defer.qh:13
const int ACTIVE_ACTIVE
Definition defs.qh:37
const float FORCETYPE_FORCEATPOS
RES_ARMOR
Definition ent_cs.qc:130
#define STAT(...)
Definition stats.qh:82
float vlen(vector v)
vector normalize(vector v)
#define UNSET_ONGROUND(s)
Definition movetypes.qh:18
const int MOVETYPE_PHYSICS
Definition movetypes.qh:142
const int MOVETYPE_NOCLIP
Definition movetypes.qh:137
@ STATUSEFFECT_REMOVE_CLEAR
Effect is being forcibly removed without calling any additional mechanics.
Definition all.qh:30
#define setthink(e, f)
vector
Definition self.qh:92
int killcount
Definition client.qh:315
#define IS_INDEPENDENT_PLAYER(e)
Definition client.qh:312
void RemoveHook(entity this)
Definition hook.qc:48
ClientState CS(Client this)
Definition state.qh:47
void StatusEffects_remove(StatusEffect this, entity actor, int removal_type)
bool StatusEffects_active(StatusEffect this, entity actor)
const int DAMAGE_AIM
Definition subs.qh:81
void vehicles_exit(entity vehic, bool eject)
const int VHEF_RELEASE
User pressed exit key 3 times fast (not implemented) or vehicle is dying.
int autocvar_teamplay_mode
Definition teamplay.qh:3
#define SAME_TEAM(a, b)
Definition teams.qh:241
#define DIFF_TEAM(a, b)
Definition teams.qh:242
#define IS_TURRET(v)
Definition utils.qh:23
#define IS_MONSTER(v)
Definition utils.qh:21
#define IS_VEHICLE(v)
Definition utils.qh:22
float autocvar_g_weaponforcefactor
float autocvar_g_weapondamagefactor

References ACTIVE_ACTIVE, autocvar_g_balance_armor_blockpercent, autocvar_g_balance_damagepush_speedfactor, autocvar_g_balance_selfdamagepercent, autocvar_g_friendlyfire, autocvar_g_friendlyfire_virtual, autocvar_g_friendlyfire_virtual_force, autocvar_g_mirrordamage, autocvar_g_mirrordamage_onlyweapons, autocvar_g_mirrordamage_virtual, autocvar_g_teamdamage_threshold, autocvar_g_weapondamagefactor, autocvar_g_weaponforcefactor, autocvar_teamplay_mode, CS(), Damage(), DAMAGE_AIM, damage_explosion_calcpush(), DEATH_ISSPECIAL, DEATH_ISWEAPON, DEATH_WEAPONOF, DIFF_TEAM, entity(), FL_GODMODE, FORCETYPE_FORCEATPOS, FRAGS_SPECTATOR, game_stopped, GetResource(), healtharmor_applydamage(), HITTYPE_SOUND, HITTYPE_SPAM, impressive_hits, IS_CLIENT, IS_DEAD, IS_INDEPENDENT_PLAYER, IS_MONSTER, IS_PLAYER, IS_TURRET, IS_VEHICLE, IsFlying(), killcount, M_ARGV, MAX_WEAPONSLOTS, MOVETYPE_NOCLIP, MOVETYPE_PHYSICS, MUTATOR_CALLHOOK, normalize(), PHYS_INPUT_BUTTON_CHAT, RemoveHook(), RES_ARMOR, SAME_TEAM, SetResourceExplicit(), setthink, STAT, STATUSEFFECT_REMOVE_CLEAR, StatusEffects_active(), StatusEffects_remove(), SUB_Remove(), teamkill_complain, time, UNSET_ONGROUND, UpdateCSQCProjectile(), vector, vehicles_exit(), VHEF_RELEASE, vlen(), weaponentities, and yoda.

Referenced by buff_Vengeance_DelayedDamage(), bumblebee_pilot_frame(), ClientKill_Now(), CommonCommand_editmob(), CreatureFrame_FallDamage(), CreatureFrame_hotliquids(), ctf_CaptureShield_Touch(), Damage(), door_blocked(), door_generic_plat_blocked(), DrownPlayer(), Fire_ApplyDamage(), fireBullet_falloff(), generic_plat_blocked(), havocbot_role_ctf_carrier(), instagib_countdown(), KillPlayerForTeamChange(), M_Zombie_Attack_Leap_Touch(), misc_laser_think(), Monster_Attack_Melee(), Monster_Move(), Monster_Think(), MUTATOR_HOOKFUNCTION(), MUTATOR_HOOKFUNCTION(), MUTATOR_HOOKFUNCTION(), MUTATOR_HOOKFUNCTION(), nade_heal_touch(), nade_spawn_DestroyDamage(), nade_translocate_DestroyDamage(), ons_CaptureShield_Touch(), Onslaught_CheckWinner(), plat_crush(), RadiusDamageForSource(), target_kill_use(), tdeath(), trigger_hurt_touch(), vehicles_impact(), vehicles_touch(), W_Arc_Beam_Think(), W_Fireball_Explode(), W_OverkillRocketPropelledChainsaw_Think(), W_Shotgun_Melee_Think(), and walker_melee_do_dmg().

◆ Fire_AddDamage()

float Fire_AddDamage ( entity e,
entity o,
float d,
float t,
float dt )

Definition at line 1002 of file damage.qc.

1003{
1004 float maxtime, mintime, maxdamage, mindamage, maxdps, mindps, totaldamage, totaltime;
1005
1006 if (d <= 0)
1007 return -1;
1008
1009 if(IS_PLAYER(e))
1010 {
1011 if(IS_DEAD(e))
1012 return -1;
1013 }
1014
1015 t = max(t, 0.1);
1016 float dps = d / t;
1017 if(StatusEffects_active(STATUSEFFECT_Burning, e))
1018 {
1019 float fireendtime = StatusEffects_gettime(STATUSEFFECT_Burning, e);
1020
1021 mintime = fireendtime - time;
1022 maxtime = max(mintime, t);
1023
1024 mindps = e.fire_damagepersec;
1025 maxdps = max(mindps, dps);
1026
1027 if(maxtime > mintime || maxdps > mindps)
1028 {
1029 // Constraints:
1030
1031 // damage we have right now
1032 mindamage = mindps * mintime;
1033
1034 // damage we want to get
1035 maxdamage = mindamage + d;
1036
1037 // but we can't exceed maxtime * maxdps!
1038 totaldamage = min(maxdamage, maxtime * maxdps);
1039
1040 // LEMMA:
1041 // Look at:
1042 // totaldamage = min(mindamage + d, maxtime * maxdps)
1043 // We see:
1044 // totaldamage <= maxtime * maxdps
1045 // ==> totaldamage / maxdps <= maxtime.
1046 // We also see:
1047 // totaldamage / mindps = min(mindamage / mindps + d, maxtime * maxdps / mindps)
1048 // >= min(mintime, maxtime)
1049 // ==> totaldamage / maxdps >= mintime.
1050
1051 /*
1052 // how long do we damage then?
1053 // at least as long as before
1054 // but, never exceed maxdps
1055 totaltime = max(mintime, totaldamage / maxdps); // always <= maxtime due to lemma
1056 */
1057
1058 // alternate:
1059 // at most as long as maximum allowed
1060 // but, never below mindps
1061 totaltime = min(maxtime, totaldamage / mindps); // always >= mintime due to lemma
1062
1063 // assuming t > mintime, dps > mindps:
1064 // we get d = t * dps = maxtime * maxdps
1065 // totaldamage = min(maxdamage, maxtime * maxdps) = min(... + d, maxtime * maxdps) = maxtime * maxdps
1066 // totaldamage / maxdps = maxtime
1067 // totaldamage / mindps > totaldamage / maxdps = maxtime
1068 // FROM THIS:
1069 // a) totaltime = max(mintime, maxtime) = maxtime
1070 // b) totaltime = min(maxtime, totaldamage / maxdps) = maxtime
1071
1072 // assuming t <= mintime:
1073 // we get maxtime = mintime
1074 // a) totaltime = max(mintime, ...) >= mintime, also totaltime <= maxtime by the lemma, therefore totaltime = mintime = maxtime
1075 // b) totaltime = min(maxtime, ...) <= maxtime, also totaltime >= mintime by the lemma, therefore totaltime = mintime = maxtime
1076
1077 // assuming dps <= mindps:
1078 // we get mindps = maxdps.
1079 // With this, the lemma says that mintime <= totaldamage / mindps = totaldamage / maxdps <= maxtime.
1080 // a) totaltime = max(mintime, totaldamage / maxdps) = totaldamage / maxdps
1081 // b) totaltime = min(maxtime, totaldamage / mindps) = totaldamage / maxdps
1082
1083 e.fire_damagepersec = totaldamage / totaltime;
1084 StatusEffects_apply(STATUSEFFECT_Burning, e, time + totaltime, 0);
1085 if(totaldamage > 1.2 * mindamage)
1086 {
1087 e.fire_deathtype = dt;
1088 if(e.fire_owner != o)
1089 {
1090 e.fire_owner = o;
1091 e.fire_hitsound = false;
1092 }
1093 }
1094 if(accuracy_isgooddamage(o, e))
1095 accuracy_add(o, DEATH_WEAPONOF(dt), 0, max(0, totaldamage - mindamage), 0); // add to hit
1096 return max(0, totaldamage - mindamage); // can never be negative, but to make sure
1097 }
1098 else
1099 return 0;
1100 }
1101 else
1102 {
1103 e.fire_damagepersec = dps;
1104 StatusEffects_apply(STATUSEFFECT_Burning, e, time + t, 0);
1105 e.fire_deathtype = dt;
1106 e.fire_owner = o;
1107 e.fire_hitsound = false;
1108 if(accuracy_isgooddamage(o, e))
1109 accuracy_add(o, DEATH_WEAPONOF(dt), 0, d, 0); // add to hit
1110 return d;
1111 }
1112}
void accuracy_add(entity this, Weapon w, float fired, float hit, float real)
update accuracy stats
Definition accuracy.qc:102
bool accuracy_isgooddamage(entity attacker, entity targ)
does this damage count towards accuracy stats?
Definition accuracy.qc:133
float min(float f,...)
float max(float f,...)
float StatusEffects_gettime(StatusEffect this, entity actor)
void StatusEffects_apply(StatusEffect this, entity actor, float eff_time, int eff_flags)

References accuracy_add(), accuracy_isgooddamage(), DEATH_WEAPONOF, entity(), IS_DEAD, IS_PLAYER, max(), min(), StatusEffects_active(), StatusEffects_apply(), StatusEffects_gettime(), and time.

Referenced by CreatureFrame_hotliquids(), MUTATOR_HOOKFUNCTION(), napalm_damage(), W_Fireball_Firemine_Touch(), and W_Fireball_LaserPlay().

◆ Fire_ApplyDamage()

void Fire_ApplyDamage ( entity e)

Definition at line 1114 of file damage.qc.

1115{
1116 float t, d, hi, ty;
1117 entity o;
1118
1119 for(t = 0, o = e.owner; o.owner && t < 16; o = o.owner, ++t);
1120 if(IS_NOT_A_CLIENT(o))
1121 o = e.fire_owner;
1122
1123 float fireendtime = StatusEffects_gettime(STATUSEFFECT_Burning, e);
1124 t = min(frametime, fireendtime - time);
1125 d = e.fire_damagepersec * t;
1126
1127 hi = e.fire_owner.hitsound_damage_dealt;
1128 ty = e.fire_owner.typehitsound;
1129 Damage(e, e, e.fire_owner, d, e.fire_deathtype, DMG_NOWEP, e.origin, '0 0 0');
1130 if(e.fire_hitsound && e.fire_owner)
1131 {
1132 e.fire_owner.hitsound_damage_dealt = hi;
1133 e.fire_owner.typehitsound = ty;
1134 }
1135 e.fire_hitsound = true;
1136
1137 if(!IS_INDEPENDENT_PLAYER(e) && !STAT(FROZEN, e) && !StatusEffects_active(STATUSEFFECT_Frozen, e))
1138 {
1139 IL_EACH(g_damagedbycontents, it.damagedbycontents && it != e,
1140 {
1141 if(!IS_DEAD(it) && it.takedamage && !IS_INDEPENDENT_PLAYER(it))
1142 if(boxesoverlap(e.absmin, e.absmax, it.absmin, it.absmax))
1143 {
1144 t = autocvar_g_balance_firetransfer_time * (fireendtime - time);
1145 d = autocvar_g_balance_firetransfer_damage * e.fire_damagepersec * t;
1146 Fire_AddDamage(it, o, d, t, DEATH_FIRE.m_id);
1147 }
1148 });
1149 }
1150}
#define IS_NOT_A_CLIENT(s)
Definition player.qh:244
float frametime
IntrusiveList g_damagedbycontents
Definition damage.qh:135
#define DMG_NOWEP
Definition damage.qh:104
#define IL_EACH(this, cond, body)

References Damage(), DMG_NOWEP, entity(), frametime, g_damagedbycontents, IL_EACH, IS_INDEPENDENT_PLAYER, IS_NOT_A_CLIENT, min(), STAT, StatusEffects_active(), StatusEffects_gettime(), and time.

◆ frag_centermessage_override()

bool frag_centermessage_override ( entity attacker,
entity targ,
int deathtype,
int kill_count_to_attacker,
int kill_count_to_target,
string attacker_name )

Definition at line 216 of file damage.qc.

217{
218 if(deathtype == DEATH_FIRE.m_id)
219 {
220 Send_Notification(NOTIF_ONE, attacker, MSG_CHOICE, CHOICE_FRAG_FIRE, targ.netname, kill_count_to_attacker, (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping));
221 Send_Notification(NOTIF_ONE, targ, MSG_CHOICE, CHOICE_FRAGGED_FIRE, attacker_name, kill_count_to_target, GetResource(attacker, RES_HEALTH), GetResource(attacker, RES_ARMOR), (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping));
222 return true;
223 }
224
225 return MUTATOR_CALLHOOK(FragCenterMessage, attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target);
226}
float ping
Definition main.qh:169
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15

References CS(), entity(), GetResource(), IS_BOT_CLIENT, MUTATOR_CALLHOOK, ping, RES_ARMOR, and Send_Notification().

Referenced by Obituary().

◆ GiveFrags()

void GiveFrags ( entity attacker,
entity targ,
float f,
int deathtype,
.entity weaponentity )

Definition at line 43 of file damage.qc.

44{
45 // TODO route through PlayerScores instead
46 if(game_stopped) return;
47
48 if(f < 0)
49 {
50 if(targ == attacker)
51 {
52 // suicide
53 GameRules_scoring_add(attacker, SUICIDES, 1);
54 }
55 else
56 {
57 // teamkill
58 int teamkills = GameRules_scoring_add(attacker, TEAMKILLS, 1);
60 f -= (teamkills * (teamkills - 1)) * 0.5; // negative 1, 2, 4, 7, 11, 16, 22, etc.
61 }
62 }
63 else
64 {
65 // regular frag
66 GameRules_scoring_add(attacker, KILLS, 1);
67 if(!warmup_stage && targ.playerid)
68 PlayerStats_GameReport_Event_Player(attacker, sprintf("kills-%d", targ.playerid), 1);
69 }
70
71 GameRules_scoring_add(targ, DEATHS, 1);
72
73 // FIXME fix the mess this is (we have REAL points now!)
74 if(MUTATOR_CALLHOOK(GiveFragsForKill, attacker, targ, f, deathtype, attacker.(weaponentity)))
75 f = M_ARGV(2, float);
76
77 attacker.totalfrags += f;
78
79 if(f)
80 GameRules_scoring_add_team(attacker, SCORE, f);
81}
bool autocvar_g_teamkill_punishing
Definition damage.qh:24
#define PlayerStats_GameReport_Event_Player(ent, eventid, val)
#define GameRules_scoring_add(client, fld, value)
Definition sv_rules.qh:85
#define GameRules_scoring_add_team(client, fld, value)
Definition sv_rules.qh:89

References autocvar_g_teamkill_punishing, entity(), game_stopped, GameRules_scoring_add, GameRules_scoring_add_team, M_ARGV, MUTATOR_CALLHOOK, PlayerStats_GameReport_Event_Player, and warmup_stage.

Referenced by MUTATOR_HOOKFUNCTION(), and Obituary().

◆ Heal()

bool Heal ( entity targ,
entity inflictor,
float amount,
float limit )

Definition at line 987 of file damage.qc.

988{
989 // TODO: mutator hook to control healing
990 if(game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR) || STAT(FROZEN, targ) || IS_DEAD(targ))
991 return false;
992
993 bool healed = false;
994 if(targ.event_heal)
995 healed = targ.event_heal(targ, inflictor, amount, limit);
996 // TODO: additional handling? what if the healing kills them? should this abort if healing would do so etc
997 // TODO: healing fx!
998 // TODO: armor healing?
999 return healed;
1000}

References CS(), entity(), FRAGS_SPECTATOR, game_stopped, IS_CLIENT, IS_DEAD, killcount, and STAT.

Referenced by bumblebee_pilot_frame(), M_Mage_Defend_Heal(), MUTATOR_HOOKFUNCTION(), trigger_heal_touch(), and W_Arc_Beam_Think().

◆ LogDeath()

void LogDeath ( string mode,
int deathtype,
entity killer,
entity killed )

Definition at line 102 of file damage.qc.

103{
104 string s;
106 return;
107 s = strcat(":kill:", mode);
108 s = strcat(s, ":", ftos(killer.playerid));
109 s = strcat(s, ":", ftos(killed.playerid));
110 s = strcat(s, ":type=", Deathtype_Name(deathtype));
111 s = strcat(s, ":items=");
112 s = AppendItemcodes(s, killer);
113 if(killed != killer)
114 {
115 s = strcat(s, ":victimitems=");
116 s = AppendItemcodes(s, killed);
117 }
118 GameLogEcho(s);
119}
string AppendItemcodes(string s, entity player)
Definition damage.qc:83
string Deathtype_Name(int deathtype)
Definition all.qc:3
void GameLogEcho(string s)
Definition gamelog.qc:15
bool autocvar_sv_eventlog
Definition gamelog.qh:3

References AppendItemcodes(), autocvar_sv_eventlog, Deathtype_Name(), entity(), ftos(), GameLogEcho(), and strcat().

Referenced by Obituary().

◆ Obituary()

void Obituary ( entity attacker,
entity inflictor,
entity targ,
int deathtype,
.entity weaponentity )

Definition at line 228 of file damage.qc.

229{
230 // Sanity check
231 if (!IS_PLAYER(targ)) { backtrace("Obituary called on non-player?!\n"); return; }
232
233 // Declarations
234 float notif_firstblood = false;
235 float kill_count_to_attacker, kill_count_to_target;
236 bool notif_anonymous = false;
237 string attacker_name = attacker.netname;
238
239 // Set final information for the death
240 targ.death_origin = targ.origin;
241 string deathlocation = (autocvar_notification_server_allows_location ? NearestLocation(targ.death_origin) : "");
242
243 // Abort now if a mutator requests it
244 if (MUTATOR_CALLHOOK(ClientObituary, inflictor, attacker, targ, deathtype, attacker.(weaponentity))) { CS(targ).killcount = 0; return; }
245 notif_anonymous = M_ARGV(5, bool);
246
247 // TODO: Replace "???" with a translatable "Anonymous player" string
248 // https://gitlab.com/xonotic/xonotic-data.pk3dir/-/issues/2839
249 if(notif_anonymous)
250 attacker_name = "???";
251
252 #ifdef NOTIFICATIONS_DEBUG
253 Debug_Notification(
254 sprintf(
255 "Obituary(%s, %s, %s, %s = %d);\n",
256 attacker_name,
257 inflictor.netname,
258 targ.netname,
259 Deathtype_Name(deathtype),
260 deathtype
261 )
262 );
263 #endif
264
265 // =======
266 // SUICIDE
267 // =======
268 if(targ == attacker)
269 {
270 if(DEATH_ISSPECIAL(deathtype))
271 {
272 if(deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
273 {
274 Obituary_SpecialDeath(targ, false, 0, deathtype, targ.netname, deathlocation, "", "", targ.team, 0, 0);
275 }
276 else
277 {
278 switch(DEATH_ENT(deathtype))
279 {
280 case DEATH_MIRRORDAMAGE:
281 {
282 Obituary_SpecialDeath(targ, false, 0, deathtype, targ.netname, deathlocation, "", "", CS(targ).killcount, 0, 0);
283 break;
284 }
285 case DEATH_HURTTRIGGER:
286 bool msg_from_ent = (inflictor && inflictor.message != "");
287 Obituary_SpecialDeath(targ, false, msg_from_ent, deathtype,
288 targ.netname,
289 (msg_from_ent ? inflictor.message : deathlocation),
290 (msg_from_ent ? deathlocation : ""),
291 "", CS(targ).killcount, 0, 0);
292 break;
293 default:
294 {
295 Obituary_SpecialDeath(targ, false, 0, deathtype, targ.netname, deathlocation, "", "", CS(targ).killcount, 0, 0);
296 break;
297 }
298 }
299 }
300 }
301 else if (!Obituary_WeaponDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0))
302 {
303 backtrace("SUICIDE: what the hell happened here?\n");
304 return;
305 }
306 LogDeath("suicide", deathtype, targ, targ);
307 if(deathtype != DEATH_AUTOTEAMCHANGE.m_id) // special case: don't negate frags if auto switched
308 GiveFrags(attacker, targ, -1, deathtype, weaponentity);
309 }
310
311 // ======
312 // MURDER
313 // ======
314 else if(IS_PLAYER(attacker))
315 {
316 if(SAME_TEAM(attacker, targ))
317 {
318 LogDeath("tk", deathtype, attacker, targ);
319 GiveFrags(attacker, targ, -1, deathtype, weaponentity);
320
321 CS(attacker).killcount = 0;
322
323 Send_Notification(NOTIF_ONE, attacker, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAG, targ.netname);
324 Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAGGED, attacker_name);
325 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(targ.team, INFO_DEATH_TEAMKILL), targ.netname, attacker_name, deathlocation, CS(targ).killcount);
326
327 // In this case, the death message will ALWAYS be "foo was betrayed by bar"
328 // No need for specific death/weapon messages...
329 }
330 else
331 {
332 LogDeath("frag", deathtype, attacker, targ);
333 GiveFrags(attacker, targ, 1, deathtype, weaponentity);
334
335 CS(attacker).taunt_soundtime = time + 1;
336 CS(attacker).killcount = CS(attacker).killcount + 1;
337
338 attacker.killsound += 1;
339
340 // TODO: improve SPREE_ITEM and KILL_SPREE_LIST
341 // these 2 macros are spread over multiple files
342 #define SPREE_ITEM(counta,countb,center,normal,gentle) \
343 case counta: \
344 Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
345 if (!warmup_stage) \
346 PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
347 break;
348
349 switch(CS(attacker).killcount)
350 {
352 default: break;
353 }
354 #undef SPREE_ITEM
355
357 {
359 notif_firstblood = true; // modify the current messages so that they too show firstblood information
362
363 // tell spree_inf and spree_cen that this is a first-blood and first-victim event
364 kill_count_to_attacker = -1;
365 kill_count_to_target = -2;
366 }
367 else
368 {
369 kill_count_to_attacker = CS(attacker).killcount;
370 kill_count_to_target = 0;
371 }
372
373 if(targ.istypefrag)
374 {
376 NOTIF_ONE,
377 attacker,
378 MSG_CHOICE,
379 CHOICE_TYPEFRAG,
380 targ.netname,
381 kill_count_to_attacker,
382 (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
383 );
385 NOTIF_ONE,
386 targ,
387 MSG_CHOICE,
388 CHOICE_TYPEFRAGGED,
389 attacker_name,
390 kill_count_to_target,
391 GetResource(attacker, RES_HEALTH),
392 GetResource(attacker, RES_ARMOR),
393 (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
394 );
395 }
396 else if(!frag_centermessage_override(attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target, attacker_name))
397 {
399 NOTIF_ONE,
400 attacker,
401 MSG_CHOICE,
402 CHOICE_FRAG,
403 targ.netname,
404 kill_count_to_attacker,
405 (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
406 );
408 NOTIF_ONE,
409 targ,
410 MSG_CHOICE,
411 CHOICE_FRAGGED,
412 attacker_name,
413 kill_count_to_target,
414 GetResource(attacker, RES_HEALTH),
415 GetResource(attacker, RES_ARMOR),
416 (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
417 );
418 }
419
420 int f3 = 0;
421 if(deathtype == DEATH_BUFF.m_id)
422 f3 = buff_FirstFromFlags(attacker).m_id;
423
424 if (!Obituary_WeaponDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker))
425 {
426 if (DEATH_ENT(deathtype) == DEATH_HURTTRIGGER)
427 {
428 bool msg_from_ent = (inflictor && inflictor.message2 != "");
429 Obituary_SpecialDeath(targ, true, msg_from_ent, deathtype,
430 targ.netname,
431 attacker_name,
432 (msg_from_ent ? inflictor.message2 : deathlocation),
433 (msg_from_ent ? deathlocation : ""),
434 CS(targ).killcount, kill_count_to_attacker, f3);
435 }
436 else
437 Obituary_SpecialDeath(targ, true, 0, deathtype, targ.netname, attacker_name, deathlocation, "", CS(targ).killcount, kill_count_to_attacker, f3);
438 }
439 }
440 }
441
442 // =============
443 // ACCIDENT/TRAP
444 // =============
445 else
446 {
447 switch(DEATH_ENT(deathtype))
448 {
449 // For now, we're just forcing HURTTRIGGER to behave as "DEATH_VOID" and giving it no special options...
450 // Later on you will only be able to make custom messages using DEATH_CUSTOM,
451 // and there will be a REAL DEATH_VOID implementation which mappers will use.
452 case DEATH_HURTTRIGGER:
453 {
454 bool msg_from_ent = (inflictor && inflictor.message != "");
455 Obituary_SpecialDeath(targ, false, msg_from_ent, deathtype,
456 targ.netname,
457 (msg_from_ent ? inflictor.message : deathlocation),
458 (msg_from_ent ? deathlocation : ""),
459 "",
460 CS(targ).killcount,
461 0,
462 0);
463 break;
464 }
465
466 case DEATH_CUSTOM:
467 {
468 Obituary_SpecialDeath(targ, false, 0, deathtype,
469 targ.netname,
470 ((strstrofs(deathmessage, "%", 0) < 0) ? strcat("%s ", deathmessage) : deathmessage),
471 deathlocation,
472 "",
473 CS(targ).killcount,
474 0,
475 0);
476 break;
477 }
478
479 default:
480 {
481 Obituary_SpecialDeath(targ, false, 0, deathtype, targ.netname, deathlocation, "", "", CS(targ).killcount, 0, 0);
482 break;
483 }
484 }
485
486 LogDeath("accident", deathtype, targ, targ);
487 GiveFrags(targ, targ, -1, deathtype, weaponentity);
488
489 if(GameRules_scoring_add(targ, SCORE, 0) == -5)
490 {
491 Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE);
492 if (!warmup_stage)
493 {
495 }
496 }
497 }
498
499 // reset target kill count
500 CS(targ).killcount = 0;
501}
void GiveFrags(entity attacker, entity targ, float f, int deathtype,.entity weaponentity)
Definition damage.qc:43
float Obituary_WeaponDeath(entity notif_target, float murder, int deathtype, string s1, string s2, string s3, float f1, float f2)
Definition damage.qc:169
void Obituary_SpecialDeath(entity notif_target, float murder, bool msg_from_ent, int deathtype, string s1, string s2, string s3, string s4, float f1, float f2, float f3)
Definition damage.qc:121
bool frag_centermessage_override(entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target, string attacker_name)
Definition damage.qc:216
void LogDeath(string mode, int deathtype, entity killer, entity killed)
Definition damage.qc:102
string deathmessage
Definition damage.qh:73
float checkrules_firstblood
Definition damage.qh:43
#define DEATH_ENT(t)
Definition all.qh:41
#define strstrofs
#define backtrace(msg)
Definition log.qh:99
#define KILL_SPREE_LIST
Definition all.qh:491
#define APP_TEAM_NUM(num, prefix)
Definition all.qh:84
float autocvar_notification_server_allows_location
Definition all.qh:318
const string PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD
const string PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM
const string PLAYERSTATS_ACHIEVEMENT_BOTLIKE
#define NULL
Definition post.qh:14
string NearestLocation(vector p)
Definition chat.qc:446
entity buff_FirstFromFlags(entity actor)
Definition sv_buffs.qc:293

References APP_TEAM_NUM, autocvar_notification_server_allows_location, backtrace, buff_FirstFromFlags(), checkrules_firstblood, CS(), DEATH_ENT, DEATH_ISSPECIAL, deathmessage, Deathtype_Name(), entity(), frag_centermessage_override(), GameRules_scoring_add, GetResource(), GiveFrags(), IS_BOT_CLIENT, IS_PLAYER, KILL_SPREE_LIST, killcount, LogDeath(), M_ARGV, MUTATOR_CALLHOOK, NearestLocation(), NULL, Obituary_SpecialDeath(), Obituary_WeaponDeath(), ping, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, PlayerStats_GameReport_Event_Player, RES_ARMOR, SAME_TEAM, Send_Notification(), strcat(), strstrofs, time, and warmup_stage.

Referenced by PlayerDamage().

◆ Obituary_SpecialDeath()

void Obituary_SpecialDeath ( entity notif_target,
float murder,
bool msg_from_ent,
int deathtype,
string s1,
string s2,
string s3,
string s4,
float f1,
float f2,
float f3 )

Definition at line 121 of file damage.qc.

128{
129 if(!DEATH_ISSPECIAL(deathtype))
130 {
131 backtrace("Obituary_SpecialDeath called without a special deathtype?\n");
132 return;
133 }
134
135 entity deathent = REGISTRY_GET(Deathtypes, deathtype - DT_FIRST);
136 if (!deathent)
137 {
138 backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n");
139 return;
140 }
141
142 if(g_cts && deathtype == DEATH_KILL.m_id)
143 return; // TODO: somehow put this in CTS gametype file!
144
145 Notification death_message = (murder) ? deathent.death_msgmurder : deathent.death_msgself;
146 if (msg_from_ent)
147 death_message = (murder) ? deathent.death_msg_ent_murder : deathent.death_msg_ent_self;
148 if(death_message)
149 {
151 NOTIF_ONE,
152 notif_target,
153 MSG_MULTI,
154 death_message,
155 s1, s2, s3, s4,
156 f1, f2, f3, 0
157 );
159 NOTIF_ALL_EXCEPT,
160 notif_target,
161 MSG_INFO,
162 death_message.nent_msginfo,
163 s1, s2, s3, s4,
164 f1, f2, f3, 0
165 );
166 }
167}
#define g_cts
Definition cts.qh:36
const int DT_FIRST
Definition all.qh:37
entity death_msg_ent_self
Definition all.qh:13
entity death_msgself
Definition all.qh:11
spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2 f1points f2
Definition all.inc:364
spree_inf s1 s2 s3loc s2 s1
Definition all.inc:281
spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2 f1points s1 s2
Definition all.inc:469
f1
Definition all.inc:561
void Send_Notification_WOCOVA(NOTIF broadcast, entity client, MSG net_type, Notification net_name, string s1, string s2, string s3, string s4, float f1, float f2, float f3, float f4)
Definition all.qc:1762
entity Notification
always last
Definition all.qh:81
#define REGISTRY_GET(id, i)
Definition registry.qh:43

References backtrace, DEATH_ISSPECIAL, DT_FIRST, entity(), f1, f2, g_cts, REGISTRY_GET, s1, s2, and Send_Notification_WOCOVA().

Referenced by Obituary().

◆ Obituary_WeaponDeath()

float Obituary_WeaponDeath ( entity notif_target,
float murder,
int deathtype,
string s1,
string s2,
string s3,
float f1,
float f2 )

Definition at line 169 of file damage.qc.

175{
176 Weapon death_weapon = DEATH_WEAPONOF(deathtype);
177 if (death_weapon == WEP_Null)
178 return false;
179
180 w_deathtype = deathtype;
181 Notification death_message = ((murder) ? death_weapon.wr_killmessage(death_weapon) : death_weapon.wr_suicidemessage(death_weapon));
182 w_deathtype = false;
183
184 if (death_message)
185 {
187 NOTIF_ONE,
188 notif_target,
189 MSG_MULTI,
190 death_message,
191 s1, s2, s3, "",
192 f1, f2, 0, 0
193 );
194 // send the info part to everyone
196 NOTIF_ALL_EXCEPT,
197 notif_target,
198 MSG_INFO,
199 death_message.nent_msginfo,
200 s1, s2, s3, "",
201 f1, f2, 0, 0
202 );
203 }
204 else
205 {
207 "Obituary_WeaponDeath(): ^1Deathtype ^7(%d)^1 has no notification for weapon %s!\n",
208 deathtype,
209 death_weapon.netname
210 );
211 }
212
213 return true;
214}
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:44
virtual void wr_suicidemessage()
(SERVER) notification number for suicide message (may inspect w_deathtype for details)
Definition weapon.qh:100
string netname
M: refname : reference name name.
Definition weapon.qh:79
virtual void wr_killmessage()
(SERVER) notification number for kill message (may inspect w_deathtype for details)
Definition weapon.qh:102
int w_deathtype
#define LOG_TRACEF(...)
Definition log.qh:77

References DEATH_WEAPONOF, entity(), f1, f2, LOG_TRACEF, Weapon::netname, s1, s2, Send_Notification_WOCOVA(), w_deathtype, Weapon::wr_killmessage(), and Weapon::wr_suicidemessage().

Referenced by Obituary().

◆ RadiusDamage()

float RadiusDamage ( entity inflictor,
entity attacker,
float coredamage,
float edgedamage,
float rad,
entity cantbe,
entity mustbe,
float forceintensity,
int deathtype,
.entity weaponentity,
entity directhitentity )

Definition at line 981 of file damage.qc.

982{
983 return RadiusDamageForSource(inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad,
984 cantbe, mustbe, false, forceintensity, '1 1 1', deathtype, weaponentity, directhitentity);
985}
float RadiusDamageForSource(entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, vector forcexyzscale, int deathtype,.entity weaponentity, entity directhitentity)
Definition damage.qc:741

References entity(), and RadiusDamageForSource().

Referenced by bumblebee_blowup(), CheatImpulse(), func_breakable_destroy(), M_Golem_Attack_Lightning_Explode(), M_Golem_Attack_Smash(), M_Mage_Attack_Push(), M_Mage_Attack_Spike_Explode(), M_Spider_Attack_Web_Explode(), M_Wyvern_Attack_Fireball_Explode(), nade_normal_boom(), PlayerTouchExplode(), racer_blowup(), raptor_blowup(), raptor_bomblet_boom(), spiderbot_blowup(), turret_flac_projectile_think_explode(), turret_projectile_explode(), vehicles_projectile_explode(), W_Arc_Bolt_Explode(), W_Arc_Bolt_Touch(), W_Crylink_LinkExplode(), W_Crylink_LinkJoinEffect_Think(), W_Crylink_Touch(), W_Devastator_DoRemoteExplode(), W_Electro_Explode(), W_Electro_ExplodeCombo(), W_Electro_ExplodeComboThink(), W_Fireball_Explode(), W_Hagar_Explode(), W_Hagar_Explode2(), W_HLAC_Touch(), W_Hook_ExplodeThink(), W_MineLayer_DoRemoteExplode(), W_MineLayer_Explode(), W_Mortar_Grenade_Explode(), W_Mortar_Grenade_Explode2(), W_OverkillRocketPropelledChainsaw_Explode(), W_RocketMinsta_Explosion(), W_RocketMinsta_Laser_Damage(), W_Seeker_Flac_Explode(), W_Seeker_Missile_Explode(), W_Tuba_NoteOn(), and walker_rocket_explode().

◆ RadiusDamageForSource()

float RadiusDamageForSource ( entity inflictor,
vector inflictororigin,
vector inflictorvelocity,
entity attacker,
float coredamage,
float edgedamage,
float rad,
entity cantbe,
entity mustbe,
float inflictorselfdamage,
float forceintensity,
vector forcexyzscale,
int deathtype,
.entity weaponentity,
entity directhitentity )

Definition at line 741 of file damage.qc.

743{
744 entity targ;
745 vector force;
746 float total_damage_to_creatures;
747 entity next;
748 float tfloordmg;
749 float tfloorforce;
750
751 float stat_damagedone;
752
754 {
755 backtrace("RadiusDamage called recursively! Expect stuff to go HORRIBLY wrong.");
756 return 0;
757 }
758
759 if (rad < 0) rad = 0;
760
762
764 tfloorforce = autocvar_g_throughfloor_force;
765
766 total_damage_to_creatures = 0;
767
768 if(!(deathtype & (HITTYPE_SOUND | HITTYPE_SPAM))) // do not send bandwidth-hogging radial spam attacks
769 {
770 force = inflictorvelocity;
771 if(force == '0 0 0')
772 force = '0 0 -1';
773 else
774 force = normalize(force);
775 if(forceintensity >= 0)
776 Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
777 else
778 Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
779 }
780
781 stat_damagedone = 0;
782
783 targ = WarpZone_FindRadius (inflictororigin, rad + MAX_DAMAGEEXTRARADIUS, false);
784 while (targ)
785 {
786 next = targ.chain;
787 if ((targ != inflictor) || inflictorselfdamage)
788 if (((cantbe != targ) && !mustbe) || (mustbe == targ))
789 if (targ.takedamage)
790 {
791 // calculate distance from nearest point on target to nearest point on inflictor
792 // instead of origin to ensure full damage on impacts
793
794 vector nearest = targ.WarpZone_findradius_nearest;
795
796 // optimize code by getting inflictororigin_wz from WarpZone_FindRadius calculations instead of
797 //vector inflictororigin_wz = WarpZone_TransformOrigin(targ, inflictororigin);
798
799 vector inflictororigin_wz = targ.WarpZone_findradius_nearest + targ.WarpZone_findradius_dist;
800 vector inflictornearest = NearestPointOnBoundingBox(
801 inflictororigin_wz + inflictor.mins, inflictororigin_wz + inflictor.maxs, nearest);
802 vector diff = inflictornearest - nearest;
803
804 // round up a little on the damage to ensure full damage on impacts
805 // and turn the distance into a fraction of the radius
806 float dist = max(0, vlen(diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS));
807 if (dist <= rad)
808 {
809 float f = (rad > 0) ? 1 - (dist / rad) : 1;
810 // at this point f can't be < 0 or > 1
811 float finaldmg = coredamage * f + edgedamage * (1 - f);
812 if (finaldmg > 0)
813 {
814 float a;
815 float c;
816 vector hitloc;
817
818 // if it's a player, use the view origin as reference
819 vector center = CENTER_OR_VIEWOFS(targ);
820
822 {
823 if (targ != attacker)
824 {
825 // always use target's bbox centerpoint
826 center = targ.origin + ((targ.mins + targ.maxs) * 0.5);
827 }
828 else // targ == attacker
829 {
830 #if 0
831 // code stolen from W_SetupShot_Dir_ProjectileSize_Range()
832 vector md = targ.(weaponentity).movedir;
833 vector dv = v_right * -md.y + v_up * md.z;
834 vector mi = '0 0 0', ma = '0 0 0';
835
836 if(IS_CLIENT(targ)) // no antilag for non-clients!
837 {
838 if(CS(targ).antilag_debug)
839 tracebox_antilag(targ, center, mi, ma, center + dv, MOVE_NORMAL, targ, CS(targ).antilag_debug);
840 else
841 tracebox_antilag(targ, center, mi, ma, center + dv, MOVE_NORMAL, targ, ANTILAG_LATENCY(targ));
842 }
843 else
844 tracebox(center, mi, ma, center + dv, MOVE_NORMAL, targ);
845
846 center.z = trace_endpos.z;
847 #else
848 // very cheap way but it skips move into solid checks which is fine most of the time for now AFAIK
849 // this should only really be an issue with some rare edge cases where
850 // shot origin was prevented from going into a ceiling but it still explodes at the ceiling
851 // shot origin wasn't raised as high as possible and the shooter gets upwards knockback
852 // TL;DR: no bugs if vertical shot origin is always within player bbox
853 center.z = center.z + targ.(weaponentity).movedir.z;
854 #endif
855 }
856 }
857
858 /* debug prints
859 print(sprintf("origin vec %v\n", targ.origin));
860 print(sprintf("movedir vec %v\n", targ.(weaponentity).movedir));
861 print(sprintf("old def vec %v\n", CENTER_OR_VIEWOFS(targ)));
862 print(sprintf("origin+vofs %v\n", targ.origin + targ.view_ofs));
863 print(sprintf("bbox center %v\n", (targ.origin + ((targ.mins + targ.maxs) * 0.5))));
864 print(sprintf("center vec %v\n", center));
865 print(sprintf("shotorg vec %v\n", w_shotorg));
866 print("\n");
867 */
868
869 force = normalize(center - inflictororigin_wz);
870 force = force * (finaldmg / max(coredamage, edgedamage)) * forceintensity;
871 hitloc = nearest;
872
873 // apply special force scalings
874 if(forcexyzscale.x)
875 force.x *= forcexyzscale.x;
876 if(forcexyzscale.y)
877 force.y *= forcexyzscale.y;
878 if(forcexyzscale.z)
879 force.z *= forcexyzscale.z;
880
881 if(targ != directhitentity)
882 {
883 float hits;
884 float total;
885 float hitratio;
886 float mininv_f, mininv_d;
887
888 // test line of sight to multiple positions on box,
889 // and do damage if any of them hit
890 hits = 0;
891
892 // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
893 // so for a given max stddev:
894 // n = (1 / (2 * max stddev of hitratio))^2
895
896 mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
897 mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
898
900 LOG_INFOF("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f);
901
902
903 total = 0.25 * (max(mininv_f, mininv_d) ** 2);
904
906 LOG_INFOF(" steps=%f", total);
907
908
909 if (IS_PLAYER(targ))
911 else
913
915 LOG_INFOF(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total)));
916
917 for(c = 0; c < total; ++c)
918 {
919 //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
920 WarpZone_TraceLine(inflictororigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
921 if (trace_fraction == 1 || trace_ent == targ)
922 {
923 ++hits;
924 if (hits > 1)
925 hitloc = hitloc + nearest;
926 else
927 hitloc = nearest;
928 }
929 nearest.x = targ.origin.x + targ.mins.x + random() * targ.size.x;
930 nearest.y = targ.origin.y + targ.mins.y + random() * targ.size.y;
931 nearest.z = targ.origin.z + targ.mins.z + random() * targ.size.z;
932 }
933
934 nearest = hitloc * (1 / max(1, hits));
935 hitratio = (hits / total);
936 a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
937 finaldmg = finaldmg * a;
938 a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
939 force = force * a;
940
942 LOG_INFOF(" D=%f F=%f", finaldmg, vlen(force));
943
944 /*if (targ == attacker)
945 {
946 print("hits ", ftos(hits), " / ", ftos(total));
947 print(" finaldmg ", ftos(finaldmg), " force ", ftos(vlen(force)));
948 print(" (", vtos(force), ") (", ftos(a), ")\n");
949 }*/
950 }
951
952 if(finaldmg || force)
953 {
954 if(targ.iscreature)
955 {
956 total_damage_to_creatures += finaldmg;
957
958 if(accuracy_isgooddamage(attacker, targ))
959 stat_damagedone += finaldmg;
960 }
961
962 if(targ == directhitentity || DEATH_ISSPECIAL(deathtype))
963 Damage(targ, inflictor, attacker, finaldmg, deathtype, weaponentity, nearest, force);
964 else
965 Damage(targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, weaponentity, nearest, force);
966 }
967 }
968 }
969 }
970 targ = next;
971 }
972
974
975 if(!DEATH_ISSPECIAL(deathtype))
976 accuracy_add(attacker, DEATH_WEAPONOF(deathtype), 0, min(max(coredamage, edgedamage), stat_damagedone), 0); // add to hit
977
978 return total_damage_to_creatures;
979}
void tracebox_antilag(entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
Definition antilag.qc:210
float antilag_debug
Definition antilag.qc:19
#define ANTILAG_LATENCY(e)
Definition antilag.qh:19
vector v_up
const float MOVE_NOMONSTERS
entity trace_ent
const float MOVE_NORMAL
vector v_right
vector trace_endpos
float trace_fraction
int autocvar_g_player_damageplayercenter
Definition damage.qh:106
float autocvar_g_throughfloor_force_max_stddev
Definition damage.qh:9
float autocvar_g_throughfloor_damage_max_stddev
Definition damage.qh:8
float autocvar_g_throughfloor_force
Definition damage.qh:7
float RadiusDamage_running
Definition damage.qh:111
bool autocvar_g_throughfloor_debug
Definition damage.qh:5
float autocvar_g_throughfloor_min_steps_other
Definition damage.qh:12
float autocvar_g_throughfloor_min_steps_player
Definition damage.qh:10
float autocvar_g_throughfloor_damage
Definition damage.qh:6
float autocvar_g_throughfloor_max_steps_other
Definition damage.qh:13
float autocvar_g_throughfloor_max_steps_player
Definition damage.qh:11
void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner)
const float MAX_DAMAGEEXTRARADIUS
const float MIN_DAMAGEEXTRARADIUS
const int HITTYPE_SPLASH
Definition all.qh:30
next
Definition all.qh:93
entity WarpZone_FindRadius(vector org, float rad, bool needlineofsight)
Definition common.qc:686
void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
Definition common.qc:348
vector WarpZone_UnTransformOrigin(entity wz, vector v)
Definition common.qc:545
#define LOG_INFOF(...)
Definition log.qh:66
vector movedir
Definition viewloc.qh:18
float ceil(float f)
float bound(float min, float value, float max)
float random(void)
float sqrt(float f)
#define CENTER_OR_VIEWOFS(ent)
Definition utils.qh:29
ERASEABLE vector NearestPointOnBoundingBox(vector mi, vector ma, vector org)
Definition vector.qh:191

References accuracy_add(), accuracy_isgooddamage(), antilag_debug, ANTILAG_LATENCY, autocvar_g_player_damageplayercenter, autocvar_g_throughfloor_damage, autocvar_g_throughfloor_damage_max_stddev, autocvar_g_throughfloor_debug, autocvar_g_throughfloor_force, autocvar_g_throughfloor_force_max_stddev, autocvar_g_throughfloor_max_steps_other, autocvar_g_throughfloor_max_steps_player, autocvar_g_throughfloor_min_steps_other, autocvar_g_throughfloor_min_steps_player, backtrace, bound(), ceil(), CENTER_OR_VIEWOFS, CS(), Damage(), Damage_DamageInfo(), DEATH_ISSPECIAL, DEATH_WEAPONOF, entity(), HITTYPE_SOUND, HITTYPE_SPAM, HITTYPE_SPLASH, IS_CLIENT, IS_PLAYER, LOG_INFOF, max(), MAX_DAMAGEEXTRARADIUS, min(), MIN_DAMAGEEXTRARADIUS, MOVE_NOMONSTERS, MOVE_NORMAL, movedir, NearestPointOnBoundingBox(), next, normalize(), RadiusDamage_running, random(), sqrt(), trace_endpos, trace_ent, trace_fraction, tracebox_antilag(), v_right, v_up, vector, vlen(), WarpZone_FindRadius(), WarpZone_TraceLine(), and WarpZone_UnTransformOrigin().

Referenced by RadiusDamage(), W_Blaster_Touch(), and W_Devastator_Explode().