]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/gamec/g_damage.c
added minstagib mutator, killmessages and a first bunch of announcer messages (soundf...
[divverent/nexuiz.git] / data / qcsrc / gamec / g_damage.c
1
2 float checkrules_firstblood;
3
4 void GiveFrags (entity attacker, entity targ, float f)
5 {
6         if(f > 0 && cvar("g_domination") && cvar("g_domination_disable_frags"))
7                 return;
8         else if(f > 0 && cvar("g_runematch"))
9                 f = RunematchHandleFrags(attacker, targ, f);
10
11         if(f)
12                 attacker.frags = attacker.frags + f;
13 }
14
15 void Obituary (entity attacker, entity targ, float deathtype)
16 {
17         string  s;
18
19         if (targ.classname == "player" || targ.classname == "corpse")
20         {
21                 if (targ.classname == "corpse")
22                         s = "A corpse";
23                 else
24                         s = targ.netname;
25
26                 if (targ == attacker)
27                 {
28                         if (deathtype == DEATH_NOAMMO)
29                                 centerprint(targ, strcat("^1You were killed for running out of ammo...\n\n\n"));
30                         else
31                                 centerprint(targ, strcat("^1You killed your dumb self!\n\n\n"));
32                         
33                         if (deathtype == IT_GRENADE_LAUNCHER)
34                                 bprint ("^1",s, "^1 detonated\n");
35                         else if (deathtype == IT_ELECTRO)
36                                 bprint ("^1",s, "^1 played with plasma\n");
37                         else if (deathtype == IT_ROCKET_LAUNCHER)
38                                 bprint ("^1",s, "^1 exploded\n");
39                         else if (deathtype == DEATH_KILL)
40                                 bprint ("^1",s, "^1 couldn't take it anymore\n");
41                         else if (deathtype == DEATH_NOAMMO)
42                         {
43                                 bprint ("^7",s, " ^7suicided after wasting all his ammo\n");
44                                 sound (self, CHAN_BODY, "minstagib/mockery.ogg", 1, ATTN_NONE);
45                         }
46                         else
47                                 bprint ("^1",s, "^1 couldn't resist the urge to self-destruct\n");
48                         GiveFrags(attacker, targ, -1);
49                         //targ.frags = targ.frags - 1;
50                         if (targ.killcount > 2)
51                                 bprint ("^1",s,"^1 ended it all with a ",ftos(targ.killcount)," kill spree\n");
52                 }
53                 else if (teamplay && attacker.team == targ.team)
54                 {
55                         centerprint(attacker, strcat("^1Moron! You killed a teammate!\n\n\n"));
56                         bprint ("^1", attacker.netname, "^1 mows down a teammate\n");
57                         GiveFrags(attacker, targ, -1);
58                         //attacker.frags = attacker.frags - 1;
59                         if (targ.killcount > 2)
60                                 bprint ("^1",s,"'s ^1",ftos(targ.killcount)," kill spree was endeded by a teammate!\n");
61                         if (attacker.killcount > 2)
62                                 bprint ("^1",attacker.netname,"^1 ended a ",ftos(attacker.killcount)," kill spree by killing a teammate\n");
63                         attacker.killcount = 0;
64                 }
65                 else if (attacker.classname == "player" || attacker.classname == "gib")
66                 {
67                         if (!checkrules_firstblood)
68                         {
69                                 checkrules_firstblood = TRUE;
70                                 //sound(world, CHAN_AUTO, "announcer/firstblood.wav", 1, ATTN_NONE);
71                                 if (cvar("g_minstagib"))
72                                         sound(world, CHAN_AUTO, "announce/male/mapkill1.ogg", 1, ATTN_NONE);
73                                 bprint("^1",attacker.netname, "^1 drew first blood", "\n");
74                         }
75
76                         centerprint(attacker, strcat("^4You killed ^7", s, "\n\n\n"));
77                         centerprint(targ, strcat("^1You were killed by ^7", attacker.netname, "\n\n\n"));
78                         
79                         if (deathtype == IT_LASER)
80                                 bprint ("^1",s, "^1 was blasted by ", attacker.netname, "\n");
81                         else if (deathtype == IT_UZI)
82                                 bprint ("^1",s, "^1 was riddled full of holes by ", attacker.netname, "\n");
83                         else if (deathtype == IT_SHOTGUN)
84                                 bprint ("^1",s, "^1 was gunned by ", attacker.netname, "\n");
85                         else if (deathtype == IT_GRENADE_LAUNCHER)
86                                 bprint ("^1", s, "^1 was blasted by ", attacker.netname, "\n");
87                         else if (deathtype == IT_ELECTRO)
88                                 bprint ("^1",s, "^1 was blasted by ", attacker.netname, "\n");
89                         else if (deathtype == IT_CRYLINK)
90                                 bprint ("^1",s, "^1 was blasted by ", attacker.netname, "\n");
91                         else if (deathtype == IT_NEX)
92                                 bprint ("^1",s, "^1 has been vaporized by ", attacker.netname, "\n");
93                         else if (deathtype == IT_HAGAR)
94                                 bprint ("^1",s, "^1 was pummeled by ", attacker.netname, "\n");
95                         else if (deathtype == IT_ROCKET_LAUNCHER)
96                                 bprint ("^1",s, "^1 was blasted by ", attacker.netname, "\n");
97                         else if (deathtype == DEATH_TELEFRAG)
98                                 bprint ("^1",s, "^1 was telefragged by ", attacker.netname, "\n");
99                         else if (deathtype == DEATH_DROWN)
100                                 bprint ("^1",s, "^1 was drowned by ", attacker.netname, "\n");
101                         else if (deathtype == DEATH_SLIME)
102                                 bprint ("^1",s, "^1 was slimed by ", attacker.netname, "\n");
103                         else if (deathtype == DEATH_LAVA)
104                                 bprint ("^1",s, "^1 was cooked by ", attacker.netname, "\n");
105                         else if (deathtype == DEATH_FALL)
106                                 bprint ("^1",s, "^1 was grounded by ", attacker.netname, "\n");
107                         else if (deathtype == DEATH_HURTTRIGGER)
108                                 bprint ("^1",s, "^1 was thrown into a world of hurt by ", attacker.netname, "\n");
109                         else
110                                 bprint ("^1",s, "^1 was killed by ", attacker.netname, "\n");
111
112                         GiveFrags(attacker, targ, 1);
113                         //attacker.frags = attacker.frags + 1;
114                         if (targ.killcount > 2)
115                                 bprint ("^1",s,"'s ^1", ftos(targ.killcount), " kill spree was ended by ", attacker.netname, "\n");
116                         attacker.killcount = attacker.killcount + 1;
117                         if (attacker.killcount > 2)
118                                 bprint ("^1",attacker.netname,"^1 has ",ftos(attacker.killcount)," kills in a row\n");
119
120                         if (attacker.killcount == 3)
121                         {
122                                 bprint (attacker.netname,"^7 made a ^1Multikill\n");
123                                 stuffcmd(attacker, "play2 announce/male/kill3.ogg\n");
124                         }
125                         else if (attacker.killcount == 4)
126                         {
127                                 bprint (attacker.netname,"^7 made a ^1Ultrakill\n");
128                                 stuffcmd(attacker, "play2 announce/male/kill4.ogg\n");
129                         }
130                         else if (attacker.killcount == 5)
131                         {
132                                 bprint (attacker.netname,"^7 made a ^1MONSTERKILL\n");
133                                 stuffcmd(attacker, "play2 announce/male/kill5.ogg\n");
134                         }
135                         else if (attacker.killcount == 6)
136                         {
137                                 bprint (attacker.netname,"^7 is on a ^1Killingspree\n");
138                                 stuffcmd(attacker, "play2 announce/male/kill6.ogg\n");
139                         }
140                         else if (attacker.killcount == 10)
141                         {
142                                 bprint (attacker.netname,"^7 is on a ^1Rampage\n");
143                                 sound(world, CHAN_AUTO, "announce/male/kill10.ogg", 1, ATTN_NONE);
144                         }
145                         else if (attacker.killcount == 15)
146                         {
147                                 bprint (attacker.netname,"^7 is ^1Dominating\n");
148                                 sound(world, CHAN_AUTO, "announce/male/kill15.ogg", 1, ATTN_NONE);
149                         }
150                         else if (attacker.killcount == 20)
151                         {
152                                 bprint (attacker.netname,"^7 is ^1Unstoppable\n");
153                                 sound(world, CHAN_AUTO, "announce/male/kill20.ogg", 1, ATTN_NONE);
154                         }
155                         else if (attacker.killcount == 25)
156                         {
157                                 bprint (attacker.netname,"^7 is ^1Godlike\n");
158                                 sound(world, CHAN_AUTO, "announce/male/kill25.ogg", 1, ATTN_NONE);
159                         }
160                         else if (attacker.killcount >= 30)
161                         {
162                                 bprint (attacker.netname,"^7 is ^1Cheating!!!\n");
163                                 sound(world, CHAN_AUTO, "announce/male/kill30.ogg", 1, ATTN_NONE);
164                         }
165
166                 }
167                 else
168                 {
169                         centerprint(targ, strcat("^1Watch your step!\n\n\n"));
170                         if (deathtype == DEATH_HURTTRIGGER && attacker.message != "")
171                                 bprint ("^1",s, "^1 ", attacker.message, "\n");
172                         else if (deathtype == DEATH_DROWN)
173                                 bprint ("^1",s, "^1 drowned\n");
174                         else if (deathtype == DEATH_SLIME)
175                                 bprint ("^1",s, "^1 was slimed\n");
176                         else if (deathtype == DEATH_LAVA)
177                                 bprint ("^1",s, "^1 turned into hot slag\n");
178                         else if (deathtype == DEATH_FALL)
179                                 bprint ("^1",s, "^1 hit the ground with a crunch\n");
180                         else
181                                 bprint ("^1",s, "^1 died\n");
182                         GiveFrags(targ, targ, -1);
183                         //targ.frags = targ.frags - 1;
184                         if (targ.killcount > 2)
185                                 bprint ("^1",s,"^1 died with a ",ftos(targ.killcount)," kill spree\n");
186                 }
187                 // FIXME: this should go in PutClientInServer
188                 if (targ.killcount)
189                         targ.killcount = 0;
190         }
191 }
192
193 // these are updated by each Damage call for use in button triggering and such
194 entity damage_targ;
195 entity damage_inflictor;
196 entity damage_attacker;
197
198 void Damage (entity targ, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
199 {
200         if (gameover)
201                 return;
202
203         local entity oldself;
204         oldself = self;
205         self = targ;
206         damage_targ = targ;
207         damage_inflictor = inflictor;
208         damage_attacker = attacker;     
209         // nullify damage if teamplay is on
210         if (teamplay)
211         if (attacker.team)
212         if (attacker.team == targ.team)
213         if (teamplay == 1 || (teamplay == 3 && attacker != targ))
214                 damage = 0;
215         
216         if (cvar("g_minstagib"))
217         {
218                 if ((deathtype == DEATH_FALL)  || 
219                     (deathtype == DEATH_DROWN) || 
220                     (deathtype == DEATH_SLIME) || 
221                     (deathtype == DEATH_LAVA))
222                         return;
223                 if (targ.extralives && (deathtype == IT_NEX))
224                 {
225                         targ.extralives = targ.extralives - 1;
226                         centerprint(targ, strcat("^3Remaining extra lives: ",ftos(targ.extralives),"\n"));
227                         damage = 0;
228                         targ.armorvalue = targ.extralives;
229                         stuffcmd(targ, "play2 misc/hit.wav\n");
230                         stuffcmd(attacker, "play2 misc/hit.wav\n");
231                 }
232                 else if (deathtype == IT_NEX && targ.items & IT_STRENGTH)
233                 {
234                         stuffcmd(attacker, "play2 announce/male/lucky_shot.ogg\n");
235                 }
236                 if (deathtype == IT_LASER)
237                 {
238                         damage = 0;
239                         if (targ != attacker)
240                         {
241                                 if (targ.classname == "player")
242                                         centerprint(attacker, "Secondary fire inflicts no damage!\n");
243                                 damage = 0;
244                                 force = '0 0 0';
245                                 attacker = targ;
246                         }
247                 }
248         }
249         
250         // midair gamemode: damage only while in the air
251         if (cvar("g_midair")
252             && self.classname == "player" // e.g. grenades take damage
253             && self.flags & FL_ONGROUND) {
254                 damage = 0;
255         }
256
257         // apply strength multiplier
258         if (attacker.items & IT_STRENGTH && !cvar("g_minstagib"))
259         {
260                 damage = damage * cvar("g_balance_powerup_strength_damage");
261                 force = force * cvar("g_balance_powerup_strength_force");
262         }
263         // apply invincibility multiplier
264         if (targ.items & IT_INVINCIBLE && !cvar("g_minstagib"))
265                 damage = damage * cvar("g_balance_powerup_invincible_takedamage");
266
267
268         if(cvar("g_runematch"))
269         {
270                 // apply strength rune
271                 if (attacker.runes & RUNE_STRENGTH)
272                 {
273                         if(attacker.runes & CURSE_WEAK) // have both curse & rune
274                         {
275                                 damage = damage * cvar("g_balance_rune_strength_combo_damage");
276                                 force = force * cvar("g_balance_rune_strength_combo_force");
277                         }
278                         else
279                         {
280                                 damage = damage * cvar("g_balance_rune_strength_damage");
281                                 force = force * cvar("g_balance_rune_strength_force");
282                         }
283                 }
284                 else if (attacker.runes & CURSE_WEAK)
285                 {
286                         damage = damage * cvar("g_balance_curse_weak_damage");
287                         force = force * cvar("g_balance_curse_weak_force");
288                 }
289
290                 // apply defense rune
291                 if (targ.runes & RUNE_DEFENSE)
292                 {
293                         if (targ.runes & CURSE_VULNER) // have both curse & rune
294                                 damage = damage * cvar("g_balance_rune_defense_combo_takedamage");
295                         else
296                                 damage = damage * cvar("g_balance_rune_defense_takedamage");
297                 }
298                 else if (targ.runes & CURSE_VULNER)
299                         damage = damage * cvar("g_balance_curse_vulner_takedamage");
300         }
301
302         // apply push
303         if (self.damageforcescale)
304         {
305                 self.velocity = self.velocity + self.damageforcescale * force;
306                 self.flags = self.flags - (self.flags & FL_ONGROUND);
307         }
308         // apply damage
309         if (self.event_damage)
310                 self.event_damage (inflictor, attacker, damage, deathtype, hitloc, force);
311         self = oldself;
312
313         if(targ.classname == "player" && attacker.classname == "player" && attacker != targ && attacker.health > 2)
314         {
315                 // Savage: vampire mode
316                 if(cvar("g_vampire") && !cvar("g_minstagib"))
317                 {
318                         attacker.health += damage;
319                 }
320                 if(cvar("g_runematch"))
321                 {
322                         if (attacker.runes & RUNE_VAMPIRE)
323                         {
324                         // apply vampire rune
325                                 if (attacker.runes & CURSE_EMPATHY) // have the curse too
326                                 {
327                                         //attacker.health = attacker.health + damage * cvar("g_balance_rune_vampire_combo_absorb");
328                                         attacker.health = bound(
329                                                 3, 
330                                                 attacker.health + damage * cvar("g_balance_rune_vampire_combo_absorb"), 
331                                                 1000);
332                                 }
333                                 else
334                                 {
335                                         //attacker.health = attacker.health + damage * cvar("g_balance_rune_vampire_absorb");
336                                         attacker.health = bound(
337                                                 3, 
338                                                 attacker.health + damage * cvar("g_balance_rune_vampire_absorb"), 
339                                                 1000);
340                                 }
341                         }
342                         // apply empathy curse
343                         else if (attacker.runes & CURSE_EMPATHY)
344                         {
345                                 attacker.health = bound(
346                                         3, 
347                                         attacker.health + damage * cvar("g_balance_curse_empathy_takedamage"), 
348                                         attacker.health);
349                         }
350                 }
351         }
352 }
353
354 void RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype)
355 {
356         entity  targ;
357         float   finaldmg;
358         float   power;
359         vector  blastorigin;
360         vector  force;
361         vector  m1;
362         vector  m2;
363         vector  nearest;
364         vector  diff;
365
366         blastorigin = (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5);
367
368         targ = findradius (blastorigin, rad);
369         while (targ)
370         {
371                 if (targ != inflictor)
372                         if (ignore != targ)
373                         {
374                                 // LordHavoc: measure distance to nearest point on target (not origin)
375                                 // (this guarentees 100% damage on a touch impact)
376                                 nearest = blastorigin;
377                                 m1 = targ.origin + targ.mins;
378                                 m2 = targ.origin + targ.maxs;
379                                 if (nearest_x < m1_x) nearest_x = m1_x;
380                                 if (nearest_y < m1_y) nearest_y = m1_y;
381                                 if (nearest_z < m1_z) nearest_z = m1_z;
382                                 if (nearest_x > m2_x) nearest_x = m2_x;
383                                 if (nearest_y > m2_y) nearest_y = m2_y;
384                                 if (nearest_z > m2_z) nearest_z = m2_z;
385                                 diff = nearest - blastorigin;
386                                 // round up a little on the damage to ensure full damage on impacts
387                                 // and turn the distance into a fraction of the radius
388                                 power = 1 - ((vlen (diff) - 2) / rad);
389                                 //bprint(" ");
390                                 //bprint(ftos(power));
391                                 if (power > 0)
392                                 {
393                                         if (power > 1)
394                                                 power = 1;
395                                         finaldmg = coredamage * power + edgedamage * (1 - power);
396                                         if (finaldmg > 0)
397                                         {
398                                                 force = normalize((m1 + m2) * 0.5 - blastorigin) * (finaldmg / coredamage) * forceintensity;
399                                                 if (targ == attacker)
400                                                         finaldmg = finaldmg * cvar("g_balance_selfdamagepercent");      // Partial damage if the attacker hits himself
401                                                 Damage (targ, inflictor, attacker, finaldmg, deathtype, inflictor.origin, force);
402                                         }
403                                 }
404                         }
405                 targ = targ.chain;
406         }
407 }
408
409 /*
410 entity  multi_ent;
411 float   multi_damage;
412 vector  multi_force;
413
414 void ClearMultiDamage (void)
415 {
416         multi_ent = world;
417         multi_damage = 0;
418         multi_force = '0 0 0';
419 }
420
421 void ApplyMultiDamage (void)
422 {
423         if (!multi_ent)
424                 return;
425
426         Damage (self, multi_ent.origin, multi_ent, 0, multi_damage, multi_force);
427 }
428
429 void AddMultiDamage (entity hit, float damage, vector force)
430 {
431         if (!hit)
432                 return;
433
434         if (hit != multi_ent)
435         {
436                 ApplyMultiDamage ();
437                 ClearMultiDamage ();
438                 multi_ent = hit;
439         }
440         multi_damage = multi_damage + damage;
441         multi_force = multi_force + force;
442 }
443
444 void FireBullets (float shotcount, vector dir, vector spread, float deathtype)
445 {
446         vector  direction;
447         vector  source;
448         vector  vel;
449         vector  org;
450
451         makevectors (self.v_angle);
452
453         source = self.origin + v_forward * 10;  // FIXME
454         source_x = self.absmin_z + self.size_z * 0.7;   // ??? whaddabout view_ofs
455
456         // LordHavoc: better to use normal damage
457         //ClearMultiDamage ();
458         while (shotcount > 0)
459         {
460                 direction = dir + crandom () * spread_x * v_right + crandom () * spread_y * v_up;
461
462                 traceline (source, source + direction * 2048, FALSE, self);
463                 if (trace_fraction != 1.0)
464                 {
465                         vel = normalize (direction + v_up * crandom () + v_right * crandom ());
466                         vel = vel + 2 * trace_plane_normal;
467                         vel = vel * 200;
468
469                         org = trace_endpos - direction * 4;
470
471                         if (!trace_ent.takedamage)
472                                 te_gunshot (org);
473                         // LordHavoc: better to use normal damage
474                         //AddMultiDamage (trace_ent, 4, direction * 4);
475                         Damage (trace_ent, self, self, 4, deathtype, trace_endpos, direction * 4);
476                 }
477
478                 shotcount = shotcount + 1;
479         }
480
481         // LordHavoc: better to use normal damage
482         //ApplyMultiDamage ();
483 }
484 */