Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
damage.qc
Go to the documentation of this file.
1#include "damage.qh"
2
3#include <common/constants.qh>
18#include <common/playerstats.qh>
20#include <common/state.qh>
21#include <common/teams.qh>
22#include <common/util.qh>
27#include <server/bot/api.qh>
28#include <server/client.qh>
29#include <server/gamelog.qh>
30#include <server/hook.qh>
31#include <server/items/items.qh>
32#include <server/main.qh>
34#include <server/scores.qh>
35#include <server/spawnpoints.qh>
36#include <server/teamplay.qh>
41#include <server/world.qh>
42
43void GiveFrags(entity attacker, entity targ, float f, int deathtype, .entity weaponentity)
44{
45 // TODO route through PlayerScores instead
46 if (game_stopped)
47 return;
48
49 if (f < 0)
50 {
51 if (targ == attacker) // suicide
52 GameRules_scoring_add(attacker, SUICIDES, 1);
53 else
54 {
55 // teamkill
56 int teamkills = GameRules_scoring_add(attacker, TEAMKILLS, 1);
58 f -= (teamkills * (teamkills - 1)) * 0.5; // negative 1, 2, 4, 7, 11, 16, 22, etc.
59 }
60 }
61 else
62 {
63 // regular frag
64 GameRules_scoring_add(attacker, KILLS, 1);
65 if (!warmup_stage && targ.playerid)
66 PlayerStats_GameReport_Event_Player(attacker, sprintf("kills-%d", targ.playerid), 1);
67 }
68
69 GameRules_scoring_add(targ, DEATHS, 1);
70
71 // FIXME fix the mess this is (we have REAL points now!)
72 if (MUTATOR_CALLHOOK(GiveFragsForKill, attacker, targ, f, deathtype, attacker.(weaponentity)))
73 f = M_ARGV(2, float);
74
75 attacker.totalfrags += f;
76
77 if (f)
78 GameRules_scoring_add_team(attacker, SCORE, f);
79}
80
81string AppendItemcodes(string s, entity player)
82{
83 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
84 {
85 .entity weaponentity = weaponentities[slot];
86 int w = player.(weaponentity).m_weapon.m_id;
87 if (w == 0)
88 w = player.(weaponentity).cnt; // previous weapon
89 if (w != 0 || slot == 0)
90 s = strcat(s, ftos(w));
91 }
92 if (PHYS_INPUT_BUTTON_CHAT(player))
93 s = strcat(s, "T");
94 // TODO: include these codes as a flag on the item itself
95 MUTATOR_CALLHOOK(LogDeath_AppendItemCodes, player, s);
96 s = M_ARGV(1, string);
97 return s;
98}
99
100void LogDeath(string mode, int deathtype, entity killer, entity killed)
101{
103 return;
104 string s = strcat(":kill:", mode,
105 ":", ftos(killer.playerid),
106 ":", ftos(killed.playerid));
107 s = strcat(s, ":type=", Deathtype_Name(deathtype), ":items=");
108 s = AppendItemcodes(s, killer);
109 if (killed != killer)
110 {
111 s = strcat(s, ":victimitems=");
112 s = AppendItemcodes(s, killed);
113 }
114 GameLogEcho(s);
115}
116
118 entity notif_target,
119 float murder,
120 bool msg_from_ent,
121 int deathtype,
122 string s1, string s2, string s3, string s4,
123 float f1, float f2, float f3)
124{
125 if (!DEATH_ISSPECIAL(deathtype))
126 {
127 backtrace("Obituary_SpecialDeath called without a special deathtype?\n");
128 return;
129 }
130
131 entity deathent = REGISTRY_GET(Deathtypes, deathtype - DT_FIRST);
132 if (!deathent)
133 {
134 backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n");
135 return;
136 }
137
138 if (g_cts && deathtype == DEATH_KILL.m_id)
139 return; // TODO: somehow put this in CTS gametype file!
140
141 Notification death_message = (msg_from_ent)
142 ? (murder ? deathent.death_msg_ent_murder : deathent.death_msg_ent_self)
143 : (murder ? deathent.death_msgmurder : deathent.death_msgself);
144 if (death_message)
145 {
147 NOTIF_ONE,
148 notif_target,
149 MSG_MULTI,
150 death_message,
151 s1, s2, s3, s4,
152 f1, f2, f3, 0
153 );
155 NOTIF_ALL_EXCEPT,
156 notif_target,
157 MSG_INFO,
158 death_message.nent_msginfo,
159 s1, s2, s3, s4,
160 f1, f2, f3, 0
161 );
162 }
163}
164
166 entity notif_target,
167 float murder,
168 int deathtype,
169 string s1, string s2, string s3,
170 float f1, float f2)
171{
172 Weapon death_weapon = DEATH_WEAPONOF(deathtype);
173 if (death_weapon == WEP_Null)
174 return false;
175
176 w_deathtype = deathtype;
177 Notification death_message = (murder ? death_weapon.wr_killmessage(death_weapon) : death_weapon.wr_suicidemessage(death_weapon));
178 w_deathtype = false;
179
180 if (death_message)
181 {
183 NOTIF_ONE,
184 notif_target,
185 MSG_MULTI,
186 death_message,
187 s1, s2, s3, "",
188 f1, f2, 0, 0
189 );
190 // send the info part to everyone
192 NOTIF_ALL_EXCEPT,
193 notif_target,
194 MSG_INFO,
195 death_message.nent_msginfo,
196 s1, s2, s3, "",
197 f1, f2, 0, 0
198 );
199 }
200 else
202 "Obituary_WeaponDeath(): ^1Deathtype ^7(%d)^1 has no notification for weapon %s!\n",
203 deathtype,
204 death_weapon.netname
205 );
206
207 return true;
208}
209
210bool frag_centermessage_override(entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target, string attacker_name)
211{
212 if (deathtype == DEATH_FIRE.m_id)
213 {
214 Send_Notification(NOTIF_ONE, attacker, MSG_CHOICE, CHOICE_FRAG_FIRE, targ.netname, kill_count_to_attacker, (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping));
215 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));
216 return true;
217 }
218
219 return MUTATOR_CALLHOOK(FragCenterMessage, attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target);
220}
221
222void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .entity weaponentity)
223{
224 // Sanity check
225 if (!IS_PLAYER(targ))
226 {
227 backtrace("Obituary called on non-player?!\n");
228 return;
229 }
230
231 // Declarations
232 float notif_firstblood = false;
233 float kill_count_to_attacker, kill_count_to_target;
234 bool notif_anonymous = false;
235 string attacker_name = attacker.netname;
236
237 // Set final information for the death
238 targ.death_origin = targ.origin;
239 string deathlocation = (autocvar_notification_server_allows_location ? NearestLocation(targ.death_origin) : "");
240
241 // Abort now if a mutator requests it
242 if (MUTATOR_CALLHOOK(ClientObituary, inflictor, attacker, targ, deathtype, attacker.(weaponentity)))
243 {
244 CS(targ).killcount = 0;
245 return;
246 }
247 notif_anonymous = M_ARGV(5, bool);
248
249 // TODO: Replace "???" with a translatable "Anonymous player" string
250 // https://gitlab.com/xonotic/xonotic-data.pk3dir/-/issues/2839
251 if (notif_anonymous)
252 attacker_name = "???";
253
254 #ifdef NOTIFICATIONS_DEBUG
255 Debug_Notification(sprintf(
256 "Obituary(%s, %s, %s, %s = %d);\n",
257 attacker_name,
258 inflictor.netname,
259 targ.netname,
260 Deathtype_Name(deathtype),
261 deathtype
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 Obituary_SpecialDeath(targ, false, 0, deathtype, targ.netname, deathlocation, "", "", targ.team, 0, 0);
274 else
275 {
276 switch (DEATH_ENT(deathtype))
277 {
278 case DEATH_MIRRORDAMAGE:
279 Obituary_SpecialDeath(targ, false, 0, deathtype, targ.netname, deathlocation, "", "", CS(targ).killcount, 0, 0);
280 break;
281 case DEATH_HURTTRIGGER:
282 {
283 bool msg_from_ent = (inflictor && inflictor.message != "");
284 Obituary_SpecialDeath(targ, false, msg_from_ent, deathtype,
285 targ.netname,
286 (msg_from_ent ? inflictor.message : deathlocation),
287 (msg_from_ent ? deathlocation : ""),
288 "", CS(targ).killcount, 0, 0
289 );
290 break;
291 }
292 default:
293 Obituary_SpecialDeath(targ, false, 0, deathtype, targ.netname, deathlocation, "", "", CS(targ).killcount, 0, 0);
294 break;
295 }
296 }
297 }
298 else if (!Obituary_WeaponDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0))
299 {
300 backtrace("SUICIDE: what the hell happened here?\n");
301 return;
302 }
303 LogDeath("suicide", deathtype, targ, targ);
304 if (deathtype != DEATH_AUTOTEAMCHANGE.m_id) // special case: don't negate frags if auto switched
305 GiveFrags(attacker, targ, -1, deathtype, weaponentity);
306 }
307
308 // ======
309 // MURDER
310 // ======
311 else if (IS_PLAYER(attacker))
312 {
313 if (SAME_TEAM(attacker, targ))
314 {
315 LogDeath("tk", deathtype, attacker, targ);
316 GiveFrags(attacker, targ, -1, deathtype, weaponentity);
317
318 CS(attacker).killcount = 0;
319
320 Send_Notification(NOTIF_ONE, attacker, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAG, targ.netname);
321 Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_DEATH_TEAMKILL_FRAGGED, attacker_name);
322 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(targ.team, INFO_DEATH_TEAMKILL), targ.netname, attacker_name, deathlocation, CS(targ).killcount);
323
324 // In this case, the death message will ALWAYS be "foo was betrayed by bar"
325 // No need for specific death/weapon messages...
326 }
327 else
328 {
329 LogDeath("frag", deathtype, attacker, targ);
330 GiveFrags(attacker, targ, 1, deathtype, weaponentity);
331
332 CS(attacker).taunt_soundtime = time + 1;
333 ++CS(attacker).killcount;
334
335 ++attacker.killsound;
336
337 // TODO: improve SPREE_ITEM and KILL_SPREE_LIST
338 // these 2 macros are spread over multiple files
339 #define SPREE_ITEM(counta,countb,center,normal,gentle) \
340 case counta: \
341 Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
342 if (!warmup_stage) \
343 PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
344 break;
345
346 switch (CS(attacker).killcount)
347 {
349 default:
350 break;
351 }
352 #undef SPREE_ITEM
353
355 {
357 notif_firstblood = true; // modify the current messages so that they too show firstblood information
360
361 // tell spree_inf and spree_cen that this is a first-blood and first-victim event
362 kill_count_to_attacker = -1;
363 kill_count_to_target = -2;
364 }
365 else
366 {
367 kill_count_to_attacker = CS(attacker).killcount;
368 kill_count_to_target = 0;
369 }
370
371 if (targ.istypefrag)
372 {
374 NOTIF_ONE,
375 attacker,
376 MSG_CHOICE,
377 CHOICE_TYPEFRAG,
378 targ.netname,
379 kill_count_to_attacker,
380 (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
381 );
383 NOTIF_ONE,
384 targ,
385 MSG_CHOICE,
386 CHOICE_TYPEFRAGGED,
387 attacker_name,
388 kill_count_to_target,
389 GetResource(attacker, RES_HEALTH),
390 GetResource(attacker, RES_ARMOR),
391 (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
392 );
393 }
394 else if (!frag_centermessage_override(attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target, attacker_name))
395 {
397 NOTIF_ONE,
398 attacker,
399 MSG_CHOICE,
400 CHOICE_FRAG,
401 targ.netname,
402 kill_count_to_attacker,
403 (IS_BOT_CLIENT(targ) ? -1 : CS(targ).ping)
404 );
406 NOTIF_ONE,
407 targ,
408 MSG_CHOICE,
409 CHOICE_FRAGGED,
410 attacker_name,
411 kill_count_to_target,
412 GetResource(attacker, RES_HEALTH),
413 GetResource(attacker, RES_ARMOR),
414 (IS_BOT_CLIENT(attacker) ? -1 : CS(attacker).ping)
415 );
416 }
417
418 if (!Obituary_WeaponDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker))
419 {
420 int f3 = (deathtype == DEATH_BUFF.m_id) ? buff_FirstFromFlags(attacker).m_id : 0;
421 if (DEATH_ENT(deathtype) == DEATH_HURTTRIGGER)
422 {
423 bool msg_from_ent = (inflictor && inflictor.message2 != "");
424 Obituary_SpecialDeath(targ, true, msg_from_ent, deathtype,
425 targ.netname,
426 attacker_name,
427 (msg_from_ent ? inflictor.message2 : deathlocation),
428 (msg_from_ent ? deathlocation : ""),
429 CS(targ).killcount, kill_count_to_attacker, f3
430 );
431 }
432 else
433 Obituary_SpecialDeath(targ, true, 0, deathtype, targ.netname, attacker_name, deathlocation, "", CS(targ).killcount, kill_count_to_attacker, f3);
434 }
435 }
436 }
437
438 // =============
439 // ACCIDENT/TRAP
440 // =============
441 else
442 {
443 switch (DEATH_ENT(deathtype))
444 {
445 // For now, we're just forcing HURTTRIGGER to behave as "DEATH_VOID" and giving it no special options...
446 // Later on you will only be able to make custom messages using DEATH_CUSTOM,
447 // and there will be a REAL DEATH_VOID implementation which mappers will use.
448 case DEATH_HURTTRIGGER:
449 {
450 bool msg_from_ent = (inflictor && inflictor.message != "");
451 Obituary_SpecialDeath(targ, false, msg_from_ent, deathtype,
452 targ.netname,
453 (msg_from_ent ? inflictor.message : deathlocation),
454 (msg_from_ent ? deathlocation : ""),
455 "",
456 CS(targ).killcount,
457 0,
458 0
459 );
460 break;
461 }
462 case DEATH_CUSTOM:
463 Obituary_SpecialDeath(targ, false, 0, deathtype,
464 targ.netname,
465 ((strstrofs(deathmessage, "%", 0) < 0) ? strcat("%s ", deathmessage) : deathmessage),
466 deathlocation,
467 "",
468 CS(targ).killcount,
469 0,
470 0
471 );
472 break;
473 default:
474 Obituary_SpecialDeath(targ, false, 0, deathtype, targ.netname, deathlocation, "", "", CS(targ).killcount, 0, 0);
475 break;
476 }
477
478 LogDeath("accident", deathtype, targ, targ);
479 GiveFrags(targ, targ, -1, deathtype, weaponentity);
480
481 if (GameRules_scoring_add(targ, SCORE, 0) == -5)
482 {
483 Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE);
484 if (!warmup_stage)
486 }
487 }
488
489 // reset target kill count
490 CS(targ).killcount = 0;
491}
492
493void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
494{
495 if (game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR))
496 return;
497
498 // special rule: gravity bombs and sound-based attacks do not affect team mates (other than for disconnecting the hook)
499 if ((DEATH_ISWEAPON(deathtype, WEP_HOOK) || (deathtype & HITTYPE_SOUND))
500 && IS_PLAYER(targ) && SAME_TEAM(targ, attacker))
501 return;
502
503 entity attacker_save = attacker;
504
505 float complainteamdamage = 0;
506 float mirrordamage = 0;
507 float mirrorforce = 0;
508
509 if (deathtype == DEATH_KILL.m_id || deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
510 {
511 // exit the vehicle before killing (fixes a crash)
512 if (IS_PLAYER(targ) && targ.vehicle)
513 vehicles_exit(targ.vehicle, VHEF_RELEASE);
514
515 // These are ALWAYS lethal
516 // No damage modification here
517 // Instead, prepare the victim for their death...
518 if (deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
519 {
521 SetResourceExplicit(targ, RES_HEALTH, 0.9); // this is < 1
522 }
523 StatusEffects_remove(STATUSEFFECT_SpawnShield, targ, STATUSEFFECT_REMOVE_CLEAR);
524 targ.flags &= ~FL_GODMODE;
525 damage = 100000;
526 }
527 else if (deathtype == DEATH_MIRRORDAMAGE.m_id || deathtype == DEATH_NOAMMO.m_id)
528 {
529 // no processing
530 }
531 else
532 {
533 // nullify damage if teamplay is on
534 if (deathtype != DEATH_TELEFRAG.m_id
535 && IS_PLAYER(attacker))
536 {
537 // avoid dealing damage or force to other independent players
538 // and avoid dealing damage or force to things owned by other independent players
539 if ((IS_PLAYER(targ) && targ != attacker && (IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(targ)))
540 || (targ.realowner && IS_INDEPENDENT_PLAYER(targ.realowner) && attacker != targ.realowner))
541 {
542 damage = 0;
543 force = '0 0 0';
544 }
545 else if (!STAT(FROZEN, targ) && SAME_TEAM(attacker, targ))
546 {
547 if (autocvar_teamplay_mode == 1)
548 damage = 0;
549 else if (attacker != targ)
550 {
551 if (autocvar_teamplay_mode == 2)
552 {
553 if (IS_PLAYER(targ) && !IS_DEAD(targ))
554 {
555 attacker.dmg_team += damage;
556 complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
557 }
558 }
559 else if (autocvar_teamplay_mode == 3)
560 damage = 0;
561 else if (autocvar_teamplay_mode == 4)
562 {
563 if (IS_PLAYER(targ) && !IS_DEAD(targ))
564 {
565 attacker.dmg_team += damage;
566 complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
567 if (complainteamdamage > 0)
568 mirrordamage = autocvar_g_mirrordamage * complainteamdamage;
569 mirrorforce = autocvar_g_mirrordamage * vlen(force);
570 damage = autocvar_g_friendlyfire * damage;
571 // mirrordamage will be used LATER
572
574 {
576 attacker.dmg_take += v.x;
577 attacker.dmg_save += v.y;
578 attacker.dmg_inflictor = inflictor;
579 mirrordamage = v.z;
580 mirrorforce = 0;
581 }
582
584 {
586 targ.dmg_take += v.x;
587 targ.dmg_save += v.y;
588 targ.dmg_inflictor = inflictor;
589 damage = 0;
591 force = '0 0 0';
592 }
593 }
594 else if (!targ.canteamdamage)
595 damage = 0;
596 }
597 }
598 }
599 }
600
601 if (!DEATH_ISSPECIAL(deathtype))
602 {
604 mirrordamage *= autocvar_g_weapondamagefactor;
605 complainteamdamage *= autocvar_g_weapondamagefactor;
607 mirrorforce *= autocvar_g_weaponforcefactor;
608 }
609
610 // should this be changed at all? If so, in what way?
611 MUTATOR_CALLHOOK(Damage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force, attacker.(weaponentity));
612 damage = M_ARGV(4, float);
613 mirrordamage = M_ARGV(5, float);
614 force = M_ARGV(6, vector);
615
616 if (IS_PLAYER(targ) && damage > 0 && attacker)
617 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
618 {
619 .entity went = weaponentities[slot];
620 if (targ.(went).hook && targ.(went).hook.aiment == attacker)
621 RemoveHook(targ.(went).hook);
622 }
623
624 if (targ == attacker)
625 damage *= autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
626
627 // count the damage
628 if (attacker && targ != attacker
629 && !IS_DEAD(targ)
630 && deathtype != DEATH_BUFF.m_id
631 && targ.takedamage == DAMAGE_AIM)
632 {
633 entity victim;
634 if (IS_VEHICLE(targ) && targ.owner)
635 victim = targ.owner;
636 else
637 victim = targ;
638
639 // TODO: allow the mutator hook to tell if the hit sound should be team or not
640 if (IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim)
641 || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
642 {
643 if (DIFF_TEAM(victim, attacker))
644 {
645 if (damage > 0)
646 {
647 if (deathtype != DEATH_FIRE.m_id)
648 {
649 if (PHYS_INPUT_BUTTON_CHAT(victim))
650 ++attacker.typehitsound;
651 else
652 attacker.hitsound_damage_dealt += damage;
653 }
654
656
657 if (!DEATH_ISSPECIAL(deathtype)
658 && IS_PLAYER(targ) // don't do this for vehicles
659 && IsFlying(victim))
660 yoda = 1;
661 }
662 }
663 else if (IS_PLAYER(attacker) && !STAT(FROZEN, victim) && !(deathtype & HITTYPE_SPAM)) // same team
664 {
665 if (deathtype != DEATH_FIRE.m_id)
666 ++attacker.typehitsound;
667 if (complainteamdamage > 0
668 && time > CS(attacker).teamkill_complain)
669 {
670 CS(attacker).teamkill_complain = time + 5;
671 CS(attacker).teamkill_soundtime = time + 0.4;
672 CS(attacker).teamkill_soundsource = targ;
673 }
674 }
675 }
676 }
677 }
678
679 // apply push
680 if (targ.damageforcescale && force
681 && (!IS_PLAYER(targ) || !StatusEffects_active(STATUSEFFECT_SpawnShield, targ) || targ == attacker))
682 {
683 vector farce = damage_explosion_calcpush(targ.damageforcescale * force, targ.velocity, autocvar_g_balance_damagepush_speedfactor);
684 if (targ.move_movetype == MOVETYPE_PHYSICS)
685 {
686 entity farcent = new(farce);
687 farcent.enemy = targ;
688 farcent.movedir = farce * 10;
689 if (targ.mass)
690 farcent.movedir *= targ.mass;
691 farcent.origin = hitloc;
692 farcent.forcetype = FORCETYPE_FORCEATPOS;
693 farcent.nextthink = time + 0.1;
694 setthink(farcent, SUB_Remove);
695 }
696 else if (targ.move_movetype != MOVETYPE_NOCLIP)
697 targ.velocity += farce;
698 UNSET_ONGROUND(targ);
700 }
701 // apply damage
702 if ((damage != 0 || (targ.damageforcescale && force))
703 && targ.event_damage)
704 targ.event_damage(targ, inflictor, attacker, damage, deathtype, weaponentity, hitloc, force);
705
706 // apply mirror damage if any
707 if ((!autocvar_g_mirrordamage_onlyweapons || DEATH_WEAPONOF(deathtype) != WEP_Null)
708 && (mirrordamage > 0 || mirrorforce > 0))
709 {
710 attacker = attacker_save;
711
712 force = normalize(attacker.origin + attacker.view_ofs - hitloc) * mirrorforce;
713 Damage(attacker, inflictor, attacker, mirrordamage, DEATH_MIRRORDAMAGE.m_id, weaponentity, attacker.origin, force);
714 }
715}
716
717// Returns total damage applied to creatures
719 entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker,
720 float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe,
721 bool inflictorselfdamage, float forceintensity, vector forcexyzscale,
722 int deathtype, .entity weaponentity, entity directhitentity)
723{
725 {
726 backtrace("RadiusDamage called recursively! Expect stuff to go HORRIBLY wrong.");
727 return 0;
728 }
729
730 if (rad < 0)
731 rad = 0;
732
734
735 float tfloordmg = autocvar_g_throughfloor_damage;
736 float tfloorforce = autocvar_g_throughfloor_force;
737
738 float stat_damagedone = 0;
739 float total_damage_to_creatures = 0;
740
741 vector force;
742 if (!(deathtype & (HITTYPE_SOUND | HITTYPE_SPAM))) // do not send bandwidth-hogging radial spam attacks
743 {
744 force = inflictorvelocity;
745 if (force == '0 0 0')
746 force = '0 0 -1';
747 else
748 force = normalize(force);
749 if (forceintensity >= 0)
750 Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
751 else
752 Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
753 }
754
755 entity targ = WarpZone_FindRadius(inflictororigin, rad + MAX_DAMAGEEXTRARADIUS, false);
756 for (; targ; targ = targ.chain)
757 if ((targ != inflictor || inflictorselfdamage)
758 && (((cantbe != targ) && !mustbe) || mustbe == targ)
759 && targ.takedamage)
760 {
761 // calculate distance from nearest point on target to nearest point on inflictor
762 // instead of origin to ensure full damage on impacts
763
764 vector nearest = targ.WarpZone_findradius_nearest;
765
766 // optimize code by getting inflictororigin_wz from WarpZone_FindRadius calculations instead of
767 //vector inflictororigin_wz = WarpZone_TransformOrigin(targ, inflictororigin);
768
769 vector inflictororigin_wz = targ.WarpZone_findradius_nearest + targ.WarpZone_findradius_dist;
770 vector inflictornearest = NearestPointOnBoundingBox(
771 inflictororigin_wz + inflictor.mins, inflictororigin_wz + inflictor.maxs, nearest);
772 vector diff = inflictornearest - nearest;
773
774 // round up a little on the damage to ensure full damage on impacts
775 // and turn the distance into a fraction of the radius
776 float dist = max(0, vlen(diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS));
777 if (dist <= rad)
778 {
779 float f = (rad > 0) ? 1 - (dist / rad) : 1;
780 // at this point f can't be < 0 or > 1
781 float finaldmg = coredamage * f + edgedamage * (1 - f);
782 if (finaldmg > 0)
783 {
784 vector hitloc;
785 float a, c;
786
787 // if it's a player, use the view origin as reference
788 vector center = CENTER_OR_VIEWOFS(targ);
789
791 {
792 if (targ != attacker) // always use target's bbox centerpoint
793 center = targ.origin + ((targ.mins + targ.maxs) * 0.5);
794 else // targ == attacker
795 {
796 #if 0
797 // code stolen from W_SetupShot_Dir_ProjectileSize_Range()
798 vector md = targ.(weaponentity).movedir;
799 vector dv = v_right * -md.y + v_up * md.z;
800 vector mi = '0 0 0', ma = '0 0 0';
801
802 if (IS_CLIENT(targ)) // no antilag for non-clients!
803 {
804 if (CS(targ).antilag_debug)
805 tracebox_antilag(targ, center, mi, ma, center + dv, MOVE_NORMAL, targ, CS(targ).antilag_debug);
806 else
807 tracebox_antilag(targ, center, mi, ma, center + dv, MOVE_NORMAL, targ, ANTILAG_LATENCY(targ));
808 }
809 else
810 tracebox(center, mi, ma, center + dv, MOVE_NORMAL, targ);
811
812 center.z = trace_endpos.z;
813 #else
814 // very cheap way but it skips move into solid checks which is fine most of the time for now AFAIK
815 // this should only really be an issue with some rare edge cases where
816 // shot origin was prevented from going into a ceiling but it still explodes at the ceiling
817 // shot origin wasn't raised as high as possible and the shooter gets upwards knockback
818 // TL;DR: no bugs if vertical shot origin is always within player bbox
819 center.z += targ.(weaponentity).movedir.z;
820 #endif
821 }
822 }
823
824 /* debug prints
825 print(sprintf("origin vec %v\n", targ.origin));
826 print(sprintf("movedir vec %v\n", targ.(weaponentity).movedir));
827 print(sprintf("old def vec %v\n", CENTER_OR_VIEWOFS(targ)));
828 print(sprintf("origin+vofs %v\n", targ.origin + targ.view_ofs));
829 print(sprintf("bbox center %v\n", (targ.origin + ((targ.mins + targ.maxs) * 0.5))));
830 print(sprintf("center vec %v\n", center));
831 print(sprintf("shotorg vec %v\n", w_shotorg));
832 print("\n");
833 */
834
835 force = normalize(center - inflictororigin_wz);
836 force *= (finaldmg / max(coredamage, edgedamage)) * forceintensity;
837 hitloc = nearest;
838
839 // apply special force scalings
840 if (forcexyzscale.x)
841 force.x *= forcexyzscale.x;
842 if (forcexyzscale.y)
843 force.y *= forcexyzscale.y;
844 if (forcexyzscale.z)
845 force.z *= forcexyzscale.z;
846
847 if (targ != directhitentity)
848 {
849 float hits;
850 float total;
851 float hitratio;
852 float mininv_f, mininv_d;
853
854 // test line of sight to multiple positions on box,
855 // and do damage if any of them hit
856 hits = 0;
857
858 // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
859 // so for a given max stddev:
860 // n = (1 / (2 * max stddev of hitratio))^2
861
862 mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
863 mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
864
866 LOG_INFOF("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f);
867
868 total = 0.25 * (max(mininv_f, mininv_d) ** 2);
869
871 LOG_INFOF(" steps=%f", total);
872
873 if (IS_PLAYER(targ))
875 else
877
879 LOG_INFOF(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total)));
880
881 for (c = 0; c < total; ++c)
882 {
883 //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
884 WarpZone_TraceLine(inflictororigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
885 if (trace_fraction == 1 || trace_ent == targ)
886 {
887 ++hits;
888 if (hits > 1)
889 hitloc += nearest;
890 else
891 hitloc = nearest;
892 }
893 nearest.x = targ.origin.x + targ.mins.x + random() * targ.size.x;
894 nearest.y = targ.origin.y + targ.mins.y + random() * targ.size.y;
895 nearest.z = targ.origin.z + targ.mins.z + random() * targ.size.z;
896 }
897
898 nearest = hitloc * (1 / max(1, hits));
899 hitratio = hits / total;
900 a = bound(0, tfloordmg + (1 - tfloordmg) * hitratio, 1);
901 finaldmg *= a;
902 a = bound(0, tfloorforce + (1 - tfloorforce) * hitratio, 1);
903 force *= a;
904
906 LOG_INFOF(" D=%f F=%f", finaldmg, vlen(force));
907
908 /*if (targ == attacker)
909 {
910 print("hits ", ftos(hits), " / ", ftos(total));
911 print(" finaldmg ", ftos(finaldmg), " force ", ftos(vlen(force)));
912 print(" (", vtos(force), ") (", ftos(a), ")\n");
913 }*/
914 }
915
916 if (finaldmg || force)
917 {
918 if (targ.iscreature)
919 {
920 total_damage_to_creatures += finaldmg;
921
922 if (accuracy_isgooddamage(attacker, targ))
923 stat_damagedone += finaldmg;
924 }
925
926 if (targ == directhitentity || DEATH_ISSPECIAL(deathtype))
927 Damage(targ, inflictor, attacker, finaldmg, deathtype, weaponentity, nearest, force);
928 else
929 Damage(targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, weaponentity, nearest, force);
930 }
931 }
932 }
933 }
934
935 RadiusDamage_running = false;
936
937 if (!DEATH_ISSPECIAL(deathtype))
938 accuracy_add(attacker, DEATH_WEAPONOF(deathtype), 0, min(max(coredamage, edgedamage), stat_damagedone), 0); // add to hit
939
940 return total_damage_to_creatures;
941}
942
944 entity inflictor, entity attacker,
945 float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe,
946 float forceintensity,
947 int deathtype, .entity weaponentity, entity directhitentity)
948{
950 inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker,
951 coredamage, edgedamage, rad, cantbe, mustbe,
952 false, forceintensity, '1 1 1',
953 deathtype, weaponentity, directhitentity
954 );
955}
956
957bool Heal(entity targ, entity inflictor, float amount, float limit)
958{
959 // TODO: mutator hook to control healing
960 if (game_stopped
961 || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR)
962 || STAT(FROZEN, targ) || IS_DEAD(targ))
963 return false;
964
965 bool healed = (targ.event_heal)
966 ? targ.event_heal(targ, inflictor, amount, limit)
967 : false;
968 // TODO: additional handling? what if the healing kills them? should this abort if healing would do so etc
969 // TODO: healing fx!
970 // TODO: armor healing?
971 return healed;
972}
973
974float Fire_AddDamage(entity e, entity o, float d, float t, float dt)
975{
976 if (d <= 0)
977 return -1;
978 if (IS_PLAYER(e) && IS_DEAD(e))
979 return -1;
980
981 t = max(t, 0.1);
982 float dps = d / t;
983
984 if (StatusEffects_active(STATUSEFFECT_Burning, e))
985 {
986 float fireendtime = StatusEffects_gettime(STATUSEFFECT_Burning, e);
987
988 float mintime = fireendtime - time;
989 float maxtime = max(mintime, t);
990
991 float mindps = e.fire_damagepersec;
992 float maxdps = max(mindps, dps);
993
994 if (maxtime > mintime || maxdps > mindps)
995 {
996 // Constraints:
997
998 // damage we have right now
999 float mindamage = mindps * mintime;
1000
1001 // damage we want to get
1002 float maxdamage = mindamage + d;
1003
1004 // but we can't exceed maxtime * maxdps!
1005 float totaldamage = min(maxdamage, maxtime * maxdps);
1006
1007 // LEMMA:
1008 // Look at:
1009 // totaldamage = min(mindamage + d, maxtime * maxdps)
1010 // We see:
1011 // totaldamage <= maxtime * maxdps
1012 // ==> totaldamage / maxdps <= maxtime.
1013 // We also see:
1014 // totaldamage / mindps = min(mindamage / mindps + d, maxtime * maxdps / mindps)
1015 // >= min(mintime, maxtime)
1016 // ==> totaldamage / maxdps >= mintime.
1017
1018 /*
1019 // how long do we damage then?
1020 // at least as long as before
1021 // but, never exceed maxdps
1022 totaltime = max(mintime, totaldamage / maxdps); // always <= maxtime due to lemma
1023 */
1024
1025 // alternate:
1026 // at most as long as maximum allowed
1027 // but, never below mindps
1028 float totaltime = min(maxtime, totaldamage / mindps); // always >= mintime due to lemma
1029
1030 // assuming t > mintime, dps > mindps:
1031 // we get d = t * dps = maxtime * maxdps
1032 // totaldamage = min(maxdamage, maxtime * maxdps) = min(... + d, maxtime * maxdps) = maxtime * maxdps
1033 // totaldamage / maxdps = maxtime
1034 // totaldamage / mindps > totaldamage / maxdps = maxtime
1035 // FROM THIS:
1036 // a) totaltime = max(mintime, maxtime) = maxtime
1037 // b) totaltime = min(maxtime, totaldamage / maxdps) = maxtime
1038
1039 // assuming t <= mintime:
1040 // we get maxtime = mintime
1041 // a) totaltime = max(mintime, ...) >= mintime, also totaltime <= maxtime by the lemma, therefore totaltime = mintime = maxtime
1042 // b) totaltime = min(maxtime, ...) <= maxtime, also totaltime >= mintime by the lemma, therefore totaltime = mintime = maxtime
1043
1044 // assuming dps <= mindps:
1045 // we get mindps = maxdps.
1046 // With this, the lemma says that mintime <= totaldamage / mindps = totaldamage / maxdps <= maxtime.
1047 // a) totaltime = max(mintime, totaldamage / maxdps) = totaldamage / maxdps
1048 // b) totaltime = min(maxtime, totaldamage / mindps) = totaldamage / maxdps
1049
1050 e.fire_damagepersec = totaldamage / totaltime;
1051 StatusEffects_apply(STATUSEFFECT_Burning, e, time + totaltime, 0);
1052 if (totaldamage > 1.2 * mindamage)
1053 {
1054 e.fire_deathtype = dt;
1055 if (e.fire_owner != o)
1056 {
1057 e.fire_owner = o;
1058 e.fire_hitsound = false;
1059 }
1060 }
1061 if (accuracy_isgooddamage(o, e))
1062 accuracy_add(o, DEATH_WEAPONOF(dt), 0, max(0, totaldamage - mindamage), 0); // add to hit
1063 return max(0, totaldamage - mindamage); // can never be negative, but to make sure
1064 }
1065 else
1066 return 0;
1067 }
1068 else
1069 {
1070 e.fire_damagepersec = dps;
1071 StatusEffects_apply(STATUSEFFECT_Burning, e, time + t, 0);
1072 e.fire_deathtype = dt;
1073 e.fire_owner = o;
1074 e.fire_hitsound = false;
1075 if (accuracy_isgooddamage(o, e))
1076 accuracy_add(o, DEATH_WEAPONOF(dt), 0, d, 0); // add to hit
1077 return d;
1078 }
1079}
1080
1082{
1083 entity o;
1084 float t;
1085 for (t = 0, o = e.owner; o.owner && t < 16; o = o.owner, ++t);
1086 if (IS_NOT_A_CLIENT(o))
1087 o = e.fire_owner;
1088
1089 float fireendtime = StatusEffects_gettime(STATUSEFFECT_Burning, e);
1090 t = min(frametime, fireendtime - time);
1091 float d = e.fire_damagepersec * t;
1092
1093 float hi = e.fire_owner.hitsound_damage_dealt;
1094 float ty = e.fire_owner.typehitsound;
1095 Damage(e, e, e.fire_owner, d, e.fire_deathtype, DMG_NOWEP, e.origin, '0 0 0');
1096 if (e.fire_hitsound && e.fire_owner)
1097 {
1098 e.fire_owner.hitsound_damage_dealt = hi;
1099 e.fire_owner.typehitsound = ty;
1100 }
1101 e.fire_hitsound = true;
1102
1103 if (!IS_INDEPENDENT_PLAYER(e) && !STAT(FROZEN, e) && !StatusEffects_active(STATUSEFFECT_Frozen, e))
1104 IL_EACH(g_damagedbycontents, it.damagedbycontents && it != e,
1105 {
1106 if (!IS_DEAD(it) && it.takedamage && !IS_INDEPENDENT_PLAYER(it)
1107 && boxesoverlap(e.absmin, e.absmax, it.absmin, it.absmax))
1108 {
1109 t = autocvar_g_balance_firetransfer_time * (fireendtime - time);
1110 d = autocvar_g_balance_firetransfer_damage * e.fire_damagepersec * t;
1111 Fire_AddDamage(it, o, d, t, DEATH_FIRE.m_id);
1112 }
1113 });
1114}
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
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
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
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.
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:42
virtual void wr_suicidemessage()
(SERVER) notification number for suicide message (may inspect w_deathtype for details)
Definition weapon.qh:118
string netname
M: refname : reference name name.
Definition weapon.qh:84
virtual void wr_killmessage()
(SERVER) notification number for kill message (may inspect w_deathtype for details)
Definition weapon.qh:123
float cnt
Definition powerups.qc:24
float ping
Definition main.qh:169
bool warmup_stage
Definition main.qh:120
#define M_ARGV(x, type)
Definition events.qh:17
bool IsFlying(entity this)
Definition player.qc:843
#define IS_NOT_A_CLIENT(s)
Definition player.qh:243
#define IS_CLIENT(s)
Definition player.qh:241
#define IS_DEAD(s)
Definition player.qh:244
#define PHYS_INPUT_BUTTON_CHAT(s)
Definition player.qh:161
#define IS_PLAYER(s)
Definition player.qh:242
float game_stopped
Definition stats.qh:81
vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage)
Definition util.qc:1413
const int FL_GODMODE
Definition constants.qh:75
const int FRAGS_SPECTATOR
Definition constants.qh:4
vector v_up
const float MOVE_NOMONSTERS
entity trace_ent
float frametime
const float MOVE_NORMAL
float time
vector v_right
vector trace_endpos
float trace_fraction
void UpdateCSQCProjectile(entity e)
#define g_cts
Definition cts.qh:36
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition damage.qc:493
bool Heal(entity targ, entity inflictor, float amount, float limit)
Definition damage.qc:957
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:943
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:165
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:117
float Fire_AddDamage(entity e, entity o, float d, float t, float dt)
Definition damage.qc:974
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:210
float RadiusDamageForSource(entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, bool inflictorselfdamage, float forceintensity, vector forcexyzscale, int deathtype,.entity weaponentity, entity directhitentity)
Definition damage.qc:718
void Fire_ApplyDamage(entity e)
Definition damage.qc:1081
string AppendItemcodes(string s, entity player)
Definition damage.qc:81
void Obituary(entity attacker, entity inflictor, entity targ, int deathtype,.entity weaponentity)
Definition damage.qc:222
void LogDeath(string mode, int deathtype, entity killer, entity killed)
Definition damage.qc:100
bool RadiusDamage_running
Definition damage.qh:111
bool autocvar_g_mirrordamage_virtual
Definition damage.qh:15
bool autocvar_g_mirrordamage_onlyweapons
Definition damage.qh:16
float yoda
Definition damage.qh:48
int autocvar_g_player_damageplayercenter
Definition damage.qh:106
float autocvar_g_throughfloor_force_max_stddev
Definition damage.qh:9
string deathmessage
Definition damage.qh:73
float autocvar_g_friendlyfire_virtual_force
Definition damage.qh:28
float autocvar_g_throughfloor_damage_max_stddev
Definition damage.qh:8
float autocvar_g_balance_armor_blockpercent
Definition damage.qh:21
IntrusiveList g_damagedbycontents
Definition damage.qh:143
float autocvar_g_throughfloor_force
Definition damage.qh:7
int impressive_hits
Definition damage.qh:49
#define DMG_NOWEP
Definition damage.qh:104
float autocvar_g_balance_selfdamagepercent
Definition damage.qh:25
bool autocvar_g_throughfloor_debug
Definition damage.qh:5
float checkrules_firstblood
Definition damage.qh:43
float autocvar_g_friendlyfire_virtual
Definition damage.qh:27
float autocvar_g_teamdamage_threshold
Definition damage.qh:23
float autocvar_g_throughfloor_min_steps_other
Definition damage.qh:12
float teamkill_complain
Definition damage.qh:54
float autocvar_g_friendlyfire
Definition damage.qh:26
float autocvar_g_throughfloor_min_steps_player
Definition damage.qh:10
float autocvar_g_balance_damagepush_speedfactor
Definition damage.qh:18
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
float autocvar_g_mirrordamage
Definition damage.qh:14
bool autocvar_g_teamkill_punishing
Definition damage.qh:24
void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner)
const float MAX_DAMAGEEXTRARADIUS
int w_deathtype
const float MIN_DAMAGEEXTRARADIUS
string Deathtype_Name(int deathtype)
Definition all.qc:3
#define DEATH_ENT(t)
Definition all.qh:41
#define DEATH_ISWEAPON(t, w)
Definition all.qh:46
const int DT_FIRST
Definition all.qh:37
#define DEATH_ISSPECIAL(t)
Definition all.qh:39
const int HITTYPE_SPAM
Definition all.qh:34
const int HITTYPE_SPLASH
Definition all.qh:30
#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
#define strstrofs
RES_ARMOR
Definition ent_cs.qc:130
void GameLogEcho(string s)
Definition gamelog.qc:15
bool autocvar_sv_eventlog
Definition gamelog.qh:3
#define IL_EACH(this, cond, body)
#define STAT(...)
Definition stats.qh:82
entity WarpZone_FindRadius(vector org, float rad, bool needlineofsight)
Definition common.qc:684
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:544
#define LOG_TRACEF(...)
Definition log.qh:77
#define backtrace(msg)
Definition log.qh:99
#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 vlen(vector v)
float sqrt(float f)
float min(float f,...)
vector normalize(vector v)
string ftos(float f)
float max(float f,...)
#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
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:366
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:471
f1
Definition all.inc:563
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
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
#define KILL_SPREE_LIST
Definition all.qh:491
entity Notification
always last
Definition all.qh:81
#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 PlayerStats_GameReport_Event_Player(ent, eventid, val)
#define NULL
Definition post.qh:14
#define REGISTRY_GET(id, i)
Definition registry.qh:43
#define setthink(e, f)
vector
Definition self.qh:92
string NearestLocation(vector p)
Definition chat.qc:446
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)
float StatusEffects_gettime(StatusEffect this, entity actor)
bool StatusEffects_active(StatusEffect this, entity actor)
void StatusEffects_apply(StatusEffect this, entity actor, float eff_time, int eff_flags)
const int DAMAGE_AIM
Definition subs.qh:81
entity buff_FirstFromFlags(entity actor)
Definition sv_buffs.qc:288
Header file that describes the resource system.
#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
void vehicles_exit(entity vehic, bool eject)
const int VHEF_RELEASE
Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (n...
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:25
#define IS_MONSTER(v)
Definition utils.qh:23
#define IS_VEHICLE(v)
Definition utils.qh:24
#define CENTER_OR_VIEWOFS(ent)
Definition utils.qh:31
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15
ERASEABLE vector NearestPointOnBoundingBox(vector mi, vector ma, vector org)
Definition vector.qh:194
const int MAX_WEAPONSLOTS
Definition weapon.qh:16
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
float autocvar_g_weaponforcefactor
float autocvar_g_weapondamagefactor
Weapon m_weapon
Definition wepent.qh:26