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 return;
45 }
46
47 float dist = W_SeekOutEnemy(this,
48 WEP_CVAR(WEP_SEEKER, missile_decel),
49 WEP_CVAR(WEP_SEEKER, missile_accel),
50 WEP_CVAR(WEP_SEEKER, missile_speed_max),
51 WEP_CVAR(WEP_SEEKER, missile_turnrate),
52 WEP_CVAR(WEP_SEEKER, missile_smart),
53 WEP_CVAR(WEP_SEEKER, missile_smart_mindist),
54 WEP_CVAR(WEP_SEEKER, missile_smart_trace_min),
55 WEP_CVAR(WEP_SEEKER, missile_smart_trace_max));
56
57 // Proxy
58 if (WEP_CVAR(WEP_SEEKER, missile_proxy))
59 {
60 if (dist <= WEP_CVAR(WEP_SEEKER, missile_proxy_maxrange))
61 {
62 if (this.cvar_cl_autoswitch == 0)
63 this.cvar_cl_autoswitch = time + WEP_CVAR(WEP_SEEKER, missile_proxy_delay);
64 else
65 {
66 if (this.cvar_cl_autoswitch <= time)
67 {
69 this.cvar_cl_autoswitch = 0;
70 }
71 }
72 }
73 else
74 {
75 if (this.cvar_cl_autoswitch != 0)
76 this.cvar_cl_autoswitch = 0;
77 }
78 }
80
81 if (IS_DEAD(this.enemy))
82 {
83 this.enemy = NULL;
84 this.cnt = time + 1 + (random() * 4);
85 this.nextthink = this.cnt;
86 return;
87 }
88
89 //this.angles = vectoangles(this.velocity); // turn model in the new flight direction
90 this.nextthink = time;// + 0.05; // csqc projectiles
92}
93
94
95
96void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
97{
98 if (GetResource(this, RES_HEALTH) <= 0)
99 return;
100 if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
101 return; // g_projectiles_damage says to halt
102
103 if (this.realowner == attacker)
104 TakeResource(this, RES_HEALTH, (damage * 0.25));
105 else
106 TakeResource(this, RES_HEALTH, damage);
107
108 if (GetResource(this, RES_HEALTH) <= 0)
110}
111
112/*
113void W_Seeker_Missile_Animate(entity this)
114{
115 ++this.frame;
116 this.nextthink = time + 0.05;
117
118 if (this.enemy != NULL
119 && (this.enemy.takedamage != DAMAGE_AIM || IS_DEAD(this.enemy)))
120 this.enemy = NULL;
121
122 if (this.frame == 5)
123 {
124 this.think = W_Seeker_Missile_Think;
125 this.nextthink = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles
126 this.move_movetype = (autocvar_g_balance_seeker_missile_proxy)
127 ? MOVETYPE_BOUNCEMISSILE
128 : MOVETYPE_FLYMISSILE;
129 }
130
131 UpdateCSQCProjectile(this);
132}
133*/
134
135void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, .entity weaponentity, entity m_target, int bullet_count)
136{
137 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, missile_ammo), weaponentity);
138
139 makevectors(actor.v_angle);
140 vector old_movedir = actor.(weaponentity).movedir;
141 if (bullet_count > 0)
142 actor.(weaponentity).movedir += W_SetupShot_GetAlternatingOffset(bullet_count);
143 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));
144 actor.(weaponentity).movedir = old_movedir;
145 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
146
147 //actor.detornator = false;
148
149 entity missile = new(seeker_missile);
150 WarpZone_RefSys_Copy(missile, actor);
151 missile.owner = missile.realowner = actor;
152 missile.bot_dodge = true;
153 missile.bot_dodgerating = WEP_CVAR(WEP_SEEKER, missile_damage);
154
157 missile.event_damage = W_Seeker_Missile_Damage;
158 missile.nextthink = time; // + 0.2; // + cvar("g_balance_seeker_missile_activate_delay");
159 missile.cnt = time + WEP_CVAR(WEP_SEEKER, missile_lifetime);
160 missile.enemy = m_target;
161 missile.scale = 2;
162 missile.takedamage = DAMAGE_YES;
163 missile.weaponentity_fld = weaponentity;
164 SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(WEP_SEEKER, missile_health));
165 missile.damageforcescale = WEP_CVAR(WEP_SEEKER, missile_damageforcescale);
166 missile.damagedbycontents = true;
168 //missile.think = W_Seeker_Missile_Animate; // csqc projectiles.
169
170 if (missile.enemy != NULL)
171 missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
172 else
173 missile.projectiledeathtype = thiswep.m_id;
174
175 PROJECTILE_MAKETRIGGER(missile);
176 setorigin(missile, w_shotorg);
177 setsize(missile, '-4 -4 -4', '4 4 4');
179 missile.flags = FL_PROJECTILE;
180 IL_PUSH(g_projectiles, missile);
181 IL_PUSH(g_bot_dodge, missile);
182 missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG;
183
184 W_SetupProjVelocity_UP_PRE(missile, WEP_SEEKER, missile_);
185
186 missile.angles = vectoangles(missile.velocity);
187
188 CSQCProjectile(missile, false, PROJECTILE_SEEKER_MISSILE, true);
189
190 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
191}
192
193// ============================
194// Begin: FLAC, close range attack meant for defeating rockets which are coming at you.
195// ============================
196void W_Seeker_Flac_Explode(entity this, entity directhitentity)
197{
198 this.event_damage = func_null;
199
200 RadiusDamage(this, this.realowner,
201 WEP_CVAR(WEP_SEEKER, flac_damage),
202 WEP_CVAR(WEP_SEEKER, flac_edgedamage),
203 WEP_CVAR(WEP_SEEKER, flac_radius),
204 NULL,
205 NULL,
206 WEP_CVAR(WEP_SEEKER, flac_force),
208 this.weaponentity_fld,
209 directhitentity
210 );
211
212 delete(this);
213}
214
221
223{
224 W_Seeker_Flac_Explode(this, trigger);
225}
226
227void W_Seeker_Fire_Flac(Weapon thiswep, entity actor, .entity weaponentity)
228{
229 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, flac_ammo), weaponentity);
230
231 vector old_movedir = actor.(weaponentity).movedir;
232 actor.(weaponentity).movedir += W_SetupShot_GetAlternatingOffset(actor.(weaponentity).bulletcounter);
233 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);
234 actor.(weaponentity).movedir = old_movedir;
235
236 // uses hagar effects!
237 W_MuzzleFlash(WEP_HAGAR, actor, weaponentity, w_shotorg, w_shotdir);
238
239 entity missile;
240 missile = new(missile);
241 missile.owner = missile.realowner = actor;
242 missile.bot_dodge = true;
243 missile.bot_dodgerating = WEP_CVAR(WEP_SEEKER, flac_damage);
245 missile.use = W_Seeker_Flac_Explode_use;
247 missile.nextthink = time + WEP_CVAR(WEP_SEEKER, flac_lifetime) + WEP_CVAR(WEP_SEEKER, flac_lifetime_rand);
248 set_movetype(missile, MOVETYPE_FLY);
249 missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
250 missile.weaponentity_fld = weaponentity;
251 missile.flags = FL_PROJECTILE;
252 IL_PUSH(g_projectiles, missile);
253 IL_PUSH(g_bot_dodge, missile);
254 missile.missile_flags = MIF_SPLASH;
255
256 // csqc projectiles
257 //missile.angles = vectoangles(missile.velocity);
258 //missile.scale = 0.4; // BUG: the model is too big
259
260 PROJECTILE_MAKETRIGGER(missile);
261 setorigin(missile, w_shotorg);
262 setsize(missile, '-2 -2 -2', '2 2 2');
263
264 W_SetupProjVelocity_UP_PRE(missile, WEP_SEEKER, flac_);
265 CSQCProjectile(missile, true, PROJECTILE_SEEKER_FLAC, true);
266
267 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
268}
269
270// ============================
271// Begin: Tag and rocket controllers
272// ============================
273entity W_Seeker_Tagged_Info(entity isowner, .entity weaponentity, entity istarget)
274{
275 IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == isowner,
276 {
277 if (it.tag_target == istarget && it.weaponentity_fld == weaponentity)
278 return it;
279 });
280
281 return NULL;
282}
283
284void W_Seeker_Attack(Weapon thiswep, entity actor, .entity weaponentity)
285{
286 entity closest = NULL;
287 vector it_trans_org;
288 float it_dist2, closest_dist2 = 0;
289 IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == actor,
290 {
291 // First check line of sight
292 it_trans_org = WarpZone_RefSys_TransformOrigin(it.tag_target, actor, it.tag_target.origin);
293 WarpZone_TraceLine(actor.origin + actor.view_ofs, it_trans_org, MOVE_NOMONSTERS, actor);
294 if (trace_fraction < 1 && trace_ent != it)
295 continue;
296
297 it_dist2 = vlen2(actor.origin - it_trans_org);
298 if (!closest || it_dist2 < closest_dist2) // new closest
299 {
300 closest_dist2 = it_dist2;
301 closest = it.tag_target;
302 }
303 });
304
305 W_Seeker_Fire_Missile(thiswep, actor, weaponentity, closest, -1);
306}
307
308void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seeker_Attack
309{
310 --this.cnt;
311
312 Weapon thiswep = WEP_SEEKER;
313 .entity weaponentity = this.weaponentity_fld;
314 if ((!(this.realowner.items & IT_UNLIMITED_AMMO) && GetResource(this.realowner, thiswep.ammo_type) < WEP_CVAR(WEP_SEEKER, missile_ammo))
315 || this.cnt <= -1 || IS_DEAD(this.realowner) || this.realowner.(weaponentity).m_switchweapon != thiswep)
316 {
317 delete(this);
318 return;
319 }
320
321 this.nextthink = time + WEP_CVAR(WEP_SEEKER, missile_delay) * W_WeaponRateFactor(this.realowner);
322
323 entity own = this.realowner;
324
325 entity oldenemy = own.enemy;
326 own.enemy = this.enemy;
327 W_Seeker_Fire_Missile(thiswep, own, weaponentity, own.enemy, own.cnt);
328 own.enemy = oldenemy;
329}
330
332{
333 .entity weaponentity = this.weaponentity_fld;
334 // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up
335 if (IS_DEAD(this.realowner) || IS_DEAD(this.tag_target) || this.realowner.(weaponentity).m_switchweapon != WEP_SEEKER
336 || (time > this.tag_time + WEP_CVAR(WEP_SEEKER, tag_tracker_lifetime)))
337 {
338 if (this)
339 {
340 WaypointSprite_Kill(this.tag_target.wps_tag_tracker);
341 delete(this);
342 }
343 return;
344 }
345
346 // Update the think method information
347 this.nextthink = time;
348}
349
350// ============================
351// Begin: Tag projectile
352// ============================
353.entity lasttag;
354
356{
357 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
358 {
359 .entity weaponentity = weaponentities[slot];
360 if (this.realowner.(weaponentity).lasttag == this)
361 this.realowner.(weaponentity).lasttag = NULL;
362 }
363 delete(this);
364}
365
367{
368 //if (other == this.realowner)
369 // return;
370 Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE, 0, this);
371
373}
374
375void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
376{
377 if (GetResource(this, RES_HEALTH) <= 0)
378 return;
379 TakeResource(this, RES_HEALTH, damage);
380 if (GetResource(this, RES_HEALTH) <= 0)
382}
383
385{
387
388 this.event_damage = func_null;
389 Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, toucher.species, this);
390
391 if (toucher.takedamage == DAMAGE_AIM && !IS_DEAD(toucher))
392 {
393 Damage(toucher, this, this.realowner,
394 WEP_CVAR(WEP_SEEKER, tag_damage),
396 this.weaponentity_fld,
397 this.origin,
398 '0 0 0'
399 );
400
401 // check to see if this person is already tagged by me
402 .entity weaponentity = this.weaponentity_fld;
403 entity tag = W_Seeker_Tagged_Info(this.realowner, weaponentity, toucher);
404
405 if (tag != NULL)
406 {
407 if (toucher.wps_tag_tracker && WEP_CVAR(WEP_SEEKER, type) == 1) // don't attach another waypointsprite without killing the old one first
408 WaypointSprite_Kill(toucher.wps_tag_tracker);
409 tag.tag_time = time;
410 }
411 else
412 {
413 //sprint(this.realowner, strcat("You just tagged ^2", toucher.netname, "^7 with a tracking device!\n"));
414 entity tracker = new(tag_tracker);
415 WarpZone_RefSys_Copy(toucher, this); // toucher is in the same reference as the tag, since the tag hit the toucher
416 tracker.weaponentity_fld = this.weaponentity_fld;
417 tracker.cnt = WEP_CVAR(WEP_SEEKER, missile_count);
418 tracker.owner = this.owner;
419 tracker.realowner = this.realowner;
420 IL_PUSH(g_seeker_trackers, tracker);
421
422 if (WEP_CVAR(WEP_SEEKER, type) == 1)
423 {
424 tracker.tag_target = toucher;
425 tracker.tag_time = time;
427 }
428 else
429 {
430 tracker.enemy = toucher;
432 }
433
434 tracker.nextthink = time;
435 }
436
437 if (WEP_CVAR(WEP_SEEKER, type) == 1)
438 {
439 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);
441 }
442 }
443
445}
446
449{
450 if (time >= this.ltime)
451 {
453 return;
454 }
455 this.nextthink = time;
456
457 // laser guided
458 .entity weaponentity = this.weaponentity_fld;
459 if (this.realowner.(weaponentity).m_weapon == WEP_SEEKER
460 && this == this.realowner.(weaponentity).lasttag
461 && !this.realowner.(weaponentity).tag_release
462 && WEP_CVAR(WEP_SEEKER, tag_guiderate)
463 && time > this.pushltime
464 && !IS_DEAD(this.realowner))
465 {
466 makevectors(vec2(-this.angles.x, this.angles.y));
467 W_GuideProjectile(this, weaponentity, this.pushltime,
468 WEP_CVAR(WEP_SEEKER, tag_guiderate),
469 WEP_CVAR(WEP_SEEKER, tag_guideratedelay),
470 WEP_CVAR(WEP_SEEKER, tag_guidegoal),
471 WEP_CVAR(WEP_SEEKER, tag_guidedelay),
472 NULL, SND_DEVASTATOR_MODE, VOL_BASE * 0.5); // TODO: add a better sound here
474 }
475}
476
477void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity)
478{
479 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, tag_ammo), weaponentity);
480
481 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);
482
483 entity missile = new(seeker_tag);
484 WarpZone_RefSys_Copy(missile, actor);
485 if (WEP_CVAR(WEP_SEEKER, tag_guiderate) > 0)
486 {
487 actor.(weaponentity).lasttag = missile;
488 missile.pushltime = time + WEP_CVAR(WEP_SEEKER, tag_guidedelay);
489 missile.ltime = time + WEP_CVAR(WEP_SEEKER, tag_lifetime);
490 missile.nextthink = time;
492 }
493 else
494 {
495 missile.nextthink = time + WEP_CVAR(WEP_SEEKER, tag_lifetime);
496 setthink(missile, SUB_Remove);
497 }
498
499 missile.owner = missile.realowner = actor;
500 missile.bot_dodge = true;
501 missile.bot_dodgerating = 50;
503 set_movetype(missile, MOVETYPE_FLY);
504 missile.projectiledeathtype = thiswep.m_id;
505 missile.weaponentity_fld = weaponentity;
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, fabs(WEP_CVAR(WEP_SEEKER, tag_guiderate)) <= 30, PROJECTILE_SEEKER_TAG, false); // has sound
526 // Network velocity for the trail only if the guiding isn't strong,
527 // or else the trail clearly goes off path (backwards)
528
529 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
530
531 if (time >= missile.nextthink)
532 getthink(missile)(missile);
533}
534
535// ============================
536// Begin: Genereal weapon functions
537// ============================
538
539METHOD(Seeker, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
540{
541 if (WEP_CVAR(WEP_SEEKER, type) == 1)
542 {
543 if (W_Seeker_Tagged_Info(actor, weaponentity, actor.enemy) != NULL)
544 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);
545 else
546 PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, false);
547 }
548 else
549 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, true);
550}
551
552METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
553{
554 if (autocvar_g_balance_seeker_reload_ammo // forced reload
555 && actor.(weaponentity).clip_load < WEP_CVAR(WEP_SEEKER, missile_ammo)
556 && actor.(weaponentity).clip_load < WEP_CVAR(WEP_SEEKER, tag_ammo))
557 {
558 thiswep.wr_reload(thiswep, actor, weaponentity);
559 return;
560 }
561
562 if (WEP_CVAR(WEP_SEEKER, type) == 1)
563 {
564 if (fire & 1)
565 {
566 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, missile_refire)))
567 {
568 W_Seeker_Attack(thiswep, actor, weaponentity);
569 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, missile_animtime), w_ready);
570 }
571 }
572 else if (fire & 2)
573 {
574 if ((actor.(weaponentity).tag_release || WEP_CVAR(WEP_SEEKER, tag_guidestop))
575 && weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, tag_refire)))
576 {
577 W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
578 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, tag_animtime), w_ready);
579 actor.(weaponentity).tag_release = false;
580 }
581 }
582 else
583 actor.(weaponentity).tag_release = true;
584 }
585 else
586 {
587 if (fire & 1)
588 {
589 if ((actor.(weaponentity).tag_release || WEP_CVAR(WEP_SEEKER, tag_guidestop))
590 && weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, tag_refire)))
591 {
592 W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
593 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, tag_animtime), w_ready);
594 actor.(weaponentity).tag_release = false;
595 }
596 }
597 else if (fire & 2)
598 {
599 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, flac_refire)))
600 {
601 W_Seeker_Fire_Flac(thiswep, actor, weaponentity);
602 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, flac_animtime), w_ready);
603 }
604 }
605 else
606 actor.(weaponentity).tag_release = true;
607 }
608}
609
610METHOD(Seeker, wr_setup, void(entity thiswep, entity actor, .entity weaponentity))
611{
612 actor.(weaponentity).tag_release = true;
613}
614
615METHOD(Seeker, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
616{
617 float ammo_amount;
618 if (WEP_CVAR(WEP_SEEKER, type) == 1)
619 {
620 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
621 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
622 }
623 else
624 {
625 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
626 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
627 }
628 return ammo_amount;
629}
630
631METHOD(Seeker, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
632{
633 float ammo_amount;
634 if (WEP_CVAR(WEP_SEEKER, type) == 1)
635 {
636 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
637 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
638 }
639 else
640 {
641 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
642 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
643 }
644 return ammo_amount;
645}
646
647METHOD(Seeker, wr_resetplayer, void(entity thiswep, entity actor))
648{
649 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
650 {
651 .entity weaponentity = weaponentities[slot];
652 actor.(weaponentity).lasttag = NULL; // stop tag guiding, no revenge from the grave!
653 actor.(weaponentity).tag_release = true;
654 }
655}
656
657METHOD(Seeker, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
658{
659 W_Reload(actor, weaponentity, min(WEP_CVAR(WEP_SEEKER, missile_ammo), WEP_CVAR(WEP_SEEKER, tag_ammo)), SND_RELOAD);
660}
661
662METHOD(Seeker, wr_suicidemessage, Notification(entity thiswep))
663{
664 return WEAPON_SEEKER_SUICIDE;
665}
666
667METHOD(Seeker, wr_killmessage, Notification(entity thiswep))
668{
670 return WEAPON_SEEKER_MURDER_TAG;
671 else
672 return WEAPON_SEEKER_MURDER_SPRAY;
673}
674
675#endif // SVQC
676#ifdef CSQC
677
678METHOD(Seeker, wr_impacteffect, void(entity thiswep, entity actor))
679{
680 // XONRELEASE TODO: xonotic-v0.9.0 post-release remove this caching & fallback, use SEEKER_TAG_IMPACT and SEEKER_EXPLODE
681 // See also client/weapons/projectile.qc.
682 static int impact_effnum;
683 if (!impact_effnum)
684 impact_effnum = particleeffectnum(EFFECT_SEEKER_TAG_IMPACT); // can't return 0
685 static entity explode_eff;
686 if (!explode_eff)
687 explode_eff = (particleeffectnum(EFFECT_SEEKER_EXPLODE) < 0)
688 ? EFFECT_HAGAR_EXPLODE // compatible with Xonotic v0.8.6 or lower
689 : EFFECT_SEEKER_EXPLODE;
690
691 vector org2 = w_org + w_backoff * 2;
693 {
695 {
696 if (impact_effnum < 0)
697 te_knightspike(org2); // compatible with Xonotic v0.8.6 or lower
698 else
699 pointparticles(EFFECT_SEEKER_TAG_IMPACT, org2, '0 0 0', 1);
700 if (!w_issilent)
701 sound(actor, CH_SHOTS, SND_TAG_IMPACT, 1, ATTEN_NORM);
702 }
703 else
704 {
705 pointparticles(explode_eff, org2, '0 0 0', 1);
706 if (!w_issilent)
708 }
709 }
710 else
711 {
712 pointparticles(explode_eff, org2, '0 0 0', 1);
713 if (!w_issilent)
715 }
716}
717
718#endif // CSQC
719#ifdef MENUQC
721
722METHOD(Seeker, describe, string(Seeker this))
723{
724 TC(Seeker, this);
726 PAR(_("The %s is a unique weapon, firing a \"tag\" which then launches a few homing missiles if it collides with a player. "
727 "The homing isn't perfect, so sometimes the missiles can hit an object or a corner."), COLORED_NAME(this));
728 PAR(_("The secondary fire launches a rapid barrage of scattered explosives that travel only a short distance."));
729 PAR(_("It consumes %s ammo, even when the tag doesn't land."), COLORED_NAME(ITEM_Rockets));
730 PAR(_("The %s primary fire deals quite a lot of damage when a tag lands, although it requires skill to aim effectively. "
731 "The secondary fire is only useful in close range combat, and sometimes the explosives can damage yourself too."), COLORED_NAME(this));
732 PAR(W_Guide_Keybinds(this));
733 PAR(W_Guide_DPS_Seeker(this.netname, "missile", "flac", "tag", "missile_count"));
734 return PAGE_TEXT;
735}
736
737#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(name,...)
Definition base.qh:186
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:36
Resource ammo_type
M: ammotype : main ammo type.
Definition weapon.qh:50
int m_id
Definition weapon.qh:37
string netname
Definition powerups.qc:20
float cnt
Definition powerups.qc:24
entity owner
Definition main.qh:87
#define COLORED_NAME(this)
Definition color.qh:189
const int IT_UNLIMITED_AMMO
Definition item.qh:23
float ltime
Definition net.qh:10
#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)
const int FL_PROJECTILE
Definition constants.qh:77
vector velocity
float time
float nextthink
vector origin
void UpdateCSQCProjectile(entity e)
void CSQCProjectile(entity e, float clientanimate, int type, float docull)
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition damage.qc:484
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:937
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:6
#define particleeffectnum(e)
Definition effect.qh:3
ent angles
Definition ent_cs.qc:146
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)
float pushltime
Definition jumppads.qh:21
#define TC(T, sym)
Definition _all.inc:82
void WarpZone_RefSys_Copy(entity me, entity from)
Copies the warpzone reference of from onto me.
Definition common.qc:828
vector movedir
Definition viewloc.qh:18
float random(void)
vector vectoangles(vector v)
float min(float f,...)
float fabs(float f)
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:86
#define METHOD(cname, name, prototype)
Definition oo.qh:274
#define NULL
Definition post.qh:14
#define makevectors
Definition post.qh:21
const int PROJECTILE_SEEKER_TAG
Definition projectiles.qh:5
const int PROJECTILE_SEEKER_FLAC
const int PROJECTILE_SEEKER_MISSILE
void W_Seeker_Vollycontroller_Think(entity this)
Definition seeker.qc:308
void W_Seeker_Fire_Flac(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:227
void W_Seeker_Fire_Tag(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:477
void W_Seeker_Flac_Explode(entity this, entity directhitentity)
Definition seeker.qc:196
void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition seeker.qc:375
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:222
entity W_Seeker_Tagged_Info(entity isowner,.entity weaponentity, entity istarget)
Definition seeker.qc:273
void W_Seeker_Tag_Delete(entity this)
Definition seeker.qc:355
void W_Seeker_Tag_Touch(entity this, entity toucher)
Definition seeker.qc:384
entity lasttag
Definition seeker.qc:353
void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition seeker.qc:96
void W_Seeker_Flac_Touch(entity this, entity toucher)
Definition seeker.qc:215
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_Tag_Think(entity this)
Think function, only for guided tags.
Definition seeker.qc:448
void W_Seeker_Tracker_Think(entity this)
Definition seeker.qc:331
void W_Seeker_Tag_Explode(entity this)
Definition seeker.qc:366
void W_Seeker_Fire_Missile(Weapon thiswep, entity actor,.entity weaponentity, entity m_target, int bullet_count)
Definition seeker.qc:135
void W_Seeker_Attack(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:284
Sound SND_SEEKEREXP_RANDOM(float rnd)
Definition seeker.qh:21
entity tag_target
Definition seeker.qh:129
bool tag_release
Definition seeker.qh:132
Sound SND_TAGEXP_RANDOM(float rnd)
Definition seeker.qh:12
IntrusiveList g_seeker_trackers
Definition seeker.qh:134
entity wps_tag_tracker
Definition seeker.qh:130
#define setthink(e, f)
#define getthink(e)
vector
Definition self.qh:96
entity entity toucher
Definition self.qh:76
#define settouch(e, f)
Definition self.qh:77
float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception)
Definition common.qc:45
void W_GuideProjectile(entity this,.entity weaponentity, float guide_start_time, float guide_rate, float guide_rate_delay, float guide_goal, float guide_delay, entity eff, entity snd, float vol)
Guides a projectile towards the crosshair (v_forward).
Definition common.qc:349
void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this) explode)
Definition common.qc:87
float W_SeekOutEnemy(entity this, float speed_decel, float speed_accel, float speed_max, float turn_rate, bool smart, float smart_mindist, float smart_trace_min, float smart_trace_max)
Automatically guides a projectile towards its enemy.
Definition common.qc:239
void adaptor_think2use_hittype_splash(entity this)
Definition common.qc:106
const int MIF_SPLASH
Definition common.qh:63
int projectiledeathtype
Definition common.qh:38
#define PROJECTILE_TOUCH(e, t)
Definition common.qh:45
IntrusiveList g_projectiles
Definition common.qh:75
const int MIF_GUIDED_TAG
Definition common.qh:70
#define PROJECTILE_MAKETRIGGER(e)
Definition common.qh:51
const float VOL_BASE
Definition sound.qh:36
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:652
#define PAR(...)
Adds an individually translatable paragraph to PAGE_TEXT without having to deal with strcat and sprin...
Definition string.qh:658
#define PAGE_TEXT_INIT()
Definition string.qh:651
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_SetupShot_GetAlternatingOffset(int bullet_count)
Definition tracing.qc:166
vector w_shotdir
Definition tracing.qh:20
#define W_SetupProjVelocity_PRE(ent, wep, prefix)
Definition tracing.qh:67
#define W_SetupProjVelocity_UP_PRE(ent, wep, prefix)
Definition tracing.qh:54
#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 vec2(...)
Definition vector.qh:95
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:803
void W_MuzzleFlash(Weapon thiswep, entity actor,.entity weaponentity, vector shotorg, vector shotdir)
Definition all.qc:694
string W_Guide_DPS_Seeker(string name, string pri, string sec, string other, string shots)
Definition all.qc:980
#define WEP_CVAR(wep, name)
Definition all.qh:339
const int MAX_WEAPONSLOTS
Definition weapon.qh:15
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:16
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)]