Descent3/scripts/aigame4.cpp
Jan Engelhardt b6ef3b591a build: avoid repeat compilation of DallasFuncs.cpp
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.
2024-09-09 15:29:40 +02:00

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