]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_damage.qc
prevent same map again
[divverent/nexuiz.git] / data / qcsrc / server / g_damage.qc
1
2 float checkrules_firstblood;
3
4 void GiveFrags (entity attacker, entity targ, float f)
5 {
6         if(gameover) return;
7
8         if(cvar("g_arena"))
9                 if(cvar("g_arena_roundbased"))
10                         return;
11
12         if(cvar("g_domination"))
13         {
14                 if(cvar("g_domination_disable_frags"))
15                         if(f > 0)
16                                 return;
17         }
18         else if(cvar("g_runematch"))
19         {
20                 if(f > 0)
21                         f = RunematchHandleFrags(attacker, targ, f);
22         }
23         else if(cvar("g_lms"))
24         {
25                 // count remaining lives, not frags in lms
26                 targ.frags -= 1;
27                 // keep track of the worst players lives
28                 if(targ.frags < lms_lowest_lives)
29                         lms_lowest_lives = targ.frags;
30                 // player has no more lives left
31                 if (!targ.frags)
32                 {
33                         lms_dead_count += 1;
34                         if(!lms_next_place)
35                                 lms_next_place = player_count;
36                         targ.frags = -lms_next_place;
37                         lms_next_place -= 1;
38                 }
39                 return;
40         }
41
42         if(f)
43                 attacker.frags = attacker.frags + f;
44 }
45
46 string AppendItemcodes(string s, entity player)
47 {
48         float w;
49         w = player.weapon;
50         //if(w == 0)
51         //      w = player.switchweapon;
52         if(w == 0)
53                 w = player.cnt; // previous weapon!
54         s = strcat(s, ftos(W_ItemCode(w)));
55         if(time < player.strength_finished)
56                 s = strcat(s, "S");
57         if(time < player.invincible_finished)
58                 s = strcat(s, "I");
59         if(player.flagcarried != world)
60                 s = strcat(s, "F");
61         if(player.buttonchat)
62                 s = strcat(s, "T");
63         if(player.runes)
64                 s = strcat(s, "|", ftos(player.runes));
65         return s;
66 }
67
68 void LogDeath(string mode, float deathtype, entity killer, entity killed)
69 {
70         string s;
71         if(!cvar("sv_eventlog"))
72                 return;
73         s = strcat(":kill:", mode);
74         s = strcat(s, ":", ftos(killer.playerid));
75         s = strcat(s, ":", ftos(killed.playerid));
76         s = strcat(s, ":type=", ftos(deathtype));
77         s = strcat(s, ":items=");
78         s = AppendItemcodes(s, killer);
79         if(killed != killer)
80         {
81                 s = strcat(s, ":victimitems=");
82                 s = AppendItemcodes(s, killed);
83         }
84         GameLogEcho(s, FALSE);
85 }
86
87 void Obituary (entity attacker, entity targ, float deathtype)
88 {
89         string  s, m;
90
91         if (targ.classname == "player" || targ.classname == "corpse")
92         {
93                 if (targ.classname == "corpse")
94                         s = "A corpse";
95                 else
96                         s = targ.netname;
97
98                 if (targ == attacker)
99                 {
100                         if (deathtype == DEATH_TEAMCHANGE)
101                         {
102                                 m = "You are now on: ";
103                                 if (targ.team == 5)
104                                         m = strcat(m, "^1Red Team");
105                                 else if (targ.team == 14)
106                                         m = strcat(m, "^4Blue Team");
107                                 else if (targ.team == 10)
108                                         m = strcat(m, "^6Pink Team");
109                                 else if (targ.team == 13)
110                                         m = strcat(m, "^3Yellow Team");
111                                 centermsg_setfor(targ, CENTERMSG_TEAMCHANGE, m);
112                         }
113                         else if (deathtype == DEATH_AUTOTEAMCHANGE)
114                         {
115                                 centermsg_setfor(targ, CENTERMSG_TEAMCHANGE, "You have been moved into a different team to improve team balance");
116                                 m = "You are now on: ";
117                                 if (targ.team == 5)
118                                         m = strcat(m, "^1Red Team");
119                                 else if (targ.team == 14)
120                                         m = strcat(m, "^4Blue Team");
121                                 else if (targ.team == 10)
122                                         m = strcat(m, "^6Pink Team");
123                                 else if (targ.team == 13)
124                                         m = strcat(m, "^3Yellow Team");
125                                 centermsg_setfor(targ, CENTERMSG_TEAMCHANGE2, m);
126                                 return;
127                         }
128                         else if (deathtype == DEATH_CAMP)
129                                 centermsg_setfor(targ, CENTERMSG_DEATH, "^1Die camper!");
130                         else if (deathtype == DEATH_NOAMMO)
131                                 centermsg_setfor(targ, CENTERMSG_DEATH, "^1You were killed for running out of ammo...");
132                         else if (deathtype == DEATH_ROT)
133                                 centermsg_setfor(targ, CENTERMSG_DEATH, "^1You grew too old without taking your medcine");
134                         else if (deathtype == DEATH_MIRRORDAMAGE)
135                                 centermsg_setfor(targ, CENTERMSG_DEATH, "^1Don't shoot your team mates!");
136                         else
137                                 centermsg_setfor(targ, CENTERMSG_DEATH, "^1You killed your own dumb self!");
138
139                         if (deathtype == IT_GRENADE_LAUNCHER)
140                                 bprint ("^1",s, "^1 detonated\n");
141                         else if (deathtype == IT_ELECTRO)
142                                 bprint ("^1",s, "^1 played with plasma\n");
143                         else if (deathtype == IT_ROCKET_LAUNCHER)
144                                 bprint ("^1",s, "^1 exploded\n");
145                         else if (deathtype == DEATH_KILL)
146                                 bprint ("^1",s, "^1 couldn't take it anymore\n");
147                         else if (deathtype == DEATH_ROT)
148                                 bprint ("^1",s, "^1 died\n");
149                         else if (deathtype == DEATH_NOAMMO)
150                         {
151                                 bprint ("^7",s, " ^7committed suicide. What's the point of living without ammo?\n");
152                                 //sound (self, CHAN_BODY, "minstagib/mockery.ogg", 1, ATTN_NONE);
153                         }
154                         else if (deathtype == DEATH_CAMP)
155                                 bprint ("^1",s, "^1 thought he found a nice camping ground\n");
156                         else if (deathtype == DEATH_MIRRORDAMAGE)
157                                 bprint ("^1",s, "^1 didn't become friends with the Lord of Teamplay\n");
158                         else if (deathtype != DEATH_TEAMCHANGE)
159                                 bprint ("^1",s, "^1 couldn't resist the urge to self-destruct\n");
160
161                         if(deathtype != DEATH_TEAMCHANGE)
162                         {
163                                 LogDeath("suicide", deathtype, targ, targ);
164                                 GiveFrags(attacker, targ, -1);
165                         }
166                         //targ.frags = targ.frags - 1;
167                         if (targ.killcount > 2)
168                                 bprint ("^1",s,"^1 ended it all with a ",ftos(targ.killcount)," kill spree\n");
169                 }
170                 else if (teamplay && attacker.team == targ.team)
171                 {
172                         centermsg_setfor(attacker, CENTERMSG_DEATH, "^1Moron! You fragged a teammate!");
173                         bprint ("^1", attacker.netname, "^1 mows down a teammate\n");
174                         GiveFrags(attacker, targ, -1);
175                         //attacker.frags = attacker.frags - 1;
176                         if (targ.killcount > 2)
177                                 bprint ("^1",s,"'s ^1",ftos(targ.killcount)," kill spree was endeded by a teammate!\n");
178                         if (attacker.killcount > 2)
179                                 bprint ("^1",attacker.netname,"^1 ended a ",ftos(attacker.killcount)," kill spree by killing a teammate\n");
180                         attacker.killcount = 0;
181
182                         LogDeath("tk", deathtype, attacker, targ);
183                 }
184                 else if (attacker.classname == "player" || attacker.classname == "gib")
185                 {
186                         if (!checkrules_firstblood)
187                         {
188                                 checkrules_firstblood = TRUE;
189                                 //sound(world, CHAN_AUTO, "announcer/firstblood.wav", 1, ATTN_NONE);
190                                 //if (cvar("g_minstagib"))
191                                         //sound(world, CHAN_AUTO, "announce/male/mapkill1.ogg", 1, ATTN_NONE);
192                                 bprint("^1",attacker.netname, "^1 drew first blood", "\n");
193                         }
194
195                         centermsg_setfor(attacker, CENTERMSG_KILL, strcat("^4You fragged ^7", s));
196                         centermsg_setfor(targ, CENTERMSG_DEATH, strcat("^1You were fragged by ^7", attacker.netname));
197
198                         if (deathtype == IT_LASER)
199                                 bprint ("^1",s, "^1 was blasted by ", attacker.netname, "\n");
200                         else if (deathtype == IT_UZI)
201                                 bprint ("^1",s, "^1 was riddled full of holes by ", attacker.netname, "\n");
202                         else if (deathtype == IT_SHOTGUN)
203                                 bprint ("^1",s, "^1 was gunned by ", attacker.netname, "\n");
204                         else if (deathtype == IT_GRENADE_LAUNCHER)
205                                 bprint ("^1", s, "^1 was blasted by ", attacker.netname, "\n");
206                         else if (deathtype == IT_ELECTRO)
207                                 bprint ("^1",s, "^1 was blasted by ", attacker.netname, "\n");
208                         else if (deathtype == IT_CRYLINK)
209                                 bprint ("^1",s, "^1 was blasted by ", attacker.netname, "\n");
210                         else if (deathtype == IT_NEX)
211                                 bprint ("^1",s, "^1 has been vaporized by ", attacker.netname, "\n");
212                         else if (deathtype == IT_HAGAR)
213                                 bprint ("^1",s, "^1 was pummeled by ", attacker.netname, "\n");
214                         else if (deathtype == IT_ROCKET_LAUNCHER)
215                                 bprint ("^1",s, "^1 was blasted by ", attacker.netname, "\n");
216                         else if (deathtype == DEATH_TELEFRAG)
217                                 bprint ("^1",s, "^1 was telefragged by ", attacker.netname, "\n");
218                         else if (deathtype == DEATH_DROWN)
219                                 bprint ("^1",s, "^1 was drowned by ", attacker.netname, "\n");
220                         else if (deathtype == DEATH_SLIME)
221                                 bprint ("^1",s, "^1 was slimed by ", attacker.netname, "\n");
222                         else if (deathtype == DEATH_LAVA)
223                                 bprint ("^1",s, "^1 was cooked by ", attacker.netname, "\n");
224                         else if (deathtype == DEATH_FALL)
225                                 bprint ("^1",s, "^1 was grounded by ", attacker.netname, "\n");
226                         else if (deathtype == DEATH_SHOOTING_STAR)
227                                 bprint ("^1",s, "^1 was shot into space by ", attacker.netname, "\n");
228                         else if (deathtype == DEATH_SWAMP)
229                                 bprint ("^1",s, "^1 was conserved by ", attacker.netname, "\n");
230                         else if (deathtype == DEATH_HURTTRIGGER)
231                                 bprint ("^1",s, "^1 was thrown into a world of hurt by ", attacker.netname, "\n");
232                         else
233                                 bprint ("^1",s, "^1 was fragged by ", attacker.netname, "\n");
234
235                         GiveFrags(attacker, targ, 1);
236                         //attacker.frags = attacker.frags + 1;
237                         if (targ.killcount > 2)
238                                 bprint ("^1",s,"'s ^1", ftos(targ.killcount), " kill spree was ended by ", attacker.netname, "\n");
239                         attacker.killcount = attacker.killcount + 1;
240                         if (attacker.killcount > 2)
241                                 bprint ("^1",attacker.netname,"^1 has ",ftos(attacker.killcount)," frags in a row\n");
242
243                         LogDeath("frag", deathtype, attacker, targ);
244
245                         if (attacker.killcount == 3)
246                         {
247                                 bprint (attacker.netname,"^7 made a ^1TRIPLE FRAG\n");
248                                 stuffcmd(attacker, "play2 announcer/male/03kills.ogg\n");
249                         }
250                         else if (attacker.killcount == 5)
251                         {
252                                 bprint (attacker.netname,"^7 made a ^1FIVE FRAG COMBO\n");
253                                 stuffcmd(attacker, "play2 announcer/male/05kills.ogg\n");
254                         }
255                         else if (attacker.killcount == 10)
256                         {
257                                 bprint (attacker.netname,"^7 is on a ^1RAGE\n");
258                                 stuffcmd(attacker, "play2 announcer/male/10kills.ogg\n");
259                         }
260                         else if (attacker.killcount == 15)
261                         {
262                                 bprint (attacker.netname,"^7 has done a ^1MASSACRE!\n");
263                                 stuffcmd(attacker, "play2 announcer/male/15kills.ogg\n");
264                         }
265                         else if (attacker.killcount == 20)
266                         {
267                                 bprint (attacker.netname,"^7 is ^1UNHUMAN!\n");
268                                 stuffcmd(attacker, "play2 announcer/male/20kills.ogg\n");
269                         }
270                         else if (attacker.killcount == 25)
271                         {
272                                 bprint (attacker.netname,"^7 is a ^1DEATH INCARNATION!\n");
273                                 stuffcmd(attacker, "play2 announcer/male/25kills.ogg\n");
274                         }
275                         else if (attacker.killcount == 30)
276                         {
277                                 bprint (attacker.netname,"^7 is maybe a ^1AIMBOTTER?!\n");
278                                 stuffcmd(attacker, "play2 announcer/male/30kills.ogg\n");
279                         }
280                 }
281                 else
282                 {
283                         centermsg_setfor(targ, CENTERMSG_DEATH, "^1Watch your step!");
284                         if (deathtype == DEATH_HURTTRIGGER && attacker.message != "")
285                                 bprint ("^1",s, "^1 ", attacker.message, "\n");
286                         else if (deathtype == DEATH_DROWN)
287                                 bprint ("^1",s, "^1 drowned\n");
288                         else if (deathtype == DEATH_SLIME)
289                                 bprint ("^1",s, "^1 was slimed\n");
290                         else if (deathtype == DEATH_LAVA)
291                                 bprint ("^1",s, "^1 turned into hot slag\n");
292                         else if (deathtype == DEATH_FALL)
293                                 bprint ("^1",s, "^1 hit the ground with a crunch\n");
294                         else if (deathtype == DEATH_SHOOTING_STAR)
295                                 bprint ("^1",s, "^1 became a shooting star\n");
296                         else if (deathtype == DEATH_SWAMP)
297                                 bprint ("^1",s, "^1 is now conserved for centuries to come\n");
298                         else
299                                 bprint ("^1",s, "^1 died\n");
300                         GiveFrags(targ, targ, -1);
301                         if(targ.frags == -5) {
302                                 stuffcmd(targ, "play2 announcer/male/botlike.ogg\n");
303                         }
304
305                         //targ.frags = targ.frags - 1;
306                         if (targ.killcount > 2)
307                                 bprint ("^1",s,"^1 died with a ",ftos(targ.killcount)," kill spree\n");
308
309                         LogDeath("accident", deathtype, targ, targ);
310                 }
311                 targ.death_origin = targ.origin;
312                 if(targ != attacker)
313                         targ.killer_origin = attacker.origin;
314                 // FIXME: this should go in PutClientInServer
315                 if (targ.killcount)
316                         targ.killcount = 0;
317         }
318 }
319
320 // these are updated by each Damage call for use in button triggering and such
321 entity damage_targ;
322 entity damage_inflictor;
323 entity damage_attacker;
324
325 void Damage (entity targ, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
326 {
327         float mirrordamage;
328         float mirrorforce;
329         entity attacker_save;
330         mirrordamage = 0;
331         mirrorforce = 0;
332
333         if (gameover || targ.killcount == -666)
334                 return;
335
336         local entity oldself;
337         oldself = self;
338         self = targ;
339         damage_targ = targ;
340         damage_inflictor = inflictor;
341         damage_attacker = attacker;
342                 attacker_save = attacker;
343
344         if (targ.classname == "player")
345         if (attacker.classname == "player")
346         if (!targ.isbot)
347         if (attacker.isbot)
348                 damage = damage * bound(0.1, (skill + 5) * 0.1, 1);
349
350         // nullify damage if teamplay is on
351         if(deathtype != DEATH_TELEFRAG)
352         {
353                 if(teamplay == 1)
354                         if(attacker.team)
355                                 if(attacker.team == targ.team)
356                                         damage = 0;
357                 if(teamplay == 3)
358                         if(attacker != targ)
359                                 if(attacker.team)
360                                         if(attacker.team == targ.team)
361                                                 damage = 0;
362                 if(teamplay == 4)
363                         if(attacker != targ)
364                                 if(attacker.team == targ.team)
365                                         if(attacker.classname == "player")
366                                                 if(targ.classname == "player")
367                                                 {
368                                                         mirrordamage = cvar("g_mirrordamage") * damage;
369                                                         mirrorforce = cvar("g_mirrordamage") * vlen(force);
370                                                         if(cvar("g_minstagib"))
371                                                         {
372                                                                 if(cvar("g_friendlyfire") == 0)
373                                                                         damage = 0;
374                                                         }
375                                                         else
376                                                                 damage = cvar("g_friendlyfire") * damage;
377                                                         // mirrordamage will be used LATER
378                                                 }
379         }
380
381         if(cvar("g_lms"))
382         if(targ.classname == "player")
383         if(attacker.classname == "player")
384         if(attacker != targ)
385         {
386                 targ.lms_traveled_distance = cvar("g_lms_campcheck_distance");
387                 attacker.lms_traveled_distance = cvar("g_lms_campcheck_distance");
388         }
389
390         if(targ != attacker)
391         if(!targ.deadflag)
392         if(damage > 0)
393         if(clienttype(attacker) == CLIENTTYPE_REAL)
394         if(targ.classname == "player")
395                 stuffcmd(attacker, "play2 misc/hit.wav\n");
396
397         if (cvar("g_minstagib"))
398         {
399                 if ((deathtype == DEATH_FALL)  ||
400                     (deathtype == DEATH_DROWN) ||
401                     (deathtype == DEATH_SLIME) ||
402                     (deathtype == DEATH_LAVA))
403                         return;
404                 if (targ.armorvalue && (deathtype == IT_NEX) && damage)
405                 {
406                         targ.armorvalue -= 1;
407                         centermsg_setfor(targ, CENTERMSG_MINSTAGIB, strcat("^3Remaining extra lives: ",ftos(targ.armorvalue)));
408                         damage = 0;
409                         if(clienttype(targ) == CLIENTTYPE_REAL) stuffcmd(targ, "play2 misc/hit.wav\n");
410                         //stuffcmd(attacker, "play2 misc/hit.wav\n");
411                 }
412                 else if (deathtype == IT_NEX && targ.items & IT_STRENGTH)
413                 {
414                         if(clienttype(attacker) == CLIENTTYPE_REAL) stuffcmd(attacker, "play2 announcer/male/yoda.ogg\n");
415                 }
416                 if (deathtype == IT_LASER)
417                 {
418                         damage = 0;
419                         if (targ != attacker)
420                         {
421                                 if (targ.classname == "player")
422                                         centermsg_setfor(attacker, CENTERMSG_MINSTAGIB, "Secondary fire inflicts no damage!\n");
423                                 damage = 0;
424                                 mirrordamage = 0;
425                                 force = '0 0 0';
426                                 // keep mirrorforce
427                                 attacker = targ;
428                         }
429                 }
430         } else {
431                 if (deathtype == IT_NEX && !targ.deadflag && !(attacker.flags & FL_ONGROUND) && !(targ.flags & FL_ONGROUND) && attacker.waterlevel < 2 && targ.waterlevel < 2 && attacker.killcount != 3 && attacker.killcount != 5 && attacker.killcount != 10 && attacker.killcount != 15 && attacker.killcount != 20 && attacker.killcount != 25 && attacker.killcount != 30)
432                 {
433                         if(clienttype(attacker) == CLIENTTYPE_REAL)  stuffcmd(attacker, "play2 announcer/male/yoda.ogg\n");
434                 }
435         }
436
437         // apply strength multiplier
438         if (attacker.items & IT_STRENGTH && !cvar("g_minstagib"))
439         {
440                 damage = damage * cvar("g_balance_powerup_strength_damage");
441                 force = force * cvar("g_balance_powerup_strength_force");
442         }
443         // apply invincibility multiplier
444         if (targ.items & IT_INVINCIBLE && !cvar("g_minstagib"))
445                 damage = damage * cvar("g_balance_powerup_invincible_takedamage");
446
447
448         if(cvar("g_runematch"))
449         {
450                 // apply strength rune
451                 if (attacker.runes & RUNE_STRENGTH)
452                 {
453                         if(attacker.runes & CURSE_WEAK) // have both curse & rune
454                         {
455                                 damage = damage * cvar("g_balance_rune_strength_combo_damage");
456                                 force = force * cvar("g_balance_rune_strength_combo_force");
457                         }
458                         else
459                         {
460                                 damage = damage * cvar("g_balance_rune_strength_damage");
461                                 force = force * cvar("g_balance_rune_strength_force");
462                         }
463                 }
464                 else if (attacker.runes & CURSE_WEAK)
465                 {
466                         damage = damage * cvar("g_balance_curse_weak_damage");
467                         force = force * cvar("g_balance_curse_weak_force");
468                 }
469
470                 // apply defense rune
471                 if (targ.runes & RUNE_DEFENSE)
472                 {
473                         if (targ.runes & CURSE_VULNER) // have both curse & rune
474                                 damage = damage * cvar("g_balance_rune_defense_combo_takedamage");
475                         else
476                                 damage = damage * cvar("g_balance_rune_defense_takedamage");
477                 }
478                 else if (targ.runes & CURSE_VULNER)
479                         damage = damage * cvar("g_balance_curse_vulner_takedamage");
480         }
481
482         // apply push
483         if (self.damageforcescale)
484         {
485                 self.velocity = self.velocity + self.damageforcescale * force;
486                 self.flags = self.flags - (self.flags & FL_ONGROUND);
487         }
488         // apply damage
489         if (self.event_damage)
490                 self.event_damage (inflictor, attacker, damage, deathtype, hitloc, force);
491         self = oldself;
492
493         if(targ.classname == "player" && attacker.classname == "player" && attacker != targ && attacker.health > 2)
494         {
495                 // Savage: vampire mode
496                 if(cvar("g_vampire") && !cvar("g_minstagib"))
497                 {
498                         attacker.health += damage;
499                 }
500                 if(cvar("g_runematch"))
501                 {
502                         if (attacker.runes & RUNE_VAMPIRE)
503                         {
504                         // apply vampire rune
505                                 if (attacker.runes & CURSE_EMPATHY) // have the curse too
506                                 {
507                                         //attacker.health = attacker.health + damage * cvar("g_balance_rune_vampire_combo_absorb");
508                                         attacker.health = bound(
509                                                 cvar("g_balance_curse_empathy_minhealth"), // LA: was 3, now 40
510                                                 attacker.health + damage * cvar("g_balance_rune_vampire_combo_absorb"),
511                                                 cvar("g_balance_rune_vampire_maxhealth"));      // LA: was 1000, now 500
512                                 }
513                                 else
514                                 {
515                                         //attacker.health = attacker.health + damage * cvar("g_balance_rune_vampire_absorb");
516                                         attacker.health = bound(
517                                                 attacker.health,        // LA: was 3, but changed so that you can't lose health
518                                                                                         // empathy won't let you gain health in the same way...
519                                                 attacker.health + damage * cvar("g_balance_rune_vampire_absorb"),
520                                                 cvar("g_balance_rune_vampire_maxhealth"));      // LA: was 1000, now 500
521                                         }
522                         }
523                         // apply empathy curse
524                         else if (attacker.runes & CURSE_EMPATHY)
525                         {
526                                 attacker.health = bound(
527                                         cvar("g_balance_curse_empathy_minhealth"), // LA: was 3, now 20
528                                         attacker.health + damage * cvar("g_balance_curse_empathy_takedamage"),
529                                         attacker.health);
530                         }
531                 }
532         }
533
534         // apply mirror damage if any
535         if(mirrordamage > 0 || mirrorforce > 0)
536         {
537                 attacker = attacker_save;
538                 if(cvar("g_minstagib"))
539                         if(mirrordamage > 0)
540                         {
541                                 // just lose extra LIVES, don't kill the player for mirror damage
542                                 if(attacker.armorvalue > 0)
543                                 {
544                                         attacker.armorvalue = attacker.armorvalue - 1;
545                                         centermsg_setfor(attacker, CENTERMSG_MINSTAGIB, strcat("^3Remaining extra lives: ",ftos(attacker.armorvalue)));
546                                         if(clienttype(attacker) == CLIENTTYPE_REAL) stuffcmd(attacker, "play2 misc/hit.wav\n");
547                                 }
548                                 mirrordamage = 0;
549                         }
550                 force = normalize(attacker.origin + attacker.view_ofs - hitloc) * mirrorforce;
551                 Damage(attacker, inflictor, attacker, mirrordamage, DEATH_MIRRORDAMAGE, attacker.origin, force);
552         }
553 }
554
555 void RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype)
556 {
557         entity  targ;
558         float   finaldmg;
559         float   power;
560         vector  blastorigin;
561         vector  force;
562         vector  m1;
563         vector  m2;
564         vector  nearest;
565         vector  diff;
566
567         blastorigin = (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5);
568
569         targ = findradius (blastorigin, rad);
570         while (targ)
571         {
572                 if (targ != inflictor)
573                         if (ignore != targ)
574                         {
575                                 // LordHavoc: measure distance to nearest point on target (not origin)
576                                 // (this guarentees 100% damage on a touch impact)
577                                 nearest = blastorigin;
578                                 m1 = targ.origin + targ.mins;
579                                 m2 = targ.origin + targ.maxs;
580                                 if (nearest_x < m1_x) nearest_x = m1_x;
581                                 if (nearest_y < m1_y) nearest_y = m1_y;
582                                 if (nearest_z < m1_z) nearest_z = m1_z;
583                                 if (nearest_x > m2_x) nearest_x = m2_x;
584                                 if (nearest_y > m2_y) nearest_y = m2_y;
585                                 if (nearest_z > m2_z) nearest_z = m2_z;
586                                 diff = nearest - blastorigin;
587                                 // round up a little on the damage to ensure full damage on impacts
588                                 // and turn the distance into a fraction of the radius
589                                 power = 1 - ((vlen (diff) - 2) / rad);
590                                 //bprint(" ");
591                                 //bprint(ftos(power));
592                                 if (power > 0)
593                                 {
594                                         if (power > 1)
595                                                 power = 1;
596                                         finaldmg = coredamage * power + edgedamage * (1 - power);
597                                         if (finaldmg > 0)
598                                         {
599                                                 force = normalize((m1 + m2) * 0.5 - blastorigin) * (finaldmg / coredamage) * forceintensity;
600                                                 if (targ == attacker)
601                                                         finaldmg = finaldmg * cvar("g_balance_selfdamagepercent");      // Partial damage if the attacker hits himself
602                                                 // test line of sight to multiple positions on box,
603                                                 // and do damage if any of them hit
604                                                 local float c;
605                                                 c = ceil(finaldmg / 10);
606                                                 if (c > 20)
607                                                         c = 20;
608                                                 while (c > 0)
609                                                 {
610                                                         c = c - 1;
611                                                         traceline(blastorigin, nearest, TRUE, inflictor);
612                                                         if (trace_fraction == 1 || trace_ent == targ
613                                                             || cvar("g_throughfloor"))
614                                                         {
615                                                                 Damage (targ, inflictor, attacker, finaldmg, deathtype, nearest, force);
616                                                                 break;
617                                                         }
618                                                         nearest_x = m1_x + random() * targ.size_x;
619                                                         nearest_y = m1_y + random() * targ.size_y;
620                                                         nearest_z = m1_z + random() * targ.size_z;
621                                                 }
622                                         }
623                                 }
624                         }
625                 targ = targ.chain;
626         }
627 }
628
629 /*
630 entity  multi_ent;
631 float   multi_damage;
632 vector  multi_force;
633
634 void ClearMultiDamage (void)
635 {
636         multi_ent = world;
637         multi_damage = 0;
638         multi_force = '0 0 0';
639 }
640
641 void ApplyMultiDamage (void)
642 {
643         if (!multi_ent)
644                 return;
645
646         Damage (self, multi_ent.origin, multi_ent, 0, multi_damage, multi_force);
647 }
648
649 void AddMultiDamage (entity hit, float damage, vector force)
650 {
651         if (!hit)
652                 return;
653
654         if (hit != multi_ent)
655         {
656                 ApplyMultiDamage ();
657                 ClearMultiDamage ();
658                 multi_ent = hit;
659         }
660         multi_damage = multi_damage + damage;
661         multi_force = multi_force + force;
662 }
663
664 void FireBullets (float shotcount, vector dir, vector spread, float deathtype)
665 {
666         vector  direction;
667         vector  source;
668         vector  vel;
669         vector  org;
670
671         makevectors (self.v_angle);
672
673         source = self.origin + v_forward * 10;  // FIXME
674         source_x = self.absmin_z + self.size_z * 0.7;   // ??? whaddabout view_ofs
675
676         // LordHavoc: better to use normal damage
677         //ClearMultiDamage ();
678         while (shotcount > 0)
679         {
680                 direction = dir + crandom () * spread_x * v_right + crandom () * spread_y * v_up;
681
682                 traceline (source, source + direction * 2048, FALSE, self);
683                 if (trace_fraction != 1.0)
684                 {
685                         vel = normalize (direction + v_up * crandom () + v_right * crandom ());
686                         vel = vel + 2 * trace_plane_normal;
687                         vel = vel * 200;
688
689                         org = trace_endpos - direction * 4;
690
691                         if (!trace_ent.takedamage)
692                                 te_gunshot (org);
693                         // LordHavoc: better to use normal damage
694                         //AddMultiDamage (trace_ent, 4, direction * 4);
695                         Damage (trace_ent, self, self, 4, deathtype, trace_endpos, direction * 4);
696                 }
697
698                 shotcount = shotcount + 1;
699         }
700
701         // LordHavoc: better to use normal damage
702         //ApplyMultiDamage ();
703 }
704 */
705
706
707