]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/mutator_riflearena.qc
3355f5baec6809b8a07517a39ef90c257e150cb7
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator_riflearena.qc
1 .entity ra_nade;
2 .float ra_nade_refire;
3
4 void ra_nade_timer_think()
5 {
6         self.skin = 8 - (self.owner.wait - time) / (autocvar_g_riflearena_nade_lifetime / 10);
7         self.nextthink = time;
8         if(!self.owner || wasfreed(self.owner))
9                 remove(self);
10         
11 }
12
13 void ra_nade_burn_spawn(entity nade)
14 {
15         switch(nade.realowner.team)
16         {
17                 case NUM_TEAM_1:
18                         CSQCProjectile(nade, TRUE, PROJECTILE_NADE_RED_BURN, TRUE);
19                         break;
20                 case NUM_TEAM_2:
21                         CSQCProjectile(nade, TRUE, PROJECTILE_NADE_BLUE_BURN, TRUE);
22                         break;
23                 default:
24                         CSQCProjectile(nade, TRUE, PROJECTILE_NADE_RED_BURN, TRUE);
25                         break;
26         }
27 }
28
29 void ra_nade_spawn(entity nade)
30 {
31         entity timer = spawn();
32         setmodel(timer, "models/ok_nade_counter/ok_nade_counter.md3");
33         setattachment(timer, nade, "");
34         timer.classname = "nade_timer";
35         timer.colormap = nade.colormap;
36         timer.glowmod = nade.glowmod;
37         timer.think = ra_nade_timer_think;
38         timer.nextthink = time;
39         timer.wait = nade.wait;
40         timer.owner = nade;     
41         timer.skin = 10;
42         
43         CSQCProjectile(nade, TRUE, ((nade.realowner.team == NUM_TEAM_2) ? PROJECTILE_NADE_BLUE: PROJECTILE_NADE_RED) , TRUE);
44         
45 }
46
47 void ra_nade_boom() // TODO: DamageInfo
48 {
49         sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTN_NORM);
50         sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
51         pointparticles(particleeffectnum((self.realowner.team == NUM_TEAM_2) ? "nade_blue_explode" : "nade_red_explode"), self.origin + '0 0 1', '0 0 0', 1);
52
53
54         self.takedamage = DAMAGE_NO;
55         RadiusDamage(self, self.realowner, autocvar_g_riflearena_nade_damage, autocvar_g_riflearena_nade_edgedamage,
56                                  autocvar_g_riflearena_nade_radius, self, autocvar_g_riflearena_nade_force, self.projectiledeathtype, self.enemy);
57
58         remove(self);
59 }
60
61 void ra_nade_touch()
62 {
63         PROJECTILE_TOUCH;
64         setsize(self, '-2 -2 -2', '2 2 2');
65         UpdateCSQCProjectile(self);
66         if(self.health == autocvar_g_riflearena_nade_health)
67         {
68                 spamsound(self, CH_SHOTS, strcat("weapons/grenade_bounce", ftos(1 + rint(random() * 5)), ".wav"), VOL_BASE, ATTN_NORM);
69                 return;
70         }
71
72         self.enemy = other;
73         ra_nade_boom();
74 }
75
76 void ra_nade_beep()
77 {
78         sound(self, CH_SHOTS_SINGLE, "overkill/grenadebip.wav", VOL_BASE, 0.5 *(ATTN_LARGE + ATTN_MAX));
79         self.think = ra_nade_boom;
80         self.nextthink = max(self.wait, time);
81 }
82
83 void ra_nade_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
84 {
85         if(DEATH_ISWEAPON(deathtype, WEP_LASER))
86                 return;
87         
88         self.velocity += force;
89
90         if(!damage)
91                 return;
92
93         if(self.health == autocvar_g_riflearena_nade_health)
94         {
95                 sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, 0.5 *(ATTN_LARGE + ATTN_MAX));
96                 self.nextthink = max(time + autocvar_g_riflearena_nade_lifetime, time);
97                 self.think = ra_nade_beep;
98         }
99
100         self.health   -= damage;
101         self.realowner = attacker;
102
103         if(self.health <= 0)
104                 W_PrepareExplosionByDamage(attacker, ra_nade_boom);
105         else
106                 ra_nade_burn_spawn(self);
107 }
108
109 void ra_toss_nade(vector _velocity, float _time)
110 {
111         entity _nade = self.ra_nade;
112         self.ra_nade = world;
113         
114         setorigin(_nade, gettaginfo(_nade, gettagindex(_nade, "Object001")));
115         setattachment(_nade, world, "");
116         PROJECTILE_MAKETRIGGER(_nade);
117         setsize(_nade, '-16 -16 -16', '16 16 16');
118         _nade.movetype = MOVETYPE_BOUNCE;
119         
120         if(self.crouch)
121                 _nade.velocity = '0 0 -10';
122         else if(autocvar_g_riflearena_nade_newton_style == 1)
123                 _nade.velocity = self.velocity + _velocity;
124         else if(autocvar_g_riflearena_nade_newton_style == 2)
125                 _nade.velocity = _velocity;
126         else
127                 _nade.velocity = W_CalculateProjectileVelocity(self.velocity, _velocity, FALSE);
128
129         _nade.solid = SOLID_BBOX;
130         _nade.touch = ra_nade_touch;
131         _nade.health = autocvar_g_riflearena_nade_health;
132         _nade.takedamage = DAMAGE_YES;
133         _nade.event_damage = ra_nade_damage;
134         _nade.teleportable = TRUE;
135
136         ra_nade_spawn(_nade);
137
138         if(_time)
139         {
140                 _nade.think = ra_nade_boom;
141                 _nade.nextthink = _time;
142         }
143         else
144                 _nade.projectiledeathtype = DEATH_NADE_NORMAL;
145
146         self.ra_nade_refire = time + autocvar_g_riflearena_nade_refire;
147 }
148
149 void ra_nade_prime()
150 {
151         if(self.ra_nade)
152                 remove(self.ra_nade);
153         
154         self.ra_nade = spawn();
155         setmodel(self.ra_nade, "models/weapons/h_ok_grenade.iqm");
156         setattachment(self.ra_nade, self.weaponentity, "");
157         self.ra_nade.classname = "nade";
158         self.ra_nade.realowner = self;
159         self.ra_nade.colormap = self.colormap;
160         self.ra_nade.glowmod = self.glowmod;
161         self.ra_nade.wait = time + autocvar_g_riflearena_nade_lifetime;
162         self.ra_nade.cnt = time;
163         self.ra_nade.think = ra_nade_beep;
164         self.ra_nade.nextthink = max(self.ra_nade.wait - 3, time);
165         self.ra_nade.projectiledeathtype = DEATH_NADE_NORMAL;
166 }
167
168 MUTATOR_HOOKFUNCTION(ra_PlayerDamage)
169 {
170         if(IS_PLAYER(frag_attacker))
171         {
172                 if (DEATH_ISWEAPON(frag_deathtype, WEP_LASER))
173                 {
174                         if(frag_attacker == frag_target)
175                                 frag_damage = 5;
176                         else
177                                 frag_damage = 0;
178                         if (frag_target != frag_attacker)
179                         {
180                                 if (frag_target.health >= 1 && IS_PLAYER(frag_target))
181                                         centerprint(frag_attacker, "Laser inflicts no damage!");
182                                 frag_force = '0 0 0';
183                         }
184                 }
185         }
186                 
187         return FALSE;
188 }
189
190 MUTATOR_HOOKFUNCTION(ra_PlayerSpawn)
191 {
192         WEPSET_CLEAR_E(self);
193         WEPSET_OR_EW(self, WEP_RIFLE);
194         WEPSET_OR_EW(self, WEP_LASER);
195         
196         self.ra_nade_refire = time + 1;
197         
198         return FALSE;
199 }
200
201 MUTATOR_HOOKFUNCTION(ra_FilterItem)
202 {
203         switch (self.items)
204         {
205                 case IT_5HP:
206                 case IT_ARMOR_SHARD:
207                         return FALSE;
208         }
209                 
210         return TRUE;
211 }
212
213 MUTATOR_HOOKFUNCTION(ra_PlayerThink)
214 {
215         if(self.ra_nade)
216                 if(self.ra_nade.wait - 0.1 <= time)
217                         ra_toss_nade('0 0 0', time + 0.05);
218
219         if(self.ra_nade_refire < time)
220         {
221                 if(self.BUTTON_HOOK)
222                 {
223                         if(!self.ra_nade)
224                                 ra_nade_prime();
225                 }
226                 else if(time - self.ra_nade.cnt >= 1)
227                 {
228                         if(self.ra_nade)
229                         {
230                                 makevectors(self.v_angle);
231                                 float _force = time - self.ra_nade.cnt;
232                                 _force /= autocvar_g_riflearena_nade_lifetime;
233                                 _force = autocvar_g_riflearena_nade_minforce + (_force * (autocvar_g_riflearena_nade_maxforce - autocvar_g_riflearena_nade_minforce));                          
234                                 ra_toss_nade((v_forward * 0.7 + v_up * 0.2 + v_right * 0.1) * _force, 0);
235                         }
236                 }
237         }
238         
239         self.hasweapon_complain_spam = time + 5; // this isn't needed, so keep it off
240         
241         return FALSE;
242 }
243
244 MUTATOR_HOOKFUNCTION(ra_RemovePlayer)
245 {
246         if(self.ra_nade)
247                 remove(self.ra_nade);
248                 
249         return FALSE;
250 }
251
252 MUTATOR_HOOKFUNCTION(ra_StartItems)
253 {
254         start_items |= IT_UNLIMITED_AMMO;
255         start_ammo_nails = 100;
256         
257         g_grappling_hook = 0;
258
259         return FALSE;
260 }
261
262 MUTATOR_HOOKFUNCTION(ra_PlayerDies)
263 {
264         if(self.ra_nade)
265                 ra_toss_nade('0 0 100', max(self.ra_nade.wait, time + 0.05));
266
267         return 0;
268 }
269
270 MUTATOR_HOOKFUNCTION(ra_ForbidThrowCurrentWeapon)
271 {
272         if(self.freezetag_frozen || !autocvar_g_riflearena_nades || self.vehicle)
273                 return 1;
274
275         if(!self.ra_nade)
276         {
277                 if(self.ra_nade_refire < time)
278                 {
279                         Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_NADE);
280                         ra_nade_prime();
281                         self.ra_nade_refire  = time + autocvar_g_riflearena_nade_refire;
282                 }
283         }
284         else
285         {
286                 if(time - self.ra_nade.cnt >= 1)
287                 {
288                         makevectors(self.v_angle);
289                         float _force = time - self.ra_nade.cnt;
290                         _force /= autocvar_g_riflearena_nade_lifetime;
291                         _force = autocvar_g_riflearena_nade_minforce + (_force * (autocvar_g_riflearena_nade_maxforce - autocvar_g_riflearena_nade_minforce));
292                         ra_toss_nade((v_forward * 0.75 + v_up * 0.2 + v_right * 0.05) * _force, 0);
293                 }
294         }
295         return 1;
296 }
297
298 MUTATOR_HOOKFUNCTION(ra_BuildMutatorsString)
299 {
300         ret_string = strcat(ret_string, ":RA");
301         return 0;
302 }
303
304 MUTATOR_HOOKFUNCTION(ra_BuildMutatorsPrettyString)
305 {
306         ret_string = strcat(ret_string, ", Rifle Arena");
307         return 0;
308 }
309
310 MUTATOR_HOOKFUNCTION(ra_SetModname)
311 {
312         modname = "Rifle Arena";
313         return TRUE;
314 }
315
316 MUTATOR_DEFINITION(mutator_riflearena)
317 {
318         MUTATOR_HOOK(PlayerDamage_Calculate, ra_PlayerDamage, CBC_ORDER_ANY);
319         MUTATOR_HOOK(PlayerSpawn, ra_PlayerSpawn, CBC_ORDER_ANY);
320         MUTATOR_HOOK(FilterItem, ra_FilterItem, CBC_ORDER_ANY);
321         MUTATOR_HOOK(SetStartItems, ra_StartItems, CBC_ORDER_ANY);
322         MUTATOR_HOOK(MakePlayerObserver, ra_RemovePlayer, CBC_ORDER_ANY);
323         MUTATOR_HOOK(ClientDisconnect, ra_RemovePlayer, CBC_ORDER_ANY);
324         MUTATOR_HOOK(PlayerDies, ra_PlayerDies, CBC_ORDER_ANY);
325         MUTATOR_HOOK(PlayerPreThink, ra_PlayerThink, CBC_ORDER_ANY);
326         MUTATOR_HOOK(ForbidThrowCurrentWeapon, ra_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
327         MUTATOR_HOOK(BuildMutatorsString, ra_BuildMutatorsString, CBC_ORDER_ANY);
328         MUTATOR_HOOK(BuildMutatorsPrettyString, ra_BuildMutatorsPrettyString, CBC_ORDER_ANY);
329         
330         MUTATOR_ONADD
331         {
332                 cvar_settemp("g_balance_rifle_secondary_spread", "0");
333                 cvar_settemp("g_balance_rifle_secondary_shots", "1");
334                 cvar_settemp("g_balance_rifle_secondary_animtime", "0.15");
335                 cvar_settemp("g_balance_rifle_secondary_refire", "0.15");
336                 cvar_settemp("g_balance_rifle_secondary_damage", "40");
337                 precache_model("models/ok_nade_counter/ok_nade_counter.md3");
338                 
339                 precache_model("models/weapons/h_ok_grenade.iqm");
340                 precache_model("models/weapons/v_ok_grenade.md3");
341                 precache_sound("weapons/rocket_impact.wav");
342                 precache_sound("weapons/grenade_bounce1.wav");
343                 precache_sound("weapons/grenade_bounce2.wav");
344                 precache_sound("weapons/grenade_bounce3.wav");
345                 precache_sound("weapons/grenade_bounce4.wav");
346                 precache_sound("weapons/grenade_bounce5.wav");
347                 precache_sound("weapons/grenade_bounce6.wav");
348                 precache_sound("overkill/grenadebip.wav");
349                 
350                 weapon_action(WEP_LASER, WR_PRECACHE);
351                 weapon_action(WEP_RIFLE, WR_PRECACHE);
352                 
353                 get_weaponinfo(WEP_HOOK).spawnflags |= WEP_FLAG_MUTATORBLOCKED;
354         }
355         
356         MUTATOR_ONROLLBACK_OR_REMOVE
357         {
358                 get_weaponinfo(WEP_HOOK).spawnflags &~= WEP_FLAG_MUTATORBLOCKED;
359         }
360         
361         MUTATOR_ONREMOVE
362         {
363                 print("This cannot be removed at runtime\n");
364                 return -1;
365         }
366
367         return 0;
368 }