Xonotic
damage.qc
Go to the documentation of this file.
1 #include "damage.qh"
2 
3 #include <common/constants.qh>
5 #include <common/effects/all.qh>
8 #include <common/items/_mod.qh>
18 #include <common/physics/player.qh>
19 #include <common/playerstats.qh>
21 #include <common/state.qh>
22 #include <common/teams.qh>
23 #include <common/util.qh>
24 #include <common/vehicles/all.qh>
25 #include <common/weapons/_all.qh>
27 #include <lib/warpzone/common.qh>
28 #include <server/bot/api.qh>
29 #include <server/client.qh>
30 #include <server/gamelog.qh>
31 #include <server/hook.qh>
32 #include <server/items/items.qh>
33 #include <server/main.qh>
34 #include <server/mutators/_mod.qh>
35 #include <server/scores.qh>
36 #include <server/spawnpoints.qh>
37 #include <server/teamplay.qh>
42 #include <server/world.qh>
43 
44 void UpdateFrags(entity player, int f)
45 {
46  GameRules_scoring_add_team(player, SCORE, f);
47 }
48 
49 void GiveFrags(entity attacker, entity targ, float f, int deathtype, .entity weaponentity)
50 {
51  // TODO route through PlayerScores instead
52  if(game_stopped) return;
53 
54  if(f < 0)
55  {
56  if(targ == attacker)
57  {
58  // suicide
59  GameRules_scoring_add(attacker, SUICIDES, 1);
60  }
61  else
62  {
63  // teamkill
64  GameRules_scoring_add(attacker, TEAMKILLS, 1);
65  }
66  }
67  else
68  {
69  // regular frag
70  GameRules_scoring_add(attacker, KILLS, 1);
71  if(!warmup_stage && targ.playerid)
72  PlayerStats_GameReport_Event_Player(attacker, sprintf("kills-%d", targ.playerid), 1);
73  }
74 
75  GameRules_scoring_add(targ, DEATHS, 1);
76 
77  // FIXME fix the mess this is (we have REAL points now!)
78  if(MUTATOR_CALLHOOK(GiveFragsForKill, attacker, targ, f, deathtype, attacker.(weaponentity)))
79  f = M_ARGV(2, float);
80 
81  attacker.totalfrags += f;
82 
83  if(f)
84  UpdateFrags(attacker, f);
85 }
86 
87 string AppendItemcodes(string s, entity player)
88 {
89  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
90  {
91  .entity weaponentity = weaponentities[slot];
92  int w = player.(weaponentity).m_weapon.m_id;
93  if(w == 0)
94  w = player.(weaponentity).cnt; // previous weapon
95  if(w != 0 || slot == 0)
96  s = strcat(s, ftos(w));
97  }
98  if(PHYS_INPUT_BUTTON_CHAT(player))
99  s = strcat(s, "T");
100  // TODO: include these codes as a flag on the item itself
101  MUTATOR_CALLHOOK(LogDeath_AppendItemCodes, player, s);
102  s = M_ARGV(1, string);
103  return s;
104 }
105 
106 void LogDeath(string mode, int deathtype, entity killer, entity killed)
107 {
108  string s;
110  return;
111  s = strcat(":kill:", mode);
112  s = strcat(s, ":", ftos(killer.playerid));
113  s = strcat(s, ":", ftos(killed.playerid));
114  s = strcat(s, ":type=", Deathtype_Name(deathtype));
115  s = strcat(s, ":items=");
116  s = AppendItemcodes(s, killer);
117  if(killed != killer)
118  {
119  s = strcat(s, ":victimitems=");
120  s = AppendItemcodes(s, killed);
121  }
122  GameLogEcho(s);
123 }
124 
126  entity notif_target,
127  float murder,
128  int deathtype,
129  string s1, string s2, string s3,
130  float f1, float f2, float f3)
131 {
132  if(!DEATH_ISSPECIAL(deathtype))
133  {
134  backtrace("Obituary_SpecialDeath called without a special deathtype?\n");
135  return;
136  }
137 
138  entity deathent = REGISTRY_GET(Deathtypes, deathtype - DT_FIRST);
139  if (!deathent)
140  {
141  backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n");
142  return;
143  }
144 
145  if(g_cts && deathtype == DEATH_KILL.m_id)
146  return; // TODO: somehow put this in CTS gamemode file!
147 
148  Notification death_message = (murder) ? deathent.death_msgmurder : deathent.death_msgself;
149  if(death_message)
150  {
151  Send_Notification_WOCOVA(
152  NOTIF_ONE,
153  notif_target,
154  MSG_MULTI,
155  death_message,
156  s1, s2, s3, "",
157  f1, f2, f3, 0
158  );
159  Send_Notification_WOCOVA(
160  NOTIF_ALL_EXCEPT,
161  notif_target,
162  MSG_INFO,
163  death_message.nent_msginfo,
164  s1, s2, s3, "",
165  f1, f2, f3, 0
166  );
167  }
168 }
169 
171  entity notif_target,
172  float murder,
173  int deathtype,
174  string s1, string s2, string s3,
175  float f1, float f2)
176 {
177  Weapon death_weapon = DEATH_WEAPONOF(deathtype);
178  if (death_weapon == WEP_Null)
179  return false;
180 
181  w_deathtype = deathtype;
182  Notification death_message = ((murder) ? death_weapon.wr_killmessage(death_weapon) : death_weapon.wr_suicidemessage(death_weapon));
183  w_deathtype = false;
184 
185  if (death_message)
186  {
187  Send_Notification_WOCOVA(
188  NOTIF_ONE,
189  notif_target,
190  MSG_MULTI,
191  death_message,
192  s1, s2, s3, "",
193  f1, f2, 0, 0
194  );
195  // send the info part to everyone
196  Send_Notification_WOCOVA(
197  NOTIF_ALL_EXCEPT,
198  notif_target,
199  MSG_INFO,
200  death_message.nent_msginfo,
201  s1, s2, s3, "",
202  f1, f2, 0, 0
203  );
204  }
205  else
206  {
207  LOG_TRACEF(
208  "Obituary_WeaponDeath(): ^1Deathtype ^7(%d)^1 has no notification for weapon %s!\n",
209  deathtype,
210  death_weapon.netname
211  );
212  }
213 
214  return true;
215 }
216 
217 bool frag_centermessage_override(entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target, string attacker_name)
218 {
219  if(deathtype == DEATH_FIRE.m_id)
220  {
221  Send_Notification(NOTIF_ONE, attacker, MSG_CHOICE, CHOICE_FRAG_FIRE, targ.netname, kill_count_to_attacker, (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping));
222  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));
223  return true;
224  }
225 
226  return MUTATOR_CALLHOOK(FragCenterMessage, attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target);
227 }
228 
229 void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .entity weaponentity)
230 {
231  // Sanity check
232  if (!IS_PLAYER(targ)) { backtrace("Obituary called on non-player?!\n"); return; }
233 
234  // Declarations
235  float notif_firstblood = false;
236  float kill_count_to_attacker, kill_count_to_target;
237  bool notif_anonymous = false;
238  string attacker_name = attacker.netname;
239 
240  // Set final information for the death
241  targ.death_origin = targ.origin;
242  string deathlocation = (autocvar_notification_server_allows_location ? NearestLocation(targ.death_origin) : "");
243 
244  // Abort now if a mutator requests it
245  if (MUTATOR_CALLHOOK(ClientObituary, inflictor, attacker, targ, deathtype, attacker.(weaponentity))) { CS(targ).killcount = 0; return; }
246  notif_anonymous = M_ARGV(5, bool);
247 
248  if(notif_anonymous)
249  attacker_name = "Anonymous player";
250 
251  #ifdef NOTIFICATIONS_DEBUG
252  Debug_Notification(
253  sprintf(
254  "Obituary(%s, %s, %s, %s = %d);\n",
255  attacker_name,
256  inflictor.netname,
257  targ.netname,
258  Deathtype_Name(deathtype),
259  deathtype
260  )
261  );
262  #endif
263 
264  // =======
265  // SUICIDE
266  // =======
267  if(targ == attacker)
268  {
269  if(DEATH_ISSPECIAL(deathtype))
270  {
271  if(deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
272  {
273  Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", targ.team, 0, 0);
274  }
275  else
276  {
277  switch(DEATH_ENT(deathtype))
278  {
279  case DEATH_MIRRORDAMAGE:
280  {
281  Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
282  break;
283  }
284  case DEATH_HURTTRIGGER:
285  Obituary_SpecialDeath(targ, false, deathtype, targ.netname, inflictor.message, deathlocation, CS(targ).killcount, 0, 0);
286  break;
287  default:
288  {
289  Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
290  break;
291  }
292  }
293  }
294  }
295  else if (!Obituary_WeaponDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0))
296  {
297  backtrace("SUICIDE: what the hell happened here?\n");
298  return;
299  }
300  LogDeath("suicide", deathtype, targ, targ);
301  if(deathtype != DEATH_AUTOTEAMCHANGE.m_id) // special case: don't negate frags if auto switched
302  GiveFrags(attacker, targ, -1, deathtype, weaponentity);
303  }
304 
305  // ======
306  // MURDER
307  // ======
308  else if(IS_PLAYER(attacker))
309  {
310  if(SAME_TEAM(attacker, targ))
311  {
312  LogDeath("tk", deathtype, attacker, targ);
313  GiveFrags(attacker, targ, -1, deathtype, weaponentity);
314 
315  CS(attacker).killcount = 0;
316 
317  Send_Notification(NOTIF_ONE, attacker, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAG, targ.netname);
318  Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAGGED, attacker_name);
319  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(targ.team, INFO_DEATH_TEAMKILL), targ.netname, attacker_name, deathlocation, CS(targ).killcount);
320 
321  // In this case, the death message will ALWAYS be "foo was betrayed by bar"
322  // No need for specific death/weapon messages...
323  }
324  else
325  {
326  LogDeath("frag", deathtype, attacker, targ);
327  GiveFrags(attacker, targ, 1, deathtype, weaponentity);
328 
329  CS(attacker).taunt_soundtime = time + 1;
330  CS(attacker).killcount = CS(attacker).killcount + 1;
331 
332  attacker.killsound += 1;
333 
334  // TODO: improve SPREE_ITEM and KILL_SPREE_LIST
335  // these 2 macros are spread over multiple files
336  #define SPREE_ITEM(counta,countb,center,normal,gentle) \
337  case counta: \
338  Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
339  if (!warmup_stage) \
340  PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
341  break;
342 
343  switch(CS(attacker).killcount)
344  {
346  default: break;
347  }
348  #undef SPREE_ITEM
349 
351  {
352  checkrules_firstblood = true;
353  notif_firstblood = true; // modify the current messages so that they too show firstblood information
354  PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
355  PlayerStats_GameReport_Event_Player(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
356 
357  // tell spree_inf and spree_cen that this is a first-blood and first-victim event
358  kill_count_to_attacker = -1;
359  kill_count_to_target = -2;
360  }
361  else
362  {
363  kill_count_to_attacker = CS(attacker).killcount;
364  kill_count_to_target = 0;
365  }
366 
367  if(targ.istypefrag)
368  {
369  Send_Notification(
370  NOTIF_ONE,
371  attacker,
372  MSG_CHOICE,
373  CHOICE_TYPEFRAG,
374  targ.netname,
375  kill_count_to_attacker,
376  (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
377  );
378  Send_Notification(
379  NOTIF_ONE,
380  targ,
381  MSG_CHOICE,
382  CHOICE_TYPEFRAGGED,
383  attacker_name,
384  kill_count_to_target,
385  GetResource(attacker, RES_HEALTH),
386  GetResource(attacker, RES_ARMOR),
387  (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
388  );
389  }
390  else if(!frag_centermessage_override(attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target, attacker_name))
391  {
392  Send_Notification(
393  NOTIF_ONE,
394  attacker,
395  MSG_CHOICE,
396  CHOICE_FRAG,
397  targ.netname,
398  kill_count_to_attacker,
399  (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
400  );
401  Send_Notification(
402  NOTIF_ONE,
403  targ,
404  MSG_CHOICE,
405  CHOICE_FRAGGED,
406  attacker_name,
407  kill_count_to_target,
408  GetResource(attacker, RES_HEALTH),
409  GetResource(attacker, RES_ARMOR),
410  (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
411  );
412  }
413 
414  int f3 = 0;
415  if(deathtype == DEATH_BUFF.m_id)
416  f3 = buff_FirstFromFlags(attacker).m_id;
417 
418  if (!Obituary_WeaponDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker))
419  Obituary_SpecialDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker, f3);
420  }
421  }
422 
423  // =============
424  // ACCIDENT/TRAP
425  // =============
426  else
427  {
428  switch(DEATH_ENT(deathtype))
429  {
430  // For now, we're just forcing HURTTRIGGER to behave as "DEATH_VOID" and giving it no special options...
431  // Later on you will only be able to make custom messages using DEATH_CUSTOM,
432  // and there will be a REAL DEATH_VOID implementation which mappers will use.
433  case DEATH_HURTTRIGGER:
434  {
435  Obituary_SpecialDeath(targ, false, deathtype,
436  targ.netname,
437  inflictor.message,
438  deathlocation,
439  CS(targ).killcount,
440  0,
441  0);
442  break;
443  }
444 
445  case DEATH_CUSTOM:
446  {
447  Obituary_SpecialDeath(targ, false, deathtype,
448  targ.netname,
449  ((strstrofs(deathmessage, "%", 0) < 0) ? strcat("%s ", deathmessage) : deathmessage),
450  deathlocation,
451  CS(targ).killcount,
452  0,
453  0);
454  break;
455  }
456 
457  default:
458  {
459  Obituary_SpecialDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0, 0);
460  break;
461  }
462  }
463 
464  LogDeath("accident", deathtype, targ, targ);
465  GiveFrags(targ, targ, -1, deathtype, weaponentity);
466 
467  if(GameRules_scoring_add(targ, SCORE, 0) == -5)
468  {
469  Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE);
470  if (!warmup_stage)
471  {
472  PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1);
473  }
474  }
475  }
476 
477  // reset target kill count
478  CS(targ).killcount = 0;
479 }
480 
481 void Ice_Think(entity this)
482 {
483  if(!STAT(FROZEN, this.owner) || this.owner.iceblock != this)
484  {
485  delete(this);
486  return;
487  }
488  vector ice_org = this.owner.origin - '0 0 16';
489  if (this.origin != ice_org)
490  setorigin(this, ice_org);
491  this.nextthink = time;
492 }
493 
494 void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint)
495 {
496  if(!IS_PLAYER(targ) && !IS_MONSTER(targ)) // TODO: only specified entities can be freezed
497  return;
498 
499  if(STAT(FROZEN, targ))
500  return;
501 
502  float targ_maxhealth = ((IS_MONSTER(targ)) ? targ.max_health : start_health);
503 
504  STAT(FROZEN, targ) = frozen_type;
505  STAT(REVIVE_PROGRESS, targ) = ((frozen_type == FROZEN_TEMP_DYING) ? 1 : 0);
506  SetResource(targ, RES_HEALTH, ((frozen_type == FROZEN_TEMP_DYING) ? targ_maxhealth : 1));
507  targ.revive_speed = revivespeed;
508  if(targ.bot_attack)
509  IL_REMOVE(g_bot_targets, targ);
510  targ.bot_attack = false;
511  targ.freeze_time = time;
512 
513  entity ice = new(ice);
514  ice.owner = targ;
515  ice.scale = targ.scale;
516  // set_movetype(ice, MOVETYPE_FOLLOW) would rotate the ice model with the player
517  setthink(ice, Ice_Think);
518  ice.nextthink = time;
519  ice.frame = floor(random() * 21); // ice model has 20 different looking frames
520  setmodel(ice, MDL_ICE);
521  ice.alpha = 1;
522  ice.colormod = Team_ColorRGB(targ.team);
523  ice.glowmod = ice.colormod;
524  targ.iceblock = ice;
525  targ.revival_time = 0;
526 
527  Ice_Think(ice);
528 
529  RemoveGrapplingHooks(targ);
530 
532  {
533  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
534  {
535  .entity weaponentity = weaponentities[slot];
536  if(it.(weaponentity).hook.aiment == targ)
537  RemoveHook(it.(weaponentity).hook);
538  }
539  });
540 
541  // add waypoint
542  if(MUTATOR_CALLHOOK(Freeze, targ, revivespeed, frozen_type) || show_waypoint)
543  WaypointSprite_Spawn(WP_Frozen, 0, 0, targ, '0 0 64', NULL, targ.team, targ, waypointsprite_attached, true, RADARICON_WAYPOINT);
544 }
545 
546 void Unfreeze(entity targ, bool reset_health)
547 {
548  if(!STAT(FROZEN, targ))
549  return;
550 
551  if (reset_health && STAT(FROZEN, targ) != FROZEN_TEMP_DYING)
552  SetResource(targ, RES_HEALTH, ((IS_PLAYER(targ)) ? start_health : targ.max_health));
553 
554  targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
555 
556  STAT(FROZEN, targ) = 0;
557  STAT(REVIVE_PROGRESS, targ) = 0;
558  targ.revival_time = time;
559  if(!targ.bot_attack)
560  IL_PUSH(g_bot_targets, targ);
561  targ.bot_attack = true;
562 
563  WaypointSprite_Kill(targ.waypointsprite_attached);
564 
566  {
567  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
568  {
569  .entity weaponentity = weaponentities[slot];
570  if(it.(weaponentity).hook.aiment == targ)
571  RemoveHook(it.(weaponentity).hook);
572  }
573  });
574 
575  // remove the ice block
576  if(targ.iceblock)
577  delete(targ.iceblock);
578  targ.iceblock = NULL;
579 
580  MUTATOR_CALLHOOK(Unfreeze, targ);
581 }
582 
583 void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
584 {
585  float complainteamdamage = 0;
586  float mirrordamage = 0;
587  float mirrorforce = 0;
588 
589  if (game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR))
590  return;
591 
592  entity attacker_save = attacker;
593 
594  // special rule: gravity bombs and sound-based attacks do not affect team mates (other than for disconnecting the hook)
595  if(DEATH_ISWEAPON(deathtype, WEP_HOOK) || (deathtype & HITTYPE_SOUND))
596  {
597  if(IS_PLAYER(targ) && SAME_TEAM(targ, attacker))
598  {
599  return;
600  }
601  }
602 
603  if(deathtype == DEATH_KILL.m_id || deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
604  {
605  // exit the vehicle before killing (fixes a crash)
606  if(IS_PLAYER(targ) && targ.vehicle)
607  vehicles_exit(targ.vehicle, VHEF_RELEASE);
608 
609  // These are ALWAYS lethal
610  // No damage modification here
611  // Instead, prepare the victim for his death...
612  if(deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
613  {
614  SetResourceExplicit(targ, RES_ARMOR, 0);
615  SetResourceExplicit(targ, RES_HEALTH, 0.9); // this is < 1
616  }
617  StatusEffects_remove(STATUSEFFECT_SpawnShield, targ, STATUSEFFECT_REMOVE_CLEAR);
618  targ.flags -= targ.flags & FL_GODMODE;
619  damage = 100000;
620  }
621  else if(deathtype == DEATH_MIRRORDAMAGE.m_id || deathtype == DEATH_NOAMMO.m_id)
622  {
623  // no processing
624  }
625  else
626  {
627  // nullify damage if teamplay is on
628  if(deathtype != DEATH_TELEFRAG.m_id)
629  if(IS_PLAYER(attacker))
630  {
631  if(IS_PLAYER(targ) && targ != attacker && (IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(targ)))
632  {
633  damage = 0;
634  force = '0 0 0';
635  }
636  else if(!STAT(FROZEN, targ) && SAME_TEAM(attacker, targ))
637  {
638  if(autocvar_teamplay_mode == 1)
639  damage = 0;
640  else if(attacker != targ)
641  {
642  if(autocvar_teamplay_mode == 2)
643  {
644  if(IS_PLAYER(targ) && !IS_DEAD(targ))
645  {
646  attacker.dmg_team = attacker.dmg_team + damage;
647  complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
648  }
649  }
650  else if(autocvar_teamplay_mode == 3)
651  damage = 0;
652  else if(autocvar_teamplay_mode == 4)
653  {
654  if(IS_PLAYER(targ) && !IS_DEAD(targ))
655  {
656  attacker.dmg_team = attacker.dmg_team + damage;
657  complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
658  if(complainteamdamage > 0)
659  mirrordamage = autocvar_g_mirrordamage * complainteamdamage;
660  mirrorforce = autocvar_g_mirrordamage * vlen(force);
661  damage = autocvar_g_friendlyfire * damage;
662  // mirrordamage will be used LATER
663 
665  {
666  vector v = healtharmor_applydamage(GetResource(attacker, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, mirrordamage);
667  attacker.dmg_take += v.x;
668  attacker.dmg_save += v.y;
669  attacker.dmg_inflictor = inflictor;
670  mirrordamage = v.z;
671  mirrorforce = 0;
672  }
673 
675  {
676  vector v = healtharmor_applydamage(GetResource(targ, RES_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage);
677  targ.dmg_take += v.x;
678  targ.dmg_save += v.y;
679  targ.dmg_inflictor = inflictor;
680  damage = 0;
682  force = '0 0 0';
683  }
684  }
685  else if(!targ.canteamdamage)
686  damage = 0;
687  }
688  }
689  }
690  }
691 
692  if (!DEATH_ISSPECIAL(deathtype))
693  {
695  mirrordamage *= autocvar_g_weapondamagefactor;
696  complainteamdamage *= autocvar_g_weapondamagefactor;
697  force = force * autocvar_g_weaponforcefactor;
698  mirrorforce *= autocvar_g_weaponforcefactor;
699  }
700 
701  // should this be changed at all? If so, in what way?
702  MUTATOR_CALLHOOK(Damage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force, attacker.(weaponentity));
703  damage = M_ARGV(4, float);
704  mirrordamage = M_ARGV(5, float);
705  force = M_ARGV(6, vector);
706 
707  if(IS_PLAYER(targ) && damage > 0 && attacker)
708  {
709  for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
710  {
711  .entity went = weaponentities[slot];
712  if(targ.(went).hook && targ.(went).hook.aiment == attacker)
713  RemoveHook(targ.(went).hook);
714  }
715  }
716 
717  if(STAT(FROZEN, targ) && !ITEM_DAMAGE_NEEDKILL(deathtype)
718  && deathtype != DEATH_TEAMCHANGE.m_id && deathtype != DEATH_AUTOTEAMCHANGE.m_id)
719  {
720  if(autocvar_g_frozen_revive_falldamage > 0 && deathtype == DEATH_FALL.m_id && damage >= autocvar_g_frozen_revive_falldamage)
721  {
722  Unfreeze(targ, false);
724  Send_Effect(EFFECT_ICEORGLASS, targ.origin, '0 0 0', 3);
725  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname);
726  Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
727  }
728 
729  damage = 0;
730  force *= autocvar_g_frozen_force;
731  }
732 
733  if(IS_PLAYER(targ) && STAT(FROZEN, targ)
735  {
736  Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1);
737 
738  entity spot = SelectSpawnPoint(targ, false);
739  if(spot)
740  {
741  damage = 0;
742  targ.deadflag = DEAD_NO;
743 
744  targ.angles = spot.angles;
745 
746  targ.effects = 0;
747  targ.effects |= EF_TELEPORT_BIT;
748 
749  targ.angles_z = 0; // never spawn tilted even if the spot says to
750  targ.fixangle = true; // turn this way immediately
751  targ.velocity = '0 0 0';
752  targ.avelocity = '0 0 0';
753  targ.punchangle = '0 0 0';
754  targ.punchvector = '0 0 0';
755  targ.oldvelocity = targ.velocity;
756 
757  targ.spawnorigin = spot.origin;
758  setorigin(targ, spot.origin + '0 0 1' * (1 - targ.mins.z - 24));
759  // don't reset back to last position, even if new position is stuck in solid
760  targ.oldorigin = targ.origin;
761 
762  Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1);
763  }
764  }
765 
766  if (targ == attacker)
767  damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
768 
769  // count the damage
770  if(attacker)
771  if(!IS_DEAD(targ))
772  if(deathtype != DEATH_BUFF.m_id)
773  if(targ.takedamage == DAMAGE_AIM)
774  if(targ != attacker)
775  {
776  entity victim;
777  if(IS_VEHICLE(targ) && targ.owner)
778  victim = targ.owner;
779  else
780  victim = targ;
781 
782  if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
783  {
784  if (DIFF_TEAM(victim, attacker))
785  {
786  if(damage > 0)
787  {
788  if(deathtype != DEATH_FIRE.m_id)
789  {
790  if(PHYS_INPUT_BUTTON_CHAT(victim))
791  attacker.typehitsound += 1;
792  else
793  attacker.hitsound_damage_dealt += damage;
794  }
795 
796  impressive_hits += 1;
797 
798  if (!DEATH_ISSPECIAL(deathtype))
799  {
800  if(IS_PLAYER(targ)) // don't do this for vehicles
801  if(IsFlying(victim))
802  yoda = 1;
803  }
804  }
805  }
806  else if (IS_PLAYER(attacker) && !STAT(FROZEN, victim)) // same team
807  {
808  if (deathtype != DEATH_FIRE.m_id)
809  {
810  attacker.typehitsound += 1;
811  }
812  if(complainteamdamage > 0)
813  if(time > CS(attacker).teamkill_complain)
814  {
815  CS(attacker).teamkill_complain = time + 5;
816  CS(attacker).teamkill_soundtime = time + 0.4;
817  CS(attacker).teamkill_soundsource = targ;
818  }
819  }
820  }
821  }
822  }
823 
824  // apply push
825  if (targ.damageforcescale)
826  if (force)
827  if (!IS_PLAYER(targ) || !StatusEffects_active(STATUSEFFECT_SpawnShield, targ) || targ == attacker)
828  {
829  vector farce = damage_explosion_calcpush(targ.damageforcescale * force, targ.velocity, autocvar_g_balance_damagepush_speedfactor);
830  if(targ.move_movetype == MOVETYPE_PHYSICS)
831  {
832  entity farcent = new(farce);
833  farcent.enemy = targ;
834  farcent.movedir = farce * 10;
835  if(targ.mass)
836  farcent.movedir = farcent.movedir * targ.mass;
837  farcent.origin = hitloc;
838  farcent.forcetype = FORCETYPE_FORCEATPOS;
839  farcent.nextthink = time + 0.1;
840  setthink(farcent, SUB_Remove);
841  }
842  else if(targ.move_movetype != MOVETYPE_NOCLIP)
843  {
844  targ.velocity = targ.velocity + farce;
845  }
846  UNSET_ONGROUND(targ);
847  UpdateCSQCProjectile(targ);
848  }
849  // apply damage
850  if (damage != 0 || (targ.damageforcescale && force))
851  if (targ.event_damage)
852  targ.event_damage (targ, inflictor, attacker, damage, deathtype, weaponentity, hitloc, force);
853 
854  // apply mirror damage if any
855  if(!autocvar_g_mirrordamage_onlyweapons || DEATH_WEAPONOF(deathtype) != WEP_Null)
856  if(mirrordamage > 0 || mirrorforce > 0)
857  {
858  attacker = attacker_save;
859 
860  force = normalize(attacker.origin + attacker.view_ofs - hitloc) * mirrorforce;
861  Damage(attacker, inflictor, attacker, mirrordamage, DEATH_MIRRORDAMAGE.m_id, weaponentity, attacker.origin, force);
862  }
863 }
864 
865 // Returns total damage applies to creatures
866 float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe,
867  float inflictorselfdamage, float forceintensity, float forcezscale, int deathtype, .entity weaponentity, entity directhitentity)
868 {
869  entity targ;
870  vector force;
871  float total_damage_to_creatures;
872  entity next;
873  float tfloordmg;
874  float tfloorforce;
875 
876  float stat_damagedone;
877 
879  {
880  backtrace("RadiusDamage called recursively! Expect stuff to go HORRIBLY wrong.");
881  return 0;
882  }
883 
885 
886  tfloordmg = autocvar_g_throughfloor_damage;
887  tfloorforce = autocvar_g_throughfloor_force;
888 
889  total_damage_to_creatures = 0;
890 
891  if(deathtype != (WEP_HOOK.m_id | HITTYPE_SECONDARY | HITTYPE_BOUNCE)) // only send gravity bomb damage once
892  if(!(deathtype & HITTYPE_SOUND)) // do not send radial sound damage (bandwidth hog)
893  {
894  force = inflictorvelocity;
895  if(force == '0 0 0')
896  force = '0 0 -1';
897  else
898  force = normalize(force);
899  if(forceintensity >= 0)
900  Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
901  else
902  Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
903  }
904 
905  stat_damagedone = 0;
906 
907  targ = WarpZone_FindRadius (inflictororigin, rad + MAX_DAMAGEEXTRARADIUS, false);
908  while (targ)
909  {
910  next = targ.chain;
911  if ((targ != inflictor) || inflictorselfdamage)
912  if (((cantbe != targ) && !mustbe) || (mustbe == targ))
913  if (targ.takedamage)
914  {
915  vector nearest;
916  vector diff;
917  float power;
918 
919  // LordHavoc: measure distance to nearest point on target (not origin)
920  // (this guarentees 100% damage on a touch impact)
921  nearest = targ.WarpZone_findradius_nearest;
922  diff = targ.WarpZone_findradius_dist;
923  // round up a little on the damage to ensure full damage on impacts
924  // and turn the distance into a fraction of the radius
925  power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
926  //bprint(" ");
927  //bprint(ftos(power));
928  //if (targ == attacker)
929  // print(ftos(power), "\n");
930  if (power > 0)
931  {
932  float finaldmg;
933  if (power > 1)
934  power = 1;
935  finaldmg = coredamage * power + edgedamage * (1 - power);
936  if (finaldmg > 0)
937  {
938  float a;
939  float c;
940  vector hitloc;
941  vector myblastorigin;
942  vector center;
943 
944  myblastorigin = WarpZone_TransformOrigin(targ, inflictororigin);
945 
946  // if it's a player, use the view origin as reference
947  center = CENTER_OR_VIEWOFS(targ);
948 
949  force = normalize(center - myblastorigin);
950  force = force * (finaldmg / coredamage) * forceintensity;
951  hitloc = nearest;
952 
953  // apply special scaling along the z axis if set
954  // NOTE: 0 value is not allowed for compatibility, in the case of weapon cvars not being set
955  if(forcezscale)
956  force.z *= forcezscale;
957 
958  if(targ != directhitentity)
959  {
960  float hits;
961  float total;
962  float hitratio;
963  float mininv_f, mininv_d;
964 
965  // test line of sight to multiple positions on box,
966  // and do damage if any of them hit
967  hits = 0;
968 
969  // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
970  // so for a given max stddev:
971  // n = (1 / (2 * max stddev of hitratio))^2
972 
973  mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
974  mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
975 
977  LOG_INFOF("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f);
978 
979 
980  total = 0.25 * (max(mininv_f, mininv_d) ** 2);
981 
983  LOG_INFOF(" steps=%f", total);
984 
985 
986  if (IS_PLAYER(targ))
988  else
990 
992  LOG_INFOF(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total)));
993 
994  for(c = 0; c < total; ++c)
995  {
996  //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
997  WarpZone_TraceLine(inflictororigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
998  if (trace_fraction == 1 || trace_ent == targ)
999  {
1000  ++hits;
1001  if (hits > 1)
1002  hitloc = hitloc + nearest;
1003  else
1004  hitloc = nearest;
1005  }
1006  nearest.x = targ.origin.x + targ.mins.x + random() * targ.size.x;
1007  nearest.y = targ.origin.y + targ.mins.y + random() * targ.size.y;
1008  nearest.z = targ.origin.z + targ.mins.z + random() * targ.size.z;
1009  }
1010 
1011  nearest = hitloc * (1 / max(1, hits));
1012  hitratio = (hits / total);
1013  a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
1014  finaldmg = finaldmg * a;
1015  a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
1016  force = force * a;
1017 
1019  LOG_INFOF(" D=%f F=%f", finaldmg, vlen(force));
1020  }
1021 
1022  //if (targ == attacker)
1023  //{
1024  // print("hits ", ftos(hits), " / ", ftos(total));
1025  // print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
1026  // print(" (", ftos(a), ")\n");
1027  //}
1028  if(finaldmg || force)
1029  {
1030  if(targ.iscreature)
1031  {
1032  total_damage_to_creatures += finaldmg;
1033 
1034  if(accuracy_isgooddamage(attacker, targ))
1035  stat_damagedone += finaldmg;
1036  }
1037 
1038  if(targ == directhitentity || DEATH_ISSPECIAL(deathtype))
1039  Damage(targ, inflictor, attacker, finaldmg, deathtype, weaponentity, nearest, force);
1040  else
1041  Damage(targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, weaponentity, nearest, force);
1042  }
1043  }
1044  }
1045  }
1046  targ = next;
1047  }
1048 
1050 
1051  if(!DEATH_ISSPECIAL(deathtype))
1052  accuracy_add(attacker, DEATH_WEAPONOF(deathtype), 0, min(coredamage, stat_damagedone));
1053 
1054  return total_damage_to_creatures;
1055 }
1056 
1057 float RadiusDamage(entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity)
1058 {
1059  return RadiusDamageForSource(inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad,
1060  cantbe, mustbe, false, forceintensity, 1, deathtype, weaponentity, directhitentity);
1061 }
1062 
1063 bool Heal(entity targ, entity inflictor, float amount, float limit)
1064 {
1065  if(game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR) || STAT(FROZEN, targ) || IS_DEAD(targ))
1066  return false;
1067 
1068  bool healed = false;
1069  if(targ.event_heal)
1070  healed = targ.event_heal(targ, inflictor, amount, limit);
1071  // TODO: additional handling? what if the healing kills them? should this abort if healing would do so etc
1072  // TODO: healing fx!
1073  // TODO: armor healing?
1074  return healed;
1075 }
1076 
1077 float Fire_AddDamage(entity e, entity o, float d, float t, float dt)
1078 {
1079  float dps;
1080  float maxtime, mintime, maxdamage, mindamage, maxdps, mindps, totaldamage, totaltime;
1081 
1082  if(IS_PLAYER(e))
1083  {
1084  if(IS_DEAD(e))
1085  return -1;
1086  }
1087 
1088  t = max(t, 0.1);
1089  dps = d / t;
1090  if(StatusEffects_active(STATUSEFFECT_Burning, e))
1091  {
1092  float fireendtime = StatusEffects_gettime(STATUSEFFECT_Burning, e);
1093 
1094  mintime = fireendtime - time;
1095  maxtime = max(mintime, t);
1096 
1097  mindps = e.fire_damagepersec;
1098  maxdps = max(mindps, dps);
1099 
1100  if(maxtime > mintime || maxdps > mindps)
1101  {
1102  // Constraints:
1103 
1104  // damage we have right now
1105  mindamage = mindps * mintime;
1106 
1107  // damage we want to get
1108  maxdamage = mindamage + d;
1109 
1110  // but we can't exceed maxtime * maxdps!
1111  totaldamage = min(maxdamage, maxtime * maxdps);
1112 
1113  // LEMMA:
1114  // Look at:
1115  // totaldamage = min(mindamage + d, maxtime * maxdps)
1116  // We see:
1117  // totaldamage <= maxtime * maxdps
1118  // ==> totaldamage / maxdps <= maxtime.
1119  // We also see:
1120  // totaldamage / mindps = min(mindamage / mindps + d, maxtime * maxdps / mindps)
1121  // >= min(mintime, maxtime)
1122  // ==> totaldamage / maxdps >= mintime.
1123 
1124  /*
1125  // how long do we damage then?
1126  // at least as long as before
1127  // but, never exceed maxdps
1128  totaltime = max(mintime, totaldamage / maxdps); // always <= maxtime due to lemma
1129  */
1130 
1131  // alternate:
1132  // at most as long as maximum allowed
1133  // but, never below mindps
1134  totaltime = min(maxtime, totaldamage / mindps); // always >= mintime due to lemma
1135 
1136  // assuming t > mintime, dps > mindps:
1137  // we get d = t * dps = maxtime * maxdps
1138  // totaldamage = min(maxdamage, maxtime * maxdps) = min(... + d, maxtime * maxdps) = maxtime * maxdps
1139  // totaldamage / maxdps = maxtime
1140  // totaldamage / mindps > totaldamage / maxdps = maxtime
1141  // FROM THIS:
1142  // a) totaltime = max(mintime, maxtime) = maxtime
1143  // b) totaltime = min(maxtime, totaldamage / maxdps) = maxtime
1144 
1145  // assuming t <= mintime:
1146  // we get maxtime = mintime
1147  // a) totaltime = max(mintime, ...) >= mintime, also totaltime <= maxtime by the lemma, therefore totaltime = mintime = maxtime
1148  // b) totaltime = min(maxtime, ...) <= maxtime, also totaltime >= mintime by the lemma, therefore totaltime = mintime = maxtime
1149 
1150  // assuming dps <= mindps:
1151  // we get mindps = maxdps.
1152  // With this, the lemma says that mintime <= totaldamage / mindps = totaldamage / maxdps <= maxtime.
1153  // a) totaltime = max(mintime, totaldamage / maxdps) = totaldamage / maxdps
1154  // b) totaltime = min(maxtime, totaldamage / mindps) = totaldamage / maxdps
1155 
1156  e.fire_damagepersec = totaldamage / totaltime;
1157  StatusEffects_apply(STATUSEFFECT_Burning, e, time + totaltime, 0);
1158  if(totaldamage > 1.2 * mindamage)
1159  {
1160  e.fire_deathtype = dt;
1161  if(e.fire_owner != o)
1162  {
1163  e.fire_owner = o;
1164  e.fire_hitsound = false;
1165  }
1166  }
1167  if(accuracy_isgooddamage(o, e))
1168  accuracy_add(o, DEATH_WEAPONOF(dt), 0, max(0, totaldamage - mindamage));
1169  return max(0, totaldamage - mindamage); // can never be negative, but to make sure
1170  }
1171  else
1172  return 0;
1173  }
1174  else
1175  {
1176  e.fire_damagepersec = dps;
1177  StatusEffects_apply(STATUSEFFECT_Burning, e, time + t, 0);
1178  e.fire_deathtype = dt;
1179  e.fire_owner = o;
1180  e.fire_hitsound = false;
1181  if(accuracy_isgooddamage(o, e))
1182  accuracy_add(o, DEATH_WEAPONOF(dt), 0, d);
1183  return d;
1184  }
1185 }
1186 
1188 {
1189  float t, d, hi, ty;
1190  entity o;
1191 
1192  for(t = 0, o = e.owner; o.owner && t < 16; o = o.owner, ++t);
1193  if(IS_NOT_A_CLIENT(o))
1194  o = e.fire_owner;
1195 
1196  float fireendtime = StatusEffects_gettime(STATUSEFFECT_Burning, e);
1197  t = min(frametime, fireendtime - time);
1198  d = e.fire_damagepersec * t;
1199 
1200  hi = e.fire_owner.hitsound_damage_dealt;
1201  ty = e.fire_owner.typehitsound;
1202  Damage(e, e, e.fire_owner, d, e.fire_deathtype, DMG_NOWEP, e.origin, '0 0 0');
1203  if(e.fire_hitsound && e.fire_owner)
1204  {
1205  e.fire_owner.hitsound_damage_dealt = hi;
1206  e.fire_owner.typehitsound = ty;
1207  }
1208  e.fire_hitsound = true;
1209 
1210  if(!IS_INDEPENDENT_PLAYER(e) && !STAT(FROZEN, e))
1211  {
1212  IL_EACH(g_damagedbycontents, it.damagedbycontents && it != e,
1213  {
1214  if(!IS_DEAD(it) && it.takedamage && !IS_INDEPENDENT_PLAYER(it))
1215  if(boxesoverlap(e.absmin, e.absmax, it.absmin, it.absmax))
1216  {
1217  t = autocvar_g_balance_firetransfer_time * (fireendtime - time);
1218  d = autocvar_g_balance_firetransfer_damage * e.fire_damagepersec * t;
1219  Fire_AddDamage(it, o, d, t, DEATH_FIRE.m_id);
1220  }
1221  });
1222  }
1223 }
const int HITTYPE_SPLASH
automatically set by RadiusDamage
Definition: all.qh:27
ERASEABLE void IL_REMOVE(IntrusiveList this, entity it)
Remove any element, anywhere in the list.
entity SelectSpawnPoint(entity this, bool anypoint)
Definition: spawnpoints.qc:329
#define APP_TEAM_NUM(num, prefix)
Definition: all.qh:85
#define IL_EACH(this, cond, body)
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition: player.qh:155
string deathmessage
Definition: damage.qh:76
const int HITTYPE_SOUND
Definition: all.qh:30
bool autocvar_g_frozen_damage_trigger
Definition: damage.qh:30
const int HITTYPE_BOUNCE
Definition: all.qh:28
entity hook
Definition: hook.qh:19
void GiveFrags(entity attacker, entity targ, float f, int deathtype,.entity weaponentity)
Definition: damage.qc:49
spree_inf s1 s2 s3loc s2 s1
Definition: all.inc:265
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
Definition: cl_resources.qc:15
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 s2loc s1 s2 f1 f1points s1 s2
Definition: all.inc:438
float autocvar_g_balance_pause_health_regen
Definition: sv_resources.qh:35
float RadiusDamage_running
Definition: damage.qh:131
const float MOVETYPE_PHYSICS
Header file that describes the resource system.
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:217
#define IS_CLIENT(v)
Definition: utils.qh:13
float teamkill_complain
Definition: damage.qh:57
#define IS_INDEPENDENT_PLAYER(e)
Definition: client.qh:314
vector Team_ColorRGB(int teamid)
Definition: teams.qh:76
float autocvar_g_frozen_revive_falldamage
Definition: damage.qh:28
const float MIN_DAMAGEEXTRARADIUS
Definition: damage.qh:138
const float FORCETYPE_FORCEATPOS
IntrusiveList g_damagedbycontents
Definition: damage.qh:155
entity() spawn
void Obituary(entity attacker, entity inflictor, entity targ, int deathtype,.entity weaponentity)
Definition: damage.qc:229
vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor)
Definition: calculations.qc:45
const int DT_FIRST
Definition: all.qh:33
#define REGISTRY_GET(id, i)
Definition: registry.qh:43
float DAMAGE_AIM
Definition: progsdefs.qc:284
vector WarpZone_TransformOrigin(entity wz, vector v)
Definition: common.qc:499
ClientState CS(Client this)
Definition: state.qh:47
void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint)
Definition: damage.qc:494
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
float autocvar_g_friendlyfire_virtual
Definition: damage.qh:26
#define GameRules_scoring_add(client, fld, value)
Definition: sv_rules.qh:78
bool warmup_stage
Definition: main.qh:103
string Deathtype_Name(int deathtype)
Definition: all.qc:3
#define IS_MONSTER(v)
Definition: utils.qh:21
origin
Definition: ent_cs.qc:114
float autocvar_g_throughfloor_min_steps_other
Definition: damage.qh:12
float FL_GODMODE
Definition: progsdefs.qc:237
float ping
Definition: main.qh:138
void LogDeath(string mode, int deathtype, entity killer, entity killed)
Definition: damage.qc:106
#define DEATH_ENT(t)
Definition: all.qh:37
entity buff_FirstFromFlags(entity actor)
Definition: sv_buffs.qc:296
#define DIFF_TEAM(a, b)
Definition: teams.qh:240
void Obituary_SpecialDeath(entity notif_target, float murder, int deathtype, string s1, string s2, string s3, float f1, float f2, float f3)
Definition: damage.qc:125
int killcount
Definition: client.qh:317
#define UNSET_ONGROUND(s)
Definition: movetypes.qh:18
float autocvar_g_throughfloor_damage
Definition: damage.qh:6
float checkrules_firstblood
Definition: damage.qh:46
int impressive_hits
Definition: damage.qh:52
float autocvar_g_throughfloor_min_steps_player
Definition: damage.qh:10
#define DMG_NOWEP
Definition: damage.qh:126
entity owner
Definition: main.qh:73
int autocvar_g_frozen_revive_falldamage_health
Definition: damage.qh:29
#define DEATH_WEAPONOF(t)
Definition: all.qh:41
float autocvar_g_throughfloor_max_steps_other
Definition: damage.qh:13
#define IS_TURRET(v)
Definition: utils.qh:23
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1
Definition: all.inc:654
IntrusiveList g_bot_targets
Definition: api.qh:149
const int EF_TELEPORT_BIT
entity trace_ent
Definition: csprogsdefs.qc:40
vector WarpZone_UnTransformOrigin(entity wz, vector v)
Definition: common.qc:535
string netname
M: refname : reference name name.
Definition: weapon.qh:76
#define setmodel(this, m)
Definition: model.qh:26
float autocvar_g_frozen_force
Definition: damage.qh:31
RES_HEALTH
Definition: ent_cs.qc:126
void Unfreeze(entity targ, bool reset_health)
Definition: damage.qc:546
Effect is being forcibly removed without calling any additional mechanics.
Definition: all.qh:27
bool autocvar_g_mirrordamage_onlyweapons
Definition: damage.qh:16
string AppendItemcodes(string s, entity player)
Definition: damage.qc:87
float autocvar_g_friendlyfire_virtual_force
Definition: damage.qh:27
float autocvar_g_weapondamagefactor
Definition: weaponsystem.qh:7
const float MOVE_NOMONSTERS
Definition: csprogsdefs.qc:253
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 s2loc s1 s2 f1 f1points f2
Definition: all.inc:348
float autocvar_g_balance_selfdamagepercent
Definition: damage.qh:24
float cnt
Definition: powerups.qc:24
#define g_cts
Definition: cts.qh:36
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
float autocvar_g_teamdamage_threshold
Definition: damage.qh:23
void UpdateCSQCProjectile(entity e)
int autocvar_teamplay_mode
Definition: teamplay.qh:3
void SetResource(entity e, Resource res_type, float amount)
Sets the current amount of resource the given entity will have.
Definition: cl_resources.qc:26
#define LOG_INFOF(...)
Definition: log.qh:71
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
const int MAX_WEAPONSLOTS
Definition: weapon.qh:13
#define GameRules_scoring_add_team(client, fld, value)
Definition: sv_rules.qh:80
void Ice_Think(entity this)
Definition: damage.qc:481
void UpdateFrags(entity player, int f)
Definition: damage.qc:44
const int ACTIVE_ACTIVE
Definition: defs.qh:37
#define NULL
Definition: post.qh:17
float frametime
Definition: csprogsdefs.qc:17
#define backtrace(msg)
Definition: log.qh:105
#define LOG_TRACEF(...)
Definition: log.qh:82
#define strstrofs
Definition: dpextensions.qh:42
string NearestLocation(vector p)
Definition: chat.qc:412
void GameLogEcho(string s)
Definition: gamelog.qc:12
float autocvar_g_friendlyfire
Definition: damage.qh:25
#define IS_NOT_A_CLIENT(v)
was: (clienttype(v) == CLIENTTYPE_NOTACLIENT)
Definition: utils.qh:19
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition: damage.qc:583
#define SAME_TEAM(a, b)
Definition: teams.qh:239
bool Heal(entity targ, entity inflictor, float amount, float limit)
Definition: damage.qc:1063
const int HITTYPE_SECONDARY
Definition: all.qh:25
#define M_ARGV(x, type)
Definition: events.qh:17
#define IS_DEAD(s)
Definition: utils.qh:26
entity WarpZone_FindRadius(vector org, float rad, bool needlineofsight)
Definition: common.qc:669
float nextthink
Definition: csprogsdefs.qc:121
bool IsFlying(entity this)
Definition: player.qc:804
float autocvar_g_balance_damagepush_speedfactor
Definition: damage.qh:18
float autocvar_g_weaponforcefactor
Definition: weaponsystem.qh:6
float w_deathtype
Definition: damage.qh:97
float autocvar_g_throughfloor_max_steps_player
Definition: damage.qh:11
void RemoveGrapplingHooks(entity pl)
Definition: hook.qc:78
#define CENTER_OR_VIEWOFS(ent)
Definition: utils.qh:28
vector(float skel, float bonenum) _skel_get_boneabs_hidden
#define IS_VEHICLE(v)
Definition: utils.qh:22
#define ITEM_DAMAGE_NEEDKILL(dt)
Definition: items.qh:130
next
Definition: all.qh:88
float autocvar_g_throughfloor_force
Definition: damage.qh:7
float MOVETYPE_NOCLIP
Definition: progsdefs.qc:254
vector v
Definition: ent_cs.qc:116
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
float start_health
Definition: world.qh:98
float Fire_AddDamage(entity e, entity o, float d, float t, float dt)
Definition: damage.qc:1077
void accuracy_add(entity this, Weapon w, float fired, float hit)
Definition: accuracy.qc:83
void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
Definition: common.qc:338
entity Notification
always last
Definition: all.qh:82
bool autocvar_g_mirrordamage_virtual
Definition: damage.qh:15
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: damage.qc:1057
bool accuracy_isgooddamage(entity attacker, entity targ)
Definition: accuracy.qc:112
float autocvar_g_mirrordamage
Definition: damage.qh:14
const int FRAGS_SPECTATOR
Definition: constants.qh:4
const float MAX_DAMAGEEXTRARADIUS
Definition: damage.qh:139
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
entity weaponentities[MAX_WEAPONSLOTS]
Definition: weapon.qh:14
void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner)
setorigin(ent, v)
float DEAD_NO
Definition: progsdefs.qc:274
#define setthink(e, f)
float RadiusDamageForSource(entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, float forcezscale, int deathtype,.entity weaponentity, entity directhitentity)
Definition: damage.qc:866
void vehicles_exit(entity vehic, bool eject)
Definition: sv_vehicles.qc:788
#define DEATH_ISWEAPON(t, w)
Definition: all.qh:42
#define DEATH_ISSPECIAL(t)
Definition: all.qh:35
bool autocvar_sv_eventlog
Definition: gamelog.qh:3
float autocvar_g_balance_armor_blockpercent
Definition: damage.qh:21
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition: weapon.qh:41
float Obituary_WeaponDeath(entity notif_target, float murder, int deathtype, string s1, string s2, string s3, float f1, float f2)
Definition: damage.qc:170
float autocvar_g_throughfloor_damage_max_stddev
Definition: damage.qh:8
float time
Definition: csprogsdefs.qc:16
void Fire_ApplyDamage(entity e)
Definition: damage.qc:1187
float trace_fraction
Definition: csprogsdefs.qc:36
const int FROZEN_TEMP_DYING
Definition: damage.qh:111
#define KILL_SPREE_LIST
Definition: all.qh:454
#define IS_PLAYER(v)
Definition: utils.qh:9
float autocvar_g_throughfloor_force_max_stddev
Definition: damage.qh:9
float yoda
Definition: damage.qh:51
void RemoveHook(entity this)
Definition: hook.qc:96
bool autocvar_g_throughfloor_debug
Definition: damage.qh:5