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 dist = W_SeekOutEnemy(this,
47 WEP_CVAR(WEP_SEEKER, missile_decel),
48 WEP_CVAR(WEP_SEEKER, missile_accel),
49 WEP_CVAR(WEP_SEEKER, missile_speed_max),
50 WEP_CVAR(WEP_SEEKER, missile_turnrate),
51 WEP_CVAR(WEP_SEEKER, missile_smart),
52 WEP_CVAR(WEP_SEEKER, missile_smart_mindist),
53 WEP_CVAR(WEP_SEEKER, missile_smart_trace_min),
54 WEP_CVAR(WEP_SEEKER, missile_smart_trace_max));
55
56 // Proxy
57 if (WEP_CVAR(WEP_SEEKER, missile_proxy))
58 {
59 if (dist <= WEP_CVAR(WEP_SEEKER, missile_proxy_maxrange))
60 {
61 if (this.cvar_cl_autoswitch == 0)
62 this.cvar_cl_autoswitch = time + WEP_CVAR(WEP_SEEKER, missile_proxy_delay);
63 else
64 {
65 if (this.cvar_cl_autoswitch <= time)
66 {
68 this.cvar_cl_autoswitch = 0;
69 }
70 }
71 }
72 else
73 {
74 if (this.cvar_cl_autoswitch != 0)
75 this.cvar_cl_autoswitch = 0;
76 }
77 }
79
80 if (IS_DEAD(this.enemy))
81 {
82 this.enemy = NULL;
83 this.cnt = time + 1 + (random() * 4);
84 this.nextthink = this.cnt;
85 return;
86 }
87
88 //this.angles = vectoangles(this.velocity); // turn model in the new flight direction
89 this.nextthink = time;// + 0.05; // csqc projectiles
91}
92
93
94
95void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
96{
97 if (GetResource(this, RES_HEALTH) <= 0)
98 return;
99 if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
100 return; // g_projectiles_damage says to halt
101
102 if (this.realowner == attacker)
103 TakeResource(this, RES_HEALTH, (damage * 0.25));
104 else
105 TakeResource(this, RES_HEALTH, damage);
106
107 if (GetResource(this, RES_HEALTH) <= 0)
109}
110
111/*
112void W_Seeker_Missile_Animate(entity this)
113{
114 ++this.frame;
115 this.nextthink = time + 0.05;
116
117 if (this.enemy != NULL
118 && (this.enemy.takedamage != DAMAGE_AIM || IS_DEAD(this.enemy)))
119 this.enemy = NULL;
120
121 if (this.frame == 5)
122 {
123 this.think = W_Seeker_Missile_Think;
124 this.nextthink = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles
125 this.move_movetype = (autocvar_g_balance_seeker_missile_proxy)
126 ? MOVETYPE_BOUNCEMISSILE
127 : MOVETYPE_FLYMISSILE;
128 }
129
130 UpdateCSQCProjectile(this);
131}
132*/
133
134void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, .entity weaponentity, entity m_target, int bullet_count)
135{
136 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, missile_ammo), weaponentity);
137
138 makevectors(actor.v_angle);
139 vector old_movedir = actor.(weaponentity).movedir;
140 if (bullet_count > 0)
141 actor.(weaponentity).movedir += W_SetupShot_GetAlternatingOffset(bullet_count);
142 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));
143 actor.(weaponentity).movedir = old_movedir;
144 W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
145
146 //actor.detornator = false;
147
148 entity missile = new(seeker_missile);
149 WarpZone_RefSys_Copy(missile, actor);
150 missile.owner = missile.realowner = actor;
151 missile.bot_dodge = true;
152 missile.bot_dodgerating = WEP_CVAR(WEP_SEEKER, missile_damage);
153
156 missile.event_damage = W_Seeker_Missile_Damage;
157 missile.nextthink = time; // + 0.2; // + cvar("g_balance_seeker_missile_activate_delay");
158 missile.cnt = time + WEP_CVAR(WEP_SEEKER, missile_lifetime);
159 missile.enemy = m_target;
160 missile.scale = 2;
161 missile.takedamage = DAMAGE_YES;
162 missile.weaponentity_fld = weaponentity;
163 SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(WEP_SEEKER, missile_health));
164 missile.damageforcescale = WEP_CVAR(WEP_SEEKER, missile_damageforcescale);
165 missile.damagedbycontents = true;
167 //missile.think = W_Seeker_Missile_Animate; // csqc projectiles.
168
169 if (missile.enemy != NULL)
170 missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
171 else
172 missile.projectiledeathtype = thiswep.m_id;
173
174 PROJECTILE_MAKETRIGGER(missile);
175 setorigin(missile, w_shotorg);
176 setsize(missile, '-4 -4 -4', '4 4 4');
178 missile.flags = FL_PROJECTILE;
179 IL_PUSH(g_projectiles, missile);
180 IL_PUSH(g_bot_dodge, missile);
181 missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG;
182
183 W_SetupProjVelocity_UP_PRE(missile, WEP_SEEKER, missile_);
184
185 missile.angles = vectoangles(missile.velocity);
186
187 CSQCProjectile(missile, false, PROJECTILE_SEEKER_MISSILE, true);
188
189 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
190}
191
192// ============================
193// Begin: FLAC, close range attack meant for defeating rockets which are coming at you.
194// ============================
195void W_Seeker_Flac_Explode(entity this, entity directhitentity)
196{
197 this.event_damage = func_null;
198
199 RadiusDamage(this, this.realowner,
200 WEP_CVAR(WEP_SEEKER, flac_damage),
201 WEP_CVAR(WEP_SEEKER, flac_edgedamage),
202 WEP_CVAR(WEP_SEEKER, flac_radius),
203 NULL,
204 NULL,
205 WEP_CVAR(WEP_SEEKER, flac_force),
207 this.weaponentity_fld,
208 directhitentity
209 );
210
211 delete(this);
212}
213
220
222{
223 W_Seeker_Flac_Explode(this, trigger);
224}
225
226void W_Seeker_Fire_Flac(Weapon thiswep, entity actor, .entity weaponentity)
227{
228 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, flac_ammo), weaponentity);
229
230 vector old_movedir = actor.(weaponentity).movedir;
231 actor.(weaponentity).movedir += W_SetupShot_GetAlternatingOffset(actor.(weaponentity).bulletcounter);
232 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);
233 actor.(weaponentity).movedir = old_movedir;
234
235 // uses hagar effects!
236 W_MuzzleFlash(WEP_HAGAR, actor, weaponentity, w_shotorg, w_shotdir);
237
238 entity missile;
239 missile = new(missile);
240 missile.owner = missile.realowner = actor;
241 missile.bot_dodge = true;
242 missile.bot_dodgerating = WEP_CVAR(WEP_SEEKER, flac_damage);
244 missile.use = W_Seeker_Flac_Explode_use;
246 missile.nextthink = time + WEP_CVAR(WEP_SEEKER, flac_lifetime) + WEP_CVAR(WEP_SEEKER, flac_lifetime_rand);
247 set_movetype(missile, MOVETYPE_FLY);
248 missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
249 missile.weaponentity_fld = weaponentity;
250 missile.flags = FL_PROJECTILE;
251 IL_PUSH(g_projectiles, missile);
252 IL_PUSH(g_bot_dodge, missile);
253 missile.missile_flags = MIF_SPLASH;
254
255 // csqc projectiles
256 //missile.angles = vectoangles(missile.velocity);
257 //missile.scale = 0.4; // BUG: the model is too big
258
259 PROJECTILE_MAKETRIGGER(missile);
260 setorigin(missile, w_shotorg);
261 setsize(missile, '-2 -2 -2', '2 2 2');
262
263 W_SetupProjVelocity_UP_PRE(missile, WEP_SEEKER, flac_);
264 CSQCProjectile(missile, true, PROJECTILE_SEEKER_FLAC, true);
265
266 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
267}
268
269// ============================
270// Begin: Tag and rocket controllers
271// ============================
272entity W_Seeker_Tagged_Info(entity isowner, .entity weaponentity, entity istarget)
273{
274 IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == isowner,
275 {
276 if (it.tag_target == istarget && it.weaponentity_fld == weaponentity)
277 return it;
278 });
279
280 return NULL;
281}
282
283void W_Seeker_Attack(Weapon thiswep, entity actor, .entity weaponentity)
284{
285 entity closest = NULL;
286 vector it_trans_org;
287 float it_dist2, closest_dist2 = 0;
288 IL_EACH(g_seeker_trackers, it.classname == "tag_tracker" && it.realowner == actor,
289 {
290 // First check line of sight
291 it_trans_org = WarpZone_RefSys_TransformOrigin(it.tag_target, actor, it.tag_target.origin);
292 WarpZone_TraceLine(actor.origin + actor.view_ofs, it_trans_org, MOVE_NOMONSTERS, actor);
293 if (trace_fraction < 1 && trace_ent != it)
294 continue;
295
296 it_dist2 = vlen2(actor.origin - it_trans_org);
297 if (!closest || it_dist2 < closest_dist2) // new closest
298 {
299 closest_dist2 = it_dist2;
300 closest = it.tag_target;
301 }
302 });
303
304 W_Seeker_Fire_Missile(thiswep, actor, weaponentity, closest, -1);
305}
306
307void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seeker_Attack
308{
309 --this.cnt;
310
311 Weapon thiswep = WEP_SEEKER;
312 .entity weaponentity = this.weaponentity_fld;
313 if ((!(this.realowner.items & IT_UNLIMITED_AMMO) && GetResource(this.realowner, thiswep.ammo_type) < WEP_CVAR(WEP_SEEKER, missile_ammo))
314 || this.cnt <= -1 || IS_DEAD(this.realowner) || this.realowner.(weaponentity).m_switchweapon != thiswep)
315 {
316 delete(this);
317 return;
318 }
319
320 this.nextthink = time + WEP_CVAR(WEP_SEEKER, missile_delay) * W_WeaponRateFactor(this.realowner);
321
322 entity own = this.realowner;
323
324 entity oldenemy = own.enemy;
325 own.enemy = this.enemy;
326 W_Seeker_Fire_Missile(thiswep, own, weaponentity, own.enemy, own.cnt);
327 own.enemy = oldenemy;
328}
329
331{
332 .entity weaponentity = this.weaponentity_fld;
333 // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up
334 if (IS_DEAD(this.realowner) || IS_DEAD(this.tag_target) || this.realowner.(weaponentity).m_switchweapon != WEP_SEEKER
335 || (time > this.tag_time + WEP_CVAR(WEP_SEEKER, tag_tracker_lifetime)))
336 {
337 if (this)
338 {
339 WaypointSprite_Kill(this.tag_target.wps_tag_tracker);
340 delete(this);
341 }
342 return;
343 }
344
345 // Update the think method information
346 this.nextthink = time;
347}
348
349// ============================
350// Begin: Tag projectile
351// ============================
353{
354 //if (other == this.realowner)
355 // return;
356 Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE, 0, this);
357
358 delete(this);
359}
360
361void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
362{
363 if (GetResource(this, RES_HEALTH) <= 0)
364 return;
365 TakeResource(this, RES_HEALTH, damage);
366 if (GetResource(this, RES_HEALTH) <= 0)
368}
369
371{
373
374 this.event_damage = func_null;
375 Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, toucher.species, this);
376
377 if (toucher.takedamage == DAMAGE_AIM && !IS_DEAD(toucher))
378 {
379 // check to see if this person is already tagged by me
380 .entity weaponentity = this.weaponentity_fld;
381 entity tag = W_Seeker_Tagged_Info(this.realowner, weaponentity, toucher);
382
383 if (tag != NULL)
384 {
385 if (toucher.wps_tag_tracker && WEP_CVAR(WEP_SEEKER, type) == 1) // don't attach another waypointsprite without killing the old one first
386 WaypointSprite_Kill(toucher.wps_tag_tracker);
387 tag.tag_time = time;
388 }
389 else
390 {
391 //sprint(this.realowner, strcat("You just tagged ^2", toucher.netname, "^7 with a tracking device!\n"));
392 entity e = new(tag_tracker);
393 WarpZone_RefSys_Copy(toucher, this); // toucher is in the same reference as the tag, since the tag hit the toucher
394 e.weaponentity_fld = this.weaponentity_fld;
395 e.cnt = WEP_CVAR(WEP_SEEKER, missile_count);
396 e.owner = this.owner;
397 e.realowner = this.realowner;
399
400 if (WEP_CVAR(WEP_SEEKER, type) == 1)
401 {
402 e.tag_target = toucher;
403 e.tag_time = time;
405 }
406 else
407 {
408 e.enemy = toucher;
410 }
411
412 e.nextthink = time;
413 }
414
415 if (WEP_CVAR(WEP_SEEKER, type) == 1)
416 {
417 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);
419 }
420 }
421
422 delete(this);
423 return;
424}
425
426void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity)
427{
428 W_DecreaseAmmo(thiswep, actor, WEP_CVAR(WEP_SEEKER, tag_ammo), weaponentity);
429
430 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);
431
432 entity missile = new(seeker_tag);
433 WarpZone_RefSys_Copy(missile, actor);
434 missile.weaponentity_fld = weaponentity;
435 missile.owner = missile.realowner = actor;
436 missile.bot_dodge = true;
437 missile.bot_dodgerating = 50;
439 setthink(missile, SUB_Remove);
440 missile.nextthink = time + WEP_CVAR(WEP_SEEKER, tag_lifetime);
441 set_movetype(missile, MOVETYPE_FLY);
442
443 missile.takedamage = DAMAGE_YES;
444 missile.event_damage = W_Seeker_Tag_Damage;
445 SetResourceExplicit(missile, RES_HEALTH, WEP_CVAR(WEP_SEEKER, tag_health));
446 missile.damageforcescale = WEP_CVAR(WEP_SEEKER, tag_damageforcescale);
447
448 PROJECTILE_MAKETRIGGER(missile);
449 setorigin(missile, w_shotorg);
450 setsize(missile, '-2 -2 -2', '2 2 2');
451
452 missile.flags = FL_PROJECTILE;
453 IL_PUSH(g_projectiles, missile);
454 IL_PUSH(g_bot_dodge, missile);
455 //missile.missile_flags = MIF_..?;
456
457 set_movetype(missile, MOVETYPE_FLY);
458 W_SetupProjVelocity_PRE(missile, WEP_SEEKER, tag_);
459 missile.angles = vectoangles(missile.velocity);
460
461 CSQCProjectile(missile, true, PROJECTILE_SEEKER_TAG, false); // has sound
462
463 MUTATOR_CALLHOOK(EditProjectile, actor, missile);
464}
465
466// ============================
467// Begin: Genereal weapon functions
468// ============================
469
470METHOD(Seeker, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
471{
472 if (WEP_CVAR(WEP_SEEKER, type) == 1)
473 {
474 if (W_Seeker_Tagged_Info(actor, weaponentity, actor.enemy) != NULL)
475 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);
476 else
477 PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, false);
478 }
479 else
480 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(WEP_SEEKER, tag_speed), 0, WEP_CVAR(WEP_SEEKER, tag_lifetime), false, true);
481}
482
483METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
484{
485 if (autocvar_g_balance_seeker_reload_ammo && actor.(weaponentity).clip_load < min(WEP_CVAR(WEP_SEEKER, missile_ammo), WEP_CVAR(WEP_SEEKER, tag_ammo)))
486 { // forced reload
487 thiswep.wr_reload(thiswep, actor, weaponentity);
488 return;
489 }
490
491 if (fire & 1)
492 {
493 if (WEP_CVAR(WEP_SEEKER, type) == 1)
494 {
495 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, missile_refire)))
496 {
497 W_Seeker_Attack(thiswep, actor, weaponentity);
498 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, missile_animtime), w_ready);
499 }
500 }
501 else
502 {
503 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, tag_refire)))
504 {
505 W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
506 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, tag_animtime), w_ready);
507 }
508 }
509 }
510 else if (fire & 2)
511 {
512 if (WEP_CVAR(WEP_SEEKER, type) == 1)
513 {
514 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, tag_refire)))
515 {
516 W_Seeker_Fire_Tag(thiswep, actor, weaponentity);
517 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, tag_animtime), w_ready);
518 }
519 }
520 else
521 {
522 if (weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(WEP_SEEKER, flac_refire)))
523 {
524 W_Seeker_Fire_Flac(thiswep, actor, weaponentity);
525 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(WEP_SEEKER, flac_animtime), w_ready);
526 }
527 }
528 }
529}
530
531METHOD(Seeker, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
532{
533 float ammo_amount;
534 if (WEP_CVAR(WEP_SEEKER, type) == 1)
535 {
536 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
537 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, missile_ammo);
538 }
539 else
540 {
541 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
542 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
543 }
544 return ammo_amount;
545}
546
547METHOD(Seeker, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
548{
549 float ammo_amount;
550 if (WEP_CVAR(WEP_SEEKER, type) == 1)
551 {
552 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
553 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, tag_ammo);
554 }
555 else
556 {
557 ammo_amount = GetResource(actor, thiswep.ammo_type) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
558 ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(WEP_SEEKER, flac_ammo);
559 }
560 return ammo_amount;
561}
562
563METHOD(Seeker, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
564{
565 W_Reload(actor, weaponentity, min(WEP_CVAR(WEP_SEEKER, missile_ammo), WEP_CVAR(WEP_SEEKER, tag_ammo)), SND_RELOAD);
566}
567
568METHOD(Seeker, wr_suicidemessage, Notification(entity thiswep))
569{
570 return WEAPON_SEEKER_SUICIDE;
571}
572
573METHOD(Seeker, wr_killmessage, Notification(entity thiswep))
574{
576 return WEAPON_SEEKER_MURDER_TAG;
577 else
578 return WEAPON_SEEKER_MURDER_SPRAY;
579}
580
581#endif // SVQC
582#ifdef CSQC
583
584METHOD(Seeker, wr_impacteffect, void(entity thiswep, entity actor))
585{
586 // XONRELEASE TODO: xonotic-v0.9.0 post-release remove this caching & fallback, use SEEKER_TAG_IMPACT and SEEKER_EXPLODE
587 // See also client/weapons/projectile.qc.
588 static int impact_effnum;
589 if (!impact_effnum)
590 impact_effnum = particleeffectnum(EFFECT_SEEKER_TAG_IMPACT); // can't return 0
591 static entity explode_eff;
592 if (!explode_eff)
593 explode_eff = (particleeffectnum(EFFECT_SEEKER_EXPLODE) < 0)
594 ? EFFECT_HAGAR_EXPLODE // compatible with Xonotic v0.8.6 or lower
595 : EFFECT_SEEKER_EXPLODE;
596
597 vector org2 = w_org + w_backoff * 2;
599 {
601 {
602 if (impact_effnum < 0)
603 te_knightspike(org2); // compatible with Xonotic v0.8.6 or lower
604 else
605 pointparticles(EFFECT_SEEKER_TAG_IMPACT, org2, '0 0 0', 1);
606 if (!w_issilent)
607 sound(actor, CH_SHOTS, SND_TAG_IMPACT, 1, ATTEN_NORM);
608 }
609 else
610 {
611 pointparticles(explode_eff, org2, '0 0 0', 1);
612 if (!w_issilent)
614 }
615 }
616 else
617 {
618 pointparticles(explode_eff, org2, '0 0 0', 1);
619 if (!w_issilent)
621 }
622}
623
624#endif // CSQC
625#ifdef MENUQC
627
628METHOD(Seeker, describe, string(Seeker this))
629{
630 TC(Seeker, this);
632 PAR(_("The %s is a unique weapon, firing a \"tag\" which then launches a few homing missiles if it collides with a player. "
633 "The homing isn't perfect, so sometimes the missiles can hit an object or a corner."), COLORED_NAME(this));
634 PAR(_("The secondary fire launches a rapid barrage of scattered explosives that travel only a short distance."));
635 PAR(_("It consumes %s ammo, even when the tag doesn't land."), COLORED_NAME(ITEM_Rockets));
636 PAR(_("The %s primary fire deals quite a lot of damage when a tag lands, although it requires skill to aim effectively. "
637 "The secondary fire is only useful in close range combat, and sometimes the explosives can damage yourself too."), COLORED_NAME(this));
638 PAR(W_Guide_Keybinds(this));
639 PAR(W_Guide_DPS_primaryMultishot(this.netname, "missile", "flac", "missile_count", "tag_refire"));
640 return PAGE_TEXT;
641}
642
643#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
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
vector velocity
float time
float nextthink
vector origin
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: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
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
void WarpZone_RefSys_Copy(entity me, entity from)
Copies the warpzone reference of from onto me.
Definition common.qc:824
vector movedir
Definition viewloc.qh:18
float random(void)
vector vectoangles(vector v)
float min(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: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:307
void W_Seeker_Fire_Flac(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:226
void W_Seeker_Fire_Tag(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:426
void W_Seeker_Flac_Explode(entity this, entity directhitentity)
Definition seeker.qc:195
void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition seeker.qc:361
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:221
entity W_Seeker_Tagged_Info(entity isowner,.entity weaponentity, entity istarget)
Definition seeker.qc:272
void W_Seeker_Tag_Touch(entity this, entity toucher)
Definition seeker.qc:370
void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition seeker.qc:95
void W_Seeker_Flac_Touch(entity this, entity toucher)
Definition seeker.qc:214
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:330
void W_Seeker_Tag_Explode(entity this)
Definition seeker.qc:352
void W_Seeker_Fire_Missile(Weapon thiswep, entity actor,.entity weaponentity, entity m_target, int bullet_count)
Definition seeker.qc:134
void W_Seeker_Attack(Weapon thiswep, entity actor,.entity weaponentity)
Definition seeker.qc:283
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
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
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:58
int projectiledeathtype
Definition common.qh:33
#define PROJECTILE_TOUCH(e, t)
Definition common.qh:40
IntrusiveList g_projectiles
Definition common.qh:70
const int MIF_GUIDED_TAG
Definition common.qh:65
#define PROJECTILE_MAKETRIGGER(e)
Definition common.qh:46
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_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
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)]