4 /* ammotype */ ATTRIB(Crylink, ammo_field, .int, ammo_cells);
5 /* impulse */ ATTRIB(Crylink, impulse, int, 6);
6 /* flags */ ATTRIB(Crylink, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_CANCLIMB | WEP_FLAG_NODUAL);
7 /* rating */ ATTRIB(Crylink, bot_pickupbasevalue, float, 6000);
8 /* color */ ATTRIB(Crylink, wpcolor, vector, '1 0.5 1');
9 /* modelname */ ATTRIB(Crylink, mdl, string, "crylink");
11 /* model */ ATTRIB(Crylink, m_model, Model, MDL_CRYLINK_ITEM);
13 /* crosshair */ ATTRIB(Crylink, w_crosshair, string, "gfx/crosshaircrylink");
14 /* crosshair */ ATTRIB(Crylink, w_crosshair_size, float, 0.5);
15 /* wepimg */ ATTRIB(Crylink, model2, string, "weaponcrylink");
16 /* refname */ ATTRIB(Crylink, netname, string, "crylink");
17 /* wepname */ ATTRIB(Crylink, m_name, string, _("Crylink"));
19 #define X(BEGIN, P, END, class, prefix) \
21 P(class, prefix, ammo, float, BOTH) \
22 P(class, prefix, animtime, float, BOTH) \
23 P(class, prefix, bouncedamagefactor, float, BOTH) \
24 P(class, prefix, bounces, float, BOTH) \
25 P(class, prefix, damage, float, BOTH) \
26 P(class, prefix, edgedamage, float, BOTH) \
27 P(class, prefix, force, float, BOTH) \
28 P(class, prefix, joindelay, float, BOTH) \
29 P(class, prefix, joinexplode, float, BOTH) \
30 P(class, prefix, joinexplode_damage, float, BOTH) \
31 P(class, prefix, joinexplode_edgedamage, float, BOTH) \
32 P(class, prefix, joinexplode_force, float, BOTH) \
33 P(class, prefix, joinexplode_radius, float, BOTH) \
34 P(class, prefix, joinspread, float, BOTH) \
35 P(class, prefix, linkexplode, float, BOTH) \
36 P(class, prefix, middle_fadetime, float, BOTH) \
37 P(class, prefix, middle_lifetime, float, BOTH) \
38 P(class, prefix, other_fadetime, float, BOTH) \
39 P(class, prefix, other_lifetime, float, BOTH) \
40 P(class, prefix, radius, float, BOTH) \
41 P(class, prefix, refire, float, BOTH) \
42 P(class, prefix, reload_ammo, float, NONE) \
43 P(class, prefix, reload_time, float, NONE) \
44 P(class, prefix, secondary, float, NONE) \
45 P(class, prefix, shots, float, BOTH) \
46 P(class, prefix, speed, float, BOTH) \
47 P(class, prefix, spreadtype, float, SEC) \
48 P(class, prefix, spread, float, BOTH) \
49 P(class, prefix, switchdelay_drop, float, NONE) \
50 P(class, prefix, switchdelay_raise, float, NONE) \
51 P(class, prefix, weaponreplace, string, NONE) \
52 P(class, prefix, weaponstartoverride, float, NONE) \
53 P(class, prefix, weaponstart, float, NONE) \
54 P(class, prefix, weaponthrowable, float, NONE) \
56 W_PROPS(X, Crylink, crylink)
60 REGISTER_WEAPON(CRYLINK, crylink, NEW(Crylink));
64 .float crylink_waitrelease;
65 .entity crylink_lastgroup;
67 .entity crylink_owner; // we can't use realowner, as that's subject to change
75 spawnfunc(weapon_crylink) { weapon_defaultspawnfunc(this, WEP_CRYLINK); }
77 void W_Crylink_CheckLinks(entity e)
83 error("W_Crylink_CheckLinks: entity is NULL");
84 if(e.classname != "spike" || wasfreed(e))
85 error(sprintf("W_Crylink_CheckLinks: entity is not a spike but a %s (freed: %d)", e.classname, wasfreed(e)));
88 for(i = 0; i < 1000; ++i)
90 if(p.queuenext.queueprev != p || p.queueprev.queuenext != p)
91 error("W_Crylink_CheckLinks: queue is inconsistent");
97 error("W_Crylink_CheckLinks: infinite chain");
100 void W_Crylink_Dequeue_Raw(entity own, entity prev, entity me, entity next)
102 W_Crylink_CheckLinks(next);
103 .entity weaponentity = me.weaponentity_fld;
104 if(me == own.(weaponentity).crylink_lastgroup)
105 own.(weaponentity).crylink_lastgroup = ((me == next) ? NULL : next);
106 prev.queuenext = next;
107 next.queueprev = prev;
108 me.classname = "spike_oktoremove";
110 W_Crylink_CheckLinks(next);
113 void W_Crylink_Dequeue(entity e)
115 W_Crylink_Dequeue_Raw(e.crylink_owner, e.queueprev, e, e.queuenext);
118 void W_Crylink_Reset(entity this)
120 W_Crylink_Dequeue(this);
124 // force projectile to explode
125 void W_Crylink_LinkExplode(entity e, entity e2, entity directhitentity)
132 a = bound(0, 1 - (time - e.fade_time) * e.fade_rate, 1);
134 .entity weaponentity = e.weaponentity_fld;
135 if(e == e.crylink_owner.(weaponentity).crylink_lastgroup)
136 e.crylink_owner.(weaponentity).crylink_lastgroup = NULL;
138 float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY);
140 RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius),
141 NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * a, e.projectiledeathtype, directhitentity);
143 W_Crylink_LinkExplode(e.queuenext, e2, directhitentity);
145 e.classname = "spike_oktoremove";
149 // adjust towards center
150 // returns the origin where they will meet... and the time till the meeting is
151 // stored in w_crylink_linkjoin_time.
152 // could possibly network this origin and time, and display a special particle
153 // effect when projectiles meet there :P
154 // jspeed: joining speed (calculate this as join spread * initial speed)
155 float w_crylink_linkjoin_time;
156 vector W_Crylink_LinkJoin(entity e, float jspeed)
158 vector avg_origin, avg_velocity;
163 // FIXME remove this debug code
164 W_Crylink_CheckLinks(e);
166 w_crylink_linkjoin_time = 0;
168 avg_origin = e.origin;
169 avg_velocity = e.velocity;
171 for(p = e; (p = p.queuenext) != e; )
173 avg_origin += WarpZone_RefSys_TransformOrigin(p, e, p.origin);
174 avg_velocity += WarpZone_RefSys_TransformVelocity(p, e, p.velocity);
177 avg_origin *= (1.0 / n);
178 avg_velocity *= (1.0 / n);
181 return avg_origin; // nothing to do
183 // yes, mathematically we can do this in ONE step, but beware of 32bit floats...
184 avg_dist = (vlen(e.origin - avg_origin) ** 2);
185 for(p = e; (p = p.queuenext) != e; )
186 avg_dist += (vlen(WarpZone_RefSys_TransformOrigin(p, e, p.origin) - avg_origin) ** 2);
187 avg_dist *= (1.0 / n);
188 avg_dist = sqrt(avg_dist);
191 return avg_origin; // no change needed
195 e.velocity = avg_velocity;
196 UpdateCSQCProjectile(e);
197 for(p = e; (p = p.queuenext) != e; )
199 p.velocity = WarpZone_RefSys_TransformVelocity(e, p, avg_velocity);
200 UpdateCSQCProjectile(p);
202 targ_origin = avg_origin + 1000000000 * normalize(avg_velocity); // HUUUUUUGE
206 w_crylink_linkjoin_time = avg_dist / jspeed;
207 targ_origin = avg_origin + w_crylink_linkjoin_time * avg_velocity;
209 e.velocity = (targ_origin - e.origin) * (1.0 / w_crylink_linkjoin_time);
210 UpdateCSQCProjectile(e);
211 for(p = e; (p = p.queuenext) != e; )
213 p.velocity = WarpZone_RefSys_TransformVelocity(e, p, (targ_origin - WarpZone_RefSys_TransformOrigin(p, e, p.origin)) * (1.0 / w_crylink_linkjoin_time));
214 UpdateCSQCProjectile(p);
218 // jspeed -> +infinity:
219 // w_crylink_linkjoin_time -> +0
220 // targ_origin -> avg_origin
221 // p->velocity -> HUEG towards center
223 // w_crylink_linkjoin_time -> +/- infinity
224 // targ_origin -> avg_velocity * +/- infinity
225 // p->velocity -> avg_velocity
226 // jspeed -> -infinity:
227 // w_crylink_linkjoin_time -> -0
228 // targ_origin -> avg_origin
229 // p->velocity -> HUEG away from center
232 W_Crylink_CheckLinks(e);
237 void W_Crylink_LinkJoinEffect_Think(entity this)
239 // is there at least 2 projectiles very close?
242 .entity weaponentity = this.weaponentity_fld;
243 e = this.owner.(weaponentity).crylink_lastgroup;
247 if(vlen2(e.origin - this.origin) < vlen2(e.velocity) * frametime)
249 for(p = e; (p = p.queuenext) != e; )
251 if(vlen2(p.origin - this.origin) < vlen2(p.velocity) * frametime)
256 float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY);
258 if(WEP_CVAR_BOTH(crylink, isprimary, joinexplode))
260 n /= WEP_CVAR_BOTH(crylink, isprimary, shots);
264 WEP_CVAR_BOTH(crylink, isprimary, joinexplode_damage) * n,
265 WEP_CVAR_BOTH(crylink, isprimary, joinexplode_edgedamage) * n,
266 WEP_CVAR_BOTH(crylink, isprimary, joinexplode_radius) * n,
269 WEP_CVAR_BOTH(crylink, isprimary, joinexplode_force) * n,
270 e.projectiledeathtype,
273 Send_Effect(EFFECT_CRYLINK_JOINEXPLODE, this.origin, '0 0 0', n);
280 float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad)
282 entity head = WarpZone_FindRadius((projectile.origin + (projectile.mins + projectile.maxs) * 0.5), rad + MAX_DAMAGEEXTRARADIUS, false);
283 float hit_friendly = 0;
288 if((head.takedamage != DAMAGE_NO) && (!IS_DEAD(head)))
290 if(SAME_TEAM(head, projectile.realowner))
299 return (hit_enemy ? false : hit_friendly);
302 // NO bounce protection, as bounces are limited!
303 void W_Crylink_Touch(entity this, entity toucher)
307 float isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY);
308 PROJECTILE_TOUCH(this, toucher);
311 a = bound(0, 1 - (time - this.fade_time) * this.fade_rate, 1);
313 finalhit = ((this.cnt <= 0) || (toucher.takedamage != DAMAGE_NO));
317 f = WEP_CVAR_BOTH(crylink, isprimary, bouncedamagefactor);
321 float totaldamage = RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * f, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * f, WEP_CVAR_BOTH(crylink, isprimary, radius), NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * f, this.projectiledeathtype, toucher);
323 if(totaldamage && ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 2) || ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 1) && !W_Crylink_Touch_WouldHitFriendly(this, WEP_CVAR_BOTH(crylink, isprimary, radius)))))
325 .entity weaponentity = this.weaponentity_fld;
326 if(this == this.crylink_owner.(weaponentity).crylink_lastgroup)
327 this.crylink_owner.(weaponentity).crylink_lastgroup = NULL;
328 W_Crylink_LinkExplode(this.queuenext, this, toucher);
329 this.classname = "spike_oktoremove";
336 W_Crylink_Dequeue(this);
340 this.cnt = this.cnt - 1;
341 this.angles = vectoangles(this.velocity);
343 this.projectiledeathtype |= HITTYPE_BOUNCE;
344 // commented out as it causes a little hitch...
346 // CSQCProjectile(proj, true, PROJECTILE_CRYLINK, true);
349 void W_Crylink_Fadethink(entity this)
351 W_Crylink_Dequeue(this);
355 void W_Crylink_Attack(Weapon thiswep, entity actor, .entity weaponentity)
357 float counter, shots;
358 entity proj, prevproj, firstproj;
360 vector forward, right, up;
363 W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(crylink, ammo), weaponentity);
365 maxdmg = WEP_CVAR_PRI(crylink, damage) * WEP_CVAR_PRI(crylink, shots);
366 maxdmg *= 1 + WEP_CVAR_PRI(crylink, bouncedamagefactor) * WEP_CVAR_PRI(crylink, bounces);
367 if(WEP_CVAR_PRI(crylink, joinexplode))
368 maxdmg += WEP_CVAR_PRI(crylink, joinexplode_damage);
370 W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, maxdmg);
375 shots = WEP_CVAR_PRI(crylink, shots);
376 Send_Effect(EFFECT_CRYLINK_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, shots);
377 proj = prevproj = firstproj = NULL;
378 for(counter = 0; counter < shots; ++counter)
381 proj.reset = W_Crylink_Reset;
382 proj.realowner = proj.owner = actor;
383 proj.crylink_owner = actor;
384 proj.weaponentity_fld = weaponentity;
385 proj.bot_dodge = true;
386 proj.bot_dodgerating = WEP_CVAR_PRI(crylink, damage);
388 proj.queuenext = proj;
389 proj.queueprev = proj;
391 else if(counter == 0) { // first projectile, store in firstproj for now
394 else if(counter == shots - 1) { // last projectile, link up with first projectile
395 prevproj.queuenext = proj;
396 firstproj.queueprev = proj;
397 proj.queuenext = firstproj;
398 proj.queueprev = prevproj;
400 else { // else link up with previous projectile
401 prevproj.queuenext = proj;
402 proj.queueprev = prevproj;
407 set_movetype(proj, MOVETYPE_BOUNCEMISSILE);
408 PROJECTILE_MAKETRIGGER(proj);
409 proj.projectiledeathtype = WEP_CRYLINK.m_id;
410 //proj.gravity = 0.001;
412 setorigin(proj, w_shotorg);
413 setsize(proj, '0 0 0', '0 0 0');
421 makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
425 s = s * WEP_CVAR_PRI(crylink, spread) * g_weaponspreadfactor;
426 W_SetupProjVelocity_Explicit(proj, w_shotdir + right * s.y + up * s.z, v_up, WEP_CVAR_PRI(crylink, speed), 0, 0, 0, false);
427 settouch(proj, W_Crylink_Touch);
429 setthink(proj, W_Crylink_Fadethink);
432 proj.fade_time = time + WEP_CVAR_PRI(crylink, middle_lifetime);
433 proj.fade_rate = 1 / WEP_CVAR_PRI(crylink, middle_fadetime);
434 proj.nextthink = time + WEP_CVAR_PRI(crylink, middle_lifetime) + WEP_CVAR_PRI(crylink, middle_fadetime);
438 proj.fade_time = time + WEP_CVAR_PRI(crylink, other_lifetime);
439 proj.fade_rate = 1 / WEP_CVAR_PRI(crylink, other_fadetime);
440 proj.nextthink = time + WEP_CVAR_PRI(crylink, other_lifetime) + WEP_CVAR_PRI(crylink, other_fadetime);
442 proj.teleport_time = time + WEP_CVAR_PRI(crylink, joindelay);
443 proj.cnt = WEP_CVAR_PRI(crylink, bounces);
444 //proj.scale = 1 + 1 * proj.cnt;
446 proj.angles = vectoangles(proj.velocity);
448 //proj.glow_size = 20;
450 proj.flags = FL_PROJECTILE;
451 IL_PUSH(g_projectiles, proj);
452 IL_PUSH(g_bot_dodge, proj);
453 proj.missile_flags = MIF_SPLASH;
455 CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true);
457 MUTATOR_CALLHOOK(EditProjectile, actor, proj);
459 if(WEP_CVAR_PRI(crylink, joinspread) != 0)
461 actor.(weaponentity).crylink_lastgroup = proj;
462 W_Crylink_CheckLinks(proj);
463 actor.(weaponentity).crylink_waitrelease = 1;
467 void W_Crylink_Attack2(Weapon thiswep, entity actor, .entity weaponentity)
469 float counter, shots;
470 entity proj, prevproj, firstproj;
472 vector forward, right, up;
475 W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(crylink, ammo), weaponentity);
477 maxdmg = WEP_CVAR_SEC(crylink, damage) * WEP_CVAR_SEC(crylink, shots);
478 maxdmg *= 1 + WEP_CVAR_SEC(crylink, bouncedamagefactor) * WEP_CVAR_SEC(crylink, bounces);
479 if(WEP_CVAR_SEC(crylink, joinexplode))
480 maxdmg += WEP_CVAR_SEC(crylink, joinexplode_damage);
482 W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE2, CH_WEAPON_A, maxdmg);
487 shots = WEP_CVAR_SEC(crylink, shots);
488 Send_Effect(EFFECT_CRYLINK_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, shots);
489 proj = prevproj = firstproj = NULL;
490 for(counter = 0; counter < shots; ++counter)
493 proj.weaponentity_fld = weaponentity;
494 proj.reset = W_Crylink_Reset;
495 proj.realowner = proj.owner = actor;
496 proj.crylink_owner = actor;
497 proj.bot_dodge = true;
498 proj.bot_dodgerating = WEP_CVAR_SEC(crylink, damage);
500 proj.queuenext = proj;
501 proj.queueprev = proj;
503 else if(counter == 0) { // first projectile, store in firstproj for now
506 else if(counter == shots - 1) { // last projectile, link up with first projectile
507 prevproj.queuenext = proj;
508 firstproj.queueprev = proj;
509 proj.queuenext = firstproj;
510 proj.queueprev = prevproj;
512 else { // else link up with previous projectile
513 prevproj.queuenext = proj;
514 proj.queueprev = prevproj;
519 set_movetype(proj, MOVETYPE_BOUNCEMISSILE);
520 PROJECTILE_MAKETRIGGER(proj);
521 proj.projectiledeathtype = WEP_CRYLINK.m_id | HITTYPE_SECONDARY;
522 //proj.gravity = 0.001;
524 setorigin(proj, w_shotorg);
525 setsize(proj, '0 0 0', '0 0 0');
527 if(WEP_CVAR_SEC(crylink, spreadtype) == 1)
534 makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
538 s = s * WEP_CVAR_SEC(crylink, spread) * g_weaponspreadfactor;
539 s = w_shotdir + right * s.y + up * s.z;
543 s = (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * WEP_CVAR_SEC(crylink, spread) * g_weaponspreadfactor);
546 W_SetupProjVelocity_Explicit(proj, s, v_up, WEP_CVAR_SEC(crylink, speed), 0, 0, 0, false);
547 settouch(proj, W_Crylink_Touch);
548 setthink(proj, W_Crylink_Fadethink);
549 if(counter == (shots - 1) / 2)
551 proj.fade_time = time + WEP_CVAR_SEC(crylink, middle_lifetime);
552 proj.fade_rate = 1 / WEP_CVAR_SEC(crylink, middle_fadetime);
553 proj.nextthink = time + WEP_CVAR_SEC(crylink, middle_lifetime) + WEP_CVAR_SEC(crylink, middle_fadetime);
557 proj.fade_time = time + WEP_CVAR_SEC(crylink, other_lifetime);
558 proj.fade_rate = 1 / WEP_CVAR_SEC(crylink, other_fadetime);
559 proj.nextthink = time + WEP_CVAR_SEC(crylink, other_lifetime) + WEP_CVAR_SEC(crylink, other_fadetime);
561 proj.teleport_time = time + WEP_CVAR_SEC(crylink, joindelay);
562 proj.cnt = WEP_CVAR_SEC(crylink, bounces);
563 //proj.scale = 1 + 1 * proj.cnt;
565 proj.angles = vectoangles(proj.velocity);
567 //proj.glow_size = 20;
569 proj.flags = FL_PROJECTILE;
570 IL_PUSH(g_projectiles, proj);
571 IL_PUSH(g_bot_dodge, proj);
572 proj.missile_flags = MIF_SPLASH;
574 CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true);
576 MUTATOR_CALLHOOK(EditProjectile, actor, proj);
578 if(WEP_CVAR_SEC(crylink, joinspread) != 0)
580 actor.(weaponentity).crylink_lastgroup = proj;
581 W_Crylink_CheckLinks(proj);
582 actor.(weaponentity).crylink_waitrelease = 2;
586 METHOD(Crylink, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
589 PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(crylink, speed), 0, WEP_CVAR_PRI(crylink, middle_lifetime), false);
591 PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_SEC(crylink, speed), 0, WEP_CVAR_SEC(crylink, middle_lifetime), false);
593 METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
595 if(autocvar_g_balance_crylink_reload_ammo && actor.(weaponentity).clip_load < min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo))) { // forced reload
596 thiswep.wr_reload(thiswep, actor, weaponentity);
601 if(actor.(weaponentity).crylink_waitrelease != 1)
602 if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(crylink, refire)))
604 W_Crylink_Attack(thiswep, actor, weaponentity);
605 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready);
609 if((fire & 2) && autocvar_g_balance_crylink_secondary)
611 if(actor.(weaponentity).crylink_waitrelease != 2)
612 if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(crylink, refire)))
614 W_Crylink_Attack2(thiswep, actor, weaponentity);
615 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready);
619 if((actor.(weaponentity).crylink_waitrelease == 1 && !(fire & 1)) || (actor.(weaponentity).crylink_waitrelease == 2 && !(fire & 2)))
621 if(!actor.(weaponentity).crylink_lastgroup || time > actor.(weaponentity).crylink_lastgroup.teleport_time)
623 // fired and released now!
624 if(actor.(weaponentity).crylink_lastgroup)
627 entity linkjoineffect;
628 float isprimary = (actor.(weaponentity).crylink_waitrelease == 1);
630 pos = W_Crylink_LinkJoin(actor.(weaponentity).crylink_lastgroup, WEP_CVAR_BOTH(crylink, isprimary, joinspread) * WEP_CVAR_BOTH(crylink, isprimary, speed));
632 linkjoineffect = new(linkjoineffect);
633 linkjoineffect.weaponentity_fld = weaponentity;
634 setthink(linkjoineffect, W_Crylink_LinkJoinEffect_Think);
635 linkjoineffect.nextthink = time + w_crylink_linkjoin_time;
636 linkjoineffect.owner = actor;
637 setorigin(linkjoineffect, pos);
639 actor.(weaponentity).crylink_waitrelease = 0;
640 if(!thiswep.wr_checkammo1(thiswep, actor, weaponentity) && !thiswep.wr_checkammo2(thiswep, actor, weaponentity))
641 if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
644 actor.cnt = WEP_CRYLINK.m_id;
645 actor.(weaponentity).m_switchweapon = w_getbestweapon(actor, weaponentity);
650 METHOD(Crylink, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
652 // don't "run out of ammo" and switch weapons while waiting for release
653 if(actor.(weaponentity).crylink_lastgroup && actor.(weaponentity).crylink_waitrelease)
656 float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(crylink, ammo);
657 ammo_amount += actor.(weaponentity).(weapon_load[WEP_CRYLINK.m_id]) >= WEP_CVAR_PRI(crylink, ammo);
660 METHOD(Crylink, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
662 // don't "run out of ammo" and switch weapons while waiting for release
663 if(actor.(weaponentity).crylink_lastgroup && actor.(weaponentity).crylink_waitrelease)
666 float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(crylink, ammo);
667 ammo_amount += actor.(weaponentity).(weapon_load[WEP_CRYLINK.m_id]) >= WEP_CVAR_SEC(crylink, ammo);
670 METHOD(Crylink, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
672 W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), SND_RELOAD);
674 METHOD(Crylink, wr_suicidemessage, Notification(entity thiswep))
676 return WEAPON_CRYLINK_SUICIDE;
678 METHOD(Crylink, wr_killmessage, Notification(entity thiswep))
680 return WEAPON_CRYLINK_MURDER;
684 METHOD(Crylink, wr_impacteffect, void(entity thiswep, entity actor))
687 org2 = w_org + w_backoff * 2;
688 if(w_deathtype & HITTYPE_SECONDARY)
690 pointparticles(EFFECT_CRYLINK_IMPACT2, org2, '0 0 0', 1);
692 sound(actor, CH_SHOTS, SND_CRYLINK_IMPACT2, VOL_BASE, ATTN_NORM);
696 pointparticles(EFFECT_CRYLINK_IMPACT, org2, '0 0 0', 1);
698 sound(actor, CH_SHOTS, SND_CRYLINK_IMPACT, VOL_BASE, ATTN_NORM);