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.scale = 2;
188 missile.takedamage = DAMAGE_YES;
189 missile.weaponentity_fld = weaponentity;
190 SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(WEP_SEEKER, missile_health));
191 missile.damageforcescale = WEP_CVAR(WEP_SEEKER, missile_damageforcescale);
192 missile.damagedbycontents = true;
194 //missile.think = W_Seeker_Missile_Animate; // csqc projectiles.
195
196 if (missile.enemy != NULL)
197 missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
198 else
199 missile.projectiledeathtype = thiswep.m_id;
200
201 PROJECTILE_MAKETRIGGER(missile);
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
247
249{
250 W_Seeker_Flac_Explode(this, trigger);
251}
252
253void W_Seeker_Fire_Flac(Weapon thiswep, entity actor, .entity weaponentity)
254{
255 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, flac_ammo), weaponentity);
256
257 vector f_diff;
258 switch (actor.(weaponentity).bulletcounter % 4)
259 {
260 case 0:
261 f_diff = '-1.25 -3.75 0';
262 break;
263 case 1:
264 f_diff = '+1.25 -3.75 0';
265 break;
266 case 2:
267 f_diff = '-1.25 +3.75 0';
268 break;
269 case 3:
270 default:
271 f_diff = '+1.25 +3.75 0';
272 break;
273 }
274 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);
275 w_shotorg += f_diff;
276
277 // uses hagar effects!
278 W_MuzzleFlash(WEP_HAGAR, actor, weaponentity, w_shotorg, w_shotdir);
279
280 entity missile;
281 missile = new(missile);
282 missile.owner = missile.realowner = actor;
283 missile.bot_dodge = true;
284 missile.bot_dodgerating = WEP_CVAR(WEP_SEEKER, flac_damage);
286 missile.use = W_Seeker_Flac_Explode_use;
288 missile.nextthink = time + WEP_CVAR(WEP_SEEKER, flac_lifetime) + WEP_CVAR(WEP_SEEKER, flac_lifetime_rand);
289 set_movetype(missile, MOVETYPE_FLY);
290 missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
291 missile.weaponentity_fld = weaponentity;
292 missile.flags = FL_PROJECTILE;
293 IL_PUSH(g_projectiles, missile);
294 IL_PUSH(g_bot_dodge, missile);
295 missile.missile_flags = MIF_SPLASH;
296
297 // csqc projectiles
298 //missile.angles = vectoangles(missile.velocity);
299 //missile.scale = 0.4; // BUG: the model is too big
300
301 PROJECTILE_MAKETRIGGER(missile);
302 setorigin(missile, w_shotorg);
303 setsize(missile, '-2 -2 -2', '2 2 2');
304
305 W_SetupProjVelocity_UP_PRE(missile, WEP_SEEKER, flac_);
306 CSQCProjectile(missile, true, PROJECTILE_FLAC, true);
307
308 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
309}
310
311// ============================
312// Begin: Tag and rocket controllers
313// ============================
314entity W_Seeker_Tagged_Info(entity isowner, .entity weaponentity, entity istarget)
315{
316 IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == isowner,
317 {
318 if (it.tag_target == istarget && it.weaponentity_fld == weaponentity)
319 return it;
320 });
321
322 return NULL;
323}
324
325void W_Seeker_Attack(Weapon thiswep, entity actor, .entity weaponentity)
326{
327 entity closest_target = NULL;
328
329 IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == actor,
330 {
331 if (closest_target)
332 {
333 if (vlen2(actor.origin - it.tag_target.origin) < vlen2(actor.origin - closest_target.origin))
334 closest_target = it.tag_target;
335 }
336 else
337 closest_target = it.tag_target;
338 });
339
340 if (closest_target)
341 {
342 traceline(actor.origin + actor.view_ofs, closest_target.origin, MOVE_NOMONSTERS, actor);
343 if (!closest_target || (trace_fraction < 1 && trace_ent != closest_target))
344 closest_target = NULL;
345 }
346
347 W_Seeker_Fire_Missile(thiswep, actor, weaponentity, '0 0 0', closest_target);
348}
349
350void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seeker_Attack
351{
352 --this.cnt;
353
354 Weapon thiswep = WEP_SEEKER;
355 .entity weaponentity = this.weaponentity_fld;
356 if ((!(this.realowner.items & IT_UNLIMITED_AMMO) && GetResource(this.realowner, thiswep.ammo_type) < WEP_CVAR(WEP_SEEKER, missile_ammo))
357 || this.cnt <= -1 || IS_DEAD(this.realowner) || this.realowner.(weaponentity).m_switchweapon != thiswep)
358 {
359 delete(this);
360 return;
361 }
362
363 this.nextthink = time + WEP_CVAR(WEP_SEEKER, missile_delay) * W_WeaponRateFactor(this.realowner);
364
365 entity own = this.realowner;
366
367 entity oldenemy = own.enemy;
368 own.enemy = this.enemy;
369
370 switch (own.cnt % 4)
371 {
372 case 0:
373 W_Seeker_Fire_Missile(thiswep, own, weaponentity, '-1.25 -3.75 0', own.enemy); // TODO
374 break;
375 case 1:
376 W_Seeker_Fire_Missile(thiswep, own, weaponentity, '+1.25 -3.75 0', own.enemy); // TODO
377 break;
378 case 2:
379 W_Seeker_Fire_Missile(thiswep, own, weaponentity, '-1.25 +3.75 0', own.enemy); // TODO
380 break;
381 case 3:
382 default:
383 W_Seeker_Fire_Missile(thiswep, own, weaponentity, '+1.25 +3.75 0', own.enemy); // TODO
384 break;
385 }
386
387 own.enemy = oldenemy;
388}
389
391{
392 .entity weaponentity = this.weaponentity_fld;
393 // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up
394 if (IS_DEAD(this.realowner) || IS_DEAD(this.tag_target) || this.realowner.(weaponentity).m_switchweapon != WEP_SEEKER
395 || (time > this.tag_time + WEP_CVAR(WEP_SEEKER, tag_tracker_lifetime)))
396 {
397 if (this)
398 {
399 WaypointSprite_Kill(this.tag_target.wps_tag_tracker);
400 delete(this);
401 }
402 return;
403 }
404
405 // Update the think method information
406 this.nextthink = time;
407}
408
409// ============================
410// Begin: Tag projectile
411// ============================
413{
414 //if (other == this.realowner)
415 // return;
416 Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE, 0, this);
417
418 delete(this);
419}
420
421void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
422{
423 if (GetResource(this, RES_HEALTH) <= 0)
424 return;
425 TakeResource(this, RES_HEALTH, damage);
426 if (GetResource(this, RES_HEALTH) <= 0)
428}
429
431{
433
434 vector dir;
435 dir = normalize(this.realowner.origin - this.origin);
436 vector org2 = findbetterlocation(this.origin, 8);
437
438 te_knightspike(org2);
439
440 this.event_damage = func_null;
441 Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, toucher.species, this);
442
443 if (toucher.takedamage == DAMAGE_AIM && !IS_DEAD(toucher))
444 {
445 // check to see if this person is already tagged by me
446 .entity weaponentity = this.weaponentity_fld;
447 entity tag = W_Seeker_Tagged_Info(this.realowner, weaponentity, toucher);
448
449 if (tag != NULL)
450 {
451 if (toucher.wps_tag_tracker && WEP_CVAR(WEP_SEEKER, type) == 1) // don't attach another waypointsprite without killing the old one first
452 WaypointSprite_Kill(toucher.wps_tag_tracker);
453 tag.tag_time = time;
454 }
455 else
456 {
457 //sprint(this.realowner, strcat("You just tagged ^2", toucher.netname, "^7 with a tracking device!\n"));
458 entity e = new(tag_tracker);
459 e.weaponentity_fld = this.weaponentity_fld;
460 e.cnt = WEP_CVAR(WEP_SEEKER, missile_count);
461 e.owner = this.owner;
462 e.realowner = this.realowner;
464
465 if (WEP_CVAR(WEP_SEEKER, type) == 1)
466 {
467 e.tag_target = toucher;
468 e.tag_time = time;
470 }
471 else
472 {
473 e.enemy = toucher;
475 }
476
477 e.nextthink = time;
478 }
479
480 if (WEP_CVAR(WEP_SEEKER, type) == 1)
481 {
482 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);
484 }
485 }
486
487 delete(this);
488 return;
489}
490
491void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity)
492{
493 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, tag_ammo), weaponentity);
494
495 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);
496
497 entity missile = new(seeker_tag);
498 missile.weaponentity_fld = weaponentity;
499 missile.owner = missile.realowner = actor;
500 missile.bot_dodge = true;
501 missile.bot_dodgerating = 50;
503 setthink(missile, SUB_Remove);
504 missile.nextthink = time + WEP_CVAR(WEP_SEEKER, tag_lifetime);
505 set_movetype(missile, MOVETYPE_FLY);
506
507 missile.takedamage = DAMAGE_YES;
508 missile.event_damage = W_Seeker_Tag_Damage;
509 SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(WEP_SEEKER, tag_health));
510 missile.damageforcescale = WEP_CVAR(WEP_SEEKER, tag_damageforcescale);
511
512 PROJECTILE_MAKETRIGGER(missile);
513 setorigin(missile, w_shotorg);
514 setsize(missile, '-2 -2 -2', '2 2 2');
515
516 missile.flags = FL_PROJECTILE;
517 IL_PUSH(g_projectiles, missile);
518 IL_PUSH(g_bot_dodge, missile);
519 //missile.missile_flags = MIF_..?;
520
521 set_movetype(missile, MOVETYPE_FLY);
522 W_SetupProjVelocity_PRE(missile, WEP_SEEKER, tag_);
523 missile.angles = vectoangles(missile.velocity);
524
525 CSQCProjectile(missile, true, PROJECTILE_TAG, false); // has sound
526
527 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
528}
529
530// ============================
531// Begin: Genereal weapon functions
532// ============================
533
534METHOD(Seeker, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
535{
536 if (WEP_CVAR(WEP_SEEKER, type) == 1)
537 {
538 if (W_Seeker_Tagged_Info(actor, weaponentity, actor.enemy) != NULL)
539 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);
540 else
541 PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, false);
542 }
543 else
544 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, true);
545}
546
547METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
548{
549 if (autocvar_g_balance_seeker_reload_ammo && actor.(weaponentity).clip_load < min(WEP_CVAR(WEP_SEEKER, missile_ammo), WEP_CVAR(WEP_SEEKER, tag_ammo)))
550 { // forced reload
551 thiswep.wr_reload(thiswep, actor, weaponentity);
552 return;
553 }
554
555 if (fire & 1)
556 {
557 if (WEP_CVAR(WEP_SEEKER, type) == 1)
558 {
559 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, missile_refire)))
560 {
561 W_Seeker_Attack(thiswep, actor, weaponentity);
562 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, missile_animtime), w_ready);
563 }
564 }
565 else
566 {
567 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, tag_refire)))
568 {
569 W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
570 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, tag_animtime), w_ready);
571 }
572 }
573 }
574 else if (fire & 2)
575 {
576 if (WEP_CVAR(WEP_SEEKER, type) == 1)
577 {
578 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, tag_refire)))
579 {
580 W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
581 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, tag_animtime), w_ready);
582 }
583 }
584 else
585 {
586 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, flac_refire)))
587 {
588 W_Seeker_Fire_Flac(thiswep, actor, weaponentity);
589 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, flac_animtime), w_ready);
590 }
591 }
592 }
593}
594
595METHOD(Seeker, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
596{
597 float ammo_amount;
598 if (WEP_CVAR(WEP_SEEKER, type) == 1)
599 {
600 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
601 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
602 }
603 else
604 {
605 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
606 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
607 }
608 return ammo_amount;
609}
610
611METHOD(Seeker, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
612{
613 float ammo_amount;
614 if (WEP_CVAR(WEP_SEEKER, type) == 1)
615 {
616 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
617 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
618 }
619 else
620 {
621 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
622 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
623 }
624 return ammo_amount;
625}
626
627METHOD(Seeker, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
628{
629 W_Reload(actor, weaponentity, min(WEP_CVAR(WEP_SEEKER, missile_ammo), WEP_CVAR(WEP_SEEKER, tag_ammo)), SND_RELOAD);
630}
631
632METHOD(Seeker, wr_suicidemessage, Notification(entity thiswep))
633{
634 return WEAPON_SEEKER_SUICIDE;
635}
636
637METHOD(Seeker, wr_killmessage, Notification(entity thiswep))
638{
640 return WEAPON_SEEKER_MURDER_TAG;
641 else
642 return WEAPON_SEEKER_MURDER_SPRAY;
643}
644
645#endif // SVQC
646#ifdef CSQC
647
648METHOD(Seeker, wr_impacteffect, void(entity thiswep, entity actor))
649{
650 vector org2 = w_org + w_backoff * 2;
652 {
654 {
655 if (!w_issilent)
656 sound(actor, CH_SHOTS, SND_TAG_IMPACT, 1, ATTEN_NORM);
657 }
658 else
659 {
660 pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
661 if (!w_issilent)
663 }
664 }
665 else
666 {
667 pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1);
668 if (!w_issilent)
670 }
671}
672
673#endif // CSQC
674#ifdef MENUQC
676
677METHOD(Seeker, describe, string(Seeker this))
678{
679 TC(Seeker, this);
681 PAR(_("The %s is a unique weapon, firing a \"tag\" which then launches a few homing missiles if it collides with a player. "
682 "The homing isn't perfect, so sometimes the missiles can hit an object or a corner."), COLORED_NAME(this));
683 PAR(_("The secondary fire launches a rapid barrage of scattered explosives that travel only a short distance."));
684 PAR(_("It consumes %s ammo, even when the tag doesn't land."), COLORED_NAME(ITEM_Rockets));
685 PAR(_("The %s primary fire deals quite a lot of damage when a tag lands, although it requires skill to aim effectively. "
686 "The secondary fire is only useful in close range combat, and sometimes the explosives can damage yourself too."), COLORED_NAME(this));
687 PAR(W_Guide_Keybinds(this));
688 PAR(W_Guide_DPS_primaryMultishot(this.netname, "missile", "flac", "missile_count", "tag_refire"));
689 return PAGE_TEXT;
690}
691
692#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.
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:189
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:76
const float MOVE_NOMONSTERS
entity trace_ent
float frametime
vector velocity
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:934
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
set manually after projectile has bounced
Definition all.qh:33
const int HITTYPE_SPLASH
automatically set by RadiusDamage
Definition all.qh:32
const int HITTYPE_SECONDARY
Definition all.qh:31
void SUB_Remove(entity this)
Remove entity.
Definition defer.qh:12
#define pointparticles(effect, org, vel, howmany)
Definition effect.qh:7
SetResourceExplicit(ent, RES_ARMOR, ReadByte() *DEC_FACTOR)) ENTCS_PROP(NAME
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:142
const int MOVETYPE_FLY
Definition movetypes.qh:138
var void func_null()
entity Notification
always last
Definition all.qh:85
#define METHOD(cname, name, prototype)
Definition oo.qh:274
#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:350
void W_Seeker_Fire_Flac(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:253
void W_Seeker_Fire_Tag(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:491
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:421
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:248
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:314
void W_Seeker_Tag_Touch(entity this, entity toucher)
Definition seeker.qc:430
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:390
void W_Seeker_Tag_Explode(entity this)
Definition seeker.qc:412
void W_Seeker_Attack(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:325
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:96
entity entity toucher
Definition self.qh:76
#define settouch(e, f)
Definition self.qh:77
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
#define PROJECTILE_MAKETRIGGER(e)
Definition common.qh:34
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:651
#define PAR(...)
Adds an individually translatable paragraph to PAGE_TEXT without having to deal with strcat and sprin...
Definition string.qh:657
#define PAGE_TEXT_INIT()
Definition string.qh:650
const int DAMAGE_YES
Definition subs.qh:80
const int DAMAGE_AIM
Definition subs.qh:81
entity enemy
Definition sv_ctf.qh:152
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)]