Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
seeker.qc
Go to the documentation of this file.
1#include "seeker.qh"
2
3#ifdef SVQC
4
5// ============================
6// Begin: Missile functions, these are general functions to be manipulated by other code
7// ============================
8void W_Seeker_Missile_Explode(entity this, entity directhitentity)
9{
10 this.event_damage = func_null;
11 RadiusDamage(this, this.realowner,
12 WEP_CVAR(WEP_SEEKER, missile_damage),
13 WEP_CVAR(WEP_SEEKER, missile_edgedamage),
14 WEP_CVAR(WEP_SEEKER, missile_radius),
15 NULL,
16 NULL,
17 WEP_CVAR(WEP_SEEKER, missile_force),
20 directhitentity
21 );
22
23 delete(this);
24}
25
30
37
39{
40 if (time > this.cnt)
41 {
44 }
45
46 float spd = vlen(this.velocity);
47 spd = bound(
48 spd - WEP_CVAR(WEP_SEEKER, missile_decel) * frametime,
49 WEP_CVAR(WEP_SEEKER, missile_speed_max),
50 spd + WEP_CVAR(WEP_SEEKER, missile_accel) * frametime
51 );
52
53 if (this.enemy != NULL
54 && (this.enemy.takedamage != DAMAGE_AIM || IS_DEAD(this.enemy)))
55 this.enemy = NULL;
56
57 float dist;
58 if (this.enemy != NULL)
59 {
60 vector eorg = 0.5 * (this.enemy.absmin + this.enemy.absmax);
61 vector desireddir = normalize(eorg - this.origin);
62 vector olddir = normalize(this.velocity); // get my current direction
63 dist = vlen(eorg - this.origin);
64
65 // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
66 if (WEP_CVAR(WEP_SEEKER, missile_smart) && dist > WEP_CVAR(WEP_SEEKER, missile_smart_mindist))
67 {
68 // Is it a better idea (shorter distance) to trace to the target itself?
69 if (vdist(this.origin + olddir * this.wait, <, dist))
70 traceline(this.origin, this.origin + olddir * this.wait, false, this);
71 else
72 traceline(this.origin, eorg, false, this);
73
74 // Setup adaptive tracelength
75 this.wait = bound(WEP_CVAR(WEP_SEEKER, missile_smart_trace_min), vlen(this.origin - trace_endpos), WEP_CVAR(WEP_SEEKER, missile_smart_trace_max));
76
77 // Calc how important it is that we turn and add this to the desierd (enemy) dir.
78 desireddir = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5);
79 }
80
81 vector newdir = normalize(olddir + desireddir * WEP_CVAR(WEP_SEEKER, missile_turnrate)); // take the average of the 2 directions; not the best method but simple & easy
82 this.velocity = newdir * spd; // make me fly in the new direction at my flight speed
83 }
84 else
85 dist = 0;
86
87 // Proxy
88 if (WEP_CVAR(WEP_SEEKER, missile_proxy))
89 {
90 if (dist <= WEP_CVAR(WEP_SEEKER, missile_proxy_maxrange))
91 {
92 if (this.cvar_cl_autoswitch == 0)
93 this.cvar_cl_autoswitch = time + WEP_CVAR(WEP_SEEKER, missile_proxy_delay);
94 else
95 {
96 if (this.cvar_cl_autoswitch <= time)
97 {
99 this.cvar_cl_autoswitch = 0;
100 }
101 }
102 }
103 else
104 {
105 if (this.cvar_cl_autoswitch != 0)
106 this.cvar_cl_autoswitch = 0;
107 }
108 }
110
111 if (IS_DEAD(this.enemy))
112 {
113 this.enemy = NULL;
114 this.cnt = time + 1 + (random() * 4);
115 this.nextthink = this.cnt;
116 return;
117 }
118
119 //this.angles = vectoangles(this.velocity); // turn model in the new flight direction
120 this.nextthink = time;// + 0.05; // csqc projectiles
122}
123
124
125
126void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
127{
128 if (GetResource(this, RES_HEALTH) <= 0)
129 return;
130 if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
131 return; // g_projectiles_damage says to halt
132
133 if (this.realowner == attacker)
134 TakeResource(this, RES_HEALTH, (damage * 0.25));
135 else
136 TakeResource(this, RES_HEALTH, damage);
137
138 if (GetResource(this, RES_HEALTH) <= 0)
140}
141
142/*
143void W_Seeker_Missile_Animate(entity this)
144{
145 ++this.frame;
146 this.nextthink = time + 0.05;
147
148 if (this.enemy != NULL
149 && (this.enemy.takedamage != DAMAGE_AIM || IS_DEAD(this.enemy)))
150 this.enemy = NULL;
151
152 if (this.frame == 5)
153 {
154 this.think = W_Seeker_Missile_Think;
155 this.nextthink = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles
156 this.move_movetype = (autocvar_g_balance_seeker_missile_proxy)
157 ? MOVETYPE_BOUNCEMISSILE
158 : MOVETYPE_FLYMISSILE;
159 }
160
161 UpdateCSQCProjectile(this);
162}
163*/
164
165void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, .entity weaponentity, vector f_diff, entity m_target)
166{
167 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, missile_ammo), weaponentity);
168
169 makevectors(actor.v_angle);
170 W_SetupShot_ProjectileSize(actor, weaponentity, '-4 -4 -4', '4 4 4', false, 2, SND_SEEKER_FIRE, CH_WEAPON_A, 0, ((m_target != NULL) ? thiswep.m_id | HITTYPE_SECONDARY : thiswep.m_id));
171 w_shotorg += f_diff;
172 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
173
174 //actor.detornator = false;
175
176 entity missile = new(seeker_missile);
177 missile.owner = missile.realowner = actor;
178 missile.bot_dodge = true;
179 missile.bot_dodgerating = WEP_CVAR(WEP_SEEKER, missile_damage);
180
183 missile.event_damage = W_Seeker_Missile_Damage;
184 missile.nextthink = time; // + 0.2; // + cvar("g_balance_seeker_missile_activate_delay");
185 missile.cnt = time + WEP_CVAR(WEP_SEEKER, missile_lifetime);
186 missile.enemy = m_target;
187 missile.solid = SOLID_BBOX;
188 missile.scale = 2;
189 missile.takedamage = DAMAGE_YES;
190 missile.weaponentity_fld = weaponentity;
191 SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(WEP_SEEKER, missile_health));
192 missile.damageforcescale = WEP_CVAR(WEP_SEEKER, missile_damageforcescale);
193 missile.damagedbycontents = true;
195 //missile.think = W_Seeker_Missile_Animate; // csqc projectiles.
196
197 if (missile.enemy != NULL)
198 missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
199 else
200 missile.projectiledeathtype = thiswep.m_id;
201
202 setorigin(missile, w_shotorg);
203 setsize(missile, '-4 -4 -4', '4 4 4');
205 missile.flags = FL_PROJECTILE;
206 IL_PUSH(g_projectiles, missile);
207 IL_PUSH(g_bot_dodge, missile);
208 missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG;
209
210 W_SetupProjVelocity_UP_PRE(missile, WEP_SEEKER, missile_);
211
212 missile.angles = vectoangles(missile.velocity);
213
214 CSQCProjectile(missile, false, PROJECTILE_SEEKER, true);
215
216 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
217}
218
219// ============================
220// Begin: FLAC, close range attack meant for defeating rockets which are coming at you.
221// ============================
222void W_Seeker_Flac_Explode(entity this, entity directhitentity)
223{
224 this.event_damage = func_null;
225
226 RadiusDamage(this, this.realowner,
227 WEP_CVAR(WEP_SEEKER, flac_damage),
228 WEP_CVAR(WEP_SEEKER, flac_edgedamage),
229 WEP_CVAR(WEP_SEEKER, flac_radius),
230 NULL,
231 NULL,
232 WEP_CVAR(WEP_SEEKER, flac_force),
234 this.weaponentity_fld,
235 directhitentity
236 );
237
238 delete(this);
239}
240
245
247{
248 W_Seeker_Flac_Explode(this, trigger);
249}
250
251void W_Seeker_Fire_Flac(Weapon thiswep, entity actor, .entity weaponentity)
252{
253 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, flac_ammo), weaponentity);
254
255 vector f_diff;
256 switch (actor.(weaponentity).bulletcounter % 4)
257 {
258 case 0:
259 f_diff = '-1.25 -3.75 0';
260 break;
261 case 1:
262 f_diff = '+1.25 -3.75 0';
263 break;
264 case 2:
265 f_diff = '-1.25 +3.75 0';
266 break;
267 case 3:
268 default:
269 f_diff = '+1.25 +3.75 0';
270 break;
271 }
272 W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_SEEKER_FLAC_FIRE, CH_WEAPON_A, WEP_CVAR(WEP_SEEKER, flac_damage), thiswep.m_id | HITTYPE_SECONDARY);
273 w_shotorg += f_diff;
274
275 // uses hagar effects!
276 W_MuzzleFlash(WEP_HAGAR, actor, weaponentity, w_shotorg, w_shotdir);
277
278 entity missile;
279 missile = new(missile);
280 missile.owner = missile.realowner = actor;
281 missile.bot_dodge = true;
282 missile.bot_dodgerating = WEP_CVAR(WEP_SEEKER, flac_damage);
284 missile.use = W_Seeker_Flac_Explode_use;
286 missile.nextthink = time + WEP_CVAR(WEP_SEEKER, flac_lifetime) + WEP_CVAR(WEP_SEEKER, flac_lifetime_rand);
287 missile.solid = SOLID_BBOX;
288 set_movetype(missile, MOVETYPE_FLY);
289 missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
290 missile.weaponentity_fld = weaponentity;
291 missile.flags = FL_PROJECTILE;
292 IL_PUSH(g_projectiles, missile);
293 IL_PUSH(g_bot_dodge, missile);
294 missile.missile_flags = MIF_SPLASH;
295
296 // csqc projectiles
297 //missile.angles = vectoangles(missile.velocity);
298 //missile.scale = 0.4; // BUG: the model is too big
299
300 setorigin(missile, w_shotorg);
301 setsize(missile, '-2 -2 -2', '2 2 2');
302
303 W_SetupProjVelocity_UP_PRE(missile, WEP_SEEKER, flac_);
304 CSQCProjectile(missile, true, PROJECTILE_FLAC, true);
305
306 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
307}
308
309// ============================
310// Begin: Tag and rocket controllers
311// ============================
312entity W_Seeker_Tagged_Info(entity isowner, .entity weaponentity, entity istarget)
313{
314 IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == isowner,
315 {
316 if (it.tag_target == istarget && it.weaponentity_fld == weaponentity)
317 return it;
318 });
319
320 return NULL;
321}
322
323void W_Seeker_Attack(Weapon thiswep, entity actor, .entity weaponentity)
324{
325 entity closest_target = NULL;
326
327 IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == actor,
328 {
329 if (closest_target)
330 {
331 if (vlen2(actor.origin - it.tag_target.origin) < vlen2(actor.origin - closest_target.origin))
332 closest_target = it.tag_target;
333 }
334 else
335 closest_target = it.tag_target;
336 });
337
338 if (closest_target)
339 {
340 traceline(actor.origin + actor.view_ofs, closest_target.origin, MOVE_NOMONSTERS, actor);
341 if (!closest_target || (trace_fraction < 1 && trace_ent != closest_target))
342 closest_target = NULL;
343 }
344
345 W_Seeker_Fire_Missile(thiswep, actor, weaponentity, '0 0 0', closest_target);
346}
347
348void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seeker_Attack
349{
350 --this.cnt;
351
352 Weapon thiswep = WEP_SEEKER;
353 .entity weaponentity = this.weaponentity_fld;
354 if ((!(this.realowner.items & IT_UNLIMITED_AMMO) && GetResource(this.realowner, thiswep.ammo_type) < WEP_CVAR(WEP_SEEKER, missile_ammo))
355 || this.cnt <= -1 || IS_DEAD(this.realowner) || this.realowner.(weaponentity).m_switchweapon != thiswep)
356 {
357 delete(this);
358 return;
359 }
360
361 this.nextthink = time + WEP_CVAR(WEP_SEEKER, missile_delay) * W_WeaponRateFactor(this.realowner);
362
363 entity own = this.realowner;
364
365 entity oldenemy = own.enemy;
366 own.enemy = this.enemy;
367
368 switch (own.cnt % 4)
369 {
370 case 0:
371 W_Seeker_Fire_Missile(thiswep, own, weaponentity, '-1.25 -3.75 0', own.enemy); // TODO
372 break;
373 case 1:
374 W_Seeker_Fire_Missile(thiswep, own, weaponentity, '+1.25 -3.75 0', own.enemy); // TODO
375 break;
376 case 2:
377 W_Seeker_Fire_Missile(thiswep, own, weaponentity, '-1.25 +3.75 0', own.enemy); // TODO
378 break;
379 case 3:
380 default:
381 W_Seeker_Fire_Missile(thiswep, own, weaponentity, '+1.25 +3.75 0', own.enemy); // TODO
382 break;
383 }
384
385 own.enemy = oldenemy;
386}
387
389{
390 .entity weaponentity = this.weaponentity_fld;
391 // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up
392 if (IS_DEAD(this.realowner) || IS_DEAD(this.tag_target) || this.realowner.(weaponentity).m_switchweapon != WEP_SEEKER
393 || (time > this.tag_time + WEP_CVAR(WEP_SEEKER, tag_tracker_lifetime)))
394 {
395 if (this)
396 {
397 WaypointSprite_Kill(this.tag_target.wps_tag_tracker);
398 delete(this);
399 }
400 return;
401 }
402
403 // Update the think method information
404 this.nextthink = time;
405}
406
407// ============================
408// Begin: Tag projectile
409// ============================
411{
412 //if (other == this.realowner)
413 // return;
414 Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE, 0, this);
415
416 delete(this);
417}
418
419void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
420{
421 if (GetResource(this, RES_HEALTH) <= 0)
422 return;
423 TakeResource(this, RES_HEALTH, damage);
424 if (GetResource(this, RES_HEALTH) <= 0)
426}
427
429{
431
432 vector dir;
433 dir = normalize(this.realowner.origin - this.origin);
434 vector org2 = findbetterlocation(this.origin, 8);
435
436 te_knightspike(org2);
437
438 this.event_damage = func_null;
439 Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, toucher.species, this);
440
441 if (toucher.takedamage == DAMAGE_AIM && !IS_DEAD(toucher))
442 {
443 // check to see if this person is already tagged by me
444 .entity weaponentity = this.weaponentity_fld;
445 entity tag = W_Seeker_Tagged_Info(this.realowner, weaponentity, toucher);
446
447 if (tag != NULL)
448 {
449 if (toucher.wps_tag_tracker && WEP_CVAR(WEP_SEEKER, type) == 1) // don't attach another waypointsprite without killing the old one first
450 WaypointSprite_Kill(toucher.wps_tag_tracker);
451 tag.tag_time = time;
452 }
453 else
454 {
455 //sprint(this.realowner, strcat("You just tagged ^2", toucher.netname, "^7 with a tracking device!\n"));
456 entity e = new(tag_tracker);
457 e.weaponentity_fld = this.weaponentity_fld;
458 e.cnt = WEP_CVAR(WEP_SEEKER, missile_count);
459 e.owner = this.owner;
460 e.realowner = this.realowner;
462
463 if (WEP_CVAR(WEP_SEEKER, type) == 1)
464 {
465 e.tag_target = toucher;
466 e.tag_time = time;
468 }
469 else
470 {
471 e.enemy = toucher;
473 }
474
475 e.nextthink = time;
476 }
477
478 if (WEP_CVAR(WEP_SEEKER, type) == 1)
479 {
480 WaypointSprite_Spawn(WP_Seeker, WEP_CVAR(WEP_SEEKER, tag_tracker_lifetime), 0, toucher, '0 0 64', this.realowner, 0, toucher, wps_tag_tracker, true, RADARICON_TAGGED);
482 }
483 }
484
485 delete(this);
486 return;
487}
488
489void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity)
490{
491 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, tag_ammo), weaponentity);
492
493 W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_TAG_FIRE, CH_WEAPON_A, WEP_CVAR(WEP_SEEKER, missile_damage) * WEP_CVAR(WEP_SEEKER, missile_count), thiswep.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY);
494
495 entity missile = new(seeker_tag);
496 missile.weaponentity_fld = weaponentity;
497 missile.owner = missile.realowner = actor;
498 missile.bot_dodge = true;
499 missile.bot_dodgerating = 50;
501 setthink(missile, SUB_Remove);
502 missile.nextthink = time + WEP_CVAR(WEP_SEEKER, tag_lifetime);
503 set_movetype(missile, MOVETYPE_FLY);
504 missile.solid = SOLID_BBOX;
505
506 missile.takedamage = DAMAGE_YES;
507 missile.event_damage = W_Seeker_Tag_Damage;
508 SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(WEP_SEEKER, tag_health));
509 missile.damageforcescale = WEP_CVAR(WEP_SEEKER, tag_damageforcescale);
510
511 setorigin(missile, w_shotorg);
512 setsize(missile, '-2 -2 -2', '2 2 2');
513
514 missile.flags = FL_PROJECTILE;
515 IL_PUSH(g_projectiles, missile);
516 IL_PUSH(g_bot_dodge, missile);
517 //missile.missile_flags = MIF_..?;
518
519 set_movetype(missile, MOVETYPE_FLY);
520 W_SetupProjVelocity_PRE(missile, WEP_SEEKER, tag_);
521 missile.angles = vectoangles(missile.velocity);
522
523 CSQCProjectile(missile, true, PROJECTILE_TAG, false); // has sound
524
525 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
526}
527
528// ============================
529// Begin: Genereal weapon functions
530// ============================
531
532METHOD(Seeker, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
533{
534 if (WEP_CVAR(WEP_SEEKER, type) == 1)
535 {
536 if (W_Seeker_Tagged_Info(actor, weaponentity, actor.enemy) != NULL)
537 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, missile_speed_max), 0, WEP_CVAR(WEP_SEEKER, missile_lifetime), false, false);
538 else
539 PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, false);
540 }
541 else
542 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, true);
543}
544
545METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
546{
547 if (autocvar_g_balance_seeker_reload_ammo && actor.(weaponentity).clip_load < min(WEP_CVAR(WEP_SEEKER, missile_ammo), WEP_CVAR(WEP_SEEKER, tag_ammo)))
548 { // forced reload
549 thiswep.wr_reload(thiswep, actor, weaponentity);
550 return;
551 }
552
553 if (fire & 1)
554 {
555 if (WEP_CVAR(WEP_SEEKER, type) == 1)
556 {
557 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, missile_refire)))
558 {
559 W_Seeker_Attack(thiswep, actor, weaponentity);
560 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, missile_animtime), w_ready);
561 }
562 }
563 else
564 {
565 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, tag_refire)))
566 {
567 W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
568 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, tag_animtime), w_ready);
569 }
570 }
571 }
572 else if (fire & 2)
573 {
574 if (WEP_CVAR(WEP_SEEKER, type) == 1)
575 {
576 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, tag_refire)))
577 {
578 W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
579 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, tag_animtime), w_ready);
580 }
581 }
582 else
583 {
584 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, flac_refire)))
585 {
586 W_Seeker_Fire_Flac(thiswep, actor, weaponentity);
587 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, flac_animtime), w_ready);
588 }
589 }
590 }
591}
592
593METHOD(Seeker, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
594{
595 float ammo_amount;
596 if (WEP_CVAR(WEP_SEEKER, type) == 1)
597 {
598 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
599 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
600 }
601 else
602 {
603 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
604 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
605 }
606 return ammo_amount;
607}
608
609METHOD(Seeker, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
610{
611 float ammo_amount;
612 if (WEP_CVAR(WEP_SEEKER, type) == 1)
613 {
614 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
615 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
616 }
617 else
618 {
619 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
620 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
621 }
622 return ammo_amount;
623}
624
625METHOD(Seeker, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
626{
627 W_Reload(actor, weaponentity, min(WEP_CVAR(WEP_SEEKER, missile_ammo), WEP_CVAR(WEP_SEEKER, tag_ammo)), SND_RELOAD);
628}
629
630METHOD(Seeker, wr_suicidemessage, Notification(entity thiswep))
631{
632 return WEAPON_SEEKER_SUICIDE;
633}
634
635METHOD(Seeker, wr_killmessage, Notification(entity thiswep))
636{
638 return WEAPON_SEEKER_MURDER_TAG;
639 else
640 return WEAPON_SEEKER_MURDER_SPRAY;
641}
642
643#endif // SVQC
644#ifdef CSQC
645
646METHOD(Seeker, wr_impacteffect, void(entity thiswep, entity actor))
647{
648 vector org2 = w_org + w_backoff * 2;
650 {
652 {
653 if (!w_issilent)
654 sound(actor, CH_SHOTS, SND_TAG_IMPACT, 1, ATTEN_NORM);
655 }
656 else
657 {
658 pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
659 if (!w_issilent)
661 }
662 }
663 else
664 {
665 pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
666 if (!w_issilent)
668 }
669}
670
671#endif // CSQC
672#ifdef MENUQC
674
675METHOD(Seeker, describe, string(Seeker this))
676{
677 TC(Seeker, this);
679 PAR(_("The %s is a unique weapon, firing a \"tag\" which then launches a few homing missiles if it collides with a player. "
680 "The homing isn't perfect, so sometimes the missiles can hit an object or a corner."), COLORED_NAME(this));
681 PAR(_("The secondary fire launches a rapid barrage of scattered explosives that travel only a short distance."));
682 PAR(_("It consumes %s ammo, even when the tag doesn't land."), COLORED_NAME(ITEM_Rockets));
683 PAR(_("The %s primary fire deals quite a lot of damage when a tag lands, although it requires skill to aim effectively. "
684 "The secondary fire is only useful in close range combat, and sometimes the explosives can damage yourself too."), COLORED_NAME(this));
685 PAR(W_Guide_Keybinds(this));
686 PAR(W_Guide_DPS_primaryMultishot(this.netname, "missile", "flac", "missile_count", "tag_refire"));
687 return PAGE_TEXT;
688}
689
690#endif // MENUQC
bool bot_aim(entity this,.entity weaponentity, float shotspeed, float shotspeedupward, float maxshottime, float applygravity, bool shot_accurate)
IntrusiveList g_bot_dodge
Definition api.qh:150
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
void TakeResource(entity receiver, Resource res_type, float amount)
Takes an entity some resource.
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:42
Resource ammo_type
M: ammotype : main ammo type.
Definition weapon.qh:56
int m_id
Definition weapon.qh:43
string netname
Definition powerups.qc:20
float cnt
Definition powerups.qc:24
float wait
Definition items.qc:17
entity owner
Definition main.qh:87
#define COLORED_NAME(this)
Definition color.qh:195
const int IT_UNLIMITED_AMMO
Definition item.qh:23
#define IS_DEAD(s)
Definition player.qh:244
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition player.qh:152
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition player.qh:154
float W_WeaponRateFactor(entity this)
vector findbetterlocation(vector org, float mindist)
Definition util.qc:116
const int FL_PROJECTILE
Definition constants.qh:85
const float MOVE_NOMONSTERS
entity trace_ent
float frametime
vector velocity
const float SOLID_BBOX
float time
vector trace_endpos
float nextthink
vector origin
float trace_fraction
vector trace_plane_normal
void UpdateCSQCProjectile(entity e)
void CSQCProjectile(entity e, float clientanimate, int type, float docull)
float RadiusDamage(entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype,.entity weaponentity, entity directhitentity)
Definition damage.qc:943
IntrusiveList g_damagedbycontents
Definition damage.qh:143
void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner)
vector w_org
int w_deathtype
float w_random
vector w_backoff
float w_issilent
const int HITTYPE_BOUNCE
Definition all.qh:31
const int HITTYPE_SPLASH
Definition all.qh:30
const int HITTYPE_SECONDARY
Definition all.qh:29
void SUB_Remove(entity this)
Remove entity.
Definition defer.qh:13
#define pointparticles(effect, org, vel, howmany)
Definition effect.qh:7
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define IL_EACH(this, cond, body)
#define TC(T, sym)
Definition _all.inc:82
float bound(float min, float value, float max)
float random(void)
float vlen(vector v)
vector vectoangles(vector v)
float min(float f,...)
vector normalize(vector v)
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
const int MOVETYPE_FLYMISSILE
Definition movetypes.qh:138
const int MOVETYPE_FLY
Definition movetypes.qh:134
var void func_null()
entity Notification
always last
Definition all.qh:81
#define METHOD(cname, name, prototype)
Definition oo.qh:269
#define NULL
Definition post.qh:14
#define makevectors
Definition post.qh:21
const int PROJECTILE_TAG
Definition projectiles.qh:5
const int PROJECTILE_FLAC
const int PROJECTILE_SEEKER
void W_Seeker_Vollycontroller_Think(entity this)
Definition seeker.qc:348
void W_Seeker_Fire_Flac(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:251
void W_Seeker_Fire_Tag(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:489
void W_Seeker_Flac_Explode(entity this, entity directhitentity)
Definition seeker.qc:222
void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition seeker.qc:419
void W_Seeker_Missile_Think(entity this)
Definition seeker.qc:38
void W_Seeker_Missile_Touch(entity this, entity toucher)
Definition seeker.qc:31
void W_Seeker_Flac_Explode_use(entity this, entity actor, entity trigger)
Definition seeker.qc:246
void W_Seeker_Fire_Missile(Weapon thiswep, entity actor,.entity weaponentity, vector f_diff, entity m_target)
Definition seeker.qc:165
entity W_Seeker_Tagged_Info(entity isowner,.entity weaponentity, entity istarget)
Definition seeker.qc:312
void W_Seeker_Tag_Touch(entity this, entity toucher)
Definition seeker.qc:428
void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition seeker.qc:126
void W_Seeker_Flac_Touch(entity this, entity toucher)
Definition seeker.qc:241
void W_Seeker_Missile_Explode(entity this, entity directhitentity)
Definition seeker.qc:8
void W_Seeker_Missile_Explode_think(entity this)
Definition seeker.qc:26
void W_Seeker_Tracker_Think(entity this)
Definition seeker.qc:388
void W_Seeker_Tag_Explode(entity this)
Definition seeker.qc:410
void W_Seeker_Attack(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:323
Sound SND_SEEKEREXP_RANDOM(float rnd)
Definition seeker.qh:21
entity tag_target
Definition seeker.qh:123
Sound SND_TAGEXP_RANDOM(float rnd)
Definition seeker.qh:12
IntrusiveList g_seeker_trackers
Definition seeker.qh:126
entity wps_tag_tracker
Definition seeker.qh:123
#define setthink(e, f)
vector
Definition self.qh:92
entity entity toucher
Definition self.qh:72
#define settouch(e, f)
Definition self.qh:73
int dir
Definition impulse.qc:89
float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception)
Definition common.qc:45
void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this) explode)
Definition common.qc:87
void adaptor_think2use_hittype_splash(entity this)
Definition common.qc:106
const int MIF_SPLASH
Definition common.qh:46
int projectiledeathtype
Definition common.qh:21
#define PROJECTILE_TOUCH(e, t)
Definition common.qh:28
IntrusiveList g_projectiles
Definition common.qh:58
const int MIF_GUIDED_TAG
Definition common.qh:53
const int CH_SHOTS
Definition sound.qh:14
const int CH_WEAPON_A
Definition sound.qh:7
const float ATTEN_NORM
Definition sound.qh:30
#define sound(e, c, s, v, a)
Definition sound.qh:52
#define PAGE_TEXT
Definition string.qh:642
#define PAR(...)
Adds an individually translatable paragraph to PAGE_TEXT without having to deal with strcat and sprin...
Definition string.qh:648
#define PAGE_TEXT_INIT()
Definition string.qh:641
const int DAMAGE_YES
Definition subs.qh:80
const int DAMAGE_AIM
Definition subs.qh:81
entity enemy
Definition sv_ctf.qh:153
entity realowner
vector w_shotdir
Definition tracing.qh:20
#define W_SetupProjVelocity_PRE(ent, wep, prefix)
Definition tracing.qh:65
#define W_SetupProjVelocity_UP_PRE(ent, wep, prefix)
Definition tracing.qh:52
#define W_SetupShot_ProjectileSize(ent, wepent, mi, ma, antilag, recoil, snd, chan, maxdamage, deathtype)
Definition tracing.qh:30
vector w_shotorg
Definition tracing.qh:19
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
void WaypointSprite_Kill(entity wp)
entity WaypointSprite_Spawn(entity spr, float _lifetime, float maxdistance, entity ref, vector ofs, entity showto, float t, entity own,.entity ownfield, float hideable, entity icon)
void WaypointSprite_UpdateRule(entity e, float t, float r)
const int SPRITERULE_DEFAULT
string W_Guide_Keybinds(Weapon wep)
Definition all.qc:824
string W_Guide_DPS_primaryMultishot(string name, string pri, string sec, string shots, string refire2)
Definition all.qc:973
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:715
#define WEP_CVAR(wep, name)
Definition all.qh:337
void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use,.entity weaponentity)
void W_Reload(entity actor,.entity weaponentity, float sent_ammo_min, Sound sent_sound)
void weapon_thinkf(entity actor,.entity weaponentity, WFRAME fr, float t, void(Weapon thiswep, entity actor,.entity weaponentity, int fire) func)
bool weapon_prepareattack(Weapon thiswep, entity actor,.entity weaponentity, bool secondary, float attacktime)
void w_ready(Weapon thiswep, entity actor,.entity weaponentity, int fire)
entity weaponentity_fld
float weapon_load[REGISTRY_MAX(Weapons)]