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