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 bool 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 bool 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, false, 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, false, 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, false, 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 bool msg_from_ent = (DEATH_ENT(deathtype) == DEATH_HURTTRIGGER && inflictor && inflictor.message2 != "");
421 Obituary_SpecialDeath(targ, true, msg_from_ent, deathtype,
422 targ.netname,
423 attacker_name,
424 (msg_from_ent ? inflictor.message2 : deathlocation),
425 (msg_from_ent ? deathlocation : ""),
426 CS(targ).killcount, kill_count_to_attacker, 0
427 );
428 }
429 }
430 }
431
432 // =============
433 // ACCIDENT/TRAP
434 // =============
435 else
436 {
437 switch (DEATH_ENT(deathtype))
438 {
439 // For now, we're just forcing HURTTRIGGER to behave as "DEATH_VOID" and giving it no special options...
440 // Later on you will only be able to make custom messages using DEATH_CUSTOM,
441 // and there will be a REAL DEATH_VOID implementation which mappers will use.
442 case DEATH_HURTTRIGGER:
443 {
444 bool msg_from_ent = (inflictor && inflictor.message != "");
445 Obituary_SpecialDeath(targ, false, msg_from_ent, deathtype,
446 targ.netname,
447 (msg_from_ent ? inflictor.message : deathlocation),
448 (msg_from_ent ? deathlocation : ""),
449 "",
450 CS(targ).killcount, 0, 0
451 );
452 break;
453 }
454 case DEATH_CUSTOM:
455 Obituary_SpecialDeath(targ, false, false, deathtype,
456 targ.netname,
457 ((strstrofs(deathmessage, "%", 0) < 0) ? strcat("%s ", deathmessage) : deathmessage),
458 deathlocation,
459 "",
460 CS(targ).killcount, 0, 0
461 );
462 break;
463 default:
464 Obituary_SpecialDeath(targ, false, false, deathtype, targ.netname, deathlocation, "", "", CS(targ).killcount, 0, 0);
465 break;
466 }
467
468 LogDeath("accident", deathtype, targ, targ);
469 GiveFrags(targ, targ, -1, deathtype, weaponentity);
470
471 if (GameRules_scoring_add(targ, SCORE, 0) == -5)
472 {
473 Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE);
474 if (!warmup_stage)
476 }
477 }
478
479 // reset target kill count
480 CS(targ).killcount = 0;
481}
482
483void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
484{
485 if (game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR))
486 return;
487
488 // special rule: gravity bombs and sound-based attacks do not affect team mates (other than for disconnecting the hook)
489 if ((DEATH_ISWEAPON(deathtype, WEP_HOOK) || (deathtype & HITTYPE_SOUND))
490 && IS_PLAYER(targ) && SAME_TEAM(targ, attacker))
491 return;
492
493 entity attacker_save = attacker;
494
495 float complainteamdamage = 0;
496 float mirrordamage = 0;
497 float mirrorforce = 0;
498
499 if (deathtype == DEATH_KILL.m_id || deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
500 {
501 // exit the vehicle before killing (fixes a crash)
502 if (IS_PLAYER(targ) && targ.vehicle)
503 vehicles_exit(targ.vehicle, VHEF_RELEASE);
504
505 // These are ALWAYS lethal
506 // No damage modification here
507 // Instead, prepare the victim for their death...
508 if (deathtype == DEATH_TEAMCHANGE.m_id || deathtype == DEATH_AUTOTEAMCHANGE.m_id)
509 {
511 SetResourceExplicit(targ, RES_HEALTH, 0.9); // this is < 1
512 }
513 StatusEffects_remove(STATUSEFFECT_SpawnShield, targ, STATUSEFFECT_REMOVE_CLEAR);
514 targ.flags &= ~FL_GODMODE;
515 damage = 100000;
516 }
517 else if (deathtype == DEATH_MIRRORDAMAGE.m_id || deathtype == DEATH_NOAMMO.m_id)
518 {
519 // no processing
520 }
521 else
522 {
523 // nullify damage if teamplay is on
524 if (deathtype != DEATH_TELEFRAG.m_id
525 && IS_PLAYER(attacker))
526 {
527 // avoid dealing damage or force to other independent players
528 // and avoid dealing damage or force to things owned by other independent players
529 if ((IS_PLAYER(targ) && targ != attacker && (IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(targ)))
530 || (targ.realowner && IS_INDEPENDENT_PLAYER(targ.realowner) && attacker != targ.realowner))
531 {
532 damage = 0;
533 force = '0 0 0';
534 }
535 else if (!STAT(FROZEN, targ) && SAME_TEAM(attacker, targ))
536 {
537 if (autocvar_teamplay_mode == 1)
538 damage = 0;
539 else if (attacker != targ)
540 {
541 if (autocvar_teamplay_mode == 2)
542 {
543 if (IS_PLAYER(targ) && !IS_DEAD(targ))
544 {
545 attacker.dmg_team += damage;
546 complainteamdamage = attacker.dmg_team - autocvar_g_teamdamage_threshold;
547 }
548 }
549 else if (autocvar_teamplay_mode == 3)
550 damage = 0;
551 else if (autocvar_teamplay_mode == 4)
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 if (complainteamdamage > 0)
558 mirrordamage = autocvar_g_mirrordamage * complainteamdamage;
559 mirrorforce = autocvar_g_mirrordamage * vlen(force);
560 damage = autocvar_g_friendlyfire * damage;
561 // mirrordamage will be used LATER
562
564 {
566 attacker.dmg_take += v.x;
567 attacker.dmg_save += v.y;
568 attacker.dmg_inflictor = inflictor;
569 mirrordamage = v.z;
570 mirrorforce = 0;
571 }
572
574 {
576 targ.dmg_take += v.x;
577 targ.dmg_save += v.y;
578 targ.dmg_inflictor = inflictor;
579 damage = 0;
581 force = '0 0 0';
582 }
583 }
584 else if (!targ.canteamdamage)
585 damage = 0;
586 }
587 }
588 }
589 }
590
591 if (!DEATH_ISSPECIAL(deathtype))
592 {
594 mirrordamage *= autocvar_g_weapondamagefactor;
595 complainteamdamage *= autocvar_g_weapondamagefactor;
597 mirrorforce *= autocvar_g_weaponforcefactor;
598 }
599
600 // should this be changed at all? If so, in what way?
601 MUTATOR_CALLHOOK(Damage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force, attacker.(weaponentity));
602 damage = M_ARGV(4, float);
603 mirrordamage = M_ARGV(5, float);
604 force = M_ARGV(6, vector);
605
606 if (IS_PLAYER(targ) && damage > 0 && attacker)
607 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
608 {
609 .entity went = weaponentities[slot];
610 if (targ.(went).hook && targ.(went).hook.aiment == attacker)
611 RemoveHook(targ.(went).hook);
612 }
613
614 if (targ == attacker)
615 damage *= autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
616
617 // count the damage
618 if (attacker && targ != attacker
619 && !IS_DEAD(targ)
620 && deathtype != DEATH_BUFF_INFERNO.m_id
621 && deathtype != DEATH_BUFF_VENGEANCE.m_id
622 && targ.takedamage == DAMAGE_AIM)
623 {
624 entity victim;
625 if (IS_VEHICLE(targ) && targ.owner)
626 victim = targ.owner;
627 else
628 victim = targ;
629
630 // TODO: allow the mutator hook to tell if the hit sound should be team or not
631 if (IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim)
632 || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
633 {
634 if (DIFF_TEAM(victim, attacker))
635 {
636 if (damage > 0)
637 {
638 if (deathtype != DEATH_FIRE.m_id)
639 {
640 if (PHYS_INPUT_BUTTON_CHAT(victim))
641 ++attacker.typehitsound;
642 else
643 attacker.hitsound_damage_dealt += damage;
644 }
645
647
648 if (!DEATH_ISSPECIAL(deathtype)
649 && IS_PLAYER(targ) // don't do this for vehicles
650 && IsFlying(victim))
651 yoda = 1;
652 }
653 }
654 else if (IS_PLAYER(attacker) && !STAT(FROZEN, victim) && !(deathtype & HITTYPE_SPAM)) // same team
655 {
656 if (deathtype != DEATH_FIRE.m_id)
657 ++attacker.typehitsound;
658 if (complainteamdamage > 0
659 && time > CS(attacker).teamkill_complain)
660 {
661 CS(attacker).teamkill_complain = time + 5;
662 CS(attacker).teamkill_soundtime = time + 0.4;
663 CS(attacker).teamkill_soundsource = targ;
664 }
665 }
666 }
667 }
668 }
669
670 // apply push
671 if (targ.damageforcescale && force
672 && (!IS_PLAYER(targ) || !StatusEffects_active(STATUSEFFECT_SpawnShield, targ) || targ == attacker))
673 {
674 vector farce = damage_explosion_calcpush(targ.damageforcescale * force, targ.velocity, autocvar_g_balance_damagepush_speedfactor);
675 if (targ.move_movetype == MOVETYPE_PHYSICS)
676 {
677 entity farcent = new(farce);
678 farcent.enemy = targ;
679 farcent.movedir = farce * 10;
680 if (targ.mass)
681 farcent.movedir *= targ.mass;
682 farcent.origin = hitloc;
683 farcent.forcetype = FORCETYPE_FORCEATPOS;
684 farcent.nextthink = time + 0.1;
685 setthink(farcent, SUB_Remove);
686 }
687 else if (targ.move_movetype != MOVETYPE_NOCLIP)
688 targ.velocity += farce;
689 UNSET_ONGROUND(targ);
691 }
692 // apply damage
693 if ((damage != 0 || (targ.damageforcescale && force))
694 && targ.event_damage)
695 targ.event_damage(targ, inflictor, attacker, damage, deathtype, weaponentity, hitloc, force);
696
697 // apply mirror damage if any
698 if ((!autocvar_g_mirrordamage_onlyweapons || DEATH_WEAPONOF(deathtype) != WEP_Null)
699 && (mirrordamage > 0 || mirrorforce > 0))
700 {
701 attacker = attacker_save;
702
703 force = normalize(attacker.origin + attacker.view_ofs - hitloc) * mirrorforce;
704 Damage(attacker, inflictor, attacker, mirrordamage, DEATH_MIRRORDAMAGE.m_id, weaponentity, attacker.origin, force);
705 }
706}
707
708// Returns total damage applied to creatures
710 entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker,
711 float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe,
712 bool inflictorselfdamage, float forceintensity, vector forcexyzscale,
713 int deathtype, .entity weaponentity, entity directhitentity)
714{
716 {
717 backtrace("RadiusDamage called recursively! Expect stuff to go HORRIBLY wrong.");
718 return 0;
719 }
720
721 if (rad < 0)
722 rad = 0;
723
725
726 float tfloordmg = autocvar_g_throughfloor_damage;
727 float tfloorforce = autocvar_g_throughfloor_force;
728
729 float stat_damagedone = 0;
730 float total_damage_to_creatures = 0;
731
732 vector force;
733 if (!(deathtype & (HITTYPE_SOUND | HITTYPE_SPAM))) // do not send bandwidth-hogging radial spam attacks
734 {
735 force = inflictorvelocity;
736 if (force == '0 0 0')
737 force = '0 0 -1';
738 else
739 force = normalize(force);
740 if (forceintensity >= 0)
741 Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
742 else
743 Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
744 }
745
746 entity targ = WarpZone_FindRadius(inflictororigin, rad + MAX_DAMAGEEXTRARADIUS, false);
747 for (; targ; targ = targ.chain)
748 if ((targ != inflictor || inflictorselfdamage)
749 && (((cantbe != targ) && !mustbe) || mustbe == targ)
750 && targ.takedamage)
751 {
752 // calculate distance from nearest point on target to nearest point on inflictor
753 // instead of origin to ensure full damage on impacts
754
755 vector nearest = targ.WarpZone_findradius_nearest;
756
757 // optimize code by getting inflictororigin_wz from WarpZone_FindRadius calculations instead of
758 //vector inflictororigin_wz = WarpZone_TransformOrigin(targ, inflictororigin);
759
760 vector inflictororigin_wz = targ.WarpZone_findradius_nearest + targ.WarpZone_findradius_dist;
761 vector inflictornearest = NearestPointOnBoundingBox(
762 inflictororigin_wz + inflictor.mins, inflictororigin_wz + inflictor.maxs, nearest);
763 vector diff = inflictornearest - nearest;
764
765 // round up a little on the damage to ensure full damage on impacts
766 // and turn the distance into a fraction of the radius
767 float dist = max(0, vlen(diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS));
768 if (dist <= rad)
769 {
770 float f = (rad > 0) ? 1 - (dist / rad) : 1;
771 // at this point f can't be < 0 or > 1
772 float finaldmg = coredamage * f + edgedamage * (1 - f);
773 if (finaldmg > 0)
774 {
775 vector hitloc;
776 float a, c;
777
778 // if it's a player, use the view origin as reference
779 vector center = CENTER_OR_VIEWOFS(targ);
780
782 {
783 if (targ != attacker) // always use target's bbox centerpoint
784 center = targ.origin + ((targ.mins + targ.maxs) * 0.5);
785 else // targ == attacker
786 {
787 #if 0
788 // code stolen from W_SetupShot_Dir_ProjectileSize_Range()
789 vector md = targ.(weaponentity).movedir;
790 vector dv = v_right * -md.y + v_up * md.z;
791 vector mi = '0 0 0', ma = '0 0 0';
792
793 if (IS_CLIENT(targ)) // no antilag for non-clients!
794 {
795 if (CS(targ).antilag_debug)
796 tracebox_antilag(targ, center, mi, ma, center + dv, MOVE_NORMAL, targ, CS(targ).antilag_debug);
797 else
798 tracebox_antilag(targ, center, mi, ma, center + dv, MOVE_NORMAL, targ, ANTILAG_LATENCY(targ));
799 }
800 else
801 tracebox(center, mi, ma, center + dv, MOVE_NORMAL, targ);
802
803 center.z = trace_endpos.z;
804 #else
805 // very cheap way but it skips move into solid checks which is fine most of the time for now AFAIK
806 // this should only really be an issue with some rare edge cases where
807 // shot origin was prevented from going into a ceiling but it still explodes at the ceiling
808 // shot origin wasn't raised as high as possible and the shooter gets upwards knockback
809 // TL;DR: no bugs if vertical shot origin is always within player bbox
810 center.z += targ.(weaponentity).movedir.z;
811 #endif
812 }
813 }
814
815 /* debug prints
816 print(sprintf("origin vec %v\n", targ.origin));
817 print(sprintf("movedir vec %v\n", targ.(weaponentity).movedir));
818 print(sprintf("old def vec %v\n", CENTER_OR_VIEWOFS(targ)));
819 print(sprintf("origin+vofs %v\n", targ.origin + targ.view_ofs));
820 print(sprintf("bbox center %v\n", (targ.origin + ((targ.mins + targ.maxs) * 0.5))));
821 print(sprintf("center vec %v\n", center));
822 print(sprintf("shotorg vec %v\n", w_shotorg));
823 print("\n");
824 */
825
826 force = normalize(center - inflictororigin_wz);
827 force *= (finaldmg / max(coredamage, edgedamage)) * forceintensity;
828 hitloc = nearest;
829
830 // apply special force scalings
831 if (forcexyzscale.x)
832 force.x *= forcexyzscale.x;
833 if (forcexyzscale.y)
834 force.y *= forcexyzscale.y;
835 if (forcexyzscale.z)
836 force.z *= forcexyzscale.z;
837
838 if (targ != directhitentity)
839 {
840 float hits;
841 float total;
842 float hitratio;
843 float mininv_f, mininv_d;
844
845 // test line of sight to multiple positions on box,
846 // and do damage if any of them hit
847 hits = 0;
848
849 // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
850 // so for a given max stddev:
851 // n = (1 / (2 * max stddev of hitratio))^2
852
853 mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
854 mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
855
857 LOG_INFOF("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f);
858
859 total = 0.25 * (max(mininv_f, mininv_d) ** 2);
860
862 LOG_INFOF(" steps=%f", total);
863
864 if (IS_PLAYER(targ))
866 else
868
870 LOG_INFOF(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total)));
871
872 for (c = 0; c < total; ++c)
873 {
874 //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
875 WarpZone_TraceLine(inflictororigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
876 if (trace_fraction == 1 || trace_ent == targ)
877 {
878 ++hits;
879 if (hits > 1)
880 hitloc += nearest;
881 else
882 hitloc = nearest;
883 }
884 nearest.x = targ.origin.x + targ.mins.x + random() * targ.size.x;
885 nearest.y = targ.origin.y + targ.mins.y + random() * targ.size.y;
886 nearest.z = targ.origin.z + targ.mins.z + random() * targ.size.z;
887 }
888
889 nearest = hitloc * (1 / max(1, hits));
890 hitratio = hits / total;
891 a = bound(0, tfloordmg + (1 - tfloordmg) * hitratio, 1);
892 finaldmg *= a;
893 a = bound(0, tfloorforce + (1 - tfloorforce) * hitratio, 1);
894 force *= a;
895
897 LOG_INFOF(" D=%f F=%f", finaldmg, vlen(force));
898
899 /*if (targ == attacker)
900 {
901 print("hits ", ftos(hits), " / ", ftos(total));
902 print(" finaldmg ", ftos(finaldmg), " force ", ftos(vlen(force)));
903 print(" (", vtos(force), ") (", ftos(a), ")\n");
904 }*/
905 }
906
907 if (finaldmg || force)
908 {
909 if (targ.iscreature)
910 {
911 total_damage_to_creatures += finaldmg;
912
913 if (accuracy_isgooddamage(attacker, targ))
914 stat_damagedone += finaldmg;
915 }
916
917 if (targ == directhitentity || DEATH_ISSPECIAL(deathtype))
918 Damage(targ, inflictor, attacker, finaldmg, deathtype, weaponentity, nearest, force);
919 else
920 Damage(targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, weaponentity, nearest, force);
921 }
922 }
923 }
924 }
925
926 RadiusDamage_running = false;
927
928 if (!DEATH_ISSPECIAL(deathtype))
929 accuracy_add(attacker, DEATH_WEAPONOF(deathtype), 0, min(max(coredamage, edgedamage), stat_damagedone), 0); // add to hit
930
931 return total_damage_to_creatures;
932}
933
935 entity inflictor, entity attacker,
936 float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe,
937 float forceintensity,
938 int deathtype, .entity weaponentity, entity directhitentity)
939{
941 inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker,
942 coredamage, edgedamage, rad, cantbe, mustbe,
943 false, forceintensity, '1 1 1',
944 deathtype, weaponentity, directhitentity
945 );
946}
947
948bool Heal(entity targ, entity inflictor, float amount, float limit)
949{
950 // TODO: mutator hook to control healing
951 if (game_stopped
952 || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR)
953 || STAT(FROZEN, targ) || IS_DEAD(targ))
954 return false;
955
956 bool healed = (targ.event_heal)
957 ? targ.event_heal(targ, inflictor, amount, limit)
958 : false;
959 // TODO: additional handling? what if the healing kills them? should this abort if healing would do so etc
960 // TODO: healing fx!
961 // TODO: armor healing?
962 return healed;
963}
964
965float Fire_AddDamage(entity e, entity o, float d, float t, float dt)
966{
967 if (d <= 0)
968 return -1;
969 if (IS_PLAYER(e) && IS_DEAD(e))
970 return -1;
971
972 t = max(t, 0.1);
973 float dps = d / t;
974
975 if (StatusEffects_active(STATUSEFFECT_Burning, e))
976 {
977 float fireendtime = StatusEffects_gettime(STATUSEFFECT_Burning, e);
978
979 float mintime = fireendtime - time;
980 float maxtime = max(mintime, t);
981
982 float mindps = e.fire_damagepersec;
983 float maxdps = max(mindps, dps);
984
985 if (maxtime > mintime || maxdps > mindps)
986 {
987 // Constraints:
988
989 // damage we have right now
990 float mindamage = mindps * mintime;
991
992 // damage we want to get
993 float maxdamage = mindamage + d;
994
995 // but we can't exceed maxtime * maxdps!
996 float totaldamage = min(maxdamage, maxtime * maxdps);
997
998 // LEMMA:
999 // Look at:
1000 // totaldamage = min(mindamage + d, maxtime * maxdps)
1001 // We see:
1002 // totaldamage <= maxtime * maxdps
1003 // ==> totaldamage / maxdps <= maxtime.
1004 // We also see:
1005 // totaldamage / mindps = min(mindamage / mindps + d, maxtime * maxdps / mindps)
1006 // >= min(mintime, maxtime)
1007 // ==> totaldamage / maxdps >= mintime.
1008
1009 /*
1010 // how long do we damage then?
1011 // at least as long as before
1012 // but, never exceed maxdps
1013 totaltime = max(mintime, totaldamage / maxdps); // always <= maxtime due to lemma
1014 */
1015
1016 // alternate:
1017 // at most as long as maximum allowed
1018 // but, never below mindps
1019 float totaltime = min(maxtime, totaldamage / mindps); // always >= mintime due to lemma
1020
1021 // assuming t > mintime, dps > mindps:
1022 // we get d = t * dps = maxtime * maxdps
1023 // totaldamage = min(maxdamage, maxtime * maxdps) = min(... + d, maxtime * maxdps) = maxtime * maxdps
1024 // totaldamage / maxdps = maxtime
1025 // totaldamage / mindps > totaldamage / maxdps = maxtime
1026 // FROM THIS:
1027 // a) totaltime = max(mintime, maxtime) = maxtime
1028 // b) totaltime = min(maxtime, totaldamage / maxdps) = maxtime
1029
1030 // assuming t <= mintime:
1031 // we get maxtime = mintime
1032 // a) totaltime = max(mintime, ...) >= mintime, also totaltime <= maxtime by the lemma, therefore totaltime = mintime = maxtime
1033 // b) totaltime = min(maxtime, ...) <= maxtime, also totaltime >= mintime by the lemma, therefore totaltime = mintime = maxtime
1034
1035 // assuming dps <= mindps:
1036 // we get mindps = maxdps.
1037 // With this, the lemma says that mintime <= totaldamage / mindps = totaldamage / maxdps <= maxtime.
1038 // a) totaltime = max(mintime, totaldamage / maxdps) = totaldamage / maxdps
1039 // b) totaltime = min(maxtime, totaldamage / mindps) = totaldamage / maxdps
1040
1041 e.fire_damagepersec = totaldamage / totaltime;
1042 StatusEffects_apply(STATUSEFFECT_Burning, e, time + totaltime, 0);
1043 if (totaldamage > 1.2 * mindamage)
1044 {
1045 e.fire_deathtype = dt;
1046 if (e.fire_owner != o)
1047 {
1048 e.fire_owner = o;
1049 e.fire_hitsound = false;
1050 }
1051 }
1052 if (accuracy_isgooddamage(o, e))
1053 accuracy_add(o, DEATH_WEAPONOF(dt), 0, max(0, totaldamage - mindamage), 0); // add to hit
1054 return max(0, totaldamage - mindamage); // can never be negative, but to make sure
1055 }
1056 else
1057 return 0;
1058 }
1059 else
1060 {
1061 e.fire_damagepersec = dps;
1062 StatusEffects_apply(STATUSEFFECT_Burning, e, time + t, 0);
1063 e.fire_deathtype = dt;
1064 e.fire_owner = o;
1065 e.fire_hitsound = false;
1066 if (accuracy_isgooddamage(o, e))
1067 accuracy_add(o, DEATH_WEAPONOF(dt), 0, d, 0); // add to hit
1068 return d;
1069 }
1070}
1071
1073{
1074 entity o;
1075 float t;
1076 for (t = 0, o = e.owner; o.owner && t < 16; o = o.owner, ++t);
1077 if (IS_NOT_A_CLIENT(o))
1078 o = e.fire_owner;
1079
1080 float fireendtime = StatusEffects_gettime(STATUSEFFECT_Burning, e);
1081 t = min(frametime, fireendtime - time);
1082 float d = e.fire_damagepersec * t;
1083
1084 float hi = e.fire_owner.hitsound_damage_dealt;
1085 float ty = e.fire_owner.typehitsound;
1086 Damage(e, e, e.fire_owner, d, e.fire_deathtype, DMG_NOWEP, e.origin, '0 0 0');
1087 if (e.fire_hitsound && e.fire_owner)
1088 {
1089 e.fire_owner.hitsound_damage_dealt = hi;
1090 e.fire_owner.typehitsound = ty;
1091 }
1092 e.fire_hitsound = true;
1093
1094 if (!IS_INDEPENDENT_PLAYER(e) && !STAT(FROZEN, e) && !StatusEffects_active(STATUSEFFECT_Frozen, e))
1095 IL_EACH(g_damagedbycontents, it.damagedbycontents && it != e,
1096 {
1097 if (!IS_DEAD(it) && it.takedamage && !IS_INDEPENDENT_PLAYER(it)
1098 && boxesoverlap(e.absmin, e.absmax, it.absmin, it.absmax))
1099 {
1100 t = autocvar_g_balance_firetransfer_time * (fireendtime - time);
1101 d = autocvar_g_balance_firetransfer_damage * e.fire_damagepersec * t;
1102 Fire_AddDamage(it, o, d, t, DEATH_FIRE.m_id);
1103 }
1104 });
1105}
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.
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:483
bool Heal(entity targ, entity inflictor, float amount, float limit)
Definition damage.qc:948
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:934
void GiveFrags(entity attacker, entity targ, float f, int deathtype,.entity weaponentity)
Definition damage.qc:43
float Fire_AddDamage(entity e, entity o, float d, float t, float dt)
Definition damage.qc:965
float Obituary_WeaponDeath(entity notif_target, bool murder, int deathtype, string s1, string s2, string s3, float f1, float f2)
Definition damage.qc:165
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:709
void Fire_ApplyDamage(entity e)
Definition damage.qc:1072
void Obituary_SpecialDeath(entity notif_target, bool 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
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:43
#define DEATH_ISWEAPON(t, w)
Definition all.qh:48
const int DT_FIRST
Definition all.qh:39
#define DEATH_ISSPECIAL(t)
Definition all.qh:41
const int HITTYPE_SPAM
set manually after first RadiusDamage, stops effect spam
Definition all.qh:36
const int HITTYPE_SPLASH
automatically set by RadiusDamage
Definition all.qh:32
#define DEATH_WEAPONOF(t)
Definition all.qh:47
const int HITTYPE_SOUND
cause bleeding from ears
Definition all.qh:35
void SUB_Remove(entity this)
Remove entity.
Definition defer.qh:12
const int ACTIVE_ACTIVE
Definition defs.qh:37
const float FORCETYPE_FORCEATPOS
#define strstrofs
RES_ARMOR
Definition ent_cs.qc:155
SetResourceExplicit(ent, RES_ARMOR, ReadByte() *DEC_FACTOR)) ENTCS_PROP(NAME
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:94
entity WarpZone_FindRadius(vector org, float rad, bool needlineofsight)
Definition common.qc:660
void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
Definition common.qc:328
vector WarpZone_UnTransformOrigin(entity wz, vector v)
Definition common.qc:519
#define LOG_TRACEF(...)
Definition log.qh:75
#define backtrace(msg)
Definition log.qh:96
#define LOG_INFOF(...)
Definition log.qh:63
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:146
const int MOVETYPE_NOCLIP
Definition movetypes.qh:141
@ 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:370
spree_inf s1 s2 s3loc s2 s1
Definition all.inc:285
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:475
f1
Definition all.inc:567
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Send_Notification_Core(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:1646
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1500
#define KILL_SPREE_LIST
Definition all.qh:486
bool autocvar_notification_server_allows_location
Definition all.qh:308
entity Notification
always last
Definition all.qh:85
#define APP_TEAM_NUM(num, prefix)
Definition all.qh:88
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:62
#define setthink(e, f)
vector
Definition self.qh:96
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
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:200
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