Index: code/fred2/sexp_tree.cpp
===================================================================
--- code/fred2/sexp_tree.cpp	(revision 11202)
+++ code/fred2/sexp_tree.cpp	(working copy)
@@ -2797,7 +2797,7 @@
 			return (sexp_variable_count() > 0) ? 1 : 0;
 
 		case OPF_SSM_CLASS:
-			return (Ssm_info_count > 0) ? 1 : 0;
+			return (Ssm_info.size() > 0) ? 1 : 0;
 
 		case OPF_MISSION_MOOD:
 			return Builtin_moods.empty() ? 0 : 1;
@@ -5058,12 +5058,12 @@
 
 sexp_list_item *sexp_tree::get_listing_opf_ssm_class()
 {
-	int i;
+	SCP_vector<ssm_info>::const_iterator it;
 	sexp_list_item head;
 
-	for (i=0; i<Ssm_info_count; i++)
+	for (it = Ssm_info.begin(); it != Ssm_info.end(); ++it)
 	{
-		head.add_data(Ssm_info[i].name);
+		head.add_data(it->name);
 	}
 
 	return head.next;
Index: code/hud/hudartillery.cpp
===================================================================
--- code/hud/hudartillery.cpp	(revision 11202)
+++ code/hud/hudartillery.cpp	(working copy)
@@ -13,6 +13,7 @@
 #include "hud/hudartillery.h"
 #include "parse/parselo.h"
 #include "weapon/weapon.h"
+#include "weapon/beam.h"
 #include "math/vecmat.h"
 #include "globalincs/linklist.h"
 #include "io/timer.h"
@@ -37,106 +38,155 @@
 // test code for subspace missile strike -------------------------------------------
 
 // ssm_info, like ship_info etc.
-int Ssm_info_count = 0;
-ssm_info Ssm_info[MAX_SSM_TYPES];
+SCP_vector<ssm_info> Ssm_info;
 
-// list of active/free strikes
-int Num_ssm_strikes = 0;
-ssm_strike Ssm_strikes[MAX_SSM_STRIKES];
-ssm_strike Ssm_free_list;
-ssm_strike Ssm_used_list;
+// list of active strikes
+SCP_list<ssm_strike> Ssm_strikes;
 
 // Goober5000
-int ssm_info_lookup(char *name)
+int ssm_info_lookup(const char *name)
 {
 	if(name == NULL)
 		return -1;
 
-	for (int i = 0; i < Ssm_info_count; i++)
-		if (!stricmp(name, Ssm_info[i].name))
-			return i;
+	for (SCP_vector<ssm_info>::const_iterator it = Ssm_info.begin(); it != Ssm_info.end(); ++it)
+		if (!stricmp(name, it->name))
+			return it - Ssm_info.begin();
 
 	return -1;
 }
 
-// game init
-void ssm_init()
-{	
-	int rval;
-	ssm_info bogus, *s;
+void parse_ssm(const char *filename)
+{
 	char weapon_name[NAME_LENGTH];
 
-	if ((rval = setjmp(parse_abort)) != 0) {
-		mprintf(("TABLES: Unable to parse '%s'!  Error code = %i.\n", "ssm.tbl", rval));
+	int err_code;
+	if ((err_code = setjmp(parse_abort)) != 0) {
+		mprintf(("TABLES: Unable to parse '%s'!  Error code = %i.\n", filename, err_code));
 		return;
 	}
 
-	read_file_text("ssm.tbl", CF_TYPE_TABLES);
+	read_file_text(filename, CF_TYPE_TABLES);
 	reset_parse();
 
 	// parse the table
-	Ssm_info_count = 0;
-	while(!optional_string("#end")){
-		// another ssm definition
-		if(optional_string("$SSM:")){
-			// pointer to info struct
-			if(Ssm_info_count >= MAX_SSM_TYPES){
-				s = &bogus;
-			} else {
-				s = &Ssm_info[Ssm_info_count];
-			}
+	while(required_string_either("#end", "$SSM:")) {
+		required_string("$SSM:");
+		ssm_info s;
 
-			// name
-			stuff_string(s->name, F_NAME, NAME_LENGTH);
+		// name
+		stuff_string(s.name, F_NAME, NAME_LENGTH);
 
-			// stuff data
-			required_string("+Weapon:");
-			stuff_string(weapon_name, F_NAME, NAME_LENGTH);
-			required_string("+Count:");
-			stuff_int(&s->count);
-			required_string("+WarpRadius:");
-			stuff_float(&s->warp_radius);
-			required_string("+WarpTime:");
-			stuff_float(&s->warp_time);
+		// stuff data
+		required_string("+Weapon:");
+		stuff_string(weapon_name, F_NAME, NAME_LENGTH);
+		if (optional_string("+Count:"))
+			stuff_int(&s.count);
+		else
+			s.count = 1;
+		required_string("+WarpRadius:");
+		stuff_float(&s.warp_radius);
+		if (optional_string("+WarpTime:")) {
+			stuff_float(&s.warp_time);
 			// According to fireballs.cpp, "Warp lifetime must be at least 4 seconds!"
-			if ( (s->warp_time) < 4.0f) {
+			if ( (s.warp_time) < 4.0f) {
 				// So let's warn them before they try to use it, shall we?
-				Warning(LOCATION, "Expected a '+WarpTime:' value equal or greater than 4.0, found '%f' in weapon '%s'.\n Setting to 4.0, please check and set to a number 4.0 or greater!\n", s->warp_time, weapon_name);
+				Warning(LOCATION, "Expected a '+WarpTime:' value equal or greater than 4.0, found '%f' in weapon '%s'.\n Setting to 4.0, please check and set to a number 4.0 or greater!\n", s.warp_time, weapon_name);
 				// And then make the Assert obsolete -- Zacam
-				s->warp_time = 4.0f;
+				s.warp_time = 4.0f;
 			}
-			required_string("+Radius:");
-			stuff_float(&s->radius);
-			required_string("+Offset:");
-			stuff_float(&s->offset);
-			if (optional_string("+HUD Message:")) 
-				stuff_boolean(&s->send_message);
-			else
-				s->send_message = true;
-			if (optional_string("+Custom Message:")) {
-				stuff_string(s->message, F_NAME, NAME_LENGTH);
-				s->use_custom_message = true;
+		} else {
+			s.warp_time = 4.0f;
+		}
+		required_string("+Radius:");
+		stuff_float(&s.radius);
+		if (optional_string("+Offset:"))
+			stuff_float(&s.offset);
+		else
+			s.offset = 0.0f;
+		if (optional_string("+Shape:")) {
+			switch(required_string_one_of(3, "Point", "Circle", "Sphere")) {
+			case 0:
+				required_string("Point");
+				s.shape = SSM_SHAPE_POINT;
+				break;
+			case 1:
+				required_string("Circle");
+			case -1:	// If we're ignoring parse errors and can't identify the shape, go with a circle.
+				s.shape = SSM_SHAPE_CIRCLE;
+				break;
+			case 2:
+				required_string("Sphere");
+				s.shape = SSM_SHAPE_SPHERE;
+				break;
+			default:
+				Assertion(false, "Impossible return value from required_string_one_of(); get a coder!\n");
 			}
-			parse_sound("+Alarm Sound:", &s->sound_index, s->name);
+		} else {
+			s.shape = SSM_SHAPE_CIRCLE;
+		}
+		if (optional_string("+HUD Message:")) 
+			stuff_boolean(&s.send_message);
+		else
+			s.send_message = true;
+		if (optional_string("+Custom Message:")) {
+			stuff_string(s.message, F_NAME, NAME_LENGTH);
+			s.use_custom_message = true;
+		} else {
+			s.use_custom_message = false;
+		}
+		s.sound_index = -1;
+		parse_sound("+Alarm Sound:", &s.sound_index, s.name);
 
-			// see if we have a valid weapon
-			s->weapon_info_index = -1;
-			s->weapon_info_index = weapon_info_lookup(weapon_name);
-			if(s->weapon_info_index >= 0){
-				// valid
-				Ssm_info_count++;
+		// see if we have a valid weapon
+		s.weapon_info_index = weapon_info_lookup(weapon_name);
+		if(s.weapon_info_index >= 0){
+			// valid
+			int existing = ssm_info_lookup(s.name);
+			if (existing >= 0) {	// Redefine the existing entry instead of adding a duplicate.
+				Ssm_info[existing] = s;
+			} else {
+				Ssm_info.push_back(s);
 			}
 		}
 	}
 }
 
+// game init
+void ssm_init()
+{	
+	if (cf_exists_full("ssm.tbl", CF_TYPE_TABLES)) {
+		mprintf(("TABLES => Starting parse of 'ssm.tbl'...\n"));
+		parse_ssm("ssm.tbl");
+	}
+	parse_modular_table(NOX("*-ssm.tbm"), parse_ssm);
+
+	// Now that we've populated Ssm_info, let's validate weapon $SSM: entries.
+	validate_SSM_entries();
+}
+
 void ssm_get_random_start_pos(vec3d *out, vec3d *start, matrix *orient, int ssm_index)
 {
 	vec3d temp;
 	ssm_info *s = &Ssm_info[ssm_index];
 
-	// get a random vector in the circle of the firing plane
-	vm_vec_random_in_circle(&temp, start, orient, s->radius, 1);
+	switch (s->shape) {
+	case SSM_SHAPE_SPHERE:
+		// get a random vector in a sphere around the target
+		vm_vec_random_in_sphere(&temp, start, orient, s->radius, 1);
+		break;
+	case SSM_SHAPE_CIRCLE:
+		// get a random vector in the circle of the firing plane
+		vm_vec_random_in_circle(&temp, start, orient, s->radius, 1);
+		break;
+	case SSM_SHAPE_POINT:
+		// boooring
+		vm_vec_scale_add(&temp, start, &orient->vec.fvec, s->radius);
+		break;
+	default:
+		Assertion(false, "Unknown shape '%d' in SSM type #%d ('%s'). This should not be possible; get a coder!\n", s->shape, ssm_index, s->name);
+		break;
+	}
 
 	// offset it a bit
 	vm_vec_scale_add(out, &temp, &orient->vec.fvec, s->offset);
@@ -145,32 +195,15 @@
 // level init
 void ssm_level_init()
 {
-	int i;
-
-	Num_ssm_strikes = 0;
-	list_init( &Ssm_free_list );
-	list_init( &Ssm_used_list );
-
-	// Link all object slots into the free list
-	for (i=0; i<MAX_SSM_STRIKES; i++)	{
-		list_append(&Ssm_free_list, &Ssm_strikes[i] );
-	}
 }
 
 // start a subspace missile effect
 void ssm_create(object *target, vec3d *start, int ssm_index, ssm_firing_info *override, int team)
 {	
-	ssm_strike *ssm;		
+	ssm_strike ssm;
 	matrix dir;
-	int idx;
+	int idx, count;
 
-	if (Num_ssm_strikes >= MAX_SSM_STRIKES ) {
-		#ifndef NDEBUG
-		mprintf(("Ssm creation failed - too many ssms!\n" ));
-		#endif
-		return;
-	}
-
 	// sanity
 	Assert(target != NULL);
 	if(target == NULL){
@@ -180,28 +213,17 @@
 	if(start == NULL){
 		return;
 	}
-	if((ssm_index < 0) || (ssm_index >= MAX_SSM_TYPES)){
+	if((ssm_index < 0) || ssm_index >= static_cast<int>(Ssm_info.size())){
 		return;
 	}
 
-	// Find next available trail
-	ssm = GET_FIRST(&Ssm_free_list);
-	Assert( ssm != &Ssm_free_list );		// shouldn't have the dummy element
+	// Init the ssm data
 
-	// remove trailp from the free list
-	list_remove( &Ssm_free_list, ssm );
-	
-	// insert trailp onto the end of used list
-	list_append( &Ssm_used_list, ssm );
+	count = Ssm_info[ssm_index].count;
 
-	// increment counter
-	Num_ssm_strikes++;	
-
-	// Init the ssm data
-
 	// override in multiplayer
 	if(override != NULL){
-		ssm->sinfo = *override;
+		ssm.sinfo = *override;
 	}
 	// single player or the server
 	else {
@@ -208,21 +230,34 @@
 		// forward orientation
 		vec3d temp;
 
-        vm_vec_sub(&temp, &target->pos, start);
-        vm_vec_normalize(&temp);
+		vm_vec_sub(&temp, &target->pos, start);
+		vm_vec_normalize(&temp);
 
 		vm_vector_2_matrix(&dir, &temp, NULL, NULL);
 
 		// stuff info
-		ssm->sinfo.ssm_index = ssm_index;
-		ssm->sinfo.target = target;
-        ssm->sinfo.ssm_team = team;
+		ssm.sinfo.ssm_index = ssm_index;
+		ssm.sinfo.target = target;
+		ssm.sinfo.ssm_team = team;
 
-		for(idx=0; idx<Ssm_info[ssm_index].count; idx++){
-			ssm->sinfo.delay_stamp[idx] = timestamp(200 + (int)frand_range(-199.0f, 1000.0f));
-			ssm_get_random_start_pos(&ssm->sinfo.start_pos[idx], start, &dir, ssm_index);
+		// Instead of pushing them on one at a time, let's just grab all the memory we'll need at once
+		// (as a side effect, don't need to change the old code from when these were arrays)
+		ssm.sinfo.delay_stamp.resize(count);
+		ssm.sinfo.start_pos.resize(count);
+
+		for(idx=0; idx<count; idx++){
+			ssm.sinfo.delay_stamp[idx] = timestamp(200 + (int)frand_range(-199.0f, 1000.0f));
+			ssm_get_random_start_pos(&ssm.sinfo.start_pos[idx], start, &dir, ssm_index);
 		}
 
+		ssm_info *si = &Ssm_info[ssm_index];
+		weapon_info *wip = &Weapon_info[si->weapon_info_index];
+		if (wip->wi_flags & WIF_BEAM) {
+			ssm.sinfo.duration = ((si->warp_time - ((wip->b_info.beam_warmup / 1000.0f) + wip->b_info.beam_life + (wip->b_info.beam_warmdown / 1000.0f))) / 2.0f) / si->warp_time;
+		} else {
+			ssm.sinfo.duration = 0.5f;
+		}
+
 		// if we're the server, send a packet
 		if(MULTIPLAYER_MASTER){
 			//
@@ -229,11 +264,10 @@
 		}
 	}
 
-	// clear timestamps, handles, etc
-	for(idx=0; idx<MAX_SSM_COUNT; idx++){
-		ssm->done_flags[idx] = 0;
-		ssm->fireballs[idx] = -1;
-	}
+	ssm.done_flags.clear();
+	ssm.done_flags.resize(count);
+	ssm.fireballs.clear();
+	ssm.fireballs.resize(count, -1);
 	
 	if(Ssm_info[ssm_index].send_message) {
 		if (!Ssm_info[ssm_index].use_custom_message)
@@ -244,21 +278,13 @@
 	if (Ssm_info[ssm_index].sound_index >= 0) {
 		snd_play(&Snds[Ssm_info[ssm_index].sound_index]);
 	}
+	Ssm_strikes.push_back(ssm);
 }
 
 // delete a finished ssm effect
-void ssm_delete(ssm_strike *ssm)
+void ssm_delete(SCP_list<ssm_strike>::iterator ssm)
 {
-	// remove objp from the used list
-	list_remove( &Ssm_used_list, ssm );
-
-	// add objp to the end of the free
-	list_append( &Ssm_free_list, ssm );
-
-	// decrement counter
-	Num_ssm_strikes--;
-
-	nprintf(("General", "Recycling SSM, %d left", Num_ssm_strikes));
+	Ssm_strikes.erase(ssm);
 }
 
 // process subspace missile stuff
@@ -265,13 +291,13 @@
 void ssm_process()
 {
 	int idx, finished;
-	ssm_strike *moveup, *next_one;
+	SCP_list<ssm_strike>::iterator moveup, eraser;
 	ssm_info *si;
-    int weapon_objnum;
+	int weapon_objnum;
 	
 	// process all strikes	
-	moveup=GET_FIRST(&Ssm_used_list);
-	while ( moveup!=END_OF_LIST(&Ssm_used_list) )	{		
+	moveup = Ssm_strikes.begin();
+	while ( moveup != Ssm_strikes.end() ) {
 		// get the type
 		if(moveup->sinfo.ssm_index < 0){
 			continue;
@@ -287,29 +313,50 @@
 
 				// if he already has the fireball effect
 				if(moveup->fireballs[idx] >= 0){
-					// if the warp effect is half done, fire the missile
-					if((1.0f - fireball_lifeleft_percent(&Objects[moveup->fireballs[idx]])) >= 0.5f){
-						// get an orientation
-						vec3d temp;
-						matrix orient;
+					if ((1.0f - fireball_lifeleft_percent(&Objects[moveup->fireballs[idx]])) >= moveup->sinfo.duration) {
+						weapon_info *wip = &Weapon_info[si->weapon_info_index];
+						// are we a beam? -MageKing17
+						if (wip->wi_flags & WIF_BEAM) {
+							beam_fire_info fire_info;
+							memset(&fire_info, 0, sizeof(beam_fire_info));
 
-						vm_vec_sub(&temp, &moveup->sinfo.target->pos, &moveup->sinfo.start_pos[idx]);
-						vm_vec_normalize(&temp);
-						vm_vector_2_matrix(&orient, &temp, NULL, NULL);
+							fire_info.accuracy = 0.000001f;		// this will guarantee a hit
+							fire_info.shooter = NULL;
+							fire_info.turret = NULL;
+							fire_info.target = moveup->sinfo.target;
+							fire_info.target_subsys = NULL;
+							fire_info.bfi_flags |= BFIF_FLOATING_BEAM;
+							fire_info.starting_pos = moveup->sinfo.start_pos[idx];
+							fire_info.beam_info_index = si->weapon_info_index;
+							fire_info.team = static_cast<char>(moveup->sinfo.ssm_team);
 
-						// fire the missile and flash the screen
-						weapon_objnum = weapon_create(&moveup->sinfo.start_pos[idx], &orient, si->weapon_info_index, -1, -1, 1);
+							// fire the beam
+							beam_fire(&fire_info);
 
-						if (weapon_objnum >= 0) {
-							Weapons[Objects[weapon_objnum].instance].team = moveup->sinfo.ssm_team;
-							Weapons[Objects[weapon_objnum].instance].homing_object = moveup->sinfo.target;
-							Weapons[Objects[weapon_objnum].instance].target_sig = moveup->sinfo.target->signature;
+							moveup->done_flags[idx] = true;
+						} else {
+							// get an orientation
+							vec3d temp;
+							matrix orient;
+
+							vm_vec_sub(&temp, &moveup->sinfo.target->pos, &moveup->sinfo.start_pos[idx]);
+							vm_vec_normalize(&temp);
+							vm_vector_2_matrix(&orient, &temp, NULL, NULL);
+
+							// fire the missile and flash the screen
+							weapon_objnum = weapon_create(&moveup->sinfo.start_pos[idx], &orient, si->weapon_info_index, -1, -1, 1);
+
+							if (weapon_objnum >= 0) {
+								Weapons[Objects[weapon_objnum].instance].team = moveup->sinfo.ssm_team;
+								Weapons[Objects[weapon_objnum].instance].homing_object = moveup->sinfo.target;
+								Weapons[Objects[weapon_objnum].instance].target_sig = moveup->sinfo.target->signature;
+							}
+
+							// this makes this particular missile done
+							moveup->done_flags[idx] = true;
 						}
-
-						// this makes this particular missile done
-						moveup->done_flags[idx] = 1;
 					}
-				} 
+				}
 				// maybe create his warpin effect
 				else if((moveup->sinfo.delay_stamp[idx] >= 0) && timestamp_elapsed(moveup->sinfo.delay_stamp[idx])){
 					// get an orientation
@@ -324,14 +371,14 @@
 			}
 		}
 		if(finished){
-			next_one = GET_NEXT(moveup);			
-			ssm_delete(moveup);															
-			moveup = next_one;
+			eraser = moveup;
+			++moveup;
+			ssm_delete(eraser);
 			continue;
 		}
 		
-		moveup=GET_NEXT(moveup);
-	}	
+		++moveup;
+	}
 }
 
 
Index: code/hud/hudartillery.h
===================================================================
--- code/hud/hudartillery.h	(revision 11202)
+++ code/hud/hudartillery.h	(working copy)
@@ -18,10 +18,12 @@
 // -----------------------------------------------------------------------------------------------------------------------
 // ARTILLERY DEFINES/VARS
 //
-#define MAX_SSM_TYPES			10
-#define MAX_SSM_STRIKES			10
-#define MAX_SSM_COUNT			10
 
+// SSM shapes
+#define SSM_SHAPE_POINT			0
+#define SSM_SHAPE_CIRCLE		1
+#define SSM_SHAPE_SPHERE		2
+
 // global ssm types
 typedef struct ssm_info {
 	char			name[NAME_LENGTH];				// strike name
@@ -35,32 +37,31 @@
 	bool		use_custom_message;
 	bool		send_message;
 	int			sound_index;
+	int			shape;
 } ssm_info;
 
 // creation info for the strike (useful for multiplayer)
 typedef struct ssm_firing_info {
-	int     delay_stamp[MAX_SSM_COUNT];	    // timestamps
-	vec3d   start_pos[MAX_SSM_COUNT];       // start positions
+	SCP_vector<int>     delay_stamp;	    // timestamps
+	SCP_vector<vec3d>   start_pos;       // start positions
 	
 	int             ssm_index;							// index info ssm_info array
 	class object*  target;								// target for the strike
     int             ssm_team;                           // team that fired the ssm.
+	float			duration;							// how far into the warp effect to fire
 } ssm_firing_info;
 
 // the strike itself
 typedef struct ssm_strike {
-	int			fireballs[MAX_SSM_COUNT];		// warpin effect fireballs
-	int			done_flags[MAX_SSM_COUNT];		// when we've fired off the individual missiles
+	SCP_vector<int>			fireballs;		// warpin effect fireballs
+	SCP_vector<bool>		done_flags;		// when we've fired off the individual missiles
 	
 	// this is the info that controls how the strike behaves (just like for beam weapons)
 	ssm_firing_info		sinfo;
-
-	ssm_strike	*next, *prev;						// for list
 } ssm_strike;
 
 
-extern int Ssm_info_count;
-extern ssm_info Ssm_info[MAX_SSM_TYPES];
+extern SCP_vector<ssm_info> Ssm_info;
 
 
 // -----------------------------------------------------------------------------------------------------------------------
@@ -80,6 +81,6 @@
 void ssm_create(object *target, vec3d *start, int ssm_index, ssm_firing_info *override, int team);
 
 // Goober5000
-extern int ssm_info_lookup(char *name);
+extern int ssm_info_lookup(const char *name);
 
 #endif
Index: code/math/vecmat.cpp
===================================================================
--- code/math/vecmat.cpp	(revision 11202)
+++ code/math/vecmat.cpp	(working copy)
@@ -2735,7 +2735,7 @@
 
 // given a start vector, an orientation and a radius, give a point on the plane of the circle
 // if on_edge is 1, the point is on the very edge of the circle
-void vm_vec_random_in_circle(vec3d *out, vec3d *in, matrix *orient, float radius, int on_edge)
+void vm_vec_random_in_circle(vec3d *out, vec3d *in, matrix *orient, const float radius, const int on_edge)
 {
 	vec3d temp;
 
@@ -2746,6 +2746,15 @@
 	vm_rot_point_around_line(out, &temp, fl_radians(frand_range(0.0f, 359.0f)), in, &orient->vec.fvec);
 }
 
+// given a start vector, an orientation, and a radius, give a point in a spherical volume
+// if on_edge is 1, the point is on the very edge of the sphere
+void vm_vec_random_in_sphere(vec3d *out, vec3d *in, matrix *orient, const float radius, const int on_edge)
+{
+	vec3d temp;
+	vm_vec_random_in_circle(&temp, in, orient, radius, on_edge);
+	vm_rot_point_around_line(out, &temp, fl_radians(frand_range(0.0f, 359.0f)), in, &orient->vec.rvec);
+}
+
 // find the nearest point on the line to p. if dist is non-NULL, it is filled in
 // returns 0 if the point is inside the line segment, -1 if "before" the line segment and 1 ir "after" the line segment
 int vm_vec_dist_to_line(vec3d *p, vec3d *l0, vec3d *l1, vec3d *nearest, float *dist)
Index: code/math/vecmat.h
===================================================================
--- code/math/vecmat.h	(revision 11202)
+++ code/math/vecmat.h	(working copy)
@@ -502,8 +502,12 @@
 
 // given a start vector, an orientation and a radius, give a point on the plane of the circle
 // if on_edge is 1, the point is on the very edge of the circle
-void vm_vec_random_in_circle(vec3d *out, vec3d *in, matrix *orient, float radius, int on_edge);
+void vm_vec_random_in_circle(vec3d *out, vec3d *in, matrix *orient, const float radius, const int on_edge);
 
+// given a start vector, an orientation, and a radius, give a point in a spherical volume
+// if on_edge is 1, the point is on the very edge of the sphere
+void vm_vec_random_in_sphere(vec3d *out, vec3d *in, matrix *orient, const float radius, const int on_edge);
+
 // find the nearest point on the line to p. if dist is non-NULL, it is filled in
 // returns 0 if the point is inside the line segment, -1 if "before" the line segment and 1 ir "after" the line segment
 int vm_vec_dist_to_line(vec3d *p, vec3d *l0, vec3d *l1, vec3d *nearest, float *dist);
Index: code/parse/sexp.cpp
===================================================================
--- code/parse/sexp.cpp	(revision 11202)
+++ code/parse/sexp.cpp	(working copy)
@@ -479,6 +479,7 @@
 	//Beams and Turrets Sub-Category
 	{ "fire-beam",						OP_BEAM_FIRE,							3,	5,			SEXP_ACTION_OPERATOR,	},
 	{ "fire-beam-at-coordinates",		OP_BEAM_FIRE_COORDS,					5,	9,			SEXP_ACTION_OPERATOR,	},
+	{ "beam-create",					OP_BEAM_FLOATING_FIRE,					7,	14,			SEXP_ACTION_OPERATOR,	},
 	{ "beam-free",						OP_BEAM_FREE,							2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
 	{ "beam-free-all",					OP_BEAM_FREE_ALL,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
 	{ "beam-lock",						OP_BEAM_LOCK,							2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
@@ -16943,6 +16944,100 @@
 	}
 }	
 
+void sexp_beam_floating_fire(int n)
+{
+	int sindex;
+	beam_fire_info fire_info;
+	memset(&fire_info, 0, sizeof(beam_fire_info));
+	fire_info.accuracy = 0.000001f;							// this will guarantee a hit
+	fire_info.bfi_flags |= BFIF_FLOATING_BEAM;
+	fire_info.turret = NULL;		// A free-floating beam isn't fired from a subsystem.
+
+	fire_info.beam_info_index = weapon_info_lookup(CTEXT(n));
+	n = CDR(n);
+	if (fire_info.beam_info_index < 0)
+	{
+		Warning(LOCATION, "Invalid weapon class passed to beam-create; weapon type '%s' does not exist!\n", CTEXT(n));
+		return;
+	}
+	if (!(Weapon_info[fire_info.beam_info_index].wi_flags & WIF_BEAM)) {
+		Warning(LOCATION, "Invalid weapon class passed to beam-create; weapon type '%s' is not a beam!\n", CTEXT(n));
+		return;
+	}
+
+	fire_info.shooter = NULL;
+	if (stricmp(CTEXT(n), SEXP_NONE_STRING))
+	{
+		sindex = ship_name_lookup(CTEXT(n));
+
+		if (sindex >= 0)
+			fire_info.shooter = &Objects[Ships[sindex].objnum];
+	}
+	n = CDR(n);
+
+	fire_info.team = static_cast<char>(iff_lookup(CTEXT(n)));
+	n = CDR(n);
+
+	fire_info.starting_pos.xyz.x = static_cast<float>(eval_num(n));
+	n = CDR(n);
+	fire_info.starting_pos.xyz.y = static_cast<float>(eval_num(n));
+	n = CDR(n);
+	fire_info.starting_pos.xyz.z = static_cast<float>(eval_num(n));
+	n = CDR(n);
+
+	fire_info.target = NULL;
+	fire_info.target_subsys = NULL;
+
+	sindex = -1;
+	if (stricmp(CTEXT(n), SEXP_NONE_STRING))
+	{
+		sindex = ship_name_lookup(CTEXT(n));
+
+		if (sindex >= 0)
+			fire_info.target = &Objects[Ships[sindex].objnum];
+	} else {
+		fire_info.bfi_flags |= BFIF_TARGETING_COORDS;
+	}
+	n = CDR(n);
+
+	if (n >= 0 && stricmp(CTEXT(n), SEXP_NONE_STRING))
+	{
+		if (sindex >= 0)
+			fire_info.target_subsys = ship_get_subsys(&Ships[sindex], CTEXT(n));
+
+		n = CDR(n);
+	}
+
+	if (n >= 0) {
+		fire_info.target_pos1.xyz.x = fire_info.target_pos2.xyz.x = static_cast<float>(eval_num(n));
+		n = CDR(n);
+	}
+	if (n >= 0) {
+		fire_info.target_pos1.xyz.y = fire_info.target_pos2.xyz.y = static_cast<float>(eval_num(n));
+		n = CDR(n);
+	}
+	if (n >= 0) {
+		fire_info.target_pos1.xyz.z = fire_info.target_pos2.xyz.z = static_cast<float>(eval_num(n));
+		n = CDR(n);
+	}
+
+	if (n >= 0) {
+		fire_info.target_pos2.xyz.x = static_cast<float>(eval_num(n));
+		n = CDR(n);
+	}
+	if (n >= 0) {
+		fire_info.target_pos2.xyz.y = static_cast<float>(eval_num(n));
+		n = CDR(n);
+	}
+	if (n >= 0) {
+		fire_info.target_pos2.xyz.z = static_cast<float>(eval_num(n));
+		n = CDR(n);
+	}
+
+	// create the weapon
+	beam_fire(&fire_info);
+}
+
 void sexp_beam_free(int node)
 {
 	int sindex;
@@ -25402,6 +25497,7 @@
 		case OP_SET_VARIABLE_BY_INDEX:
 		case OP_BEAM_FIRE:
 		case OP_BEAM_FIRE_COORDS:
+		case OP_BEAM_FLOATING_FIRE:
 		case OP_BEAM_FREE:
 		case OP_BEAM_FREE_ALL:
 		case OP_BEAM_LOCK:
@@ -26939,6 +27035,18 @@
 					return OPF_NUMBER;
 			}
 
+		case OP_BEAM_FLOATING_FIRE:
+			if (argnum == 0)
+				return OPF_WEAPON_NAME;
+			else if (argnum == 1 || argnum == 6)
+				return OPF_SHIP_OR_NONE;
+			else if (argnum == 2)
+				return OPF_IFF;
+			else if (argnum == 7)
+				return OPF_SUBSYSTEM_OR_NONE;
+			else
+				return OPF_NUMBER;
+
 		case OP_IS_TAGGED:
 			return OPF_SHIP;
 
@@ -29231,6 +29339,7 @@
 
 		case OP_BEAM_FIRE:
 		case OP_BEAM_FIRE_COORDS:
+		case OP_BEAM_FLOATING_FIRE:
 		case OP_BEAM_FREE:
 		case OP_BEAM_FREE_ALL:
 		case OP_BEAM_LOCK:
@@ -31843,6 +31952,23 @@
 		"\t8:\tsecond y coordinate to be targeted (optional; only used for slash beams)\r\n"
 		"\t9:\tsecond z coordinate to be targeted (optional; only used for slash beams)\r\n" },
 
+	{ OP_BEAM_FLOATING_FIRE, "beam-create\r\n"
+		"\tFire a beam weapon from the specified coordinates to the specified target. Not compatible with multiplayer.\r\n"
+		"\t1:\tBeam weapon to fire\r\n"
+		"\t2:\tParent ship (for kill credit, if applicable; can be none)\r\n"
+		"\t3:\tTeam for this beam to be on (related to difficulty-based damage)\r\n"
+		"\t4:\tX coordinate to fire from\r\n"
+		"\t5:\tY coordinate to fire from\r\n"
+		"\t6:\tZ coordinate to fire from\r\n"
+		"\t7:\tTarget ship (can be none)\r\n"
+		"\t8:\tTarget subsystem (optional, can be none)\r\n"
+		"\t9:\tX coordinate to fire at (optional)\r\n"
+		"\t10:\tY coordinate to fire at (optional)\r\n"
+		"\t11:\tZ coordinate to fire at (optional)\r\n"
+		"\t12:\tSecond X coordinate to fire at (optional; used for slash beams)\r\n"
+		"\t13:\tSecond Y coordinate to fire at (optional; used for slash beams)\r\n"
+		"\t14:\tSecond Z coordinate to fire at (optional; used for slash beams)\r\n" },
+
 	{ OP_IS_TAGGED, "is-tagged\r\n"
 		"\tReturns whether a given ship is tagged or not\r\n"},
 
Index: code/parse/sexp.h
===================================================================
--- code/parse/sexp.h	(revision 11202)
+++ code/parse/sexp.h	(working copy)
@@ -726,6 +726,7 @@
 #define OP_HUD_SET_RETAIL_GAUGE_ACTIVE		(0x0027 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) 	// The E, just revamped a bit by Axem
 #define OP_SCRIPT_EVAL_MULTI				(0x0028 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG)	// Karajorma
 #define OP_PAUSE_SOUND_FROM_FILE			(0x0029 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG)	// Goober5000
+#define OP_BEAM_FLOATING_FIRE				(0x002a | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG)	// MageKing17
 
 // defined for AI goals
 #define OP_AI_CHASE							(0x0000 | OP_CATEGORY_AI | OP_NONCAMPAIGN_FLAG)
Index: code/weapon/beam.cpp
===================================================================
--- code/weapon/beam.cpp	(revision 11202)
+++ code/weapon/beam.cpp	(working copy)
@@ -286,7 +286,7 @@
 {
 	beam *new_item;
 	weapon_info *wip;
-	ship *firing_ship;
+	ship *firing_ship = NULL;
 	int objnum;		
 
 	// sanity check
@@ -311,18 +311,20 @@
 	}
 
 	// make sure the beam_info_index is valid
-	Assert((fire_info->beam_info_index >= 0) && (fire_info->beam_info_index < MAX_WEAPON_TYPES) && (Weapon_info[fire_info->beam_info_index].wi_flags & WIF_BEAM));
 	if((fire_info->beam_info_index < 0) || (fire_info->beam_info_index >= MAX_WEAPON_TYPES) || !(Weapon_info[fire_info->beam_info_index].wi_flags & WIF_BEAM)){
+		Assertion(false, "beam_info_index (%d) invalid (either <0 or >= %d or not actually a beam)!\n", fire_info->beam_info_index, MAX_WEAPON_TYPES);
 		return -1;
 	}
 
 	wip = &Weapon_info[fire_info->beam_info_index];	
 	// make sure a ship is firing this
-	Assert((fire_info->shooter->type == OBJ_SHIP) && (fire_info->shooter->instance >= 0) && (fire_info->shooter->instance < MAX_SHIPS));
-	if ( (fire_info->shooter->type != OBJ_SHIP) || (fire_info->shooter->instance < 0) || (fire_info->shooter->instance >= MAX_SHIPS) ) {
+	if (!(fire_info->bfi_flags & BFIF_FLOATING_BEAM) && ((fire_info->shooter->type != OBJ_SHIP) || (fire_info->shooter->instance < 0) || (fire_info->shooter->instance >= MAX_SHIPS)) ) {
+		Assertion(false, "Non-floating beam not fired from a valid ship!\n");
 		return -1;
 	}
-	firing_ship = &Ships[fire_info->shooter->instance];
+	if (fire_info->shooter != NULL) {
+		firing_ship = &Ships[fire_info->shooter->instance];
+	}
 
 	// get a free beam
 	new_item = GET_FIRST(&Beam_free_list);
@@ -352,7 +354,7 @@
 	new_item->warmdown_stamp = -1;
 	new_item->weapon_info_index = fire_info->beam_info_index;	
 	new_item->objp = fire_info->shooter;
-	new_item->sig = fire_info->shooter->signature;
+	new_item->sig = (fire_info->shooter != NULL) ? fire_info->shooter->signature : 0;
 	new_item->subsys = fire_info->turret;	
 	new_item->life_left = wip->b_info.beam_life;	
 	new_item->life_total = wip->b_info.beam_life;
@@ -368,19 +370,22 @@
 	new_item->flags = 0;
 	new_item->shot_index = 0;
 	new_item->shrink = 1.0f;	
-	new_item->team = (char)firing_ship->team;
+	new_item->team = (firing_ship == NULL) ? fire_info->team : static_cast<char>(firing_ship->team);
 	new_item->range = wip->b_info.range;
 	new_item->damage_threshold = wip->b_info.damage_threshold;
 	new_item->bank = fire_info->bank;
 	new_item->Beam_muzzle_stamp = -1;
 	new_item->beam_glow_frame = 0.0f;
-	new_item->firingpoint = fire_info->turret->turret_next_fire_pos;
+	new_item->firingpoint = (fire_info->bfi_flags & BFIF_FLOATING_BEAM) ? -1 : fire_info->turret->turret_next_fire_pos;
 	new_item->beam_width = wip->b_info.beam_width;
+	new_item->last_start = fire_info->starting_pos;
 
 	if (fire_info->bfi_flags & BFIF_FORCE_FIRING)
 		new_item->flags |= BF_FORCE_FIRING;
 	if (fire_info->bfi_flags & BFIF_IS_FIGHTER_BEAM)
 		new_item->flags |= BF_IS_FIGHTER_BEAM;
+	if (fire_info->bfi_flags & BFIF_FLOATING_BEAM)
+		new_item->flags |= BF_FLOATING_BEAM;
 
 	if (fire_info->bfi_flags & BFIF_TARGETING_COORDS) {
 		new_item->flags |= BF_TARGETING_COORDS;
@@ -392,7 +397,7 @@
 	}
 
 	for (int i = 0; i < MAX_BEAM_SECTIONS; i++)
-		new_item->beam_secion_frame[i] = 0.0f;
+		new_item->beam_section_frame[i] = 0.0f;
 	
 	if (fire_info->bfi_flags & BFIF_IS_FIGHTER_BEAM) {
 		new_item->type = BEAM_TYPE_C;
@@ -427,7 +432,7 @@
 	}	
 
 	// create the associated object
-	objnum = obj_create(OBJ_BEAM, OBJ_INDEX(fire_info->shooter), new_item - Beams, &vmd_identity_matrix, &vmd_zero_vector, 1.0f, OF_COLLIDES);
+	objnum = obj_create(OBJ_BEAM, ((fire_info->shooter != NULL) ? OBJ_INDEX(fire_info->shooter) : -1), new_item - Beams, &vmd_identity_matrix, &vmd_zero_vector, 1.0f, OF_COLLIDES);
 	if(objnum < 0){
 		beam_delete(new_item);
 		nprintf(("General", "obj_create() failed for beam weapon! bah!\n"));
@@ -590,7 +595,6 @@
 	}
 	b = &Beams[bm->instance];
 
-	Assert(b->objp != NULL);
 	if(b->objp == NULL){
 		return -1;
 	}
@@ -734,7 +738,8 @@
 
 	// LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN
 	// get the "originating point" of the beam for this frame. essentially bashes last_start
-	beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &temp2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
+	if (b->subsys != NULL)
+		beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &temp2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
 
 	// if the "warming up" timestamp has not expired
 	if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
@@ -758,7 +763,8 @@
 
 	// LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN
 	// get the "originating point" of the beam for this frame. essentially bashes last_start
-	beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &temp2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
+	if (b->subsys != NULL)
+		beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &temp2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
 
 	// if the "warming up" timestamp has not expired
 	if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
@@ -825,7 +831,8 @@
 
 	// LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN
 	// get the "originating point" of the beam for this frame. essentially bashes last_start
-	beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &temp2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
+	if (b->subsys != NULL)
+		beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &temp2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
 
 	// if the "warming up" timestamp has not expired
 	if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){
@@ -879,8 +886,12 @@
 // type e functions
 void beam_type_e_move(beam *b)
 {
-	vec3d temp, turret_norm;	
+	vec3d temp, turret_norm;
 
+	if (b->subsys == NULL) {	// If we're a free-floating beam, there's nothing to calculate here.
+		return;
+	}
+
 	// LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN
 	// get the "originating point" of the beam for this frame. essentially bashes last_start
 	beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &turret_norm, 1, &temp, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
@@ -911,7 +922,7 @@
 		b = moveup;
 
 		// check if parent object has died, if so then delete beam
-		if (b->objp->type == OBJ_NONE) {
+		if (b->objp != NULL && b->objp->type == OBJ_NONE) {
 			// set next beam
 			moveup = GET_NEXT(moveup);
 			// delete current beam
@@ -925,10 +936,13 @@
 
 		if ( !physics_paused ) {
 			// make sure to check that firingpoint is still properly set
-			int temp = b->subsys->turret_next_fire_pos;
+			int temp = -1;
+			if (b->subsys != NULL) {
+				temp = b->subsys->turret_next_fire_pos;
 
-			if (!(b->flags & BF_IS_FIGHTER_BEAM))
-				b->subsys->turret_next_fire_pos = b->firingpoint;
+				if (!(b->flags & BF_IS_FIGHTER_BEAM))
+					b->subsys->turret_next_fire_pos = b->firingpoint;
+			}
 
 			// move the beam
 			switch (b->type)
@@ -962,7 +976,9 @@
 				default :
 					Int3();
 			}
-			b->subsys->turret_next_fire_pos = temp;
+			if (b->subsys != NULL) {
+				b->subsys->turret_next_fire_pos = temp;
+			}
 		}
 
 		// next
@@ -994,8 +1010,10 @@
 			if(bf_status < 0){
 				beam_delete(moveup);
 			} else {
-				// add a muzzle light for the shooter
-				beam_add_light(moveup, OBJ_INDEX(moveup->objp), 0, NULL);
+				if (moveup->objp != NULL) {
+					// add a muzzle light for the shooter
+					beam_add_light(moveup, OBJ_INDEX(moveup->objp), 0, NULL);
+				}
 
 				// if the warming up timestamp has expired, start firing
 				if(timestamp_elapsed(moveup->warmup_stamp)){							
@@ -1018,8 +1036,10 @@
 			if(bf_status < 0){
 				beam_delete(moveup);
 			} else {
-				// add a muzzle light for the shooter
-				beam_add_light(moveup, OBJ_INDEX(moveup->objp), 0, NULL);
+				if (moveup->objp != NULL) {
+					// add a muzzle light for the shooter
+					beam_add_light(moveup, OBJ_INDEX(moveup->objp), 0, NULL);
+				}
 
 				// if we're done warming down, the beam is finished
 				if(timestamp_elapsed(moveup->warmdown_stamp)){
@@ -1033,8 +1053,10 @@
 		}
 		// otherwise, we're firing away.........		
 
-		// add a muzzle light for the shooter
-		beam_add_light(moveup, OBJ_INDEX(moveup->objp), 0, NULL);
+		if (moveup->objp != NULL) {
+			// add a muzzle light for the shooter
+			beam_add_light(moveup, OBJ_INDEX(moveup->objp), 0, NULL);
+		}
 
 		// subtract out the life left for the beam
 		if(!physics_paused){
@@ -1055,7 +1077,7 @@
 		}		
 
 		// add tube light for the beam
-		if(Use_GLSL > 1)
+		if(Use_GLSL > 1 && moveup->objp != NULL)
 			beam_add_light(moveup, OBJ_INDEX(moveup->objp), 1, NULL);
 
 		// stop shooting?
@@ -1229,18 +1251,18 @@
 		int framenum = 0;
 
 		if (bwsi->texture.num_frames > 1) {
-			b->beam_secion_frame[s_idx] += flFrametime;
+			b->beam_section_frame[s_idx] += flFrametime;
 
 			// Sanity checks
-			if (b->beam_secion_frame[s_idx] < 0.0f)
-				b->beam_secion_frame[s_idx] = 0.0f;
-			if (b->beam_secion_frame[s_idx] > 100.0f)
-				b->beam_secion_frame[s_idx] = 0.0f;
+			if (b->beam_section_frame[s_idx] < 0.0f)
+				b->beam_section_frame[s_idx] = 0.0f;
+			if (b->beam_section_frame[s_idx] > 100.0f)
+				b->beam_section_frame[s_idx] = 0.0f;
 
-			while (b->beam_secion_frame[s_idx] > bwsi->texture.total_time)
-				b->beam_secion_frame[s_idx] -= bwsi->texture.total_time;
+			while (b->beam_section_frame[s_idx] > bwsi->texture.total_time)
+				b->beam_section_frame[s_idx] -= bwsi->texture.total_time;
 
-			framenum = fl2i( (b->beam_secion_frame[s_idx] * bwsi->texture.num_frames) / bwsi->texture.total_time );
+			framenum = fl2i( (b->beam_section_frame[s_idx] * bwsi->texture.num_frames) / bwsi->texture.total_time );
 
 			CLAMP(framenum, 0, bwsi->texture.num_frames-1);
 		}
@@ -1298,7 +1320,11 @@
 
 	// get turret info - position and normal
 	turret_pos = b->last_start;
-	turret_norm = b->subsys->system_info->turret_norm;	
+	if (b->subsys != NULL) {
+		turret_norm = b->subsys->system_info->turret_norm;
+	} else {
+		vm_vec_normalized_dir(&turret_norm, &b->last_shot, &b->last_start);
+	}
 
 	// randomly perturb a vector within a cone around the normal
 	vm_vector_2_matrix(&m, &turret_norm, NULL, NULL);
@@ -1312,7 +1338,9 @@
 		float p_life = frand_range(p_time_ref * 0.5f, p_time_ref * 0.7f);
 		float p_vel = (wip->b_info.beam_muzzle_radius / p_life) * frand_range(0.85f, 1.2f);
 		vm_vec_scale(&particle_dir, -p_vel);
-		vm_vec_add2(&particle_dir, &b->objp->phys_info.vel);	//move along with our parent
+		if (b->objp != NULL) {
+			vm_vec_add2(&particle_dir, &b->objp->phys_info.vel);	//move along with our parent
+		}
 
 		memset(&pinfo, 0, sizeof(particle_info));
 		pinfo.pos = particle_pos;
@@ -1933,15 +1961,25 @@
 	beam_weapon_info *bwi;
 	float miss_factor;
 
-	int temp = b->subsys->turret_next_fire_pos;
+	if (b->subsys != NULL) {
+		int temp = b->subsys->turret_next_fire_pos;
 
-	if (!(b->flags & BF_IS_FIGHTER_BEAM))
-		b->subsys->turret_next_fire_pos = b->firingpoint;
+		if (!(b->flags & BF_IS_FIGHTER_BEAM))
+			b->subsys->turret_next_fire_pos = b->firingpoint;
 
-	// where the shot is originating from (b->last_start gets filled in)
-	beam_get_global_turret_gun_info(b->objp, b->subsys, &turret_point, &turret_norm, 1, &p2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
+		// where the shot is originating from (b->last_start gets filled in)
+		beam_get_global_turret_gun_info(b->objp, b->subsys, &turret_point, &turret_norm, 1, &p2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
 
-	b->subsys->turret_next_fire_pos = temp;
+		b->subsys->turret_next_fire_pos = temp;
+	} else {
+		turret_point = b->last_start;
+		if (b->flags & BF_TARGETING_COORDS) {
+			p2 = b->target_pos1;
+		} else {
+			p2 = b->target->pos;
+		}
+		vm_vec_normalized_dir(&turret_norm, &p2, &turret_point);
+	}
 
 	// get a model # to work with
 	model_num = beam_get_model(b->target);
@@ -2065,23 +2103,27 @@
 		}
 	}
 
-	int temp_int = b->subsys->turret_next_fire_pos;
+	if (b->subsys != NULL && b->type != BEAM_TYPE_C) {	// Type C beams don't use this information.
+		int temp_int = b->subsys->turret_next_fire_pos;
 
-	if (!(b->flags & BF_IS_FIGHTER_BEAM))
-		b->subsys->turret_next_fire_pos = b->firingpoint;
+		if (!(b->flags & BF_IS_FIGHTER_BEAM))
+			b->subsys->turret_next_fire_pos = b->firingpoint;
 
-	// setup our initial shot point and aim direction
-	switch(b->type){
-	case BEAM_TYPE_A:	
 		// where the shot is originating from (b->last_start gets filled in)
 		beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &p2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
 
+		b->subsys->turret_next_fire_pos = temp_int;
+	}
+
+	// setup our initial shot point and aim direction
+	switch(b->type){
+	case BEAM_TYPE_A:
 		// if we're targeting a subsystem - shoot directly at it
 		if(b->target_subsys != NULL){
 			vm_vec_unrotate(&b->last_shot, &b->target_subsys->system_info->pnt, &b->target->orient);
-			vm_vec_add2(&b->last_shot, &b->target->pos);		 
+			vm_vec_add2(&b->last_shot, &b->target->pos);
 			vm_vec_sub(&temp, &b->last_shot, &b->last_start);
-			
+
 			vm_vec_scale_add(&b->last_shot, &b->last_start, &temp, 2.0f);
 			break;
 		}
@@ -2097,7 +2139,7 @@
 			vm_vec_scale_add(&b->last_shot, &b->last_start, &p2, 2.0f);
 			break;
 		}
-		
+
 		// point at the center of the target...
 		if (b->flags & BF_TARGETING_COORDS) {
 			b->last_shot = b->target_pos1;
@@ -2106,29 +2148,23 @@
 			// ...then jitter based on shot_aim (requires target)
 			beam_jitter_aim(b, b->binfo.shot_aim[0]);
 		}
-		break;	
+		break;
 
-	case BEAM_TYPE_B:		
-		// where the shot is originating from (b->last_start gets filled in)
-		beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &p2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
-
+	case BEAM_TYPE_B:
 		// set the shot point
 		vm_vec_scale_add(&b->last_shot, &b->last_start, &b->binfo.dir_a, b->range);
-		Assert(is_valid_vec(&b->last_shot));		
+		Assert(is_valid_vec(&b->last_shot));
 		break;
 
 	case BEAM_TYPE_C:
 		// start point
-		temp = b->targeting_laser_offset;	
+		temp = b->targeting_laser_offset;
 		vm_vec_unrotate(&b->last_start, &temp, &b->objp->orient);
 		vm_vec_add2(&b->last_start, &b->objp->pos);
-		vm_vec_scale_add(&b->last_shot, &b->last_start, &b->objp->orient.vec.fvec, b->range);		
+		vm_vec_scale_add(&b->last_shot, &b->last_start, &b->objp->orient.vec.fvec, b->range);
 		break;
 
-	case BEAM_TYPE_D:				
-		// where the shot is originating from (b->last_start gets filled in)
-		beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &p2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);		
-		
+	case BEAM_TYPE_D:
 		// point at the center of the target...
 		if (b->flags & BF_TARGETING_COORDS) {
 			b->last_shot = b->target_pos1;
@@ -2142,7 +2178,7 @@
 
 	case BEAM_TYPE_E:
 		// where the shot is originating from (b->last_start gets filled in)
-		beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &p2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);		
+		beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, &temp, 1, &p2, (b->flags & BF_IS_FIGHTER_BEAM) > 0);
 
 		// point directly in the direction of the turret
 		vm_vec_scale_add(&b->last_shot, &b->last_start, &temp, b->range);
@@ -2149,11 +2185,9 @@
 		break;
 
 	default:
-		Int3();
-	}		
+		Assertion(false, "Impossible beam type (%d); get a coder!\n", b->type);
+	}
 
-	b->subsys->turret_next_fire_pos = temp_int;
-
 	// recalculate object pairs
 	OBJ_RECALC_PAIRS((&Objects[b->objnum]));
 }
@@ -2947,6 +2981,14 @@
 			snd_play_3d( &Snds[wi->impact_snd], &b->f_collisions[idx].cinfo.hit_point_world, &Eye_position );
 		}
 
+		// spawn subweapons as needed
+		if ( !b->has_hit ) {
+			b->has_hit = true;
+			vec3d normal;
+			vm_vec_unrotate(&normal, &b->f_collisions[idx].cinfo.hit_normal, &Objects[target].orient);
+			spawn_child_weapons(&Objects[b->objnum], &b->f_collisions[idx].cinfo.hit_point_world, &normal);
+		}
+
 		// KOMET_EXT -->
 
 		// draw flash, explosion
@@ -3236,6 +3278,10 @@
 // if it is legal for the beam to fire, or continue firing
 int beam_ok_to_fire(beam *b)
 {
+	if (b->objp == NULL) {	// If we don't have a firing object, none of these checks make sense.
+		return 1;
+	}
+
 	// if my own object is invalid, stop firing
 	if (b->objp->signature != b->sig) {
 		mprintf(("BEAM : killing beam because of invalid parent object SIGNATURE!\n"));
@@ -3265,6 +3311,10 @@
 		}
 	}
 
+	if (b->subsys == NULL) {	// If we don't have a firing turret, none of these checks make sense.
+		return 1;
+	}
+
 	if (!(b->flags & BF_FORCE_FIRING)) {
 		// if the shooting turret is destroyed	
 		if (b->subsys->current_hits <= 0.0f) {
Index: code/weapon/beam.h
===================================================================
--- code/weapon/beam.h	(revision 11202)
+++ code/weapon/beam.h	(working copy)
@@ -50,6 +50,7 @@
 #define BFIF_IS_FIGHTER_BEAM	(1<<0)
 #define BFIF_FORCE_FIRING		(1<<1)
 #define BFIF_TARGETING_COORDS	(1<<2)
+#define BFIF_FLOATING_BEAM		(1<<3)
 
 // pass to beam fire 
 typedef struct beam_fire_info {
@@ -62,11 +63,13 @@
 	ship_subsys		*target_subsys;						// (optional), specific subsystem to be targeted on the target 
 	vec3d			target_pos1;							// if we're shooting off into space
 	vec3d			target_pos2;							// if we're shooting off into space (optional second point)
+	vec3d			starting_pos;							// starting position for floating beams -MageKing17
 	beam_info		*beam_info_override;			// (optional), pass this in to override all beam movement info (for multiplayer)
 	int				num_shots;						// (optional), only used for type D weapons
 	int bank;									// for fighters, which bank of the primary weapons are they in
 	int point;									// for fighters, which point on the bank it is from
 	int bfi_flags;
+	char team;									// for floating beams, determines which team the beam is on
 } beam_fire_info;
 
 typedef struct fighter_beam_fire_info {
@@ -105,6 +108,7 @@
 #define BF_FORCE_FIRING					(1<<2)
 #define BF_IS_FIGHTER_BEAM				(1<<3)
 #define BF_TARGETING_COORDS				(1<<4)
+#define BF_FLOATING_BEAM				(1<<5)
 
 // beam struct (the actual weapon/object)
 typedef struct beam {
@@ -139,7 +143,7 @@
 	vec3d	last_start;				
 	int		shot_index;				// for type D beam weapons
 	float	beam_glow_frame;		// what frame a beam glow animation is on
-	float	beam_secion_frame[MAX_BEAM_SECTIONS];	// what frame a beam secion animation is on
+	float	beam_section_frame[MAX_BEAM_SECTIONS];	// what frame a beam secion animation is on
 
 	// recent collisions
 	beam_collision r_collisions[MAX_FRAME_COLLISIONS];					// recent collisions
@@ -167,6 +171,8 @@
 	int firingpoint;
 
 	float		beam_width;
+
+	bool	has_hit;	// Set to true after the first time the beam hits something.
 } beam;
 
 extern beam Beams[MAX_BEAMS];				// all beams
Index: code/weapon/weapon.h
===================================================================
--- code/weapon/weapon.h	(revision 11202)
+++ code/weapon/weapon.h	(working copy)
@@ -631,7 +631,7 @@
 bool weapon_armed(weapon *wp, bool hit_target);
 void weapon_hit( object * weapon_obj, object * other_obj, vec3d * hitpos, int quadrant = -1 );
 int cmeasure_name_lookup(char *name);
-void spawn_child_weapons( object *objp );
+void spawn_child_weapons( object *objp, vec3d *override_pos = NULL, vec3d *override_fvec = NULL );
 
 // call to detonate a weapon. essentially calls weapon_hit() with other_obj as NULL, and sends a packet in multiplayer
 void weapon_detonate(object *objp);
@@ -670,4 +670,7 @@
 // Unpauses all running weapon sounds
 void weapon_unpause_sounds();
 
+// Called by hudartillery.cpp after SSMs have been parsed to make sure that $SSM: entries defined in weapons are valid.
+void validate_SSM_entries();
+
 #endif
Index: code/weapon/weapons.cpp
===================================================================
--- code/weapon/weapons.cpp	(revision 11202)
+++ code/weapon/weapons.cpp	(working copy)
@@ -48,8 +48,26 @@
 #include "stats/scoring.h"
 #include "mod_table/mod_table.h"
 #include "debugconsole/console.h"
+#include "hud/hudartillery.h"
 
 
+// Since SSMs are parsed after weapons, if we want to allow SSM strikes to be specified by name, we need to store those names until after SSMs are parsed.
+typedef struct delayed_ssm_data {
+	SCP_string filename;
+	int linenum;
+	SCP_string ssm_entry;
+} delayed_ssm_data;
+SCP_map<SCP_string, delayed_ssm_data> Delayed_SSM_data;
+SCP_vector<SCP_string> Delayed_SSM_names;
+
+typedef struct delayed_ssm_index_data {
+	SCP_string filename;
+	int linenum;
+} delayed_ssm_index_data;
+SCP_map<SCP_string, delayed_ssm_index_data> Delayed_SSM_indices_data;
+SCP_vector<SCP_string> Delayed_SSM_indices;
+
+
 #ifndef NDEBUG
 int Weapon_flyby_sound_enabled = 1;
 DCF_BOOL( weapon_flyby, Weapon_flyby_sound_enabled )
@@ -1082,7 +1100,7 @@
 // return 0 if successful, otherwise return -1
 #define WEAPONS_MULTITEXT_LENGTH 2048
 
-int parse_weapon(int subtype, bool replace)
+int parse_weapon(int subtype, bool replace, const char *filename)
 {
 	char buf[WEAPONS_MULTITEXT_LENGTH];
 	weapon_info *wip = NULL;
@@ -2508,7 +2526,25 @@
 	}	
 
 	if( optional_string("$SSM:")){
-		stuff_int(&wip->SSM_index);
+		if (stuff_int_optional(&wip->SSM_index) != 2) {
+			// We can't make an SSM lookup yet, because weapons are parsed first, but we can save the data to process later. -MageKing17
+			stuff_string(fname, F_NAME, NAME_LENGTH);
+			delayed_ssm_data temp_data;
+			temp_data.filename = filename;
+			temp_data.linenum = get_line_num();
+			temp_data.ssm_entry = fname;
+			if (Delayed_SSM_data.find(wip->name) == Delayed_SSM_data.end())
+				Delayed_SSM_names.push_back(wip->name);
+			Delayed_SSM_data[wip->name] = temp_data;
+		} else {
+			// We'll still want to validate the index later. -MageKing17
+			delayed_ssm_index_data temp_data;
+			temp_data.filename = filename;
+			temp_data.linenum = get_line_num();
+			if (Delayed_SSM_indices_data.find(wip->name) == Delayed_SSM_indices_data.end())
+				Delayed_SSM_indices.push_back(wip->name);
+			Delayed_SSM_indices_data[wip->name] = temp_data;
+		}
 	}// SSM index -Bobboau
 
 	if(optional_string("$FOF:")){
@@ -2797,7 +2833,7 @@
 	{
 		while (required_string_either("#End", "$Name:")) {
 			// AL 28-3-98: If parse_weapon() fails, try next .tbl weapon
-			if ( parse_weapon(WP_LASER, Parsing_modular_table) < 0 ) {
+			if ( parse_weapon(WP_LASER, Parsing_modular_table, filename) < 0 ) {
 				continue;
 			}
 		}
@@ -2808,7 +2844,7 @@
 	{
 		while (required_string_either("#End", "$Name:")) {
 			// AL 28-3-98: If parse_weapon() fails, try next .tbl weapon
-			if ( parse_weapon(WP_MISSILE, Parsing_modular_table) < 0) {
+			if ( parse_weapon(WP_MISSILE, Parsing_modular_table, filename) < 0) {
 				continue;
 			}
 		}
@@ -2819,7 +2855,7 @@
 	{
 		while (required_string_either("#End", "$Name:")) {
 			// AL 28-3-98: If parse_weapon() fails, try next .tbl weapon
-			if ( parse_weapon(WP_BEAM, Parsing_modular_table) < 0) {
+			if ( parse_weapon(WP_BEAM, Parsing_modular_table, filename) < 0) {
 				continue;
 			}
 		}
@@ -2830,7 +2866,7 @@
 	{
 		while (required_string_either("#End", "$Name:"))
 		{
-			int idx = parse_weapon(WP_MISSILE, Parsing_modular_table);
+			int idx = parse_weapon(WP_MISSILE, Parsing_modular_table, filename);
 
 			if(idx < 0) {
 				continue;
@@ -5444,21 +5480,33 @@
 /**
  * Spawn child weapons from object *objp.
  */
-void spawn_child_weapons(object *objp)
+void spawn_child_weapons(object *objp, vec3d *override_pos, vec3d *override_fvec)
 {
 	int	i, j;
 	int	child_id;
 	int	parent_num;
 	ushort starting_sig;
-	weapon	*wp;
+	weapon	*wp = NULL;
+	beam	*bp = NULL;
 	weapon_info	*wip, *child_wip;
+	vec3d	*opos, *fvec;
 
-	Assert(objp->type == OBJ_WEAPON);
-	Assert((objp->instance >= 0) && (objp->instance < MAX_WEAPONS));
+	Assertion(objp->type == OBJ_WEAPON || objp->type == OBJ_BEAM, "spawn_child_weapons() doesn't make sense for non-weapon non-beam objects; get a coder!\n");
+	Assertion(objp->instance >= 0, "spawn_child_weapons() called with an object with an instance of %d; get a coder!\n", objp->instance);
+	Assertion(!(objp->type == OBJ_WEAPON) || (objp->instance < MAX_WEAPONS), "spawn_child_weapons() called with a weapon with an instance of %d while MAX_WEAPONS is %d; get a coder!\n", objp->instance, MAX_WEAPONS);
+	Assertion(!(objp->type == OBJ_BEAM) || (objp->instance < MAX_BEAMS), "spawn_child_weapons() called with a beam with an instance of %d while MAX_BEAMS is %d; get a coder!\n", objp->instance, MAX_BEAMS);
 
-	wp = &Weapons[objp->instance];
-	Assert((wp->weapon_info_index >= 0) && (wp->weapon_info_index < MAX_WEAPON_TYPES));
-	wip = &Weapon_info[wp->weapon_info_index];
+	if (objp->type == OBJ_WEAPON) {
+		wp = &Weapons[objp->instance];
+		Assertion((wp->weapon_info_index >= 0) && (wp->weapon_info_index < MAX_WEAPON_TYPES), "Invalid weapon_info_index of %d; get a coder!\n", wp->weapon_info_index);
+		wip = &Weapon_info[wp->weapon_info_index];
+	} else if (objp->type == OBJ_BEAM) {
+		bp = &Beams[objp->instance];
+		Assertion((bp->weapon_info_index >= 0) && (bp->weapon_info_index < MAX_WEAPON_TYPES), "Invalid weapon_info_index of %d; get a coder!\n", bp->weapon_info_index);
+		wip = &Weapon_info[bp->weapon_info_index];
+	} else {	// Let's make sure we don't do screwball things in a release build if this gets called with a non-weapon non-beam.
+		return;
+	}
 
 	parent_num = objp->parent;
 
@@ -5479,56 +5527,87 @@
 		multi_set_network_signature( objp->net_signature, MULTI_SIG_NON_PERMANENT );
 	}
 
-    for (i = 0; i < wip->num_spawn_weapons_defined; i++)
-    {
-        for (j = 0; j < wip->spawn_info[i].spawn_count; j++) 
-        {
-    		int		weapon_objnum;
-	    	vec3d	tvec, pos;
-		    matrix	orient;
+	if (override_pos != NULL) {
+		opos = override_pos;
+	} else {
+		opos = &objp->pos;
+	}
+	if (override_fvec != NULL) {
+		fvec = override_fvec;
+	} else {
+		fvec = &objp->orient.vec.fvec;
+	}
 
-            child_id = wip->spawn_info[i].spawn_type;
+	for (i = 0; i < wip->num_spawn_weapons_defined; i++)
+	{
+		for (j = 0; j < wip->spawn_info[i].spawn_count; j++) 
+		{
+			int		weapon_objnum;
+			vec3d	tvec, pos;
+			matrix	orient;
+
+			child_id = wip->spawn_info[i].spawn_type;
 			child_wip = &Weapon_info[child_id];
 
-		    // for multiplayer, use the static randvec functions based on the network signatures to provide
-		    // the randomness so that it is the same on all machines.
-		    if ( Game_mode & GM_MULTIPLAYER ) {
-    			static_rand_cone(objp->net_signature + j, &tvec, &objp->orient.vec.fvec, wip->spawn_info[i].spawn_angle);
-	    	} else {
-		    	vm_vec_random_cone(&tvec, &objp->orient.vec.fvec, wip->spawn_info[i].spawn_angle);
-		    }
-		    vm_vec_scale_add(&pos, &objp->pos, &tvec, objp->radius);
+			// for multiplayer, use the static randvec functions based on the network signatures to provide
+			// the randomness so that it is the same on all machines.
+			if ( Game_mode & GM_MULTIPLAYER ) {
+				static_rand_cone(objp->net_signature + j, &tvec, fvec, wip->spawn_info[i].spawn_angle);
+			} else {
+				vm_vec_random_cone(&tvec, fvec, wip->spawn_info[i].spawn_angle);
+			}
+			vm_vec_scale_add(&pos, opos, &tvec, objp->radius);
 
-		    vm_vector_2_matrix(&orient, &tvec, NULL, NULL);
-		    weapon_objnum = weapon_create(&pos, &orient, child_id, parent_num, -1, wp->weapon_flags & WF_LOCKED_WHEN_FIRED, 1);
+			// Let's allow beam-spawn! -MageKing17
+			if (child_wip->wi_flags & WIF_BEAM) {
+				beam_fire_info fire_info;
+				memset(&fire_info, 0, sizeof(beam_fire_info));
 
-			//if the child inherits parent target, do it only if the parent weapon was locked to begin with
-			if ((child_wip->wi_flags2 & WIF2_INHERIT_PARENT_TARGET) && (wp->homing_object != &obj_used_list))
-			{
-				//Deal with swarm weapons
-				if (wp->swarm_index >= 0) {
-					swarm_info	*swarmp;
-					swarmp = &Swarm_missiles[wp->swarm_index];
+				fire_info.accuracy = 0.000001f;		// this will guarantee a hit
+				fire_info.shooter = &Objects[parent_num];
+				fire_info.turret = NULL;
+				fire_info.target = NULL;
+				fire_info.target_subsys = NULL;
+				fire_info.target_pos1 = fire_info.target_pos2 = pos;
+				fire_info.bfi_flags |= BFIF_FLOATING_BEAM | BFIF_TARGETING_COORDS;
+				fire_info.starting_pos = objp->pos;
+				fire_info.beam_info_index = child_id;
+				fire_info.team = static_cast<char>(obj_team(&Objects[parent_num]));
 
-					weapon_set_tracking_info(weapon_objnum, parent_num, swarmp->homing_objnum, 1, wp->homing_subsys);
-				} else {
-					weapon_set_tracking_info(weapon_objnum, parent_num, wp->target_num, 1, wp->homing_subsys);
+				// fire the beam
+				beam_fire(&fire_info);
+			} else {
+				vm_vector_2_matrix(&orient, &tvec, NULL, NULL);
+				weapon_objnum = weapon_create(&pos, &orient, child_id, parent_num, -1, wp->weapon_flags & WF_LOCKED_WHEN_FIRED, 1);
+
+				//if the child inherits parent target, do it only if the parent weapon was locked to begin with
+				if ((child_wip->wi_flags2 & WIF2_INHERIT_PARENT_TARGET) && (wp->homing_object != &obj_used_list))
+				{
+					//Deal with swarm weapons
+					if (wp->swarm_index >= 0) {
+						swarm_info	*swarmp;
+						swarmp = &Swarm_missiles[wp->swarm_index];
+
+						weapon_set_tracking_info(weapon_objnum, parent_num, swarmp->homing_objnum, 1, wp->homing_subsys);
+					} else {
+						weapon_set_tracking_info(weapon_objnum, parent_num, wp->target_num, 1, wp->homing_subsys);
+					}
 				}
-			}
 
-    		//	Assign a little randomness to lifeleft so they don't all disappear at the same time.
-		    if (weapon_objnum != -1) {
-			    float rand_val;
+				//	Assign a little randomness to lifeleft so they don't all disappear at the same time.
+				if (weapon_objnum != -1) {
+					float rand_val;
 
-			    if ( Game_mode & GM_NORMAL ){
-				    rand_val = frand();
-			    } else {
-    				rand_val = static_randf(objp->net_signature + j);
-			    }
+					if ( Game_mode & GM_NORMAL ){
+						rand_val = frand();
+					} else {
+						rand_val = static_randf(objp->net_signature + j);
+					}
 
-			    Weapons[Objects[weapon_objnum].instance].lifeleft *= rand_val*0.4f + 0.8f;
-		    }
-        }
+					Weapons[Objects[weapon_objnum].instance].lifeleft *= rand_val*0.4f + 0.8f;
+				}
+			}
+		}
 
 	}
 
@@ -6991,3 +7070,43 @@
 	// Pause all beam sounds
 	beam_unpause_sounds();
 }
+
+// Called by hudartillery.cpp after SSMs have been parsed to make sure that $SSM: entries defined in weapons are valid.
+void validate_SSM_entries()
+{
+	int wi;
+	SCP_vector<SCP_string>::const_iterator it;
+	weapon_info *wip;
+
+	for (it = Delayed_SSM_names.begin(); it != Delayed_SSM_names.end(); ++it) {
+		delayed_ssm_data *dat = &Delayed_SSM_data[*it];
+		wi = weapon_info_lookup(it->c_str());
+		Assertion(wi >= 0, "Trying to validate non-existant weapon '%s'; get a coder!\n", it->c_str());
+		wip = &Weapon_info[wi];
+		nprintf(("parse", "Starting validation of '%s' [wip->name is '%s'], currently has an SSM_index of %d.\n", it->c_str(), wip->name, wip->SSM_index));
+		wip->SSM_index = ssm_info_lookup(dat->ssm_entry.c_str());
+		if (wip->SSM_index < 0) {
+			Warning(LOCATION, "Unknown SSM entry '%s' in specification for %s (%s:line %d).\n", dat->ssm_entry.c_str(), it->c_str(), dat->filename.c_str(), dat->linenum);
+		}
+		nprintf(("parse", "Validation complete, SSM_index is %d.\n", wip->SSM_index));
+	}
+
+	// This information is no longer relevant, so might as well clear it out.
+	Delayed_SSM_data.clear();
+	Delayed_SSM_names.clear();
+
+	for (it = Delayed_SSM_indices.begin(); it != Delayed_SSM_indices.end(); ++it) {
+		delayed_ssm_index_data *dat = &Delayed_SSM_indices_data[*it];
+		wi = weapon_info_lookup(it->c_str());
+		Assertion(wi >= 0, "Trying to validate non-existant weapon '%s'; get a coder!\n", it->c_str());
+		wip = &Weapon_info[wi];
+		nprintf(("parse", "Starting validation of '%s' [wip->name is '%s'], currently has an SSM_index of %d.\n", it->c_str(), wip->name, wip->SSM_index));
+		if (wip->SSM_index < -1 || wip->SSM_index >= static_cast<int>(Ssm_info.size())) {
+			Warning(LOCATION, "Invalid SSM index '%d' (should be 0-%d) in specification for %s (%s:line %d).\n", wip->SSM_index, Ssm_info.size()-1, it->c_str(), dat->filename.c_str(), dat->linenum);
+		}
+		nprintf(("parse", "Validation complete, SSM_index is %d.\n", wip->SSM_index));
+	}
+
+	Delayed_SSM_indices_data.clear();
+	Delayed_SSM_indices.clear();
+}
