From 84827527b69b7038685e67bf437934310209301c Mon Sep 17 00:00:00 2001
From: Samual Lenks <samual@xonotic.org>
Date: Wed, 26 Feb 2014 21:30:54 -0500
Subject: [PATCH] Move the Draw_ArcBeam code to w_arc.qc

---
 qcsrc/client/particles.qc     | 750 ---------------------------------
 qcsrc/common/weapons/w_arc.qc | 753 ++++++++++++++++++++++++++++++++++
 2 files changed, 753 insertions(+), 750 deletions(-)

diff --git a/qcsrc/client/particles.qc b/qcsrc/client/particles.qc
index b28c687957..8ab746f2f1 100644
--- a/qcsrc/client/particles.qc
+++ b/qcsrc/client/particles.qc
@@ -361,753 +361,3 @@ void Net_ReadShockwaveParticle()
 
 	shockwave.sw_time = time;
 }
-
-.vector beam_color;
-.float beam_alpha;
-.float beam_thickness;
-.float beam_traileffect;
-.float beam_hiteffect;
-.float beam_hitlight[4]; // 0: radius, 123: rgb
-.float beam_muzzleeffect;
-.float beam_muzzlelight[4]; // 0: radius, 123: rgb
-.string beam_image;
-
-.entity beam_muzzleentity;
-
-.float beam_degreespersegment;
-.float beam_distancepersegment;
-.float beam_usevieworigin;
-.float beam_initialized;
-.float beam_maxangle;
-.float beam_range;
-.float beam_returnspeed;
-.float beam_tightness;
-.vector beam_shotorigin;
-.vector beam_dir;
-
-entity Draw_ArcBeam_callback_entity;
-vector Draw_ArcBeam_callback_new_dir;
-float Draw_ArcBeam_callback_segmentdist;
-float Draw_ArcBeam_callback_last_thickness;
-vector Draw_ArcBeam_callback_last_top;
-vector Draw_ArcBeam_callback_last_bottom;
-
-void Draw_ArcBeam_callback(vector start, vector hit, vector end)
-{
-	entity beam = Draw_ArcBeam_callback_entity;
-	vector transformed_view_org;
-	transformed_view_org = WarpZone_TransformOrigin(WarpZone_trace_transform, view_origin);
-
-	vector thickdir = normalize(cross(normalize(start - hit), transformed_view_org - start));
-
-	vector hitorigin;
-
-	// draw segment
-	#if 0
-	if(trace_fraction != 1)
-	{
-		// calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
-		hitorigin = start + (Draw_ArcBeam_callback_new_dir * Draw_ArcBeam_callback_segmentdist * trace_fraction);
-		hitorigin = WarpZone_TransformOrigin(WarpZone_trace_transform, hitorigin);
-	}
-	else
-	{
-		hitorigin = hit;
-	}
-	#else
-	hitorigin = hit;
-	#endif
-
-	// decide upon thickness
-	float thickness = beam.beam_thickness;
-
-	// draw primary beam render
-	vector top    = hitorigin + (thickdir * thickness);
-	vector bottom = hitorigin - (thickdir * thickness);
-	//vector last_top    = start + (thickdir * Draw_ArcBeam_callback_last_thickness);
-	//vector last_bottom = start - (thickdir * Draw_ArcBeam_callback_last_thickness);
-
-	R_BeginPolygon(beam.beam_image, DRAWFLAG_NORMAL); // DRAWFLAG_ADDITIVE
-	R_PolygonVertex(
-		top,
-		'0 0.5 0' + ('0 0.5 0' * (thickness / beam.beam_thickness)),
-		beam.beam_color,
-		beam.beam_alpha
-	);
-	R_PolygonVertex(
-		Draw_ArcBeam_callback_last_top,
-		'0 0.5 0' + ('0 0.5 0' * (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)),
-		beam.beam_color,
-		beam.beam_alpha
-	);
-	R_PolygonVertex(
-		Draw_ArcBeam_callback_last_bottom,
-		'0 0.5 0' * (1 - (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)),
-		beam.beam_color,
-		beam.beam_alpha
-	);
-	R_PolygonVertex(
-		bottom,
-		'0 0.5 0' * (1 - (thickness / beam.beam_thickness)),
-		beam.beam_color,
-		beam.beam_alpha
-	);
-	R_EndPolygon();
-
-	// draw trailing particles
-	// NOTES:
-	//  - Don't use spammy particle counts here, use a FEW small particles around the beam
-	//  - We're not using WarpZone_TrailParticles here because we will handle warpzones ourselves.
-	if(beam.beam_traileffect)
-	{
-		trailparticles(beam, beam.beam_traileffect, start, hitorigin);
-	}
-
-	// set up for the next 
-	Draw_ArcBeam_callback_last_thickness = thickness;
-	Draw_ArcBeam_callback_last_top = top;
-	Draw_ArcBeam_callback_last_bottom = bottom;
-}
-
-void Draw_ArcBeam()
-{
-	if(!self.beam_usevieworigin)
-	{
-		InterpolateOrigin_Do();
-	}
-
-	// origin = beam starting origin
-	// v_angle = wanted/aim direction
-	// angles = current direction of beam
-
-	vector start_pos;
-	vector wantdir; //= view_forward;
-	vector beamdir; //= self.beam_dir;
-
-	float segments;
-	if(self.beam_usevieworigin)
-	{
-		// WEAPONTODO:
-		// Currently we have to replicate nearly the same method of figuring
-		// out the shotdir that the server does... Ideally in the future we
-		// should be able to acquire this from a generalized function built
-		// into a weapon system for client code. 
-
-		// find where we are aiming
-		makevectors(view_angles);
-
-		// decide upon start position
-		if(self.beam_usevieworigin == 2)
-			{ start_pos = view_origin; }
-		else
-			{ start_pos = self.origin; }
-
-		// trace forward with an estimation
-		WarpZone_TraceLine(
-			start_pos,
-			start_pos + view_forward * self.beam_range,
-			MOVE_NOMONSTERS,
-			self
-		);
-
-		// untransform in case our trace went through a warpzone
-		vector vf, vr, vu;
-		vf = view_forward;
-		vr = view_right;
-		vu = view_up;
-		vector shothitpos = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); // warpzone support
-		view_forward = vf;
-		view_right = vr;
-		view_up = vu;
-
-		// un-adjust trueaim if shotend is too close
-		if(vlen(shothitpos - view_origin) < g_trueaim_minrange)
-			shothitpos = view_origin + (view_forward * g_trueaim_minrange);
-
-		// move shot origin to the actual gun muzzle origin
-		vector origin_offset =
-			view_forward * self.beam_shotorigin_x
-			+ view_right * -self.beam_shotorigin_y 
-			+ view_up * self.beam_shotorigin_z;
-
-		start_pos = start_pos + origin_offset;
-
-		// calculate the aim direction now
-		wantdir = normalize(shothitpos - start_pos);
-
-		if(!self.beam_initialized)
-		{
-			self.beam_dir = wantdir;
-			self.beam_initialized = TRUE;
-		}
-
-		if(self.beam_dir != wantdir)
-		{
-			// calculate how much we're going to move the end of the beam to the want position
-			// WEAPONTODO (server and client):
-			// blendfactor never actually becomes 0 in this situation, which is a problem
-			// regarding precision... this means that self.beam_dir and w_shotdir approach
-			// eachother, however they never actually become the same value with this method.
-			// Perhaps we should do some form of rounding/snapping?
-			float angle = vlen(wantdir - self.beam_dir) * RAD2DEG;
-			if(angle && (angle > self.beam_maxangle))
-			{
-				// if the angle is greater than maxangle, force the blendfactor to make this the maximum factor
-				float blendfactor = bound(
-					0,
-					(1 - (self.beam_returnspeed * frametime)),
-					min(self.beam_maxangle / angle, 1)
-				);
-				self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
-			}
-			else
-			{
-				// the radius is not too far yet, no worries :D
-				float blendfactor = bound(
-					0,
-					(1 - (self.beam_returnspeed * frametime)),
-					1
-				);
-				self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
-			}
-
-			// calculate how many segments are needed
-			float max_allowed_segments;
-
-			if(self.beam_distancepersegment)
-			{
-				max_allowed_segments = min(
-					ARC_MAX_SEGMENTS,
-					1 + (vlen(wantdir / self.beam_distancepersegment))
-				);
-			}
-			else { max_allowed_segments = ARC_MAX_SEGMENTS; }
-
-			if(self.beam_degreespersegment)
-			{
-				segments = bound(
-					1, 
-					(
-						min(
-							angle,
-							self.beam_maxangle
-						)
-						/
-						self.beam_degreespersegment
-					),
-					max_allowed_segments
-				);
-			}
-			else { segments = 1; }
-		}
-		else { segments = 1; }
-
-		// set the beam direction which the rest of the code will refer to
-		beamdir = self.beam_dir;
-
-		// finally, set self.angles to the proper direction so that muzzle attachment points in proper direction
-		self.angles = fixedvectoangles2(view_forward, view_up);
-	}
-	else
-	{
-		// set the values from the provided info from the networked entity
-		start_pos = self.origin;
-		wantdir = self.v_angle;
-		beamdir = self.angles;
-
-		if(beamdir != wantdir)
-		{
-			float angle = vlen(wantdir - beamdir) * RAD2DEG;
-
-			// calculate how many segments are needed
-			float max_allowed_segments;
-
-			if(self.beam_distancepersegment)
-			{
-				max_allowed_segments = min(
-					ARC_MAX_SEGMENTS,
-					1 + (vlen(wantdir / self.beam_distancepersegment))
-				);
-			}
-			else { max_allowed_segments = ARC_MAX_SEGMENTS; }
-
-			if(self.beam_degreespersegment)
-			{
-				segments = bound(
-					1, 
-					(
-						min(
-							angle,
-							self.beam_maxangle
-						)
-						/
-						self.beam_degreespersegment
-					),
-					max_allowed_segments
-				);
-			}
-			else { segments = 1; }
-		}
-		else { segments = 1; }
-	}
-
-	setorigin(self, start_pos);
-	self.beam_muzzleentity.angles_z = random() * 360; // WEAPONTODO: use avelocity instead?
-
-	vector beam_endpos_estimate = (start_pos + (beamdir * self.beam_range));
-
-	Draw_ArcBeam_callback_entity = self;
-	Draw_ArcBeam_callback_last_thickness = 0;
-	Draw_ArcBeam_callback_last_top = start_pos;
-	Draw_ArcBeam_callback_last_bottom = start_pos;
-
-	vector last_origin = start_pos;
-
-	float i;
-	for(i = 1; i <= segments; ++i)
-	{
-		// WEAPONTODO (server and client):
-		// Segment blend and distance should probably really be calculated in a better way,
-		// however I am not sure how to do it properly. There are a few things I have tried,
-		// but most of them do not work properly due to my lack of understanding regarding
-		// the mathematics behind them.
-
-		// Ideally, we should calculate the positions along a perfect curve
-		// between wantdir and self.beam_dir with an option for depth of arc
-
-		// Another issue is that (on the client code) we must separate the
-		// curve into multiple rendered curves when handling warpzones.
-
-		// I can handle this by detecting it for each segment, however that
-		// is a fairly inefficient method in comparison to having a curved line
-		// drawing function similar to Draw_CylindricLine that accepts
-		// top and bottom origins as input, this way there would be no
-		// overlapping edges when connecting the curved pieces.
-
-		// WEAPONTODO (client):
-		// In order to do nice fading and pointing on the starting segment, we must always
-		// have that drawn as a separate triangle... However, that is difficult to do when
-		// keeping in mind the above problems and also optimizing the amount of segments
-		// drawn on screen at any given time. (Automatic beam quality scaling, essentially)
-
-		// calculate this on every segment to ensure that we always reach the full length of the attack
-		float segmentblend = bound(0, (i/segments) + self.beam_tightness, 1);
-		float segmentdist = vlen(beam_endpos_estimate - last_origin) * (i/segments);
-
-		// WEAPONTODO: Apparently, normalize is not the correct function to use here...
-		// Figure out how this actually should work.
-		vector new_dir = normalize(
-			(wantdir * (1 - segmentblend))
-			+
-			(normalize(beam_endpos_estimate - last_origin) * segmentblend)
-		);
-		vector new_origin = last_origin + (new_dir * segmentdist);
-
-		Draw_ArcBeam_callback_segmentdist = segmentdist;
-		Draw_ArcBeam_callback_new_dir = new_dir;
-
-		WarpZone_TraceBox_ThroughZone(
-			last_origin,
-			'0 0 0',
-			'0 0 0',
-			new_origin,
-			MOVE_NORMAL,
-			world,
-			world,
-			Draw_ArcBeam_callback
-		);
-
-		//printf("segment: %d, warpzone transform: %d\n", i, (WarpZone_trace_transform != world));
-
-		// WEAPONTODO:
-		// Figure out some way to detect a collision with geometry with callback...
-		// That way we can know when we are done drawing the beam and skip
-		// the rest of the segments without breaking warpzone support.
-
-		last_origin = WarpZone_TransformOrigin(WarpZone_trace_transform, new_origin);
-		beam_endpos_estimate = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos_estimate);
-	}
-
-	// startpoint and endpoint drawn visual effects
-	if(self.beam_hiteffect)
-	{
-		pointparticles(
-			self.beam_hiteffect,
-			last_origin,
-			beamdir * -1,
-			frametime * 2
-		);
-	}
-	if(self.beam_hitlight[0])
-	{
-		adddynamiclight(
-			last_origin,
-			self.beam_hitlight[0],
-			vec3(
-				self.beam_hitlight[1],
-				self.beam_hitlight[2],
-				self.beam_hitlight[3]
-			)
-		);
-	}
-	if(self.beam_muzzleeffect)
-	{
-		pointparticles(
-			self.beam_muzzleeffect,
-			start_pos + wantdir * 20,
-			wantdir * 1000,
-			frametime * 0.1
-		);
-	}
-	if(self.beam_muzzlelight[0])
-	{
-		adddynamiclight(
-			start_pos + wantdir * 20,
-			self.beam_muzzlelight[0],
-			vec3(
-				self.beam_muzzlelight[1],
-				self.beam_muzzlelight[2],
-				self.beam_muzzlelight[3]
-			)
-		);
-	}
-
-	// cleanup
-	Draw_ArcBeam_callback_entity = world;
-	Draw_ArcBeam_callback_new_dir = '0 0 0';
-	Draw_ArcBeam_callback_segmentdist = 0;
-	Draw_ArcBeam_callback_last_thickness = 0;
-	Draw_ArcBeam_callback_last_top = '0 0 0';
-	Draw_ArcBeam_callback_last_bottom = '0 0 0';
-}
-
-void Remove_ArcBeam(void)
-{
-	remove(self.beam_muzzleentity);
-	sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
-}
-
-void Ent_ReadArcBeam(float isnew)
-{
-	float sf = ReadByte();
-	entity flash;
-
-	if(isnew)
-	{
-		// calculate shot origin offset from gun alignment
-		float gunalign = autocvar_cl_gunalign;
-		if(gunalign != 1 && gunalign != 2 && gunalign != 4)
-			gunalign = 3; // default value
-		--gunalign;
-
-		self.beam_shotorigin = arc_shotorigin[gunalign];
-
-		// set other main attributes of the beam
-		self.draw = Draw_ArcBeam;
-		self.entremove = Remove_ArcBeam;
-		sound(self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTEN_NORM);
-
-		flash = spawn();
-		flash.owner = self;
-		flash.effects = EF_ADDITIVE | EF_FULLBRIGHT;
-		flash.drawmask = MASK_NORMAL;
-		flash.solid = SOLID_NOT;
-		setattachment(flash, self, "");
-		setorigin(flash, '0 0 0');
-
-		self.beam_muzzleentity = flash;
-	}
-	else
-	{
-		flash = self.beam_muzzleentity;
-	}
-
-	if(sf & 1) // settings information
-	{
-		self.beam_degreespersegment = ReadShort();
-		self.beam_distancepersegment = ReadShort();
-		self.beam_maxangle = ReadShort();
-		self.beam_range = ReadCoord();
-		self.beam_returnspeed = ReadShort();
-		self.beam_tightness = (ReadByte() / 10);
-
-		if(ReadByte())
-		{
-			if(autocvar_chase_active)
-				{ self.beam_usevieworigin = 1; }
-			else // use view origin
-				{ self.beam_usevieworigin = 2; }
-		}
-		else
-		{
-			self.beam_usevieworigin = 0;
-		}
-	}
-
-	if(!self.beam_usevieworigin)
-	{
-		// self.iflags = IFLAG_ORIGIN | IFLAG_ANGLES | IFLAG_V_ANGLE; // why doesn't this work?
-		self.iflags = IFLAG_ORIGIN;
-
-		InterpolateOrigin_Undo();
-	}
-
-	if(sf & 2) // starting location
-	{
-		self.origin_x = ReadCoord();
-		self.origin_y = ReadCoord();
-		self.origin_z = ReadCoord();
-	}
-	else if(self.beam_usevieworigin) // infer the location from player location
-	{
-		if(self.beam_usevieworigin == 2)
-		{
-			// use view origin
-			self.origin = view_origin;
-		}
-		else
-		{
-			// use player origin so that third person display still works
-			self.origin = getplayerorigin(player_localnum) + ('0 0 1' * getstati(STAT_VIEWHEIGHT));
-		}
-	}
-
-	setorigin(self, self.origin);
-
-	if(sf & 4) // want/aim direction
-	{
-		self.v_angle_x = ReadCoord();
-		self.v_angle_y = ReadCoord();
-		self.v_angle_z = ReadCoord();
-	}
-
-	if(sf & 8) // beam direction
-	{
-		self.angles_x = ReadCoord();
-		self.angles_y = ReadCoord();
-		self.angles_z = ReadCoord();
-	}
-
-	if(sf & 16) // beam type
-	{
-		self.beam_type = ReadByte();
-		switch(self.beam_type)
-		{
-			case ARC_BT_MISS:
-			{
-				self.beam_color = '-1 -1 1';
-				self.beam_alpha = 0.5;
-				self.beam_thickness = 8;
-				self.beam_traileffect = FALSE;
-				self.beam_hiteffect = particleeffectnum("electro_lightning");
-				self.beam_hitlight[0] = 0;
-				self.beam_hitlight[1] = 1;
-				self.beam_hitlight[2] = 1;
-				self.beam_hitlight[3] = 1;
-				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
-				self.beam_muzzlelight[0] = 0;
-				self.beam_muzzlelight[1] = 1;
-				self.beam_muzzlelight[2] = 1;
-				self.beam_muzzlelight[3] = 1;
-				self.beam_image = "particles/lgbeam";
-				setmodel(flash, "models/flash.md3");
-				flash.alpha = self.beam_alpha;
-				flash.colormod = self.beam_color;
-				flash.scale = 0.5;
-				break;
-			}
-			case ARC_BT_WALL: // grenadelauncher_muzzleflash healray_muzzleflash
-			{
-				self.beam_color = '0.5 0.5 1';
-				self.beam_alpha = 0.5;
-				self.beam_thickness = 8;
-				self.beam_traileffect = FALSE;
-				self.beam_hiteffect = particleeffectnum("electro_lightning");
-				self.beam_hitlight[0] = 0;
-				self.beam_hitlight[1] = 1;
-				self.beam_hitlight[2] = 1;
-				self.beam_hitlight[3] = 1;
-				self.beam_muzzleeffect = FALSE; // particleeffectnum("grenadelauncher_muzzleflash");
-				self.beam_muzzlelight[0] = 0;
-				self.beam_muzzlelight[1] = 1;
-				self.beam_muzzlelight[2] = 1;
-				self.beam_muzzlelight[3] = 1;
-				self.beam_image = "particles/lgbeam";
-				setmodel(flash, "models/flash.md3");
-				flash.alpha = self.beam_alpha;
-				flash.colormod = self.beam_color;
-				flash.scale = 0.5;
-				break;
-			}
-			case ARC_BT_HEAL:
-			{
-				self.beam_color = '0 1 0';
-				self.beam_alpha = 0.5;
-				self.beam_thickness = 8;
-				self.beam_traileffect = FALSE;
-				self.beam_hiteffect = particleeffectnum("healray_impact"); 
-				self.beam_hitlight[0] = 0;
-				self.beam_hitlight[1] = 1;
-				self.beam_hitlight[2] = 1;
-				self.beam_hitlight[3] = 1;
-				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
-				self.beam_muzzlelight[0] = 0;
-				self.beam_muzzlelight[1] = 1;
-				self.beam_muzzlelight[2] = 1;
-				self.beam_muzzlelight[3] = 1;
-				self.beam_image = "particles/lgbeam";
-				setmodel(flash, "models/flash.md3");
-				flash.alpha = self.beam_alpha;
-				flash.colormod = self.beam_color;
-				flash.scale = 0.5;
-				break;
-			}
-			case ARC_BT_HIT:
-			{
-				self.beam_color = '1 0 1';
-				self.beam_alpha = 0.5;
-				self.beam_thickness = 8;
-				self.beam_traileffect = particleeffectnum("nex_beam");
-				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
-				self.beam_hitlight[0] = 20;
-				self.beam_hitlight[1] = 1;
-				self.beam_hitlight[2] = 0;
-				self.beam_hitlight[3] = 0;
-				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
-				self.beam_muzzlelight[0] = 50;
-				self.beam_muzzlelight[1] = 1;
-				self.beam_muzzlelight[2] = 0;
-				self.beam_muzzlelight[3] = 0;
-				self.beam_image = "particles/lgbeam";
-				setmodel(flash, "models/flash.md3");
-				flash.alpha = self.beam_alpha;
-				flash.colormod = self.beam_color;
-				flash.scale = 0.5;
-				break;
-			}
-			case ARC_BT_BURST_MISS:
-			{
-				self.beam_color = '-1 -1 1';
-				self.beam_alpha = 0.5;
-				self.beam_thickness = 14;
-				self.beam_traileffect = FALSE;
-				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
-				self.beam_hitlight[0] = 0;
-				self.beam_hitlight[1] = 1;
-				self.beam_hitlight[2] = 1;
-				self.beam_hitlight[3] = 1;
-				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
-				self.beam_muzzlelight[0] = 0;
-				self.beam_muzzlelight[1] = 1;
-				self.beam_muzzlelight[2] = 1;
-				self.beam_muzzlelight[3] = 1;
-				self.beam_image = "particles/lgbeam";
-				setmodel(flash, "models/flash.md3");
-				flash.alpha = self.beam_alpha;
-				flash.colormod = self.beam_color;
-				flash.scale = 0.5;
-				break;
-			}
-			case ARC_BT_BURST_WALL:
-			{
-				self.beam_color = '0.5 0.5 1';
-				self.beam_alpha = 0.5;
-				self.beam_thickness = 14;
-				self.beam_traileffect = FALSE;
-				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
-				self.beam_hitlight[0] = 0;
-				self.beam_hitlight[1] = 1;
-				self.beam_hitlight[2] = 1;
-				self.beam_hitlight[3] = 1;
-				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
-				self.beam_muzzlelight[0] = 0;
-				self.beam_muzzlelight[1] = 1;
-				self.beam_muzzlelight[2] = 1;
-				self.beam_muzzlelight[3] = 1;
-				self.beam_image = "particles/lgbeam";
-				setmodel(flash, "models/flash.md3");
-				flash.alpha = self.beam_alpha;
-				flash.colormod = self.beam_color;
-				flash.scale = 0.5;
-				break;
-			}
-			case ARC_BT_BURST_HEAL:
-			{
-				self.beam_color = '0 1 0';
-				self.beam_alpha = 0.5;
-				self.beam_thickness = 14;
-				self.beam_traileffect = FALSE;
-				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
-				self.beam_hitlight[0] = 0;
-				self.beam_hitlight[1] = 1;
-				self.beam_hitlight[2] = 1;
-				self.beam_hitlight[3] = 1;
-				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
-				self.beam_muzzlelight[0] = 0;
-				self.beam_muzzlelight[1] = 1;
-				self.beam_muzzlelight[2] = 1;
-				self.beam_muzzlelight[3] = 1;
-				self.beam_image = "particles/lgbeam";
-				setmodel(flash, "models/flash.md3");
-				flash.alpha = self.beam_alpha;
-				flash.colormod = self.beam_color;
-				flash.scale = 0.5;
-				break;
-			}
-			case ARC_BT_BURST_HIT:
-			{
-				self.beam_color = '1 0 1';
-				self.beam_alpha = 0.5;
-				self.beam_thickness = 14;
-				self.beam_traileffect = FALSE;
-				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
-				self.beam_hitlight[0] = 0;
-				self.beam_hitlight[1] = 1;
-				self.beam_hitlight[2] = 1;
-				self.beam_hitlight[3] = 1;
-				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
-				self.beam_muzzlelight[0] = 0;
-				self.beam_muzzlelight[1] = 1;
-				self.beam_muzzlelight[2] = 1;
-				self.beam_muzzlelight[3] = 1;
-				self.beam_image = "particles/lgbeam";
-				setmodel(flash, "models/flash.md3");
-				flash.alpha = self.beam_alpha;
-				flash.colormod = self.beam_color;
-				flash.scale = 0.5;
-				break;
-			}
-
-			// shouldn't be possible, but lets make it colorful if it does :D
-			default:
-			{
-				self.beam_color = randomvec();
-				self.beam_alpha = 1;
-				self.beam_thickness = 8;
-				self.beam_traileffect = FALSE;
-				self.beam_hiteffect = FALSE; 
-				self.beam_hitlight[0] = 0;
-				self.beam_hitlight[1] = 1;
-				self.beam_hitlight[2] = 1;
-				self.beam_hitlight[3] = 1;
-				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
-				self.beam_muzzlelight[0] = 0;
-				self.beam_muzzlelight[1] = 1;
-				self.beam_muzzlelight[2] = 1;
-				self.beam_muzzlelight[3] = 1;
-				self.beam_image = "particles/lgbeam";
-				setmodel(flash, "models/flash.md3");
-				flash.alpha = self.beam_alpha;
-				flash.colormod = self.beam_color;
-				flash.scale = 0.5;
-				break;
-			}
-		}
-	}
-
-	if(!self.beam_usevieworigin)
-	{
-		InterpolateOrigin_Note();
-	}
-}
-
diff --git a/qcsrc/common/weapons/w_arc.qc b/qcsrc/common/weapons/w_arc.qc
index 39f65a36b5..a6d4cee233 100644
--- a/qcsrc/common/weapons/w_arc.qc
+++ b/qcsrc/common/weapons/w_arc.qc
@@ -74,6 +74,9 @@ ARC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
 .float beam_initialized;
 .float beam_bursting;
 #endif
