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_MISSILE, 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_SEEKER_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
437 this.event_damage = func_null;
438 Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, toucher.species, this);
439
440 if (toucher.takedamage == DAMAGE_AIM && !IS_DEAD(toucher))
441 {
442 // check to see if this person is already tagged by me
443 .entity weaponentity = this.weaponentity_fld;
444 entity tag = W_Seeker_Tagged_Info(this.realowner, weaponentity, toucher);
445
446 if (tag != NULL)
447 {
448 if (toucher.wps_tag_tracker && WEP_CVAR(WEP_SEEKER, type) == 1) // don't attach another waypointsprite without killing the old one first
449 WaypointSprite_Kill(toucher.wps_tag_tracker);
450 tag.tag_time = time;
451 }
452 else
453 {
454 //sprint(this.realowner, strcat("You just tagged ^2", toucher.netname, "^7 with a tracking device!\n"));
455 entity e = new(tag_tracker);
456 e.weaponentity_fld = this.weaponentity_fld;
457 e.cnt = WEP_CVAR(WEP_SEEKER, missile_count);
458 e.owner = this.owner;
459 e.realowner = this.realowner;
461
462 if (WEP_CVAR(WEP_SEEKER, type) == 1)
463 {
464 e.tag_target = toucher;
465 e.tag_time = time;
467 }
468 else
469 {
470 e.enemy = toucher;
472 }
473
474 e.nextthink = time;
475 }
476
477 if (WEP_CVAR(WEP_SEEKER, type) == 1)
478 {
479 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);
481 }
482 }
483
484 delete(this);
485 return;
486}
487
488void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity)
489{
490 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, tag_ammo), weaponentity);
491
492 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);
493
494 entity missile = new(seeker_tag);
495 missile.weaponentity_fld = weaponentity;
496 missile.owner = missile.realowner = actor;
497 missile.bot_dodge = true;
498 missile.bot_dodgerating = 50;
500 setthink(missile, SUB_Remove);
501 missile.nextthink = time + WEP_CVAR(WEP_SEEKER, tag_lifetime);
502 set_movetype(missile, MOVETYPE_FLY);
503
504 missile.takedamage = DAMAGE_YES;
505 missile.event_damage = W_Seeker_Tag_Damage;
506 SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(WEP_SEEKER, tag_health));
507 missile.damageforcescale = WEP_CVAR(WEP_SEEKER, tag_damageforcescale);
508
509 PROJECTILE_MAKETRIGGER(missile);
510 setorigin(missile, w_shotorg);
511 setsize(missile, '-2 -2 -2', '2 2 2');
512
513 missile.flags = FL_PROJECTILE;
514 IL_PUSH(g_projectiles, missile);
515 IL_PUSH(g_bot_dodge, missile);
516 //missile.missile_flags = MIF_..?;
517
518 set_movetype(missile, MOVETYPE_FLY);
519 W_SetupProjVelocity_PRE(missile, WEP_SEEKER, tag_);
520 missile.angles = vectoangles(missile.velocity);
521
522 CSQCProjectile(missile, true, PROJECTILE_SEEKER_TAG, false); // has sound
523
524 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
525}
526
527// ============================
528// Begin: Genereal weapon functions
529// ============================
530
531METHOD(Seeker, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
532{
533 if (WEP_CVAR(WEP_SEEKER, type) == 1)
534 {
535 if (W_Seeker_Tagged_Info(actor, weaponentity, actor.enemy) != NULL)
536 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);
537 else
538 PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, false);
539 }
540 else
541 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, true);
542}
543
544METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
545{
546 if (autocvar_g_balance_seeker_reload_ammo && actor.(weaponentity).clip_load < min(WEP_CVAR(WEP_SEEKER, missile_ammo), WEP_CVAR(WEP_SEEKER, tag_ammo)))
547 { // forced reload
548 thiswep.wr_reload(thiswep, actor, weaponentity);
549 return;
550 }
551
552 if (fire & 1)
553 {
554 if (WEP_CVAR(WEP_SEEKER, type) == 1)
555 {
556 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, missile_refire)))
557 {
558 W_Seeker_Attack(thiswep, actor, weaponentity);
559 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, missile_animtime), w_ready);
560 }
561 }
562 else
563 {
564 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, tag_refire)))
565 {
566 W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
567 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, tag_animtime), w_ready);
568 }
569 }
570 }
571 else if (fire & 2)
572 {
573 if (WEP_CVAR(WEP_SEEKER, type) == 1)
574 {
575 if (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 }
580 }
581 else
582 {
583 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, flac_refire)))
584 {
585 W_Seeker_Fire_Flac(thiswep, actor, weaponentity);
586 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, flac_animtime), w_ready);
587 }
588 }
589 }
590}
591
592METHOD(Seeker, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
593{
594 float ammo_amount;
595 if (WEP_CVAR(WEP_SEEKER, type) == 1)
596 {
597 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
598 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
599 }
600 else
601 {
602 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
603 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
604 }
605 return ammo_amount;
606}
607
608METHOD(Seeker, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
609{
610 float ammo_amount;
611 if (WEP_CVAR(WEP_SEEKER, type) == 1)
612 {
613 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
614 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
615 }
616 else
617 {
618 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
619 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
620 }
621 return ammo_amount;
622}
623
624METHOD(Seeker, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
625{
626 W_Reload(actor, weaponentity, min(WEP_CVAR(WEP_SEEKER, missile_ammo), WEP_CVAR(WEP_SEEKER, tag_ammo)), SND_RELOAD);
627}
628
629METHOD(Seeker, wr_suicidemessage, Notification(entity thiswep))
630{
631 return WEAPON_SEEKER_SUICIDE;
632}
633
634METHOD(Seeker, wr_killmessage, Notification(entity thiswep))
635{
637 return WEAPON_SEEKER_MURDER_TAG;
638 else
639 return WEAPON_SEEKER_MURDER_SPRAY;
640}
641
642#endif // SVQC
643#ifdef CSQC
644
645METHOD(Seeker, wr_impacteffect, void(entity thiswep, entity actor))
646{
647 // XONRELEASE TODO: xonotic-v0.9.0 post-release remove this caching & fallback, use SEEKER_TAG_IMPACT and SEEKER_EXPLODE
648 // See also client/weapons/projectile.qc.
649 static int impact_effnum;
650 if (!impact_effnum)
651 impact_effnum = particleeffectnum(EFFECT_SEEKER_TAG_IMPACT); // can't return 0
652 static entity explode_eff;
653 if (!explode_eff)
654 explode_eff = (particleeffectnum(EFFECT_SEEKER_EXPLODE) < 0)
655 ? EFFECT_HAGAR_EXPLODE // compatible with Xonotic v0.8.6 or lower
656 : EFFECT_SEEKER_EXPLODE;
657
658 vector org2 = w_org + w_backoff * 2;
660 {
662 {
663 if (impact_effnum < 0)
664 te_knightspike(org2); // compatible with Xonotic v0.8.6 or lower
665 else
666 pointparticles(EFFECT_SEEKER_TAG_IMPACT, org2, '0 0 0', 1);
667 if (!w_issilent)
668 sound(actor, CH_SHOTS, SND_TAG_IMPACT, 1, ATTEN_NORM);
669 }
670 else
671 {
672 pointparticles(explode_eff, org2, '0 0 0', 1);
673 if (!w_issilent)
675 }
676 }
677 else
678 {
679 pointparticles(explode_eff, org2, '0 0 0', 1);
680 if (!w_issilent)
682 }
683}
684
685#endif // CSQC
686#ifdef MENUQC
688
689METHOD(Seeker, describe, string(Seeker this))
690{
691 TC(Seeker, this);
693 PAR(_("The %s is a unique weapon, firing a \"tag\" which then launches a few homing missiles if it collides with a player. "
694 "The homing isn't perfect, so sometimes the missiles can hit an object or a corner."), COLORED_NAME(this));
695 PAR(_("The secondary fire launches a rapid barrage of scattered explosives that travel only a short distance."));
696 PAR(_("It consumes %s ammo, even when the tag doesn't land."), COLORED_NAME(ITEM_Rockets));
697 PAR(_("The %s primary fire deals quite a lot of damage when a tag lands, although it requires skill to aim effectively. "
698 "The secondary fire is only useful in close range combat, and sometimes the explosives can damage yourself too."), COLORED_NAME(this));
699 PAR(W_Guide_Keybinds(this));
700 PAR(W_Guide_DPS_primaryMultishot(this.netname, "missile", "flac", "missile_count", "tag_refire"));
701 return PAGE_TEXT;
702}
703
704#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)
const int FL_PROJECTILE
Definition constants.qh:77
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:929
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
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_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: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:488
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)]