]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/w_arc.qc
Merge branch 'master' into samual/weapons
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / w_arc.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(
3 /* WEP_##id  */ ARC,
4 /* function  */ W_Arc,
5 /* ammotype  */ ammo_cells,
6 /* impulse   */ 3,
7 /* flags     */ WEP_FLAG_NORMAL,
8 /* rating    */ BOT_PICKUP_RATING_HIGH,
9 /* color     */ '1 1 1',
10 /* modelname */ "hlac",
11 /* simplemdl */ "foobar",
12 /* crosshair */ "gfx/crosshairhlac 0.7",
13 /* wepimg    */ "weaponhlac",
14 /* refname   */ "arc",
15 /* wepname   */ _("Arc")
16 );
17
18 #define ARC_SETTINGS(w_cvar,w_prop) ARC_SETTINGS_LIST(w_cvar, w_prop, ARC, arc)
19 #define ARC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
20         w_cvar(id, sn, NONE, beam_ammo) \
21         w_cvar(id, sn, NONE, beam_animtime) \
22         w_cvar(id, sn, NONE, beam_botaimspeed) \
23         w_cvar(id, sn, NONE, beam_botaimlifetime) \
24         w_cvar(id, sn, NONE, beam_damage) \
25         w_cvar(id, sn, NONE, beam_degreespersegment) \
26         w_cvar(id, sn, NONE, beam_distancepersegment) \
27         w_cvar(id, sn, NONE, beam_falloff_halflifedist) \
28         w_cvar(id, sn, NONE, beam_falloff_maxdist) \
29         w_cvar(id, sn, NONE, beam_falloff_mindist) \
30         w_cvar(id, sn, NONE, beam_force) \
31         w_cvar(id, sn, NONE, beam_healing_amax) \
32         w_cvar(id, sn, NONE, beam_healing_aps) \
33         w_cvar(id, sn, NONE, beam_healing_hmax) \
34         w_cvar(id, sn, NONE, beam_healing_hps) \
35         w_cvar(id, sn, NONE, beam_maxangle) \
36         w_cvar(id, sn, NONE, beam_nonplayerdamage) \
37         w_cvar(id, sn, NONE, beam_range) \
38         w_cvar(id, sn, NONE, beam_refire) \
39         w_cvar(id, sn, NONE, beam_returnspeed) \
40         w_cvar(id, sn, NONE, beam_tightness) \
41         w_cvar(id, sn, NONE, burst_ammo) \
42         w_cvar(id, sn, NONE, burst_damage) \
43         w_cvar(id, sn, NONE, burst_healing_aps) \
44         w_cvar(id, sn, NONE, burst_healing_hps) \
45         w_prop(id, sn, float,  switchdelay_raise, switchdelay_raise) \
46         w_prop(id, sn, float,  switchdelay_drop, switchdelay_drop) \
47         w_prop(id, sn, string, weaponreplace, weaponreplace) \
48         w_prop(id, sn, float,  weaponstart, weaponstart) \
49         w_prop(id, sn, float,  weaponstartoverride, weaponstartoverride) \
50         w_prop(id, sn, float,  weaponthrowable, weaponthrowable)
51
52 #ifndef MENUQC
53 #define ARC_MAX_SEGMENTS 20
54 vector arc_shotorigin[4];
55 .vector beam_start;
56 .vector beam_dir;
57 .vector beam_wantdir;
58 .float beam_type;
59
60 #define ARC_BT_MISS        0
61 #define ARC_BT_WALL        1
62 #define ARC_BT_HEAL        2
63 #define ARC_BT_HIT         3
64 #define ARC_BT_BURST_MISS  10
65 #define ARC_BT_BURST_WALL  11
66 #define ARC_BT_BURST_HEAL  12
67 #define ARC_BT_BURST_HIT   13
68 #define ARC_BT_BURSTMASK   10
69
70 #define ARC_SF_SETTINGS    1
71 #define ARC_SF_START       2
72 #define ARC_SF_WANTDIR     4
73 #define ARC_SF_BEAMDIR     8
74 #define ARC_SF_BEAMTYPE    16
75 #define ARC_SF_LOCALMASK   14
76 #endif
77 #ifdef SVQC
78 ARC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
79 .entity arc_beam;
80 .float BUTTON_ATCK_prev; // for better animation control
81 .float beam_prev;
82 .float beam_initialized;
83 .float beam_bursting;
84 #endif
85 #ifdef CSQC
86 void Ent_ReadArcBeam(float isnew);
87
88 .vector beam_color;
89 .float beam_alpha;
90 .float beam_thickness;
91 .float beam_traileffect;
92 .float beam_hiteffect;
93 .float beam_hitlight[4]; // 0: radius, 123: rgb
94 .float beam_muzzleeffect;
95 .float beam_muzzlelight[4]; // 0: radius, 123: rgb
96 .string beam_image;
97
98 .entity beam_muzzleentity;
99
100 .float beam_degreespersegment;
101 .float beam_distancepersegment;
102 .float beam_usevieworigin;
103 .float beam_initialized;
104 .float beam_maxangle;
105 .float beam_range;
106 .float beam_returnspeed;
107 .float beam_tightness;
108 .vector beam_shotorigin;
109
110 entity Draw_ArcBeam_callback_entity;
111 float Draw_ArcBeam_callback_last_thickness;
112 vector Draw_ArcBeam_callback_last_top; // NOTE: in same coordinate system as player.
113 vector Draw_ArcBeam_callback_last_bottom; // NOTE: in same coordinate system as player.
114 #endif
115 #else
116 #ifdef SVQC
117 void spawnfunc_weapon_arc(void) { weapon_defaultspawnfunc(WEP_ARC); }
118
119 float W_Arc_Beam_Send(entity to, float sf)
120 {
121         WriteByte(MSG_ENTITY, ENT_CLIENT_ARC_BEAM);
122
123         // Truncate information when this beam is displayed to the owner client
124         // - The owner client has no use for beam start position or directions,
125         //    it always figures this information out for itself with csqc code.
126         // - Spectating the owner also truncates this information.
127         float drawlocal = ((to == self.owner) || ((to.enemy == self.owner) && IS_SPEC(to)));
128         if(drawlocal) { sf &= ~ARC_SF_LOCALMASK; }
129
130         WriteByte(MSG_ENTITY, sf);
131
132         if(sf & ARC_SF_SETTINGS) // settings information
133         {
134                 WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_degreespersegment));
135                 WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_distancepersegment));
136                 WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_maxangle));
137                 WriteCoord(MSG_ENTITY, WEP_CVAR(arc, beam_range));
138                 WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_returnspeed));
139                 WriteByte(MSG_ENTITY, WEP_CVAR(arc, beam_tightness) * 10);
140
141                 WriteByte(MSG_ENTITY, drawlocal);
142         }
143         if(sf & ARC_SF_START) // starting location
144         {
145                 WriteCoord(MSG_ENTITY, self.beam_start_x);
146                 WriteCoord(MSG_ENTITY, self.beam_start_y);
147                 WriteCoord(MSG_ENTITY, self.beam_start_z);
148         }
149         if(sf & ARC_SF_WANTDIR) // want/aim direction
150         {
151                 WriteCoord(MSG_ENTITY, self.beam_wantdir_x);
152                 WriteCoord(MSG_ENTITY, self.beam_wantdir_y);
153                 WriteCoord(MSG_ENTITY, self.beam_wantdir_z);
154         }
155         if(sf & ARC_SF_BEAMDIR) // beam direction
156         {
157                 WriteCoord(MSG_ENTITY, self.beam_dir_x);
158                 WriteCoord(MSG_ENTITY, self.beam_dir_y);
159                 WriteCoord(MSG_ENTITY, self.beam_dir_z);
160         }
161         if(sf & ARC_SF_BEAMTYPE) // beam type
162         {
163                 WriteByte(MSG_ENTITY, self.beam_type);
164         }
165
166         return TRUE;
167 }
168
169 void W_Arc_Beam_Think(void)
170 {
171         if(self != self.owner.arc_beam)
172         {
173                 remove(self);
174                 return;
175         }
176
177         if(
178                 (self.owner.WEP_AMMO(ARC) <= 0 && !(self.owner.items & IT_UNLIMITED_WEAPON_AMMO))
179                 ||
180                 self.owner.deadflag != DEAD_NO
181                 ||
182                 (!self.owner.BUTTON_ATCK && !self.beam_bursting)
183                 ||
184                 self.owner.freezetag_frozen
185         )
186         {
187                 if(self == self.owner.arc_beam) { self.owner.arc_beam = world; }
188                 entity oldself = self;
189                 self = self.owner;
190                 if(!WEP_ACTION(WEP_ARC, WR_CHECKAMMO1) && !WEP_ACTION(WEP_ARC, WR_CHECKAMMO2))
191                 {
192                         // note: this doesn't force the switch
193                         W_SwitchToOtherWeapon(self);
194                 }
195                 self = oldself;
196                 remove(self);
197                 return;
198         }
199
200         float burst = 0;
201         if(self.owner.BUTTON_ATCK2 || self.beam_bursting)
202         {
203                 if(!self.beam_bursting)
204                         self.beam_bursting = TRUE;
205                 burst = ARC_BT_BURSTMASK;
206         }
207
208         // decrease ammo
209         float coefficient = frametime;
210         if(!(self.owner.items & IT_UNLIMITED_WEAPON_AMMO))
211         {
212                 float rootammo;
213                 if(burst)
214                         { rootammo = WEP_CVAR(arc, burst_ammo); }
215                 else
216                         { rootammo = WEP_CVAR(arc, beam_ammo); }
217
218                 if(rootammo)
219                 {
220                         coefficient = min(coefficient, self.owner.WEP_AMMO(ARC) / rootammo);
221                         self.owner.WEP_AMMO(ARC) = max(0, self.owner.WEP_AMMO(ARC) - (rootammo * frametime));
222                 }
223         }
224
225         makevectors(self.owner.v_angle);
226
227         W_SetupShot_Range(
228                 self.owner,
229                 TRUE,
230                 0,
231                 "",
232                 0,
233                 WEP_CVAR(arc, beam_damage) * coefficient,
234                 WEP_CVAR(arc, beam_range)
235         );
236
237         // network information: shot origin and want/aim direction
238         if(self.beam_start != w_shotorg)
239         {
240                 self.SendFlags |= ARC_SF_START;
241                 self.beam_start = w_shotorg;
242         }
243         if(self.beam_wantdir != w_shotdir)
244         {
245                 self.SendFlags |= ARC_SF_WANTDIR;
246                 self.beam_wantdir = w_shotdir;
247         }
248
249         if(!self.beam_initialized)
250         {
251                 self.beam_dir = w_shotdir;
252                 self.beam_initialized = TRUE;
253         }
254
255         // WEAPONTODO: Detect player velocity so that the beam curves when moving too
256         // idea: blend together self.beam_dir with the inverted direction the player is moving in
257         // might have to make some special accomodation so that it only uses view_right and view_up
258
259         // note that if we do this, it'll always be corrected to a maximum angle by beam_maxangle handling
260
261         float segments; 
262         if(self.beam_dir != w_shotdir)
263         {
264                 // calculate how much we're going to move the end of the beam to the want position
265                 // WEAPONTODO (server and client):
266                 // blendfactor never actually becomes 0 in this situation, which is a problem
267                 // regarding precision... this means that self.beam_dir and w_shotdir approach
268                 // eachother, however they never actually become the same value with this method.
269                 // Perhaps we should do some form of rounding/snapping?
270                 float angle = vlen(w_shotdir - self.beam_dir) * RAD2DEG;
271                 if(angle && (angle > WEP_CVAR(arc, beam_maxangle)))
272                 {
273                         // if the angle is greater than maxangle, force the blendfactor to make this the maximum factor
274                         float blendfactor = bound(
275                                 0,
276                                 (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)),
277                                 min(WEP_CVAR(arc, beam_maxangle) / angle, 1)
278                         );
279                         self.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
280                 }
281                 else
282                 {
283                         // the radius is not too far yet, no worries :D
284                         float blendfactor = bound(
285                                 0,
286                                 (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)),
287                                 1
288                         );
289                         self.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
290                 }
291
292                 // network information: beam direction
293                 self.SendFlags |= ARC_SF_BEAMDIR;
294
295                 // calculate how many segments are needed
296                 float max_allowed_segments;
297
298                 if(WEP_CVAR(arc, beam_distancepersegment))
299                 {
300                         max_allowed_segments = min(
301                                 ARC_MAX_SEGMENTS,
302                                 1 + (vlen(w_shotdir / WEP_CVAR(arc, beam_distancepersegment)))
303                         );
304                 }
305                 else { max_allowed_segments = ARC_MAX_SEGMENTS; }
306
307                 if(WEP_CVAR(arc, beam_degreespersegment))
308                 {
309                         segments = bound(
310                                 1, 
311                                 (
312                                         min(
313                                                 angle,
314                                                 WEP_CVAR(arc, beam_maxangle)
315                                         )
316                                         /
317                                         WEP_CVAR(arc, beam_degreespersegment)
318                                 ),
319                                 max_allowed_segments
320                         );
321                 }
322                 else { segments = 1; }
323         }
324         else { segments = 1; }
325
326         vector beam_endpos = (w_shotorg + (self.beam_dir * WEP_CVAR(arc, beam_range)));
327         vector beam_controlpoint = w_shotorg + w_shotdir * (WEP_CVAR(arc, beam_range) * (1 - WEP_CVAR(arc, beam_tightness)));
328
329         float i;
330         float new_beam_type = 0;
331         vector last_origin = w_shotorg;
332         for(i = 1; i <= segments; ++i)
333         {
334                 // WEAPONTODO (client):
335                 // In order to do nice fading and pointing on the starting segment, we must always
336                 // have that drawn as a separate triangle... However, that is difficult to do when
337                 // keeping in mind the above problems and also optimizing the amount of segments
338                 // drawn on screen at any given time. (Automatic beam quality scaling, essentially)
339
340                 vector new_origin = bezier_quadratic_getpoint(
341                         w_shotorg,
342                         beam_controlpoint,
343                         beam_endpos,
344                         i / segments);
345                 vector new_dir = normalize(new_origin - last_origin);
346
347                 WarpZone_traceline_antilag(
348                         self.owner,
349                         last_origin,
350                         new_origin,
351                         MOVE_NORMAL,
352                         self.owner,
353                         ANTILAG_LATENCY(self.owner)
354                 );
355
356                 // Do all the transforms for warpzones right now, as we already
357                 // "are" in the post-trace system (if we hit a player, that's
358                 // always BEHIND the last passed wz).
359                 last_origin = trace_endpos;
360                 w_shotorg = WarpZone_TransformOrigin(WarpZone_trace_transform, w_shotorg);
361                 beam_controlpoint = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_controlpoint);
362                 beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos);
363                 new_dir = WarpZone_TransformVelocity(WarpZone_trace_transform, new_dir);
364
365                 float is_player = (
366                         trace_ent.classname == "player"
367                         ||
368                         trace_ent.classname == "body"
369                         ||
370                         (trace_ent.flags & FL_MONSTER)
371                 );
372
373                 if(trace_ent && trace_ent.takedamage && (is_player || WEP_CVAR(arc, beam_nonplayerdamage)))
374                 {
375                         // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
376                         // NO. trace_endpos should be just fine. If not,
377                         // that's an engine bug that needs proper debugging.
378                         vector hitorigin = trace_endpos;
379
380                         float falloff = ExponentialFalloff(
381                                 WEP_CVAR(arc, beam_falloff_mindist),
382                                 WEP_CVAR(arc, beam_falloff_maxdist),
383                                 WEP_CVAR(arc, beam_falloff_halflifedist),
384                                 vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg)
385                         );
386
387                         if(is_player && SAME_TEAM(self.owner, trace_ent))
388                         {
389                                 float roothealth, rootarmor;
390                                 if(burst)
391                                 {
392                                         roothealth = WEP_CVAR(arc, burst_healing_hps);
393                                         rootarmor = WEP_CVAR(arc, burst_healing_aps);
394                                 }
395                                 else
396                                 {
397                                         roothealth = WEP_CVAR(arc, beam_healing_hps);
398                                         rootarmor = WEP_CVAR(arc, beam_healing_aps);
399                                 }
400
401                                 if(trace_ent.health <= WEP_CVAR(arc, beam_healing_hmax) && roothealth)
402                                 {
403                                         trace_ent.health = min(
404                                                 trace_ent.health + (roothealth * coefficient),
405                                                 WEP_CVAR(arc, beam_healing_hmax)
406                                         );
407                                 }
408                                 if(trace_ent.armorvalue <= WEP_CVAR(arc, beam_healing_amax) && rootarmor)
409                                 {
410                                         trace_ent.armorvalue = min(
411                                                 trace_ent.armorvalue + (rootarmor * coefficient),
412                                                 WEP_CVAR(arc, beam_healing_amax)
413                                         );
414                                 }
415
416                                 // stop rot, set visual effect
417                                 if(roothealth || rootarmor)
418                                 {
419                                         trace_ent.pauserothealth_finished = max(
420                                                 trace_ent.pauserothealth_finished,
421                                                 time + autocvar_g_balance_pause_health_rot
422                                         );
423                                         trace_ent.pauserotarmor_finished = max(
424                                                 trace_ent.pauserotarmor_finished,
425                                                 time + autocvar_g_balance_pause_armor_rot
426                                         );
427                                         new_beam_type = ARC_BT_HEAL;
428                                 }
429                         }
430                         else
431                         {
432                                 float rootdamage;
433                                 if(is_player)
434                                 {
435                                         if(burst)
436                                                 { rootdamage = WEP_CVAR(arc, burst_damage); }
437                                         else
438                                                 { rootdamage = WEP_CVAR(arc, beam_damage); }
439                                 }
440                                 else
441                                         { rootdamage = WEP_CVAR(arc, beam_nonplayerdamage); }
442
443                                 if(accuracy_isgooddamage(self.owner, trace_ent))
444                                 {
445                                         accuracy_add(
446                                                 self.owner,
447                                                 WEP_ARC,
448                                                 0,
449                                                 rootdamage * coefficient * falloff
450                                         );
451                                 }
452
453                                 Damage(
454                                         trace_ent,
455                                         self.owner,
456                                         self.owner,
457                                         rootdamage * coefficient * falloff,
458                                         WEP_ARC,
459                                         hitorigin,
460                                         WEP_CVAR(arc, beam_force) * new_dir * coefficient * falloff
461                                 );
462
463                                 new_beam_type = ARC_BT_HIT;
464                         }
465                         break; 
466                 }
467                 else if(trace_fraction != 1)
468                 {
469                         // we collided with geometry
470                         new_beam_type = ARC_BT_WALL;
471                         break;
472                 }
473         }
474
475         // if we're bursting, use burst visual effects
476         new_beam_type += burst;
477
478         // network information: beam type
479         if(new_beam_type != self.beam_type)
480         {
481                 self.SendFlags |= ARC_SF_BEAMTYPE;
482                 self.beam_type = new_beam_type;
483         }
484
485         self.owner.beam_prev = time;
486         self.nextthink = time;
487 }
488
489 void W_Arc_Beam(float burst)
490 {
491         // only play fire sound if 1 sec has passed since player let go the fire button
492         if(time - self.beam_prev > 1)
493         {
494                 sound(self, CH_WEAPON_A, "weapons/lgbeam_fire.wav", VOL_BASE, ATTN_NORM);
495         }
496
497         entity beam = self.arc_beam = spawn();
498         beam.classname = "W_Arc_Beam";
499         beam.solid = SOLID_NOT;
500         beam.think = W_Arc_Beam_Think;
501         beam.owner = self;
502         beam.movetype = MOVETYPE_NONE;
503         beam.bot_dodge = TRUE;
504         beam.bot_dodgerating = WEP_CVAR(arc, beam_damage);
505         beam.beam_bursting = burst;
506         Net_LinkEntity(beam, FALSE, 0, W_Arc_Beam_Send);
507
508         entity oldself = self;
509         self = beam;
510         self.think();
511         self = oldself;
512 }
513
514 float W_Arc(float req)
515 {
516         switch(req)
517         {
518                 case WR_AIM:
519                 {
520                         if(WEP_CVAR(arc, beam_botaimspeed))
521                         {
522                                 self.BUTTON_ATCK = bot_aim(
523                                         WEP_CVAR(arc, beam_botaimspeed),
524                                         0,
525                                         WEP_CVAR(arc, beam_botaimlifetime),
526                                         FALSE
527                                 );
528                         }
529                         else
530                         {
531                                 self.BUTTON_ATCK = bot_aim(
532                                         1000000,
533                                         0,
534                                         0.001,
535                                         FALSE
536                                 );
537                         }
538                         return TRUE;
539                 }
540                 case WR_THINK:
541                 {
542                         #if 0
543                         if(self.arc_beam.beam_heat > threshold)
544                         {
545                                 stop the beam somehow
546                                 play overheat animation
547                         }
548                         #endif
549
550                         if(self.BUTTON_ATCK || self.BUTTON_ATCK2 || self.arc_beam.beam_bursting)
551                         {
552                                 if(self.BUTTON_ATCK_prev)
553                                 {
554                                         #if 0
555                                         if(self.animstate_startframe == self.anim_shoot_x && self.animstate_numframes == self.anim_shoot_y)
556                                                 weapon_thinkf(WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready);
557                                         else
558                                         #endif
559                                                 weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
560                                 }
561
562                                 if((!self.arc_beam) || wasfreed(self.arc_beam))
563                                 {
564                                         if(weapon_prepareattack(!!self.BUTTON_ATCK2, 0))
565                                         {
566                                                 W_Arc_Beam(!!self.BUTTON_ATCK2);
567                                                 
568                                                 if(!self.BUTTON_ATCK_prev)
569                                                 {
570                                                         weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
571                                                         self.BUTTON_ATCK_prev = 1;
572                                                 }
573                                         }
574                                 }
575                         } 
576                         else // todo
577                         {
578                                 if(self.BUTTON_ATCK_prev != 0)
579                                 {
580                                         weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready);
581                                         ATTACK_FINISHED(self) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor();
582                                 }
583                                 self.BUTTON_ATCK_prev = 0;
584                         }
585
586                         #if 0
587                         if(self.BUTTON_ATCK2)
588                         if(weapon_prepareattack(1, autocvar_g_balance_arc_secondary_refire))
589                         {
590                                 W_Arc_Attack2();
591                                 self.arc_count = autocvar_g_balance_arc_secondary_count;
592                                 weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack);
593                                 self.arc_secondarytime = time + autocvar_g_balance_arc_secondary_refire2 * W_WeaponRateFactor();
594                         }
595                         #endif
596
597                         return TRUE;
598                 }
599                 case WR_INIT:
600                 {
601                         precache_model("models/weapons/g_arc.md3");
602                         precache_model("models/weapons/v_arc.md3");
603                         precache_model("models/weapons/h_arc.iqm");
604                         precache_sound("weapons/lgbeam_fire.wav");
605                         if(!arc_shotorigin[0])
606                         {
607                                 arc_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 1);
608                                 arc_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 2);
609                                 arc_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 3);
610                                 arc_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 4);
611                         }
612                         ARC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP)
613                         return TRUE;
614                 }
615                 case WR_CHECKAMMO1:
616                 {
617                         return ((!WEP_CVAR(arc, beam_ammo)) || (self.WEP_AMMO(ARC) > 0));
618                 }
619                 case WR_CHECKAMMO2:
620                 {
621                         return ((!WEP_CVAR(arc, burst_ammo)) || (self.WEP_AMMO(ARC) > 0));
622                 }
623                 case WR_CONFIG:
624                 {
625                         ARC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
626                         return TRUE;
627                 }
628                 case WR_KILLMESSAGE:
629                 {
630                         if(w_deathtype & HITTYPE_SECONDARY)
631                         {
632                                 return WEAPON_ELECTRO_MURDER_ORBS;
633                         }
634                         else
635                         {
636                                 if(w_deathtype & HITTYPE_BOUNCE)
637                                         return WEAPON_ELECTRO_MURDER_COMBO;
638                                 else
639                                         return WEAPON_ELECTRO_MURDER_BOLT;
640                         }
641                 }
642         }
643         return FALSE;
644 }
645 #endif
646 #ifdef CSQC
647 void Draw_ArcBeam_callback(vector start, vector hit, vector end)
648 {
649         entity beam = Draw_ArcBeam_callback_entity;
650         vector transformed_view_org;
651         transformed_view_org = WarpZone_TransformOrigin(WarpZone_trace_transform, view_origin);
652
653         // Thickdir shall be perpendicular to the beam and to the view-to-beam direction (WEAPONTODO: WHY)
654         // WEAPONTODO: Wouldn't it be better to be perpendicular to the beam and to the view FORWARD direction?
655         vector thickdir = normalize(cross(normalize(start - hit), transformed_view_org - start));
656
657         vector hitorigin;
658
659         // draw segment
660         #if 0
661         if(trace_fraction != 1)
662         {
663                 // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
664                 hitorigin = start + (Draw_ArcBeam_callback_new_dir * Draw_ArcBeam_callback_segmentdist * trace_fraction);
665                 hitorigin = WarpZone_TransformOrigin(WarpZone_trace_transform, hitorigin);
666         }
667         else
668         {
669                 hitorigin = hit;
670         }
671         #else
672         hitorigin = hit;
673         #endif
674
675         // decide upon thickness
676         float thickness = beam.beam_thickness;
677
678         // draw primary beam render
679         vector top    = hitorigin + (thickdir * thickness);
680         vector bottom = hitorigin - (thickdir * thickness);
681         
682         vector last_top = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_top);
683         vector last_bottom = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_bottom);
684
685         R_BeginPolygon(beam.beam_image, DRAWFLAG_NORMAL); // DRAWFLAG_ADDITIVE
686         R_PolygonVertex(
687                 top,
688                 '0 0.5 0' + ('0 0.5 0' * (thickness / beam.beam_thickness)),
689                 beam.beam_color,
690                 beam.beam_alpha
691         );
692         R_PolygonVertex(
693                 last_top,
694                 '0 0.5 0' + ('0 0.5 0' * (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)),
695                 beam.beam_color,
696                 beam.beam_alpha
697         );
698         R_PolygonVertex(
699                 last_bottom,
700                 '0 0.5 0' * (1 - (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)),
701                 beam.beam_color,
702                 beam.beam_alpha
703         );
704         R_PolygonVertex(
705                 bottom,
706                 '0 0.5 0' * (1 - (thickness / beam.beam_thickness)),
707                 beam.beam_color,
708                 beam.beam_alpha
709         );
710         R_EndPolygon();
711
712         // draw trailing particles
713         // NOTES:
714         //  - Don't use spammy particle counts here, use a FEW small particles around the beam
715         //  - We're not using WarpZone_TrailParticles here because we will handle warpzones ourselves.
716         if(beam.beam_traileffect)
717         {
718                 trailparticles(beam, beam.beam_traileffect, start, hitorigin);
719         }
720
721         // set up for the next 
722         Draw_ArcBeam_callback_last_thickness = thickness;
723         Draw_ArcBeam_callback_last_top = WarpZone_UnTransformOrigin(WarpZone_trace_transform, top);
724         Draw_ArcBeam_callback_last_bottom = WarpZone_UnTransformOrigin(WarpZone_trace_transform, bottom);
725 }
726
727 void Draw_ArcBeam(void)
728 {
729         if(!self.beam_usevieworigin)
730         {
731                 InterpolateOrigin_Do();
732         }
733
734         // origin = beam starting origin
735         // v_angle = wanted/aim direction
736         // angles = current direction of beam
737
738         vector start_pos;
739         vector wantdir; //= view_forward;
740         vector beamdir; //= self.beam_dir;
741
742         float segments;
743         if(self.beam_usevieworigin)
744         {
745                 // WEAPONTODO:
746                 // Currently we have to replicate nearly the same method of figuring
747                 // out the shotdir that the server does... Ideally in the future we
748                 // should be able to acquire this from a generalized function built
749                 // into a weapon system for client code. 
750
751                 // find where we are aiming
752                 makevectors(view_angles);
753
754                 // decide upon start position
755                 if(self.beam_usevieworigin == 2)
756                         { start_pos = view_origin; }
757                 else
758                         { start_pos = self.origin; }
759
760                 // trace forward with an estimation
761                 WarpZone_TraceLine(
762                         start_pos,
763                         start_pos + view_forward * self.beam_range,
764                         MOVE_NOMONSTERS,
765                         self
766                 );
767
768                 // untransform in case our trace went through a warpzone
769                 vector vf, vr, vu;
770                 vf = view_forward;
771                 vr = view_right;
772                 vu = view_up;
773                 vector end_pos = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
774                 view_forward = vf;
775                 view_right = vr;
776                 view_up = vu;
777
778                 // un-adjust trueaim if shotend is too close
779                 if(vlen(end_pos - view_origin) < g_trueaim_minrange)
780                         end_pos = view_origin + (view_forward * g_trueaim_minrange);
781
782                 // move shot origin to the actual gun muzzle origin
783                 vector origin_offset =
784                         view_forward * self.beam_shotorigin_x
785                         + view_right * -self.beam_shotorigin_y 
786                         + view_up * self.beam_shotorigin_z;
787
788                 start_pos = start_pos + origin_offset;
789
790                 // calculate the aim direction now
791                 wantdir = normalize(end_pos - start_pos);
792
793                 if(!self.beam_initialized)
794                 {
795                         self.beam_dir = wantdir;
796                         self.beam_initialized = TRUE;
797                 }
798
799                 if(self.beam_dir != wantdir)
800                 {
801                         // calculate how much we're going to move the end of the beam to the want position
802                         // WEAPONTODO (server and client):
803                         // blendfactor never actually becomes 0 in this situation, which is a problem
804                         // regarding precision... this means that self.beam_dir and w_shotdir approach
805                         // eachother, however they never actually become the same value with this method.
806                         // Perhaps we should do some form of rounding/snapping?
807                         float angle = vlen(wantdir - self.beam_dir) * RAD2DEG;
808                         if(angle && (angle > self.beam_maxangle))
809                         {
810                                 // if the angle is greater than maxangle, force the blendfactor to make this the maximum factor
811                                 float blendfactor = bound(
812                                         0,
813                                         (1 - (self.beam_returnspeed * frametime)),
814                                         min(self.beam_maxangle / angle, 1)
815                                 );
816                                 self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
817                         }
818                         else
819                         {
820                                 // the radius is not too far yet, no worries :D
821                                 float blendfactor = bound(
822                                         0,
823                                         (1 - (self.beam_returnspeed * frametime)),
824                                         1
825                                 );
826                                 self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
827                         }
828
829                         // calculate how many segments are needed
830                         float max_allowed_segments;
831
832                         if(self.beam_distancepersegment)
833                         {
834                                 max_allowed_segments = min(
835                                         ARC_MAX_SEGMENTS,
836                                         1 + (vlen(wantdir / self.beam_distancepersegment))
837                                 );
838                         }
839                         else { max_allowed_segments = ARC_MAX_SEGMENTS; }
840
841                         if(self.beam_degreespersegment)
842                         {
843                                 segments = bound(
844                                         1, 
845                                         (
846                                                 min(
847                                                         angle,
848                                                         self.beam_maxangle
849                                                 )
850                                                 /
851                                                 self.beam_degreespersegment
852                                         ),
853                                         max_allowed_segments
854                                 );
855                         }
856                         else { segments = 1; }
857                 }
858                 else { segments = 1; }
859
860                 // set the beam direction which the rest of the code will refer to
861                 beamdir = self.beam_dir;
862
863                 // finally, set self.angles to the proper direction so that muzzle attachment points in proper direction
864                 self.angles = fixedvectoangles2(view_forward, view_up);
865         }
866         else
867         {
868                 // set the values from the provided info from the networked entity
869                 start_pos = self.origin;
870                 wantdir = self.v_angle;
871                 beamdir = self.angles;
872
873                 if(beamdir != wantdir)
874                 {
875                         float angle = vlen(wantdir - beamdir) * RAD2DEG;
876
877                         // calculate how many segments are needed
878                         float max_allowed_segments;
879
880                         if(self.beam_distancepersegment)
881                         {
882                                 max_allowed_segments = min(
883                                         ARC_MAX_SEGMENTS,
884                                         1 + (vlen(wantdir / self.beam_distancepersegment))
885                                 );
886                         }
887                         else { max_allowed_segments = ARC_MAX_SEGMENTS; }
888
889                         if(self.beam_degreespersegment)
890                         {
891                                 segments = bound(
892                                         1, 
893                                         (
894                                                 min(
895                                                         angle,
896                                                         self.beam_maxangle
897                                                 )
898                                                 /
899                                                 self.beam_degreespersegment
900                                         ),
901                                         max_allowed_segments
902                                 );
903                         }
904                         else { segments = 1; }
905                 }
906                 else { segments = 1; }
907         }
908
909         setorigin(self, start_pos);
910         self.beam_muzzleentity.angles_z = random() * 360; // WEAPONTODO: use avelocity instead?
911
912         vector beam_endpos = (start_pos + (beamdir * self.beam_range));
913         vector beam_controlpoint = start_pos + wantdir * (self.beam_range * (1 - self.beam_tightness));
914
915         Draw_ArcBeam_callback_entity = self;
916         Draw_ArcBeam_callback_last_thickness = 0;
917         Draw_ArcBeam_callback_last_top = start_pos;
918         Draw_ArcBeam_callback_last_bottom = start_pos;
919
920         vector last_origin = start_pos;
921         vector original_start_pos = start_pos;
922
923         float i;
924         for(i = 1; i <= segments; ++i)
925         {
926                 // WEAPONTODO (client):
927                 // In order to do nice fading and pointing on the starting segment, we must always
928                 // have that drawn as a separate triangle... However, that is difficult to do when
929                 // keeping in mind the above problems and also optimizing the amount of segments
930                 // drawn on screen at any given time. (Automatic beam quality scaling, essentially)
931
932                 vector new_origin = bezier_quadratic_getpoint(
933                         start_pos,
934                         beam_controlpoint,
935                         beam_endpos,
936                         i / segments);
937
938                 WarpZone_TraceBox_ThroughZone(
939                         last_origin,
940                         '0 0 0',
941                         '0 0 0',
942                         new_origin,
943                         MOVE_NORMAL,
944                         world,
945                         world,
946                         Draw_ArcBeam_callback
947                 );
948
949                 // Do all the transforms for warpzones right now, as we already "are" in the post-trace
950                 // system (if we hit a player, that's always BEHIND the last passed wz).
951                 last_origin = trace_endpos;
952                 start_pos = WarpZone_TransformOrigin(WarpZone_trace_transform, start_pos);
953                 beam_controlpoint = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_controlpoint);
954                 beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos);
955                 beamdir = WarpZone_TransformVelocity(WarpZone_trace_transform, beamdir);
956                 Draw_ArcBeam_callback_last_top = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_top);
957                 Draw_ArcBeam_callback_last_bottom = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_bottom);
958
959                 if(trace_fraction < 1) { break; }
960         }
961
962         // visual effects for startpoint and endpoint
963         if(self.beam_hiteffect)
964         {
965                 pointparticles(
966                         self.beam_hiteffect,
967                         last_origin,
968                         beamdir * -1,
969                         frametime * 2
970                 );
971         }
972         if(self.beam_hitlight[0])
973         {
974                 adddynamiclight(
975                         last_origin,
976                         self.beam_hitlight[0],
977                         vec3(
978                                 self.beam_hitlight[1],
979                                 self.beam_hitlight[2],
980                                 self.beam_hitlight[3]
981                         )
982                 );
983         }
984         if(self.beam_muzzleeffect)
985         {
986                 pointparticles(
987                         self.beam_muzzleeffect,
988                         original_start_pos + wantdir * 20,
989                         wantdir * 1000,
990                         frametime * 0.1
991                 );
992         }
993         if(self.beam_muzzlelight[0])
994         {
995                 adddynamiclight(
996                         original_start_pos + wantdir * 20,
997                         self.beam_muzzlelight[0],
998                         vec3(
999                                 self.beam_muzzlelight[1],
1000                                 self.beam_muzzlelight[2],
1001                                 self.beam_muzzlelight[3]
1002                         )
1003                 );
1004         }
1005
1006         // cleanup
1007         Draw_ArcBeam_callback_entity = world;
1008         Draw_ArcBeam_callback_last_thickness = 0;
1009         Draw_ArcBeam_callback_last_top = '0 0 0';
1010         Draw_ArcBeam_callback_last_bottom = '0 0 0';
1011 }
1012
1013 void Remove_ArcBeam(void)
1014 {
1015         remove(self.beam_muzzleentity);
1016         sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
1017 }
1018
1019 void Ent_ReadArcBeam(float isnew)
1020 {
1021         float sf = ReadByte();
1022         entity flash;
1023
1024         if(isnew)
1025         {
1026                 // calculate shot origin offset from gun alignment
1027                 float gunalign = autocvar_cl_gunalign;
1028                 if(gunalign != 1 && gunalign != 2 && gunalign != 4)
1029                         gunalign = 3; // default value
1030                 --gunalign;
1031
1032                 self.beam_shotorigin = arc_shotorigin[gunalign];
1033
1034                 // set other main attributes of the beam
1035                 self.draw = Draw_ArcBeam;
1036                 self.entremove = Remove_ArcBeam;
1037                 sound(self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTEN_NORM);
1038
1039                 flash = spawn();
1040                 flash.owner = self;
1041                 flash.effects = EF_ADDITIVE | EF_FULLBRIGHT;
1042                 flash.drawmask = MASK_NORMAL;
1043                 flash.solid = SOLID_NOT;
1044                 flash.avelocity_z = 5000;
1045                 setattachment(flash, self, "");
1046                 setorigin(flash, '0 0 0');
1047
1048                 self.beam_muzzleentity = flash;
1049         }
1050         else
1051         {
1052                 flash = self.beam_muzzleentity;
1053         }
1054
1055         if(sf & ARC_SF_SETTINGS) // settings information
1056         {
1057                 self.beam_degreespersegment = ReadShort();
1058                 self.beam_distancepersegment = ReadShort();
1059                 self.beam_maxangle = ReadShort();
1060                 self.beam_range = ReadCoord();
1061                 self.beam_returnspeed = ReadShort();
1062                 self.beam_tightness = (ReadByte() / 10);
1063
1064                 if(ReadByte())
1065                 {
1066                         if(autocvar_chase_active)
1067                                 { self.beam_usevieworigin = 1; }
1068                         else // use view origin
1069                                 { self.beam_usevieworigin = 2; }
1070                 }
1071                 else
1072                 {
1073                         self.beam_usevieworigin = 0;
1074                 }
1075         }
1076
1077         if(!self.beam_usevieworigin)
1078         {
1079                 // self.iflags = IFLAG_ORIGIN | IFLAG_ANGLES | IFLAG_V_ANGLE; // why doesn't this work?
1080                 self.iflags = IFLAG_ORIGIN;
1081
1082                 InterpolateOrigin_Undo();
1083         }
1084
1085         if(sf & ARC_SF_START) // starting location
1086         {
1087                 self.origin_x = ReadCoord();
1088                 self.origin_y = ReadCoord();
1089                 self.origin_z = ReadCoord();
1090         }
1091         else if(self.beam_usevieworigin) // infer the location from player location
1092         {
1093                 if(self.beam_usevieworigin == 2)
1094                 {
1095                         // use view origin
1096                         self.origin = view_origin;
1097                 }
1098                 else
1099                 {
1100                         // use player origin so that third person display still works
1101                         self.origin = getplayerorigin(player_localnum) + ('0 0 1' * getstati(STAT_VIEWHEIGHT));
1102                 }
1103         }
1104
1105         setorigin(self, self.origin);
1106
1107         if(sf & ARC_SF_WANTDIR) // want/aim direction
1108         {
1109                 self.v_angle_x = ReadCoord();
1110                 self.v_angle_y = ReadCoord();
1111                 self.v_angle_z = ReadCoord();
1112         }
1113
1114         if(sf & ARC_SF_BEAMDIR) // beam direction
1115         {
1116                 self.angles_x = ReadCoord();
1117                 self.angles_y = ReadCoord();
1118                 self.angles_z = ReadCoord();
1119         }
1120
1121         if(sf & ARC_SF_BEAMTYPE) // beam type
1122         {
1123                 self.beam_type = ReadByte();
1124                 switch(self.beam_type)
1125                 {
1126                         case ARC_BT_MISS:
1127                         {
1128                                 self.beam_color = '-1 -1 1';
1129                                 self.beam_alpha = 0.5;
1130                                 self.beam_thickness = 8;
1131                                 self.beam_traileffect = FALSE;
1132                                 self.beam_hiteffect = particleeffectnum("electro_lightning");
1133                                 self.beam_hitlight[0] = 0;
1134                                 self.beam_hitlight[1] = 1;
1135                                 self.beam_hitlight[2] = 1;
1136                                 self.beam_hitlight[3] = 1;
1137                                 self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
1138                                 self.beam_muzzlelight[0] = 0;
1139                                 self.beam_muzzlelight[1] = 1;
1140                                 self.beam_muzzlelight[2] = 1;
1141                                 self.beam_muzzlelight[3] = 1;
1142                                 self.beam_image = "particles/lgbeam";
1143                                 setmodel(flash, "models/flash.md3");
1144                                 flash.alpha = self.beam_alpha;
1145                                 flash.colormod = self.beam_color;
1146                                 flash.scale = 0.5;
1147                                 break;
1148                         }
1149                         case ARC_BT_WALL: // grenadelauncher_muzzleflash healray_muzzleflash
1150                         {
1151                                 self.beam_color = '0.5 0.5 1';
1152                                 self.beam_alpha = 0.5;
1153                                 self.beam_thickness = 8;
1154                                 self.beam_traileffect = FALSE;
1155                                 self.beam_hiteffect = particleeffectnum("electro_lightning");
1156                                 self.beam_hitlight[0] = 0;
1157                                 self.beam_hitlight[1] = 1;
1158                                 self.beam_hitlight[2] = 1;
1159                                 self.beam_hitlight[3] = 1;
1160                                 self.beam_muzzleeffect = FALSE; // particleeffectnum("grenadelauncher_muzzleflash");
1161                                 self.beam_muzzlelight[0] = 0;
1162                                 self.beam_muzzlelight[1] = 1;
1163                                 self.beam_muzzlelight[2] = 1;
1164                                 self.beam_muzzlelight[3] = 1;
1165                                 self.beam_image = "particles/lgbeam";
1166                                 setmodel(flash, "models/flash.md3");
1167                                 flash.alpha = self.beam_alpha;
1168                                 flash.colormod = self.beam_color;
1169                                 flash.scale = 0.5;
1170                                 break;
1171                         }
1172                         case ARC_BT_HEAL:
1173                         {
1174                                 self.beam_color = '0 1 0';
1175                                 self.beam_alpha = 0.5;
1176                                 self.beam_thickness = 8;
1177                                 self.beam_traileffect = FALSE;
1178                                 self.beam_hiteffect = particleeffectnum("healray_impact"); 
1179                                 self.beam_hitlight[0] = 0;
1180                                 self.beam_hitlight[1] = 1;
1181                                 self.beam_hitlight[2] = 1;
1182                                 self.beam_hitlight[3] = 1;
1183                                 self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
1184                                 self.beam_muzzlelight[0] = 0;
1185                                 self.beam_muzzlelight[1] = 1;
1186                                 self.beam_muzzlelight[2] = 1;
1187                                 self.beam_muzzlelight[3] = 1;
1188                                 self.beam_image = "particles/lgbeam";
1189                                 setmodel(flash, "models/flash.md3");
1190                                 flash.alpha = self.beam_alpha;
1191                                 flash.colormod = self.beam_color;
1192                                 flash.scale = 0.5;
1193                                 break;
1194                         }
1195                         case ARC_BT_HIT:
1196                         {
1197                                 self.beam_color = '1 0 1';
1198                                 self.beam_alpha = 0.5;
1199                                 self.beam_thickness = 8;
1200                                 self.beam_traileffect = particleeffectnum("nex_beam");
1201                                 self.beam_hiteffect = particleeffectnum("electro_lightning"); 
1202                                 self.beam_hitlight[0] = 20;
1203                                 self.beam_hitlight[1] = 1;
1204                                 self.beam_hitlight[2] = 0;
1205                                 self.beam_hitlight[3] = 0;
1206                                 self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
1207                                 self.beam_muzzlelight[0] = 50;
1208                                 self.beam_muzzlelight[1] = 1;
1209                                 self.beam_muzzlelight[2] = 0;
1210                                 self.beam_muzzlelight[3] = 0;
1211                                 self.beam_image = "particles/lgbeam";
1212                                 setmodel(flash, "models/flash.md3");
1213                                 flash.alpha = self.beam_alpha;
1214                                 flash.colormod = self.beam_color;
1215                                 flash.scale = 0.5;
1216                                 break;
1217                         }
1218                         case ARC_BT_BURST_MISS:
1219                         {
1220                                 self.beam_color = '-1 -1 1';
1221                                 self.beam_alpha = 0.5;
1222                                 self.beam_thickness = 14;
1223                                 self.beam_traileffect = FALSE;
1224                                 self.beam_hiteffect = particleeffectnum("electro_lightning"); 
1225                                 self.beam_hitlight[0] = 0;
1226                                 self.beam_hitlight[1] = 1;
1227                                 self.beam_hitlight[2] = 1;
1228                                 self.beam_hitlight[3] = 1;
1229                                 self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
1230                                 self.beam_muzzlelight[0] = 0;
1231                                 self.beam_muzzlelight[1] = 1;
1232                                 self.beam_muzzlelight[2] = 1;
1233                                 self.beam_muzzlelight[3] = 1;
1234                                 self.beam_image = "particles/lgbeam";
1235                                 setmodel(flash, "models/flash.md3");
1236                                 flash.alpha = self.beam_alpha;
1237                                 flash.colormod = self.beam_color;
1238                                 flash.scale = 0.5;
1239                                 break;
1240                         }
1241                         case ARC_BT_BURST_WALL:
1242                         {
1243                                 self.beam_color = '0.5 0.5 1';
1244                                 self.beam_alpha = 0.5;
1245                                 self.beam_thickness = 14;
1246                                 self.beam_traileffect = FALSE;
1247                                 self.beam_hiteffect = particleeffectnum("electro_lightning"); 
1248                                 self.beam_hitlight[0] = 0;
1249                                 self.beam_hitlight[1] = 1;
1250                                 self.beam_hitlight[2] = 1;
1251                                 self.beam_hitlight[3] = 1;
1252                                 self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
1253                                 self.beam_muzzlelight[0] = 0;
1254                                 self.beam_muzzlelight[1] = 1;
1255                                 self.beam_muzzlelight[2] = 1;
1256                                 self.beam_muzzlelight[3] = 1;
1257                                 self.beam_image = "particles/lgbeam";
1258                                 setmodel(flash, "models/flash.md3");
1259                                 flash.alpha = self.beam_alpha;
1260                                 flash.colormod = self.beam_color;
1261                                 flash.scale = 0.5;
1262                                 break;
1263                         }
1264                         case ARC_BT_BURST_HEAL:
1265                         {
1266                                 self.beam_color = '0 1 0';
1267                                 self.beam_alpha = 0.5;
1268                                 self.beam_thickness = 14;
1269                                 self.beam_traileffect = FALSE;
1270                                 self.beam_hiteffect = particleeffectnum("electro_lightning"); 
1271                                 self.beam_hitlight[0] = 0;
1272                                 self.beam_hitlight[1] = 1;
1273                                 self.beam_hitlight[2] = 1;
1274                                 self.beam_hitlight[3] = 1;
1275                                 self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
1276                                 self.beam_muzzlelight[0] = 0;
1277                                 self.beam_muzzlelight[1] = 1;
1278                                 self.beam_muzzlelight[2] = 1;
1279                                 self.beam_muzzlelight[3] = 1;
1280                                 self.beam_image = "particles/lgbeam";
1281                                 setmodel(flash, "models/flash.md3");
1282                                 flash.alpha = self.beam_alpha;
1283                                 flash.colormod = self.beam_color;
1284                                 flash.scale = 0.5;
1285                                 break;
1286                         }
1287                         case ARC_BT_BURST_HIT:
1288                         {
1289                                 self.beam_color = '1 0 1';
1290                                 self.beam_alpha = 0.5;
1291                                 self.beam_thickness = 14;
1292                                 self.beam_traileffect = FALSE;
1293                                 self.beam_hiteffect = particleeffectnum("electro_lightning"); 
1294                                 self.beam_hitlight[0] = 0;
1295                                 self.beam_hitlight[1] = 1;
1296                                 self.beam_hitlight[2] = 1;
1297                                 self.beam_hitlight[3] = 1;
1298                                 self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
1299                                 self.beam_muzzlelight[0] = 0;
1300                                 self.beam_muzzlelight[1] = 1;
1301                                 self.beam_muzzlelight[2] = 1;
1302                                 self.beam_muzzlelight[3] = 1;
1303                                 self.beam_image = "particles/lgbeam";
1304                                 setmodel(flash, "models/flash.md3");
1305                                 flash.alpha = self.beam_alpha;
1306                                 flash.colormod = self.beam_color;
1307                                 flash.scale = 0.5;
1308                                 break;
1309                         }
1310
1311                         // shouldn't be possible, but lets make it colorful if it does :D
1312                         default:
1313                         {
1314                                 self.beam_color = randomvec();
1315                                 self.beam_alpha = 1;
1316                                 self.beam_thickness = 8;
1317                                 self.beam_traileffect = FALSE;
1318                                 self.beam_hiteffect = FALSE; 
1319                                 self.beam_hitlight[0] = 0;
1320                                 self.beam_hitlight[1] = 1;
1321                                 self.beam_hitlight[2] = 1;
1322                                 self.beam_hitlight[3] = 1;
1323                                 self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
1324                                 self.beam_muzzlelight[0] = 0;
1325                                 self.beam_muzzlelight[1] = 1;
1326                                 self.beam_muzzlelight[2] = 1;
1327                                 self.beam_muzzlelight[3] = 1;
1328                                 self.beam_image = "particles/lgbeam";
1329                                 setmodel(flash, "models/flash.md3");
1330                                 flash.alpha = self.beam_alpha;
1331                                 flash.colormod = self.beam_color;
1332                                 flash.scale = 0.5;
1333                                 break;
1334                         }
1335                 }
1336         }
1337
1338         if(!self.beam_usevieworigin)
1339         {
1340                 InterpolateOrigin_Note();
1341         }
1342 }
1343
1344 float W_Arc(float req)
1345 {
1346         switch(req)
1347         {
1348                 case WR_IMPACTEFFECT:
1349                 {
1350                         // todo
1351                         return TRUE;
1352                 }
1353                 case WR_INIT:
1354                 {
1355                         precache_sound("weapons/lgbeam_fly.wav");
1356                         return TRUE;
1357                 }
1358                 case WR_ZOOMRETICLE:
1359                 {
1360                         // no weapon specific image for this weapon
1361                         return FALSE;
1362                 }
1363         }
1364         return FALSE;
1365 }
1366 #endif
1367 #endif