+#ifdef CSQC
+void Ent_ReadArcBeam(float isnew);
+#endif
 #else
 #ifdef SVQC
 void spawnfunc_weapon_arc(void) { weapon_defaultspawnfunc(WEP_ARC); }
@@ -612,6 +615,756 @@ float W_Arc(float req)
 }
 #endif
 #ifdef CSQC
+
+.vector beam_color;
+.float beam_alpha;
+.float beam_thickness;
+.float beam_traileffect;
+.float beam_hiteffect;
+.float beam_hitlight[4]; // 0: radius, 123: rgb
+.float beam_muzzleeffect;
+.float beam_muzzlelight[4]; // 0: radius, 123: rgb
+.string beam_image;
+
+.entity beam_muzzleentity;
+
+.float beam_degreespersegment;
+.float beam_distancepersegment;
+.float beam_usevieworigin;
+.float beam_initialized;
+.float beam_maxangle;
+.float beam_range;
+.float beam_returnspeed;
+.float beam_tightness;
+.vector beam_shotorigin;
+.vector beam_dir;
+
+entity Draw_ArcBeam_callback_entity;
+vector Draw_ArcBeam_callback_new_dir;
+float Draw_ArcBeam_callback_segmentdist;
+float Draw_ArcBeam_callback_last_thickness;
+vector Draw_ArcBeam_callback_last_top;
+vector Draw_ArcBeam_callback_last_bottom;
+
+void Draw_ArcBeam_callback(vector start, vector hit, vector end)
+{
+	entity beam = Draw_ArcBeam_callback_entity;
+	vector transformed_view_org;
+	transformed_view_org = WarpZone_TransformOrigin(WarpZone_trace_transform, view_origin);
+
+	vector thickdir = normalize(cross(normalize(start - hit), transformed_view_org - start));
+
+	vector hitorigin;
+
+	// draw segment
+	#if 0
+	if(trace_fraction != 1)
+	{
+		// calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?)
+		hitorigin = start + (Draw_ArcBeam_callback_new_dir * Draw_ArcBeam_callback_segmentdist * trace_fraction);
+		hitorigin = WarpZone_TransformOrigin(WarpZone_trace_transform, hitorigin);
+	}
+	else
+	{
+		hitorigin = hit;
+	}
+	#else
+	hitorigin = hit;
+	#endif
+
+	// decide upon thickness
+	float thickness = beam.beam_thickness;
+
+	// draw primary beam render
+	vector top    = hitorigin + (thickdir * thickness);
+	vector bottom = hitorigin - (thickdir * thickness);
+	//vector last_top    = start + (thickdir * Draw_ArcBeam_callback_last_thickness);
+	//vector last_bottom = start - (thickdir * Draw_ArcBeam_callback_last_thickness);
+
+	R_BeginPolygon(beam.beam_image, DRAWFLAG_NORMAL); // DRAWFLAG_ADDITIVE
+	R_PolygonVertex(
+		top,
+		'0 0.5 0' + ('0 0.5 0' * (thickness / beam.beam_thickness)),
+		beam.beam_color,
+		beam.beam_alpha
+	);
+	R_PolygonVertex(
+		Draw_ArcBeam_callback_last_top,
+		'0 0.5 0' + ('0 0.5 0' * (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)),
+		beam.beam_color,
+		beam.beam_alpha
+	);
+	R_PolygonVertex(
+		Draw_ArcBeam_callback_last_bottom,
+		'0 0.5 0' * (1 - (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)),
+		beam.beam_color,
+		beam.beam_alpha
+	);
+	R_PolygonVertex(
+		bottom,
+		'0 0.5 0' * (1 - (thickness / beam.beam_thickness)),
+		beam.beam_color,
+		beam.beam_alpha
+	);
+	R_EndPolygon();
+
+	// draw trailing particles
+	// NOTES:
+	//  - Don't use spammy particle counts here, use a FEW small particles around the beam
+	//  - We're not using WarpZone_TrailParticles here because we will handle warpzones ourselves.
+	if(beam.beam_traileffect)
+	{
+		trailparticles(beam, beam.beam_traileffect, start, hitorigin);
+	}
+
+	// set up for the next 
+	Draw_ArcBeam_callback_last_thickness = thickness;
+	Draw_ArcBeam_callback_last_top = top;
+	Draw_ArcBeam_callback_last_bottom = bottom;
+}
+
+void Draw_ArcBeam()
+{
+	if(!self.beam_usevieworigin)
+	{
+		InterpolateOrigin_Do();
+	}
+
+	// origin = beam starting origin
+	// v_angle = wanted/aim direction
+	// angles = current direction of beam
+
+	vector start_pos;
+	vector wantdir; //= view_forward;
+	vector beamdir; //= self.beam_dir;
+
+	float segments;
+	if(self.beam_usevieworigin)
+	{
+		// WEAPONTODO:
+		// Currently we have to replicate nearly the same method of figuring
+		// out the shotdir that the server does... Ideally in the future we
+		// should be able to acquire this from a generalized function built
+		// into a weapon system for client code. 
+
+		// find where we are aiming
+		makevectors(view_angles);
+
+		// decide upon start position
+		if(self.beam_usevieworigin == 2)
+			{ start_pos = view_origin; }
+		else
+			{ start_pos = self.origin; }
+
+		// trace forward with an estimation
+		WarpZone_TraceLine(
+			start_pos,
+			start_pos + view_forward * self.beam_range,
+			MOVE_NOMONSTERS,
+			self
+		);
+
+		// untransform in case our trace went through a warpzone
+		vector vf, vr, vu;
+		vf = view_forward;
+		vr = view_right;
+		vu = view_up;
+		vector shothitpos = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); // warpzone support
+		view_forward = vf;
+		view_right = vr;
+		view_up = vu;
+
+		// un-adjust trueaim if shotend is too close
+		if(vlen(shothitpos - view_origin) < g_trueaim_minrange)
+			shothitpos = view_origin + (view_forward * g_trueaim_minrange);
+
+		// move shot origin to the actual gun muzzle origin
+		vector origin_offset =
+			view_forward * self.beam_shotorigin_x
+			+ view_right * -self.beam_shotorigin_y 
+			+ view_up * self.beam_shotorigin_z;
+
+		start_pos = start_pos + origin_offset;
+
+		// calculate the aim direction now
+		wantdir = normalize(shothitpos - start_pos);
+
+		if(!self.beam_initialized)
+		{
+			self.beam_dir = wantdir;
+			self.beam_initialized = TRUE;
+		}
+
+		if(self.beam_dir != wantdir)
+		{
+			// calculate how much we're going to move the end of the beam to the want position
+			// WEAPONTODO (server and client):
+			// blendfactor never actually becomes 0 in this situation, which is a problem
+			// regarding precision... this means that self.beam_dir and w_shotdir approach
+			// eachother, however they never actually become the same value with this method.
+			// Perhaps we should do some form of rounding/snapping?
+			float angle = vlen(wantdir - self.beam_dir) * RAD2DEG;
+			if(angle && (angle > self.beam_maxangle))
+			{
+				// if the angle is greater than maxangle, force the blendfactor to make this the maximum factor
+				float blendfactor = bound(
+					0,
+					(1 - (self.beam_returnspeed * frametime)),
+					min(self.beam_maxangle / angle, 1)
+				);
+				self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
+			}
+			else
+			{
+				// the radius is not too far yet, no worries :D
+				float blendfactor = bound(
+					0,
+					(1 - (self.beam_returnspeed * frametime)),
+					1
+				);
+				self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor));
+			}
+
+			// calculate how many segments are needed
+			float max_allowed_segments;
+
+			if(self.beam_distancepersegment)
+			{
+				max_allowed_segments = min(
+					ARC_MAX_SEGMENTS,
+					1 + (vlen(wantdir / self.beam_distancepersegment))
+				);
+			}
+			else { max_allowed_segments = ARC_MAX_SEGMENTS; }
+
+			if(self.beam_degreespersegment)
+			{
+				segments = bound(
+					1, 
+					(
+						min(
+							angle,
+							self.beam_maxangle
+						)
+						/
+						self.beam_degreespersegment
+					),
+					max_allowed_segments
+				);
+			}
+			else { segments = 1; }
+		}
+		else { segments = 1; }
+
+		// set the beam direction which the rest of the code will refer to
+		beamdir = self.beam_dir;
+
+		// finally, set self.angles to the proper direction so that muzzle attachment points in proper direction
+		self.angles = fixedvectoangles2(view_forward, view_up);
+	}
+	else
+	{
+		// set the values from the provided info from the networked entity
+		start_pos = self.origin;
+		wantdir = self.v_angle;
+		beamdir = self.angles;
+
+		if(beamdir != wantdir)
+		{
+			float angle = vlen(wantdir - beamdir) * RAD2DEG;
+
+			// calculate how many segments are needed
+			float max_allowed_segments;
+
+			if(self.beam_distancepersegment)
+			{
+				max_allowed_segments = min(
+					ARC_MAX_SEGMENTS,
+					1 + (vlen(wantdir / self.beam_distancepersegment))
+				);
+			}
+			else { max_allowed_segments = ARC_MAX_SEGMENTS; }
+
+			if(self.beam_degreespersegment)
+			{
+				segments = bound(
+					1, 
+					(
+						min(
+							angle,
+							self.beam_maxangle
+						)
+						/
+						self.beam_degreespersegment
+					),
+					max_allowed_segments
+				);
+			}
+			else { segments = 1; }
+		}
+		else { segments = 1; }
+	}
+
+	setorigin(self, start_pos);
+	self.beam_muzzleentity.angles_z = random() * 360; // WEAPONTODO: use avelocity instead?
+
+	vector beam_endpos_estimate = (start_pos + (beamdir * self.beam_range));
+
+	Draw_ArcBeam_callback_entity = self;
+	Draw_ArcBeam_callback_last_thickness = 0;
+	Draw_ArcBeam_callback_last_top = start_pos;
+	Draw_ArcBeam_callback_last_bottom = start_pos;
+
+	vector last_origin = start_pos;
+
+	float i;
+	for(i = 1; i <= segments; ++i)
+	{
+		// WEAPONTODO (server and client):
+		// Segment blend and distance should probably really be calculated in a better way,
+		// however I am not sure how to do it properly. There are a few things I have tried,
+		// but most of them do not work properly due to my lack of understanding regarding
+		// the mathematics behind them.
+
+		// Ideally, we should calculate the positions along a perfect curve
+		// between wantdir and self.beam_dir with an option for depth of arc
+
+		// Another issue is that (on the client code) we must separate the
+		// curve into multiple rendered curves when handling warpzones.
+
+		// I can handle this by detecting it for each segment, however that
+		// is a fairly inefficient method in comparison to having a curved line
+		// drawing function similar to Draw_CylindricLine that accepts
+		// top and bottom origins as input, this way there would be no
+		// overlapping edges when connecting the curved pieces.
+
+		// WEAPONTODO (client):
+		// In order to do nice fading and pointing on the starting segment, we must always
+		// have that drawn as a separate triangle... However, that is difficult to do when
+		// keeping in mind the above problems and also optimizing the amount of segments
+		// drawn on screen at any given time. (Automatic beam quality scaling, essentially)
+
+		// calculate this on every segment to ensure that we always reach the full length of the attack
+		float segmentblend = bound(0, (i/segments) + self.beam_tightness, 1);
+		float segmentdist = vlen(beam_endpos_estimate - last_origin) * (i/segments);
+
+		// WEAPONTODO: Apparently, normalize is not the correct function to use here...
+		// Figure out how this actually should work.
+		vector new_dir = normalize(
+			(wantdir * (1 - segmentblend))
+			+
+			(normalize(beam_endpos_estimate - last_origin) * segmentblend)
+		);
+		vector new_origin = last_origin + (new_dir * segmentdist);
+
+		Draw_ArcBeam_callback_segmentdist = segmentdist;
+		Draw_ArcBeam_callback_new_dir = new_dir;
+
+		WarpZone_TraceBox_ThroughZone(
+			last_origin,
+			'0 0 0',
+			'0 0 0',
+			new_origin,
+			MOVE_NORMAL,
+			world,
+			world,
+			Draw_ArcBeam_callback
+		);
+
+		//printf("segment: %d, warpzone transform: %d\n", i, (WarpZone_trace_transform != world));
+
+		// WEAPONTODO:
+		// Figure out some way to detect a collision with geometry with callback...
+		// That way we can know when we are done drawing the beam and skip
+		// the rest of the segments without breaking warpzone support.
+
+		last_origin = WarpZone_TransformOrigin(WarpZone_trace_transform, new_origin);
+		beam_endpos_estimate = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos_estimate);
+	}
+
+	// startpoint and endpoint drawn visual effects
+	if(self.beam_hiteffect)
+	{
+		pointparticles(
+			self.beam_hiteffect,
+			last_origin,
+			beamdir * -1,
+			frametime * 2
+		);
+	}
+	if(self.beam_hitlight[0])
+	{
+		adddynamiclight(
+			last_origin,
+			self.beam_hitlight[0],
+			vec3(
+				self.beam_hitlight[1],
+				self.beam_hitlight[2],
+				self.beam_hitlight[3]
+			)
+		);
+	}
+	if(self.beam_muzzleeffect)
+	{
+		pointparticles(
+			self.beam_muzzleeffect,
+			start_pos + wantdir * 20,
+			wantdir * 1000,
+			frametime * 0.1
+		);
+	}
+	if(self.beam_muzzlelight[0])
+	{
+		adddynamiclight(
+			start_pos + wantdir * 20,
+			self.beam_muzzlelight[0],
+			vec3(
+				self.beam_muzzlelight[1],
+				self.beam_muzzlelight[2],
+				self.beam_muzzlelight[3]
+			)
+		);
+	}
+
+	// cleanup
+	Draw_ArcBeam_callback_entity = world;
+	Draw_ArcBeam_callback_new_dir = '0 0 0';
+	Draw_ArcBeam_callback_segmentdist = 0;
+	Draw_ArcBeam_callback_last_thickness = 0;
+	Draw_ArcBeam_callback_last_top = '0 0 0';
+	Draw_ArcBeam_callback_last_bottom = '0 0 0';
+}
+
+void Remove_ArcBeam(void)
+{
+	remove(self.beam_muzzleentity);
+	sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
+}
+
+void Ent_ReadArcBeam(float isnew)
+{
+	float sf = ReadByte();
+	entity flash;
+
+	if(isnew)
+	{
+		// calculate shot origin offset from gun alignment
+		float gunalign = autocvar_cl_gunalign;
+		if(gunalign != 1 && gunalign != 2 && gunalign != 4)
+			gunalign = 3; // default value
+		--gunalign;
+
+		self.beam_shotorigin = arc_shotorigin[gunalign];
+
+		// set other main attributes of the beam
+		self.draw = Draw_ArcBeam;
+		self.entremove = Remove_ArcBeam;
+		sound(self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTEN_NORM);
+
+		flash = spawn();
+		flash.owner = self;
+		flash.effects = EF_ADDITIVE | EF_FULLBRIGHT;
+		flash.drawmask = MASK_NORMAL;
+		flash.solid = SOLID_NOT;
+		setattachment(flash, self, "");
+		setorigin(flash, '0 0 0');
+
+		self.beam_muzzleentity = flash;
+	}
+	else
+	{
+		flash = self.beam_muzzleentity;
+	}
+
+	if(sf & 1) // settings information
+	{
+		self.beam_degreespersegment = ReadShort();
+		self.beam_distancepersegment = ReadShort();
+		self.beam_maxangle = ReadShort();
+		self.beam_range = ReadCoord();
+		self.beam_returnspeed = ReadShort();
+		self.beam_tightness = (ReadByte() / 10);
+
+		if(ReadByte())
+		{
+			if(autocvar_chase_active)
+				{ self.beam_usevieworigin = 1; }
+			else // use view origin
+				{ self.beam_usevieworigin = 2; }
+		}
+		else
+		{
+			self.beam_usevieworigin = 0;
+		}
+	}
+
+	if(!self.beam_usevieworigin)
+	{
+		// self.iflags = IFLAG_ORIGIN | IFLAG_ANGLES | IFLAG_V_ANGLE; // why doesn't this work?
+		self.iflags = IFLAG_ORIGIN;
+
+		InterpolateOrigin_Undo();
+	}
+
+	if(sf & 2) // starting location
+	{
+		self.origin_x = ReadCoord();
+		self.origin_y = ReadCoord();
+		self.origin_z = ReadCoord();
+	}
+	else if(self.beam_usevieworigin) // infer the location from player location
+	{
+		if(self.beam_usevieworigin == 2)
+		{
+			// use view origin
+			self.origin = view_origin;
+		}
+		else
+		{
+			// use player origin so that third person display still works
+			self.origin = getplayerorigin(player_localnum) + ('0 0 1' * getstati(STAT_VIEWHEIGHT));
+		}
+	}
+
+	setorigin(self, self.origin);
+
+	if(sf & 4) // want/aim direction
+	{
+		self.v_angle_x = ReadCoord();
+		self.v_angle_y = ReadCoord();
+		self.v_angle_z = ReadCoord();
+	}
+
+	if(sf & 8) // beam direction
+	{
+		self.angles_x = ReadCoord();
+		self.angles_y = ReadCoord();
+		self.angles_z = ReadCoord();
+	}
+
+	if(sf & 16) // beam type
+	{
+		self.beam_type = ReadByte();
+		switch(self.beam_type)
+		{
+			case ARC_BT_MISS:
+			{
+				self.beam_color = '-1 -1 1';
+				self.beam_alpha = 0.5;
+				self.beam_thickness = 8;
+				self.beam_traileffect = FALSE;
+				self.beam_hiteffect = particleeffectnum("electro_lightning");
+				self.beam_hitlight[0] = 0;
+				self.beam_hitlight[1] = 1;
+				self.beam_hitlight[2] = 1;
+				self.beam_hitlight[3] = 1;
+				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
+				self.beam_muzzlelight[0] = 0;
+				self.beam_muzzlelight[1] = 1;
+				self.beam_muzzlelight[2] = 1;
+				self.beam_muzzlelight[3] = 1;
+				self.beam_image = "particles/lgbeam";
+				setmodel(flash, "models/flash.md3");
+				flash.alpha = self.beam_alpha;
+				flash.colormod = self.beam_color;
+				flash.scale = 0.5;
+				break;
+			}
+			case ARC_BT_WALL: // grenadelauncher_muzzleflash healray_muzzleflash
+			{
+				self.beam_color = '0.5 0.5 1';
+				self.beam_alpha = 0.5;
+				self.beam_thickness = 8;
+				self.beam_traileffect = FALSE;
+				self.beam_hiteffect = particleeffectnum("electro_lightning");
+				self.beam_hitlight[0] = 0;
+				self.beam_hitlight[1] = 1;
+				self.beam_hitlight[2] = 1;
+				self.beam_hitlight[3] = 1;
+				self.beam_muzzleeffect = FALSE; // particleeffectnum("grenadelauncher_muzzleflash");
+				self.beam_muzzlelight[0] = 0;
+				self.beam_muzzlelight[1] = 1;
+				self.beam_muzzlelight[2] = 1;
+				self.beam_muzzlelight[3] = 1;
+				self.beam_image = "particles/lgbeam";
+				setmodel(flash, "models/flash.md3");
+				flash.alpha = self.beam_alpha;
+				flash.colormod = self.beam_color;
+				flash.scale = 0.5;
+				break;
+			}
+			case ARC_BT_HEAL:
+			{
+				self.beam_color = '0 1 0';
+				self.beam_alpha = 0.5;
+				self.beam_thickness = 8;
+				self.beam_traileffect = FALSE;
+				self.beam_hiteffect = particleeffectnum("healray_impact"); 
+				self.beam_hitlight[0] = 0;
+				self.beam_hitlight[1] = 1;
+				self.beam_hitlight[2] = 1;
+				self.beam_hitlight[3] = 1;
+				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
+				self.beam_muzzlelight[0] = 0;
+				self.beam_muzzlelight[1] = 1;
+				self.beam_muzzlelight[2] = 1;
+				self.beam_muzzlelight[3] = 1;
+				self.beam_image = "particles/lgbeam";
+				setmodel(flash, "models/flash.md3");
+				flash.alpha = self.beam_alpha;
+				flash.colormod = self.beam_color;
+				flash.scale = 0.5;
+				break;
+			}
+			case ARC_BT_HIT:
+			{
+				self.beam_color = '1 0 1';
+				self.beam_alpha = 0.5;
+				self.beam_thickness = 8;
+				self.beam_traileffect = particleeffectnum("nex_beam");
+				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
+				self.beam_hitlight[0] = 20;
+				self.beam_hitlight[1] = 1;
+				self.beam_hitlight[2] = 0;
+				self.beam_hitlight[3] = 0;
+				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
+				self.beam_muzzlelight[0] = 50;
+				self.beam_muzzlelight[1] = 1;
+				self.beam_muzzlelight[2] = 0;
+				self.beam_muzzlelight[3] = 0;
+				self.beam_image = "particles/lgbeam";
+				setmodel(flash, "models/flash.md3");
+				flash.alpha = self.beam_alpha;
+				flash.colormod = self.beam_color;
+				flash.scale = 0.5;
+				break;
+			}
+			case ARC_BT_BURST_MISS:
+			{
+				self.beam_color = '-1 -1 1';
+				self.beam_alpha = 0.5;
+				self.beam_thickness = 14;
+				self.beam_traileffect = FALSE;
+				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
+				self.beam_hitlight[0] = 0;
+				self.beam_hitlight[1] = 1;
+				self.beam_hitlight[2] = 1;
+				self.beam_hitlight[3] = 1;
+				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
+				self.beam_muzzlelight[0] = 0;
+				self.beam_muzzlelight[1] = 1;
+				self.beam_muzzlelight[2] = 1;
+				self.beam_muzzlelight[3] = 1;
+				self.beam_image = "particles/lgbeam";
+				setmodel(flash, "models/flash.md3");
+				flash.alpha = self.beam_alpha;
+				flash.colormod = self.beam_color;
+				flash.scale = 0.5;
+				break;
+			}
+			case ARC_BT_BURST_WALL:
+			{
+				self.beam_color = '0.5 0.5 1';
+				self.beam_alpha = 0.5;
+				self.beam_thickness = 14;
+				self.beam_traileffect = FALSE;
+				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
+				self.beam_hitlight[0] = 0;
+				self.beam_hitlight[1] = 1;
+				self.beam_hitlight[2] = 1;
+				self.beam_hitlight[3] = 1;
+				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
+				self.beam_muzzlelight[0] = 0;
+				self.beam_muzzlelight[1] = 1;
+				self.beam_muzzlelight[2] = 1;
+				self.beam_muzzlelight[3] = 1;
+				self.beam_image = "particles/lgbeam";
+				setmodel(flash, "models/flash.md3");
+				flash.alpha = self.beam_alpha;
+				flash.colormod = self.beam_color;
+				flash.scale = 0.5;
+				break;
+			}
+			case ARC_BT_BURST_HEAL:
+			{
+				self.beam_color = '0 1 0';
+				self.beam_alpha = 0.5;
+				self.beam_thickness = 14;
+				self.beam_traileffect = FALSE;
+				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
+				self.beam_hitlight[0] = 0;
+				self.beam_hitlight[1] = 1;
+				self.beam_hitlight[2] = 1;
+				self.beam_hitlight[3] = 1;
+				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
+				self.beam_muzzlelight[0] = 0;
+				self.beam_muzzlelight[1] = 1;
+				self.beam_muzzlelight[2] = 1;
+				self.beam_muzzlelight[3] = 1;
+				self.beam_image = "particles/lgbeam";
+				setmodel(flash, "models/flash.md3");
+				flash.alpha = self.beam_alpha;
+				flash.colormod = self.beam_color;
+				flash.scale = 0.5;
+				break;
+			}
+			case ARC_BT_BURST_HIT:
+			{
+				self.beam_color = '1 0 1';
+				self.beam_alpha = 0.5;
+				self.beam_thickness = 14;
+				self.beam_traileffect = FALSE;
+				self.beam_hiteffect = particleeffectnum("electro_lightning"); 
+				self.beam_hitlight[0] = 0;
+				self.beam_hitlight[1] = 1;
+				self.beam_hitlight[2] = 1;
+				self.beam_hitlight[3] = 1;
+				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
+				self.beam_muzzlelight[0] = 0;
+				self.beam_muzzlelight[1] = 1;
+				self.beam_muzzlelight[2] = 1;
+				self.beam_muzzlelight[3] = 1;
+				self.beam_image = "particles/lgbeam";
+				setmodel(flash, "models/flash.md3");
+				flash.alpha = self.beam_alpha;
+				flash.colormod = self.beam_color;
+				flash.scale = 0.5;
+				break;
+			}
+
+			// shouldn't be possible, but lets make it colorful if it does :D
+			default:
+			{
+				self.beam_color = randomvec();
+				self.beam_alpha = 1;
+				self.beam_thickness = 8;
+				self.beam_traileffect = FALSE;
+				self.beam_hiteffect = FALSE; 
+				self.beam_hitlight[0] = 0;
+				self.beam_hitlight[1] = 1;
+				self.beam_hitlight[2] = 1;
+				self.beam_hitlight[3] = 1;
+				self.beam_muzzleeffect = FALSE; //particleeffectnum("nex_muzzleflash");
+				self.beam_muzzlelight[0] = 0;
+				self.beam_muzzlelight[1] = 1;
+				self.beam_muzzlelight[2] = 1;
+				self.beam_muzzlelight[3] = 1;
+				self.beam_image = "particles/lgbeam";
+				setmodel(flash, "models/flash.md3");
+				flash.alpha = self.beam_alpha;
+				flash.colormod = self.beam_color;
+				flash.scale = 0.5;
+				break;
+			}
+		}
+	}
+
+	if(!self.beam_usevieworigin)
+	{
+		InterpolateOrigin_Note();
+	}
+}
+
 float W_Arc(float req)
 {
 	switch(req)
-- 
2.39.5