mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
b6ef3b591a
Due to ``#include "DallasFuncs.cpp"``, DF is recompiled 52 times. Rework it to build just once. The compile time goes down for me from 1m45.3s to 1m38.8s on my 1135G7 CPU running make -j8.
1350 lines
41 KiB
C++
1350 lines
41 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// AIGame.cpp
|
|
//
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "osiris_import.h"
|
|
#include "osiris_common.h"
|
|
#include "osiris_vector.h"
|
|
#include "DallasFuncs.h"
|
|
|
|
#include "module.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
DLLEXPORT char STDCALL InitializeDLL(tOSIRISModuleInit *func_list);
|
|
DLLEXPORT void STDCALL ShutdownDLL(void);
|
|
DLLEXPORT int STDCALL GetGOScriptID(const char *name, uint8_t isdoor);
|
|
DLLEXPORT void STDCALLPTR CreateInstance(int id);
|
|
DLLEXPORT void STDCALL DestroyInstance(int id, void *ptr);
|
|
DLLEXPORT int16_t STDCALL CallInstanceEvent(int id, void *ptr, int event, tOSIRISEventInfo *data);
|
|
DLLEXPORT int STDCALL SaveRestoreState(void *file_ptr, uint8_t saving_state);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
int String_table_size = 0;
|
|
char **String_table = NULL;
|
|
static const char *_Error_string = "!!ERROR MISSING STRING!!";
|
|
static const char *_Empty_string = "";
|
|
const char *GetStringFromTable(int index) {
|
|
if ((index < 0) || (index >= String_table_size))
|
|
return _Error_string;
|
|
if (!String_table[index])
|
|
return _Empty_string;
|
|
return String_table[index];
|
|
}
|
|
#define TXT(x) GetStringFromTable(x)
|
|
|
|
static void PortalBreakGlass(int portalnum, int roomnum);
|
|
// Returns the new child's handle
|
|
static int CreateAndAttach(int me, const char *child_name, uint8_t child_type, char parent_ap, char child_ap,
|
|
bool f_aligned = true, bool f_set_parent = false);
|
|
static float Obj_GetObjDist(int me, int it, bool f_sub_rads);
|
|
|
|
void PortalBreakGlass(int portalnum, int roomnum) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.roomnum = roomnum;
|
|
mstruct.portalnum = portalnum;
|
|
|
|
MSafe_CallFunction(MSAFE_ROOM_BREAK_GLASS, &mstruct);
|
|
}
|
|
|
|
// Returns the new child's handle
|
|
int CreateAndAttach(int me, const char *child_name, uint8_t child_type, char parent_ap, char child_ap, bool f_aligned,
|
|
bool f_set_parent) {
|
|
int child_handle = OBJECT_HANDLE_NONE;
|
|
int child_id = Obj_FindID(child_name);
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
MSafe_GetValue(MSAFE_OBJECT_POS, &m);
|
|
MSafe_GetValue(MSAFE_OBJECT_ROOMNUM, &m);
|
|
|
|
if (child_id >= 0) {
|
|
int parent;
|
|
if (f_set_parent)
|
|
parent = me;
|
|
else
|
|
parent = OBJECT_HANDLE_NONE;
|
|
|
|
child_handle = Obj_Create(child_type, child_id, m.roomnum, &m.pos, NULL, parent);
|
|
if (child_handle != OBJECT_HANDLE_NONE) {
|
|
if (!Obj_AttachObjectAP(me, parent_ap, child_handle, child_ap, f_aligned)) {
|
|
// chrishack (we need a way to instantly kill scripted objects)
|
|
}
|
|
}
|
|
}
|
|
|
|
return child_handle;
|
|
}
|
|
|
|
// ==========================
|
|
// Script class definitions//
|
|
// ==========================
|
|
#define NUM_IDS 6 // maximum number of IDs
|
|
|
|
#define ID_MERCENDBOSS 0 // Mercenary End Boss
|
|
#define ID_GUN 1 // Mercenary Mino Boss for level 6
|
|
#define ID_CASING 2
|
|
#define ID_HANGLIGHT 3 // Mercenary Hanging Light
|
|
#define ID_COMBWALLHIT 4 // Wall hit for purple big ass gun
|
|
#define ID_DROPTARGET 5
|
|
|
|
struct tScriptInfo {
|
|
int id;
|
|
const char *name;
|
|
};
|
|
|
|
tScriptInfo ScriptInfo[NUM_IDS] = {{ID_MERCENDBOSS, "MercEndBoss"}, {ID_GUN, "Gun"},
|
|
{ID_CASING, "Casing"}, {ID_HANGLIGHT, "HangLight"},
|
|
{ID_COMBWALLHIT, "CombWallHit"}, {ID_DROPTARGET, "DropTarget"}};
|
|
|
|
static int aigame_mod_id;
|
|
// use this macro to create unique timer IDs
|
|
#define CREATE_TIMER_ID(id) ((aigame_mod_id << 16) | (id))
|
|
|
|
class BaseObjScript {
|
|
public:
|
|
BaseObjScript();
|
|
~BaseObjScript();
|
|
virtual int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
struct tShieldOrbInfo {
|
|
int hitcount;
|
|
};
|
|
|
|
//------------------
|
|
// CombWallHit class
|
|
//------------------
|
|
|
|
struct combwallhit_data {
|
|
float time_left;
|
|
};
|
|
|
|
class CombWallHit : public BaseObjScript {
|
|
private:
|
|
combwallhit_data *memory;
|
|
|
|
public:
|
|
CombWallHit() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// DropTarget class
|
|
//------------------
|
|
|
|
struct droptarget_data {
|
|
float time_left;
|
|
float last_set_time;
|
|
};
|
|
|
|
class DropTarget : public BaseObjScript {
|
|
private:
|
|
droptarget_data *memory;
|
|
|
|
public:
|
|
DropTarget() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Gun class
|
|
//------------------
|
|
|
|
struct gun_data {
|
|
int shell_id;
|
|
bool f_fire;
|
|
};
|
|
|
|
class Gun : public BaseObjScript {
|
|
gun_data *memory;
|
|
|
|
public:
|
|
Gun() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Casing class
|
|
//------------------
|
|
|
|
struct casing_data {
|
|
float time_left;
|
|
};
|
|
|
|
class Casing : public BaseObjScript {
|
|
private:
|
|
casing_data *memory;
|
|
|
|
public:
|
|
Casing() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// MercEndBoss class
|
|
//------------------
|
|
|
|
#define MERCENDBOSS_WAIT 0
|
|
#define MERCENDBOSS_INTRO 1
|
|
#define MERCENDBOSS_MATTER 2
|
|
#define MERCENDBOSS_ENERGY 3
|
|
#define MERCENDBOSS_DEATH_BLOSSUM 4
|
|
#define MERCENDBOSS_DEATH 5
|
|
|
|
// Mercenary End Boss Unique Goal IDs
|
|
#define MEB_TURN_TOWARDS_CAMERA 0x00010001
|
|
|
|
struct mercendboss_data {
|
|
int flags;
|
|
char mode;
|
|
char next_mode;
|
|
int turret_object;
|
|
|
|
bool f_target_ghost;
|
|
float time_till_fire;
|
|
|
|
int wallhit_id;
|
|
|
|
bool f_oriented;
|
|
|
|
float last_frame;
|
|
|
|
int laser_sound;
|
|
float laser_time_left;
|
|
|
|
bool f_sub_super;
|
|
float mode_time;
|
|
float sub_mode_time_left;
|
|
|
|
float last_spark_time;
|
|
|
|
float time_to_next_random_sound;
|
|
|
|
int emitter[2];
|
|
int camera;
|
|
int combine_object;
|
|
};
|
|
|
|
class MercEndBoss : public BaseObjScript {
|
|
private:
|
|
mercendboss_data *memory;
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
bool DoNotify(int me_handle, tOSIRISEventInfo *data);
|
|
void SetMode(int me, char mode);
|
|
void SetSuper(int obj, bool f_super);
|
|
float GetSubModeTime(void);
|
|
void PlayRandomSound(int me);
|
|
|
|
public:
|
|
MercEndBoss() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// HangLight class
|
|
//------------------
|
|
|
|
class HangLight : public BaseObjScript {
|
|
private:
|
|
void DoInit(int me);
|
|
|
|
public:
|
|
HangLight() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//----------------
|
|
// Standard stuff
|
|
//----------------
|
|
|
|
// SaveRestoreState
|
|
// Purpose:
|
|
// This function is called when Descent 3 is saving or restoring the game state. In this function
|
|
// you should save/restore any global data that you want preserved through load/save (which includes
|
|
// demos). You must be very careful with this function, corrupting the file (reading or writing too
|
|
// much or too little) may be hazardous to the game (possibly making it impossible to restore the
|
|
// state). It would be best to use version information to keep older versions of saved states still
|
|
// able to be used. IT IS VERY IMPORTANT WHEN SAVING THE STATE TO RETURN THE NUMBER OF _BYTES_ WROTE
|
|
// TO THE FILE. When restoring the data, the return value is ignored. saving_state is 1 when you should
|
|
// write data to the file_ptr, 0 when you should read in the data.
|
|
int STDCALL SaveRestoreState(void *file_ptr, uint8_t saving_state) { return 0; }
|
|
|
|
char STDCALL InitializeDLL(tOSIRISModuleInit *func_list) {
|
|
osicommon_Initialize((tOSIRISModuleInit *)func_list);
|
|
if (func_list->game_checksum != CHECKSUM) {
|
|
mprintf(0, "Game-Checksum FAIL!!! (%ul!=%ul)\n", func_list->game_checksum, CHECKSUM);
|
|
mprintf(0, "RECOMPILE YOUR SCRIPTS!!!\n");
|
|
return 0;
|
|
}
|
|
aigame_mod_id = func_list->module_identifier;
|
|
String_table_size = func_list->string_count;
|
|
String_table = func_list->string_table;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void STDCALL ShutdownDLL(void) {}
|
|
|
|
int STDCALL GetGOScriptID(const char *name, uint8_t isdoor) {
|
|
for (int i = 0; i < NUM_IDS; i++) {
|
|
if (!stricmp(name, ScriptInfo[i].name)) {
|
|
return ScriptInfo[i].id;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void STDCALLPTR CreateInstance(int id) {
|
|
switch (id) {
|
|
case ID_MERCENDBOSS:
|
|
return new MercEndBoss;
|
|
break;
|
|
case ID_GUN:
|
|
return new Gun;
|
|
break;
|
|
case ID_CASING:
|
|
return new Casing;
|
|
break;
|
|
case ID_HANGLIGHT:
|
|
return new HangLight;
|
|
break;
|
|
case ID_COMBWALLHIT:
|
|
return new CombWallHit;
|
|
break;
|
|
case ID_DROPTARGET:
|
|
return new DropTarget;
|
|
break;
|
|
default:
|
|
mprintf(0, "SCRIPT: Illegal ID (%d)\n", id);
|
|
break;
|
|
};
|
|
return NULL;
|
|
}
|
|
|
|
void STDCALL DestroyInstance(int id, void *ptr) {
|
|
switch (id) {
|
|
case ID_MERCENDBOSS:
|
|
delete ((MercEndBoss *)ptr);
|
|
break;
|
|
case ID_GUN:
|
|
delete ((Gun *)ptr);
|
|
break;
|
|
case ID_CASING:
|
|
delete ((Casing *)ptr);
|
|
break;
|
|
case ID_HANGLIGHT:
|
|
delete ((HangLight *)ptr);
|
|
break;
|
|
case ID_COMBWALLHIT:
|
|
delete ((CombWallHit *)ptr);
|
|
break;
|
|
case ID_DROPTARGET:
|
|
delete ((DropTarget *)ptr);
|
|
break;
|
|
default:
|
|
mprintf(0, "SCRIPT: Illegal ID (%d)\n", id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int16_t STDCALL CallInstanceEvent(int id, void *ptr, int event, tOSIRISEventInfo *data) {
|
|
return ((BaseObjScript *)ptr)->CallEvent(event, data);
|
|
}
|
|
|
|
//============================================
|
|
// Functions
|
|
//============================================
|
|
|
|
float Obj_GetObjDist(int me, int it, bool f_sub_rads) {
|
|
vector me_pos;
|
|
vector it_pos;
|
|
float dist;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &me_pos);
|
|
Obj_Value(it, VF_GET, OBJV_V_POS, &it_pos);
|
|
|
|
dist = vm_VectorDistance(&me_pos, &it_pos);
|
|
|
|
if (f_sub_rads) {
|
|
float size;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_F_SIZE, &size);
|
|
dist -= size;
|
|
Obj_Value(it, VF_GET, OBJV_F_SIZE, &size);
|
|
dist -= size;
|
|
}
|
|
|
|
return dist;
|
|
}
|
|
|
|
static inline void AIClearNonHPGoals(int handle) {
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_GOALS; i++) {
|
|
if (i != 3)
|
|
AI_ClearGoal(handle, i);
|
|
}
|
|
}
|
|
|
|
static inline bool IsGoalFinishedNotify(int index) {
|
|
return (index == AIN_GOAL_COMPLETE || index == AIN_GOAL_INVALID || index == AIN_GOAL_FAIL || index == AIN_GOAL_ERROR);
|
|
}
|
|
|
|
//============================================
|
|
// Script Implementation
|
|
//============================================
|
|
BaseObjScript::BaseObjScript() {}
|
|
|
|
BaseObjScript::~BaseObjScript() {}
|
|
|
|
int16_t BaseObjScript::CallEvent(int event, tOSIRISEventInfo *data) { return CONTINUE_CHAIN | CONTINUE_DEFAULT; }
|
|
|
|
//------------------
|
|
// MercEndBoss class
|
|
//------------------
|
|
|
|
#define MATTER_TIME 10.0f
|
|
#define MATTER_SUPER_TIME 10.0f
|
|
#define ENERGY_TIME 20.0f
|
|
#define ENERGY_SUPER_TIME 10.0f
|
|
|
|
float MercEndBoss::GetSubModeTime(void) {
|
|
if (memory->mode == MERCENDBOSS_MATTER) {
|
|
if (memory->f_sub_super) {
|
|
return MATTER_TIME;
|
|
} else {
|
|
return MATTER_SUPER_TIME;
|
|
}
|
|
} else {
|
|
if (memory->f_sub_super) {
|
|
return ENERGY_SUPER_TIME;
|
|
} else {
|
|
return ENERGY_TIME;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MercEndBoss::SetSuper(int obj, bool f_super) {
|
|
int me = obj;
|
|
memory->f_sub_super = f_super;
|
|
|
|
if (memory->mode == MERCENDBOSS_MATTER) {
|
|
if (f_super) {
|
|
Obj_SetCustomAnim(memory->turret_object, 0.0f, 5.0f, 0.5f, AIAF_LOOPING, Sound_FindId("MercFBWinding"), AS_ALERT);
|
|
|
|
int flags = AIF_FIRE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
AI_Value(memory->turret_object, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
// flags = DWBF_ENABLED;
|
|
// Obj_WBValue(me, 0, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
// Obj_WBValue(me, 4, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
} else {
|
|
Obj_SetCustomAnim(memory->turret_object, 0.0f, 0.0f, 0.5f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
|
|
int flags = AIF_FIRE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
AI_Value(memory->turret_object, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
// flags = DWBF_ENABLED;
|
|
// Obj_WBValue(me, 0, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
// Obj_WBValue(me, 4, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
}
|
|
} else if (memory->mode == MERCENDBOSS_ENERGY) {
|
|
// int flags = DWBF_ENABLED;
|
|
//
|
|
// Obj_WBValue(me, 0, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
// Obj_WBValue(me, 4, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
|
|
int flags = AIF_FIRE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
if (f_super) {
|
|
aObjGhostSet(0, memory->combine_object);
|
|
aObjGhostSet(0, memory->emitter[0]);
|
|
aObjGhostSet(0, memory->emitter[1]);
|
|
|
|
Sound_Play3d(me, Sound_FindId("MercFBBeamCanon"));
|
|
|
|
aLightningCreate(memory->emitter[0], memory->combine_object, ENERGY_SUPER_TIME, 3.0f, 1,
|
|
Scrpt_FindTextureName("endbossbeamweapon2"), 1.0f, 1, 255, 255, 255, true);
|
|
aLightningCreate(memory->emitter[0], memory->combine_object, ENERGY_SUPER_TIME, 2.8f, 1,
|
|
Scrpt_FindTextureName("endbossbeamweapon2"), 1.95f, 1, 255, 255, 255, true);
|
|
|
|
aLightningCreate(memory->emitter[1], memory->combine_object, ENERGY_SUPER_TIME, 3.0f, 1,
|
|
Scrpt_FindTextureName("endbossbeamweapon2"), 1.0f, 1, 255, 255, 255, true);
|
|
aLightningCreate(memory->emitter[1], memory->combine_object, ENERGY_SUPER_TIME, 2.8f, 1,
|
|
Scrpt_FindTextureName("endbossbeamweapon2"), 1.96f, 1, 255, 255, 255, true);
|
|
|
|
aLightningCreate(memory->turret_object, memory->combine_object, ENERGY_SUPER_TIME, 3.0f, 1,
|
|
Scrpt_FindTextureName("endbossbeamweapon2"), 1.0f, 1, 255, 255, 255, true);
|
|
aLightningCreate(memory->turret_object, memory->combine_object, ENERGY_SUPER_TIME, 2.8f, 1,
|
|
Scrpt_FindTextureName("endbossbeamweapon2"), 1.96f, 1, 255, 255, 255, true);
|
|
|
|
float max_speed = 8500.0f;
|
|
AI_Value(me, VF_SET, AIV_F_MAX_TURN_RATE, &max_speed);
|
|
|
|
int flags = AIF_FIRE;
|
|
AI_Value(memory->turret_object, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
} else {
|
|
aObjGhostSet(1, memory->combine_object);
|
|
aObjGhostSet(1, memory->emitter[0]);
|
|
aObjGhostSet(1, memory->emitter[1]);
|
|
|
|
int flags = AIF_FIRE;
|
|
AI_Value(memory->turret_object, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
float max_speed = 10100.0f;
|
|
AI_Value(me, VF_SET, AIV_F_MAX_TURN_RATE, &max_speed);
|
|
}
|
|
}
|
|
|
|
memory->sub_mode_time_left = GetSubModeTime();
|
|
}
|
|
|
|
#define DEATH_ROT_ACC 5000.0f
|
|
|
|
bool MercEndBoss::DoNotify(int me, tOSIRISEventInfo *data) {
|
|
switch (memory->mode) {
|
|
case MERCENDBOSS_WAIT: {
|
|
if (data->evt_ai_notify.notify_type == AIN_MOVIE_START) {
|
|
SetMode(me, MERCENDBOSS_INTRO);
|
|
}
|
|
} break;
|
|
case MERCENDBOSS_INTRO: {
|
|
switch (data->evt_ai_notify.notify_type) {
|
|
case AIN_MOVIE_END:
|
|
SetMode(me, MERCENDBOSS_MATTER);
|
|
break;
|
|
case AIN_SCRIPTED_ORIENT: {
|
|
vector uvec = {0.0f, 1.0f, 0.0f};
|
|
vector fvec = {-1.0f, 0.0f, 0.0f};
|
|
if (AI_TurnTowardsVectors(me, &fvec, &uvec)) {
|
|
memory->f_oriented = true;
|
|
}
|
|
} break;
|
|
case AIN_SCRIPTED_GOAL:
|
|
vector zv = {0.0f, 0.0f, 0.0f};
|
|
AI_Value(me, VF_SET, AIV_V_MOVEMENT_DIR, &zv);
|
|
break;
|
|
}
|
|
} break;
|
|
case MERCENDBOSS_DEATH: {
|
|
// NOTE: I am also doing the rotational stuff here... So, no need to make a separate AIN_SCRIPTED_ORIENT event
|
|
if (data->evt_ai_notify.goal_num == 0 && data->evt_ai_notify.notify_type == AIN_SCRIPTED_GOAL) {
|
|
vector rvel;
|
|
Obj_Value(me, VF_GET, OBJV_V_ROTVELOCITY, &rvel);
|
|
|
|
rvel.x -= 0.1f * DEATH_ROT_ACC * Game_GetFrameTime();
|
|
rvel.y -= DEATH_ROT_ACC * Game_GetFrameTime();
|
|
rvel.z += 0.3f * DEATH_ROT_ACC * Game_GetFrameTime();
|
|
|
|
Obj_Value(me, VF_SET, OBJV_V_ROTVELOCITY, &rvel);
|
|
|
|
vector dir = Zero_vector;
|
|
AI_Value(me, VF_SET, AIV_V_MOVEMENT_DIR, &dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MercEndBoss::SetMode(int me, char mode) {
|
|
AI_SetType(me, AIT_EVADER1);
|
|
memory->mode_time = 0.0f;
|
|
|
|
if (memory->mode == MERCENDBOSS_INTRO) {
|
|
mprintf(0, "Here end of intro\n");
|
|
int flags = DWBF_ENABLED;
|
|
Obj_WBValue(me, 0, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(me, 4, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
|
|
// The (AIF_DETERMINE_TARGET | AIF_AIM) flags get set here to make absolute sure that no timing issues mess things
|
|
// up
|
|
flags = AIF_FIRE | AIF_DODGE | AIF_DETERMINE_TARGET | AIF_AIM;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
PortalBreakGlass(i, 167);
|
|
}
|
|
}
|
|
|
|
switch (mode) {
|
|
case MERCENDBOSS_INTRO: {
|
|
mprintf(0, "Here intro begin\n");
|
|
|
|
// Make him fire during the cinematics :)
|
|
int flags = AISR_OK_TO_FIRE_DURING_CINEMATICS;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_STATUS_REG, &flags);
|
|
|
|
// Make sure he doesn't sleep
|
|
flags = AIF_FORCE_AWARENESS;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
float awareness = AWARE_FULLY;
|
|
AI_Value(me, VF_SET, AIV_F_AWARENESS, &awareness);
|
|
|
|
// Destroy all non-boss robots
|
|
aDestroyAllRobotsInit();
|
|
aDestroyAllRobotsSpareHandle(me);
|
|
aDestroyAllRobotsSpareHandle(memory->turret_object);
|
|
aDestroyAllRobotsSpareHandle(memory->emitter[0]);
|
|
aDestroyAllRobotsSpareHandle(memory->emitter[1]);
|
|
aDestroyAllRobotsSpareHandle(memory->combine_object);
|
|
aDestroyAllRobotsEnd();
|
|
|
|
// flags = AIF_FIRE;
|
|
// AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
// AI_Value(memory->turret_object, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
flags = DWBF_ENABLED;
|
|
Obj_WBValue(me, 0, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(me, 4, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
} break;
|
|
case MERCENDBOSS_MATTER: {
|
|
aShowHUDMessage("GUIDEBOT: We should stay as far away from him as we can.");
|
|
|
|
int flags = AIF_DETERMINE_TARGET | AIF_AIM;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
flags = AISR_RANGED_ATTACK;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_STATUS_REG, &flags);
|
|
mprintf(0, "Here matter\n");
|
|
} break;
|
|
case MERCENDBOSS_ENERGY: {
|
|
aShowHUDMessage("GUIDEBOT: Wow! We better get behind him.");
|
|
|
|
Obj_SetCustomAnim(me, 1.0f, 5.0f, 2.0f, 0, Sound_FindId("MercFBServo"), -1);
|
|
Obj_Kill(memory->turret_object, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BREAKS_APART | DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_LARGE | DF_CONTACT_BREAKS_APART |
|
|
DF_CONTACT_BLAST_RING | DF_DEBRIS_SMOKES | DF_DEBRIS_FIREBALL | DF_DEBRIS_BLAST_RING,
|
|
0.0f, 0.0f);
|
|
memory->turret_object = CreateAndAttach(me, "FinalbossLITTLETIT", OBJ_BUILDING, 0, 0, true, true);
|
|
|
|
aObjSpark(memory->turret_object, 15, 1000000.0f);
|
|
|
|
float tl;
|
|
AI_Value(me, VF_GET, AIV_F_LEAD_ACCURACY, &tl);
|
|
tl *= 0.8f;
|
|
AI_Value(me, VF_SET, AIV_F_LEAD_ACCURACY, &tl);
|
|
|
|
aTurnOnSpew(memory->turret_object, 0, MED_SMOKE_INDEX, 10.0f, .001f, PF_REVERSE_GRAVITY, 0, 1.0f, .05f, 100000.0f,
|
|
2.0f, 0.0, -1, -1);
|
|
} break;
|
|
case MERCENDBOSS_DEATH_BLOSSUM:
|
|
break;
|
|
case MERCENDBOSS_DEATH: {
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
int flags = AIF_FIRE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
AI_Value(memory->turret_object, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
vector vel = {0.0f, 0.0f, 0.0f};
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
|
|
AI_AddGoal(me, AIG_SCRIPTED, 0, 1000.0f, -1, GF_NONFLUSHABLE | GF_ORIENT_SCRIPTED, NULL);
|
|
|
|
float drag = 0.0f;
|
|
Obj_Value(me, VF_SET, OBJV_F_ROTDRAG, &drag);
|
|
|
|
flags = PF_LEVELING;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
// float circle_dist = -1.0f;
|
|
// AI_Value(me, VF_SET, AIV_F_CIRCLE_DIST, &circle_dist);
|
|
|
|
// Obj_SetCustomAnim(me, 495.0f, 650.0f, 15.0f, 0, Sound_FindId("RbtMercEndBossDeath"), -1);
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
memory->mode = mode;
|
|
SetSuper(me, false);
|
|
}
|
|
|
|
void MercEndBoss::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(mercendboss_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
int i;
|
|
|
|
memory = (mercendboss_data *)Scrpt_MemAlloc(&ch);
|
|
memory->flags = 0;
|
|
memory->mode = MERCENDBOSS_WAIT;
|
|
|
|
memory->turret_object = CreateAndAttach(me, "FinalbossBIGGUN", OBJ_BUILDING, 0, 0, true, true);
|
|
|
|
int proom;
|
|
vector ppos;
|
|
matrix porient;
|
|
msafe_struct mstruct;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &proom);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &ppos);
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &porient);
|
|
|
|
memory->wallhit_id = Obj_FindID("Finalbosswallhit");
|
|
|
|
memory->combine_object = Obj_Create(OBJ_POWERUP, Obj_FindID("FinalbossBEAMCOMBINE"), proom, &ppos, &porient, me);
|
|
mprintf(0, "OBJECT COMB is %d\n", memory->combine_object);
|
|
aObjGhostSet(1, memory->combine_object);
|
|
|
|
memory->camera = Obj_Create(OBJ_POWERUP, Obj_FindID("Invisiblepowerup"), proom, &ppos, &porient, me);
|
|
mstruct.objhandle = memory->camera;
|
|
MSafe_CallFunction(MSAFE_OBJECT_NO_RENDER, &mstruct);
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
if (i == 0)
|
|
memory->emitter[i] = CreateAndAttach(me, "Finalbossemitter", OBJ_BUILDING, 1 + i, 0, true, true);
|
|
else
|
|
memory->emitter[i] = CreateAndAttach(me, "Finalbossemitterleft", OBJ_BUILDING, 1 + i, 0, true, true);
|
|
|
|
aObjGhostSet(1, memory->emitter[i]);
|
|
}
|
|
|
|
memory->laser_sound = -1;
|
|
memory->last_frame = 0.0f;
|
|
memory->laser_time_left = 0.0f;
|
|
|
|
memory->f_oriented = false;
|
|
|
|
memory->f_target_ghost = false;
|
|
memory->time_till_fire = 0.0f;
|
|
|
|
int flags = AIF_DETERMINE_TARGET;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
memory->last_spark_time = Game_GetTime();
|
|
memory->time_to_next_random_sound = 0.0f;
|
|
|
|
SetMode(me, MERCENDBOSS_WAIT);
|
|
}
|
|
|
|
void MercEndBoss::PlayRandomSound(int me) {
|
|
memory->time_to_next_random_sound -= Game_GetFrameTime();
|
|
if (memory->time_to_next_random_sound < 0.0f) {
|
|
switch (rand() % 9) {
|
|
case 0:
|
|
Sound_Play3d(me, Sound_FindId("MercFBDanger1"));
|
|
break;
|
|
case 1:
|
|
Sound_Play3d(me, Sound_FindId("MercFBDanger2"));
|
|
break;
|
|
case 2:
|
|
Sound_Play3d(me, Sound_FindId("MercFBDanger3"));
|
|
break;
|
|
case 3:
|
|
Sound_Play3d(me, Sound_FindId("MercFBDanger4"));
|
|
break;
|
|
case 4:
|
|
Sound_Play3d(me, Sound_FindId("MercFBIntruder1"));
|
|
break;
|
|
case 5:
|
|
Sound_Play3d(me, Sound_FindId("MercFBIntruder2"));
|
|
break;
|
|
case 6:
|
|
Sound_Play3d(me, Sound_FindId("MercFBIntruder3"));
|
|
break;
|
|
case 7:
|
|
Sound_Play3d(me, Sound_FindId("MercFBIntruder4"));
|
|
break;
|
|
case 8:
|
|
Sound_Play3d(me, Sound_FindId("MercFBIntruder5"));
|
|
break;
|
|
}
|
|
|
|
memory->time_to_next_random_sound = (((float)rand() / (float)RAND_MAX) * 3.0f) + 3.0f;
|
|
}
|
|
}
|
|
|
|
void MercEndBoss::DoFrame(int me) {
|
|
float frame;
|
|
float shields;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &frame);
|
|
Obj_Value(memory->turret_object, VF_GET, OBJV_F_SHIELDS, &shields);
|
|
|
|
if (memory->mode == MERCENDBOSS_MATTER || memory->mode == MERCENDBOSS_ENERGY) {
|
|
int target;
|
|
int type;
|
|
AI_Value(me, VF_GET, AIV_I_TARGET_HANDLE, &target);
|
|
Obj_Value(target, VF_GET, OBJV_I_TYPE, &type);
|
|
int flags = 0;
|
|
|
|
if (type == OBJ_PLAYER) {
|
|
Player_Value(target, VF_GET, PLYV_I_FLAGS, &flags);
|
|
}
|
|
|
|
if (type == OBJ_PLAYER && (flags & PLAYER_FLAGS_DEAD)) {
|
|
memory->f_target_ghost = true;
|
|
memory->time_till_fire = 3.0f;
|
|
|
|
int flags = DWBF_ENABLED;
|
|
Obj_WBValue(me, 0, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(me, 4, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(memory->turret_object, 0, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(memory->turret_object, 1, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
} else if (memory->f_target_ghost) {
|
|
memory->time_till_fire -= Game_GetFrameTime();
|
|
if (memory->time_till_fire <= 0.0f) {
|
|
memory->time_till_fire = 0.0f;
|
|
memory->f_target_ghost = false;
|
|
|
|
int flags = DWBF_ENABLED;
|
|
Obj_WBValue(me, 0, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(me, 4, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(memory->turret_object, 0, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(memory->turret_object, 1, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
}
|
|
}
|
|
} else if (memory->f_target_ghost) {
|
|
memory->time_till_fire = 0.0f;
|
|
memory->f_target_ghost = false;
|
|
|
|
int flags = DWBF_ENABLED;
|
|
Obj_WBValue(me, 0, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(me, 4, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(memory->turret_object, 0, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
Obj_WBValue(memory->turret_object, 1, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
}
|
|
|
|
if (memory->mode == MERCENDBOSS_WAIT) {
|
|
} else if (memory->mode == MERCENDBOSS_INTRO) {
|
|
if (memory->mode_time > 4.0f && memory->mode_time < 9.0f) {
|
|
if (!memory->f_oriented) {
|
|
int flags = AISR_RANGED_ATTACK;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_STATUS_REG, &flags);
|
|
} else {
|
|
int flags = AIF_DETERMINE_TARGET | AIF_AIM;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
flags = AISR_RANGED_ATTACK;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_STATUS_REG, &flags);
|
|
}
|
|
} else if (memory->mode_time < 5.0f && memory->mode_time + Game_GetFrameTime() >= 4.0f) {
|
|
AI_AddGoal(me, AIG_SCRIPTED, 3, 1.0f, MEB_TURN_TOWARDS_CAMERA, GF_ORIENT_SCRIPTED, NULL);
|
|
}
|
|
|
|
} else if (memory->mode == MERCENDBOSS_DEATH) {
|
|
int flags = AISR_RANGED_ATTACK;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_STATUS_REG, &flags);
|
|
AI_Value(memory->turret_object, VF_SET_FLAGS, AIV_I_STATUS_REG, &flags);
|
|
|
|
if (memory->last_spark_time + .1f < Game_GetTime()) {
|
|
memory->last_spark_time = Game_GetTime();
|
|
|
|
int room;
|
|
vector pos;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
Game_CreateRandomSparks(15, &pos, room);
|
|
}
|
|
|
|
if (memory->mode_time < 5.0f && memory->mode_time + Game_GetFrameTime() >= 5.0f) {
|
|
Sound_Play3d(me, Sound_FindId("MercFBError1"));
|
|
}
|
|
if (memory->mode_time < 10.0f && memory->mode_time + Game_GetFrameTime() >= 10.0f) {
|
|
Sound_Play3d(me, Sound_FindId("MercFBError2"));
|
|
}
|
|
if (memory->mode_time < 15.0f && memory->mode_time + Game_GetFrameTime() >= 15.0f) {
|
|
Sound_Play3d(me, Sound_FindId("MercFBError3"));
|
|
}
|
|
|
|
if (memory->mode_time < 3.0f && memory->mode_time + Game_GetFrameTime() >= 3.0f) {
|
|
Obj_Burning(me, 1000000.0f, 1.0f);
|
|
}
|
|
|
|
if (memory->mode_time > 20.0f) {
|
|
Obj_Kill(me, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_LARGE | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
// Obj_Kill(me, OBJECT_HANDLE_NONE, 1000.0f,
|
|
// DF_BREAKS_APART|DF_BLAST_RING|DF_LOSES_ANTIGRAV|DF_EXPL_LARGE|DF_CONTACT_BREAKS_APART|DF_CONTACT_BLAST_RING|DF_DEBRIS_SMOKES|DF_DEBRIS_FIREBALL|DF_DEBRIS_BLAST_RING,
|
|
// 0.0f, 0.0f);
|
|
Obj_Kill(memory->turret_object, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BREAKS_APART | DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_LARGE | DF_CONTACT_BREAKS_APART |
|
|
DF_CONTACT_BLAST_RING | DF_DEBRIS_SMOKES | DF_DEBRIS_FIREBALL | DF_DEBRIS_BLAST_RING,
|
|
0.0f, 0.0f);
|
|
|
|
aShowHUDMessage("GUIDEBOT: I love this job.");
|
|
}
|
|
} else if (memory->mode == MERCENDBOSS_MATTER) {
|
|
int flags;
|
|
Obj_Value(memory->turret_object, VF_GET, OBJV_I_FLAGS, &flags);
|
|
|
|
if (flags & OF_AI_DEATH) {
|
|
SetMode(me, MERCENDBOSS_ENERGY);
|
|
}
|
|
|
|
PlayRandomSound(me);
|
|
} else if (memory->mode == MERCENDBOSS_ENERGY) {
|
|
if (memory->f_sub_super) {
|
|
if (memory->sub_mode_time_left > ENERGY_SUPER_TIME - 1.5f &&
|
|
memory->sub_mode_time_left - Game_GetFrameTime() <= ENERGY_SUPER_TIME - 1.5f) {
|
|
aLightningCreate(memory->combine_object, memory->camera, ENERGY_SUPER_TIME - 1.5f, 6.0f, 1,
|
|
Scrpt_FindTextureName("endbossbeamweapon2"), 1.0f, 1, 255, 255, 255, true);
|
|
aLightningCreate(memory->combine_object, memory->camera, ENERGY_SUPER_TIME - 1.5f, 6.0f, 1,
|
|
Scrpt_FindTextureName("endbossbeamweapon2"), 3.1111f, 1, 255, 255, 255, true);
|
|
}
|
|
|
|
vector pos;
|
|
matrix orient;
|
|
vector start_pos;
|
|
vector end_pos;
|
|
vector collision_pos;
|
|
vector collision_normal;
|
|
int num_iterations = 0;
|
|
int num_ignore = 0;
|
|
int ignore_list[100];
|
|
int fate;
|
|
ray_info ray;
|
|
|
|
int room = 76;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
pos += (orient.fvec * 50.0f) + (orient.uvec * 2.0f);
|
|
Obj_Value(memory->combine_object, VF_SET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->combine_object, VF_SET, OBJV_I_ROOMNUM, &room);
|
|
start_pos = pos;
|
|
|
|
pos += (orient.fvec * 1200.0f);
|
|
Obj_Value(memory->camera, VF_SET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->camera, VF_SET, OBJV_I_ROOMNUM, &room);
|
|
end_pos = pos;
|
|
|
|
if (memory->sub_mode_time_left <= 8.5f) {
|
|
ignore_list[num_ignore++] = memory->camera;
|
|
ignore_list[num_ignore++] = memory->emitter[0];
|
|
ignore_list[num_ignore++] = memory->emitter[1];
|
|
ignore_list[num_ignore++] = me;
|
|
ignore_list[num_ignore++] = memory->turret_object;
|
|
ignore_list[num_ignore++] = memory->combine_object;
|
|
ignore_list[num_ignore++] = -1;
|
|
|
|
vector saved_start = start_pos;
|
|
|
|
do {
|
|
Obj_Value(me, VF_SET, OBJV_PI_HACK_FVI_IGNORE_LIST, ignore_list);
|
|
fate = FVI_RayCast(OBJECT_HANDLE_NONE, &start_pos, &end_pos, 76, 13.0f,
|
|
(FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_OBJ_BACKFACE | FQ_BACKFACE |
|
|
FQ_PLAYERS_AS_SPHERE | FQ_IGNORE_WALLS | FQ_NO_RELINK),
|
|
&ray);
|
|
Obj_Value(me, VF_SET, OBJV_PI_HACK_FVI_IGNORE_LIST, NULL);
|
|
|
|
if (fate != HIT_NONE) {
|
|
start_pos = ray.hit_point + (orient.fvec * 13.0f);
|
|
}
|
|
|
|
if (fate == HIT_OBJECT || fate == HIT_SPHERE_2_POLY_OBJECT) {
|
|
int type;
|
|
|
|
Obj_Value(ray.hit_object, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_PLAYER || type == OBJ_ROBOT) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = ray.hit_object;
|
|
mstruct.killer_handle = me;
|
|
mstruct.damage_type = PD_ENERGY_WEAPON;
|
|
mstruct.amount = 10.0f * Game_GetFrameTime();
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
|
|
// if(ray.hit_wallnorm * orient.fvec <= 0.0f)
|
|
// {
|
|
vector bf_pos = ray.hit_point + (orient.fvec * 3.0f);
|
|
// vector bf_pos = ray.hit_point + (orient.fvec *
|
|
//-9.0f);
|
|
Obj_Create(OBJ_POWERUP, memory->wallhit_id, 76, &bf_pos);
|
|
// //mprintf(0, "HERE X\n");
|
|
// }
|
|
// else
|
|
// {
|
|
// vector bf_pos = ray.hit_point + (orient.fvec
|
|
//* 9.0f); Obj_Create(OBJ_POWERUP,
|
|
// memory->wallhit_id, 76, &bf_pos);
|
|
/// //mprintf(0, "HERE X\n");
|
|
// }
|
|
}
|
|
|
|
ignore_list[num_ignore++] = ray.hit_object;
|
|
}
|
|
ignore_list[num_ignore] = -1;
|
|
|
|
num_iterations++;
|
|
} while (fate != HIT_NONE && num_iterations < 10);
|
|
|
|
start_pos = saved_start;
|
|
num_iterations = 0;
|
|
|
|
// CHECK THE WALLS
|
|
do {
|
|
fate = FVI_RayCast(OBJECT_HANDLE_NONE, &start_pos, &end_pos, 76, 0.0f, FQ_NO_RELINK, &ray);
|
|
|
|
if (fate != HIT_NONE) {
|
|
start_pos = ray.hit_point + (orient.fvec * 5.0f);
|
|
|
|
if (ray.hit_wallnorm * orient.fvec <= 0.0f) {
|
|
vector bf_pos = ray.hit_point + (orient.fvec * -9.0f);
|
|
Obj_Create(OBJ_POWERUP, memory->wallhit_id, 76, &bf_pos);
|
|
// mprintf(0, "HERE X\n");
|
|
} else {
|
|
vector bf_pos = ray.hit_point + (orient.fvec * 9.0f);
|
|
Obj_Create(OBJ_POWERUP, memory->wallhit_id, 76, &bf_pos);
|
|
// mprintf(0, "HERE X\n");
|
|
}
|
|
}
|
|
|
|
num_iterations++;
|
|
} while (fate != HIT_NONE && num_iterations < 10);
|
|
|
|
start_pos = saved_start;
|
|
num_iterations = 0;
|
|
|
|
// CHECK THE WALLS
|
|
do {
|
|
fate = FVI_RayCast(OBJECT_HANDLE_NONE, &end_pos, &start_pos, 76, 0.0f, FQ_NO_RELINK, &ray);
|
|
|
|
if (fate != HIT_NONE) {
|
|
end_pos = ray.hit_point - (orient.fvec * 5.0f);
|
|
|
|
if (ray.hit_wallnorm * orient.fvec <= 0.0f) {
|
|
vector bf_pos = ray.hit_point + (orient.fvec * -9.0f);
|
|
Obj_Create(OBJ_POWERUP, memory->wallhit_id, 76, &bf_pos);
|
|
// mprintf(0, "HERE X\n");
|
|
} else {
|
|
vector bf_pos = ray.hit_point + (orient.fvec * 9.0f);
|
|
Obj_Create(OBJ_POWERUP, memory->wallhit_id, 76, &bf_pos);
|
|
// mprintf(0, "HERE X\n");
|
|
}
|
|
}
|
|
|
|
num_iterations++;
|
|
} while (fate != HIT_NONE && num_iterations < 10);
|
|
|
|
float last_see_game_time = Game_GetTime();
|
|
AI_Value(me, VF_SET, AIV_F_LAST_SEE_TARGET_TIME, &last_see_game_time);
|
|
AI_Value(memory->turret_object, VF_SET, AIV_F_LAST_SEE_TARGET_TIME, &last_see_game_time);
|
|
}
|
|
} else {
|
|
int flags;
|
|
Obj_Value(memory->turret_object, VF_GET, OBJV_I_FLAGS, &flags);
|
|
|
|
if (flags & OF_AI_DEATH) {
|
|
SetMode(me, MERCENDBOSS_DEATH);
|
|
}
|
|
}
|
|
|
|
PlayRandomSound(me);
|
|
}
|
|
|
|
memory->sub_mode_time_left -= Game_GetFrameTime();
|
|
if (memory->sub_mode_time_left <= 0.0f) {
|
|
SetSuper(me, !memory->f_sub_super);
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
memory->last_frame = frame;
|
|
}
|
|
|
|
int16_t MercEndBoss::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_COLLIDE: {
|
|
int type;
|
|
Obj_Value(data->evt_collide.it_handle, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_PLAYER || type == OBJ_ROBOT) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = data->evt_collide.it_handle;
|
|
mstruct.killer_handle = data->me_handle;
|
|
mstruct.damage_type = PD_ENERGY_WEAPON;
|
|
mstruct.amount = 50.0f * Game_GetFrameTime();
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
}
|
|
} break;
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return (DoNotify(data->me_handle, data) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_DESTROY: {
|
|
// shake players
|
|
msafe_struct mstruct;
|
|
mstruct.amount = 90.0f;
|
|
MSafe_CallFunction(MSAFE_OBJECT_VIEWER_SHAKE, &mstruct);
|
|
|
|
Obj_Kill(memory->turret_object, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_LARGE | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES, 0.0f,
|
|
0.0f);
|
|
|
|
int i;
|
|
msafe_struct mo;
|
|
|
|
mo.objhandle = memory->camera;
|
|
mo.playsound = 0;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
mo.objhandle = memory->emitter[i];
|
|
mo.playsound = 0;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
}
|
|
|
|
mo.objhandle = memory->combine_object;
|
|
mo.playsound = 0;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
} break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (mercendboss_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Gun class
|
|
//------------------
|
|
|
|
int16_t Gun::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT: {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(gun_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = data->me_handle;
|
|
|
|
memory = (gun_data *)Scrpt_MemAlloc(&ch);
|
|
memory->shell_id = Obj_FindID("finalbossshellcasing");
|
|
memory->f_fire = true;
|
|
} break;
|
|
case EVT_COLLIDE: {
|
|
int type;
|
|
Obj_Value(data->evt_collide.it_handle, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_PLAYER || type == OBJ_ROBOT) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = data->evt_collide.it_handle;
|
|
mstruct.killer_handle = data->me_handle;
|
|
mstruct.damage_type = PD_ENERGY_WEAPON;
|
|
mstruct.amount = 50.0f * Game_GetFrameTime();
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
}
|
|
} break;
|
|
case EVT_AI_NOTIFY:
|
|
if (data->evt_ai_notify.notify_type == AIN_FIRED_WEAPON) {
|
|
memory->f_fire = !memory->f_fire;
|
|
|
|
if (memory->f_fire)
|
|
;
|
|
{
|
|
int room;
|
|
vector pos;
|
|
vector dir;
|
|
matrix orient;
|
|
vector velocity;
|
|
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
Obj_GetGunPos(data->me_handle, 5, &pos, &dir);
|
|
dir *= -1.0f;
|
|
vm_VectorToMatrix(&orient, &dir, NULL, NULL);
|
|
|
|
velocity = dir * qRandomValue(30.0f, 35.0f);
|
|
|
|
Obj_Create(OBJ_BUILDING, memory->shell_id, room, &pos, &orient, data->me_handle, &velocity);
|
|
}
|
|
}
|
|
return CONTINUE_CHAIN;
|
|
break;
|
|
case EVT_DESTROY: {
|
|
} break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (gun_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Casing class
|
|
//------------------
|
|
|
|
int16_t Casing::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_CREATED: {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(casing_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = data->me_handle;
|
|
|
|
memory = (casing_data *)Scrpt_MemAlloc(&ch);
|
|
memory->time_left = qRandomValue(5.0f, 10.0f);
|
|
} break;
|
|
case EVT_INTERVAL:
|
|
memory->time_left -= Game_GetFrameTime();
|
|
if (memory->time_left < 0.0f) {
|
|
msafe_struct mo;
|
|
mo.objhandle = data->me_handle;
|
|
mo.playsound = 0;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
}
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return CONTINUE_CHAIN;
|
|
break;
|
|
case EVT_DESTROY: {
|
|
} break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (casing_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// CombWallHit class
|
|
//------------------
|
|
|
|
int16_t CombWallHit::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_CREATED: {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(combwallhit_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = data->me_handle;
|
|
|
|
memory = (combwallhit_data *)Scrpt_MemAlloc(&ch);
|
|
memory->time_left = Game_GetFrameTime() * 1.05f;
|
|
// mprintf(0, "HERE\n");
|
|
} break;
|
|
case EVT_INTERVAL:
|
|
memory->time_left -= Game_GetFrameTime();
|
|
if (memory->time_left < 0.0f) {
|
|
msafe_struct mo;
|
|
mo.objhandle = data->me_handle;
|
|
mo.playsound = 0;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
}
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return CONTINUE_CHAIN;
|
|
break;
|
|
case EVT_DESTROY: {
|
|
} break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (combwallhit_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// DropTarget class
|
|
//------------------
|
|
|
|
int16_t DropTarget::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_CREATED: {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(droptarget_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = data->me_handle;
|
|
|
|
memory = (droptarget_data *)Scrpt_MemAlloc(&ch);
|
|
memory->time_left = 5.0f;
|
|
memory->last_set_time = Game_GetTime();
|
|
// mprintf(0, "HERE\n");
|
|
} break;
|
|
case EVT_INTERVAL: {
|
|
float frame;
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_F_ANIM_FRAME, &frame);
|
|
|
|
if (frame == 5.0f) {
|
|
memory->time_left -= Game_GetFrameTime();
|
|
if (memory->time_left < 0.0f) {
|
|
memory->time_left = 5.0f;
|
|
Obj_SetCustomAnim(data->me_handle, 5.0f, 10.0f, 0.5f, 0, Sound_FindId("DorPTMCCovertDOpenB"), -1);
|
|
}
|
|
}
|
|
} break;
|
|
case EVT_COLLIDE: {
|
|
float frame;
|
|
char type;
|
|
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_F_ANIM_FRAME, &frame);
|
|
AI_Value(data->me_handle, VF_GET, AIV_C_NEXT_ANIMATION_TYPE, &type);
|
|
|
|
if ((frame == 0.0f || frame == 10.0f) && type != AS_CUSTOM && memory->last_set_time + 5.0f < Game_GetTime()) {
|
|
memory->last_set_time = Game_GetTime();
|
|
Obj_SetCustomAnim(data->me_handle, 0.0f, 5.0f, 0.5f, 0, Sound_FindId("DorPTMCCovertDOpenB"), -1);
|
|
}
|
|
} break;
|
|
case EVT_DESTROY: {
|
|
} break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (droptarget_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Hanging Light
|
|
//------------------
|
|
|
|
void HangLight::DoInit(int me) { CreateAndAttach(me, "MERC3_danglinglightbulb", OBJ_CLUTTER, 0, 0, true, true); }
|
|
|
|
int16_t HangLight::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_CREATED:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|