Files
Goober5000 4797ce55a2 use vm_vector_2_matrix_norm on normalized vectors
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.
2025-03-02 23:07:09 -05:00

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();
}