1013 lines
24 KiB
C
1013 lines
24 KiB
C
#include "rmissile.h"
|
|
#include "blast.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <libgte.h>
|
|
#include <libgpu.h>
|
|
|
|
#include "common.h"
|
|
#include "libgv/libgv.h"
|
|
#include "libdg/libdg.h"
|
|
#include "libhzd/libhzd.h"
|
|
#include "takabe/prim.h"
|
|
#include "game/game.h"
|
|
#include "linkvar.h"
|
|
#include "menu/menuman.h"
|
|
#include "thing/sgtrect3.h"
|
|
#include "thing/sight.h"
|
|
#include "sd/g_sound.h"
|
|
|
|
extern BLAST_DATA blast_data_8009F4B8[8];
|
|
extern TARGET *target_800BDF00;
|
|
extern int dword_8009F604;
|
|
extern int dword_800BDEF8[];
|
|
extern int GM_CameraTrackSave;
|
|
extern SVECTOR GM_CameraRotateSave;
|
|
extern int GM_event_camera_flag;
|
|
extern GM_CAMERA GM_Camera;
|
|
extern UnkCameraStruct gUnkCameraStruct_800B77B8;
|
|
extern CAMERA GM_CameraList[8];
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
// RC Missile (Nikita)
|
|
|
|
#define EXEC_LEVEL GV_ACTOR_AFTER
|
|
|
|
#define NIKITA_NAME 0x50ae // GV_StrCode("RCM")
|
|
#define NIKITA_SIGHT 0x15a9 // GV_StrCode("nikita")
|
|
#define NIKITA_MSG 0x15a9 // GV_StrCode("nikita")
|
|
#define NIKITA_MODEL 0x9a90 // GV_StrCode("nik_mis")
|
|
|
|
#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE | \
|
|
DG_FLAG_GBOUND | DG_FLAG_ONEPIECE | \
|
|
DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE )
|
|
|
|
#define SEGMENT_ATR (HZD_SEG_NO_MISSILE)
|
|
|
|
#define MISSILE_SPEED_SLOW 50
|
|
#define MISSILE_SPEED_FAST 200
|
|
|
|
#define FUEL_BAR_XOFFSET 31
|
|
#define FUEL_BAR_YOFFSET 39
|
|
#define FUEL_BAR_HEIGHT 10
|
|
|
|
#define COLOR_LIGHT_GREEN MAKE_RGB0(158,184,138)
|
|
#define COLOR_DARK_GREEN MAKE_RGB0( 93,135, 90)
|
|
#define COLOR_LIGHT_RED MAKE_RGB0(212, 78, 78)
|
|
#define COLOR_DARK_RED MAKE_RGB0(128, 26, 12)
|
|
|
|
typedef struct _Work
|
|
{
|
|
GV_ACT actor;
|
|
CONTROL control;
|
|
OBJECT_NO_ROTS object;
|
|
MATRIX light[2];
|
|
SVECTOR field_100_svector; // work->control.mov
|
|
SVECTOR field_108_svector; // work->control.mov
|
|
char field_110;
|
|
char boost_delay;
|
|
char field_112;
|
|
char fpv_mode;
|
|
char hud_delay;
|
|
char field_115;
|
|
char field_116;
|
|
char field_117;
|
|
short field_118;
|
|
short speed;
|
|
int field_11C;
|
|
TARGET target;
|
|
short enemy_time;
|
|
short cam_interp;
|
|
SVECTOR cam_pos;
|
|
POLY_F4 *fuel_bar[2];
|
|
GM_CAMERA saved_camera;
|
|
UnkCameraStruct saved_camera_unk;
|
|
CAMERA saved_camera_list[8];
|
|
int saved_camera_track;
|
|
SVECTOR saved_camera_rotate;
|
|
int saved_camera_event;
|
|
DG_PRIM *flame;
|
|
DG_TEX *flame_tex;
|
|
char field_2E0_pad[4];
|
|
SVECTOR flame_vec[8];
|
|
int flame_delay;
|
|
} Work;
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
int dword_8009F46C = 0; // FIXME: this is referenced as an array in several places
|
|
int dword_8009F470 = 0;
|
|
int dword_8009F474 = 0;
|
|
SVECTOR svector_8009F478 = {0, 0, 0, 0};
|
|
int dword_8009F480 = 0;
|
|
short Nik_Blast = 0;
|
|
SVECTOR svector_8009F488 = {100, 100, 100, 0};
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void SaveCameraState(Work *work)
|
|
{
|
|
GV_CopyMemory(&GM_Camera, &work->saved_camera, sizeof(work->saved_camera));
|
|
GV_CopyMemory(&gUnkCameraStruct_800B77B8, &work->saved_camera_unk, sizeof(work->saved_camera_unk));
|
|
GV_CopyMemory(GM_CameraList, &work->saved_camera_list, sizeof(work->saved_camera_list));
|
|
GV_CopyMemory(&GM_CameraRotateSave, &work->saved_camera_rotate, sizeof(work->saved_camera_rotate));
|
|
|
|
work->saved_camera_track = GM_CameraTrackSave;
|
|
work->saved_camera_event = GM_event_camera_flag;
|
|
}
|
|
|
|
static void ResetCameraState(Work *work)
|
|
{
|
|
GV_CopyMemory(&work->saved_camera, &GM_Camera, sizeof(work->saved_camera));
|
|
GV_CopyMemory(&work->saved_camera_unk, &gUnkCameraStruct_800B77B8, sizeof(work->saved_camera_unk));
|
|
GV_CopyMemory(&work->saved_camera_list, &GM_CameraList, sizeof(work->saved_camera_list));
|
|
GV_CopyMemory(&work->saved_camera_rotate, &GM_CameraRotateSave, sizeof(work->saved_camera_rotate));
|
|
|
|
GM_CameraTrackSave = work->saved_camera_track;
|
|
GM_event_camera_flag = work->saved_camera_event;
|
|
|
|
sub_800309B4(2, 30);
|
|
GM_CameraEventReset();
|
|
}
|
|
|
|
static void rmissile_8006B924(Work *work)
|
|
{
|
|
CONTROL *ctrl;
|
|
|
|
ctrl = &work->control;
|
|
ctrl->skip_flag |= CTRL_SKIP_TRAP;
|
|
|
|
ResetCameraState(work);
|
|
GM_GameStatus &= ~GAME_FLAG_BIT_07;
|
|
HZD_ExecLeaveEvent(ctrl->map->hzd, &ctrl->event);
|
|
|
|
ctrl = GM_PlayerControl;
|
|
|
|
if (ctrl)
|
|
{
|
|
HZD_ReExecEvent(ctrl->map->hzd, &ctrl->event, 0x102);
|
|
}
|
|
}
|
|
|
|
static void SetFirstPersonView(Work *work)
|
|
{
|
|
Nik_Blast = 1;
|
|
|
|
if (dword_8009F604 != NIKITA_SIGHT)
|
|
{
|
|
NewSight(NIKITA_SIGHT, NIKITA_SIGHT, &Nik_Blast, IT_Scope, NULL);
|
|
}
|
|
|
|
GM_Camera.first_person = 1;
|
|
|
|
if (work->fpv_mode == FALSE)
|
|
{
|
|
work->fpv_mode = TRUE;
|
|
dword_800BDEF8[0] = COLOR_DARK_GREEN;
|
|
dword_800BDEF8[1] = COLOR_DARK_RED;
|
|
NewSgtRect3(&Nik_Blast, 1, dword_800BDEF8, 0);
|
|
GM_SeSet2(0, 0x3F, SE_ITEM_OPENWINDOW);
|
|
}
|
|
}
|
|
|
|
static void DrawEnemyText(Work *work)
|
|
{
|
|
if (!target_800BDF00)
|
|
{
|
|
work->enemy_time = 0;
|
|
return;
|
|
}
|
|
|
|
work->enemy_time++;
|
|
|
|
if (work->enemy_time <= 16)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (work->enemy_time == 32)
|
|
{
|
|
work->enemy_time = 0;
|
|
}
|
|
|
|
if (!(GM_PlayerStatus & PLAYER_NOT_SIGHT))
|
|
{
|
|
MENU_Color(158, 184, 138);
|
|
MENU_Locate(116, 98, 0);
|
|
MENU_Printf("ENEMY\n");
|
|
menu_Text_Init_80038B98();
|
|
}
|
|
}
|
|
|
|
static void DrawMissileHud(Work *work)
|
|
{
|
|
int width;
|
|
u_long *ot;
|
|
POLY_F4 *bar;
|
|
CONTROL ctrl;
|
|
|
|
width = ((1000 - work->field_118) * 60) / 1000;
|
|
|
|
ot = (u_long *)DG_ChanlOTag(1);
|
|
bar = work->fuel_bar[GV_Clock];
|
|
|
|
if (width > 60)
|
|
{
|
|
width = 60;
|
|
}
|
|
|
|
/* update fuel gauge */
|
|
bar->x3 = width + FUEL_BAR_XOFFSET;
|
|
bar->x1 = width + FUEL_BAR_XOFFSET;
|
|
|
|
if (GM_PlayerStatus & PLAYER_NOT_SIGHT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (width < 15)
|
|
{
|
|
/* low fuel remaining */
|
|
setRGB0(bar, 212, 78, 78);
|
|
|
|
if (!(work->field_118 % 15))
|
|
{
|
|
GM_SeSet2(0, 63, SE_SIGNAL02);
|
|
}
|
|
}
|
|
|
|
addPrim(ot, bar);
|
|
|
|
/* print missile XYZ position */
|
|
ctrl = work->control;
|
|
|
|
MENU_Locate(8, 136, 0);
|
|
MENU_Printf("%d", ctrl.mov.vx);
|
|
|
|
MENU_Locate(8, 144, 0);
|
|
MENU_Printf("%d", ctrl.mov.vy);
|
|
|
|
MENU_Locate(8, 152, 0);
|
|
MENU_Printf("%d", ctrl.mov.vz);
|
|
|
|
menu_Text_Init_80038B98();
|
|
}
|
|
|
|
static void rmissile_act_helper_8006BD24(Work *work, int pad_status)
|
|
{
|
|
if (work->field_112 || work->field_117 || work->field_110)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pad_status & PAD_TRIANGLE)
|
|
{
|
|
SetFirstPersonView(work);
|
|
|
|
gUnkCameraStruct_800B77B8.rotate2.vx = 0;
|
|
gUnkCameraStruct_800B77B8.rotate2.vy = work->control.rot.vy;
|
|
gUnkCameraStruct_800B77B8.rotate2.vz = 0;
|
|
|
|
DG_InvisibleObjs(work->object.objs);
|
|
|
|
work->field_11C = -2;
|
|
|
|
if (work->hud_delay < 16)
|
|
{
|
|
work->hud_delay++;
|
|
return;
|
|
}
|
|
|
|
DrawMissileHud(work);
|
|
DrawEnemyText(work);
|
|
return;
|
|
}
|
|
|
|
if (work->fpv_mode)
|
|
{
|
|
work->field_115 = 5;
|
|
}
|
|
|
|
work->hud_delay = 0;
|
|
work->fpv_mode = FALSE;
|
|
|
|
GM_Camera.first_person = 0;
|
|
Nik_Blast = 0;
|
|
|
|
if (work->field_115)
|
|
{
|
|
work->field_115--;
|
|
|
|
if (work->field_115 == 0)
|
|
{
|
|
DG_VisibleObjs(work->object.objs);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SetMissileDir3P(Work *work, int pad_dir)
|
|
{
|
|
if (GM_PlayerStatus & PLAYER_PAD_OFF)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pad_dir >= 0 && work->control.turn.vy != pad_dir)
|
|
{
|
|
work->boost_delay = 30;
|
|
work->control.turn.vy = pad_dir;
|
|
}
|
|
}
|
|
|
|
static void SetMissileDir1P(Work *work, int pad_status)
|
|
{
|
|
if (GM_PlayerStatus & PLAYER_PAD_OFF)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pad_status & PAD_LEFT)
|
|
{
|
|
work->boost_delay = 30;
|
|
work->control.turn.vy += 64;
|
|
work->control.turn.vy &= 0x0FFF;
|
|
}
|
|
|
|
if (pad_status & PAD_RIGHT)
|
|
{
|
|
work->boost_delay = 30;
|
|
work->control.turn.vy -= 64;
|
|
work->control.turn.vy &= 0x0FFF;
|
|
}
|
|
}
|
|
|
|
static void rmissile_act_helper_8006BEEC(Work *work)
|
|
{
|
|
if (work->field_117)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (GM_PlayerStatus & (PLAYER_DAMAGED | PLAYER_GAME_OVER) ||
|
|
dword_8009F474 == 1 || GM_GameStatus & (STATE_CHAFF | STATE_JAMMING))
|
|
{
|
|
work->field_117 = 1;
|
|
work->cam_interp = 15;
|
|
work->cam_pos = work->control.mov;
|
|
work->fpv_mode = FALSE;
|
|
|
|
GM_Camera.first_person = 0;
|
|
Nik_Blast = 0;
|
|
|
|
if (!work->field_112)
|
|
{
|
|
DG_VisibleObjs(work->object.objs);
|
|
}
|
|
|
|
dword_8009F474 = 0;
|
|
rmissile_8006B924(work);
|
|
}
|
|
}
|
|
|
|
static void rmissile_act_helper_8006BFD4(Work *work)
|
|
{
|
|
if (work->boost_delay)
|
|
{
|
|
work->boost_delay--;
|
|
|
|
if (work->boost_delay == 0)
|
|
{
|
|
GM_SeSet2(0, 63, SE_MISSILE_BOOST);
|
|
work->field_116 = 12;
|
|
}
|
|
|
|
work->speed = MISSILE_SPEED_SLOW;
|
|
|
|
if (!work->field_112)
|
|
{
|
|
work->field_11C = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (work->field_116)
|
|
{
|
|
work->field_116--;
|
|
gUnkCameraStruct_800B77B8.eye.vy += GV_RandS(512) * work->field_116 / 32;
|
|
}
|
|
|
|
work->speed = MISSILE_SPEED_FAST;
|
|
|
|
if (!work->field_112)
|
|
{
|
|
work->field_11C = 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int CheckMessage(void)
|
|
{
|
|
GV_MSG *msg;
|
|
int len;
|
|
|
|
len = GV_ReceiveMessage(NIKITA_MSG, &msg);
|
|
if (len > 0)
|
|
{
|
|
for (len--; len >= 0; msg++, len--)
|
|
{
|
|
if (msg->message[0] == HASH_KILL)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void rmissile_act_helper_8006C114(Work *work)
|
|
{
|
|
SVECTOR *position;
|
|
int result;
|
|
MATRIX rotation;
|
|
BLAST_DATA *blast;
|
|
|
|
if (work->field_112)
|
|
{
|
|
return;
|
|
}
|
|
|
|
position = &work->control.mov;
|
|
result = CheckMessage();
|
|
|
|
if (work->control.touch_flag <= 0 && !(work->control.level_flag & 2))
|
|
{
|
|
if (position->vy - work->field_108_svector.vy < 200)
|
|
{
|
|
if (++work->field_118 != 1000 && !GM_PowerTarget(&work->target) && !dword_8009F480 && !result)
|
|
{
|
|
if (!HZD_PointCheck(work->control.map->hzd, position, 250, HZD_CHECK_ALL, SEGMENT_ATR))
|
|
{
|
|
if (abs(position->vx) <= 30000 &&
|
|
abs(position->vy) <= 30000 &&
|
|
abs(position->vz) <= 30000)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GM_Camera.first_person && !work->field_117)
|
|
{
|
|
GM_Camera.first_person = 0;
|
|
Nik_Blast = 0;
|
|
}
|
|
|
|
work->fpv_mode = FALSE;
|
|
dword_8009F480 = 0;
|
|
work->field_112 = 1;
|
|
work->field_118 = result ? 28 : 0;
|
|
work->control.step = DG_ZeroVector;
|
|
DG_InvisibleObjs(work->object.objs);
|
|
work->object.objs->group_id = 0;
|
|
work->field_11C = -2;
|
|
|
|
if (!result)
|
|
{
|
|
DG_SetPos2(position, &work->control.rot);
|
|
ReadRotMatrix(&rotation);
|
|
|
|
GM_CurrentMap = work->control.map->index;
|
|
|
|
if (GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO))
|
|
{
|
|
blast = &blast_data_8009F4B8[7];
|
|
#ifdef VR_EXE
|
|
if ((GM_GameStatus & STATE_PADDEMO) &&
|
|
!(GM_PlayerStatus & PLAYER_PAD_OFF) &&
|
|
!(GM_GameStatus & STATE_PADRELEASE))
|
|
{
|
|
blast = &blast_data_8009F4B8[4];
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
blast = &blast_data_8009F4B8[4];
|
|
}
|
|
NewBlast(&rotation, blast);
|
|
}
|
|
}
|
|
|
|
static void rmissile_act_helper_8006C37C(Work *work)
|
|
{
|
|
SVECTOR vecs[8];
|
|
POLY_FT4 *poly;
|
|
DG_TEX *tex;
|
|
int i;
|
|
int inc;
|
|
int offx;
|
|
int offy;
|
|
int width;
|
|
int height;
|
|
int y;
|
|
int rand1;
|
|
int rand2;
|
|
|
|
if (work->field_11C > 0)
|
|
{
|
|
inc = 80;
|
|
y = 120;
|
|
}
|
|
else
|
|
{
|
|
inc = 64;
|
|
y = 120;
|
|
}
|
|
|
|
poly = &work->flame->packs[GV_Clock]->poly_ft4;
|
|
|
|
for (i = 0; i < 8; i++, poly++)
|
|
{
|
|
if (work->field_11C && (i == 0))
|
|
{
|
|
poly->tpage &= ~0x0060;
|
|
poly->tpage |= 0x0020;
|
|
}
|
|
else
|
|
{
|
|
poly->tpage |= 0x60;
|
|
}
|
|
|
|
vecs[i].vx = 0;
|
|
|
|
if (work->field_11C)
|
|
{
|
|
vecs[i].vy = 150 + i * inc;
|
|
}
|
|
else
|
|
{
|
|
vecs[i].vy = 250 + i * inc;
|
|
}
|
|
|
|
vecs[i].vz = 0;
|
|
|
|
work->flame_vec[i].pad = y;
|
|
y = 15 * y / 16;
|
|
|
|
|
|
rand1 = GV_RandU(4) & 1;
|
|
rand2 = GV_RandU(4);
|
|
|
|
tex = work->flame_tex;
|
|
|
|
offx = work->flame_tex->off_x;
|
|
width = work->flame_tex->w + 1;
|
|
|
|
poly->u3 = offx + (width << rand1) / 2 - 1;
|
|
poly->u1 = offx + (width << rand1) / 2 - 1;
|
|
|
|
poly->u2 = offx + ((width * rand1) >> 1);
|
|
poly->u0 = offx + ((width * rand1) >> 1);
|
|
|
|
|
|
offy = work->flame_tex->off_y;
|
|
height = work->flame_tex->h + 1;
|
|
|
|
poly->v1 = offy + (height * (rand2 / 2)) / 2;
|
|
poly->v0 = offy + (height * (rand2 / 2)) / 2;
|
|
|
|
poly->v3 = offy + (height * (rand2 / 2 + 1)) / 2 - 1;
|
|
poly->v2 = offy + (height * (rand2 / 2 + 1)) / 2 - 1;
|
|
|
|
setRGB0(poly, 128u - i * 16, 128u - i * 16, 128u - i * 16);
|
|
}
|
|
|
|
DG_SetPos2(&work->control.mov, &work->control.rot);
|
|
DG_PutVector(vecs, work->flame_vec, 8);
|
|
}
|
|
|
|
static void Act(Work *work)
|
|
{
|
|
GV_PAD *pad;
|
|
BLAST_DATA *blast;
|
|
SVECTOR vector;
|
|
SVECTOR vector2;
|
|
MATRIX rotation;
|
|
|
|
pad = &GV_PadData[0];
|
|
|
|
if (GM_PlayerStatus & PLAYER_SECOND_CONTROLLER)
|
|
{
|
|
pad = &GV_PadData[1];
|
|
}
|
|
|
|
if (work->flame_delay-- > 0 || (work->field_11C < 0 && work->field_11C > -3))
|
|
{
|
|
DG_InvisiblePrim(work->flame);
|
|
}
|
|
else
|
|
{
|
|
DG_VisiblePrim(work->flame);
|
|
rmissile_act_helper_8006C37C(work);
|
|
}
|
|
|
|
if (work->field_110 && !work->field_112)
|
|
{
|
|
if (--work->field_110 != 0)
|
|
{
|
|
if (work->field_118 != 0)
|
|
{
|
|
vector2 = work->control.mov;
|
|
GV_NearExp2V(&vector2.vx, &work->field_100_svector.vx, 3);
|
|
GV_SubVec3(&vector2, &work->control.mov, &work->control.step);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DG_VisibleObjs(work->object.objs);
|
|
work->target.side = 0;
|
|
GV_SubVec3(&work->field_100_svector, &work->control.mov, &work->control.step);
|
|
}
|
|
}
|
|
|
|
if (GM_CurrentWeaponId != 3)
|
|
{
|
|
/* player has unequipped the Nikita launcher */
|
|
if (!work->field_112)
|
|
{
|
|
GM_CurrentMap = work->control.map->index;
|
|
DG_SetPos2(&work->control.mov, &work->control.rot);
|
|
ReadRotMatrix(&rotation);
|
|
|
|
if (GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO)
|
|
|| !GM_SnakeCurrentHealth || GM_GameOverTimer)
|
|
{
|
|
blast = &blast_data_8009F4B8[7];
|
|
#ifdef VR_EXE
|
|
if ((GM_GameStatus & STATE_PADDEMO) &&
|
|
!(GM_PlayerStatus & PLAYER_PAD_OFF) &&
|
|
!(GM_GameStatus & STATE_PADRELEASE))
|
|
{
|
|
blast = &blast_data_8009F4B8[4];
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
blast = &blast_data_8009F4B8[4];
|
|
}
|
|
|
|
NewBlast(&rotation, blast);
|
|
}
|
|
|
|
dword_8009F480 = 0;
|
|
|
|
if (GM_Camera.first_person && GM_PlayerBody)
|
|
{
|
|
DG_InvisibleObjs(GM_PlayerBody->objs);
|
|
}
|
|
|
|
work->field_11C = -1;
|
|
GV_DestroyActor(&work->actor);
|
|
}
|
|
else
|
|
{
|
|
rmissile_act_helper_8006BEEC(work);
|
|
rmissile_act_helper_8006C114(work);
|
|
|
|
if (!work->field_117)
|
|
{
|
|
gUnkCameraStruct_800B77B8.eye = work->control.mov;
|
|
}
|
|
else
|
|
{
|
|
vector = work->cam_pos;
|
|
GV_NearTimeV(&work->cam_pos.vx, &GM_PlayerPosition.vx, work->cam_interp, 3);
|
|
gUnkCameraStruct_800B77B8.eye = work->cam_pos;
|
|
|
|
if (work->cam_interp > 0)
|
|
{
|
|
work->cam_interp--;
|
|
}
|
|
}
|
|
|
|
dword_8009F46C = !work->field_117;
|
|
|
|
if (work->field_112)
|
|
{
|
|
if (++work->field_118 == 30)
|
|
{
|
|
GV_DestroyActor(&work->actor);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GM_SetNoise(5, 2, &work->control.mov);
|
|
work->field_108_svector = work->control.mov;
|
|
|
|
GM_ActControl(&work->control);
|
|
svector_8009F478 = vector2 = work->control.mov;
|
|
|
|
GM_ActObject2((OBJECT *)&work->object);
|
|
DG_GetLightMatrix2(&vector2, work->light);
|
|
|
|
if (!work->field_117 && !work->field_110)
|
|
{
|
|
if (GM_PlayerBody)
|
|
{
|
|
DG_VisibleObjs(GM_PlayerBody->objs);
|
|
}
|
|
|
|
if (work->fpv_mode == FALSE)
|
|
{
|
|
SetMissileDir3P(work, pad->dir);
|
|
}
|
|
else
|
|
{
|
|
SetMissileDir1P(work, pad->status);
|
|
}
|
|
}
|
|
|
|
rmissile_act_helper_8006BFD4(work);
|
|
|
|
if (1000 - work->field_118 < 100)
|
|
{
|
|
work->control.rot.vy += GV_RandS(32) * (work->field_118 - 900) / 32;
|
|
work->field_11C = GV_RandU(2);
|
|
}
|
|
|
|
if (work->field_117 == 1)
|
|
{
|
|
work->control.rot.vy += GV_RandS(128);
|
|
}
|
|
|
|
GV_DirVec2(work->control.rot.vy, work->speed, &work->control.step);
|
|
rmissile_act_helper_8006BD24(work, pad->status);
|
|
GM_MoveTarget(&work->target, &vector2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void Die(Work *work)
|
|
{
|
|
GM_FreeControl(&work->control);
|
|
GM_FreeObject((OBJECT *)&work->object);
|
|
|
|
if (work->fuel_bar[0])
|
|
{
|
|
GV_DelayedFree(work->fuel_bar[0]);
|
|
dword_8009F46C = 0;
|
|
}
|
|
else
|
|
{
|
|
dword_8009F46C = 0;
|
|
}
|
|
|
|
dword_8009F470 = 0;
|
|
Nik_Blast = 0;
|
|
|
|
GM_FreePrim(work->flame);
|
|
|
|
if (!work->field_117)
|
|
{
|
|
rmissile_8006B924(work);
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int InitTarget(Work *work, int side)
|
|
{
|
|
TARGET *target = &work->target;
|
|
|
|
GM_SetTarget(target, TARGET_POWER, side, &svector_8009F488);
|
|
GM_Target_8002DCCC(target, 0, -1, 1, 0, &DG_ZeroVector);
|
|
GM_MoveTarget(target, &work->control.mov);
|
|
return 0;
|
|
}
|
|
|
|
static int InitFuelBar(Work *work)
|
|
{
|
|
POLY_F4 *poly1;
|
|
POLY_F4 *poly2;
|
|
|
|
work->fuel_bar[0] = poly1 = GV_Malloc(sizeof(POLY_F4) * 2);
|
|
|
|
if (poly1 == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
work->fuel_bar[1] = &poly1[1];
|
|
|
|
LSTORE(COLOR_LIGHT_GREEN, &poly1[0].r0);
|
|
LSTORE(COLOR_LIGHT_GREEN, &poly1[1].r0);
|
|
|
|
poly2 = &poly1[1];
|
|
|
|
SetPolyF4(poly1);
|
|
SetPolyF4(poly2);
|
|
SetSemiTrans(poly1, 1);
|
|
SetSemiTrans(poly2, 1);
|
|
|
|
/* left side screen coordinates */
|
|
poly2->x2 = FUEL_BAR_XOFFSET;
|
|
poly2->x0 = FUEL_BAR_XOFFSET;
|
|
poly1->x2 = FUEL_BAR_XOFFSET;
|
|
poly1->x0 = FUEL_BAR_XOFFSET;
|
|
|
|
/* top side screen coordinates */
|
|
poly2->y1 = FUEL_BAR_YOFFSET;
|
|
poly2->y0 = FUEL_BAR_YOFFSET;
|
|
poly1->y1 = FUEL_BAR_YOFFSET;
|
|
poly1->y0 = FUEL_BAR_YOFFSET;
|
|
|
|
/* bottom side screen coordinates */
|
|
poly2->y3 = (FUEL_BAR_YOFFSET + FUEL_BAR_HEIGHT);
|
|
poly2->y2 = (FUEL_BAR_YOFFSET + FUEL_BAR_HEIGHT);
|
|
poly1->y3 = (FUEL_BAR_YOFFSET + FUEL_BAR_HEIGHT);
|
|
poly1->y2 = (FUEL_BAR_YOFFSET + FUEL_BAR_HEIGHT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void set_poly_tex(POLY_FT4 *poly, DG_TEX *tex)
|
|
{
|
|
int u, v, w, h;
|
|
|
|
setPolyFT4(poly);
|
|
setSemiTrans(poly, 1);
|
|
|
|
u = tex->off_x;
|
|
w = (tex->w + 1) / 2;
|
|
poly->u2 = u;
|
|
poly->u0 = u;
|
|
u += w;
|
|
poly->u3 = u - 1;
|
|
poly->u1 = u - 1;
|
|
|
|
v = tex->off_y;
|
|
h = (tex->h + 1) / 2;
|
|
poly->v1 = v;
|
|
poly->v0 = v;
|
|
v += h;
|
|
poly->v3 = v - 1;
|
|
poly->v2 = v - 1;
|
|
|
|
poly->tpage = tex->tpage;
|
|
poly->clut = tex->clut;
|
|
}
|
|
|
|
static void SetPolyTexture(POLY_FT4 *poly, DG_TEX *tex, int n_poly)
|
|
{
|
|
set_poly_tex(poly, tex);
|
|
poly->tpage = (poly->tpage & ~0x0060) | 0x20;
|
|
|
|
poly->r0 = 0;
|
|
poly->g0 = 0;
|
|
poly->b0 = 0;
|
|
|
|
poly++;
|
|
n_poly--;
|
|
|
|
while (n_poly-- != 0)
|
|
{
|
|
set_poly_tex(poly, tex);
|
|
poly->tpage |= 0x0060;
|
|
|
|
setRGB0(poly, 0, 0, 0);
|
|
|
|
poly++;
|
|
}
|
|
}
|
|
|
|
static void InitMissileFlame(Work *work)
|
|
{
|
|
DG_TEX *tex;
|
|
SVECTOR *vec;
|
|
DG_PRIM *prim;
|
|
int count;
|
|
|
|
tex = DG_GetTexture(GV_StrCode("socom_f"));
|
|
|
|
work->flame_tex = tex;
|
|
prim = work->flame = Takabe_MakeIndividualRect3DPrim(8, work->flame_vec);
|
|
|
|
SetPolyTexture(&prim->packs[0]->poly_ft4, tex, 8);
|
|
SetPolyTexture(&prim->packs[1]->poly_ft4, tex, 8);
|
|
|
|
prim->world = DG_ZeroMatrix;
|
|
prim->field_2E_k500 = 100;
|
|
|
|
vec = work->flame_vec;
|
|
for (count = 8; count > 0; vec++, count--)
|
|
{
|
|
vec->pad = 0;
|
|
}
|
|
}
|
|
|
|
static inline int GetEight(void)
|
|
{
|
|
return 8; // what???
|
|
}
|
|
|
|
static int GetResources(Work *work, MATRIX *world, int side)
|
|
{
|
|
CONTROL *ctrl;
|
|
OBJECT_NO_ROTS *object;
|
|
|
|
dword_8009F46C = 1;
|
|
dword_8009F470 = 1;
|
|
|
|
SaveCameraState(work);
|
|
|
|
ctrl = &work->control;
|
|
|
|
if (GM_InitControl(ctrl, NIKITA_NAME, 0) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
GM_ConfigControlMatrix(ctrl, world);
|
|
|
|
work->field_100_svector = ctrl->mov;
|
|
work->field_110 = 8;
|
|
ctrl->mov = GM_PlayerPosition;
|
|
work->field_108_svector = GM_PlayerPosition;
|
|
svector_8009F478 = GM_PlayerPosition;
|
|
|
|
GM_ConfigControlHazard(ctrl, 400, 0xC8, 0xC8);
|
|
ctrl->exclude_flag = GetEight();
|
|
GM_ConfigControlTrapCheck(ctrl);
|
|
|
|
object = &work->object;
|
|
ctrl->turn.vz = 0;
|
|
ctrl->rot.vz = 0;
|
|
ctrl->skip_flag |= CTRL_SKIP_NEAR_CHECK;
|
|
|
|
GM_InitObjectNoRots(object, NIKITA_MODEL, BODY_FLAG, 0);
|
|
|
|
if (!object->objs)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
GM_ConfigObjectLight((OBJECT *)object, work->light);
|
|
|
|
if (InitTarget(work, side) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (InitFuelBar(work) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
DG_InvisibleObjs(work->object.objs);
|
|
work->field_11C = -1;
|
|
work->flame_delay = 10;
|
|
InitMissileFlame(work);
|
|
return 0;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
void *NewRMissile(MATRIX *world, int side)
|
|
{
|
|
Work *work;
|
|
|
|
work = GV_NewActor(EXEC_LEVEL, sizeof(Work));
|
|
|
|
if (work)
|
|
{
|
|
GV_SetNamedActor(&work->actor, Act, Die, "rmissile.c");
|
|
|
|
if (GetResources(work, world, side) < 0)
|
|
{
|
|
GV_DestroyActor(&work->actor);
|
|
return NULL;
|
|
}
|
|
|
|
work->boost_delay = 30;
|
|
work->field_112 = 0;
|
|
work->fpv_mode = FALSE;
|
|
work->field_117 = 0;
|
|
work->hud_delay = 0;
|
|
work->field_115 = 0;
|
|
work->field_116 = 0;
|
|
dword_8009F480 = 0;
|
|
work->field_118 = 0;
|
|
work->speed = 0;
|
|
work->field_11C = 0;
|
|
work->enemy_time = 0;
|
|
Nik_Blast = 0;
|
|
}
|
|
|
|
return (void *)work;
|
|
}
|