subspace_beams.patch (59,568 bytes)
2014-12-22 23:12
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();
+}