mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 07:28:22 -10:00
Most places in the code that use `vm_vector_2_matrix` do so on already-normalized vectors. Changing these to `vm_vector_2_matrix_norm` avoids redundant normalization.
358 lines
9.9 KiB
C++
358 lines
9.9 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "debugconsole/console.h"
|
|
#include "freespace.h" // for Missiontime
|
|
#include "io/timer.h"
|
|
#include "object/object.h"
|
|
#include "ship/ship.h"
|
|
#include "weapon/corkscrew.h"
|
|
#include "weapon/weapon.h"
|
|
|
|
|
|
|
|
// corkscrew structure flags
|
|
#define CS_FLAG_USED (1<<0) // this structure is in use
|
|
#define CS_FLAG_COUNTER (1<<1) // counterrotate this guy
|
|
|
|
// corkscrew settings
|
|
int Corkscrew_missile_delay = 30; // delay between missile firings
|
|
int Corkscrew_num_missiles_fired = 4; // # of missiles fire in one shot
|
|
float Corkscrew_radius = 1.25f; // radius of the corkscrew itself
|
|
float Corkscrew_twist = 5.0f; // in degrees/second
|
|
int Corkscrew_helix = 1; // attempt to point the missile in the right direction
|
|
int Corkscrew_counterrotate = 1; // counterrotate every other missile
|
|
int Corkscrew_shrink = 0; // shrink the radius of every successive missile
|
|
float Corkscrew_shrink_val = 0.3f; // rate at which the radius shrinks
|
|
int Corkscrew_down_first = 1; // have the corkscrew go "down" first
|
|
|
|
// current counterrotation and radius shrink values
|
|
float Corkscrew_radius_cur = Corkscrew_radius;
|
|
|
|
typedef struct cscrew_info {
|
|
int flags; // flags for the missile
|
|
|
|
// info about the corkscrew effect for this missile
|
|
vec3d cen_p; // vector pointing to the "center" of the corkscrew
|
|
float radius; // radius of the corkscrew
|
|
matrix real_orient; // the orientation used when calling physics (bashed before rendering)
|
|
vec3d last_corkscrew_pos; // last position along the corkscrew
|
|
} cscrew_info;
|
|
|
|
#define MAX_CORKSCREW_MISSILES 200
|
|
cscrew_info Corkscrew_missiles[MAX_CORKSCREW_MISSILES];
|
|
|
|
// ------------------------------------------------------------------
|
|
// cscrew_level_init()
|
|
//
|
|
// Called at the start of each new mission
|
|
//
|
|
void cscrew_level_init()
|
|
{
|
|
memset(Corkscrew_missiles, 0, sizeof(cscrew_info) * MAX_CORKSCREW_MISSILES);
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// cscrew_maybe_fire_missile()
|
|
//
|
|
// Check if there are any swarm missiles to fire, and if enough time
|
|
// has elapsed since last one fired, go ahead and fire it.
|
|
//
|
|
// This is called once per ship frame in ship_move()
|
|
//
|
|
void cscrew_maybe_fire_missile(int shipnum)
|
|
{
|
|
ship *sp;
|
|
ship_weapon *swp;
|
|
int weapon_info_index;
|
|
|
|
Assert(shipnum >= 0 && shipnum < MAX_SHIPS );
|
|
sp = &Ships[shipnum];
|
|
|
|
// make sure we're supposed to be firing some missiles
|
|
if ( sp->num_corkscrew_to_fire <= 0 ){
|
|
sp->corkscrew_missile_bank = -1;
|
|
return;
|
|
}
|
|
|
|
// make sure we have a valid weapon bank
|
|
swp = &sp->weapons;
|
|
if ( sp->corkscrew_missile_bank == -1 ) {
|
|
sp->num_corkscrew_to_fire = 0;
|
|
return;
|
|
}
|
|
|
|
weapon_info_index = swp->secondary_bank_weapons[sp->corkscrew_missile_bank];
|
|
Assert( weapon_info_index >= 0 && weapon_info_index < weapon_info_size());
|
|
|
|
// if current secondary bank is not a corkscrew missile, return
|
|
if ( !(Weapon_info[weapon_info_index].wi_flags[Weapon::Info_Flags::Corkscrew]) ) {
|
|
sp->num_corkscrew_to_fire = 0;
|
|
sp->corkscrew_missile_bank = -1;
|
|
return;
|
|
}
|
|
|
|
if ( timestamp_elapsed(sp->next_corkscrew_fire) ) {
|
|
sp->next_corkscrew_fire = timestamp(Weapon_info[weapon_info_index].cs_delay);
|
|
ship_fire_secondary( &Objects[sp->objnum], 1 );
|
|
sp->num_corkscrew_to_fire--;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// cscrew_create()
|
|
//
|
|
// Get a free corkscrew missile entry, and initialize the struct members
|
|
//
|
|
int cscrew_create(object *obj)
|
|
{
|
|
int i;
|
|
cscrew_info *cscrewp = NULL;
|
|
weapon_info *wip;
|
|
|
|
wip = &Weapon_info[Weapons[obj->instance].weapon_info_index];
|
|
|
|
for ( i = 0; i < MAX_CORKSCREW_MISSILES; i++ ) {
|
|
cscrewp = &Corkscrew_missiles[i];
|
|
if ( !(cscrewp->flags & CS_FLAG_USED) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( i >= MAX_CORKSCREW_MISSILES ) {
|
|
nprintf(("Warning","No more corkscrew missiles are available\n"));
|
|
return -1;
|
|
}
|
|
|
|
// mark the guy as "used"
|
|
cscrewp->flags = CS_FLAG_USED;
|
|
|
|
// determine if he is counterrotating
|
|
if(wip->cs_crotate){
|
|
if(frand_range(0.0f, 1.0f) < 0.5f){
|
|
cscrewp->flags |= CS_FLAG_COUNTER;
|
|
}
|
|
}
|
|
|
|
// get the "center" pointing vector
|
|
vec3d neg;
|
|
neg = obj->orient.vec.uvec;
|
|
|
|
if (wip->cs_random_angle) {
|
|
vm_rot_point_around_line(&neg, &neg, frand_range(0.0f, PI2), &vmd_zero_vector, &obj->orient.vec.fvec);
|
|
} else {
|
|
vm_rot_point_around_line(&neg, &neg, wip->cs_angle, &vmd_zero_vector, &obj->orient.vec.fvec);
|
|
}
|
|
|
|
if(Corkscrew_down_first){
|
|
vm_vec_negate(&neg);
|
|
}
|
|
vm_vec_scale_add2(&cscrewp->cen_p, &neg, wip->cs_radius);
|
|
|
|
// move the missile up so that the corkscrew point is at the muzzle of the gun
|
|
// vm_vec_scale_add2(&obj->pos, &obj->orient.vec.uvec, Corkscrew_radius);
|
|
|
|
// store some initial helix params
|
|
cscrewp->real_orient = obj->orient;
|
|
cscrewp->last_corkscrew_pos = obj->pos;
|
|
|
|
return i;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// cscrew_delete()
|
|
//
|
|
//
|
|
void cscrew_delete(int i)
|
|
{
|
|
if ( !(Corkscrew_missiles[i].flags & CS_FLAG_USED) ) {
|
|
Int3();
|
|
}
|
|
|
|
memset(&Corkscrew_missiles[i], 0, sizeof(cscrew_info));
|
|
}
|
|
|
|
// pre process the corkscrew weapon by putting him in the "center" of his corkscrew
|
|
void cscrew_process_pre(object *objp)
|
|
{
|
|
cscrew_info *ci;
|
|
|
|
// check stuff
|
|
Assert(objp->type == OBJ_WEAPON);
|
|
Assert(Weapons[objp->instance].cscrew_index >= 0);
|
|
Assert(Corkscrew_missiles[Weapons[objp->instance].cscrew_index].flags & CS_FLAG_USED);
|
|
|
|
ci = &Corkscrew_missiles[Weapons[objp->instance].cscrew_index];
|
|
|
|
// unrotate the missile itself
|
|
if(Corkscrew_helix){
|
|
// restore the "real" matrix now
|
|
objp->orient = ci->real_orient;
|
|
}
|
|
// move the missile back to the center of the corkscrew
|
|
vm_vec_add2(&objp->pos, &ci->cen_p);
|
|
}
|
|
|
|
// post process the corkscrew weapon by putting him back to the right spot on his corkscrew
|
|
void cscrew_process_post(object *objp)
|
|
{
|
|
vec3d cen, neg;
|
|
vec3d new_pt;
|
|
weapon *wp;
|
|
weapon_info *wip;
|
|
cscrew_info *ci;
|
|
float twist_val;
|
|
|
|
// check stuff
|
|
Assert(objp->type == OBJ_WEAPON);
|
|
Assert(Weapons[objp->instance].cscrew_index >= 0);
|
|
Assert(Corkscrew_missiles[Weapons[objp->instance].cscrew_index].flags & CS_FLAG_USED);
|
|
|
|
// get various useful pointers
|
|
wp = &Weapons[objp->instance];
|
|
wip = &Weapon_info[wp->weapon_info_index];
|
|
ci = &Corkscrew_missiles[wp->cscrew_index];
|
|
|
|
// move to the outside of the corkscrew
|
|
neg = ci->cen_p;
|
|
cen = objp->pos;
|
|
vm_vec_negate(&neg);
|
|
vm_vec_add2(&objp->pos, &neg);
|
|
|
|
// determine what direction (clockwise or counterclockwise) the missile will spin
|
|
twist_val = ci->flags & CS_FLAG_COUNTER ? -wip->cs_twist : wip->cs_twist;
|
|
twist_val *= flFrametime;
|
|
|
|
// rotate the missile position
|
|
vm_rot_point_around_line(&new_pt, &objp->pos, twist_val, &cen, &objp->orient.vec.fvec);
|
|
objp->pos = new_pt;
|
|
|
|
// rotate the missile itself
|
|
if(Corkscrew_helix){
|
|
vec3d dir;
|
|
|
|
// compute a "fake" orient and store the old one for safekeeping
|
|
ci->real_orient = objp->orient;
|
|
vm_vec_normalized_dir(&dir, &objp->pos, &ci->last_corkscrew_pos);
|
|
vm_vector_2_matrix_norm(&objp->orient, &dir, nullptr, nullptr);
|
|
|
|
// mark down this position so we can orient nicely _next_ frame
|
|
ci->last_corkscrew_pos = objp->pos;
|
|
}
|
|
|
|
// get the new center pointing vector
|
|
vm_vec_sub(&ci->cen_p, &cen, &objp->pos);
|
|
|
|
// do trail stuff here
|
|
if ( wp->trail_ptr != nullptr && wp->lssm_stage != 3) {
|
|
if (trail_stamp_elapsed(wp->trail_ptr)) {
|
|
trail_add_segment( wp->trail_ptr, &objp->pos, &ci->real_orient);
|
|
trail_set_stamp(wp->trail_ptr);
|
|
} else {
|
|
trail_set_segment( wp->trail_ptr, &objp->pos );
|
|
}
|
|
}
|
|
}
|
|
|
|
// debug console functionality
|
|
void cscrew_display_dcf()
|
|
{
|
|
dc_printf("Corkscrew settings\n\n");
|
|
dc_printf("Delay (cscrew_delay) : %d\n",Corkscrew_missile_delay);
|
|
dc_printf("Count (cscrew_count) : %d\n",Corkscrew_num_missiles_fired);
|
|
dc_printf("Radius (cscrew_radius) : %f\n",Corkscrew_radius);
|
|
dc_printf("Twist (cscrew_twist) : %f\n",Corkscrew_twist);
|
|
if(Corkscrew_helix){
|
|
dc_printf("Helix (cscrew_helix): ON\n");
|
|
} else {
|
|
dc_printf("Helix (cscrew_helix): OFF\n");
|
|
}
|
|
if(Corkscrew_counterrotate){
|
|
dc_printf("Counterrotate (cscrew_counter): ON\n");
|
|
} else {
|
|
dc_printf("Counterrotate (cscrew_counter): OFF\n");
|
|
}
|
|
if(Corkscrew_shrink){
|
|
dc_printf("Shrink (cscrew_shrink): ON\n");
|
|
} else {
|
|
dc_printf("Shrink (cscrew_shrink): OFF\n");
|
|
}
|
|
dc_printf("Corkscrew shrink (cscrew_shrinkval): %f\n", Corkscrew_shrink_val);
|
|
if(Corkscrew_down_first){
|
|
dc_printf("Corkscrew down first : ON\n");
|
|
} else {
|
|
dc_printf("Corkscrew down first : OFF\n");
|
|
}
|
|
}
|
|
|
|
DCF(cscrew, "Listing of corkscrew missile debug console functions")
|
|
{
|
|
cscrew_display_dcf();
|
|
}
|
|
|
|
DCF(cscrew_delay, "Change the delay between corkscrew firing")
|
|
{
|
|
dc_stuff_int(&Corkscrew_missile_delay);
|
|
cscrew_display_dcf();
|
|
}
|
|
|
|
DCF(cscrew_count, "Change the # of corkscrew missiles fired")
|
|
{
|
|
dc_stuff_int(&Corkscrew_num_missiles_fired);
|
|
cscrew_display_dcf();
|
|
}
|
|
|
|
DCF(cscrew_radius, "Change the radius of corkscrew missiles")
|
|
{
|
|
dc_stuff_float(&Corkscrew_radius);
|
|
cscrew_display_dcf();
|
|
}
|
|
|
|
DCF(cscrew_twist, "Change the rate of the corkscrew twist")
|
|
{
|
|
dc_stuff_float(&Corkscrew_twist);
|
|
cscrew_display_dcf();
|
|
}
|
|
|
|
DCF(cscrew_helix, "Attempt to orient missile nicely along the corkscrew")
|
|
{
|
|
Corkscrew_helix = !Corkscrew_helix;
|
|
|
|
cscrew_display_dcf();
|
|
}
|
|
|
|
DCF(cscrew_counter, "Counterrotate every other missile")
|
|
{
|
|
Corkscrew_counterrotate = !Corkscrew_counterrotate;
|
|
|
|
cscrew_display_dcf();
|
|
}
|
|
|
|
DCF(cscrew_shrink, "Shrink the radius of every other missile")
|
|
{
|
|
Corkscrew_shrink = !Corkscrew_shrink;
|
|
|
|
cscrew_display_dcf();
|
|
}
|
|
|
|
DCF(cscrew_shrinkval, "Change the rate at which the radii shrink")
|
|
{
|
|
dc_stuff_float(&Corkscrew_shrink_val);
|
|
cscrew_display_dcf();
|
|
}
|
|
|
|
DCF(cscrew_down, "Cause the missile to spiral down first")
|
|
{
|
|
Corkscrew_down_first = !Corkscrew_down_first;
|
|
|
|
cscrew_display_dcf();
|
|
}
|