]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_damage.qc
make the minstagib Nex a separate weapon
[divverent/nexuiz.git] / data / qcsrc / server / g_damage.qc
1
2 float checkrules_firstblood;
3
4 float yoda;
5
6 float IsDifferentTeam(entity a, entity b)
7 {
8         if(teams_matter)
9         {
10                 if(a.team == b.team)
11                         return 0;
12         }
13         else
14         {
15                 if(a == b)
16                         return 0;
17         }
18         return 1;
19 }
20
21 float IsFlying(entity a)
22 {
23         if(a.flags & FL_ONGROUND)
24                 return 0;
25         if(a.waterlevel >= 2)
26                 return 0;
27         traceline(a.origin, a.origin - '0 0 48', MOVE_NORMAL, a);
28         if(trace_fraction < 1)
29                 return 0;
30         return 1;
31 }
32
33 void UpdateFrags(entity player, float f)
34 {
35         PlayerTeamScore_AddScore(player, f);
36 }
37
38 void GiveFrags (entity attacker, entity targ, float f)
39 {
40         // TODO route through PlayerScores instead
41         if(gameover) return;
42
43         if(f < 0)
44         {
45                 if(targ == attacker)
46                 {
47                         // suicide
48                         PlayerScore_Add(attacker, SP_SUICIDES, 1);
49                 }
50                 else
51                 {
52                         // teamkill
53                         PlayerScore_Add(attacker, SP_KILLS, -1); // or maybe add a teamkills field?
54                 }
55         }
56         else
57         {
58                 // regular frag
59                 PlayerScore_Add(attacker, SP_KILLS, 1);
60         }
61
62         PlayerScore_Add(targ, SP_DEATHS, 1);
63
64         if(g_arena)
65                 if(cvar("g_arena_roundbased"))
66                         return;
67
68         // FIXME fix the mess this is (we have REAL points now!)
69         if(g_runematch)
70         {
71                 f = RunematchHandleFrags(attacker, targ, f);
72         }
73         else if(g_keyhunt)
74         {
75                 f = kh_HandleFrags(attacker, targ, f);
76         }
77         else if(g_lms)
78         {
79                 // remove a life
80                 float tl;
81                 tl = PlayerScore_Add(targ, SP_LMS_LIVES, -1);
82                 if(tl < lms_lowest_lives)
83                         lms_lowest_lives = tl;
84                 if(tl <= 0)
85                 {
86                         if(!lms_next_place)
87                                 lms_next_place = player_count;
88                         PlayerScore_Add(targ, SP_LMS_RANK, lms_next_place); // won't ever spawn again
89                         --lms_next_place;
90                 }
91                 f = 0;
92         }
93
94         attacker.totalfrags += f;
95
96         if(f)
97                 UpdateFrags(attacker, f);
98 }
99
100 string AppendItemcodes(string s, entity player)
101 {
102         float w;
103         w = player.weapon;
104         //if(w == 0)
105         //      w = player.switchweapon;
106         if(w == 0)
107                 w = player.cnt; // previous weapon!
108         s = strcat(s, ftos(w));
109         if(time < player.strength_finished)
110                 s = strcat(s, "S");
111         if(time < player.invincible_finished)
112                 s = strcat(s, "I");
113         if(player.flagcarried != world)
114                 s = strcat(s, "F");
115         if(player.BUTTON_CHAT)
116                 s = strcat(s, "T");
117         if(player.kh_next)
118                 s = strcat(s, "K");
119         if(player.runes)
120                 s = strcat(s, "|", ftos(player.runes));
121         return s;
122 }
123
124 void LogDeath(string mode, float deathtype, entity killer, entity killed)
125 {
126         string s;
127         if(!cvar("sv_eventlog"))
128                 return;
129         s = strcat(":kill:", mode);
130         s = strcat(s, ":", ftos(killer.playerid));
131         s = strcat(s, ":", ftos(killed.playerid));
132         s = strcat(s, ":type=", ftos(deathtype));
133         s = strcat(s, ":items=");
134         s = AppendItemcodes(s, killer);
135         if(killed != killer)
136         {
137                 s = strcat(s, ":victimitems=");
138                 s = AppendItemcodes(s, killed);
139         }
140         GameLogEcho(s);
141 }
142
143 void Obituary (entity attacker, entity targ, float deathtype)
144 {
145         string  s, a;
146         float p;
147
148         if (targ.classname == "player" || targ.classname == "corpse")
149         {
150                 if (targ.classname == "corpse")
151                         s = "A corpse";
152                 else
153                         s = targ.netname;
154                 a = attacker.netname;
155
156                 if (targ == attacker)
157                 {
158                         if (deathtype == DEATH_TEAMCHANGE) {
159                                 centerprint(targ, strcat("You are now on: ", ColoredTeamName(targ.team)));
160                         } else if (deathtype == DEATH_AUTOTEAMCHANGE) {
161                                 centerprint(targ, strcat("You have been moved into a different team to improve team balance\nYou are now on: ", ColoredTeamName(targ.team)));
162                                 return;
163                         } else if (deathtype == DEATH_CAMP) {
164                                 if(sv_gentle)
165                                         centerprint(targ, "^1Reconsider your tactics, camper!\n\n\n");
166                                 else
167                                         centerprint(targ, "^1Die camper!\n\n\n");
168                         } else if (deathtype == DEATH_NOAMMO) {
169                                 if(sv_gentle)
170                                         centerprint(targ, "^1You are reinserted into the game for running out of ammo...\n\n\n");
171                                 else
172                                         centerprint(targ, "^1You were killed for running out of ammo...\n\n\n");
173                         } else if (deathtype == DEATH_ROT) {
174                                 if(sv_gentle)
175                                         centerprint(targ, "^1You need to preserve your health\n\n\n");
176                                 else
177                                         centerprint(targ, "^1You grew too old without taking your medicine\n\n\n");
178                         } else if (deathtype == DEATH_MIRRORDAMAGE) {
179                                 if(sv_gentle)
180                                         centerprint(targ, "^1Don't go against team mates!\n\n\n");
181                                 else
182                                         centerprint(targ, "^1Don't shoot your team mates!\n\n\n");
183                         } else {
184                                 if(sv_gentle)
185                                         centerprint(targ, "^1You need to be more careful!\n\n\n");
186                                 else
187                                         centerprint(targ, "^1You killed your own dumb self!\n\n\n");
188                         }
189
190                         if(sv_gentle) {
191                                 if (deathtype == DEATH_CAMP)
192                                         bprint ("^1",s, "^1 thought he found a nice camping ground\n");
193                                 else if (deathtype == DEATH_MIRRORDAMAGE)
194                                         bprint ("^1",s, "^1 didn't become friends with the Lord of Teamplay\n");
195                                 else
196                                         bprint ("^1",s, "^1 will be reinserted into the game due to his own actions\n");
197
198                                 if(deathtype != DEATH_TEAMCHANGE)
199                                 {
200                                         LogDeath("suicide", deathtype, targ, targ);
201                                         GiveFrags(attacker, targ, -1);
202                                 }
203                                 if (targ.killcount > 2)
204                                         bprint ("^1",s,"^1 ended it all with a ",ftos(targ.killcount)," scoring spree\n");
205                         } else {
206                                 if(deathtype >= WEP_FIRST && deathtype <= WEP_LAST)
207                                 {
208                                         w_deathtypestring = "couldn't resist the urge to self-destruct";
209                                         weapon_action(deathtype, WR_SUICIDEMESSAGE);
210                                         bprint("^1", s, "^1 ", w_deathtypestring, "\n");
211                                 }
212                                 else if (deathtype == DEATH_KILL)
213                                         bprint ("^1",s, "^1 couldn't take it anymore\n");
214                                 else if (deathtype == DEATH_ROT)
215                                         bprint ("^1",s, "^1 died\n");
216                                 else if (deathtype == DEATH_NOAMMO)
217                                 {
218                                         bprint ("^7",s, " ^7committed suicide. What's the point of living without ammo?\n");
219                                 }
220                                 else if (deathtype == DEATH_CAMP)
221                                         bprint ("^1",s, "^1 thought he found a nice camping ground\n");
222                                 else if (deathtype == DEATH_MIRRORDAMAGE)
223                                         bprint ("^1",s, "^1 didn't become friends with the Lord of Teamplay\n");
224                                 else if (deathtype != DEATH_TEAMCHANGE)
225                                         bprint ("^1",s, "^1 couldn't resist the urge to self-destruct\n");
226
227                                 if(deathtype != DEATH_TEAMCHANGE)
228                                 {
229                                         LogDeath("suicide", deathtype, targ, targ);
230                                         GiveFrags(attacker, targ, -1);
231                                 }
232                                 if (targ.killcount > 2)
233                                         bprint ("^1",s,"^1 ended it all with a ",ftos(targ.killcount)," kill spree\n");
234                         }
235                 }
236                 else if (attacker.classname == "player" || attacker.classname == "gib")
237                 {
238                         if(teamplay && attacker.team == targ.team)
239                         {
240                                 if(sv_gentle) {
241                                         centerprint(attacker, "^1Moron! You went against a teammate!\n\n\n");
242                                         bprint ("^1", a, "^1 took action against a teammate\n");
243                                 } else {
244                                         centerprint(attacker, "^1Moron! You fragged a teammate!\n\n\n");
245                                         bprint ("^1", a, "^1 mows down a teammate\n");
246                                 }
247                                 GiveFrags(attacker, targ, -1);
248                                 if (targ.killcount > 2) {
249                                         if(sv_gentle)
250                                                 bprint ("^1",s,"'s ^1",ftos(targ.killcount)," scoring spree was ended by a teammate!\n");
251                                         else
252                                                 bprint ("^1",s,"'s ^1",ftos(targ.killcount)," kill spree was ended by a teammate!\n");
253                                 }
254                                 if (attacker.killcount > 2) {
255                                         if(sv_gentle)
256                                                 bprint ("^1",a,"^1 ended a ",ftos(attacker.killcount)," scoring spree by going against a teammate\n");
257                                         else
258                                                 bprint ("^1",a,"^1 ended a ",ftos(attacker.killcount)," kill spree by killing a teammate\n");
259                                 }
260                                 attacker.killcount = 0;
261
262                                 LogDeath("tk", deathtype, attacker, targ);
263                         }
264                         else
265                         {
266                                 if (!checkrules_firstblood)
267                                 {
268                                         checkrules_firstblood = TRUE;
269                                         if(sv_gentle)
270                                                 bprint("^1",a, "^1 was the first to score", "\n");
271                                         else
272                                                 bprint("^1",a, "^1 drew first blood", "\n");
273                                 }
274
275                                 if(sv_gentle > 0) {
276                                         centerprint(attacker, strcat("^4You scored against ^7", s, "\n\n\n"));
277                                         centerprint(targ, strcat(a,"^1 scored against you ^7\n\n\n"));
278                                 } else {
279                                         centerprint(attacker, strcat("^4You fragged ^7", s, "\n\n\n"));
280                                         centerprint(targ, strcat("^1You were fragged by ^7", a, "\n\n\n"));
281                                 }
282
283                                 if(sv_gentle) {
284                                         bprint ("^1",s, "^1 needs a restart thanks to ", a, "\n");
285                                 } else {
286                                         if(deathtype >= WEP_FIRST && deathtype <= WEP_LAST)
287                                         {
288                                                 w_deathtypestring = "was blasted by";
289                                                 weapon_action(deathtype, WR_KILLMESSAGE);
290                                                 p = strstrofs(w_deathtypestring, "#", 0);
291                                                 if(p < 0)
292                                                         bprint("^1", s, "^1 ", w_deathtypestring, " ", a, "\n");
293                                                 else
294                                                         bprint("^1", s, "^1 ", substring(w_deathtypestring, 0, p), a, "^1", substring(w_deathtypestring, p+1, strlen(w_deathtypestring) - (p+1)), "\n");
295                                         }
296                                         else if (deathtype == DEATH_TELEFRAG)
297                                                 bprint ("^1",s, "^1 was telefragged by ", a, "\n");
298                                         else if (deathtype == DEATH_DROWN)
299                                                 bprint ("^1",s, "^1 was drowned by ", a, "\n");
300                                         else if (deathtype == DEATH_SLIME)
301                                                 bprint ("^1",s, "^1 was slimed by ", a, "\n");
302                                         else if (deathtype == DEATH_LAVA)
303                                                 bprint ("^1",s, "^1 was cooked by ", a, "\n");
304                                         else if (deathtype == DEATH_FALL)
305                                                 bprint ("^1",s, "^1 was grounded by ", a, "\n");
306                                         else if (deathtype == DEATH_SHOOTING_STAR)
307                                                 bprint ("^1",s, "^1 was shot into space by ", a, "\n");
308                                         else if (deathtype == DEATH_SWAMP)
309                                                 bprint ("^1",s, "^1 was conserved by ", a, "\n");
310                                         else if (deathtype == DEATH_HURTTRIGGER)
311                                                 bprint ("^1",s, "^1 was thrown into a world of hurt by ", a, "\n");
312                     else if(deathtype == DEATH_TURRET)
313                         bprint ("^1",s, "^1 was pushed into the line of fire by ^1", a, "\n");
314                                         else
315                                                 bprint ("^1",s, "^1 was fragged by ", a, "\n");
316                                 }
317                                 if(g_ctf && targ.flagcarried)
318                                 {
319                                         GiveFrags(attacker, targ, cvar("g_ctf_flagscore_kill"));
320                                         PlayerScore_Add(attacker, SP_CTF_FCKILLS, 1);
321                                 }
322                                 else
323                                         GiveFrags(attacker, targ, 1);
324                                 if (targ.killcount > 2) {
325                                         if(sv_gentle)
326                                                 bprint ("^1",s,"'s ^1", ftos(targ.killcount), " scoring spree was ended by ", a, "\n");
327                                         else
328                                                 bprint ("^1",s,"'s ^1", ftos(targ.killcount), " kill spree was ended by ", a, "\n");
329                                 }
330                                 attacker.killcount = attacker.killcount + 1;
331                                 if (attacker.killcount > 2) {
332                                         if(sv_gentle)
333                                                 bprint ("^1",a,"^1 made ",ftos(attacker.killcount)," scores in a row\n");
334                                         else
335                                                 bprint ("^1",a,"^1 has ",ftos(attacker.killcount)," frags in a row\n");
336                                 }
337
338                                 LogDeath("frag", deathtype, attacker, targ);
339
340                                 if (attacker.killcount == 3)
341                                 {
342                                         if(sv_gentle) {
343                                                 bprint (a,"^7 made a ^1TRIPLE SCORE\n");
344                                         } else {
345                                                 bprint (a,"^7 made a ^1TRIPLE FRAG\n");
346                                                 announce(attacker, "announcer/male/03kills.ogg");
347                                         }
348                                 }
349                                 else if (attacker.killcount == 5)
350                                 {
351                                         if(sv_gentle) {
352                                                 bprint (a,"^7 unleashes ^1SCORING RAGE\n");
353                                         } else {
354                                                 bprint (a,"^7 unleashes ^1RAGE\n");
355                                                 announce(attacker, "announcer/male/05kills.ogg");
356                                         }
357                                 }
358                                 else if (attacker.killcount == 10)
359                                 {
360                                         if(sv_gentle) {
361                                                 bprint (a,"^7 made ^1TEN SCORES IN A ROW!\n");
362                                         } else {
363                                                 bprint (a,"^7 starts the ^1MASSACRE!\n");
364                                                 announce(attacker, "announcer/male/10kills.ogg");
365                                         }
366                                 }
367                                 else if (attacker.killcount == 15)
368                                 {
369                                         if(sv_gentle) {
370                                                 bprint (a,"^7 made ^1FIFTEEN SCORES IN A ROW!\n");
371                                         } else {
372                                                 bprint (a,"^7 executes ^1MAYHEM!\n");
373                                                 announce(attacker, "announcer/male/15kills.ogg");
374                                         }
375                                 }
376                                 else if (attacker.killcount == 20)
377                                 {
378                                         if(sv_gentle) {
379                                                 bprint (a,"^7 made ^1TWENTY SCORES IN A ROW!\n");
380                                         } else {
381                                                 bprint (a,"^7 is a ^1BERSERKER!\n");
382                                                 announce(attacker, "announcer/male/20kills.ogg");
383                                         }
384                                 }
385                                 else if (attacker.killcount == 25)
386                                 {
387                                         if(sv_gentle) {
388                                                 bprint (a,"^7 made ^1TWENTY FIFE SCORES IN A ROW!\n");
389                                         } else {
390                                                 bprint (a,"^7 inflicts ^1CARNAGE!\n");
391                                                 announce(attacker, "announcer/male/25kills.ogg");
392                                         }
393                                 }
394                                 else if (attacker.killcount == 30)
395                                 {
396                                         if(sv_gentle) {
397                                                 bprint (a,"^7 made ^1THIRTY SCORES IN A ROW!\n");
398                                         } else {
399                                                 bprint (a,"^7 unleashes ^1ARMAGEDDON!\n");
400                                                 announce(attacker, "announcer/male/30kills.ogg");
401                                         }
402                                 }
403                         }
404                 }
405                 else
406                 {
407                         centerprint(targ, "^1Watch your step!\n\n\n");
408                         if (deathtype == DEATH_HURTTRIGGER && attacker.message != "")
409                                 bprint ("^1",s, "^1 ", attacker.message, "\n");
410                         else if (deathtype == DEATH_DROWN)
411                                 if(sv_gentle)
412                                         bprint ("^1",s, "^1 was in the water for too long\n");
413                                 else
414                                         bprint ("^1",s, "^1 drowned\n");
415                         else if (deathtype == DEATH_SLIME)
416                                 bprint ("^1",s, "^1 was slimed\n");
417                         else if (deathtype == DEATH_LAVA)
418                                 if(sv_gentle)
419                                         bprint ("^1",s, "^1 found a hot place\n");
420                                 else
421                                         bprint ("^1",s, "^1 turned into hot slag\n");
422                         else if (deathtype == DEATH_FALL)
423                                 if(sv_gentle)
424                                         bprint ("^1",s, "^1 tested gravity (and it worked)\n");
425                                 else
426                                         bprint ("^1",s, "^1 hit the ground with a crunch\n");
427                         else if (deathtype == DEATH_SHOOTING_STAR)
428                                 bprint ("^1",s, "^1 became a shooting star\n");
429                         else if (deathtype == DEATH_SWAMP)
430                                 if(sv_gentle)
431                                         bprint ("^1",s, "^1 discovered a swamp\n");
432                                 else
433                                         bprint ("^1",s, "^1 is now conserved for centuries to come\n");
434             else if(deathtype == DEATH_TURRET)
435                     bprint ("^1",s, "^1 was mowed down by a turret \n");
436                         else
437                                 if(sv_gentle)
438                                         bprint ("^1",s, "^1 needs a restart\n");
439                                 else
440                                         bprint ("^1",s, "^1 died\n");
441                         GiveFrags(targ, targ, -1);
442                         if(PlayerScore_Add(targ, SP_SCORE, 0) == -5) {
443                                 announce(targ, "announcer/male/botlike.ogg");
444                         }
445
446                         if (targ.killcount > 2)
447                                 if(sv_gentle)
448                                         bprint ("^1",s,"^1 needs a restart after a ",ftos(targ.killcount)," scoring spree\n");
449                                 else
450                                         bprint ("^1",s,"^1 died with a ",ftos(targ.killcount)," kill spree\n");
451
452                         LogDeath("accident", deathtype, targ, targ);
453                 }
454                 targ.death_origin = targ.origin;
455                 if(targ != attacker)
456                         targ.killer_origin = attacker.origin;
457                 // FIXME: this should go in PutClientInServer
458                 if (targ.killcount)
459                         targ.killcount = 0;
460         }
461 }
462
463 // these are updated by each Damage call for use in button triggering and such
464 entity damage_targ;
465 entity damage_inflictor;
466 entity damage_attacker;
467
468 void Damage (entity targ, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
469 {
470         float mirrordamage;
471         float mirrorforce;
472         entity attacker_save;
473         mirrordamage = 0;
474         mirrorforce = 0;
475
476         if (gameover || targ.killcount == -666)
477                 return;
478
479         local entity oldself;
480         oldself = self;
481         self = targ;
482         damage_targ = targ;
483         damage_inflictor = inflictor;
484         damage_attacker = attacker;
485                 attacker_save = attacker;
486
487         if(deathtype == DEATH_KILL || deathtype == DEATH_TEAMCHANGE || deathtype == DEATH_AUTOTEAMCHANGE)
488         {
489                 // These are ALWAYS lethal
490                 // No damage modification here
491                 // Instead, prepare the victim for his death...
492                 targ.armorvalue = 0;
493                 targ.spawnshieldtime = 0;
494                 targ.health = 0.9; // this is < 1
495                 targ.flags -= targ.flags & FL_GODMODE;
496                 damage = 100000;
497         }
498         else
499         {
500                 if (targ.classname == "player")
501                 if (attacker.classname == "player")
502                 if (!targ.isbot)
503                 if (attacker.isbot)
504                         damage = damage * bound(0.1, (skill + 5) * 0.1, 1);
505
506                 // nullify damage if teamplay is on
507                 if(deathtype != DEATH_TELEFRAG)
508                 if(attacker.classname == "player")
509                 {
510                         if(targ.classname == "player" && targ != attacker && (IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(targ)))
511                         {
512                                 damage = 0;
513                                 force = '0 0 0';
514                         }
515                         else if(attacker.team == targ.team)
516                         {
517                                 if(teamplay == 1)
518                                         damage = 0;
519                                 else if(attacker != targ)
520                                 {
521                                         if(teamplay == 3)
522                                                 damage = 0;
523                                         else if(teamplay == 4)
524                                         {
525                                                 if(targ.classname == "player" && targ.deadflag == DEAD_NO)
526                                                 {
527                                                         mirrordamage = cvar("g_mirrordamage") * damage;
528                                                         mirrorforce = cvar("g_mirrordamage") * vlen(force);
529                                                         if(g_minstagib)
530                                                         {
531                                                                 if(cvar("g_friendlyfire") == 0)
532                                                                         damage = 0;
533                                                         }
534                                                         else
535                                                                 damage = cvar("g_friendlyfire") * damage;
536                                                         // mirrordamage will be used LATER
537                                                 }
538                                                 else
539                                                         damage = 0;
540                                         }
541                                 }
542                         }
543                 }
544
545                 if(targ.classname == "player")
546                 if(attacker.classname == "player")
547                 if(attacker != targ)
548                 {
549                         targ.lms_traveled_distance = cvar("g_lms_campcheck_distance");
550                         attacker.lms_traveled_distance = cvar("g_lms_campcheck_distance");
551                 }
552
553                 if(targ != attacker)
554                 if(!targ.deadflag)
555                 if(damage > 0)
556                 if(targ.classname == "player")
557                 if(attacker)
558                         attacker.hitsound += 1;
559
560                 if (g_minstagib)
561                 {
562                         if ((deathtype == DEATH_FALL)  ||
563                                 (deathtype == DEATH_DROWN) ||
564                                 (deathtype == DEATH_SLIME) ||
565                                 (deathtype == DEATH_LAVA))
566                                 return;
567                         if (targ.armorvalue && (deathtype == WEP_MINSTANEX) && damage)
568                         {
569                                 targ.armorvalue -= 1;
570                                 centerprint(targ, strcat("^3Remaining extra lives: ",ftos(targ.armorvalue),"\n"));
571                                 damage = 0;
572                                 targ.hitsound += 1;
573                         }
574                         else if (deathtype == WEP_MINSTANEX && targ.items & IT_STRENGTH)
575                         {
576                                 if(clienttype(attacker) == CLIENTTYPE_REAL)
577                                         if(IsDifferentTeam(targ, attacker))
578                                                 yoda = 1;
579                         }
580                         if (deathtype == WEP_LASER)
581                         {
582                                 damage = 0;
583                                 if (targ != attacker)
584                                 {
585                                         if (targ.classname == "player")
586                                                 centerprint(attacker, "Secondary fire inflicts no damage!\n");
587                                         damage = 0;
588                                         mirrordamage = 0;
589                                         force = '0 0 0';
590                                         // keep mirrorforce
591                                         attacker = targ;
592                                 }
593                         }
594                 } else {
595                         if (!targ.deadflag)
596                                 if(targ.takedamage == DAMAGE_AIM)
597                                         if(IsFlying(targ))
598                                                 if(IsDifferentTeam(targ, attacker))
599                                                         yoda = 1;
600                 }
601
602                 // apply strength multiplier
603                 if (attacker.items & IT_STRENGTH && !g_minstagib)
604                 {
605                         damage = damage * cvar("g_balance_powerup_strength_damage");
606                         force = force * cvar("g_balance_powerup_strength_force");
607                 }
608                 // apply invincibility multiplier
609                 if (targ.items & IT_INVINCIBLE && !g_minstagib)
610                         damage = damage * cvar("g_balance_powerup_invincible_takedamage");
611
612
613                 if(g_runematch)
614                 {
615                         // apply strength rune
616                         if (attacker.runes & RUNE_STRENGTH)
617                         {
618                                 if(attacker.runes & CURSE_WEAK) // have both curse & rune
619                                 {
620                                         damage = damage * cvar("g_balance_rune_strength_combo_damage");
621                                         force = force * cvar("g_balance_rune_strength_combo_force");
622                                 }
623                                 else
624                                 {
625                                         damage = damage * cvar("g_balance_rune_strength_damage");
626                                         force = force * cvar("g_balance_rune_strength_force");
627                                 }
628                         }
629                         else if (attacker.runes & CURSE_WEAK)
630                         {
631                                 damage = damage * cvar("g_balance_curse_weak_damage");
632                                 force = force * cvar("g_balance_curse_weak_force");
633                         }
634
635                         // apply defense rune
636                         if (targ.runes & RUNE_DEFENSE)
637                         {
638                                 if (targ.runes & CURSE_VULNER) // have both curse & rune
639                                         damage = damage * cvar("g_balance_rune_defense_combo_takedamage");
640                                 else
641                                         damage = damage * cvar("g_balance_rune_defense_takedamage");
642                         }
643                         else if (targ.runes & CURSE_VULNER)
644                                 damage = damage * cvar("g_balance_curse_vulner_takedamage");
645                 }
646         }
647
648         // apply push
649         if (self.damageforcescale)
650         if (vlen(force))
651         {
652                 self.velocity = self.velocity + self.damageforcescale * force;
653                 self.flags = self.flags - (self.flags & FL_ONGROUND);
654         }
655         // apply damage
656         if (damage != 0)
657         if (self.event_damage)
658                 self.event_damage (inflictor, attacker, damage, deathtype, hitloc, force);
659         self = oldself;
660
661         if(targ.classname == "player" && attacker.classname == "player" && attacker != targ && attacker.health > 2)
662         {
663                 // Savage: vampire mode
664                 if (g_vampire)
665                 if (!g_minstagib)
666                 if (time > self.spawnshieldtime)
667                 {
668                         attacker.health += damage;
669                 }
670                 if(g_runematch)
671                 {
672                         if (attacker.runes & RUNE_VAMPIRE)
673                         {
674                         // apply vampire rune
675                                 if (attacker.runes & CURSE_EMPATHY) // have the curse too
676                                 {
677                                         //attacker.health = attacker.health + damage * cvar("g_balance_rune_vampire_combo_absorb");
678                                         attacker.health = bound(
679                                                 cvar("g_balance_curse_empathy_minhealth"), // LA: was 3, now 40
680                                                 attacker.health + damage * cvar("g_balance_rune_vampire_combo_absorb"),
681                                                 cvar("g_balance_rune_vampire_maxhealth"));      // LA: was 1000, now 500
682                                 }
683                                 else
684                                 {
685                                         //attacker.health = attacker.health + damage * cvar("g_balance_rune_vampire_absorb");
686                                         attacker.health = bound(
687                                                 attacker.health,        // LA: was 3, but changed so that you can't lose health
688                                                                                         // empathy won't let you gain health in the same way...
689                                                 attacker.health + damage * cvar("g_balance_rune_vampire_absorb"),
690                                                 cvar("g_balance_rune_vampire_maxhealth"));      // LA: was 1000, now 500
691                                         }
692                         }
693                         // apply empathy curse
694                         else if (attacker.runes & CURSE_EMPATHY)
695                         {
696                                 attacker.health = bound(
697                                         cvar("g_balance_curse_empathy_minhealth"), // LA: was 3, now 20
698                                         attacker.health + damage * cvar("g_balance_curse_empathy_takedamage"),
699                                         attacker.health);
700                         }
701                 }
702         }
703
704         // apply mirror damage if any
705         if(mirrordamage > 0 || mirrorforce > 0)
706         {
707                 attacker = attacker_save;
708                 if(g_minstagib)
709                         if(mirrordamage > 0)
710                         {
711                                 // just lose extra LIVES, don't kill the player for mirror damage
712                                 if(attacker.armorvalue > 0)
713                                 {
714                                         attacker.armorvalue = attacker.armorvalue - 1;
715                                         centerprint(attacker, strcat("^3Remaining extra lives: ",ftos(attacker.armorvalue),"\n"));
716                                         attacker.hitsound += 1;
717                                 }
718                                 mirrordamage = 0;
719                         }
720                 force = normalize(attacker.origin + attacker.view_ofs - hitloc) * mirrorforce;
721                 Damage(attacker, inflictor, attacker, mirrordamage, DEATH_MIRRORDAMAGE, attacker.origin, force);
722         }
723 }
724
725 vector NearestPointOnBox(entity box, vector org)
726 {
727         vector m1, m2, nearest;
728
729         m1 = box.mins + box.origin;
730         m2 = box.maxs + box.origin;
731
732         nearest_x = bound(m1_x, org_x, m2_x);
733         nearest_y = bound(m1_y, org_y, m2_y);
734         nearest_z = bound(m1_z, org_z, m2_z);
735
736         return nearest;
737 }
738
739 float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype)
740 // Returns total damage applies to creatures
741 {
742         entity  targ;
743         float   finaldmg;
744         float   power;
745         vector  blastorigin;
746         vector  force;
747         vector  diff;
748         vector  center;
749         vector  nearest;
750         float   total_damage_to_creatures;
751
752         blastorigin = (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5);
753         total_damage_to_creatures = 0;
754
755         targ = findradius (blastorigin, rad);
756         while (targ)
757         {
758                 if (targ != inflictor)
759                         if (ignore != targ)
760                         {
761                                 // LordHavoc: measure distance to nearest point on target (not origin)
762                                 // (this guarentees 100% damage on a touch impact)
763                                 nearest = NearestPointOnBox(targ, blastorigin);
764                                 diff = nearest - blastorigin;
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                                 power = 1 - ((vlen (diff) - 2) / rad);
768                                 //bprint(" ");
769                                 //bprint(ftos(power));
770                                 if (power > 0)
771                                 {
772                                         if (power > 1)
773                                                 power = 1;
774                                         finaldmg = coredamage * power + edgedamage * (1 - power);
775                                         if (finaldmg > 0)
776                                         {
777                                                 center = targ.origin + (targ.mins + targ.maxs) * 0.5;
778                                                 // if it's a player, use the view origin as reference
779                                                 if (targ.classname == "player")
780                                                         center = targ.origin + targ.view_ofs;
781                                                 force = normalize(center - blastorigin) * (finaldmg / coredamage) * forceintensity;
782                                                 if (targ == attacker)
783                                                         finaldmg = finaldmg * cvar("g_balance_selfdamagepercent");      // Partial damage if the attacker hits himself
784                                                 // test line of sight to multiple positions on box,
785                                                 // and do damage if any of them hit
786                                                 local float c;
787                                                 c = ceil(finaldmg / 10);
788                                                 if (c > 20)
789                                                         c = 20;
790                                                 while (c > 0)
791                                                 {
792                                                         c = c - 1;
793                                                         traceline(blastorigin, nearest, TRUE, inflictor);
794                                                         if (trace_fraction == 1 || trace_ent == targ
795                                                             || cvar("g_throughfloor"))
796                                                         {
797                                                                 if(targ.iscreature)
798                                                                         total_damage_to_creatures += finaldmg;
799                                                                 Damage (targ, inflictor, attacker, finaldmg, deathtype, nearest, force);
800                                                                 break;
801                                                         }
802                                                         nearest_x = targ.mins_x + random() * targ.size_x;
803                                                         nearest_y = targ.mins_y + random() * targ.size_y;
804                                                         nearest_z = targ.mins_z + random() * targ.size_z;
805                                                 }
806                                         }
807                                 }
808                         }
809                 targ = targ.chain;
810         }
811
812         return total_damage_to_creatures;
813 }