mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
11281 lines
318 KiB
C++
11281 lines
318 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.cpp"
|
|
|
|
#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
|
|
|
|
static int String_table_size = 0;
|
|
static 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)
|
|
#define TXT_GBM_RENAME 0 //"Rename GB Unit"
|
|
#define TXT_GBM_REPAIR 1 //"Repair GB"
|
|
#define TXT_GBM_EXITSHIP 2 //"Exit ship"
|
|
#define TXT_GBM_P0 3 //"P0"
|
|
#define TXT_GBM_P1 4 //"P1"
|
|
#define TXT_GBM_P2 5 //"P2"
|
|
#define TXT_GBM_P3 6 //"P3"
|
|
#define TXT_GBM_P4 7 //"P4"
|
|
#define TXT_GBM_P5 8 //"P5"
|
|
#define TXT_GBM_P6 9 //"P6"
|
|
#define TXT_GBM_P7 10 //"P7"
|
|
#define TXT_GBM_S0 11 //"S0"
|
|
#define TXT_GBM_S1 12 //"S1"
|
|
#define TXT_GBM_S2 13 //"S2"
|
|
#define TXT_GBM_S3 14 //"S3"
|
|
#define TXT_GBM_S4 15 //"S4"
|
|
#define TXT_GBM_S5 16 //"S5"
|
|
#define TXT_GBM_S6 17 //"S6"
|
|
#define TXT_GBM_S7 18 //"S7"
|
|
#define TXT_GBM_FINDPOWERUPS 19 //"Find my powerups"
|
|
#define TXT_GBM_FINEPOWERUP 20 //"Find powerup"
|
|
#define TXT_GBM_FINDENERGY 21 //"Find energy"
|
|
#define TXT_GBM_FINDROBOT 22 //"Find robot"
|
|
#define TXT_GBM_FINDTHIEF 23 //"Find Thief"
|
|
#define TXT_GBM_FINDEXIT 24 //"Find an exit to the mine"
|
|
#define TXT_GBM_FINDMINE 25 //"Find a mine"
|
|
#define TXT_GBM_FINDCMARK 26 //"Find closest marker"
|
|
#define TXT_GBM_M0 27 //"Find Marker 0"
|
|
#define TXT_GBM_M1 28 //"Find Marker 1"
|
|
#define TXT_GBM_M2 29 //"Find Marker 2"
|
|
#define TXT_GBM_M3 30 //"Find Marker 3"
|
|
#define TXT_GBM_M4 31 //"Find Marker 4"
|
|
#define TXT_GBM_M5 32 //"Find Marker 5"
|
|
#define TXT_GBM_M6 33 //"Find Marker 6"
|
|
#define TXT_GBM_M7 34 //"Find Marker 7"
|
|
#define TXT_GBM_ESCORT 35 //"Escort ship"
|
|
#define TXT_GBM_USEEXTIN 36 //"Use fire extinguisher"
|
|
#define TXT_GBM_WINGNUT 37 //"Use Go Wingnut Powerup"
|
|
#define TXT_GBM_PILOTPOWER 38 //"Use Manual Pilot Powerup"
|
|
#define TXT_GBM_GUARDIAN 39 //"Use Guardian Powerup"
|
|
#define TXT_GBM_ANTIVIRUS 40 //"Use Anti-Virus Powerup"
|
|
#define TXT_GBM_ALLOWCHAT 41 //"Allow GB chatter"
|
|
#define TXT_GBM_DISALLOWCHAT 42 //"Disallow GB chatter"
|
|
#define TXT_GBM_RETURNTOSHIP 43 //"Return to ship
|
|
#define TXT_GB_ENTERINGSHIP TXT(44) //"Entering ship!"
|
|
#define TXT_GB_HELPMSG TXT(45) //"HELP! I am about to die..."
|
|
#define TXT_GB_NOTLOCATION TXT(46) //"This goal is not location based."
|
|
#define TXT_GB_NONAV TXT(47) //"I do not have nav. data on this goal."
|
|
#define TXT_GB_ONMYWAY TXT(48) //"On my way!"
|
|
#define TXT_GB_NOTREACH TXT(49) //"The goal isn't currently reachable."
|
|
#define TXT_GB_ENTERSHIP TXT(50) //"Returning to ship."
|
|
#define TXT_GB_ESCORT TXT(51) //"Escorting ship..."
|
|
#define TXT_GB_FINDROBOT TXT(52) //"Finding a robot."
|
|
#define TXT_GB_NOROBOT TXT(53) //"No robots are reachable."
|
|
#define TXT_GB_FINDPOWERUP TXT(54) //"Finding a powerup."
|
|
#define TXT_GB_NOPOWERUP TXT(55) //"No powerups are reachable."
|
|
#define TXT_GB_GOMARKER TXT(56) //"Going to the '%s' marker."
|
|
#define TXT_GB_NOMARKER TXT(57) //"None of your markers are reachable."
|
|
#define TXT_GB_NOMARK TXT(58) //"The marker is not reachable."
|
|
#define TXT_GB_FINDTHIEF TXT(59) //"Finding the Thief."
|
|
#define TXT_GB_FOUNDTHIEF TXT(60) //"Let's get him!"
|
|
#define TXT_GB_NOTHIEF TXT(61) //"The Thief is not reachable."
|
|
#define TXT_GB_FINDPOWERUPS TXT(62) //"Finding your powerups."
|
|
#define TXT_GB_NOMESSAGE TXT(63) //"No spew reachable."
|
|
#define TXT_GB_CHATOFF TXT(64) //"Message Processor off."
|
|
#define TXT_GB_CHATON TXT(65) //"Message Processor On."
|
|
#define TXT_GB_FINDENERGY TXT(66) //"Finding energy."
|
|
#define TXT_GB_NOENERGY TXT(67) //"No energy centers are reachable."
|
|
#define TXT_GB_REPAIRED TXT(68) //"Repaired!"
|
|
#define TXT_GB_NOTENSH TXT(69) //"NOt eNougH shIelDs to do rePAir..."
|
|
#define TXT_GB_NEWNAME TXT(70) //"Thank you for the new name!"
|
|
#define TXT_GB_NOWAY TXT(71) //"No way!"
|
|
#define TXT_GB_STRIKE TXT(72) //"I am on strike! I won't do task %d"
|
|
#define TXT_GB_MENUTITLE TXT(73) //"GB Command Menu"
|
|
#define TXT_GB_FINDMARKMENU TXT(74) //"Find marker: '%s'"
|
|
#define TXT_GB_GETTO TXT(75) //"Get to %s"
|
|
#define TXT_GB_SHIELDAMOUNT TXT(76) //" (%d shields)"
|
|
#define TXT_GB_ENTERNAME TXT(77) //"Enter GuideBot name:"
|
|
#define TXT_GBHIT1 TXT(78) //"Stop shooting me!"
|
|
#define TXT_GBHIT2 TXT(79) //"You hit me!"
|
|
#define TXT_GBHIT3 TXT(80) //"Stop it!"
|
|
#define TXT_GBHIT4 TXT(81) //"Im getting attacked!"
|
|
#define TXT_GBHIT5 TXT(82) //"Ouch!"
|
|
#define TXT_GBDAMAGED TXT(83) //"T?o Dama.xgf.ed to exit. NExfvD rEpair."
|
|
#define TXT_GB_OOPS TXT(84) //"Oops... Sorry about that!"
|
|
#define TXT_GB_SHOOTROBOT TXT(85) //"Take that!"
|
|
#define TXT_GB_GOAL1 TXT(86) //"I am at the goal, coming back to get you."
|
|
#define TXT_GB_GOAL2 TXT(87) //"Come on!"
|
|
#define TXT_GB_GOAL3 TXT(88) //"Let's go!"
|
|
#define TXT_GB_COMINGBACK TXT(89) //"I am coming back to get you."
|
|
#define TXT_THIEFSTEAL TXT(90) //"The thief stole your %s!"
|
|
#define TXT_THIEFSTEAL1 TXT(91) //"The thief stole a %s!"
|
|
#define TXT_THIEFSTEAL2 TXT(92) //"The thief stole %d of your %ss!"
|
|
#define TXT_GB_POWERUP TXT(93) //"This powerup is for the GuideBot."
|
|
#define TXT_STHIEFSTEAL TXT(94) //"The Super Thief stole a %s!"
|
|
#define TXT_STHIEFSTEAL2 TXT(95) //"The Super Thief stole %d of your %ss!"
|
|
#define TXT_GBP_PUTOUTFIRE 96 //"Putting out fires!"
|
|
#define TXT_GBP_COWABUNGA 97 //"Cowabunga!"
|
|
#define TXT_GBP_BEGENTAL 98 //"Be gental..."
|
|
#define TXT_GBP_LETSDOIT 99 //"Let's do it!"
|
|
#define TXT_GBP_CONVERTEN 100 //"I will convert the infidal!"
|
|
#define TXT_GBP_ACCELERATOR 101 //"Accelerator!!!!!!!"
|
|
#define TXT_GBPN_NOFIRE 102 //"No fires to put out!"
|
|
#define TXT_GBPN_NOFIGHT 103 //"I don't want to fight!"
|
|
#define TXT_GBPN_GOPLAY 104 //"Go play with yourself..."
|
|
#define TXT_GBPN_TOODISSY 105 //"Too dissy..."
|
|
#define TXT_GBPN_NOENEMY 106 //"No enemies are near!"
|
|
#define TXT_GBPN_NOCOFFEE 107 //"I am sick of coffee..."
|
|
#define TXT_GBPP_EXTIN 108 //"I got an extinguisher!"
|
|
#define TXT_GBPP_WINGNUT 109 //"I got the Wingnut Powerup!
|
|
#define TXT_GBPP_MANCON 110 //"I got the Manual Control Powerup!"
|
|
#define TXT_GBPP_GUARD 111 //"I got the Guardian Powerup!"
|
|
#define TXT_GBPP_ANTIV 112 //"I got a temporary Anti-Virus!"
|
|
#define TXT_GBPP_ACCEL 113 //"I got the Accelerator!!!!!!"
|
|
#define TXT_WEAP_LASERS 114
|
|
#define TXT_WEAP_VAUSS 115
|
|
#define TXT_WEAP_MICROWAVE 116
|
|
#define TXT_WEAP_PLASMA 117
|
|
#define TXT_WEAP_FUSION 118
|
|
#define TXT_WEAP_SUPER 119
|
|
#define TXT_WEAP_MASS 120
|
|
#define TXT_WEAP_NAPALM 121
|
|
#define TXT_WEAP_EMD 122
|
|
#define TXT_WEAP_OMEGA 123
|
|
#define TXT_WEAP_CONCUSSION 124
|
|
#define TXT_WEAP_HOMING 125
|
|
#define TXT_WEAP_IMPACT 126
|
|
#define TXT_WEAP_SMART 127
|
|
#define TXT_WEAP_MEGA 128
|
|
#define TXT_WEAP_FRAG 129
|
|
#define TXT_WEAP_GUIDED 130
|
|
#define TXT_WEAP_NAPALMR 131
|
|
#define TXT_WEAP_CYCLONE 132
|
|
#define TXT_WEAP_BLACKSHARK 133
|
|
#define TXT_WEAP_AUTOMAP 134
|
|
#define TXT_WEAP_HEADLIGHT 135
|
|
#define TXT_WEAP_ETSCONVERTER 136
|
|
#define TXT_WEAP_CLOAK 137
|
|
#define TXT_WEAP_INVULNERABILITY 138
|
|
#define TXT_WEAP_RAPIDFIRE 139
|
|
#define TXT_WEAP_QUADLASERS 140
|
|
#define TXT_GB_FIFTEEN_SEC_TO_GO TXT(141)
|
|
#define TXT_GB_POW_DEPLETED TXT(142)
|
|
#define TXT_GB_POW_ACC_ALMOST_DONE TXT(143)
|
|
#define TXT_GB_DONT_HAVE_POWERUP TXT(144)
|
|
#define TXT_GB_I_FOUND_A_GB_POWERUP TXT(145)
|
|
|
|
// 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);
|
|
|
|
// 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 44 // maximum number of IDs
|
|
|
|
#define ID_PEST 0 // Pest robot
|
|
#define ID_STINGER 1 // Stringer robot
|
|
#define ID_DRAGON 2 // Dragon
|
|
#define ID_TRACKER 3 // Tracker
|
|
#define ID_LANCE 4 // Lance
|
|
#define ID_FLAK 5 // Flak
|
|
#define ID_SUPERTROOPER 6 // Super Trooper
|
|
#define ID_REDSUPERTROOPER 7 // Red Super Trooper
|
|
#define ID_JUGG 8 // Juggernaut
|
|
#define ID_SIXGUN 9 // Sixgun
|
|
#define ID_SICKLE 10 // Sickle
|
|
#define ID_GUIDEBOT 11 // GuideBot
|
|
#define ID_FIREATDIST 12 // Fire at distance
|
|
#define ID_TUBBS 13 // Tubbs
|
|
#define ID_THIEF 14 // Thief :)
|
|
#define ID_GBPOWERUP 15 // GBPowerup
|
|
#define ID_BARNSWALLOW 16 // Barn Shallow
|
|
#define ID_SPARKY 17
|
|
#define ID_MANTARAY 18
|
|
#define ID_SPYHUNTER 19
|
|
#define ID_SNIPER 20
|
|
#define ID_HUMONCULOUS 21
|
|
#define ID_SEEKER 22
|
|
#define ID_BETTY 23
|
|
#define ID_CHAFF 24
|
|
#define ID_CHAFFCHUNK 25
|
|
#define ID_PROXMINE 26
|
|
#define ID_BETTYBOMB 27 // parent of Bouncing Betty...created to make 3 Bettys
|
|
#define ID_JOSHBELL 28
|
|
#define ID_SKIFF 29
|
|
#define ID_EXPLODEONCONTACT 30
|
|
#define ID_EXPLODETIMEOUT 31
|
|
#define ID_GUNBOY 32
|
|
#define ID_DEATH_TOWER 33
|
|
#define ID_DEATH_COLLECTOR 34
|
|
#define ID_CHEMICAL_BALL 35
|
|
#define ID_HELLION 36
|
|
#define ID_SUPERTHIEF 37
|
|
#define ID_EVADERMODA 38
|
|
#define ID_DESTROYONCONTACT 39
|
|
#define ID_HATEPTMC 40
|
|
#define ID_SNIPERNORUN 41
|
|
#define ID_FLAMERAS 42
|
|
#define ID_OLDSCRATCH 43
|
|
|
|
struct tScriptInfo {
|
|
int id;
|
|
const char *name;
|
|
};
|
|
|
|
static tScriptInfo ScriptInfo[NUM_IDS] = {{ID_PEST, "Pest"},
|
|
{ID_STINGER, "Stinger"},
|
|
{ID_DRAGON, "Dragontorso"},
|
|
{ID_TRACKER, "Tracker"},
|
|
{ID_LANCE, "Lance"},
|
|
{ID_FLAK, "Flak"},
|
|
{ID_SUPERTROOPER, "supertrooper"},
|
|
{ID_REDSUPERTROOPER, "red supertrooper"},
|
|
{ID_JUGG, "jugg"},
|
|
{ID_SIXGUN, "Sixgun"},
|
|
{ID_SICKLE, "Sickle"},
|
|
{ID_GUIDEBOT, "Guidebot"},
|
|
{ID_FIREATDIST, "FireAtDist"},
|
|
{ID_TUBBS, "Tubbs"},
|
|
{ID_THIEF, "Thief"},
|
|
{ID_GBPOWERUP, "GBPowerup"},
|
|
{ID_BARNSWALLOW, "BarnSwallow"},
|
|
{ID_SPARKY, "Sparky"},
|
|
{ID_MANTARAY, "Mantaray"},
|
|
{ID_SPYHUNTER, "Spyhunter"},
|
|
{ID_SNIPER, "Sniper"},
|
|
{ID_HUMONCULOUS, "Humonculous"},
|
|
{ID_SEEKER, "Seeker"},
|
|
{ID_BETTY, "Betty"},
|
|
{ID_CHAFF, "ChaffBomb"},
|
|
{ID_CHAFFCHUNK, "ChaffChunk"},
|
|
{ID_PROXMINE, "ProxMine"},
|
|
{ID_BETTYBOMB, "BettyBomb"},
|
|
{ID_JOSHBELL, "JoshBell"},
|
|
{ID_SKIFF, "Skiff"},
|
|
{ID_EXPLODEONCONTACT, "ExplodeOnContact"},
|
|
{ID_EXPLODETIMEOUT, "ExplodeTimeOut"},
|
|
{ID_GUNBOY, "Gunboy"},
|
|
{ID_DEATH_TOWER, "Ltowerbase"},
|
|
{ID_DEATH_COLLECTOR, "Collectorbase"},
|
|
{ID_CHEMICAL_BALL, "ChemicalBall"},
|
|
{ID_HELLION, "Hellion"},
|
|
{ID_SUPERTHIEF, "superthief"},
|
|
{ID_EVADERMODA, "EvaderModA"},
|
|
{ID_DESTROYONCONTACT, "DestroyOnContact"},
|
|
{ID_HATEPTMC, "HatePTMC"},
|
|
{ID_SNIPERNORUN, "SniperNoRun"},
|
|
{ID_FLAMERAS, "FlameRAS"},
|
|
{ID_OLDSCRATCH, "old scratch"}};
|
|
|
|
static int aigame_mod_id;
|
|
// use this macro to create unique timer IDs
|
|
#define CREATE_TIMER_ID(id) ((aigame_mod_id << 16) | (id))
|
|
|
|
static int GetObjectParent(int object);
|
|
static int GetObjectType(int object);
|
|
|
|
class BaseObjScript {
|
|
public:
|
|
BaseObjScript();
|
|
~BaseObjScript();
|
|
virtual int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
struct tShieldOrbInfo {
|
|
int hitcount;
|
|
};
|
|
|
|
//------------------
|
|
// Pest class
|
|
//------------------
|
|
struct pest_data {
|
|
int tail_handle;
|
|
float last_anim_frame;
|
|
int foot_sounds[3];
|
|
};
|
|
|
|
class Pest : public BaseObjScript {
|
|
private:
|
|
pest_data *memory;
|
|
void DoInit(int me_handle);
|
|
|
|
public:
|
|
Pest() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Stinger class
|
|
//------------------
|
|
#define STINGER_RANGED 0
|
|
#define STINGER_MELEE 1
|
|
|
|
struct stinger_data {
|
|
float next_mode_time;
|
|
float force_melee_shields;
|
|
char mode;
|
|
char f_very_hurt;
|
|
};
|
|
|
|
class Stinger : public BaseObjScript {
|
|
private:
|
|
stinger_data *memory;
|
|
|
|
void DoInit(int me_handle);
|
|
void DoInterval(int me_handle);
|
|
|
|
public:
|
|
Stinger() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// SuperThief class
|
|
//------------------
|
|
|
|
#define MAX_STOLEN_WEAPONS 25
|
|
|
|
struct weapon_item {
|
|
char index;
|
|
int owner;
|
|
char amount;
|
|
};
|
|
|
|
#define SUPERTHIEF_RANGED 0
|
|
#define SUPERTHIEF_MELEE 1
|
|
#define SUPERTHIEF_DEATH 2
|
|
#define SUPERTHIEF_WAIT 3
|
|
#define SUPERTHIEF_INTRO 4
|
|
|
|
struct superthief_data {
|
|
float mode_time;
|
|
|
|
float next_mode_time;
|
|
float force_ranged_shields;
|
|
char mode;
|
|
char f_very_hurt;
|
|
|
|
float last_frame;
|
|
float time_till_fire_secondary;
|
|
|
|
int cur_weapon;
|
|
|
|
int num_stolen_weapons;
|
|
weapon_item stolen_weapons[MAX_STOLEN_WEAPONS];
|
|
|
|
char weapon[100];
|
|
char sound[100];
|
|
float latency;
|
|
|
|
float next_fire_flare_time;
|
|
int flare_fire_index;
|
|
|
|
int camera_obj;
|
|
int laser_obj;
|
|
|
|
bool laser_on;
|
|
};
|
|
|
|
class SuperThief : public BaseObjScript {
|
|
private:
|
|
superthief_data *memory;
|
|
|
|
bool DoSteal(int me, int it);
|
|
void SpewEverything(int me);
|
|
bool DoNotify(int me, tOSIRISEVTAINOTIFY *notify);
|
|
|
|
void SetMode(int me, char mode);
|
|
void CheckAndFireSecondary(int me);
|
|
|
|
void FireFlare(int me);
|
|
|
|
void DoInit(int me_handle);
|
|
void DoInterval(int me_handle);
|
|
|
|
public:
|
|
SuperThief() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Humonculous class
|
|
//------------------
|
|
|
|
struct humonculous_data {
|
|
float next_wall_hit_check_time;
|
|
float mode_time;
|
|
|
|
float force_mid_damage_shields;
|
|
float force_wall_hit_shields;
|
|
float force_melee_shields;
|
|
|
|
float last_shields;
|
|
|
|
uint16_t mode;
|
|
uint16_t next_mode;
|
|
|
|
vector land_pos;
|
|
vector land_fvec;
|
|
// Note: land_uvec is always {0.0, 1.0, 0.0}
|
|
|
|
float ground_pnt_offset; // This can be computed at start of level
|
|
|
|
float max_speed;
|
|
float max_delta_speed;
|
|
|
|
float next_mode_time;
|
|
|
|
int josh[2];
|
|
|
|
int rocks[5];
|
|
|
|
int16_t flags;
|
|
};
|
|
|
|
class Humonculous : public BaseObjScript {
|
|
private:
|
|
humonculous_data *memory;
|
|
|
|
void DetermineDeathPos(int me, vector *dpos, int *droom);
|
|
bool SetMode(int me, uint16_t mode);
|
|
void DoInit(int me);
|
|
void DoInterval(int me);
|
|
bool DoNotify(int me, tOSIRISEventInfo *data);
|
|
|
|
public:
|
|
Humonculous() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Dragon class
|
|
//------------------
|
|
#define DRAGON_ARMOR 0
|
|
#define DRAGON_SKELETON 1
|
|
|
|
struct dragon_data {
|
|
int mode;
|
|
int head_object;
|
|
int tail_object;
|
|
int green_turret;
|
|
int t[6];
|
|
int tentacle[6];
|
|
};
|
|
|
|
class Dragon : public BaseObjScript {
|
|
private:
|
|
dragon_data *memory;
|
|
|
|
void DoInit(int me_handle);
|
|
|
|
public:
|
|
Dragon() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//----------------
|
|
// Tracker
|
|
//----------------
|
|
|
|
struct tracker_data {
|
|
int turret_object;
|
|
int hatch_object;
|
|
};
|
|
|
|
class Tracker : public BaseObjScript {
|
|
private:
|
|
tracker_data *memory;
|
|
|
|
void DoInit(int me_handle);
|
|
|
|
public:
|
|
Tracker() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Lance class
|
|
//------------------
|
|
|
|
#define LANCE_PAUSED 0
|
|
#define LANCE_MOVING 1
|
|
|
|
struct lance_data {
|
|
int mode;
|
|
};
|
|
|
|
class Lance : public BaseObjScript {
|
|
private:
|
|
void DoInit(int me_handle);
|
|
void DoFrame(int me_handle);
|
|
lance_data *memory;
|
|
|
|
public:
|
|
Lance() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Flak class
|
|
//------------------
|
|
struct flak_data {
|
|
int canopy_handle;
|
|
bool f_dead;
|
|
float death_time;
|
|
};
|
|
|
|
class Flak : public BaseObjScript {
|
|
private:
|
|
flak_data *memory;
|
|
void DoInit(int me_handle);
|
|
|
|
public:
|
|
Flak() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Seeker class
|
|
//------------------
|
|
|
|
class Seeker : public BaseObjScript {
|
|
private:
|
|
void DoInit(int me);
|
|
void DoCollide(int me);
|
|
|
|
public:
|
|
Seeker() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// SuperTrooper class
|
|
//------------------
|
|
|
|
struct supertrooper_data {
|
|
int body_handle;
|
|
};
|
|
|
|
class SuperTrooper : public BaseObjScript {
|
|
private:
|
|
supertrooper_data *memory;
|
|
void DoInit(int me_handle);
|
|
|
|
public:
|
|
SuperTrooper() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Sparky class
|
|
//------------------
|
|
|
|
#define MALF_TIME 5.0f
|
|
#define MIN_MALF_SPEED 6000.0f
|
|
|
|
#define DEATH_TIME 5.0f
|
|
#define DEATH_ROT_ACC 50000.0f
|
|
|
|
#define SPARKY_NO_ROT 0
|
|
#define SPARKY_ROT_LEFT 1
|
|
#define SPARKY_ROT_RIGHT 2
|
|
|
|
#define SPARKY_NORMAL 0
|
|
#define SPARKY_DYING 1
|
|
|
|
struct sparky_data {
|
|
char mode;
|
|
float mode_time;
|
|
bool f_rotation_death_enabled;
|
|
float drag;
|
|
|
|
float last_spark_time;
|
|
|
|
int matcen_id;
|
|
|
|
matrix orient;
|
|
|
|
char spin_dir;
|
|
float spin_time;
|
|
};
|
|
|
|
class Sparky : public BaseObjScript {
|
|
private:
|
|
sparky_data *memory;
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
bool DoNotify(int me_handle, tOSIRISEventInfo *data);
|
|
void SetMode(int me, char mode);
|
|
|
|
public:
|
|
Sparky() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Hellion class
|
|
//------------------
|
|
|
|
#define HELLION_WAIT 0
|
|
#define HELLION_INTRO 1
|
|
#define HELLION_MATTER 2
|
|
#define HELLION_ENERGY 3
|
|
#define HELLION_FIRE 4
|
|
#define HELLION_SLEEP 5
|
|
#define HELLION_BIRTHING 6
|
|
#define HELLION_DEATH 7
|
|
#define HELLION_BETWEEN_MODE 8
|
|
|
|
struct hellion_data {
|
|
int flags;
|
|
char mode;
|
|
char next_mode;
|
|
float mode_time;
|
|
int head_object;
|
|
int turret_object;
|
|
|
|
float last_frame;
|
|
|
|
int laser_sound;
|
|
|
|
// Save and restore information
|
|
char weapon[100];
|
|
char sound[100];
|
|
float start;
|
|
float fire;
|
|
float end;
|
|
float time;
|
|
float latency;
|
|
int index;
|
|
char f_mask;
|
|
|
|
float alert_start;
|
|
float alert_end;
|
|
float alert_time;
|
|
// End of save and restore information
|
|
|
|
float laser_time_left;
|
|
int emitter[2];
|
|
int camera[2];
|
|
};
|
|
|
|
class Hellion : public BaseObjScript {
|
|
private:
|
|
hellion_data *memory;
|
|
void DoInit(int me);
|
|
void RemapAlert(int me, float start, float end, float time);
|
|
void RemapWB(int me, float start, float fire, int fire_sound, float end, float time, float latency, int index,
|
|
uint16_t w_id, char f_mask);
|
|
void DoFrame(int me);
|
|
bool DoNotify(int me_handle, tOSIRISEventInfo *data);
|
|
void SetMode(int me, char mode);
|
|
|
|
public:
|
|
Hellion() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// MantaRay class
|
|
//------------------
|
|
|
|
// Modes
|
|
#define MRM_NORMAL 0
|
|
#define MRM_ATTACK 1
|
|
#define MRM_ATTACK_CIRCLE_BACK 2
|
|
|
|
// Flags
|
|
#define MRF_LEADER 0x01
|
|
#define MRF_SQUADIE 0x02
|
|
#define MRF_CATCHUP 0x04
|
|
|
|
// Squad commands
|
|
#define MRC_JOIN_ME 11
|
|
#define MRC_LEAVE_YOU 12
|
|
#define MRC_GET_GOAL_POS 13
|
|
#define MRC_GET_GOAL_ROOM 14
|
|
#define MRC_GET_GOAL_ORIENT 15
|
|
|
|
#define MR_MAX_TEAMMATES 4
|
|
|
|
// Squad offsets
|
|
#define MRO_FVEC 10.0f
|
|
#define MRO_RVEC 20.0f
|
|
#define MRO_UVEC 5.0f
|
|
|
|
// Squad catchup offset
|
|
#define MRO_CATCHUP_DIST 5.0f
|
|
|
|
struct mantaray_data {
|
|
char mode;
|
|
float mode_time;
|
|
float next_mode_time;
|
|
|
|
float base_speed; // We alter this when we are in catchup mode
|
|
float attack_speed;
|
|
|
|
int flags;
|
|
|
|
uint16_t mantaray_id;
|
|
|
|
int leader_handle;
|
|
int num_teammates;
|
|
int teammate[MR_MAX_TEAMMATES];
|
|
float next_update_squad_time;
|
|
|
|
vector goal_pos;
|
|
int goal_room;
|
|
|
|
};
|
|
|
|
class MantaRay : public BaseObjScript {
|
|
private:
|
|
mantaray_data *memory;
|
|
void DoInit(int me);
|
|
|
|
void DoSquadieFrame(int me);
|
|
void DoFrame(int me);
|
|
void SetMode(int me, char mode);
|
|
bool DoNotify(int me, tOSIRISEventInfo *data);
|
|
|
|
bool SendCommand(int me, int it, char command, void *ptr);
|
|
bool ReceiveCommand(int me, int it, char command, void *ptr);
|
|
|
|
void UpdateSquadList(int me);
|
|
void UpdateSquad(int me);
|
|
|
|
public:
|
|
MantaRay() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Skiff class
|
|
//------------------
|
|
|
|
struct skiff_data {
|
|
char mode;
|
|
float mode_time;
|
|
float next_mode_time;
|
|
|
|
float base_speed; // We alter this when we are in catchup mode
|
|
float attack_speed;
|
|
|
|
int flags;
|
|
|
|
uint16_t skiff_id;
|
|
|
|
int leader_handle;
|
|
int num_teammates;
|
|
int teammate[MR_MAX_TEAMMATES];
|
|
float next_update_squad_time;
|
|
|
|
vector goal_pos;
|
|
int goal_room;
|
|
|
|
};
|
|
|
|
class Skiff : public BaseObjScript {
|
|
private:
|
|
skiff_data *memory;
|
|
void DoInit(int me);
|
|
|
|
void DoSquadieFrame(int me);
|
|
void DoFrame(int me);
|
|
void SetMode(int me, char mode);
|
|
bool DoNotify(int me, tOSIRISEventInfo *data);
|
|
|
|
bool SendCommand(int me, int it, char command, void *ptr);
|
|
bool ReceiveCommand(int me, int it, char command, void *ptr);
|
|
|
|
void UpdateSquadList(int me);
|
|
void UpdateSquad(int me);
|
|
|
|
public:
|
|
Skiff() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// SpyHunter class
|
|
//------------------
|
|
|
|
#define SHM_NORMAL 0
|
|
#define SHM_ATTACK 1
|
|
#define SHM_BOMB_ATTACK 2
|
|
|
|
#define SH_MAX_BOMB_TIME 15.0f
|
|
#define SH_BOMB_ACCELERATION 8.0f
|
|
#define SH_BOMB_ROT_ACC 4000.0f
|
|
|
|
#define SH_BOMB_TICK_RAMP_TIME 5.0
|
|
#define SH_MAX_TICK_INTERVAL 0.5f
|
|
#define SH_MIN_TICK_INTERVAL .06f
|
|
|
|
struct spyhunter_data {
|
|
char mode;
|
|
float next_mode_time;
|
|
float next_summon_time;
|
|
bool f_hit_by_emd;
|
|
float mode_time;
|
|
char last_attack_mode;
|
|
uint16_t emd_id;
|
|
int tick_sound;
|
|
float last_tick_time;
|
|
|
|
};
|
|
|
|
class SpyHunter : public BaseObjScript {
|
|
private:
|
|
spyhunter_data *memory;
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
void DoSummon(int me);
|
|
bool DoNotify(int me, tOSIRISEventInfo *data);
|
|
void SetMode(int me, char mode);
|
|
|
|
public:
|
|
SpyHunter() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Sniper class
|
|
//------------------
|
|
|
|
#define SNIPER_BASE 0
|
|
#define SNIPER_SNIPE 1
|
|
|
|
struct sniper_data {
|
|
char mode;
|
|
float mode_time;
|
|
float time_till_next_mode;
|
|
|
|
float base_speed;
|
|
float base_acc;
|
|
};
|
|
|
|
class Sniper : public BaseObjScript {
|
|
sniper_data *memory;
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
bool DoNotify(int me_handle, tOSIRISEventInfo *data);
|
|
void SetMode(int me, char mode);
|
|
|
|
public:
|
|
Sniper() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// SniperNoRun class
|
|
//------------------
|
|
|
|
#define SNIPER_BASE 0
|
|
#define SNIPER_SNIPE 1
|
|
|
|
struct snipernorun_data {
|
|
char mode;
|
|
float mode_time;
|
|
float time_till_next_mode;
|
|
|
|
float base_speed;
|
|
float base_acc;
|
|
};
|
|
|
|
class SniperNoRun : public BaseObjScript {
|
|
snipernorun_data *memory;
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
bool DoNotify(int me_handle, tOSIRISEventInfo *data);
|
|
void SetMode(int me, char mode);
|
|
|
|
public:
|
|
SniperNoRun() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// EvaderModA class
|
|
//------------------
|
|
|
|
struct evadermoda_data {
|
|
char mode;
|
|
float mode_time;
|
|
float time_till_next_mode;
|
|
|
|
float base_speed;
|
|
float base_acc;
|
|
};
|
|
|
|
class EvaderModA : public BaseObjScript {
|
|
evadermoda_data *memory;
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
bool DoNotify(int me_handle, tOSIRISEventInfo *data);
|
|
void SetMode(int me, char mode);
|
|
|
|
public:
|
|
EvaderModA() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// FlameRAS class
|
|
//------------------
|
|
|
|
struct flameras_data {
|
|
char mode;
|
|
float mode_time;
|
|
float time_till_next_mode;
|
|
|
|
float base_speed;
|
|
float base_acc;
|
|
|
|
float time_till_fire_toggle;
|
|
bool f_firing_ok;
|
|
};
|
|
|
|
class FlameRAS : public BaseObjScript {
|
|
flameras_data *memory;
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
bool DoNotify(int me_handle, tOSIRISEventInfo *data);
|
|
void SetMode(int me, char mode);
|
|
|
|
public:
|
|
FlameRAS() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Jugg
|
|
//-----------------
|
|
|
|
#define JUGG_NORMAL 0
|
|
#define JUGG_DYING 1
|
|
|
|
#define JUGG_F_BELLY_DEAD 0x01
|
|
#define JUGG_F_HEAD_DEAD 0x02
|
|
#define JUGG_F_BODY_DEAD 0x04
|
|
|
|
struct jugg_data {
|
|
float last_frame;
|
|
int foot_sound;
|
|
|
|
int head_object;
|
|
int belly_object;
|
|
int turret_object[6];
|
|
int head_turret[2];
|
|
int flame_turret[3];
|
|
|
|
float mode_time;
|
|
char mode;
|
|
char flags;
|
|
};
|
|
|
|
class Jugg : public BaseObjScript {
|
|
private:
|
|
jugg_data *memory;
|
|
void DoInit(int me_handle);
|
|
void DoFrame(int me_handle);
|
|
void SetMode(int me, char mode);
|
|
|
|
public:
|
|
Jugg() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Death Tower
|
|
//-----------------
|
|
|
|
struct dtower_data {
|
|
int gun_handle;
|
|
int ball_handle;
|
|
|
|
float mode_time;
|
|
|
|
bool f_died;
|
|
float last_frame;
|
|
};
|
|
|
|
class DTower : public BaseObjScript {
|
|
private:
|
|
dtower_data *memory;
|
|
void DoInit(int me_handle);
|
|
void DoFrame(int me_handle);
|
|
|
|
public:
|
|
DTower() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Death Collector
|
|
//-----------------
|
|
|
|
struct dcollector_data {
|
|
int ball_handle;
|
|
int rod_handle;
|
|
int target_handle;
|
|
|
|
bool f_dead;
|
|
};
|
|
|
|
class DCollector : public BaseObjScript {
|
|
private:
|
|
dcollector_data *memory;
|
|
void DoInit(int me_handle);
|
|
void DoFrame(int me_handle);
|
|
|
|
public:
|
|
DCollector() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Chemical Ball
|
|
//-----------------
|
|
|
|
struct cball_data {
|
|
bool f_picked_up;
|
|
float mode_time;
|
|
};
|
|
|
|
class CBall : public BaseObjScript {
|
|
private:
|
|
cball_data *memory;
|
|
void DoInit(int me_handle);
|
|
void DoFrame(int me_handle);
|
|
|
|
public:
|
|
CBall() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// SixGun
|
|
//-----------------
|
|
|
|
struct sixgun_data {
|
|
int mode;
|
|
float max_speed;
|
|
float circle_dist;
|
|
};
|
|
|
|
class SixGun : public BaseObjScript {
|
|
private:
|
|
sixgun_data *memory;
|
|
void DoInit(int me_handle);
|
|
bool DoNotify(int me_handle, tOSIRISEVTAINOTIFY *notify);
|
|
|
|
public:
|
|
SixGun() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Explode Time Out
|
|
//-----------------
|
|
|
|
struct explodetimeout_data {
|
|
float life_left;
|
|
};
|
|
|
|
class ExplodeTimeOut : public BaseObjScript {
|
|
private:
|
|
explodetimeout_data *memory;
|
|
void DoInit(int me_handle);
|
|
|
|
public:
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Sickle
|
|
//-----------------
|
|
|
|
#define SICKLE_GOING_HOME 3 // Going home
|
|
#define SICKLE_LAND_ON_CEILING 10 // Rotating upsidedown and going to the ceiling
|
|
#define SICKLE_LANDED 12 // Stopped on the ceiling
|
|
#define SICKLE_MELEE 15 // Melee
|
|
|
|
struct sickle_data {
|
|
int mode;
|
|
float mode_time;
|
|
|
|
vector ceiling_pos;
|
|
|
|
float last_fvi_check_time;
|
|
float rot_speed;
|
|
float speed;
|
|
float sleep_speed;
|
|
float sleep_rot_speed;
|
|
bool done_turning;
|
|
bool done_moving;
|
|
vector home_pos;
|
|
int home_room;
|
|
vector home_fvec;
|
|
|
|
};
|
|
|
|
class Sickle : public BaseObjScript {
|
|
private:
|
|
sickle_data *memory;
|
|
void SetMode(int me, char mode);
|
|
void DoInit(int me_handle);
|
|
void DoFrame(int me_handle);
|
|
bool DoNotify(int me_handle, tOSIRISEVTAINOTIFY *notify);
|
|
|
|
public:
|
|
Sickle() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Bouncing Betty
|
|
//-----------------
|
|
|
|
struct tBettyBombInfo {
|
|
bool explode;
|
|
};
|
|
|
|
class BettyBomb : public BaseObjScript {
|
|
public:
|
|
BettyBomb() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
|
|
private:
|
|
tBettyBombInfo *memory;
|
|
};
|
|
|
|
struct tBettyInfo {
|
|
bool explode;
|
|
float lasttime;
|
|
};
|
|
|
|
class BettyScript : public BaseObjScript {
|
|
public:
|
|
BettyScript() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
|
|
protected:
|
|
void DoInit(int me);
|
|
|
|
private:
|
|
tBettyInfo *memory;
|
|
};
|
|
|
|
//-----------------
|
|
// Chaff
|
|
//-----------------
|
|
|
|
struct tChaffInfo {
|
|
bool killme;
|
|
};
|
|
|
|
class ChaffScript : public BaseObjScript {
|
|
public:
|
|
ChaffScript() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
|
|
protected:
|
|
void DoInit(int me);
|
|
void DoInterval(int handle);
|
|
|
|
private:
|
|
tChaffInfo *memory;
|
|
};
|
|
|
|
struct tChaffChunkInfo {
|
|
bool killme;
|
|
float lifeleft;
|
|
};
|
|
|
|
class ChaffChunkScript : public BaseObjScript {
|
|
public:
|
|
ChaffChunkScript() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
|
|
protected:
|
|
void DoInit(int me);
|
|
void DoInterval(int handle, float time);
|
|
void DoCollide(tOSIRISEventInfo *data);
|
|
|
|
private:
|
|
tChaffChunkInfo *memory;
|
|
};
|
|
|
|
//------------------
|
|
// ProxMine class
|
|
//------------------
|
|
|
|
class ProxMine : public BaseObjScript {
|
|
private:
|
|
void DoInit(int me);
|
|
void DoCollide(int me);
|
|
|
|
public:
|
|
ProxMine() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//------------------
|
|
// Gunboy class
|
|
//------------------
|
|
|
|
class Gunboy : public BaseObjScript {
|
|
public:
|
|
Gunboy() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// GBPowerup
|
|
//-----------------
|
|
|
|
struct gbpowerup_data {
|
|
char type; // 0 - 5 matching up with the gb powerups!
|
|
float next_check_time;
|
|
float time_till_next_hud_message;
|
|
};
|
|
|
|
class GBPowerup : public BaseObjScript {
|
|
private:
|
|
gbpowerup_data *memory;
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
|
|
public:
|
|
GBPowerup() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Josh Bell
|
|
//-----------------
|
|
|
|
struct joshbell_data {
|
|
int me;
|
|
int attach_cage;
|
|
float last_play_time;
|
|
int bell_sound;
|
|
};
|
|
|
|
class JoshBell : public BaseObjScript {
|
|
private:
|
|
joshbell_data *memory;
|
|
void DoInit(int me);
|
|
void DoCollide(int me);
|
|
|
|
public:
|
|
JoshBell() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Explode On Contact
|
|
//-----------------
|
|
|
|
class ExplodeOnContact : public BaseObjScript {
|
|
private:
|
|
void DoCollide(int me, int it_handle);
|
|
|
|
public:
|
|
ExplodeOnContact() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Destroy On Contact
|
|
//-----------------
|
|
|
|
class DestroyOnContact : public BaseObjScript {
|
|
private:
|
|
void DoCollide(int me, int it_handle);
|
|
|
|
public:
|
|
DestroyOnContact() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// GuideBot
|
|
//-----------------
|
|
|
|
// Goal levels:
|
|
#define GBL_AMBIENT 0 // Ambient movement and activities
|
|
#define GBL_COMMAND 1 // Regular commands (like find powerup)
|
|
#define GBL_OVERRIDE 2 // Overriding commands like (return to player) or putting out fires??? --chrishack
|
|
#define GBL_MANDITORY 3 // Flying back to ship
|
|
|
|
#define GB_GUID_RTYPE_SUB_DIR_DONE 0x10010001
|
|
#define GB_GUID_RETURNED_TO_PLAYER 0x10010002
|
|
#define GB_GUID_AT_GOAL 0x10010003
|
|
|
|
// Modes
|
|
#define GBM_IN_SHIP 0 // In the player ship
|
|
#define GBM_AMBIENT 1 // Doing command or idling
|
|
#define GBM_DOING_TASK 2 // -- might get rid of this one!!!!!!!!! I will just add a GBSM_ (doing task)
|
|
#define GBM_KIDNAPPED 3
|
|
#define GBM_TALKING 4
|
|
#define GBM_THIEF 5
|
|
#define GBM_ON_FIRE 6
|
|
#define GBM_MANUAL 7
|
|
#define GBM_RTYPE 8
|
|
#define GBM_GO_WINGNUT 9
|
|
#define GBM_EXTINGUISH 10
|
|
#define GBM_BIRTH 11
|
|
#define GBM_ENTER_SHIP 12
|
|
#define GBM_RESPAWN_ENTER_SHIP 13
|
|
|
|
// Sub-modes for normal mode
|
|
#define GBSM_NONE 0
|
|
#define GBSM_OPEN_DOOR 1
|
|
#define GBSM_GET_POWERUP 2
|
|
#define GBSM_RETURNING 3
|
|
#define GBSM_ANTI_VIRUS 4
|
|
|
|
#define GBF_VERY_DAMAGED 0x0001
|
|
#define GBF_THIEF_REPEL 0x0002
|
|
#define GBF_POW_SPEED 0x0004
|
|
#define GBF_NO_CHATTER 0x0008
|
|
#define GBF_SAW_BOSS 0x0010
|
|
#define GBF_WANT_TO_EXTINGUISH 0x0020
|
|
#define GBF_EXTINGUISHING 0x0040
|
|
#define GBF_IN_CUSTOM_ANIM 0x0080
|
|
#define GBF_WANT_TO_USE_ANTI_VIRUS 0x0100
|
|
#define GBF_USING_ANTI_VIRUS 0x0200
|
|
#define GBF_WANTING_TO_ENTER_PDEAD 0x0400
|
|
|
|
#define NUM_GB_COMMANDS 44
|
|
|
|
// Commands
|
|
#define GBC_EXIT_SHIP 0
|
|
#define GBC_REPAIR_GUIDEBOT 1
|
|
#define GBC_RENAME 2
|
|
#define GBC_FIND_ACTIVE_GOAL_0 3
|
|
#define GBC_FIND_ACTIVE_GOAL_1 4
|
|
#define GBC_FIND_ACTIVE_GOAL_2 5
|
|
#define GBC_FIND_ACTIVE_GOAL_3 6
|
|
#define GBC_FIND_ACTIVE_GOAL_4 7
|
|
#define GBC_FIND_ACTIVE_GOAL_5 8
|
|
#define GBC_FIND_ACTIVE_GOAL_6 9
|
|
#define GBC_FIND_ACTIVE_GOAL_7 10
|
|
#define GBC_FIND_ACTIVE_SEC_GOAL_0 11
|
|
#define GBC_FIND_ACTIVE_SEC_GOAL_1 12
|
|
#define GBC_FIND_ACTIVE_SEC_GOAL_2 13
|
|
#define GBC_FIND_ACTIVE_SEC_GOAL_3 14
|
|
#define GBC_FIND_ACTIVE_SEC_GOAL_4 15
|
|
#define GBC_FIND_ACTIVE_SEC_GOAL_5 16
|
|
#define GBC_FIND_ACTIVE_SEC_GOAL_6 17
|
|
#define GBC_FIND_ACTIVE_SEC_GOAL_7 18
|
|
#define GBC_FIND_SPEW 19
|
|
#define GBC_FIND_POWERUP 20
|
|
#define GBC_FIND_ENERGY_CENTER 21
|
|
#define GBC_FIND_ROBOT 22
|
|
#define GBC_FIND_THIEF 23
|
|
#define GBC_FIND_OUTSIDE 24
|
|
#define GBC_FIND_MINE 25
|
|
#define GBC_FIND_CLOSEST_MARKER 26
|
|
#define GBC_FIND_MARKER_0 27
|
|
#define GBC_FIND_MARKER_1 28
|
|
#define GBC_FIND_MARKER_2 29
|
|
#define GBC_FIND_MARKER_3 30
|
|
#define GBC_FIND_MARKER_4 31
|
|
#define GBC_FIND_MARKER_5 32
|
|
#define GBC_FIND_MARKER_6 33
|
|
#define GBC_FIND_MARKER_7 34
|
|
#define GBC_ESCORT_SHIP 35
|
|
#define GBC_EXTINGUISH 36
|
|
#define GBC_GO_WINGNUT 37
|
|
#define GBC_TELEPRESENCE 38
|
|
#define GBC_RTYPE 39
|
|
#define GBC_ANTI_VIRUS 40
|
|
#define GBC_ALLOW_CHATTER 41
|
|
#define GBC_NO_CHATTER 42
|
|
#define GBC_RETURN_TO_SHIP 43
|
|
#define GBC_RENAME_SILENT 44
|
|
|
|
static int gb_command_text[NUM_GB_COMMANDS] = {
|
|
TXT_GBM_EXITSHIP, TXT_GBM_REPAIR, TXT_GBM_RENAME, TXT_GBM_P0, TXT_GBM_P1,
|
|
TXT_GBM_P2, TXT_GBM_P3, TXT_GBM_P4, TXT_GBM_P5, TXT_GBM_P6,
|
|
TXT_GBM_P7, TXT_GBM_S0, TXT_GBM_S1, TXT_GBM_S2, TXT_GBM_S3,
|
|
TXT_GBM_S4, TXT_GBM_S5, TXT_GBM_S6, TXT_GBM_S7, TXT_GBM_FINDPOWERUPS,
|
|
TXT_GBM_FINEPOWERUP, TXT_GBM_FINDENERGY, TXT_GBM_FINDROBOT, TXT_GBM_FINDTHIEF, TXT_GBM_FINDEXIT,
|
|
TXT_GBM_FINDMINE, TXT_GBM_FINDCMARK, TXT_GBM_M0, TXT_GBM_M1, TXT_GBM_M2,
|
|
TXT_GBM_M3, TXT_GBM_M4, TXT_GBM_M5, TXT_GBM_M6, TXT_GBM_M7,
|
|
TXT_GBM_ESCORT, TXT_GBM_USEEXTIN, TXT_GBM_WINGNUT, TXT_GBM_PILOTPOWER, TXT_GBM_GUARDIAN,
|
|
TXT_GBM_ANTIVIRUS, TXT_GBM_ALLOWCHAT, TXT_GBM_DISALLOWCHAT, TXT_GBM_RETURNTOSHIP,
|
|
};
|
|
|
|
// Speed is automatic
|
|
#define NUM_GB_USABLE_POWERUPS 5
|
|
#define NUM_GB_POWERUPS 6
|
|
#define GB_POW_STRING_OFFSET GBC_EXTINGUISH
|
|
|
|
#define MAX_AV_ROBOTS 100
|
|
|
|
static int gb_pow_text[NUM_GB_POWERUPS] = {TXT_GBP_PUTOUTFIRE, TXT_GBP_COWABUNGA, TXT_GBP_BEGENTAL,
|
|
TXT_GBP_LETSDOIT, TXT_GBP_CONVERTEN, TXT_GBP_ACCELERATOR};
|
|
|
|
static int gb_pow_not_text[NUM_GB_POWERUPS] = {TXT_GBPN_NOFIRE, TXT_GBPN_NOFIGHT, TXT_GBPN_GOPLAY,
|
|
TXT_GBPN_TOODISSY, TXT_GBPN_NOENEMY, TXT_GBPN_NOCOFFEE};
|
|
|
|
static int gb_pow_pickup_text[NUM_GB_POWERUPS] = {TXT_GBPP_EXTIN, TXT_GBPP_WINGNUT, TXT_GBPP_MANCON,
|
|
TXT_GBPP_GUARD, TXT_GBPP_ANTIV, TXT_GBPP_ACCEL};
|
|
|
|
#define GB_POW_FIRE 0
|
|
#define GB_POW_WINGNUT 1
|
|
#define GB_POW_MANUAL 2
|
|
#define GB_POW_RTYPE 3
|
|
#define GB_POW_ANTIVIRUS 4
|
|
#define GB_POW_SPEED 5
|
|
|
|
#define NUM_GB_SOUNDS 5
|
|
#define GBS_VERY_DAMAGED 0
|
|
#define GBS_AMBIENT 1
|
|
|
|
// Extensions to the command interface
|
|
#define COM_POWERUP_NOTIFY 4
|
|
#define COM_POWERUP_PICKUP 5
|
|
|
|
struct guidebot_data {
|
|
char name[8];
|
|
char powerups[NUM_GB_USABLE_POWERUPS];
|
|
|
|
int sounds[NUM_GB_SOUNDS];
|
|
|
|
float mode_time;
|
|
|
|
uint16_t mp_slot; // Owner's slot number
|
|
int my_player; // Owner's object reference
|
|
|
|
bool f_parented; // Buddy will not collide with parent until it isn't parented
|
|
bool f_pickup; // Marked for pickup by the owner
|
|
|
|
int mode; // See above
|
|
int sub_mode; // See above
|
|
|
|
char last_rtype_dir;
|
|
|
|
int g_powerup;
|
|
int num_pg;
|
|
int num_sg;
|
|
|
|
int camera_obj;
|
|
|
|
int last_command; // If negative, there is isn't any command
|
|
float last_at_goal_time;
|
|
float next_ambient_time; // Next time the ambient goal changes
|
|
|
|
float last_sound_time;
|
|
|
|
int flags;
|
|
|
|
float last_retreat_time;
|
|
float return_time;
|
|
|
|
int me;
|
|
float last_anim;
|
|
|
|
int extinguish_spew_id;
|
|
int extinguish_obj_list[5];
|
|
int num_extinguish_objs;
|
|
float extinguish_obj_time;
|
|
|
|
float max_acc;
|
|
float max_speed;
|
|
|
|
int num_av_robots;
|
|
int av_robot_list[MAX_AV_ROBOTS];
|
|
float av_revert_time[MAX_AV_ROBOTS];
|
|
|
|
float speed_time_left;
|
|
|
|
float time_till_next_pvis_check;
|
|
float time_till_next_flare;
|
|
|
|
int amb_camera_handle;
|
|
|
|
float next_powerup_check_time;
|
|
uint16_t powerup_ids[6];
|
|
};
|
|
|
|
class GuideBot : public BaseObjScript {
|
|
private:
|
|
guidebot_data *memory;
|
|
void DoMessage(const char *str, bool f_high_priority, const char *sound_name = NULL, bool f_sound_2d = false);
|
|
// void InitPowerup(int me, char pow_id);
|
|
// void DoPowerupFrame(int me);
|
|
void DoPowerupCheck(int me);
|
|
void ReInitAmbient(int me);
|
|
bool InitExtinguish(bool f_player_on_fire);
|
|
bool SetMode(int me, char mode);
|
|
bool SetSubMode(int me, char sub_mode, int it = OBJECT_HANDLE_NONE);
|
|
bool DoInit(int me_handle, bool f_reinit);
|
|
void DoCollide(int me, tOSIRISEVTCOLLIDE *evt_collide);
|
|
bool DoUse(int me);
|
|
bool DoNotify(int me_handle, tOSIRISEventInfo *data);
|
|
void DoFrame(int me_handle);
|
|
bool DoExternalCommands(int me, gb_com *command, int it);
|
|
int MakeCommandList(int *gbc_list);
|
|
void AddGetToGoalCommonGoals(int me);
|
|
|
|
public:
|
|
GuideBot() { memory = NULL; }
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Thief
|
|
//-----------------
|
|
|
|
#define THIEF_AMBIENT 0 // Roaming the mine
|
|
#define THIEF_TRACK_PLAYER 1 // Getting to the player
|
|
#define THIEF_STEAL 2 // Stealing an item (colliding with player)
|
|
#define THIEF_RUN_AWAY 3 // In run-away mode
|
|
#define THIEF_SURRENDER 4 // Surrender and then...
|
|
#define THIEF_TIME_BOMB 5 // Go into bomb mode
|
|
#define THIEF_PLAYER_DEATH_TAUNT 6 // Thief laughing at dead player
|
|
#define THIEF_DEATH 7 // Thief dying
|
|
#define THIEF_KIDNAP_GB 8 // Kipnapping the GB
|
|
#define THIEF_CORNERED 9 // Cornered behavior
|
|
#define THIEF_ATTACK 10 // Used by SUPER THIEF
|
|
|
|
#define THIEF_SUB_NONE 0 // Normal mode of operation
|
|
#define THIEF_SUB_SUMMON 1 // Summon other robots
|
|
#define THIEF_SUB_CLOAK 2 // Cloak effect
|
|
#define THIEF_SUB_THROW_ITEM 3 // Throw an item from your insides(ewww)
|
|
#define THIEF_SUB_GET_POWERUP 4 // Pickup a powerup
|
|
#define THIEF_SUB_DROP_POWERUP 5 // Quick anim to drop an externally carried item
|
|
|
|
#define THIEF_F_SURRENDERED 1
|
|
#define THIEF_F_GOT_POWERUP 2
|
|
|
|
// Chrishack make an "occurance" 2D table SUB vs MODE with float for chance per check interval
|
|
|
|
#define MAX_STOLEN_OBJS 5
|
|
#define MAX_STOLEN_INV 10
|
|
|
|
#define THIEFABLEITEM_PRIMARY 0
|
|
#define THIEFABLEITEM_SECONDARY 1
|
|
#define THIEFABLEITEM_ACCESSORY 2
|
|
struct tThiefItems {
|
|
int index, type;
|
|
float attempt_one, attempt_two, attempt_two_no_one;
|
|
int name_idx;
|
|
};
|
|
|
|
static tThiefItems ThiefableItems[] = {
|
|
{0, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_LASERS}, // Laser
|
|
{1, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_VAUSS}, // Vauss
|
|
{2, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_MICROWAVE}, // Microwave
|
|
{3, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_PLASMA}, // Plasma
|
|
{4, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_FUSION}, // Fusion
|
|
{5, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_SUPER}, // SuperLaser
|
|
{6, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_MASS}, // Mass Driver
|
|
{7, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_NAPALM}, // Napalm
|
|
{8, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_EMD}, // EMD Gun
|
|
{9, THIEFABLEITEM_PRIMARY, 0.70f, 0.50f, 0.70f, TXT_WEAP_OMEGA}, // Omega
|
|
|
|
{10, THIEFABLEITEM_SECONDARY, 0.50f, 0.00f, 0.90f, TXT_WEAP_CONCUSSION}, // Concussion
|
|
{11, THIEFABLEITEM_SECONDARY, 0.60f, 0.00f, 0.90f, TXT_WEAP_HOMING}, // Homing
|
|
{12, THIEFABLEITEM_SECONDARY, 0.70f, 0.00f, 0.90f, TXT_WEAP_IMPACT}, // Impact Mortar
|
|
{13, THIEFABLEITEM_SECONDARY, 0.80f, 0.00f, 0.90f, TXT_WEAP_SMART}, // Smart
|
|
{14, THIEFABLEITEM_SECONDARY, 0.90f, 0.00f, 0.90f, TXT_WEAP_MEGA}, // Mega
|
|
{15, THIEFABLEITEM_SECONDARY, 0.50f, 0.00f, 0.90f, TXT_WEAP_FRAG}, // Frag
|
|
{16, THIEFABLEITEM_SECONDARY, 0.60f, 0.00f, 0.90f, TXT_WEAP_GUIDED}, // Guided
|
|
{17, THIEFABLEITEM_SECONDARY, 0.70f, 0.00f, 0.90f, TXT_WEAP_NAPALMR}, // Napalm Rocket
|
|
{18, THIEFABLEITEM_SECONDARY, 0.80f, 0.00f, 0.90f, TXT_WEAP_CYCLONE}, // Cyclone
|
|
{19, THIEFABLEITEM_SECONDARY, 0.90f, 0.00f, 0.90f, TXT_WEAP_BLACKSHARK}, // Black Shark
|
|
|
|
{1, THIEFABLEITEM_ACCESSORY, 0.20f, 0.20f, 0.20f, TXT_WEAP_AUTOMAP}, // Automap
|
|
{2, THIEFABLEITEM_ACCESSORY, 0.0f, 0.20f, 0.20f, TXT_WEAP_HEADLIGHT}, // Headlight
|
|
{3, THIEFABLEITEM_ACCESSORY, 0.80f, 0.40f, 0.80f, TXT_WEAP_ETSCONVERTER}, // E2S Converter
|
|
{4, THIEFABLEITEM_ACCESSORY, 1.00f, 0.60f, 1.00f, TXT_WEAP_CLOAK}, // Cloak
|
|
{5, THIEFABLEITEM_ACCESSORY, 1.00f, 0.60f, 1.00f, TXT_WEAP_INVULNERABILITY}, // Invuln
|
|
{6, THIEFABLEITEM_ACCESSORY, 1.00f, 0.60f, 1.00f, TXT_WEAP_RAPIDFIRE}, // RapidFire
|
|
{7, THIEFABLEITEM_ACCESSORY, 1.00f, 0.60f, 1.00f, TXT_WEAP_QUADLASERS}, // Quads
|
|
};
|
|
static int numThiefableItems = sizeof(ThiefableItems) / sizeof(tThiefItems);
|
|
|
|
struct inv_item {
|
|
uint16_t id;
|
|
int owner;
|
|
}; // not really inventory items, but items such as quads, automap, headlight, etc (non-weapons)
|
|
|
|
struct thief_data {
|
|
char mode;
|
|
char sub_mode;
|
|
char last_special_sub_mode;
|
|
|
|
char flags;
|
|
|
|
char f_cloaked;
|
|
char f_see_player;
|
|
|
|
float sub_mode_time;
|
|
float mode_time;
|
|
float next_test_time;
|
|
float last_frame;
|
|
int bomb_handle;
|
|
float last_summon_time;
|
|
|
|
float last_shields;
|
|
|
|
int num_stolen_objs;
|
|
int stolen_objs[MAX_STOLEN_OBJS];
|
|
|
|
int num_stolen_weapons;
|
|
weapon_item stolen_weapons[MAX_STOLEN_WEAPONS];
|
|
|
|
int num_stolen_inv;
|
|
inv_item stolen_inv[MAX_STOLEN_INV];
|
|
|
|
float fear;
|
|
float agression;
|
|
|
|
};
|
|
|
|
class Thief : public BaseObjScript {
|
|
private:
|
|
thief_data *memory;
|
|
void DoMessage(const char *str);
|
|
|
|
void SetMode(int me, int mode);
|
|
void SetSubMode(int me, int submode);
|
|
|
|
void SpewEverything(int me);
|
|
void DoAttack(int me, int it);
|
|
bool DoSteal(int me, int it, int attempt_num, bool f_last_success);
|
|
void DoKidnap(int me, int it);
|
|
|
|
void DoInit(int me_handle);
|
|
void DoCollide(int me, tOSIRISEVTCOLLIDE *evt_collide);
|
|
bool DoNotify(int me, tOSIRISEVTAINOTIFY *notify);
|
|
void DoDestroy(int me, tOSIRISEVTDESTROY *evt_destroy);
|
|
void DoFrame(int me);
|
|
void DoSubModeFrame(int me);
|
|
|
|
public:
|
|
Thief() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// SuperThief
|
|
//-----------------
|
|
|
|
struct tSuperThiefItems {
|
|
int index, autoselect, type;
|
|
float fire_delay;
|
|
int name_idx;
|
|
const char *weapon_name;
|
|
const char *fire_sound;
|
|
};
|
|
|
|
static tSuperThiefItems SuperThiefableItems[] = {
|
|
{0, -1, THIEFABLEITEM_PRIMARY, .25, TXT_WEAP_LASERS, "", ""}, // Laser
|
|
{1, 4, THIEFABLEITEM_PRIMARY, .1f, TXT_WEAP_VAUSS, "Vauss", "WpnPyroGLVaussSidefire"}, // Vauss
|
|
{2, 2, THIEFABLEITEM_PRIMARY, .1f, TXT_WEAP_MICROWAVE, "Raygun", "WpnPyroGLMicrowaveSide"}, // Microwave
|
|
{3, 3, THIEFABLEITEM_PRIMARY, .15f, TXT_WEAP_PLASMA, "Plasma", "WpnPyroGLPlasmaFire"}, // Plasma
|
|
{4, 5, THIEFABLEITEM_PRIMARY, .8f, TXT_WEAP_FUSION, "Fusion", "WpnPyroGLFusionFire"}, // Fusion
|
|
{5, 6, THIEFABLEITEM_PRIMARY, .25f, TXT_WEAP_SUPER, "Laser Level 5 -Yellow", "WpnPyroGLSuperLaser"}, // SuperLaser
|
|
{6, 7, THIEFABLEITEM_PRIMARY, 1.0f, TXT_WEAP_MASS, "Mass Driver", "WpnPyroGLMDriverFire"}, // Mass Driver
|
|
{7, -1, THIEFABLEITEM_PRIMARY, 1.0f, TXT_WEAP_NAPALM, "", ""}, // Napalm
|
|
{8, 1, THIEFABLEITEM_PRIMARY, .2f, TXT_WEAP_EMD, "EMDblob", "WpnPyroGLEMDfire"}, // EMD Gun
|
|
{9, -1, THIEFABLEITEM_PRIMARY, 1.0f, TXT_WEAP_OMEGA, "", ""}, // Omega
|
|
|
|
{10, 1, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_CONCUSSION, "Concussion", "concmissilefire71"}, // Concussion
|
|
{11, 2, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_HOMING, "homing", "HomingfireB1"}, // Homing
|
|
{12, -1, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_IMPACT, "Impact Mortar", "Drop weapon"}, // Impact Mortar
|
|
{13, 3, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_SMART, "Smart", "Smartfire1"}, // Smart
|
|
{14, 8, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_MEGA, "Mega", "Mega missile fire"}, // Mega
|
|
{15, 6, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_FRAG, "Frag", "WpnFragFire"}, // Frag
|
|
{16, 4, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_GUIDED, "Guided", "Guided31"}, // Guided
|
|
{17, 5, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_NAPALMR, "NapalmRocket", "napalmrocket31"}, // Napalm Rocket
|
|
{18, 7, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_CYCLONE, "Cyclone Pack", "CycloneFire1"}, // Cyclone
|
|
{19, -1, THIEFABLEITEM_SECONDARY, .2f, TXT_WEAP_BLACKSHARK, "", ""}, // Black Shark
|
|
};
|
|
|
|
static int numSuperThiefableItems = sizeof(SuperThiefableItems) / sizeof(tSuperThiefItems);
|
|
|
|
#define SUPERTHIEF_MELEE_DIST 50.0f
|
|
|
|
void SuperThief::SetMode(int me, char mode) {
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
switch (mode) {
|
|
case SUPERTHIEF_WAIT: {
|
|
} break;
|
|
|
|
case SUPERTHIEF_INTRO: {
|
|
} break;
|
|
case SUPERTHIEF_RANGED:
|
|
AI_SetType(me, AIT_EVADER1);
|
|
break;
|
|
case SUPERTHIEF_MELEE:
|
|
AI_SetType(me, AIT_MELEE1);
|
|
break;
|
|
case SUPERTHIEF_DEATH: {
|
|
int flags = AIF_DODGE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
flags = AIF_DISABLE_FIRING | AIF_DISABLE_MELEE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1, GF_ORIENT_TARGET | GF_KEEP_AT_COMPLETION, me);
|
|
|
|
float temp = 0.0f;
|
|
AI_Value(me, VF_SET, AIV_F_MAX_TURN_RATE, &temp);
|
|
Obj_SetCustomAnim(me, 246.0f, 292.0f, 6.0f, 0, Sound_FindId("RbtThiefDeath"), -1);
|
|
} break;
|
|
}
|
|
|
|
memory->time_till_fire_secondary = 1.0f + (float)rand() / (float)RAND_MAX;
|
|
memory->mode = mode;
|
|
memory->mode_time = 0.0f;
|
|
}
|
|
|
|
void SuperThief::SpewEverything(int me) {
|
|
int i;
|
|
int powerup_handle;
|
|
int room;
|
|
vector pos;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
for (i = 0; i < memory->num_stolen_weapons; i++) {
|
|
uint16_t id;
|
|
int j;
|
|
|
|
for (j = 0; j < memory->stolen_weapons[i].amount; j++) {
|
|
float speed = rand() / (float)RAND_MAX * 20.0f + 5.0f;
|
|
vector dir;
|
|
|
|
dir.x = rand() / (float)RAND_MAX - 0.5f;
|
|
dir.y = rand() / (float)RAND_MAX - 0.5f;
|
|
dir.z = rand() / (float)RAND_MAX - 0.5f;
|
|
|
|
vm_VectorNormalize(&dir);
|
|
dir *= speed;
|
|
|
|
Player_Value(memory->stolen_weapons[i].owner, VF_GET, PLYSV_US_WEAPON_POWERUP_ID, &id,
|
|
memory->stolen_weapons[i].index);
|
|
powerup_handle = Obj_Create(OBJ_POWERUP, id, room, &pos, NULL, OBJECT_HANDLE_NONE, &dir);
|
|
}
|
|
|
|
memory->stolen_weapons[i].amount = 0;
|
|
}
|
|
}
|
|
|
|
bool SuperThief::DoNotify(int me, tOSIRISEVTAINOTIFY *notify) {
|
|
if (notify->notify_type == AIN_MELEE_HIT) {
|
|
int target_handle;
|
|
int target_type;
|
|
|
|
AI_Value(me, VF_GET, AIV_I_TARGET_HANDLE, &target_handle);
|
|
Obj_Value(target_handle, VF_GET, OBJV_I_TYPE, &target_type);
|
|
|
|
if (target_type == OBJ_PLAYER) {
|
|
bool f_success;
|
|
|
|
memory->time_till_fire_secondary = 1.0f + (float)rand() / (float)RAND_MAX;
|
|
f_success = DoSteal(me, target_handle);
|
|
}
|
|
}
|
|
|
|
switch (memory->mode) {
|
|
case SUPERTHIEF_WAIT: {
|
|
if (notify->notify_type == AIN_MOVIE_START) {
|
|
SetMode(me, SUPERTHIEF_INTRO);
|
|
}
|
|
} break;
|
|
|
|
case SUPERTHIEF_INTRO: {
|
|
if (notify->notify_type == AIN_MOVIE_END) {
|
|
SetMode(me, SUPERTHIEF_RANGED);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SuperThief::DoSteal(int me, int it) {
|
|
int max_tries = numSuperThiefableItems;
|
|
float gen_perc, perc_chance;
|
|
;
|
|
int i;
|
|
int count_max, count_num;
|
|
bool *attempted_steals;
|
|
|
|
attempted_steals = (bool *)malloc(numSuperThiefableItems * sizeof(bool));
|
|
if (!attempted_steals)
|
|
return false;
|
|
|
|
float ftime = Game_GetFrameTime();
|
|
if (ftime == 0)
|
|
ftime = 1.0f;
|
|
srand(Game_GetTime() / ftime);
|
|
|
|
for (i = 0; i < numSuperThiefableItems; i++) {
|
|
attempted_steals[i] = false;
|
|
}
|
|
|
|
while (max_tries--) {
|
|
i = rand() % numSuperThiefableItems;
|
|
|
|
if (attempted_steals[i]) {
|
|
// we already tried this one, try another
|
|
max_tries++;
|
|
continue;
|
|
}
|
|
|
|
attempted_steals[i] = true;
|
|
|
|
{
|
|
count_max = MAX_STOLEN_WEAPONS;
|
|
count_num = memory->num_stolen_weapons;
|
|
|
|
if (count_num >= count_max) {
|
|
mprintf(0, "SUPER THIEF: no mo' room\n");
|
|
continue; // we have no more room
|
|
}
|
|
|
|
// mprintf(0,"THIEF: Checking %s\n",TXT(SuperThiefableItems[i].name_idx));
|
|
|
|
bool can_take = false;
|
|
int amount = 0;
|
|
char message[256];
|
|
|
|
switch (SuperThiefableItems[i].type) {
|
|
case THIEFABLEITEM_PRIMARY:
|
|
// check to see if we can steal the primary
|
|
Player_Value(it, VF_GET, PLYSV_I_WEAPON, &amount, SuperThiefableItems[i].index);
|
|
if (SuperThiefableItems[i].index != 0 && amount) {
|
|
can_take = true;
|
|
snprintf(message, sizeof(message), TXT_THIEFSTEAL, TXT(SuperThiefableItems[i].name_idx));
|
|
}
|
|
break;
|
|
|
|
case THIEFABLEITEM_SECONDARY:
|
|
// check to see if we can steal the secondary
|
|
{
|
|
Player_Value(it, VF_GET, PLYSV_I_WEAPON, &amount, SuperThiefableItems[i].index);
|
|
|
|
if (amount) {
|
|
int max;
|
|
|
|
if (SuperThiefableItems[i].index > 14) {
|
|
// 2nd selection group (black shark, etc)
|
|
max = (19 - SuperThiefableItems[i].index) / 2.5f + 1;
|
|
} else {
|
|
// 1st selection group (mega, etc)
|
|
max = (14 - SuperThiefableItems[i].index) / 2.5f + 1;
|
|
}
|
|
|
|
int new_amount = (rand() / (float)(RAND_MAX + 1)) * max + 1;
|
|
|
|
if (new_amount < amount)
|
|
amount = new_amount;
|
|
|
|
can_take = true;
|
|
|
|
if (amount == 1) {
|
|
snprintf(message, sizeof(message), TXT_STHIEFSTEAL, TXT(SuperThiefableItems[i].name_idx));
|
|
} else {
|
|
snprintf(message, sizeof(message), TXT_STHIEFSTEAL2, amount, TXT(SuperThiefableItems[i].name_idx));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!can_take)
|
|
continue;
|
|
|
|
mprintf(0, "Super Thief: Nice %s!\n", TXT(SuperThiefableItems[i].name_idx));
|
|
|
|
Player_AddHudMessage(it, message);
|
|
if (rand() % 50) {
|
|
Sound_Play3d(me, Sound_FindId("RbtThiefHehHehHeh"));
|
|
} else {
|
|
Sound_Play3d(me, Sound_FindId("RbtThiefLaugh"));
|
|
}
|
|
|
|
memory->stolen_weapons[memory->num_stolen_weapons].index = SuperThiefableItems[i].index;
|
|
memory->stolen_weapons[memory->num_stolen_weapons].owner = it;
|
|
memory->stolen_weapons[memory->num_stolen_weapons].amount = amount;
|
|
memory->num_stolen_weapons++;
|
|
|
|
if (SuperThiefableItems[i].type == THIEFABLEITEM_PRIMARY) {
|
|
amount = 0;
|
|
if (SuperThiefableItems[i].autoselect > memory->cur_weapon) {
|
|
uint16_t wpn = Wpn_FindID(SuperThiefableItems[i].weapon_name);
|
|
int snd = Sound_FindId(SuperThiefableItems[i].fire_sound);
|
|
|
|
Obj_WBValue(me, 1, VF_SET, WBSV_I_FIRE_SOUND, &snd, 0);
|
|
Obj_WBValue(me, 1, VF_SET, WBSV_F_LATENCY, &SuperThiefableItems[i].fire_delay, 0);
|
|
Obj_WBValue(me, 1, VF_SET, WBSV_US_GUNPT_WEAPON_ID, &wpn, 0);
|
|
|
|
strcpy(memory->weapon, SuperThiefableItems[i].weapon_name);
|
|
strcpy(memory->sound, SuperThiefableItems[i].weapon_name);
|
|
memory->latency = SuperThiefableItems[i].fire_delay;
|
|
}
|
|
} else {
|
|
amount *= -1;
|
|
}
|
|
|
|
Player_Value(it, VF_SET, PLYSV_I_WEAPON, &amount, SuperThiefableItems[i].index);
|
|
|
|
free(attempted_steals);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
mprintf(0, "Super Thief: Not taking anything\n");
|
|
free(attempted_steals);
|
|
return false; // we didn't take anything this try
|
|
}
|
|
|
|
void SuperThief::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(superthief_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (superthief_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
MSafe_GetValue(MSAFE_OBJECT_SHIELDS, &m);
|
|
|
|
memory->next_mode_time = Game_GetTime() + ((float)rand() / (float)RAND_MAX) * 3.0f + 3.0f;
|
|
memory->force_ranged_shields = m.shields / 15.0f;
|
|
memory->mode = SUPERTHIEF_WAIT;
|
|
memory->f_very_hurt = 0;
|
|
|
|
memory->num_stolen_weapons = 0;
|
|
memory->cur_weapon = -1;
|
|
|
|
memory->mode_time = 0.0f;
|
|
memory->last_frame = 0.0f;
|
|
memory->time_till_fire_secondary = 1.0f + (float)rand() / (float)RAND_MAX;
|
|
|
|
memory->laser_obj = CreateAndAttach(me, "STEmitter", OBJ_ROBOT, 2, 0, true, true);
|
|
|
|
uint16_t wpn = Wpn_FindID("Laser Level 1 - Red");
|
|
int snd = Sound_FindId("Laser level 1");
|
|
|
|
strcpy(memory->weapon, "Laser Level 1 - Red");
|
|
strcpy(memory->sound, "Laser level 1");
|
|
|
|
memory->latency = 0.25f;
|
|
|
|
memory->laser_on = false;
|
|
|
|
memory->next_fire_flare_time = 0.0f;
|
|
memory->flare_fire_index = 0;
|
|
|
|
Obj_WBValue(me, 1, VF_SET, WBSV_I_FIRE_SOUND, &snd, 0);
|
|
Obj_WBValue(me, 1, VF_SET, WBSV_F_LATENCY, &memory->latency, 0);
|
|
Obj_WBValue(me, 1, VF_SET, WBSV_US_GUNPT_WEAPON_ID, &wpn, 0);
|
|
}
|
|
|
|
void SuperThief::FireFlare(int me) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = me;
|
|
mstruct.index = Wpn_FindID("Yellow flare");
|
|
if (mstruct.index == -1)
|
|
return;
|
|
mstruct.gunpoint = 1;
|
|
|
|
Sound_Play3d(me, Sound_FindId("Flare"));
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_FIRE_WEAPON, &mstruct);
|
|
}
|
|
|
|
void SuperThief::CheckAndFireSecondary(int me) {
|
|
int i;
|
|
const char *wname;
|
|
const char *sname;
|
|
int best_select = -1;
|
|
int best_index;
|
|
|
|
for (i = 0; i < memory->num_stolen_weapons; i++) {
|
|
if (memory->stolen_weapons[i].amount > 0 &&
|
|
SuperThiefableItems[memory->stolen_weapons[i].index].type == THIEFABLEITEM_SECONDARY) {
|
|
if (best_select < SuperThiefableItems[memory->stolen_weapons[i].index].autoselect) {
|
|
wname = SuperThiefableItems[memory->stolen_weapons[i].index].weapon_name;
|
|
sname = SuperThiefableItems[memory->stolen_weapons[i].index].fire_sound;
|
|
best_select = SuperThiefableItems[memory->stolen_weapons[i].index].autoselect;
|
|
best_index = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (best_select >= 0) {
|
|
memory->stolen_weapons[best_index].amount--;
|
|
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = me;
|
|
mstruct.index = Wpn_FindID(wname);
|
|
if (mstruct.index == -1)
|
|
return;
|
|
mstruct.gunpoint = 1;
|
|
|
|
Sound_Play3d(me, Sound_FindId(sname));
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_FIRE_WEAPON, &mstruct);
|
|
}
|
|
}
|
|
|
|
void SuperThief::DoInterval(int me) {
|
|
bool f_force_ranged = false;
|
|
int target_handle;
|
|
float anim_frame;
|
|
float shields;
|
|
int flags;
|
|
msafe_struct m;
|
|
int room;
|
|
vector pos;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &anim_frame);
|
|
Obj_Value(me, VF_GET, OBJV_F_SHIELDS, &shields);
|
|
Obj_Value(me, VF_GET, OBJV_I_FLAGS, &flags);
|
|
|
|
if (memory->laser_on) {
|
|
ray_info ray;
|
|
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
// Determine real start pos - room
|
|
vector end_pos = pos;
|
|
end_pos += orient.fvec * 2000.0f;
|
|
|
|
int fvi_flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS;
|
|
int fate = FVI_RayCast(me, &pos, &end_pos, room, 0.0f, fvi_flags, &ray);
|
|
|
|
Obj_Value(memory->camera_obj, VF_SET, OBJV_I_ROOMNUM, &ray.hit_room);
|
|
Obj_Value(memory->camera_obj, VF_SET, OBJV_V_POS, &ray.hit_point);
|
|
}
|
|
|
|
memory->f_very_hurt = f_force_ranged = (shields < memory->force_ranged_shields) != 0;
|
|
|
|
if ((memory->mode != SUPERTHIEF_DEATH) && (flags & OF_AI_DEATH) && anim_frame >= 0.0f && anim_frame <= 6.0f) {
|
|
mprintf(0, "ST: Dying\n");
|
|
SetMode(me, SUPERTHIEF_DEATH);
|
|
}
|
|
|
|
if (memory->mode == SUPERTHIEF_DEATH) {
|
|
// Do dynamic death
|
|
if ((memory->mode_time < 4.0f && memory->mode_time + Game_GetFrameTime() >= 4.0f) ||
|
|
(memory->mode_time < 6.0f && memory->mode_time + Game_GetFrameTime() >= 6.0f)) {
|
|
int weapon_id;
|
|
|
|
weapon_id = Wpn_FindID("TubbsHitBlast");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, NULL, me);
|
|
}
|
|
|
|
if (memory->mode_time < 3.0f && memory->mode_time + Game_GetFrameTime() >= 3.0f) {
|
|
Obj_Burning(me, 1000000.0f, 1.0f);
|
|
}
|
|
|
|
if (memory->mode_time > 6.2f) {
|
|
mprintf(0, "ST: Died\n");
|
|
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);
|
|
}
|
|
} else if (memory->mode == SUPERTHIEF_INTRO) {
|
|
if (memory->mode_time < 6.0f && memory->mode_time + Game_GetFrameTime() >= 6.0f) {
|
|
int flags = AIF_FORCE_AWARENESS | AIF_DETERMINE_TARGET;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
float awareness = AWARE_FULLY;
|
|
AI_Value(me, VF_SET, AIV_F_AWARENESS, &awareness);
|
|
}
|
|
|
|
if (memory->last_frame < 234.0f && anim_frame >= 234.0f) {
|
|
msafe_struct mstruct;
|
|
int type;
|
|
|
|
memory->laser_on = true;
|
|
memory->camera_obj = Obj_Create(OBJ_POWERUP, Obj_FindID("Invisiblepowerup"), room, &pos, NULL, me);
|
|
mstruct.objhandle = memory->camera_obj;
|
|
MSafe_CallFunction(MSAFE_OBJECT_NO_RENDER, &mstruct);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &mstruct.roomnum);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &mstruct.pos);
|
|
Obj_Value(memory->camera_obj, VF_GET, OBJV_V_POS, &mstruct.pos2);
|
|
|
|
mstruct.objhandle = memory->laser_obj;
|
|
mstruct.ithandle = memory->camera_obj;
|
|
mstruct.lifetime = 10000000.0f;
|
|
mstruct.size = 1.0f;
|
|
|
|
mstruct.interval = 1.0f;
|
|
mstruct.count = 2;
|
|
mstruct.index = 1;
|
|
mstruct.texnum = Scrpt_FindTextureName("Stlaser");
|
|
mstruct.color = ((128 >> 3) << 10) | ((128 >> 3) << 5) | (128 >> 3);
|
|
|
|
mstruct.state = 0;
|
|
mstruct.flags = 0;
|
|
|
|
MSafe_CallFunction(MSAFE_WEATHER_LIGHTNING_BOLT, &mstruct);
|
|
}
|
|
} else if (memory->mode == SUPERTHIEF_WAIT) {
|
|
} else {
|
|
// Put the mod stuff here!
|
|
|
|
if (memory->mode == SUPERTHIEF_RANGED) {
|
|
memory->time_till_fire_secondary -= Game_GetFrameTime();
|
|
float last_see_time;
|
|
float last_hear_time;
|
|
AI_Value(me, VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &last_see_time);
|
|
AI_Value(me, VF_GET, AIV_F_LAST_HEAR_TARGET_TIME, &last_hear_time);
|
|
|
|
if (memory->next_fire_flare_time < Game_GetTime() && last_see_time + 7.0f >= Game_GetTime() &&
|
|
last_hear_time + 7.0f >= Game_GetTime()) {
|
|
FireFlare(me);
|
|
memory->flare_fire_index++;
|
|
|
|
if (memory->flare_fire_index >= 3) {
|
|
memory->flare_fire_index = 0;
|
|
memory->next_fire_flare_time = Game_GetTime() + (rand() / (float)RAND_MAX) * 10.0f + 5.0f;
|
|
} else {
|
|
memory->next_fire_flare_time = Game_GetTime() + .75f;
|
|
}
|
|
}
|
|
|
|
if (memory->time_till_fire_secondary < 0.0f) {
|
|
memory->time_till_fire_secondary = 1.0f + (float)rand() / (float)RAND_MAX;
|
|
CheckAndFireSecondary(me);
|
|
}
|
|
}
|
|
|
|
if ((!f_force_ranged) && (memory->next_mode_time < Game_GetTime())) {
|
|
if (memory->mode == SUPERTHIEF_MELEE) {
|
|
AI_SetType(me, AIT_EVADER1);
|
|
memory->mode = SUPERTHIEF_RANGED;
|
|
} else if ((rand() % 150) > 20) {
|
|
AI_SetType(me, AIT_MELEE1);
|
|
memory->mode = SUPERTHIEF_MELEE;
|
|
}
|
|
|
|
if (memory->mode == SUPERTHIEF_RANGED)
|
|
memory->next_mode_time = Game_GetTime() + 5.0f + ((float)rand() / (float)RAND_MAX) * 3.0f;
|
|
else
|
|
memory->next_mode_time = Game_GetTime() + 3.0f + ((float)rand() / (float)RAND_MAX) * 3.0f;
|
|
} else if ((f_force_ranged) && (memory->mode != SUPERTHIEF_RANGED)) {
|
|
// PlaySoundObj3d("RbtStingeattack1", me)
|
|
AI_SetType(me, AIT_EVADER1);
|
|
memory->mode = SUPERTHIEF_RANGED;
|
|
}
|
|
}
|
|
|
|
memory->last_frame = anim_frame;
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
int16_t SuperThief::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoInterval(data->me_handle);
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return (DoNotify(data->me_handle, &data->evt_ai_notify) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_DESTROY: {
|
|
SpewEverything(data->me_handle);
|
|
msafe_struct mo;
|
|
mo.objhandle = memory->laser_obj;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
mo.objhandle = memory->camera_obj;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
} break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (superthief_data *)data->evt_memrestore.memory_ptr;
|
|
|
|
uint16_t wpn = Wpn_FindID(memory->weapon);
|
|
int snd = Sound_FindId(memory->sound);
|
|
|
|
Obj_WBValue(data->me_handle, 1, VF_SET, WBSV_I_FIRE_SOUND, &snd, 0);
|
|
Obj_WBValue(data->me_handle, 1, VF_SET, WBSV_F_LATENCY, &memory->latency, 0);
|
|
Obj_WBValue(data->me_handle, 1, VF_SET, WBSV_US_GUNPT_WEAPON_ID, &wpn, 0);
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//-----------------
|
|
// FireAtDist
|
|
//-----------------
|
|
|
|
struct fireatdist_data {
|
|
int me;
|
|
float fire_dist;
|
|
float last_test_time;
|
|
bool last_f_fire;
|
|
};
|
|
|
|
class FireAtDist : public BaseObjScript {
|
|
private:
|
|
fireatdist_data *memory;
|
|
void DoInit(int me_handle);
|
|
void DoFrame(int me_handle);
|
|
|
|
public:
|
|
FireAtDist() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// HatePTMC
|
|
//-----------------
|
|
|
|
struct hateptmc_data {
|
|
int me;
|
|
float time_till_pulse;
|
|
};
|
|
|
|
class HatePTMC : public BaseObjScript {
|
|
private:
|
|
hateptmc_data *memory;
|
|
void DoInit(int me_handle);
|
|
void DoFrame(int me_handle);
|
|
|
|
public:
|
|
HatePTMC() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Tubbs
|
|
//-----------------
|
|
|
|
struct tubbs_data {
|
|
float base_speed;
|
|
float base_acc;
|
|
float max_speed;
|
|
float max_acc;
|
|
float full_anger_time;
|
|
};
|
|
|
|
class Tubbs : public BaseObjScript {
|
|
private:
|
|
tubbs_data *memory;
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
bool DoNotify(int me, tOSIRISEVTAINOTIFY *notify);
|
|
|
|
public:
|
|
Tubbs() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// Old Scratch
|
|
//-----------------
|
|
|
|
struct oldscratch_data {
|
|
float base_speed;
|
|
float base_acc;
|
|
float max_speed;
|
|
float max_acc;
|
|
float full_anger_time;
|
|
};
|
|
|
|
class OldScratch : public BaseObjScript {
|
|
private:
|
|
oldscratch_data *memory;
|
|
bool DoSteal(int me, int it);
|
|
void DoInit(int me);
|
|
void DoFrame(int me);
|
|
bool DoNotify(int me, tOSIRISEVTAINOTIFY *notify);
|
|
|
|
public:
|
|
OldScratch() {}
|
|
int16_t CallEvent(int event, tOSIRISEventInfo *data);
|
|
};
|
|
|
|
//-----------------
|
|
// BarnSwallow
|
|
//-----------------
|
|
|
|
#define BSM_NEST 0
|
|
#define BSM_FOLLOW 1
|
|
#define BSM_GET_POWERUP 2
|
|
#define BSM_ATTACK 3
|
|
#define BSM_ATTACK_CIRCLE_BACK 4
|
|
#define BSM_FLEE 5
|
|
#define BSM_RETURN_TO_NEST 6
|
|
|
|
#define BSCOMM_FOLLOW_ME 0 // send a percent chance so we can have a line sometimes
|
|
#define BSCOMM_FOLLOW_CANCEL_FROM_FOLLOWER 1
|
|
#define BSCOMM_FOLLOW_CANCEL_FROM_LEADER 2
|
|
#define BSCOMM_ATTACK_MY_TARGET 3
|
|
#define BSCOMM_ARE_YOU_IN_MODE 4
|
|
#define BSCOMM_ARE_YOU_AFTER_POWERUP 5
|
|
|
|
#define BSF_HAVE_POWERUP 1
|
|
#define BSF_ACCEPT_ORDER 2 // clear it before the call...
|
|
|
|
#define BS_MAX_FRIENDS 20
|
|
|
|
struct barnswallow_data {
|
|
int home_room;
|
|
vector nest_center;
|
|
float nest_rad;
|
|
|
|
int follower;
|
|
|
|
char num_friends;
|
|
int friends[BS_MAX_FRIENDS];
|
|
float next_friend_update_time;
|
|
|
|
char mode;
|
|
char last_non_attack_mode;
|
|
|
|
float mode_time;
|
|
float next_mode_time;
|
|
float next_attack_time;
|
|
float next_powerup_time;
|
|
float max_time_on_road;
|
|
int powerup_id;
|
|
|
|
int flags;
|
|
};
|
|
|
|
class BarnSwallow : public BaseObjScript {
|
|
private:
|
|
barnswallow_data *memory;
|
|
void DoInit(int me);
|
|
bool SendCommand(int me, int it, char command, int value = 0);
|
|
bool ReceiveCommand(int me, int it, gb_com *command);
|
|
void ComputeNextNestPnt(int me, vector *pos);
|
|
bool DoNotify(int me, tOSIRISEventInfo *data);
|
|
void ComputeNest(int me);
|
|
void UpdateFriendList(int me);
|
|
void DoFrame(int me);
|
|
bool SetMode(int me, char mode, int it = OBJECT_HANDLE_NONE);
|
|
|
|
public:
|
|
BarnSwallow() {}
|
|
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_PEST:
|
|
return new Pest;
|
|
break;
|
|
case ID_STINGER:
|
|
return new Stinger;
|
|
break;
|
|
case ID_DRAGON:
|
|
return new Dragon;
|
|
break;
|
|
case ID_TRACKER:
|
|
return new Tracker;
|
|
break;
|
|
case ID_LANCE:
|
|
return new Lance;
|
|
break;
|
|
case ID_FLAK:
|
|
return new Flak;
|
|
break;
|
|
case ID_SUPERTROOPER:
|
|
case ID_REDSUPERTROOPER:
|
|
return new SuperTrooper;
|
|
break;
|
|
case ID_JUGG:
|
|
return new Jugg;
|
|
break;
|
|
case ID_SIXGUN:
|
|
return new SixGun;
|
|
break;
|
|
case ID_SICKLE:
|
|
return new Sickle;
|
|
break;
|
|
case ID_GUIDEBOT:
|
|
return new GuideBot;
|
|
break;
|
|
case ID_FIREATDIST:
|
|
return new FireAtDist;
|
|
break;
|
|
case ID_TUBBS:
|
|
return new Tubbs;
|
|
break;
|
|
case ID_THIEF:
|
|
return new Thief;
|
|
break;
|
|
case ID_GBPOWERUP:
|
|
return new GBPowerup;
|
|
break;
|
|
case ID_BARNSWALLOW:
|
|
return new BarnSwallow;
|
|
break;
|
|
case ID_SPARKY:
|
|
return new Sparky;
|
|
break;
|
|
case ID_MANTARAY:
|
|
return new MantaRay;
|
|
break;
|
|
case ID_SPYHUNTER:
|
|
return new SpyHunter;
|
|
break;
|
|
case ID_SNIPER:
|
|
return new Sniper;
|
|
break;
|
|
case ID_HUMONCULOUS:
|
|
return new Humonculous;
|
|
break;
|
|
case ID_SEEKER:
|
|
return new Seeker;
|
|
break;
|
|
case ID_BETTY:
|
|
return new BettyScript;
|
|
break;
|
|
case ID_CHAFF:
|
|
return new ChaffScript;
|
|
break;
|
|
case ID_CHAFFCHUNK:
|
|
return new ChaffChunkScript;
|
|
break;
|
|
case ID_PROXMINE:
|
|
return new ProxMine;
|
|
break;
|
|
case ID_BETTYBOMB:
|
|
return new BettyBomb;
|
|
break;
|
|
case ID_JOSHBELL:
|
|
return new JoshBell;
|
|
break;
|
|
case ID_SKIFF:
|
|
return new Skiff;
|
|
break;
|
|
case ID_EXPLODEONCONTACT:
|
|
return new ExplodeOnContact;
|
|
break;
|
|
case ID_EXPLODETIMEOUT:
|
|
return new ExplodeTimeOut;
|
|
break;
|
|
case ID_GUNBOY:
|
|
return new Gunboy;
|
|
break;
|
|
case ID_DEATH_TOWER:
|
|
return new DTower;
|
|
break;
|
|
case ID_DEATH_COLLECTOR:
|
|
return new DCollector;
|
|
break;
|
|
case ID_CHEMICAL_BALL:
|
|
return new CBall;
|
|
break;
|
|
case ID_HELLION:
|
|
return new Hellion;
|
|
break;
|
|
case ID_SUPERTHIEF:
|
|
return new SuperThief;
|
|
break;
|
|
case ID_EVADERMODA:
|
|
return new EvaderModA;
|
|
break;
|
|
case ID_DESTROYONCONTACT:
|
|
return new DestroyOnContact;
|
|
break;
|
|
case ID_HATEPTMC:
|
|
return new HatePTMC;
|
|
break;
|
|
case ID_SNIPERNORUN:
|
|
return new SniperNoRun;
|
|
break;
|
|
case ID_FLAMERAS:
|
|
return new FlameRAS;
|
|
break;
|
|
case ID_OLDSCRATCH:
|
|
return new OldScratch;
|
|
break;
|
|
default:
|
|
mprintf(0, "SCRIPT: Illegal ID (%d)\n", id);
|
|
break;
|
|
};
|
|
return NULL;
|
|
}
|
|
|
|
void STDCALL DestroyInstance(int id, void *ptr) {
|
|
switch (id) {
|
|
case ID_PEST:
|
|
delete ((Pest *)ptr);
|
|
break;
|
|
case ID_STINGER:
|
|
delete ((Stinger *)ptr);
|
|
break;
|
|
case ID_DRAGON:
|
|
delete ((Dragon *)ptr);
|
|
break;
|
|
case ID_TRACKER:
|
|
delete ((Tracker *)ptr);
|
|
break;
|
|
case ID_LANCE:
|
|
delete ((Lance *)ptr);
|
|
break;
|
|
case ID_FLAK:
|
|
delete ((Flak *)ptr);
|
|
break;
|
|
case ID_SUPERTROOPER:
|
|
case ID_REDSUPERTROOPER:
|
|
delete ((SuperTrooper *)ptr);
|
|
break;
|
|
case ID_JUGG:
|
|
delete ((Jugg *)ptr);
|
|
break;
|
|
case ID_SIXGUN:
|
|
delete ((SixGun *)ptr);
|
|
break;
|
|
case ID_SICKLE:
|
|
delete ((Sickle *)ptr);
|
|
break;
|
|
case ID_GUIDEBOT:
|
|
delete ((GuideBot *)ptr);
|
|
break;
|
|
case ID_FIREATDIST:
|
|
delete ((FireAtDist *)ptr);
|
|
break;
|
|
case ID_TUBBS:
|
|
delete ((Tubbs *)ptr);
|
|
break;
|
|
case ID_THIEF:
|
|
delete ((Thief *)ptr);
|
|
break;
|
|
case ID_GBPOWERUP:
|
|
delete ((GBPowerup *)ptr);
|
|
break;
|
|
case ID_BARNSWALLOW:
|
|
delete ((BarnSwallow *)ptr);
|
|
break;
|
|
case ID_SPARKY:
|
|
delete ((Sparky *)ptr);
|
|
break;
|
|
case ID_MANTARAY:
|
|
delete ((MantaRay *)ptr);
|
|
break;
|
|
case ID_SPYHUNTER:
|
|
delete ((SpyHunter *)ptr);
|
|
break;
|
|
case ID_SNIPER:
|
|
delete ((Sniper *)ptr);
|
|
break;
|
|
case ID_HUMONCULOUS:
|
|
delete ((Humonculous *)ptr);
|
|
break;
|
|
case ID_SEEKER:
|
|
delete ((Seeker *)ptr);
|
|
break;
|
|
case ID_BETTY:
|
|
delete ((BettyScript *)ptr);
|
|
break;
|
|
case ID_CHAFF:
|
|
delete ((ChaffScript *)ptr);
|
|
break;
|
|
case ID_CHAFFCHUNK:
|
|
delete ((ChaffChunkScript *)ptr);
|
|
break;
|
|
case ID_PROXMINE:
|
|
delete ((ProxMine *)ptr);
|
|
break;
|
|
case ID_BETTYBOMB:
|
|
delete ((BettyBomb *)ptr);
|
|
break;
|
|
case ID_JOSHBELL:
|
|
delete ((JoshBell *)ptr);
|
|
break;
|
|
case ID_SKIFF:
|
|
delete ((Skiff *)ptr);
|
|
break;
|
|
case ID_EXPLODEONCONTACT:
|
|
delete ((ExplodeOnContact *)ptr);
|
|
break;
|
|
case ID_EXPLODETIMEOUT:
|
|
delete ((ExplodeTimeOut *)ptr);
|
|
break;
|
|
case ID_GUNBOY:
|
|
delete ((Gunboy *)ptr);
|
|
break;
|
|
case ID_DEATH_TOWER:
|
|
delete ((DTower *)ptr);
|
|
break;
|
|
case ID_DEATH_COLLECTOR:
|
|
delete ((DCollector *)ptr);
|
|
break;
|
|
case ID_CHEMICAL_BALL:
|
|
delete ((CBall *)ptr);
|
|
break;
|
|
case ID_HELLION:
|
|
delete ((Hellion *)ptr);
|
|
break;
|
|
case ID_SUPERTHIEF:
|
|
delete ((SuperThief *)ptr);
|
|
break;
|
|
case ID_EVADERMODA:
|
|
delete ((EvaderModA *)ptr);
|
|
break;
|
|
case ID_DESTROYONCONTACT:
|
|
delete ((DestroyOnContact *)ptr);
|
|
break;
|
|
case ID_HATEPTMC:
|
|
delete ((HatePTMC *)ptr);
|
|
break;
|
|
case ID_SNIPERNORUN:
|
|
delete ((SniperNoRun *)ptr);
|
|
break;
|
|
case ID_FLAMERAS:
|
|
delete ((FlameRAS *)ptr);
|
|
break;
|
|
case ID_OLDSCRATCH:
|
|
delete ((OldScratch *)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
|
|
//============================================
|
|
|
|
static 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; }
|
|
|
|
//-----------------
|
|
// Josh Bell
|
|
//-----------------
|
|
|
|
void JoshBell::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(joshbell_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (joshbell_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->attach_cage = CreateAndAttach(me, "Bellbars", OBJ_CLUTTER, 0, 0);
|
|
memory->bell_sound = Sound_FindId("Bell");
|
|
memory->last_play_time = Game_GetTime();
|
|
}
|
|
|
|
void JoshBell::DoCollide(int me) {
|
|
int child_handle = Obj_GetAttachChildHandle(me, 0);
|
|
|
|
if (child_handle == OBJECT_HANDLE_NONE /* && memory->last_play_time + 2.0f < Game_GetTime()*/) {
|
|
memory->last_play_time = Game_GetTime();
|
|
Sound_Play3d(me, memory->bell_sound);
|
|
}
|
|
}
|
|
|
|
int16_t JoshBell::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_CREATED:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_COLLIDE:
|
|
DoCollide(data->me_handle);
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (joshbell_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//-----------------
|
|
// Explode On Contact
|
|
//-----------------
|
|
|
|
void ExplodeOnContact::DoCollide(int me, int it) {
|
|
int type;
|
|
Obj_Value(it, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_PLAYER) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = me;
|
|
mstruct.killer_handle = OBJECT_HANDLE_NONE;
|
|
mstruct.damage_type = GD_SCRIPTED;
|
|
mstruct.amount = 1000.0f;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
}
|
|
}
|
|
|
|
int16_t ExplodeOnContact::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_COLLIDE:
|
|
DoCollide(data->me_handle, data->evt_collide.it_handle);
|
|
break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//-----------------
|
|
// Destroy On Contact
|
|
//-----------------
|
|
|
|
void DestroyOnContact::DoCollide(int me, int it) {
|
|
int type;
|
|
Obj_Value(it, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_BUILDING || type == OBJ_ROBOT || type == OBJ_CLUTTER || type == OBJ_PLAYER) {
|
|
Obj_Kill(it, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
}
|
|
}
|
|
|
|
int16_t DestroyOnContact::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_COLLIDE:
|
|
DoCollide(data->me_handle, data->evt_collide.it_handle);
|
|
break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
// --------------
|
|
// Pest
|
|
// --------------
|
|
|
|
void Pest::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(pest_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (pest_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->tail_handle = CreateAndAttach(me, "Pesttail", OBJ_ROBOT, 0, 0);
|
|
memory->last_anim_frame = 0;
|
|
|
|
memory->foot_sounds[0] = Sound_FindId("RbtPestFoot1");
|
|
memory->foot_sounds[1] = Sound_FindId("RbtPestFoot2");
|
|
memory->foot_sounds[2] = Sound_FindId("RbtPestFoot3");
|
|
|
|
AI_SetType(me, AIT_EVADER1);
|
|
}
|
|
|
|
int16_t Pest::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME: {
|
|
float frame;
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_F_ANIM_FRAME, &frame);
|
|
if ((frame > 9 && memory->last_anim_frame <= 9) || (frame > 10 && memory->last_anim_frame <= 10) ||
|
|
(frame > 13 && memory->last_anim_frame <= 13)) {
|
|
int index = rand() % 3;
|
|
|
|
Sound_Play3d(data->me_handle, memory->foot_sounds[index]);
|
|
}
|
|
|
|
memory->last_anim_frame = frame;
|
|
} break;
|
|
case EVT_DESTROY: {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = memory->tail_handle;
|
|
mstruct.killer_handle = OBJECT_HANDLE_NONE;
|
|
mstruct.damage_type = GD_SCRIPTED;
|
|
mstruct.amount = 1000.0f;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
} break;
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (pest_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
// --------------
|
|
// Stinger
|
|
// --------------
|
|
|
|
#define STINGER_MELEE_DIST 50.0f
|
|
|
|
void Stinger::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(stinger_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (stinger_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
MSafe_GetValue(MSAFE_OBJECT_SHIELDS, &m);
|
|
|
|
memory->next_mode_time = Game_GetTime() + ((float)rand() / (float)RAND_MAX) * 3.0f + 3.0f;
|
|
memory->force_melee_shields = m.shields / 3.0f + ((((float)rand()) / (float)RAND_MAX) * m.shields) / 10.0f;
|
|
memory->mode = STINGER_RANGED;
|
|
memory->f_very_hurt = 0;
|
|
|
|
AI_SetType(me, AIT_EVADER1);
|
|
}
|
|
|
|
void Stinger::DoInterval(int me) {
|
|
bool f_force_melee = false;
|
|
int target_handle;
|
|
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
MSafe_GetValue(MSAFE_OBJECT_SHIELDS, &m);
|
|
|
|
memory->f_very_hurt = f_force_melee = (m.shields < memory->force_melee_shields) != 0;
|
|
|
|
if (!f_force_melee) {
|
|
AI_Value(me, VF_GET, AIV_I_TARGET_HANDLE, &target_handle);
|
|
|
|
if (target_handle != OBJECT_HANDLE_NONE) {
|
|
float dist;
|
|
AI_Value(me, VF_GET, AIV_F_DIST_TO_TARGET, &dist);
|
|
|
|
if (dist < STINGER_MELEE_DIST) {
|
|
f_force_melee = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((!f_force_melee) && (memory->next_mode_time < Game_GetTime())) {
|
|
memory->next_mode_time = Game_GetTime() + 7.0f + ((float)rand() / (float)RAND_MAX) * 3.0f;
|
|
|
|
if (rand() % 150 > 10) {
|
|
if (memory->mode == STINGER_MELEE) {
|
|
AI_SetType(me, AIT_EVADER1);
|
|
memory->mode = STINGER_RANGED;
|
|
}
|
|
} else {
|
|
if (memory->mode == STINGER_RANGED) {
|
|
AI_SetType(me, AIT_MELEE1);
|
|
memory->mode = STINGER_MELEE;
|
|
}
|
|
}
|
|
} else if ((f_force_melee) && (memory->mode != STINGER_MELEE)) {
|
|
// PlaySoundObj3d("RbtStingeattack1", me)
|
|
AI_SetType(me, AIT_MELEE1);
|
|
memory->mode = STINGER_MELEE;
|
|
}
|
|
}
|
|
|
|
int16_t Stinger::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoInterval(data->me_handle);
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (stinger_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
// --------------
|
|
// Humonculous
|
|
// --------------
|
|
|
|
// Humonculous modes are in prescidence order (lowest to highest)
|
|
#define HM_IDLE 0x0001
|
|
#define HM_INTRO_CUTSCENE 0x0002
|
|
#define HM_RANGED 0x0004
|
|
#define HM_MELEE 0x0008
|
|
#define HM_WALL_HIT 0x0010
|
|
#define HM_MID_DAMAGE_TAUNT 0x0020
|
|
#define HM_ABOUT_TO_FAKE_DEATH 0x0040
|
|
#define HM_FAKE_DEATH 0x0080
|
|
#define HM_DEATH_CHARGE 0x0100
|
|
#define HM_ABOUT_TO_DIE 0x0200
|
|
#define HM_DEAD 0x0400
|
|
|
|
#define H_WALL_HIT_INTERVAL 15.0f
|
|
|
|
#define H_DOOR_WAIT_TIME 8.0f
|
|
|
|
// Valid next modes from each mode (Humonculous Mode Valid Next)
|
|
const int16_t hm_valid_next_modes[11] = {
|
|
(HM_INTRO_CUTSCENE),
|
|
(HM_RANGED | HM_MELEE | HM_WALL_HIT | HM_MID_DAMAGE_TAUNT | HM_ABOUT_TO_FAKE_DEATH),
|
|
(HM_RANGED | HM_MELEE | HM_WALL_HIT | HM_MID_DAMAGE_TAUNT | HM_ABOUT_TO_FAKE_DEATH),
|
|
(HM_RANGED | HM_MELEE | HM_WALL_HIT | HM_MID_DAMAGE_TAUNT | HM_ABOUT_TO_FAKE_DEATH),
|
|
(HM_RANGED | HM_MELEE | HM_MID_DAMAGE_TAUNT | HM_ABOUT_TO_FAKE_DEATH),
|
|
(HM_RANGED | HM_MELEE | HM_WALL_HIT | HM_ABOUT_TO_FAKE_DEATH),
|
|
(HM_FAKE_DEATH),
|
|
(HM_DEATH_CHARGE),
|
|
(HM_ABOUT_TO_DIE),
|
|
(HM_DEAD),
|
|
(0),
|
|
};
|
|
|
|
#define HF_PLAYER_DEAD 0x01
|
|
#define HF_VERY_HURT 0x02
|
|
#define HF_DID_FINAL_MELEE 0x04
|
|
#define HF_LANDED 0x08
|
|
#define HF_ORIENTED 0x10
|
|
#define HF_HIT_WALL 0x20
|
|
#define HF_DID_MID_DAMAGE 0x40
|
|
#define HF_HIT_WALL_SHAKED 0x80
|
|
#define HF_HIT_WALL_ASSIGNED 0x100
|
|
#define HF_MID_DAMAGE_ASSIGNED 0x200
|
|
#define HF_AT_FDPOS 0x400
|
|
#define HF_AT_RDPOS 0x800
|
|
|
|
#define H_GUID_LANDED 0x1010002B
|
|
#define H_GUID_AT_FDPOS 0x101000BE
|
|
#define H_GUID_AT_RDPOS 0x10100022
|
|
|
|
// chrishack -- NOTE: Give the player an extra powerup if he kills the boss before he can do his
|
|
// final melee attack
|
|
|
|
#define H_MELEE_DIST 50.0f
|
|
|
|
void Humonculous::DetermineDeathPos(int me, vector *dpos, int *droom) {
|
|
float best_dist = 100000000.0f;
|
|
int best_dp;
|
|
vector dp[6];
|
|
int dr[6];
|
|
int i;
|
|
|
|
vector mpos;
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &mpos);
|
|
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath01"), VF_GET, OBJV_V_POS, &dp[0]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath01"), VF_GET, OBJV_I_ROOMNUM, &dr[0]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath02"), VF_GET, OBJV_V_POS, &dp[1]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath02"), VF_GET, OBJV_I_ROOMNUM, &dr[1]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath03"), VF_GET, OBJV_V_POS, &dp[2]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath03"), VF_GET, OBJV_I_ROOMNUM, &dr[2]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath04"), VF_GET, OBJV_V_POS, &dp[3]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath04"), VF_GET, OBJV_I_ROOMNUM, &dr[3]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath05"), VF_GET, OBJV_V_POS, &dp[4]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath05"), VF_GET, OBJV_I_ROOMNUM, &dr[4]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath06"), VF_GET, OBJV_V_POS, &dp[5]);
|
|
Obj_Value(Scrpt_FindObjectName("SafeDeath06"), VF_GET, OBJV_I_ROOMNUM, &dr[5]);
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
dp[i].y = -440.0f;
|
|
|
|
float cur_dist = vm_VectorDistance(&mpos, &dp[i]);
|
|
|
|
if (cur_dist < best_dist) {
|
|
*dpos = dp[i];
|
|
*droom = dr[i];
|
|
best_dist = cur_dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Humonculous::DoNotify(int me, tOSIRISEventInfo *data) {
|
|
switch (memory->mode) {
|
|
case HM_IDLE: {
|
|
if (data->evt_ai_notify.notify_type == AIN_MOVIE_START) {
|
|
SetMode(me, HM_INTRO_CUTSCENE);
|
|
}
|
|
} break;
|
|
|
|
case HM_INTRO_CUTSCENE: {
|
|
if (data->evt_ai_notify.notify_type == AIN_MOVIE_END) {
|
|
SetMode(me, HM_MELEE);
|
|
}
|
|
} break;
|
|
|
|
case HM_WALL_HIT: {
|
|
if (data->evt_ai_notify.notify_type == AIN_GOAL_COMPLETE && data->evt_ai_notify.goal_uid == H_GUID_LANDED) {
|
|
Obj_SetCustomAnim(me, 101.0f, 125.0f, 4.0f, AIAF_NOTIFY, Sound_FindId("RbtHmclIWallHit"), AS_ALERT);
|
|
}
|
|
} break;
|
|
|
|
case HM_ABOUT_TO_FAKE_DEATH:
|
|
case HM_ABOUT_TO_DIE: {
|
|
if (data->evt_ai_notify.notify_type == AIN_SCRIPTED_ORIENT) {
|
|
vector uvec = Zero_vector;
|
|
uvec.y = 1.0f;
|
|
|
|
if (AI_TurnTowardsVectors(me, &memory->land_fvec, &uvec))
|
|
memory->flags |= HF_ORIENTED;
|
|
else
|
|
memory->flags &= ~HF_ORIENTED;
|
|
}
|
|
|
|
if (data->evt_ai_notify.notify_type == AIN_GOAL_COMPLETE && data->evt_ai_notify.goal_uid == H_GUID_LANDED) {
|
|
memory->flags |= HF_LANDED;
|
|
}
|
|
|
|
if (data->evt_ai_notify.notify_type == AIN_GOAL_COMPLETE && data->evt_ai_notify.goal_uid == H_GUID_AT_FDPOS) {
|
|
memory->flags |= HF_AT_FDPOS;
|
|
memory->mode_time = 0.0f;
|
|
|
|
vector vel = {0.0f, 0.0f, 0.0f};
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
|
|
Obj_SetCustomAnim(me, 167.0f, 210.0f, 6.0f, 0, Sound_FindId("RbtHmclFakeDeath"), -1);
|
|
}
|
|
|
|
if (data->evt_ai_notify.notify_type == AIN_GOAL_COMPLETE && data->evt_ai_notify.goal_uid == H_GUID_AT_RDPOS) {
|
|
memory->flags |= HF_AT_RDPOS;
|
|
memory->mode_time = 0.0f;
|
|
|
|
vector vel = {0.0f, 0.0f, 0.0f};
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
|
|
Obj_SetCustomAnim(me, 242.0f, 350.0f, 15.0f, 0, Sound_FindId("RbtHmclDeath"), -1);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Humonculous::SetMode(int me, uint16_t mode) {
|
|
int new_mode_index = -1;
|
|
int old_mode_index = -1;
|
|
|
|
int temp = mode;
|
|
|
|
// Determine the index (bit position from 0 to 31) of the mode (they are bit flags)
|
|
do {
|
|
temp >>= 1;
|
|
new_mode_index++;
|
|
} while (temp != 0);
|
|
|
|
temp = memory->mode;
|
|
do {
|
|
temp >>= 1;
|
|
old_mode_index++;
|
|
} while (temp != 0);
|
|
|
|
// Reactivates firing and melee attacks
|
|
if (memory->mode == HM_WALL_HIT) {
|
|
memory->next_wall_hit_check_time = Game_GetTime() + H_WALL_HIT_INTERVAL;
|
|
int flags = AIF_DISABLE_FIRING | AIF_DISABLE_MELEE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_FIRE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
flags = PF_POINT_COLLIDE_WALLS;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
memory->flags &= ~(HF_HIT_WALL_SHAKED | HF_HIT_WALL);
|
|
} else if (memory->mode == HM_MID_DAMAGE_TAUNT) {
|
|
int flags = AIF_DISABLE_FIRING | AIF_DISABLE_MELEE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_FIRE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
}
|
|
|
|
// Determine if we can goto the next mode
|
|
if (!(hm_valid_next_modes[old_mode_index] & mode))
|
|
return false;
|
|
|
|
// Clear out the robot
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
switch (mode) {
|
|
case HM_IDLE: {
|
|
} break;
|
|
case HM_INTRO_CUTSCENE: {
|
|
} break;
|
|
case HM_RANGED: {
|
|
AI_SetType(me, AIT_EVADER1);
|
|
} break;
|
|
case HM_MELEE: {
|
|
AI_SetType(me, AIT_MELEE1);
|
|
} break;
|
|
case HM_WALL_HIT: {
|
|
memory->flags |= HF_HIT_WALL_ASSIGNED;
|
|
|
|
int flags = AIF_DISABLE_FIRING | AIF_DISABLE_MELEE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_FIRE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
vector start_pos;
|
|
int start_room;
|
|
vector end_pos;
|
|
|
|
ray_info ray;
|
|
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
memory->land_fvec = orient.fvec;
|
|
memory->land_fvec.y = 0.0f;
|
|
vm_VectorNormalize(&memory->land_fvec);
|
|
orient.fvec = memory->land_fvec;
|
|
|
|
vm_VectorNormalize(&orient.rvec);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &start_pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &start_room);
|
|
|
|
// Determine real start pos - room
|
|
end_pos = start_pos;
|
|
end_pos += orient.rvec * 29.0f;
|
|
end_pos += orient.fvec * 33.215f;
|
|
|
|
flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS |
|
|
FQ_IGNORE_NON_LIGHTMAP_OBJECTS;
|
|
int fate = FVI_RayCast(me, &start_pos, &end_pos, start_room, 0.0f, flags, &ray);
|
|
|
|
start_pos = end_pos = ray.hit_point;
|
|
start_room = ray.hit_room;
|
|
|
|
end_pos += 5000.0f * orient.rvec;
|
|
|
|
flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS |
|
|
FQ_IGNORE_NON_LIGHTMAP_OBJECTS;
|
|
fate = FVI_RayCast(me, &start_pos, &end_pos, start_room, 0.0f, flags, &ray);
|
|
|
|
if (ray.hit_wallnorm * orient.rvec > -0.95) {
|
|
memory->mode = HM_WALL_HIT;
|
|
SetMode(me, HM_MELEE);
|
|
return false;
|
|
}
|
|
|
|
memory->land_pos = ray.hit_point - (orient.fvec * 33.215f) - (orient.rvec * 29.0f);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 1, 1.0, H_GUID_LANDED, GF_ORIENT_SCRIPTED | GF_USE_BLINE_IF_SEES_GOAL | GF_NOTIFIES,
|
|
&memory->land_pos, ray.hit_room);
|
|
|
|
float dist = 0.0f;
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
flags = PF_POINT_COLLIDE_WALLS;
|
|
Obj_Value(me, VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
} break;
|
|
case HM_MID_DAMAGE_TAUNT: {
|
|
memory->flags |= HF_MID_DAMAGE_ASSIGNED;
|
|
|
|
int flags = AIF_DISABLE_FIRING | AIF_DISABLE_MELEE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_FIRE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
Obj_SetCustomAnim(me, 360.0f, 420.0f, 8.0f, AIAF_NOTIFY, Sound_FindId("RbtHmclMidTaunt"), AS_ALERT);
|
|
} break;
|
|
case HM_ABOUT_TO_FAKE_DEATH: {
|
|
int flags = AIF_DISABLE_FIRING | AIF_DISABLE_MELEE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_FIRE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
// Guarentee no wall hits or mid damage stuff after the fake death
|
|
memory->flags |= (HF_HIT_WALL_ASSIGNED | HF_MID_DAMAGE_ASSIGNED);
|
|
|
|
vector dpos;
|
|
int droom;
|
|
DetermineDeathPos(me, &dpos, &droom);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 3, 1.0, H_GUID_AT_FDPOS, GF_USE_BLINE_IF_SEES_GOAL | GF_NOTIFIES, &dpos, droom);
|
|
AI_SetGoalCircleDist(me, 3, 0.0f);
|
|
} break;
|
|
case HM_FAKE_DEATH: {
|
|
Obj_SetCustomAnim(me, 210.0f, 242.0f, 5.0f, AIAF_NOTIFY, Sound_FindId("RbtHmclFakeGetUp"), AS_ALERT);
|
|
} break;
|
|
case HM_DEATH_CHARGE: {
|
|
memory->flags &= ~(HF_LANDED | HF_ORIENTED);
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->max_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &memory->max_delta_speed);
|
|
|
|
int flags = PF_POINT_COLLIDE_WALLS;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
flags = PF_LEVELING;
|
|
Obj_Value(me, VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
flags = AIF_DISABLE_MELEE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_FIRE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
flags = OF_AI_DEATH;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_FLAGS, &flags);
|
|
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
m.shields = 20.0f;
|
|
MSafe_CallFunction(MSAFE_OBJECT_SHIELDS, &m);
|
|
|
|
AI_SetType(me, AIT_MELEE1);
|
|
} break;
|
|
case HM_ABOUT_TO_DIE: {
|
|
int flags = AIF_DISABLE_FIRING | AIF_DISABLE_MELEE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_FIRE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
float circle_dist = -1.0f;
|
|
AI_Value(me, VF_SET, AIV_F_CIRCLE_DIST, &circle_dist);
|
|
|
|
vector dpos;
|
|
int droom;
|
|
DetermineDeathPos(me, &dpos, &droom);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 3, 1.0, H_GUID_AT_RDPOS, GF_USE_BLINE_IF_SEES_GOAL | GF_NOTIFIES, &dpos, droom);
|
|
AI_SetGoalCircleDist(me, 3, 0.0f);
|
|
} break;
|
|
case HM_DEAD: {
|
|
} break;
|
|
default:
|
|
mprintf(0, "WHAT ARE YOU DOING!!!!!!!!!!!!!");
|
|
}
|
|
|
|
// Reset the important mode variables
|
|
memory->mode = mode;
|
|
memory->mode_time = 0.0f;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Humonculous::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(humonculous_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (humonculous_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
char diff = Game_GetDiffLevel();
|
|
float shields;
|
|
float speed;
|
|
int flags;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_F_SHIELDS, &shields);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &speed);
|
|
|
|
if (diff == DIFFICULTY_TRAINEE) {
|
|
shields = shields * 0.7f;
|
|
speed = speed * 0.6f;
|
|
} else if (diff == DIFFICULTY_ROOKIE) {
|
|
shields = shields * 0.85f;
|
|
speed = speed * 0.7f;
|
|
}
|
|
|
|
Obj_Value(me, VF_SET, OBJV_F_SHIELDS, &shields);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &speed);
|
|
|
|
memory->last_shields = shields;
|
|
memory->next_mode_time = Game_GetTime() + ((float)rand() / (float)RAND_MAX) * 3.0f + 3.0f;
|
|
memory->force_melee_shields = shields / 3.0f + ((((float)rand()) / (float)RAND_MAX) * shields) / 10.0f;
|
|
memory->force_mid_damage_shields = shields * 0.5f;
|
|
memory->force_wall_hit_shields = shields * 0.80f;
|
|
memory->mode = HM_IDLE;
|
|
memory->flags = 0;
|
|
memory->next_wall_hit_check_time = Game_GetTime() + 25.0f;
|
|
|
|
matrix orient;
|
|
vector pos;
|
|
vector g_pos;
|
|
vector g_norm;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_GetGroundPos(me, 0, &g_pos, &g_norm);
|
|
|
|
vector from_ground = pos - g_pos;
|
|
memory->ground_pnt_offset = fabs(from_ground * orient.uvec);
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->max_speed);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &memory->max_delta_speed);
|
|
|
|
memory->josh[0] = CreateAndAttach(me, "Joshbot", OBJ_ROBOT, 2, 0, true, true);
|
|
flags = PF_NO_COLLIDE_PARENT;
|
|
Obj_Value(memory->josh[0], VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
flags = OF_DESTROYABLE;
|
|
Obj_Value(memory->josh[0], VF_CLEAR_FLAGS, OBJV_I_FLAGS, &flags);
|
|
|
|
// CreateAndAttach(me, "Joshbot", OBJ_ROBOT, 3, 0 ,true, false);
|
|
memory->josh[1] = CreateAndAttach(me, "Joshbot", OBJ_ROBOT, 4, 0, true, true);
|
|
flags = PF_NO_COLLIDE_PARENT;
|
|
Obj_Value(memory->josh[1], VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
flags = OF_DESTROYABLE;
|
|
Obj_Value(memory->josh[1], VF_CLEAR_FLAGS, OBJV_I_FLAGS, &flags);
|
|
|
|
memory->rocks[0] = Scrpt_FindObjectName("BossRock01");
|
|
memory->rocks[1] = Scrpt_FindObjectName("BossRock02");
|
|
memory->rocks[2] = Scrpt_FindObjectName("BossRock03");
|
|
memory->rocks[3] = Scrpt_FindObjectName("BossRock04");
|
|
memory->rocks[4] = Scrpt_FindObjectName("BossRock05");
|
|
|
|
CreateAndAttach(me, "Humonbodyarmor", OBJ_ROBOT, 0, 0, true, false);
|
|
CreateAndAttach(me, "Humonwristarmor", OBJ_ROBOT, 1, 0, true, false);
|
|
|
|
SetMode(me, HM_IDLE);
|
|
}
|
|
|
|
void Humonculous::DoInterval(int me) {
|
|
int flags;
|
|
Obj_Value(me, VF_GET, OBJV_I_FLAGS, &flags);
|
|
|
|
// float frame;
|
|
// Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &frame);
|
|
// mprintf(0, "Anim frame is %f\n", frame);
|
|
float shields;
|
|
Obj_Value(me, VF_GET, OBJV_F_SHIELDS, &shields);
|
|
|
|
if (shields < memory->force_mid_damage_shields && !(memory->flags & HF_MID_DAMAGE_ASSIGNED)) {
|
|
char anim_type;
|
|
AI_Value(me, VF_GET, AIV_C_ANIMATION_TYPE, &anim_type);
|
|
|
|
if (anim_type != AS_RANGED_ATTACK && anim_type != AS_MELEE1 && anim_type != AS_MELEE2)
|
|
SetMode(me, HM_MID_DAMAGE_TAUNT);
|
|
}
|
|
|
|
if (shields < memory->force_wall_hit_shields && !(memory->flags & HF_HIT_WALL_ASSIGNED)) {
|
|
char anim_type;
|
|
AI_Value(me, VF_GET, AIV_C_ANIMATION_TYPE, &anim_type);
|
|
|
|
if (anim_type != AS_RANGED_ATTACK && anim_type != AS_MELEE1 && anim_type != AS_MELEE2) {
|
|
mprintf(0, "HEHRE HERHERHERHERHEHREHRHERHEHREHR\n");
|
|
SetMode(me, HM_WALL_HIT);
|
|
}
|
|
}
|
|
|
|
if ((memory->mode < HM_ABOUT_TO_FAKE_DEATH) && (flags & OF_AI_DEATH)) {
|
|
char anim_type;
|
|
AI_Value(me, VF_GET, AIV_C_ANIMATION_TYPE, &anim_type);
|
|
|
|
if (anim_type != AS_RANGED_ATTACK && anim_type != AS_MELEE1 && anim_type != AS_MELEE2)
|
|
SetMode(me, HM_ABOUT_TO_FAKE_DEATH);
|
|
}
|
|
|
|
if (memory->mode == HM_DEATH_CHARGE && memory->mode_time > 4.0f && (flags & OF_AI_DEATH)) {
|
|
char anim_type;
|
|
AI_Value(me, VF_GET, AIV_C_ANIMATION_TYPE, &anim_type);
|
|
|
|
if (anim_type != AS_RANGED_ATTACK && anim_type != AS_MELEE1 && anim_type != AS_MELEE2)
|
|
SetMode(me, HM_ABOUT_TO_DIE);
|
|
}
|
|
|
|
switch (memory->mode) {
|
|
case HM_INTRO_CUTSCENE: {
|
|
if (memory->mode_time < H_DOOR_WAIT_TIME && memory->mode_time + Game_GetFrameTime() >= H_DOOR_WAIT_TIME) {
|
|
vector velocity = Zero_vector;
|
|
velocity.y = 65.0f;
|
|
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &velocity);
|
|
|
|
int flags = AIF_FORCE_AWARENESS | AIF_DETERMINE_TARGET;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
float awareness = AWARE_FULLY;
|
|
AI_Value(me, VF_SET, AIV_F_AWARENESS, &awareness);
|
|
}
|
|
} break;
|
|
case HM_RANGED:
|
|
case HM_MELEE: {
|
|
bool f_force_melee;
|
|
int target_handle;
|
|
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
MSafe_GetValue(MSAFE_OBJECT_SHIELDS, &m);
|
|
|
|
f_force_melee = (m.shields < memory->force_melee_shields);
|
|
|
|
if (f_force_melee)
|
|
memory->flags |= HF_VERY_HURT;
|
|
else
|
|
memory->flags &= ~HF_VERY_HURT;
|
|
|
|
if (!f_force_melee) {
|
|
AI_Value(me, VF_GET, AIV_I_TARGET_HANDLE, &target_handle);
|
|
|
|
if (target_handle != OBJECT_HANDLE_NONE) {
|
|
float dist;
|
|
AI_Value(me, VF_GET, AIV_F_DIST_TO_TARGET, &dist);
|
|
|
|
if (dist < H_MELEE_DIST) {
|
|
f_force_melee = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((!f_force_melee) && (memory->next_mode_time < Game_GetTime())) {
|
|
memory->next_mode_time = Game_GetTime() + 3.0f + ((float)rand() / (float)RAND_MAX) * 2.0f;
|
|
|
|
if ((rand() % 150) > 10) {
|
|
SetMode(me, HM_RANGED);
|
|
} else {
|
|
SetMode(me, HM_MELEE);
|
|
}
|
|
} else if ((f_force_melee) && (memory->mode != HM_MELEE)) {
|
|
// PlaySoundObj3d("RbtStingeattack1", me)
|
|
SetMode(me, HM_MELEE);
|
|
}
|
|
} break;
|
|
|
|
case HM_MID_DAMAGE_TAUNT: {
|
|
float frame;
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &frame);
|
|
|
|
if (frame >= 360.0f && frame <= 420.0f) {
|
|
memory->flags |= HF_DID_MID_DAMAGE;
|
|
|
|
if (memory->mode_time < 4.53f && memory->mode_time + Game_GetFrameTime() >= 4.53f) {
|
|
int xxx;
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
// Launch joshbots
|
|
for (xxx = 0; xxx < 2; xxx++) {
|
|
int flags = OF_DESTROYABLE;
|
|
vector vel = orient.fvec * 80.0f;
|
|
|
|
Obj_UnattachFromParent(memory->josh[xxx]);
|
|
Obj_Value(memory->josh[xxx], VF_SET, OBJV_V_VELOCITY, &vel);
|
|
|
|
Obj_Value(memory->josh[xxx], VF_SET_FLAGS, OBJV_I_FLAGS, &flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((memory->flags & HF_DID_MID_DAMAGE) && !(frame >= 360.0f && frame <= 420.0f)) {
|
|
SetMode(me, HM_RANGED);
|
|
}
|
|
} break;
|
|
|
|
case HM_WALL_HIT: {
|
|
char anim_type;
|
|
char next_anim_type;
|
|
AI_Value(me, VF_GET, AIV_C_ANIMATION_TYPE, &anim_type);
|
|
AI_Value(me, VF_GET, AIV_C_NEXT_ANIMATION_TYPE, &next_anim_type);
|
|
|
|
float frame;
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &frame);
|
|
|
|
if ((memory->flags & HF_HIT_WALL) == 0) {
|
|
|
|
if (frame >= 101.0f && frame <= 125.0f) {
|
|
memory->flags |= HF_HIT_WALL;
|
|
}
|
|
} else if (frame >= 110.0f && !(memory->flags & HF_HIT_WALL_SHAKED)) {
|
|
memory->flags |= HF_HIT_WALL_SHAKED;
|
|
|
|
// shake players at wall hit
|
|
msafe_struct mstruct;
|
|
mstruct.amount = 65.0f;
|
|
MSafe_CallFunction(MSAFE_OBJECT_VIEWER_SHAKE, &mstruct);
|
|
|
|
int i;
|
|
char mtype = MT_PHYSICS;
|
|
for (i = 0; i < 5; i++) {
|
|
Obj_Value(memory->rocks[i], VF_SET, OBJV_C_MOVEMENT_TYPE, &mtype);
|
|
}
|
|
}
|
|
|
|
if (memory->mode_time > 4.0f && anim_type != AS_CUSTOM && next_anim_type != AS_CUSTOM &&
|
|
(memory->flags & HF_HIT_WALL)) {
|
|
SetMode(me, HM_RANGED);
|
|
} else if (memory->mode_time > 15.0f &&
|
|
anim_type != AS_CUSTOM) // Safety case - if there is a messup, return to ranged (worked fine)
|
|
{
|
|
SetMode(me, HM_RANGED);
|
|
}
|
|
} break;
|
|
|
|
case HM_ABOUT_TO_FAKE_DEATH: {
|
|
if (memory->flags & HF_AT_FDPOS) {
|
|
if (memory->mode_time < 6.0f && memory->mode_time + Game_GetFrameTime() >= 6.0f) {
|
|
vector start_pos;
|
|
int start_room;
|
|
vector end_pos;
|
|
|
|
ray_info ray;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &start_pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &start_room);
|
|
end_pos = start_pos;
|
|
end_pos.y -= 2000.0f;
|
|
|
|
int flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS |
|
|
FQ_IGNORE_NON_LIGHTMAP_OBJECTS;
|
|
int fate = FVI_RayCast(me, &start_pos, &end_pos, start_room, 0.0f, flags, &ray);
|
|
|
|
memory->land_pos = ray.hit_point;
|
|
memory->land_pos.y += memory->ground_pnt_offset;
|
|
|
|
float max_speed = 20.0f;
|
|
float max_delta_speed = 50.0f;
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &max_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &max_delta_speed);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 1, 1.0, H_GUID_LANDED,
|
|
GF_ORIENT_SCRIPTED | GF_USE_BLINE_IF_SEES_GOAL | GF_NOTIFIES, &memory->land_pos, ray.hit_room);
|
|
|
|
float dist = 0.0f;
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
flags = PF_POINT_COLLIDE_WALLS;
|
|
Obj_Value(me, VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
flags = PF_LEVELING;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
memory->land_fvec = orient.fvec;
|
|
memory->land_fvec.y = 0.0f;
|
|
vm_VectorNormalize(&memory->land_fvec);
|
|
}
|
|
|
|
if (memory->mode_time > 15.0f && (memory->flags & HF_LANDED) && (memory->flags & HF_ORIENTED)) {
|
|
SetMode(me, HM_FAKE_DEATH);
|
|
}
|
|
}
|
|
|
|
if (memory->mode_time > 60.0f) {
|
|
SetMode(me, HM_FAKE_DEATH);
|
|
}
|
|
} break;
|
|
|
|
case HM_FAKE_DEATH: {
|
|
if (memory->mode_time > 4.0f) {
|
|
vector velocity = Zero_vector;
|
|
velocity.y = 40.0f;
|
|
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &velocity);
|
|
SetMode(me, HM_DEATH_CHARGE);
|
|
}
|
|
} break;
|
|
|
|
case HM_ABOUT_TO_DIE: {
|
|
if (memory->flags & HF_AT_RDPOS) {
|
|
if (memory->mode_time < 10.0f && memory->mode_time + Game_GetFrameTime() >= 10.0f) {
|
|
Obj_Burning(me, 1000000.0f, 1.0f);
|
|
}
|
|
|
|
if (memory->mode_time < 12.5f && memory->mode_time + Game_GetFrameTime() >= 12.5f) {
|
|
vector start_pos;
|
|
int start_room;
|
|
vector end_pos;
|
|
|
|
ray_info ray;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &start_pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &start_room);
|
|
end_pos = start_pos;
|
|
end_pos.y -= 2000.0f;
|
|
|
|
int flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS |
|
|
FQ_IGNORE_NON_LIGHTMAP_OBJECTS;
|
|
int fate = FVI_RayCast(me, &start_pos, &end_pos, start_room, 0.0f, flags, &ray);
|
|
|
|
memory->land_pos = ray.hit_point;
|
|
memory->land_pos.y += memory->ground_pnt_offset;
|
|
|
|
float max_speed = 20.0f;
|
|
float max_delta_speed = 50.0f;
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &max_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &max_delta_speed);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 1, 1.0, H_GUID_LANDED,
|
|
GF_ORIENT_SCRIPTED | GF_USE_BLINE_IF_SEES_GOAL | GF_NOTIFIES, &memory->land_pos, ray.hit_room);
|
|
|
|
float dist = 0.0f;
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
flags = PF_POINT_COLLIDE_WALLS;
|
|
Obj_Value(me, VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
flags = PF_LEVELING;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
memory->land_fvec = orient.fvec;
|
|
memory->land_fvec.y = 0.0f;
|
|
vm_VectorNormalize(&memory->land_fvec);
|
|
}
|
|
}
|
|
|
|
if (memory->mode_time > 45.0f && !(memory->flags & HF_AT_RDPOS)) {
|
|
memory->flags |= HF_AT_RDPOS;
|
|
memory->mode_time = 0.0f;
|
|
|
|
vector vel = {0.0f, 0.0f, 0.0f};
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
|
|
Obj_SetCustomAnim(me, 242.0f, 350.0f, 15.0f, 0, Sound_FindId("RbtHmclDeath"), -1);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
memory->last_shields = shields;
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
int16_t Humonculous::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoInterval(data->me_handle);
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return (DoNotify(data->me_handle, data) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (humonculous_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
// --------------
|
|
// Dragon
|
|
// --------------
|
|
|
|
void Dragon::DoInit(int me) {
|
|
int i;
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(dragon_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (dragon_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->head_object = CreateAndAttach(me, "Dragonhead", OBJ_ROBOT, 1, 0);
|
|
memory->tail_object = CreateAndAttach(me, "Dragontail", OBJ_ROBOT, 0, 6);
|
|
memory->green_turret = CreateAndAttach(me, "Dragonturret", OBJ_ROBOT, 2, 0);
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
memory->t[i] = CreateAndAttach(memory->tail_object, "Dragontailturret", OBJ_ROBOT, i, 1);
|
|
memory->tentacle[i] = CreateAndAttach(memory->t[i], "Dragontentacle", OBJ_ROBOT, 0, 0);
|
|
}
|
|
|
|
memory->mode = DRAGON_ARMOR;
|
|
|
|
AI_SetType(me, AIT_EVADER1);
|
|
}
|
|
|
|
int16_t Dragon::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (dragon_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
case EVT_DESTROY: {
|
|
int i;
|
|
|
|
Obj_Kill(memory->head_object, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
Obj_Kill(memory->tail_object, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
Obj_Kill(memory->green_turret, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
for (i = 0; i < 6; i++) {
|
|
Obj_Kill(memory->t[i], OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
Obj_Kill(memory->tentacle[i], OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
}
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//-----------
|
|
// Tracker
|
|
//-----------
|
|
|
|
void Tracker::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(tracker_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (tracker_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->turret_object = CreateAndAttach(me, "Trackerturret", OBJ_ROBOT, 1, 0);
|
|
memory->hatch_object = CreateAndAttach(me, "Trackerhatch", OBJ_ROBOT, 0, 0);
|
|
|
|
AI_SetType(me, AIT_EVADER1);
|
|
}
|
|
|
|
int16_t Tracker::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (tracker_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
case EVT_DESTROY: {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = memory->hatch_object;
|
|
mstruct.killer_handle = OBJECT_HANDLE_NONE;
|
|
mstruct.damage_type = GD_SCRIPTED;
|
|
mstruct.amount = 1000.0f;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
|
|
mstruct.objhandle = memory->turret_object;
|
|
mstruct.killer_handle = OBJECT_HANDLE_NONE;
|
|
mstruct.damage_type = GD_SCRIPTED;
|
|
mstruct.amount = 1000.0f;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
// --------------
|
|
// Lance
|
|
// --------------
|
|
|
|
void Lance::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(lance_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (lance_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->mode = LANCE_MOVING;
|
|
}
|
|
|
|
void Lance::DoFrame(int me) {
|
|
float frame;
|
|
|
|
Obj_WBValue(me, 0, VF_GET, WBV_F_ANIM_FRAME, &frame);
|
|
|
|
if (frame > 2.0f && frame < 6.0f) {
|
|
if (memory->mode == LANCE_MOVING) {
|
|
int flags = AIF_DISABLE_FIRING;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
memory->mode = LANCE_PAUSED;
|
|
}
|
|
} else if (memory->mode == LANCE_PAUSED) {
|
|
int flags = AIF_DISABLE_FIRING;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
memory->mode = LANCE_MOVING;
|
|
}
|
|
}
|
|
|
|
int16_t Lance::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (lance_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
// --------------
|
|
// Flak
|
|
// --------------
|
|
|
|
void Flak::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(flak_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (flak_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->canopy_handle = CreateAndAttach(me, "Flakcanopy", OBJ_ROBOT, 0, 0);
|
|
memory->f_dead = false;
|
|
|
|
AI_SetType(me, AIT_EVADER1);
|
|
}
|
|
|
|
int16_t Flak::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_INTERVAL: {
|
|
if (!memory->f_dead) {
|
|
int type;
|
|
Obj_Value(memory->canopy_handle, VF_GET, OBJV_I_TYPE, &type);
|
|
if (type != OBJ_ROBOT) {
|
|
char ctype = CT_NONE;
|
|
Obj_Value(data->me_handle, VF_SET, OBJV_C_CONTROL_TYPE, &ctype);
|
|
|
|
vector vel = {0.0f, 0.0f, 0.0f};
|
|
Obj_Value(data->me_handle, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.random = 21;
|
|
mstruct.is_real = 0;
|
|
mstruct.objhandle = data->me_handle;
|
|
mstruct.gunpoint = -2;
|
|
mstruct.effect_type = MED_SMOKE_INDEX;
|
|
mstruct.phys_info = 0;
|
|
mstruct.drag = .001f;
|
|
mstruct.mass = .001f;
|
|
mstruct.interval = .1f;
|
|
mstruct.longevity = 5.0f;
|
|
mstruct.lifetime = 1.0;
|
|
mstruct.size = 6.0f;
|
|
mstruct.speed = 25.0;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_START_SPEW, &mstruct);
|
|
|
|
memory->f_dead = true;
|
|
memory->death_time = Game_GetTime();
|
|
}
|
|
} else {
|
|
if (Game_GetTime() > memory->death_time + 5.0f) {
|
|
Obj_Kill(data->me_handle, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
}
|
|
}
|
|
} break;
|
|
case EVT_DESTROY:
|
|
Obj_Kill(memory->canopy_handle, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (flak_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Super Trooper
|
|
//---------------
|
|
|
|
void SuperTrooper::DoInit(int me) {
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
MSafe_GetValue(MSAFE_OBJECT_ID, &m);
|
|
int st_id = Obj_FindID("supertrooper");
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(supertrooper_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (supertrooper_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
if (m.id == st_id) {
|
|
memory->body_handle = CreateAndAttach(me, "supertorso", OBJ_ROBOT, 0, 0);
|
|
} else {
|
|
memory->body_handle = CreateAndAttach(me, "red supertorso", OBJ_ROBOT, 0, 0);
|
|
}
|
|
|
|
AI_SetType(me, AIT_EVADER1);
|
|
}
|
|
|
|
int16_t SuperTrooper::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_INTERVAL: {
|
|
int body_handle = Obj_GetAttachChildHandle(data->me_handle, 0);
|
|
if (body_handle != memory->body_handle) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = data->me_handle;
|
|
mstruct.killer_handle = OBJECT_HANDLE_NONE;
|
|
mstruct.damage_type = GD_SCRIPTED;
|
|
mstruct.amount = 1000.0f;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
}
|
|
} break;
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_DESTROY: {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = memory->body_handle;
|
|
mstruct.killer_handle = OBJECT_HANDLE_NONE;
|
|
mstruct.damage_type = GD_SCRIPTED;
|
|
mstruct.amount = 1000.0f;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
} break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (supertrooper_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Juggernaut
|
|
//---------------
|
|
|
|
void Jugg::SetMode(int me, char mode) {
|
|
switch (mode) {
|
|
case JUGG_NORMAL:
|
|
break;
|
|
case JUGG_DYING: {
|
|
char type = MT_NONE;
|
|
char aimtype = MC_STANDING;
|
|
Obj_Value(me, VF_SET, OBJV_C_MOVEMENT_TYPE, &type);
|
|
AI_Value(me, VF_SET, AIV_C_MOVEMENT_TYPE, &aimtype);
|
|
|
|
Obj_SetCustomAnim(me, 8.0f, 20.0f, 4.0f, 0, -1, -1);
|
|
} break;
|
|
}
|
|
|
|
memory->mode = mode;
|
|
memory->mode_time = 0.0f;
|
|
}
|
|
|
|
void Jugg::DoInit(int me) {
|
|
int head_object;
|
|
int belly_object;
|
|
int turret_object;
|
|
int flame_object;
|
|
int i;
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(jugg_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (jugg_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->head_object = CreateAndAttach(me, "Jugghead", OBJ_ROBOT, 0, 0);
|
|
|
|
// Left
|
|
memory->head_turret[0] = CreateAndAttach(memory->head_object, "Juggheadturretright", OBJ_ROBOT, 1, 0);
|
|
// Right
|
|
memory->head_turret[1] = CreateAndAttach(memory->head_object, "Juggheadturretleft", OBJ_ROBOT, 2, 0);
|
|
|
|
memory->belly_object = CreateAndAttach(me, "Juggbelly", OBJ_ROBOT, 1, 0);
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
memory->turret_object[i] = CreateAndAttach(me, "Jugg main turret", OBJ_ROBOT, i + 2, 0);
|
|
}
|
|
|
|
memory->flame_turret[0] = CreateAndAttach(memory->belly_object, "Jugg flame turret", OBJ_ROBOT, 1, 0);
|
|
memory->flame_turret[1] = CreateAndAttach(memory->belly_object, "Jugg flame turret", OBJ_ROBOT, 2, 0);
|
|
memory->flame_turret[2] = CreateAndAttach(me, "Jugg flame turret", OBJ_ROBOT, 8, 0);
|
|
|
|
memory->flags = 0;
|
|
|
|
SetMode(me, JUGG_NORMAL);
|
|
|
|
memory->foot_sound = Sound_FindId("JugFootHit");
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &memory->last_frame);
|
|
}
|
|
|
|
void Jugg::DoFrame(int me) {
|
|
float current_anim_frame;
|
|
matrix orient;
|
|
int flags;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, ¤t_anim_frame);
|
|
Obj_Value(me, VF_GET, OBJV_I_FLAGS, &flags);
|
|
|
|
if (!(memory->flags & JUGG_F_HEAD_DEAD)) {
|
|
int h_flags;
|
|
Obj_Value(memory->head_object, VF_GET, OBJV_I_FLAGS, &h_flags);
|
|
|
|
if (h_flags & OF_AI_DEATH) {
|
|
memory->flags |= JUGG_F_HEAD_DEAD;
|
|
AI_PowerSwitch(memory->head_object, 0);
|
|
Obj_Burning(memory->head_object, 10.0f, 3.0f);
|
|
}
|
|
}
|
|
|
|
if (!(memory->flags & JUGG_F_BODY_DEAD)) {
|
|
if (flags & OF_AI_DEATH) {
|
|
memory->flags |= JUGG_F_BODY_DEAD;
|
|
Obj_Burning(me, 10.0f, 3.0f);
|
|
}
|
|
}
|
|
|
|
if (memory->mode == JUGG_NORMAL && (memory->flags & JUGG_F_BODY_DEAD) && (memory->flags & JUGG_F_HEAD_DEAD)) {
|
|
SetMode(me, JUGG_DYING);
|
|
}
|
|
|
|
if (memory->mode == JUGG_DYING && current_anim_frame == 20.0f) {
|
|
AI_PowerSwitch(me, 0);
|
|
}
|
|
|
|
if (current_anim_frame > 1.0f && memory->last_frame <= 1.0f) {
|
|
Sound_Play3d(me, memory->foot_sound);
|
|
}
|
|
|
|
if (current_anim_frame > 3.0f && memory->last_frame <= 3.0f) {
|
|
Sound_Play3d(me, memory->foot_sound);
|
|
}
|
|
|
|
if (current_anim_frame > 4.0f && memory->last_frame <= 4.0f) {
|
|
Sound_Play3d(me, memory->foot_sound);
|
|
}
|
|
|
|
if (current_anim_frame > 7.0f && memory->last_frame <= 7.0f) {
|
|
Sound_Play3d(me, memory->foot_sound);
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
memory->last_frame = current_anim_frame;
|
|
}
|
|
|
|
int16_t Jugg::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_INTERVAL: {
|
|
if (!(memory->flags & JUGG_F_BELLY_DEAD)) {
|
|
int type;
|
|
int i;
|
|
|
|
Obj_Value(memory->belly_object, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_NONE) {
|
|
memory->flags |= JUGG_F_BELLY_DEAD;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
Obj_Kill(memory->flame_turret[i], OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART |
|
|
DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (jugg_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Death Tower
|
|
//---------------
|
|
|
|
void DTower::DoInit(int me) {
|
|
int head_object;
|
|
int belly_object;
|
|
int turret_object;
|
|
int flame_object;
|
|
int i;
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(dtower_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (dtower_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->gun_handle = CreateAndAttach(me, "Ltowergun", OBJ_CLUTTER, 0, 0);
|
|
memory->ball_handle = CreateAndAttach(me, "Ltowerball", OBJ_CLUTTER, 1, 0);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &memory->last_frame);
|
|
memory->f_died = false;
|
|
}
|
|
|
|
void DTower::DoFrame(int me) {
|
|
float current_anim_frame;
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, ¤t_anim_frame);
|
|
|
|
if (!memory->f_died) {
|
|
int flags;
|
|
Obj_Value(me, VF_GET, OBJV_I_FLAGS, &flags);
|
|
|
|
if (flags & OF_AI_DEATH) {
|
|
memory->mode_time = 0.0f;
|
|
memory->f_died = true;
|
|
}
|
|
} else if (memory->mode_time < 3.0f && memory->mode_time + Game_GetFrameTime() >= 3.0f) {
|
|
int room;
|
|
vector pos;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_GetGunPos(me, 1, &pos);
|
|
|
|
int weapon_id = Wpn_FindID("TubbsHitBlast");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, NULL, me);
|
|
|
|
Obj_Kill(memory->gun_handle, 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_Burning(me, 10.0f, 5.0f);
|
|
|
|
vector vel = {0.0f, 0.0f, 0.0f};
|
|
vel.y = (float)rand() / (float)RAND_MAX * 30.0f + 90.0f;
|
|
vel.x = (float)rand() / (float)RAND_MAX * 16.0f - 8.0f;
|
|
vel.z = (float)rand() / (float)RAND_MAX * 16.0f - 8.0f;
|
|
|
|
Obj_UnattachFromParent(memory->ball_handle);
|
|
Obj_Value(memory->ball_handle, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
Obj_Burning(memory->ball_handle, 15.0f, 3.0f);
|
|
|
|
Obj_SetCustomAnim(me, 0.0f, 10.0f, 2.0f, 0, -1, -1);
|
|
}
|
|
|
|
if (current_anim_frame == 10.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);
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
memory->last_frame = current_anim_frame;
|
|
}
|
|
|
|
int16_t DTower::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (dtower_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Death Collector
|
|
//---------------
|
|
|
|
void DCollector::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(dcollector_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (dcollector_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->target_handle = CreateAndAttach(me, "Collectortarget", OBJ_CLUTTER, 0, 1);
|
|
memory->rod_handle = CreateAndAttach(memory->target_handle, "Collectorrod", OBJ_CLUTTER, 0, 0);
|
|
memory->ball_handle = CreateAndAttach(memory->rod_handle, "Collectorball", OBJ_CLUTTER, 1, 0);
|
|
|
|
memory->f_dead = false;
|
|
}
|
|
|
|
void DCollector::DoFrame(int me) {
|
|
if (!memory->f_dead) {
|
|
int type;
|
|
Obj_Value(memory->target_handle, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_NONE) {
|
|
memory->f_dead = true;
|
|
Obj_Kill(memory->rod_handle, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
|
|
Obj_Burning(me, 6.0f, 3.0f);
|
|
|
|
vector vel = {0.0f, 0.0f, 0.0f};
|
|
vel.y = (float)rand() / (float)RAND_MAX * 30.0f + 90.0f;
|
|
vel.x = (float)rand() / (float)RAND_MAX * 8.0f - 4.0f;
|
|
vel.z = (float)rand() / (float)RAND_MAX * 8.0f - 4.0f;
|
|
Obj_Value(memory->ball_handle, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
Obj_Burning(memory->ball_handle, 15.0f, 3.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
int16_t DCollector::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (dcollector_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Chemical Ball
|
|
//---------------
|
|
|
|
void CBall::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(cball_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (cball_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->f_picked_up = false;
|
|
memory->mode_time = 0.0f;
|
|
}
|
|
|
|
void CBall::DoFrame(int me) {
|
|
bool f_picked_up;
|
|
|
|
f_picked_up = (Obj_GetAttachParent(me) != OBJECT_HANDLE_NONE);
|
|
|
|
if (!memory->f_picked_up && f_picked_up) {
|
|
memory->f_picked_up = true;
|
|
} else if (memory->f_picked_up && !f_picked_up) {
|
|
memory->mode_time += Game_GetFrameTime();
|
|
|
|
if (memory->mode_time > 12.0f) {
|
|
Obj_Kill(me, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
int16_t CBall::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_CREATED:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_INTERVAL:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (cball_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Sixgun
|
|
//---------------
|
|
|
|
void SixGun::DoInit(int me) {
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(sixgun_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (sixgun_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->mode = 0;
|
|
int flags = DWBF_ENABLED;
|
|
Obj_WBValue(me, 0, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->max_speed);
|
|
AI_Value(me, VF_GET, AIV_F_CIRCLE_DIST, &memory->circle_dist);
|
|
|
|
float temp = 0.0f;
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &temp);
|
|
temp = 20.0f;
|
|
AI_Value(me, VF_SET, AIV_F_CIRCLE_DIST, &temp);
|
|
}
|
|
|
|
bool SixGun::DoNotify(int me, tOSIRISEVTAINOTIFY *notify) {
|
|
if (memory->mode == 0) {
|
|
if ((notify->notify_type == AIN_OBJ_FIRED) || (notify->notify_type == AIN_NEAR_TARGET) ||
|
|
(notify->notify_type == AIN_HIT_BY_WEAPON) || (notify->notify_type == AIN_BUMPED_OBJ)) {
|
|
int flags = DWBF_ENABLED;
|
|
Obj_WBValue(me, 0, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &flags);
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->max_speed);
|
|
AI_Value(me, VF_SET, AIV_F_CIRCLE_DIST, &memory->circle_dist);
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ, 0, 1.0f, -1, GF_OBJ_IS_TARGET | GF_KEEP_AT_COMPLETION | GF_NONFLUSHABLE,
|
|
OBJECT_HANDLE_NONE);
|
|
memory->mode = 1;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int16_t SixGun::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return (DoNotify(data->me_handle, &data->evt_ai_notify) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (sixgun_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Explode Time Out
|
|
//---------------
|
|
|
|
void ExplodeTimeOut::DoInit(int me) {
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(explodetimeout_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (explodetimeout_data *)Scrpt_MemAlloc(&ch);
|
|
memory->life_left = 160.0f;
|
|
}
|
|
|
|
int16_t ExplodeTimeOut::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_INTERVAL: {
|
|
memory->life_left -= Game_GetFrameTime();
|
|
if (memory->life_left <= 0.0f) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = data->me_handle;
|
|
mstruct.killer_handle = OBJECT_HANDLE_NONE;
|
|
mstruct.damage_type = GD_SCRIPTED;
|
|
mstruct.amount = 1000.0f;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
}
|
|
} break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (explodetimeout_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// GuideBot
|
|
//---------------
|
|
|
|
void GuideBot::ReInitAmbient(int me) {
|
|
memory->next_ambient_time = Game_GetTime() + 1.0f;
|
|
|
|
// In general, Turn on friend avoidance
|
|
if (memory->last_command != GBC_RETURN_TO_SHIP) {
|
|
int flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
}
|
|
|
|
int dir_index = rand() % 6;
|
|
if (dir_index == GST_NEG_UVEC)
|
|
dir_index = GST_NEG_RVEC;
|
|
else if (dir_index == GST_UVEC)
|
|
dir_index = GST_RVEC;
|
|
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 0, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_USE_BLINE_IF_SEES_GOAL,
|
|
memory->amb_camera_handle, dir_index);
|
|
AI_SetGoalCircleDist(me, 0, 4.0);
|
|
// AddGetToGoalCommonGoals(me);
|
|
}
|
|
|
|
void GuideBot::DoMessage(const char *str, bool f_high_priority, const char *sound_name, bool f_sound_2d) {
|
|
if ((memory->flags & GBF_NO_CHATTER) && !f_high_priority)
|
|
return;
|
|
|
|
if (memory->last_sound_time + 2.5f < Game_GetTime()) {
|
|
if (sound_name) {
|
|
int s_id = Sound_FindId(sound_name);
|
|
if (f_sound_2d)
|
|
Sound_Play2d(memory->me, s_id);
|
|
else
|
|
Sound_Play3d(memory->me, s_id);
|
|
}
|
|
|
|
memory->last_sound_time = Game_GetTime();
|
|
}
|
|
|
|
char gb_message[256];
|
|
snprintf(gb_message, sizeof(gb_message), "\1\255\255\1%s:\1\1\255\1 %s", memory->name, str);
|
|
Player_AddHudMessage(memory->my_player, gb_message);
|
|
}
|
|
|
|
bool GuideBot::SetSubMode(int me, char sub_mode, int it) {
|
|
switch (sub_mode) {
|
|
case GBSM_GET_POWERUP: {
|
|
if (memory->sub_mode == GBSM_NONE) {
|
|
float dist = -10000.0f;
|
|
// chrishack -- need a message!
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 2, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, it);
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
if (memory->sub_mode != GBSM_GET_POWERUP) {
|
|
DoMessage(TXT_GB_I_FOUND_A_GB_POWERUP, true, "GBotExcited1");
|
|
}
|
|
}
|
|
} break;
|
|
case GBSM_NONE: {
|
|
} break;
|
|
}
|
|
|
|
memory->sub_mode = sub_mode;
|
|
// mprintf(0, "Sub Mode is %d\n", memory->sub_mode);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GuideBot::SetMode(int me, char mode) {
|
|
AI_SetType(me, AIT_AIS); // Gets rid of all goal BS
|
|
memory->mode_time = 0.0f;
|
|
|
|
memory->time_till_next_flare = 3.0f + ((float)rand() / (float)RAND_MAX);
|
|
memory->time_till_next_pvis_check = 0.5f + (0.5f * ((float)rand() / (float)RAND_MAX));
|
|
memory->last_retreat_time = memory->return_time = Game_GetTime();
|
|
|
|
if (memory->mode == GBM_RTYPE) {
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &memory->max_acc);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->max_speed);
|
|
}
|
|
|
|
if (memory->camera_obj != OBJECT_HANDLE_NONE) {
|
|
msafe_struct mo;
|
|
mo.objhandle = memory->camera_obj;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
|
|
memory->camera_obj = OBJECT_HANDLE_NONE;
|
|
}
|
|
|
|
float dist = 4.0f;
|
|
int g_index =
|
|
AI_AddGoal(me, AIG_GET_AROUND_OBJ, ACTIVATION_BLEND_LEVEL, 1.0f, -1, GF_OBJ_IS_TARGET, OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, g_index, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
int hack_flags = AIF_GB_MIMIC_PLAYER_FIRING_HACK;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &hack_flags);
|
|
|
|
int wbflags = DWBF_ENABLED;
|
|
Obj_WBValue(me, 3, VF_CLEAR_FLAGS, WBV_I_DYNAMIC_FLAGS, &wbflags);
|
|
|
|
if (memory->mode == GBM_EXTINGUISH && mode != GBM_EXTINGUISH) {
|
|
msafe_struct mstruct;
|
|
|
|
if (memory->flags & GBF_EXTINGUISHING) {
|
|
memory->flags &= ~GBF_EXTINGUISHING;
|
|
mstruct.id = memory->extinguish_spew_id;
|
|
MSafe_CallFunction(MSAFE_OBJECT_STOP_SPEW, &mstruct);
|
|
}
|
|
|
|
mstruct.id = memory->extinguish_spew_id;
|
|
MSafe_CallFunction(MSAFE_OBJECT_STOP_SPEW, &mstruct);
|
|
Obj_SetCustomAnim(me, 33.0f, 41.0f, 1.2f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
}
|
|
|
|
switch (mode) {
|
|
case GBM_IN_SHIP:
|
|
break;
|
|
case GBM_ENTER_SHIP: {
|
|
DoMessage(TXT_GB_ENTERINGSHIP, true);
|
|
}
|
|
case GBM_RESPAWN_ENTER_SHIP: {
|
|
int i;
|
|
|
|
if (mode == GBM_RESPAWN_ENTER_SHIP) {
|
|
float gb_shields = 250;
|
|
int flags = OF_AI_DEATH;
|
|
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_FLAGS, &flags);
|
|
Obj_Value(me, VF_SET, OBJV_F_SHIELDS, &gb_shields);
|
|
|
|
flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
Obj_Burning(me, 0.0f, 0.0f);
|
|
}
|
|
|
|
memory->flags &= ~GBF_WANTING_TO_ENTER_PDEAD;
|
|
|
|
Obj_Burning(me, 0.0f, 0.0f);
|
|
for (i = 0; i < NUM_GB_SOUNDS; i++) {
|
|
if (memory->sounds[i] != -1) {
|
|
Sound_Stop(memory->sounds[i], true);
|
|
memory->sounds[i] = -1;
|
|
}
|
|
}
|
|
|
|
msafe_struct m;
|
|
m.objhandle = memory->my_player;
|
|
m.type = OBJ_ROBOT;
|
|
m.id = ROBOT_GUIDEBOT;
|
|
m.flags = 0;
|
|
|
|
MSafe_CallFunction(MSAFE_INVEN_ADD_TYPE_ID, &m);
|
|
|
|
Obj_Ghost(me, true);
|
|
mode = GBM_IN_SHIP;
|
|
} break;
|
|
case GBM_BIRTH: {
|
|
int room;
|
|
vector pos;
|
|
matrix orient;
|
|
vector vel;
|
|
int flags;
|
|
|
|
flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
memory->f_parented = true;
|
|
memory->f_pickup = false;
|
|
memory->last_anim = 0.0f;
|
|
|
|
Obj_SetCustomAnim(me, 6.0f, 12.0f, 1.0f, AIAF_NOTIFY | AIAF_IMMEDIATE, -1, AS_ALERT);
|
|
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
Obj_Value(me, VF_SET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_SET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_SET, OBJV_M_ORIENT, &orient);
|
|
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_V_VELOCITY, &vel);
|
|
|
|
vel += orient.fvec * 40.0f;
|
|
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
|
|
Obj_Ghost(me, false);
|
|
|
|
msafe_struct m;
|
|
m.objhandle = memory->my_player;
|
|
m.type = OBJ_ROBOT;
|
|
m.id = ROBOT_GUIDEBOT;
|
|
m.flags = 0;
|
|
|
|
MSafe_CallFunction(MSAFE_INVEN_REMOVE, &m);
|
|
|
|
float time = Game_GetTime();
|
|
Obj_Value(me, VF_SET, OBJV_F_CREATION_TIME, &time);
|
|
Obj_Value(me, VF_SET, OBJV_I_PARENT_HANDLE, &memory->my_player);
|
|
|
|
memory->next_ambient_time = Game_GetTime() + 1.2f;
|
|
|
|
// AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 0, 1.0f, -1, GF_KEEP_AT_COMPLETION, memory->my_player,
|
|
// GST_FVEC); AI_SetGoalCircleDist(me, 0, 40.0f);
|
|
|
|
pos += orient.fvec * 200.0f;
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 1, 1.0, -1, GF_USE_BLINE_IF_SEES_GOAL | GF_NOTIFIES, &pos, room);
|
|
AI_SetGoalCircleDist(me, 1, 1.0f);
|
|
|
|
int s_id = Sound_FindId("GBExpulsionA");
|
|
Sound_Play3d(memory->my_player, s_id);
|
|
|
|
memory->last_command = -1;
|
|
} break;
|
|
case GBM_AMBIENT: {
|
|
ReInitAmbient(me);
|
|
} break;
|
|
case GBM_DOING_TASK:
|
|
memory->last_retreat_time = memory->return_time = Game_GetTime();
|
|
break;
|
|
case GBM_KIDNAPPED:
|
|
break;
|
|
case GBM_TALKING:
|
|
break;
|
|
case GBM_THIEF:
|
|
break;
|
|
case GBM_ON_FIRE: {
|
|
int flags;
|
|
|
|
// In general, Turn on friend avoidance
|
|
flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
DoMessage(TXT_GB_HELPMSG, true, "GBotConcern1");
|
|
|
|
int s_index = Sound_FindId("GBotHurt1");
|
|
memory->sounds[GBS_VERY_DAMAGED] = Sound_Play3d(me, s_index);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, memory->my_player);
|
|
AI_SetGoalCircleDist(me, 1, -1.0f);
|
|
|
|
Obj_Burning(me, 1000000.0f, 1.0f);
|
|
memory->flags = GBF_VERY_DAMAGED;
|
|
|
|
memory->f_pickup = true;
|
|
} break;
|
|
case GBM_MANUAL:
|
|
break;
|
|
case GBM_RTYPE: {
|
|
// In general, Turn on friend avoidance
|
|
int flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
float acc = memory->max_acc * 4.0f;
|
|
float speed = memory->max_speed * 1.6f;
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &acc);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &speed);
|
|
|
|
int room;
|
|
vector pos;
|
|
matrix orient;
|
|
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
pos += orient.fvec * 3.0f;
|
|
pos -= orient.uvec * 10.0f;
|
|
|
|
memory->camera_obj = Obj_Create(OBJ_POWERUP, Obj_FindID("Invisiblepowerup"), room, &pos, &orient, me);
|
|
msafe_struct mstruct;
|
|
mstruct.objhandle = memory->camera_obj;
|
|
MSafe_CallFunction(MSAFE_OBJECT_NO_RENDER, &mstruct);
|
|
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 3, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_ORIENT_SCRIPTED,
|
|
memory->camera_obj, GST_NEG_FVEC);
|
|
// AI_SetGoalCircleDist(me, 3, 4.0f);
|
|
|
|
int hack_flags = AIF_GB_MIMIC_PLAYER_FIRING_HACK;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &hack_flags);
|
|
} break;
|
|
case GBM_GO_WINGNUT: {
|
|
AI_SetType(me, AIT_EVADER1);
|
|
int flags = GF_ORIENT_TARGET;
|
|
AI_GoalValue(me, 1, VF_SET_FLAGS, AIGV_I_FLAGS, &flags);
|
|
|
|
memory->next_ambient_time = Game_GetTime() + 1.0f;
|
|
|
|
// In general, Turn on friend avoidance
|
|
flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
int dir_index = rand() % 6;
|
|
float interval = 2.0f;
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 3, 1.0f, -1, GF_KEEP_AT_COMPLETION, memory->my_player, dir_index);
|
|
AI_GoalAddEnabler(me, 3, AIE_GT_LAST_SEE_TARGET_INTERVAL, 1.0f, 0.0f, &interval);
|
|
|
|
Obj_WBValue(me, 3, VF_SET_FLAGS, WBV_I_DYNAMIC_FLAGS, &wbflags);
|
|
} break;
|
|
case GBM_EXTINGUISH:
|
|
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 0, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_ORIENT_GOAL_OBJ,
|
|
memory->extinguish_obj_list[0], 0);
|
|
|
|
AI_SetGoalCircleDist(me, 0, 20.0f);
|
|
memory->extinguish_obj_time = 0.0f;
|
|
memory->next_ambient_time = Game_GetTime() + 2.0f;
|
|
break;
|
|
}
|
|
|
|
memory->mode = mode;
|
|
memory->sub_mode = GBSM_NONE;
|
|
|
|
// mprintf(0, "Mode is %d\n", memory->mode);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GuideBot::InitExtinguish(bool f_player_on_fire) {
|
|
vector pos;
|
|
int room;
|
|
int scan_objs[25];
|
|
int n_scan;
|
|
int i;
|
|
|
|
memory->num_extinguish_objs = 0;
|
|
Obj_Value(memory->me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, 1000.0f, scan_objs, 25, false, false, false, true);
|
|
|
|
if (f_player_on_fire) {
|
|
memory->extinguish_obj_list[memory->num_extinguish_objs++] = memory->my_player;
|
|
}
|
|
|
|
// mprintf(0, "Scanned %d objects\n", n_scan);
|
|
for (i = 0; i < n_scan; i++) {
|
|
if (memory->num_extinguish_objs >= 5)
|
|
break;
|
|
|
|
if (f_player_on_fire && scan_objs[i] == memory->my_player)
|
|
break;
|
|
|
|
if (Obj_IsEffect(scan_objs[i], EF_NAPALMED)) {
|
|
// mprintf(0, "its on fire\n");
|
|
if (scan_objs[i] != memory->me) {
|
|
if (!AI_IsObjEnemy(memory->me, scan_objs[i])) {
|
|
// mprintf(0, "its not a enemy\n");
|
|
memory->extinguish_obj_list[memory->num_extinguish_objs++] = scan_objs[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (memory->num_extinguish_objs) {
|
|
SetMode(memory->me, GBM_EXTINGUISH);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int GuideBot::MakeCommandList(int *gbc_list) {
|
|
int count = 0;
|
|
int i;
|
|
|
|
if (memory->mode == GBM_IN_SHIP) {
|
|
for (i = 0; i <= GBC_RENAME; i++) {
|
|
if (!(memory->flags & GBF_VERY_DAMAGED) && i == GBC_REPAIR_GUIDEBOT)
|
|
continue;
|
|
|
|
if ((memory->flags & GBF_VERY_DAMAGED) && i == GBC_EXIT_SHIP)
|
|
continue;
|
|
|
|
gbc_list[count++] = i;
|
|
}
|
|
} else if (memory->mode == GBM_AMBIENT) {
|
|
int roomnum;
|
|
Obj_Value(memory->me, VF_GET, OBJV_I_ROOMNUM, &roomnum);
|
|
|
|
LGoal_Value(VF_GET, LGV_I_NUM_ACTIVE_PRIMARIES, &memory->num_pg);
|
|
LGoal_Value(VF_GET, LGV_I_NUM_ACTIVE_SECONDARIES, &memory->num_sg);
|
|
|
|
for (i = GBC_FIND_ACTIVE_GOAL_0; i < NUM_GB_COMMANDS; i++) {
|
|
if (i >= GBC_FIND_ACTIVE_GOAL_0 && i <= GBC_FIND_ACTIVE_GOAL_7) {
|
|
if (!(i - GBC_FIND_ACTIVE_GOAL_0 < memory->num_pg))
|
|
continue;
|
|
} else if (i >= GBC_FIND_ACTIVE_SEC_GOAL_0 && i <= GBC_FIND_ACTIVE_SEC_GOAL_7) {
|
|
if (!(i - GBC_FIND_ACTIVE_SEC_GOAL_0 < memory->num_sg))
|
|
continue;
|
|
} else if (i >= GBC_FIND_MARKER_0 && i <= GBC_FIND_MARKER_7) {
|
|
if (Obj_MakeListOfType(memory->me, OBJ_MARKER, i - GBC_FIND_MARKER_0, false, OBJECT_HANDLE_NONE, 1,
|
|
&memory->g_powerup) == 0)
|
|
continue;
|
|
} else if (i == GBC_FIND_SPEW) {
|
|
int handle;
|
|
if (Obj_MakeListOfType(memory->me, OBJ_POWERUP, -1, false, memory->my_player, 1, &handle) == 0)
|
|
continue;
|
|
} else if (i == GBC_FIND_POWERUP) {
|
|
int handle;
|
|
if (Obj_MakeListOfType(memory->me, OBJ_POWERUP, -1, false, OBJECT_HANDLE_NONE, 1, &handle) == 0)
|
|
continue;
|
|
} else if (i == GBC_FIND_ROBOT) {
|
|
int handle;
|
|
if (Obj_MakeListOfType(memory->me, OBJ_ROBOT, -1, false, OBJECT_HANDLE_NONE, 1, &handle) == 0)
|
|
continue;
|
|
} else if (i == GBC_FIND_THIEF) {
|
|
int id = Obj_FindID("Thief");
|
|
int handle;
|
|
|
|
if (id < 0 || Obj_MakeListOfType(memory->me, OBJ_ROBOT, id, false, OBJECT_HANDLE_NONE, 1, &handle) == 0)
|
|
continue;
|
|
} else if (i == GBC_FIND_CLOSEST_MARKER) {
|
|
int handle;
|
|
if (Obj_MakeListOfType(memory->me, OBJ_MARKER, -1, false, memory->my_player, 1, &handle) == 0)
|
|
continue;
|
|
} else if (i >= GB_POW_STRING_OFFSET && i < GB_POW_STRING_OFFSET + NUM_GB_USABLE_POWERUPS) {
|
|
if (memory->powerups[i - GB_POW_STRING_OFFSET] <= 0)
|
|
continue;
|
|
} else if (i == GBC_FIND_OUTSIDE /*&& (0x80000000 & roomnum)*/)
|
|
continue;
|
|
else if (i == GBC_FIND_MINE /*&& !(0x80000000 & roomnum)*/)
|
|
continue;
|
|
else if (i == GBC_NO_CHATTER && (memory->flags & GBF_NO_CHATTER))
|
|
continue;
|
|
else if (i == GBC_ALLOW_CHATTER && !(memory->flags & GBF_NO_CHATTER))
|
|
continue;
|
|
|
|
gbc_list[count++] = i;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void GuideBot::AddGetToGoalCommonGoals(int me) {
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 2, 1.0f, -1, GF_ORIENT_GOAL_OBJ, memory->my_player, GST_NEG_FVEC);
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 3, 1.0f, -1, GF_ORIENT_VELOCITY | GF_NOTIFIES, memory->my_player);
|
|
AI_SetGoalCircleDist(me, 3, 20.0f);
|
|
}
|
|
|
|
bool GuideBot::DoExternalCommands(int me, gb_com *command, int it) {
|
|
int g_robot;
|
|
int roomnum;
|
|
vector pos;
|
|
int flags;
|
|
|
|
mprintf(0, "Command %d for GB\n", command->action);
|
|
|
|
// In general, Turn on friend avoidance
|
|
flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
if (command->action == COM_REINIT) {
|
|
return DoInit(me, true);
|
|
} else if (command->action == COM_DO_ACTION) {
|
|
int param1 = command->index;
|
|
// mprintf(0, "P is %d\n", param1);
|
|
|
|
memory->last_command = param1;
|
|
|
|
if ((param1 >= GBC_FIND_ACTIVE_GOAL_0 && param1 <= GBC_FIND_ACTIVE_GOAL_7) ||
|
|
(param1 >= GBC_FIND_ACTIVE_SEC_GOAL_0 && param1 <= GBC_FIND_ACTIVE_SEC_GOAL_7)) {
|
|
int g_id;
|
|
int flags;
|
|
char type;
|
|
int rhandle[32];
|
|
bool rdone[32];
|
|
int handle[32];
|
|
bool done[32];
|
|
int i;
|
|
int rnum_items;
|
|
int num_items;
|
|
|
|
if (param1 >= GBC_FIND_ACTIVE_GOAL_0 && param1 <= GBC_FIND_ACTIVE_GOAL_7)
|
|
LGoal_Value(VF_GET, LGSV_I_ACTIVE_PRIMARY_GOAL, &g_id, param1 - GBC_FIND_ACTIVE_GOAL_0);
|
|
else
|
|
LGoal_Value(VF_GET, LGSV_I_ACTIVE_SECONDARY_GOAL, &g_id, param1 - GBC_FIND_ACTIVE_SEC_GOAL_0);
|
|
|
|
LGoal_Value(VF_GET, LGSV_I_STATUS, &flags, g_id);
|
|
LGoal_Value(VF_GET, LGSSV_C_ITEM_TYPE, &type, g_id, 0);
|
|
|
|
LGoal_Value(VF_GET, LGSV_I_NUM_ITEMS, &rnum_items, g_id);
|
|
|
|
for (i = 0; i < rnum_items; i++) {
|
|
LGoal_Value(VF_GET, LGSSV_I_ITEM_HANDLE, &rhandle[i], g_id, i);
|
|
LGoal_Value(VF_GET, LGSSV_B_ITEM_DONE, &rdone[i], g_id, i);
|
|
}
|
|
|
|
num_items = 0;
|
|
for (i = 0; i < rnum_items; i++) {
|
|
if (!rdone[i]) {
|
|
handle[num_items++] = rhandle[i];
|
|
}
|
|
}
|
|
|
|
if (num_items == 0) {
|
|
num_items = 1;
|
|
handle[0] = rhandle[0];
|
|
}
|
|
|
|
if (flags & LGF_NOT_LOC_BASED) {
|
|
DoMessage(TXT_GB_NOTLOCATION, true, "GBotConcern1");
|
|
} else if (flags & LGF_GB_DOESNT_KNOW_LOC) {
|
|
DoMessage(TXT_GB_NONAV, true, "GBotConcern1");
|
|
} else if (type == LIT_OBJECT) {
|
|
bool f_ok = false;
|
|
|
|
for (i = 0; i < num_items; i++) // chrishack - do distance check (should be trial)
|
|
{
|
|
int type;
|
|
Obj_Value(handle[i], VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type != OBJ_NONE && AI_IsObjReachable(me, handle[i])) {
|
|
DoMessage(TXT_GB_ONMYWAY, false, "GBotAcceptOrder1");
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, handle[i]);
|
|
AI_SetGoalCircleDist(me, 1, 20.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
f_ok = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!f_ok) {
|
|
DoMessage(TXT_GB_NOTREACH, true, "GBotConcern1");
|
|
}
|
|
} else if (type == LIT_INTERNAL_ROOM) {
|
|
if (AI_IsDestReachable(me, handle[0])) {
|
|
vector pnt;
|
|
Room_Value(handle[0], VF_GET, RMSV_V_PORTAL_PATH_PNT, &pnt);
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 1, 1.0, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, &pnt, handle[0]);
|
|
AI_SetGoalCircleDist(me, 1, 20.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
|
|
DoMessage(TXT_GB_ONMYWAY, false, "GBotAcceptOrder1");
|
|
} else {
|
|
DoMessage(TXT_GB_NOTREACH, true, "GBotConcern1");
|
|
}
|
|
} else if (type == LIT_TRIGGER) {
|
|
|
|
int room = Scrpt_GetTriggerRoom(handle[0]);
|
|
int face = Scrpt_GetTriggerFace(handle[0]);
|
|
|
|
if (AI_IsDestReachable(me, room)) {
|
|
vector pnt;
|
|
vector normal;
|
|
Room_Value(room, VF_GET, RMSV_V_FACE_CENTER_PNT, &pnt, face);
|
|
Room_Value(room, VF_GET, RMSV_V_FACE_NORMAL, &normal, face);
|
|
|
|
pnt += (normal * 5.0f);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 1, 1.0, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, &pnt, room);
|
|
AI_SetGoalCircleDist(me, 1, 20.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
|
|
DoMessage(TXT_GB_ONMYWAY, false, "GBotAcceptOrder1");
|
|
} else {
|
|
DoMessage(TXT_GB_NOTREACH, true, "GBotConcern1");
|
|
}
|
|
}
|
|
} else if (param1 == GBC_RETURN_TO_SHIP) {
|
|
int flags;
|
|
|
|
// Turn off friend avoidance
|
|
flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
DoMessage(TXT_GB_ENTERSHIP, false, "GBotAcceptOrder1");
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, memory->my_player);
|
|
AI_SetGoalCircleDist(me, 1, -10.0f);
|
|
memory->f_pickup = true;
|
|
} else if (param1 == GBC_ESCORT_SHIP) {
|
|
DoMessage(TXT_GB_ESCORT, false, "GBotAcceptOrder1");
|
|
SetMode(me, GBM_AMBIENT);
|
|
} else if (param1 == GBC_FIND_ROBOT) {
|
|
|
|
g_robot = AI_FindObjOfType(me, OBJ_ROBOT, -1, false);
|
|
|
|
if (g_robot != OBJECT_HANDLE_NONE) {
|
|
DoMessage(TXT_GB_FINDROBOT, false, "GBotAcceptOrder1");
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, g_robot);
|
|
AI_SetGoalCircleDist(me, 1, 30.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
} else {
|
|
DoMessage(TXT_GB_NOROBOT, true, "GBotConcern1");
|
|
}
|
|
} else if (param1 == GBC_FIND_POWERUP) {
|
|
|
|
memory->g_powerup = AI_FindObjOfType(me, OBJ_POWERUP, -1, false);
|
|
|
|
if (memory->g_powerup != OBJECT_HANDLE_NONE) {
|
|
DoMessage(TXT_GB_FINDPOWERUP, false, "GBotAcceptOrder1");
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, memory->g_powerup);
|
|
AI_SetGoalCircleDist(me, 1, 3.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
} else {
|
|
DoMessage(TXT_GB_NOPOWERUP, true, "GBotConcern1");
|
|
}
|
|
} else if (param1 == GBC_FIND_CLOSEST_MARKER) {
|
|
|
|
memory->g_powerup = AI_FindObjOfType(me, OBJ_MARKER, -1, false, memory->my_player);
|
|
|
|
if (memory->g_powerup != OBJECT_HANDLE_NONE) {
|
|
char message[256];
|
|
char marker_message[256];
|
|
|
|
Obj_Value(memory->g_powerup, VF_GET, OBJV_PC_MARKER_MESSAGE, marker_message);
|
|
|
|
if (strlen(marker_message) > 20) {
|
|
marker_message[17] = '\0';
|
|
|
|
while (strlen(marker_message) > 0 && marker_message[strlen(marker_message) - 1] == ' ') {
|
|
marker_message[strlen(marker_message) - 1] = '\0';
|
|
}
|
|
|
|
strcat(marker_message, "...");
|
|
}
|
|
|
|
snprintf(message, sizeof(message), TXT_GB_GOMARKER, marker_message);
|
|
DoMessage(message, false, "GBotAcceptOrder1");
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, memory->g_powerup);
|
|
AI_SetGoalCircleDist(me, 1, 3.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
} else {
|
|
DoMessage(TXT_GB_NOMARKER, true, "GBotConcern1");
|
|
}
|
|
} else if (param1 >= GBC_FIND_MARKER_0 && param1 <= GBC_FIND_MARKER_7) {
|
|
memory->g_powerup = AI_FindObjOfType(me, OBJ_MARKER, param1 - GBC_FIND_MARKER_0, false, memory->my_player);
|
|
|
|
if (memory->g_powerup != OBJECT_HANDLE_NONE) {
|
|
char message[256];
|
|
char marker_message[256];
|
|
|
|
Obj_Value(memory->g_powerup, VF_GET, OBJV_PC_MARKER_MESSAGE, marker_message);
|
|
|
|
if (/*strlen(marker_message) > 20*/ 0) {
|
|
marker_message[17] = '\0';
|
|
|
|
while (strlen(marker_message) > 0 && marker_message[strlen(marker_message) - 1] == ' ') {
|
|
marker_message[strlen(marker_message) - 1] = '\0';
|
|
}
|
|
|
|
strcat(marker_message, "...");
|
|
}
|
|
|
|
snprintf(message, sizeof(message), TXT_GB_GOMARKER, marker_message);
|
|
DoMessage(message, false, "GBotAcceptOrder1");
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, memory->g_powerup);
|
|
AI_SetGoalCircleDist(me, 1, 3.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
} else {
|
|
DoMessage(TXT_GB_NOMARK, true, "GBotConcern1");
|
|
}
|
|
} else if (param1 == GBC_FIND_THIEF) {
|
|
int id = Obj_FindID("Thief");
|
|
|
|
memory->g_powerup = AI_FindObjOfType(me, OBJ_ROBOT, id, false);
|
|
|
|
if (memory->g_powerup != OBJECT_HANDLE_NONE) {
|
|
if (rand() > RAND_MAX / 2)
|
|
DoMessage(TXT_GB_FINDTHIEF, false, "GBotAcceptOrder1");
|
|
else
|
|
DoMessage(TXT_GB_FOUNDTHIEF, false, "GBotAcceptOrder1");
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, memory->g_powerup);
|
|
AI_SetGoalCircleDist(me, 1, 3.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
} else {
|
|
DoMessage(TXT_GB_NOTHIEF, true, "GBotConcern1");
|
|
}
|
|
} else if (param1 == GBC_FIND_SPEW) {
|
|
memory->g_powerup = AI_FindObjOfType(me, OBJ_POWERUP, -1, false, memory->my_player);
|
|
|
|
if (memory->g_powerup != OBJECT_HANDLE_NONE) {
|
|
DoMessage(TXT_GB_FINDPOWERUPS, false, "GBotAcceptOrder1");
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, memory->g_powerup);
|
|
AI_SetGoalCircleDist(me, 1, 3.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
} else {
|
|
DoMessage(TXT_GB_NOMESSAGE, true, "GBotConcern1");
|
|
}
|
|
} else if (param1 == GBC_NO_CHATTER) {
|
|
memory->flags |= GBF_NO_CHATTER;
|
|
DoMessage(TXT_GB_CHATOFF, true, "GBotConcern1");
|
|
} else if (param1 == GBC_ALLOW_CHATTER) {
|
|
memory->flags &= (~GBF_NO_CHATTER);
|
|
DoMessage(TXT_GB_CHATON, false, "GBotAcceptOrder1");
|
|
} else if (param1 == GBC_FIND_ENERGY_CENTER) {
|
|
roomnum = AI_FindEnergyCenter(me);
|
|
char f_used;
|
|
Room_Value(roomnum, VF_GET, RMV_C_USED, &f_used);
|
|
|
|
if (f_used != 0) {
|
|
Room_Value(roomnum, VF_GET, RMSV_V_PATH_PNT, &pos, 1);
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 1, 1.0, -1, GF_KEEP_AT_COMPLETION | GF_NOTIFIES, &pos, roomnum);
|
|
AI_SetGoalCircleDist(me, 1, 20.0f);
|
|
AddGetToGoalCommonGoals(me);
|
|
|
|
DoMessage(TXT_GB_FINDENERGY, false, "GBotAcceptOrder1");
|
|
} else {
|
|
DoMessage(TXT_GB_NOENERGY, true, "GBotConcern1");
|
|
}
|
|
} else if (param1 == GBC_REPAIR_GUIDEBOT) {
|
|
float shields;
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_F_SHIELDS, &shields);
|
|
|
|
if (shields >= 5 + 5 * Game_GetDiffLevel()) {
|
|
shields -= 5 + 5 * Game_GetDiffLevel();
|
|
|
|
memory->flags &= (~GBF_VERY_DAMAGED);
|
|
Obj_Value(memory->my_player, VF_SET, OBJV_F_SHIELDS, &shields);
|
|
|
|
DoMessage(TXT_GB_REPAIRED, false, "GBotAcceptOrder1", true);
|
|
|
|
float gb_shields = 250;
|
|
int flags = OF_AI_DEATH;
|
|
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_FLAGS, &flags);
|
|
Obj_Value(me, VF_SET, OBJV_F_SHIELDS, &gb_shields);
|
|
|
|
flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
Obj_Burning(me, 0.0f, 0.0f);
|
|
} else {
|
|
DoMessage(TXT_GB_NOTENSH, true, "GBotConcern1", true);
|
|
}
|
|
} else if (param1 == GBC_EXIT_SHIP) {
|
|
tOSIRISEventInfo ei;
|
|
Obj_CallEvent(me, EVT_USE, &ei);
|
|
} else if (param1 >= GB_POW_STRING_OFFSET && param1 < GB_POW_STRING_OFFSET + NUM_GB_USABLE_POWERUPS) {
|
|
char pow_message[50];
|
|
bool f_do = true;
|
|
|
|
if (memory->powerups[param1 - GB_POW_STRING_OFFSET] > 0) {
|
|
switch (param1 - GB_POW_STRING_OFFSET) {
|
|
case GB_POW_FIRE: {
|
|
int room;
|
|
float damage = 0.0f;
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Room_Value(room, VF_GET, RMV_F_DAMAGE, &damage);
|
|
if (damage == 0.0f) {
|
|
f_do = InitExtinguish(false);
|
|
} else {
|
|
int s_id = Sound_FindId("GBotConcern1");
|
|
Sound_Play2d(memory->my_player, s_id);
|
|
|
|
return true;
|
|
}
|
|
} break;
|
|
|
|
case GB_POW_ANTIVIRUS: {
|
|
vector pos;
|
|
int room;
|
|
int n_scan;
|
|
int scan_objs[25];
|
|
int n_converted = 0;
|
|
int i;
|
|
|
|
Obj_Value(memory->me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, 500.0f, scan_objs, 25, false, false, false, true);
|
|
|
|
for (i = 0; i < n_scan; i++) {
|
|
if (n_converted > 2)
|
|
break;
|
|
|
|
if (memory->num_av_robots >= MAX_AV_ROBOTS)
|
|
break;
|
|
|
|
if (scan_objs[i] != memory->me) {
|
|
if (AI_IsObjEnemy(memory->me, scan_objs[i])) {
|
|
ray_info ray;
|
|
int flags;
|
|
int fate;
|
|
vector end_pos;
|
|
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_V_POS, &end_pos);
|
|
|
|
flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS |
|
|
FQ_IGNORE_NON_LIGHTMAP_OBJECTS;
|
|
fate = FVI_RayCast(me, &pos, &end_pos, room, 1.0f, flags, &ray);
|
|
|
|
if (fate == HIT_NONE) {
|
|
msafe_struct mstruct;
|
|
int type;
|
|
|
|
Obj_Value(memory->me, VF_GET, OBJV_I_ROOMNUM, &mstruct.roomnum);
|
|
Obj_Value(memory->me, VF_GET, OBJV_V_POS, &mstruct.pos);
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_V_POS, &mstruct.pos2);
|
|
|
|
mstruct.objhandle = memory->me;
|
|
mstruct.ithandle = scan_objs[i];
|
|
mstruct.lifetime = 3.5f;
|
|
mstruct.size = 3.0f;
|
|
|
|
mstruct.interval = 0.2f;
|
|
mstruct.count = 2;
|
|
mstruct.index = 4;
|
|
mstruct.texnum = Scrpt_FindTextureName("FunkyEffectAntiV");
|
|
mstruct.color = ((128 >> 3) << 10) | ((128 >> 3) << 5) | (128 >> 3);
|
|
|
|
mstruct.state = 0;
|
|
mstruct.flags = 0;
|
|
|
|
MSafe_CallFunction(MSAFE_WEATHER_LIGHTNING_BOLT, &mstruct);
|
|
|
|
Obj_Value(memory->me, VF_GET, OBJV_I_ROOMNUM, &mstruct.roomnum);
|
|
Obj_Value(memory->me, VF_GET, OBJV_V_POS, &mstruct.pos);
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_V_POS, &mstruct.pos2);
|
|
|
|
int flags = AIF_TEAM_MASK;
|
|
AI_Value(scan_objs[i], VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_TEAM_REBEL;
|
|
AI_Value(scan_objs[i], VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
int target_handle = OBJECT_HANDLE_NONE;
|
|
AI_Value(scan_objs[i], VF_SET, AIV_I_TARGET_HANDLE, &target_handle);
|
|
|
|
memory->av_revert_time[memory->num_av_robots] = Game_GetTime() + 30.0f;
|
|
memory->av_robot_list[memory->num_av_robots++] = scan_objs[i];
|
|
|
|
n_converted++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
f_do = (n_converted > 0);
|
|
} break;
|
|
|
|
case GB_POW_WINGNUT: {
|
|
SetMode(me, GBM_GO_WINGNUT);
|
|
f_do = true;
|
|
} break;
|
|
|
|
case GB_POW_RTYPE: {
|
|
SetMode(me, GBM_RTYPE);
|
|
f_do = true;
|
|
} break;
|
|
|
|
case GB_POW_SPEED: {
|
|
float speed;
|
|
float acc;
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &acc);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &speed);
|
|
|
|
if (acc == memory->max_acc && speed == memory->max_speed) {
|
|
speed *= 3.0f;
|
|
acc *= 6.0f;
|
|
|
|
memory->speed_time_left = 30.0f;
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &acc);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &speed);
|
|
|
|
f_do = true;
|
|
} else {
|
|
f_do = false;
|
|
}
|
|
|
|
} break;
|
|
}
|
|
|
|
if (f_do) {
|
|
snprintf(pow_message, sizeof(pow_message), "%s", TXT(gb_pow_text[param1 - GB_POW_STRING_OFFSET]));
|
|
DoMessage(pow_message, false, "GBotAcceptOrder1");
|
|
memory->powerups[param1 - GB_POW_STRING_OFFSET]--;
|
|
} else {
|
|
snprintf(pow_message, sizeof(pow_message), "%s", TXT(gb_pow_not_text[param1 - GB_POW_STRING_OFFSET]));
|
|
DoMessage(pow_message, false, "GBotConcern1");
|
|
}
|
|
} else {
|
|
DoMessage(TXT_GB_DONT_HAVE_POWERUP, false, "GBotConcern1");
|
|
}
|
|
} else if (param1 == GBC_RENAME) {
|
|
char str[80];
|
|
if (strlen((char *)command->ptr) > 0) {
|
|
strncpy(memory->name, (char *)command->ptr, 7);
|
|
memory->name[7] = '\0';
|
|
Player_Value(memory->my_player, VF_SET, PLYV_CS_GUIDEBOTNAME, memory->name);
|
|
|
|
snprintf(str, sizeof(str), TXT_GB_NEWNAME, (char *)command->ptr);
|
|
DoMessage(str, true, "GBotAcceptOrder1");
|
|
} else {
|
|
snprintf(str, sizeof(str), TXT_GB_NOWAY, (char *)command->ptr);
|
|
DoMessage(str, true, "GBotConcern1");
|
|
}
|
|
} else if (param1 == GBC_RENAME_SILENT) {
|
|
if (strlen((char *)command->ptr) > 0) {
|
|
strncpy(memory->name, (char *)command->ptr, 7);
|
|
memory->name[7] = '\0';
|
|
}
|
|
} else {
|
|
char str[80];
|
|
snprintf(str, sizeof(str), TXT_GB_STRIKE, param1);
|
|
DoMessage(str, true, "GBotConern1", true);
|
|
}
|
|
} else if (command->action == COM_GET_MENU) {
|
|
int i;
|
|
gb_menu *menu = (gb_menu *)command->ptr;
|
|
int cur_command = 0;
|
|
|
|
strcpy(menu->title, TXT_GB_MENUTITLE);
|
|
|
|
int gbc_list[NUM_GB_COMMANDS];
|
|
menu->num_commands = MakeCommandList(gbc_list);
|
|
|
|
for (i = 0; i < menu->num_commands; i++) {
|
|
int c_index = gbc_list[i];
|
|
|
|
if (c_index >= GBC_FIND_MARKER_0 && c_index <= GBC_FIND_MARKER_7) {
|
|
memory->g_powerup = AI_FindObjOfType(me, OBJ_MARKER, c_index - GBC_FIND_MARKER_0, false, memory->my_player);
|
|
|
|
char message[256];
|
|
char marker_message[256];
|
|
|
|
Obj_Value(memory->g_powerup, VF_GET, OBJV_PC_MARKER_MESSAGE, marker_message);
|
|
|
|
if (/*strlen(marker_message) > 12*/ 0) {
|
|
marker_message[11] = '\0';
|
|
|
|
while (strlen(marker_message) > 0 && marker_message[strlen(marker_message) - 1] == ' ') {
|
|
marker_message[strlen(marker_message) - 1] = '\0';
|
|
}
|
|
|
|
strcat(marker_message, "...");
|
|
}
|
|
|
|
snprintf(message, sizeof(message), TXT_GB_FINDMARKMENU, marker_message);
|
|
strcpy(menu->command_text[i], message);
|
|
} else if (c_index >= GBC_FIND_ACTIVE_GOAL_0 && c_index <= GBC_FIND_ACTIVE_GOAL_7) {
|
|
int g_id;
|
|
char message[256];
|
|
|
|
LGoal_Value(VF_GET, LGSV_I_ACTIVE_PRIMARY_GOAL, &g_id, c_index - GBC_FIND_ACTIVE_GOAL_0);
|
|
LGoal_Value(VF_GET, LGSV_PC_LOCATION_NAME, &message, g_id);
|
|
|
|
if (/* strlen(message) > 19 */ 0) {
|
|
message[18] = '\0';
|
|
|
|
while (strlen(message) > 0 && message[strlen(message) - 1] == ' ') {
|
|
message[strlen(message) - 1] = '\0';
|
|
}
|
|
|
|
strcat(message, "...");
|
|
}
|
|
snprintf(menu->command_text[i], sizeof(menu->command_text[i]), TXT_GB_GETTO, message);
|
|
} else if (c_index >= GBC_FIND_ACTIVE_SEC_GOAL_0 && c_index <= GBC_FIND_ACTIVE_SEC_GOAL_7) {
|
|
int g_id;
|
|
char message[256];
|
|
|
|
LGoal_Value(VF_GET, LGSV_I_ACTIVE_SECONDARY_GOAL, &g_id, c_index - GBC_FIND_ACTIVE_SEC_GOAL_0);
|
|
LGoal_Value(VF_GET, LGSV_PC_LOCATION_NAME, &message, g_id);
|
|
|
|
if (/*strlen(message) > 19*/ 0) {
|
|
message[18] = '\0';
|
|
|
|
while (strlen(message) > 0 && message[strlen(message) - 1] == ' ') {
|
|
message[strlen(message) - 1] = '\0';
|
|
}
|
|
|
|
strcat(message, "...");
|
|
}
|
|
snprintf(menu->command_text[i], sizeof(menu->command_text[i]), TXT_GB_GETTO, message);
|
|
} else
|
|
strcpy(menu->command_text[i], TXT(gb_command_text[gbc_list[i]]));
|
|
|
|
if (gbc_list[i] == GBC_REPAIR_GUIDEBOT) {
|
|
char message[256];
|
|
snprintf(message, sizeof(message), TXT_GB_SHIELDAMOUNT, 5 + 5 * Game_GetDiffLevel());
|
|
strcat(menu->command_text[i], message);
|
|
}
|
|
|
|
menu->command_id[i] = gbc_list[i];
|
|
menu->command_type[i] = GBCT_EXIT_IMMEDIATELY;
|
|
|
|
if (gbc_list[i] == GBC_RENAME) {
|
|
menu->command_type[i] = GBCT_STRING_DIALOG;
|
|
strcpy(menu->dialog_text[i], TXT_GB_ENTERNAME);
|
|
}
|
|
}
|
|
} else if (command->action == COM_POWERUP_NOTIFY) {
|
|
SetSubMode(me, GBSM_GET_POWERUP, it);
|
|
} else if (command->action == COM_POWERUP_PICKUP) {
|
|
int type = command->index;
|
|
|
|
if (type >= 0 && type <= 5) {
|
|
int s_id = Sound_FindId("Powerup pickup");
|
|
Sound_Play3d(me, s_id);
|
|
|
|
if (type == GB_POW_SPEED) {
|
|
float speed;
|
|
float acc;
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &acc);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &speed);
|
|
|
|
if (acc == memory->max_acc && speed == memory->max_speed) {
|
|
speed *= 3.0f;
|
|
acc *= 6.0f;
|
|
|
|
memory->speed_time_left = 30.0f;
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &acc);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &speed);
|
|
}
|
|
} else {
|
|
memory->powerups[type]++;
|
|
}
|
|
|
|
msafe_struct mo;
|
|
mo.objhandle = it;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
|
|
DoMessage(TXT(gb_pow_pickup_text[type]), true, "GBotExcited1");
|
|
SetSubMode(me, GBSM_NONE);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GuideBot::DoCollide(int me, tOSIRISEVTCOLLIDE *evt_collide) {
|
|
int obj_type;
|
|
int its_parent;
|
|
float rand_val;
|
|
|
|
int it_type;
|
|
Obj_Value(evt_collide->it_handle, VF_GET, OBJV_I_TYPE, &it_type);
|
|
|
|
rand_val = (float)rand() / (float)RAND_MAX;
|
|
|
|
// Add a buddy bot to you inventory :)
|
|
if (memory->f_pickup == true) {
|
|
if (it_type == OBJ_PLAYER) {
|
|
uint16_t id;
|
|
Obj_Value(evt_collide->it_handle, VF_GET, OBJV_US_ID, &id);
|
|
|
|
if (id == memory->mp_slot) {
|
|
SetMode(me, GBM_ENTER_SHIP);
|
|
}
|
|
}
|
|
} else {
|
|
// Run back to the player when you get hit
|
|
if (it_type == OBJ_WEAPON) {
|
|
// 30% of the time - and not within 5 seconds - the buddy will come
|
|
// back to the player for protection
|
|
|
|
Obj_Value(evt_collide->it_handle, VF_GET, OBJV_I_PARENT_HANDLE, &its_parent);
|
|
|
|
if (its_parent == memory->my_player) {
|
|
if (rand_val > 0.8f) {
|
|
DoMessage(TXT_GBHIT1, false, "GBotConcern1");
|
|
} else if (rand_val > 0.6) {
|
|
DoMessage(TXT_GBHIT2, false, "GBotConcern1");
|
|
} else if (rand_val > 0.4) {
|
|
DoMessage(TXT_GBHIT3, false, "GBotConcern1");
|
|
}
|
|
} else if (memory->last_retreat_time + 5.0 < Game_GetTime())
|
|
;
|
|
{
|
|
if (rand_val > 0.7) {
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 2, 1.0f, -1, 0, memory->my_player);
|
|
AI_SetGoalCircleDist(me, 2, 40.0f);
|
|
|
|
memory->last_retreat_time = Game_GetTime();
|
|
|
|
DoMessage(TXT_GBHIT4, true, "GBotConcern1");
|
|
} else {
|
|
DoMessage(TXT_GBHIT5, true, "GBotConcern1");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GuideBot::DoUse(int me) {
|
|
int p_handle;
|
|
int sad_sound;
|
|
int buddy_handle;
|
|
|
|
// NOTE: Use is done on the inventory item and/or the real guidebot
|
|
// don't set and guidebot global variables in here. Do it in the
|
|
// EVT_CREATE in the f_valid block
|
|
|
|
gb_com command;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_PARENT_HANDLE, &p_handle);
|
|
|
|
if (p_handle == OBJECT_HANDLE_NONE) {
|
|
p_handle = memory->my_player;
|
|
} else {
|
|
memory->my_player = p_handle;
|
|
}
|
|
|
|
mprintf(0, "1 Player %d\n", p_handle);
|
|
mprintf(0, "2 Player %d\n", memory->my_player);
|
|
|
|
Player_Value(p_handle, VF_GET, PLYV_I_BUDDY_HANDLE, &buddy_handle);
|
|
|
|
command.action = COM_REINIT;
|
|
tOSIRISEventInfo ei;
|
|
ei.extra_info = (void *)&command;
|
|
ei.evt_ai_notify.notify_type = AIN_USER_DEFINED;
|
|
|
|
return Obj_CallEvent(buddy_handle, EVT_AI_NOTIFY, &ei);
|
|
}
|
|
|
|
bool GuideBot::DoInit(int me, bool f_reinit) {
|
|
vector fvec;
|
|
vector vel;
|
|
bool f_valid;
|
|
matrix orient;
|
|
vector pos;
|
|
int room;
|
|
int i;
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(guidebot_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
if (!f_reinit) {
|
|
memory = (guidebot_data *)Scrpt_MemAlloc(&ch);
|
|
memory->flags = 0;
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &memory->max_acc);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->max_speed);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_PARENT_HANDLE, &memory->my_player);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_US_ID, &memory->mp_slot);
|
|
|
|
Player_Value(memory->my_player, VF_GET, PLYV_CS_GUIDEBOTNAME, memory->name);
|
|
|
|
// Sets up my invisible helper
|
|
int proom;
|
|
vector ppos;
|
|
matrix porient;
|
|
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_I_ROOMNUM, &proom);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_V_POS, &ppos);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_M_ORIENT, &porient);
|
|
|
|
ppos += porient.fvec * 10.0f;
|
|
|
|
memory->amb_camera_handle = Obj_Create(OBJ_POWERUP, Obj_FindID("Invisiblepowerup"), proom, &ppos, &porient, me);
|
|
msafe_struct mstruct;
|
|
mstruct.objhandle = memory->amb_camera_handle;
|
|
MSafe_CallFunction(MSAFE_OBJECT_NO_RENDER, &mstruct);
|
|
|
|
// Three fire extinguishers per level
|
|
memory->powerups[0] = 3;
|
|
memory->powerups[1] = 0;
|
|
memory->powerups[2] = 0;
|
|
memory->powerups[3] = 0;
|
|
memory->powerups[4] = 0;
|
|
|
|
for (i = 0; i < NUM_GB_SOUNDS; i++) {
|
|
memory->sounds[i] = -1;
|
|
}
|
|
|
|
memory->me = me;
|
|
|
|
memory->mode = GBM_IN_SHIP;
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
memory->powerup_ids[0] = Obj_FindID("Buddyextinguisher");
|
|
memory->powerup_ids[1] = Obj_FindID("buddywingnut");
|
|
memory->powerup_ids[2] = Obj_FindID("buddycontrol");
|
|
memory->powerup_ids[3] = Obj_FindID("buddyassist");
|
|
memory->powerup_ids[4] = Obj_FindID("buddyantivirus");
|
|
memory->powerup_ids[5] = Obj_FindID("buddyspeed");
|
|
|
|
memory->num_av_robots = 0;
|
|
|
|
memory->last_sound_time = Game_GetTime();
|
|
memory->last_at_goal_time = Game_GetTime();
|
|
|
|
memory->camera_obj = OBJECT_HANDLE_NONE;
|
|
memory->speed_time_left = 0.0f;
|
|
} else {
|
|
// If too damaged to exit
|
|
if (memory->flags & GBF_VERY_DAMAGED) {
|
|
DoMessage(TXT_GBDAMAGED, true, "GBotConcern1", true);
|
|
return false;
|
|
}
|
|
|
|
SetMode(me, GBM_BIRTH);
|
|
SetSubMode(me, GBSM_NONE);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GuideBot::DoNotify(int me, tOSIRISEventInfo *data) {
|
|
tOSIRISEVTAINOTIFY *notify = &data->evt_ai_notify;
|
|
|
|
if (notify->notify_type == AIN_USER_DEFINED) {
|
|
return DoExternalCommands(me, (gb_com *)data->extra_info, data->evt_ai_notify.it_handle);
|
|
} else if (notify->notify_type == AIN_WHIT_OBJECT) {
|
|
int type;
|
|
Obj_Value(notify->it_handle, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_PLAYER) {
|
|
if (notify->it_handle == memory->my_player) {
|
|
DoMessage(TXT_GB_OOPS, true, "GBotConcern1");
|
|
}
|
|
} else if (type == OBJ_ROBOT) {
|
|
// 35% of the time, the buddy is happy enough to say something when
|
|
// he hits a robot
|
|
if (rand() > static_cast<float>(RAND_MAX) * 0.65f) {
|
|
DoMessage(TXT_GB_SHOOTROBOT, false, "GBotAcceptOrder1");
|
|
}
|
|
}
|
|
} else if (IsGoalFinishedNotify(
|
|
notify->notify_type)) // Goal complete -- chrishack -- print something else on error cases
|
|
{
|
|
mprintf(0, "Goal %d done\n", notify->goal_num);
|
|
|
|
if (notify->goal_num == 1) {
|
|
if (memory->last_at_goal_time + 1.0f > Game_GetTime()) {
|
|
gb_com command;
|
|
|
|
command.action = COM_DO_ACTION;
|
|
command.index = GBC_ESCORT_SHIP;
|
|
DoExternalCommands(me, &command, memory->my_player);
|
|
return true;
|
|
}
|
|
|
|
DoMessage(TXT_GB_GOAL1, false, "GBotAcceptOrder1");
|
|
memory->last_at_goal_time = Game_GetTime();
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 2, 1.0, GB_GUID_RETURNED_TO_PLAYER, GF_NOTIFIES, memory->my_player);
|
|
AI_SetGoalCircleDist(me, 2, 30.0f);
|
|
} else if (notify->goal_num == 2 && notify->goal_uid == GB_GUID_RETURNED_TO_PLAYER) {
|
|
mprintf(0, "HERERWERSEFSDF\n");
|
|
bool f_used;
|
|
AI_GoalValue(me, 1, VF_GET, AIGV_B_USED, &f_used);
|
|
if (f_used) {
|
|
mprintf(0, "FUCKME\n");
|
|
|
|
if ((rand() % 100) > 50)
|
|
DoMessage(TXT_GB_GOAL2, false, "GBotGreetB1");
|
|
else
|
|
DoMessage(TXT_GB_GOAL3, false, "GBotGreetB1");
|
|
}
|
|
|
|
memory->time_till_next_flare = 3.0f + ((float)rand() / (float)RAND_MAX);
|
|
memory->return_time = Game_GetTime();
|
|
}
|
|
} else if (notify->notify_type == AIN_SCRIPTED_ORIENT) {
|
|
matrix orient;
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
AI_TurnTowardsVectors(me, &orient.fvec, &orient.uvec);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GuideBot::DoPowerupCheck(int me) {
|
|
if (memory->next_powerup_check_time <= Game_GetTime() && memory->sub_mode == GBSM_NONE) {
|
|
vector pos;
|
|
int room;
|
|
int scan_objs[80];
|
|
int n_scan;
|
|
int i;
|
|
|
|
memory->next_powerup_check_time = Game_GetTime() + 2.0f + ((float)rand() / (float)RAND_MAX);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, 200.0f, scan_objs, 80, false, true, false, true);
|
|
|
|
for (i = 0; i < n_scan; i++) {
|
|
int type;
|
|
int16_t id;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_I_TYPE, &type);
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_US_ID, &id);
|
|
|
|
if (type == OBJ_POWERUP) {
|
|
bool f_for_me = false;
|
|
int j;
|
|
|
|
for (j = 0; j < 6; j++) {
|
|
if (memory->powerup_ids[j] == id) {
|
|
f_for_me = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (f_for_me) {
|
|
tOSIRISEventInfo data;
|
|
gb_com command;
|
|
|
|
command.action = COM_POWERUP_NOTIFY;
|
|
|
|
DoExternalCommands(me, &command, scan_objs[i]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuideBot::DoFrame(int me) {
|
|
int dir_index;
|
|
int flags;
|
|
float anim;
|
|
float last_mode_time;
|
|
|
|
int proom;
|
|
vector ppos;
|
|
matrix porient;
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_I_ROOMNUM, &proom);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_V_POS, &ppos);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_M_ORIENT, &porient);
|
|
|
|
ppos += porient.fvec * 40.0f;
|
|
ppos -= porient.uvec * 10.0f;
|
|
|
|
Obj_Value(memory->amb_camera_handle, VF_SET, OBJV_I_ROOMNUM, &proom);
|
|
Obj_Value(memory->amb_camera_handle, VF_SET, OBJV_V_POS, &ppos);
|
|
Obj_Value(memory->amb_camera_handle, VF_SET, OBJV_M_ORIENT, &porient);
|
|
|
|
if (memory->mode == GBM_AMBIENT)
|
|
DoPowerupCheck(me);
|
|
|
|
if (memory->flags & GBF_WANTING_TO_ENTER_PDEAD) {
|
|
int flags;
|
|
Player_Value(memory->my_player, VF_GET, PLYV_I_FLAGS, &flags);
|
|
if (!(flags & PLAYER_FLAGS_DEAD)) {
|
|
SetMode(me, GBM_RESPAWN_ENTER_SHIP);
|
|
}
|
|
} else {
|
|
int flags;
|
|
Player_Value(memory->my_player, VF_GET, PLYV_I_FLAGS, &flags);
|
|
if (flags & PLAYER_FLAGS_DEAD) {
|
|
Obj_Burning(me, 0.0f, 0.0f);
|
|
memory->flags |= GBF_WANTING_TO_ENTER_PDEAD;
|
|
}
|
|
}
|
|
|
|
if (memory->speed_time_left > 0.0f) {
|
|
if (memory->speed_time_left > 15.0f && memory->speed_time_left - Game_GetFrameTime() <= 15.0f) {
|
|
DoMessage(TXT_GB_POW_ACC_ALMOST_DONE, true, "GBotExcited1");
|
|
}
|
|
|
|
memory->speed_time_left -= Game_GetFrameTime();
|
|
if (memory->speed_time_left <= 0.0f) {
|
|
memory->speed_time_left = 0.0f;
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->max_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &memory->max_acc);
|
|
|
|
DoMessage(TXT_GB_POW_DEPLETED, true, "GBotPlayerKillBot1");
|
|
}
|
|
}
|
|
|
|
if (memory->mode == GBM_AMBIENT && Obj_IsEffect(memory->my_player, EF_NAPALMED)) {
|
|
int room;
|
|
float damage = 0.0f;
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Room_Value(room, VF_GET, RMV_F_DAMAGE, &damage);
|
|
|
|
if (damage == 0.0f) {
|
|
InitExtinguish(true);
|
|
}
|
|
}
|
|
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &anim);
|
|
if (anim < 0 || anim > 12) {
|
|
memory->flags |= GBF_IN_CUSTOM_ANIM;
|
|
} else {
|
|
memory->flags &= ~GBF_IN_CUSTOM_ANIM;
|
|
}
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_FLAGS, &flags);
|
|
|
|
last_mode_time = memory->mode_time;
|
|
memory->mode_time += Game_GetFrameTime();
|
|
|
|
if (memory->mode == GBM_ON_FIRE) {
|
|
} else if ((flags & OF_AI_DEATH)) {
|
|
SetMode(me, GBM_ON_FIRE);
|
|
} else if (memory->mode == GBM_BIRTH) {
|
|
if (memory->mode_time > 1.2f) {
|
|
SetMode(me, GBM_AMBIENT);
|
|
}
|
|
|
|
return;
|
|
} else if (memory->mode == GBM_EXTINGUISH) {
|
|
if (!(memory->flags & GBF_IN_CUSTOM_ANIM) && last_mode_time < 0.5f && memory->mode_time >= 0.5f) {
|
|
Obj_SetCustomAnim(me, 12.0f, 23.0f, 1.0f, 0);
|
|
}
|
|
|
|
if (Game_GetTime() > memory->next_ambient_time) {
|
|
memory->next_ambient_time = Game_GetTime() + 0.6f;
|
|
|
|
int dir_index = rand() % 6;
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 0, 1.0f, -1, GF_ORIENT_GOAL_OBJ | GF_KEEP_AT_COMPLETION,
|
|
memory->extinguish_obj_list[0], dir_index);
|
|
AI_SetGoalCircleDist(me, 0, 20.0);
|
|
}
|
|
|
|
if (memory->mode_time < 25.0f) {
|
|
if (memory->last_anim <= 18 && anim > 18) {
|
|
Obj_SetCustomAnim(me, 23.0f, 33.0f, 1.5f, AIAF_LOOPING);
|
|
memory->flags |= GBF_WANT_TO_EXTINGUISH;
|
|
}
|
|
|
|
if ((memory->flags & GBF_WANT_TO_EXTINGUISH) && anim >= 23 && anim <= 33) {
|
|
int s_id = Sound_FindId("GBotEuphoria1");
|
|
Sound_Play3d(me, s_id);
|
|
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.random = 21;
|
|
mstruct.is_real = 0;
|
|
mstruct.objhandle = me;
|
|
mstruct.gunpoint = 1;
|
|
mstruct.effect_type = MED_SMOKE_INDEX;
|
|
mstruct.phys_info = 0;
|
|
mstruct.drag = .001f;
|
|
mstruct.mass = .001f;
|
|
mstruct.interval = .025f;
|
|
mstruct.longevity = 100000.0f;
|
|
mstruct.lifetime = 1.0;
|
|
mstruct.size = 4.2f;
|
|
mstruct.speed = 120.0;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_START_SPEW, &mstruct);
|
|
|
|
memory->extinguish_spew_id = mstruct.id;
|
|
|
|
memory->flags &= ~GBF_WANT_TO_EXTINGUISH;
|
|
memory->flags |= GBF_EXTINGUISHING;
|
|
}
|
|
|
|
if (memory->flags | GBF_EXTINGUISHING) {
|
|
int flags;
|
|
vector me_pos;
|
|
vector it_pos;
|
|
matrix orient;
|
|
|
|
AI_GoalValue(me, 0, VF_GET, AIGV_I_STATUS_REG, &flags);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &me_pos);
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
Obj_Value(memory->extinguish_obj_list[0], VF_GET, OBJV_V_POS, &it_pos);
|
|
|
|
vector dir_to_goal = it_pos - me_pos;
|
|
vm_VectorNormalize(&dir_to_goal);
|
|
|
|
if ((flags & AISR_CIRCLE_DIST) && (dir_to_goal * orient.fvec > 0.4717f) && anim >= 23 && anim <= 33)
|
|
memory->extinguish_obj_time += Game_GetFrameTime();
|
|
|
|
if (memory->extinguish_obj_time > 1.5f) {
|
|
// Turns off fire if being sprayed for 2.5 seconds
|
|
Obj_Burning(memory->extinguish_obj_list[0], 0.0f, 0.0f);
|
|
|
|
if (memory->num_extinguish_objs > 1) {
|
|
int i;
|
|
for (i = 0; i < memory->num_extinguish_objs - 1; i++) {
|
|
memory->extinguish_obj_list[i] = memory->extinguish_obj_list[i + 1];
|
|
}
|
|
memory->num_extinguish_objs--;
|
|
|
|
SetMode(me, GBM_EXTINGUISH);
|
|
} else {
|
|
SetMode(me, GBM_AMBIENT);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
SetMode(me, GBM_AMBIENT);
|
|
}
|
|
} else if (memory->mode == GBM_RTYPE) {
|
|
int room;
|
|
vector pos;
|
|
matrix orient;
|
|
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
pos += orient.fvec * 3.0f;
|
|
pos -= orient.uvec * 10.0f;
|
|
|
|
Obj_Value(memory->camera_obj, VF_SET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(memory->camera_obj, VF_SET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->camera_obj, VF_SET, OBJV_M_ORIENT, &orient);
|
|
|
|
if (memory->mode_time < 15.0f && memory->mode_time + Game_GetFrameTime() >= 15.0f) {
|
|
DoMessage(TXT_GB_FIFTEEN_SEC_TO_GO, true, "GBotExcited1");
|
|
}
|
|
|
|
if (memory->mode_time > 30.0) {
|
|
DoMessage(TXT_GB_POW_DEPLETED, true, "GBotPlayerKillBot1");
|
|
SetMode(me, GBM_AMBIENT);
|
|
}
|
|
} else if (memory->mode == GBM_GO_WINGNUT) {
|
|
if (Game_GetTime() > memory->next_ambient_time) {
|
|
memory->next_ambient_time = Game_GetTime() + 1.0f;
|
|
|
|
// In general, Turn on friend avoidance
|
|
int flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
int dir_index = rand() % 6;
|
|
float interval = 2.0f;
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 3, 1.0f, -1, GF_KEEP_AT_COMPLETION, memory->my_player, dir_index);
|
|
AI_GoalAddEnabler(me, 3, AIE_GT_LAST_SEE_TARGET_INTERVAL, 1.0f, 0.0f, &interval);
|
|
|
|
AI_SetGoalCircleDist(me, 3, 27.0);
|
|
}
|
|
|
|
if (memory->mode_time < 15.0f && memory->mode_time + Game_GetFrameTime() >= 15.0f) {
|
|
DoMessage(TXT_GB_FIFTEEN_SEC_TO_GO, true, "GBotExcited1");
|
|
}
|
|
|
|
if (memory->mode_time > 30.0) {
|
|
DoMessage(TXT_GB_POW_DEPLETED, true, "GBotPlayerKillBot1");
|
|
SetMode(me, GBM_AMBIENT);
|
|
}
|
|
} else {
|
|
if (memory->sounds[GBS_AMBIENT] == -1) {
|
|
int s_index = Sound_FindId("GBotEngineB1");
|
|
memory->sounds[GBS_AMBIENT] = Sound_Play3d(me, s_index);
|
|
}
|
|
|
|
if (memory->mode == GBM_AMBIENT) {
|
|
if (Game_GetTime() > memory->next_ambient_time) {
|
|
ReInitAmbient(me);
|
|
}
|
|
}
|
|
|
|
// Allow the buddy to collide with his player after 3 seconds
|
|
if (memory->f_parented == true) {
|
|
float time;
|
|
Obj_Value(me, VF_GET, OBJV_F_CREATION_TIME, &time);
|
|
|
|
if (Game_GetTime() - time > 3.0f) {
|
|
int parent = OBJECT_HANDLE_NONE;
|
|
|
|
Obj_Value(me, VF_SET, OBJV_I_PARENT_HANDLE, &parent);
|
|
memory->f_parented = false;
|
|
}
|
|
}
|
|
|
|
memory->time_till_next_pvis_check -= Game_GetFrameTime();
|
|
if (memory->time_till_next_pvis_check <= 0.0f) {
|
|
vector start_pos;
|
|
vector end_pos;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &start_pos);
|
|
Obj_Value(memory->my_player, VF_GET, OBJV_V_POS, &end_pos);
|
|
|
|
memory->time_till_next_pvis_check = 0.5f + (0.5f * ((float)rand() / (float)RAND_MAX));
|
|
|
|
if (vm_VectorDistance(&start_pos, &end_pos) < 200.0f) {
|
|
int start_room;
|
|
ray_info ray;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &start_room);
|
|
|
|
flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS |
|
|
FQ_IGNORE_NON_LIGHTMAP_OBJECTS;
|
|
int fate = FVI_RayCast(me, &start_pos, &end_pos, start_room, 0.0f, flags, &ray);
|
|
|
|
if (fate == HIT_NONE) {
|
|
memory->return_time = Game_GetTime();
|
|
}
|
|
}
|
|
// mprintf(0, "Vis check %s\n", (fate==HIT_NONE)?"passed":"failed");
|
|
// mprintf(0, "Submode %d\n", memory->sub_mode);
|
|
}
|
|
|
|
if (memory->mode == GBM_AMBIENT) {
|
|
memory->time_till_next_flare -= Game_GetFrameTime();
|
|
|
|
if (memory->time_till_next_flare <= 0.0f) {
|
|
memory->time_till_next_flare = 3.0f + ((float)rand() / (float)RAND_MAX);
|
|
|
|
bool f_used;
|
|
AI_GoalValue(me, 1, VF_GET, AIGV_B_USED, &f_used);
|
|
if (f_used) {
|
|
AI_GoalValue(me, 2, VF_GET, AIGV_B_USED, &f_used);
|
|
if (!f_used) {
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &f_used);
|
|
if (!f_used) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = me;
|
|
|
|
uint16_t id;
|
|
Obj_Value(me, VF_GET, OBJV_US_ID, &id);
|
|
|
|
if (Obj_FindID("GuideBot") == id) {
|
|
mstruct.index = Wpn_FindID("Yellow flare");
|
|
} else {
|
|
mstruct.index = Wpn_FindID("GreenFlare");
|
|
}
|
|
|
|
if (mstruct.index != -1) {
|
|
mstruct.gunpoint = -1;
|
|
Sound_Play3d(me, Sound_FindId("Flare"));
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_FIRE_WEAPON, &mstruct);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Makes the buddy stay pretty close to his player
|
|
if (memory->return_time + 2.5 < Game_GetTime()) {
|
|
if (Obj_GetObjDist(me, memory->my_player, true) > 10.0f && memory->sub_mode == GBSM_NONE) {
|
|
DoMessage(TXT_GB_COMINGBACK, false, "GBotAcceptOrder1");
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 2, 1.0f, GB_GUID_RETURNED_TO_PLAYER, GF_NOTIFIES, memory->my_player);
|
|
AI_SetGoalCircleDist(me, 2, 30.0f);
|
|
}
|
|
|
|
memory->return_time = Game_GetTime();
|
|
}
|
|
}
|
|
|
|
memory->last_anim = anim;
|
|
}
|
|
|
|
int16_t GuideBot::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
return (DoInit(data->me_handle, false) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_INTERVAL: {
|
|
int i;
|
|
|
|
while (memory->num_av_robots > 0 && memory->av_revert_time[0] <= Game_GetTime()) {
|
|
|
|
int flags = AIF_TEAM_MASK;
|
|
AI_Value(memory->av_robot_list[0], VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_TEAM_PTMC;
|
|
AI_Value(memory->av_robot_list[0], VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
int target_handle = OBJECT_HANDLE_NONE;
|
|
AI_Value(memory->av_robot_list[0], VF_SET, AIV_I_TARGET_HANDLE, &target_handle);
|
|
|
|
for (i = 1; i < memory->num_av_robots; i++) {
|
|
memory->av_robot_list[i - 1] = memory->av_robot_list[i];
|
|
}
|
|
|
|
memory->num_av_robots--;
|
|
}
|
|
} break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_COLLIDE:
|
|
DoCollide(data->me_handle, &data->evt_collide);
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return (DoNotify(data->me_handle, data) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_USE:
|
|
return (DoUse(data->me_handle) != 0) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_DESTROY:
|
|
if (memory->camera_obj != OBJECT_HANDLE_NONE) {
|
|
msafe_struct mo;
|
|
mo.objhandle = memory->camera_obj;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
|
|
memory->camera_obj = OBJECT_HANDLE_NONE;
|
|
}
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (guidebot_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Thief
|
|
//---------------
|
|
|
|
void Thief::DoAttack(int me, int it) {}
|
|
|
|
bool Thief::DoSteal(int me, int it, int attempt_num, bool f_last_success) {
|
|
int max_tries = numThiefableItems;
|
|
float gen_perc, perc_chance;
|
|
;
|
|
int i;
|
|
int count_max, count_num;
|
|
bool *attempted_steals;
|
|
|
|
attempted_steals = (bool *)malloc(numThiefableItems * sizeof(bool));
|
|
if (!attempted_steals)
|
|
return false;
|
|
|
|
float ftime = Game_GetFrameTime();
|
|
if (ftime == 0)
|
|
ftime = 1.0f;
|
|
srand(Game_GetTime() / ftime);
|
|
|
|
for (i = 0; i < numThiefableItems; i++) {
|
|
attempted_steals[i] = false;
|
|
}
|
|
|
|
while (max_tries--) {
|
|
i = rand() % numThiefableItems;
|
|
gen_perc = ((float)rand()) / ((float)(RAND_MAX));
|
|
|
|
perc_chance = (attempt_num == 1) ? ThiefableItems[i].attempt_one
|
|
: (f_last_success) ? ThiefableItems[i].attempt_two
|
|
: ThiefableItems[i].attempt_two_no_one;
|
|
|
|
if (attempted_steals[i]) {
|
|
// we already tried this one, try another
|
|
max_tries++;
|
|
continue;
|
|
}
|
|
|
|
attempted_steals[i] = true;
|
|
|
|
if (gen_perc <= perc_chance) {
|
|
// check to see if we have the index, if so, take it!
|
|
switch (ThiefableItems[i].type) {
|
|
case THIEFABLEITEM_PRIMARY:
|
|
case THIEFABLEITEM_SECONDARY:
|
|
count_max = MAX_STOLEN_WEAPONS;
|
|
count_num = memory->num_stolen_weapons;
|
|
break;
|
|
case THIEFABLEITEM_ACCESSORY:
|
|
count_max = MAX_STOLEN_INV;
|
|
count_num = memory->num_stolen_inv;
|
|
break;
|
|
default:
|
|
mprintf(0, "THIEF: INVALID THIEFABLE TYPE!\n");
|
|
count_max = 0;
|
|
count_num = 0;
|
|
break;
|
|
}
|
|
|
|
if (count_num >= count_max) {
|
|
mprintf(0, "THIEF: no mo' room\n");
|
|
continue; // we have no more room
|
|
}
|
|
|
|
// mprintf(0,"THIEF: Checking %s\n",TXT(ThiefableItems[i].name_idx));
|
|
|
|
bool can_take = false;
|
|
int amount = 0;
|
|
char message[256];
|
|
|
|
switch (ThiefableItems[i].type) {
|
|
case THIEFABLEITEM_PRIMARY:
|
|
// check to see if we can steal the primary
|
|
Player_Value(it, VF_GET, PLYSV_I_WEAPON, &amount, ThiefableItems[i].index);
|
|
if (ThiefableItems[i].index != 0 && amount) {
|
|
can_take = true;
|
|
snprintf(message, sizeof(message), TXT_THIEFSTEAL, TXT(ThiefableItems[i].name_idx));
|
|
}
|
|
break;
|
|
|
|
case THIEFABLEITEM_SECONDARY:
|
|
// check to see if we can steal the secondary
|
|
if (attempt_num <= 1 || (!f_last_success)) {
|
|
Player_Value(it, VF_GET, PLYSV_I_WEAPON, &amount, ThiefableItems[i].index);
|
|
|
|
if (amount) {
|
|
int max;
|
|
|
|
if (ThiefableItems[i].index > 14) {
|
|
// 2nd selection group (black shark, etc)
|
|
max = (19 - ThiefableItems[i].index) / 2.5f + 1;
|
|
} else {
|
|
// 1st selection group (mega, etc)
|
|
max = (14 - ThiefableItems[i].index) / 2.5f + 1;
|
|
}
|
|
|
|
int new_amount = (rand() / (float)(RAND_MAX + 1)) * max + 1;
|
|
|
|
if (new_amount < amount)
|
|
amount = new_amount;
|
|
|
|
can_take = true;
|
|
|
|
if (amount == 1) {
|
|
snprintf(message, sizeof(message), TXT_THIEFSTEAL1, TXT(ThiefableItems[i].name_idx));
|
|
} else {
|
|
snprintf(message, sizeof(message), TXT_THIEFSTEAL2, amount, TXT(ThiefableItems[i].name_idx));
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case THIEFABLEITEM_ACCESSORY:
|
|
// check to see if we can steal the accessory
|
|
Player_Value(it, VF_GET, PLYV_B_THIEF_PLAYERHASITEM, &can_take, ThiefableItems[i].index);
|
|
amount = 1;
|
|
|
|
if (can_take && ThiefableItems[i].index == THIEFITEM_AUTOMAP) {
|
|
// make sure the thief hasn't taken your automap already
|
|
for (int k = 0; k < memory->num_stolen_inv; k++) {
|
|
if (memory->stolen_inv[k].id == THIEFITEM_AUTOMAP) {
|
|
// he has already taken an automap
|
|
can_take = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (can_take) {
|
|
snprintf(message, sizeof(message), TXT_THIEFSTEAL, TXT(ThiefableItems[i].name_idx));
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!can_take)
|
|
continue;
|
|
|
|
mprintf(0, "THIEF: Taking %s\n", TXT(ThiefableItems[i].name_idx));
|
|
|
|
Player_AddHudMessage(it, message);
|
|
if (rand() % 50) {
|
|
Sound_Play3d(me, Sound_FindId("RbtThiefHehHehHeh"));
|
|
} else {
|
|
Sound_Play3d(me, Sound_FindId("RbtThiefLaugh"));
|
|
}
|
|
|
|
switch (ThiefableItems[i].type) {
|
|
case THIEFABLEITEM_PRIMARY:
|
|
case THIEFABLEITEM_SECONDARY:
|
|
memory->stolen_weapons[memory->num_stolen_weapons].index = ThiefableItems[i].index;
|
|
memory->stolen_weapons[memory->num_stolen_weapons].owner = it;
|
|
memory->stolen_weapons[memory->num_stolen_weapons].amount = amount;
|
|
memory->num_stolen_weapons++;
|
|
|
|
if (ThiefableItems[i].type == THIEFABLEITEM_PRIMARY) {
|
|
amount = 0;
|
|
} else {
|
|
amount *= -1;
|
|
}
|
|
|
|
Player_Value(it, VF_SET, PLYSV_I_WEAPON, &amount, ThiefableItems[i].index);
|
|
|
|
break;
|
|
case THIEFABLEITEM_ACCESSORY:
|
|
memory->stolen_inv[memory->num_stolen_inv].id = ThiefableItems[i].index;
|
|
memory->stolen_inv[memory->num_stolen_inv].owner = it;
|
|
memory->num_stolen_inv++;
|
|
|
|
Player_Value(it, VF_SET, PLYV_I_THIEF_STEALPLAYERITEM, NULL, ThiefableItems[i].index);
|
|
break;
|
|
}
|
|
|
|
free(attempted_steals);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
mprintf(0, "THIEF: Not taking anything\n");
|
|
free(attempted_steals);
|
|
return false; // we didn't take anything this try
|
|
}
|
|
|
|
void Thief::DoKidnap(int me, int it) {
|
|
if (memory->num_stolen_objs < MAX_STOLEN_OBJS) {
|
|
Obj_Ghost(it, true);
|
|
memory->stolen_objs[memory->num_stolen_objs++] = it;
|
|
}
|
|
}
|
|
|
|
void Thief::SetSubMode(int me, int submode) {
|
|
if (submode == memory->last_special_sub_mode)
|
|
return;
|
|
|
|
if (memory->sub_mode == THIEF_SUB_GET_POWERUP) {
|
|
// Make sure we reset the Alert animation
|
|
Obj_SetCustomAnim(me, 0.0f, 6.0f, 1.0f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
}
|
|
|
|
if (submode == THIEF_SUB_GET_POWERUP && (memory->flags & THIEF_F_GOT_POWERUP))
|
|
return;
|
|
|
|
if (submode == THIEF_SUB_DROP_POWERUP && !(memory->flags & THIEF_F_GOT_POWERUP))
|
|
return;
|
|
|
|
// CHRISHACK - we might want to make sure the thief only does some of stuff during
|
|
if (submode != THIEF_SUB_NONE && submode != THIEF_SUB_CLOAK) {
|
|
float time;
|
|
float last_hear_target_time;
|
|
AI_Value(me, VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &time);
|
|
AI_Value(me, VF_GET, AIV_F_LAST_HEAR_TARGET_TIME, &last_hear_target_time);
|
|
|
|
if (Game_GetTime() > time + 2.0f && Game_GetTime() > last_hear_target_time + 2.0f) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (submode) {
|
|
case THIEF_SUB_NONE: {
|
|
AI_SetGoalFlags(me, 2, GF_ORIENT_TARGET, false);
|
|
memory->next_test_time = Game_GetTime() + 10.0f + (float)rand() * (1.0f / (float)RAND_MAX) * 10.0f;
|
|
} break;
|
|
case THIEF_SUB_SUMMON: {
|
|
AI_SetGoalFlags(me, 2, GF_ORIENT_TARGET, false);
|
|
Obj_SetCustomAnim(me, 12.0f, 19.0f, 0.8f, 0, -1, -1);
|
|
} break;
|
|
case THIEF_SUB_CLOAK: {
|
|
AI_SetGoalFlags(me, 2, GF_ORIENT_TARGET, true);
|
|
Obj_SetCustomAnim(me, 50.0f, 64.0f, 3.5f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
} break;
|
|
case THIEF_SUB_THROW_ITEM: {
|
|
AI_SetGoalFlags(me, 2, GF_ORIENT_TARGET, true);
|
|
Obj_SetCustomAnim(me, 29.0f, 50.0f, 3.5f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
} break;
|
|
case THIEF_SUB_GET_POWERUP: {
|
|
AI_SetGoalFlags(me, 2, GF_ORIENT_TARGET, false);
|
|
Obj_SetCustomAnim(me, 29.0f, 29.0f, 0.1f, AIAF_LOOPING);
|
|
} break;
|
|
case THIEF_SUB_DROP_POWERUP: {
|
|
AI_SetGoalFlags(me, 2, GF_ORIENT_TARGET, true);
|
|
Obj_SetCustomAnim(me, 145.0f, 160.0f, 2.3f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
} break;
|
|
}
|
|
|
|
if (submode != THIEF_SUB_NONE) {
|
|
memory->last_special_sub_mode = submode;
|
|
}
|
|
|
|
memory->sub_mode_time = 0.0f;
|
|
memory->sub_mode = submode;
|
|
}
|
|
|
|
void Thief::DoSubModeFrame(int me) {
|
|
float anim_frame;
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &anim_frame);
|
|
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
|
|
switch (memory->sub_mode) {
|
|
case THIEF_SUB_NONE: {
|
|
if (Game_GetTime() > memory->next_test_time) {
|
|
int r_val = (rand() % 5) + 1;
|
|
SetSubMode(me, r_val);
|
|
|
|
// In case above failed, make sure there is a time gap between submode checks
|
|
memory->next_test_time = Game_GetTime() + 10.0f + (float)rand() * (1.0f / (float)RAND_MAX) * 10.0f;
|
|
}
|
|
} break;
|
|
case THIEF_SUB_SUMMON: {
|
|
|
|
if (anim_frame >= 12.0f && anim_frame < 19.0f) {
|
|
Obj_SetCustomAnim(me, 19.0f, 23.0f, 0.8f, AIAF_LOOPING);
|
|
}
|
|
|
|
if (anim_frame > 19.0f && anim_frame < 23.0f) {
|
|
if (memory->last_summon_time + 0.25f <= Game_GetTime()) {
|
|
memory->last_summon_time = Game_GetTime();
|
|
|
|
// Chrishack - do summon stuff here
|
|
}
|
|
}
|
|
|
|
if (memory->sub_mode_time > 6.0f && anim_frame >= 19.0f && anim_frame <= 23.0f) {
|
|
Obj_SetCustomAnim(me, 23.0f, 29.0f, 0.8f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
SetSubMode(me, THIEF_SUB_NONE);
|
|
}
|
|
|
|
} break;
|
|
case THIEF_SUB_CLOAK: {
|
|
if (anim_frame > 61.0f && memory->last_frame <= 61.0f) {
|
|
m.lifetime = 15.0f;
|
|
m.state = 1;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_CLOAK, &m);
|
|
|
|
AI_SetGoalFlags(me, 2, GF_ORIENT_TARGET, false);
|
|
}
|
|
} break;
|
|
case THIEF_SUB_THROW_ITEM: {
|
|
if (anim_frame > 41.0f && memory->last_frame <= 41.0f) {
|
|
memory->bomb_handle = CreateAndAttach(me, "PTMCSeeker", OBJ_ROBOT, 1, 0, true, true);
|
|
}
|
|
|
|
if (anim_frame > 45.0f && memory->last_frame <= 45.0f) {
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
vector velocity = orient.fvec * 50.0f;
|
|
|
|
Obj_UnattachFromParent(memory->bomb_handle);
|
|
|
|
Obj_Value(memory->bomb_handle, VF_SET, OBJV_V_VELOCITY, &velocity);
|
|
|
|
AI_SetGoalFlags(me, 2, GF_ORIENT_TARGET, false);
|
|
}
|
|
} break;
|
|
case THIEF_SUB_GET_POWERUP: {
|
|
} break;
|
|
case THIEF_SUB_DROP_POWERUP: {
|
|
} break;
|
|
default: {
|
|
} break;
|
|
}
|
|
|
|
if (memory->sub_mode != THIEF_SUB_NONE && memory->sub_mode_time > 10.0) {
|
|
SetSubMode(me, THIEF_SUB_NONE);
|
|
}
|
|
}
|
|
|
|
void Thief::SetMode(int me, int new_mode) {
|
|
vector pos;
|
|
int room = 0;
|
|
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
// Makes sure that the dodge flag is set by default (certain modes turn it off)
|
|
int flags = AIF_DODGE; // Don't dodge while surrendering
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
switch (new_mode) {
|
|
case THIEF_AMBIENT: {
|
|
AI_AddGoal(me, AIG_WANDER_AROUND, 2, 1.0f, -1, GF_ORIENT_VELOCITY | GF_NOTIFIES | GF_KEEP_AT_COMPLETION, &pos,
|
|
room);
|
|
AI_SetGoalFlags(me, 2, GF_ORIENT_TARGET, false);
|
|
} break;
|
|
case THIEF_STEAL: {
|
|
flags = AIF_DODGE; // Don't dodge while stealing
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
AI_SetType(me, AIT_MELEE1);
|
|
} break;
|
|
case THIEF_DEATH: {
|
|
flags = AIF_DODGE; // Don't dodge while dying
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
float temp = 0.0f;
|
|
AI_Value(me, VF_SET, AIV_F_MAX_TURN_RATE, &temp);
|
|
Obj_SetCustomAnim(me, 85.0f, 140.0f, 8.0f, 0, Sound_FindId("RbtThiefDeath"), -1);
|
|
} break;
|
|
case THIEF_SURRENDER: {
|
|
flags = AIF_DODGE; // Don't dodge while surrendering
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1, GF_ORIENT_TARGET | GF_KEEP_AT_COMPLETION, me);
|
|
Obj_SetCustomAnim(me, 64.0f, 71.0f, 1.0f, 0, -1, -1);
|
|
} break;
|
|
case THIEF_TRACK_PLAYER: {
|
|
} break;
|
|
case THIEF_RUN_AWAY: {
|
|
} break;
|
|
case THIEF_TIME_BOMB: {
|
|
} break;
|
|
case THIEF_PLAYER_DEATH_TAUNT: {
|
|
} break;
|
|
case THIEF_KIDNAP_GB: {
|
|
} break;
|
|
case THIEF_CORNERED: {
|
|
} break;
|
|
}
|
|
|
|
memory->mode_time = 0.0f;
|
|
memory->mode = new_mode;
|
|
}
|
|
|
|
void Thief::DoInit(int me) {
|
|
|
|
int room;
|
|
vector pos;
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(thief_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (thief_data *)Scrpt_MemAlloc(&ch);
|
|
memory->next_test_time = Game_GetTime() + 5.0f;
|
|
memory->last_frame = 0;
|
|
memory->last_summon_time = Game_GetTime();
|
|
|
|
memory->num_stolen_objs = 0;
|
|
memory->num_stolen_weapons = 0;
|
|
memory->num_stolen_inv = 0;
|
|
Obj_Value(me, VF_GET, OBJV_F_SHIELDS, &memory->last_shields);
|
|
|
|
SetMode(me, THIEF_AMBIENT);
|
|
SetSubMode(me, THIEF_SUB_NONE);
|
|
}
|
|
|
|
void Thief::DoCollide(int me, tOSIRISEVTCOLLIDE *evt_collide) {}
|
|
|
|
bool Thief::DoNotify(int me, tOSIRISEVTAINOTIFY *notify) {
|
|
switch (notify->notify_type) {
|
|
case AIN_MELEE_HIT: {
|
|
int target_handle;
|
|
int target_type;
|
|
|
|
AI_Value(me, VF_GET, AIV_I_TARGET_HANDLE, &target_handle);
|
|
Obj_Value(target_handle, VF_GET, OBJV_I_TYPE, &target_type);
|
|
|
|
if (target_type == OBJ_PLAYER) {
|
|
bool f_success;
|
|
|
|
f_success = DoSteal(me, target_handle, 1, false);
|
|
DoSteal(me, target_handle, 2, f_success);
|
|
} else if (target_type == OBJ_ROBOT) {
|
|
uint16_t id;
|
|
|
|
Obj_Value(target_handle, VF_GET, OBJV_US_ID, &id);
|
|
if (id == ROBOT_GUIDEBOT || id == ROBOT_GUIDEBOTRED) {
|
|
DoKidnap(me, target_handle);
|
|
} else {
|
|
DoAttack(me, target_handle);
|
|
}
|
|
}
|
|
|
|
// If successful or dot to player greater than threshold then -- chrishack
|
|
SetMode(me, THIEF_AMBIENT);
|
|
} break;
|
|
case AIN_HIT_BY_WEAPON: {
|
|
if (memory->mode == THIEF_STEAL) {
|
|
SetMode(me, THIEF_AMBIENT);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Thief::DoFrame(int me) {
|
|
// Drop bomb:29-50 (4.5 seconds, bomb created at 41 and leaves hand at frame 45)
|
|
// Button push: 50-64 (3.5 seconds) button presses: 57,59
|
|
// surrender: 64-85 (2.5-3.0 seconds)
|
|
// summon: 12-29 (3.0 seconds) 19-23 (summons)
|
|
|
|
float anim_frame;
|
|
float shields;
|
|
int flags;
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &anim_frame);
|
|
Obj_Value(me, VF_GET, OBJV_F_SHIELDS, &shields);
|
|
Obj_Value(me, VF_GET, OBJV_I_FLAGS, &flags);
|
|
|
|
bool f_used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &f_used);
|
|
if (f_used) {
|
|
return;
|
|
}
|
|
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
|
|
MSafe_GetValue(MSAFE_OBJECT_POS, &m);
|
|
MSafe_GetValue(MSAFE_OBJECT_ROOMNUM, &m);
|
|
|
|
if ((flags & OF_AI_DEATH) &&
|
|
!((anim_frame >= 0.0f && anim_frame <= 6.0f) || (anim_frame >= 85.0f && anim_frame <= 140.0f))) {
|
|
int room;
|
|
vector pos;
|
|
int weapon_id;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
weapon_id = Wpn_FindID("TubbsHitBlast");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, NULL, me);
|
|
|
|
Obj_Kill(me, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
}
|
|
|
|
if ((memory->mode != THIEF_DEATH) && (flags & OF_AI_DEATH) && anim_frame >= 0.0f && anim_frame <= 6.0f) {
|
|
SetMode(me, THIEF_DEATH);
|
|
}
|
|
|
|
if (memory->last_frame >= 50.0f && memory->last_frame <= 64.0f && memory->last_frame < 57.0f && anim_frame >= 57.0f) {
|
|
Sound_Play3d(me, Sound_FindId("RbtThiefButtonA"));
|
|
}
|
|
|
|
if (memory->last_frame >= 50.0f && memory->last_frame <= 64.0f && memory->last_frame < 59.0f && anim_frame >= 59.0f) {
|
|
Sound_Play3d(me, Sound_FindId("RbtThiefButtonB"));
|
|
}
|
|
|
|
if (memory->mode == THIEF_DEATH) {
|
|
if (anim_frame > 86 && memory->last_frame <= 86) {
|
|
int room;
|
|
vector pos;
|
|
int weapon_id;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
weapon_id = Wpn_FindID("TubbsHitBlast");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, NULL, me);
|
|
}
|
|
|
|
if (memory->mode_time < 5.0f && memory->mode_time + Game_GetFrameTime() >= 5.0f) {
|
|
Obj_Burning(me, 1000000.0f, 1.0f);
|
|
}
|
|
|
|
if (memory->mode_time > 8.0f) {
|
|
Obj_Kill(me, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
}
|
|
} else if (memory->mode == THIEF_SURRENDER) {
|
|
if (anim_frame >= 65.0 && anim_frame <= 71.0) {
|
|
Obj_SetCustomAnim(me, 71.0f, 79.0f, 1.0f, AIAF_LOOPING, -1, -1);
|
|
}
|
|
|
|
if (memory->mode_time < 2.2f && memory->mode_time + Game_GetFrameTime() >= 2.2f) {
|
|
SpewEverything(me);
|
|
}
|
|
|
|
if (memory->mode_time < 3.0f && memory->mode_time + Game_GetFrameTime() >= 3.0f) {
|
|
Obj_SetCustomAnim(me, 79.0f, 85.0f, 1.5f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
}
|
|
|
|
if (memory->mode_time > 3.0f && anim_frame >= 0 && anim_frame <= 6) {
|
|
SetMode(me, THIEF_AMBIENT);
|
|
}
|
|
} else if (memory->mode == THIEF_AMBIENT) {
|
|
DoSubModeFrame(me);
|
|
|
|
if (memory->mode_time > 10.0f && memory->sub_mode == THIEF_SUB_NONE) {
|
|
SetMode(me, THIEF_STEAL);
|
|
}
|
|
|
|
if (shields < 20.0f && memory->last_shields >= 20.0) {
|
|
SetMode(me, THIEF_SURRENDER);
|
|
}
|
|
} else {
|
|
if (memory->mode_time > 10.0 && memory->sub_mode == THIEF_SUB_NONE) {
|
|
SetMode(me, THIEF_AMBIENT);
|
|
}
|
|
}
|
|
|
|
// Update the last frame
|
|
memory->mode_time += Game_GetFrameTime();
|
|
memory->sub_mode_time += Game_GetFrameTime();
|
|
|
|
memory->last_frame = anim_frame;
|
|
memory->last_shields = shields;
|
|
}
|
|
|
|
void Thief::SpewEverything(int me) {
|
|
int i;
|
|
int powerup_handle;
|
|
int room;
|
|
vector pos;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
for (i = 0; i < memory->num_stolen_weapons; i++) {
|
|
uint16_t id;
|
|
int j;
|
|
|
|
for (j = 0; j < memory->stolen_weapons[i].amount; j++) {
|
|
float speed = rand() / (float)RAND_MAX * 20.0f + 5.0f;
|
|
vector dir;
|
|
|
|
dir.x = rand() / (float)RAND_MAX - 0.5f;
|
|
dir.y = rand() / (float)RAND_MAX - 0.5f;
|
|
dir.z = rand() / (float)RAND_MAX - 0.5f;
|
|
|
|
vm_VectorNormalize(&dir);
|
|
dir *= speed;
|
|
|
|
Player_Value(memory->stolen_weapons[i].owner, VF_GET, PLYSV_US_WEAPON_POWERUP_ID, &id,
|
|
memory->stolen_weapons[i].index);
|
|
powerup_handle = Obj_Create(OBJ_POWERUP, id, room, &pos, NULL, OBJECT_HANDLE_NONE, &dir);
|
|
}
|
|
|
|
memory->stolen_weapons[i].amount = 0;
|
|
}
|
|
|
|
for (i = 0; i < memory->num_stolen_objs; i++) {
|
|
}
|
|
memory->num_stolen_objs = 0;
|
|
|
|
for (i = 0; i < memory->num_stolen_inv; i++) {
|
|
static int am_id = -2, hl_id = -2, etos_id = -2, cl_id = -2, iv_id = -2, rf_id = -2, qf_id = -2;
|
|
int id;
|
|
|
|
switch (memory->stolen_inv[i].id) {
|
|
case THIEFITEM_AUTOMAP:
|
|
if (am_id == -2)
|
|
am_id = Obj_FindID("ThiefAutoMap");
|
|
id = am_id;
|
|
break;
|
|
case THIEFITEM_HEADLIGHT:
|
|
if (hl_id == -2)
|
|
hl_id = Obj_FindID("HeadlightPowerup");
|
|
id = hl_id;
|
|
break;
|
|
case THIEFITEM_ETOSCONV:
|
|
if (etos_id == -2)
|
|
etos_id = Obj_FindID("Converter");
|
|
id = etos_id;
|
|
break;
|
|
case THIEFITEM_CLOAK:
|
|
if (cl_id == -2)
|
|
cl_id = Obj_FindID("Cloak");
|
|
id = cl_id;
|
|
break;
|
|
case THIEFITEM_INVULN:
|
|
if (iv_id == -2)
|
|
iv_id = Obj_FindID("Invulnerability");
|
|
id = iv_id;
|
|
break;
|
|
case THIEFITEM_RAPIDFIRE:
|
|
if (rf_id == -2)
|
|
rf_id = Obj_FindID("Rapidfire");
|
|
id = rf_id;
|
|
break;
|
|
case THIEFITEM_QUADFIRE:
|
|
if (qf_id == -2)
|
|
qf_id = Obj_FindID("QuadLaser");
|
|
id = qf_id;
|
|
break;
|
|
default:
|
|
id = -1;
|
|
break;
|
|
}
|
|
|
|
if (id > -1) {
|
|
float speed = rand() / (float)RAND_MAX * 20.0f + 5.0f;
|
|
vector dir;
|
|
|
|
dir.x = rand() / (float)RAND_MAX - 0.5f;
|
|
dir.y = rand() / (float)RAND_MAX - 0.5f;
|
|
dir.z = rand() / (float)RAND_MAX - 0.5f;
|
|
|
|
vm_VectorNormalize(&dir);
|
|
dir *= speed;
|
|
|
|
powerup_handle = Obj_Create(OBJ_POWERUP, id, room, &pos, NULL, OBJECT_HANDLE_NONE, &dir);
|
|
}
|
|
}
|
|
memory->num_stolen_inv = 0;
|
|
}
|
|
|
|
void Thief::DoDestroy(int me, tOSIRISEVTDESTROY *evt_destroy) { SpewEverything(me); }
|
|
|
|
int16_t Thief::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_COLLIDE:
|
|
DoCollide(data->me_handle, &data->evt_collide);
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return (DoNotify(data->me_handle, &data->evt_ai_notify) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_DESTROY:
|
|
DoDestroy(data->me_handle, &data->evt_destroy);
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (thief_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Super Thief
|
|
//---------------
|
|
|
|
//---------------
|
|
// Sickle
|
|
//---------------
|
|
|
|
void Sickle::SetMode(int me, char mode) {
|
|
if (mode == memory->mode)
|
|
return;
|
|
|
|
float fov = -1.0f;
|
|
AI_Value(me, VF_SET, AIV_F_FOV, &fov);
|
|
|
|
mprintf(0, "Set mode %d\n", (int)mode);
|
|
memory->done_moving = false;
|
|
memory->done_turning = false;
|
|
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
switch (mode) {
|
|
case SICKLE_GOING_HOME: {
|
|
int flags;
|
|
|
|
// Turn off friend avoidance
|
|
flags = AIF_AUTO_AVOID_FRIENDS;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
// Orient to vel
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 2, 1.0f, -1, GF_ORIENT_VELOCITY | GF_NOTIFIES, &memory->home_pos, memory->home_room);
|
|
AI_SetGoalCircleDist(me, 2, 8.0f);
|
|
} break;
|
|
case SICKLE_LAND_ON_CEILING: {
|
|
ray_info ray;
|
|
int flags;
|
|
int fate;
|
|
|
|
int start_room;
|
|
vector start_pos;
|
|
vector end_pos;
|
|
|
|
int ceiling_room;
|
|
// mprintf(0, "In mode 5\n");
|
|
|
|
// Turn off auto-level
|
|
flags = PF_LEVELING;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &start_pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &start_room);
|
|
end_pos = start_pos;
|
|
end_pos.y = end_pos.y + 400.0;
|
|
flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS |
|
|
FQ_IGNORE_NON_LIGHTMAP_OBJECTS;
|
|
fate = FVI_RayCast(me, &start_pos, &end_pos, start_room, 0.0f, flags, &ray);
|
|
|
|
memory->ceiling_pos = ray.hit_point;
|
|
memory->ceiling_pos.y -= 3.2f;
|
|
|
|
fate = FVI_RayCast(me, &start_pos, &memory->ceiling_pos, start_room, 0.0f, flags, &ray);
|
|
ceiling_room = ray.hit_room;
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 2, 1.0f, -1, /*GF_NONFLUSHABLE | GF_KEEP_AT_COMPLETION |*/ GF_ORIENT_SCRIPTED,
|
|
&memory->ceiling_pos, ceiling_room);
|
|
AI_SetGoalCircleDist(me, 2, 0.0f);
|
|
|
|
// Turn on point-collide-walls
|
|
flags = PF_POINT_COLLIDE_WALLS;
|
|
Obj_Value(me, VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
flags = PF_LOCK_MASK;
|
|
Obj_Value(me, VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
} break;
|
|
case SICKLE_LANDED: {
|
|
int flags;
|
|
char movement_type;
|
|
|
|
// Stop on ceiling
|
|
flags = AIF_FORCE_AWARENESS | AIF_PERSISTANT;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
float awareness = AWARE_BARELY;
|
|
AI_Value(me, VF_SET, AIV_F_AWARENESS, &awareness);
|
|
|
|
float sleep_fov = 1.0f;
|
|
AI_Value(me, VF_SET, AIV_F_FOV, &sleep_fov);
|
|
|
|
movement_type = MT_NONE;
|
|
Obj_Value(me, VF_SET, OBJV_C_MOVEMENT_TYPE, &movement_type);
|
|
} break;
|
|
case SICKLE_MELEE: {
|
|
int flags;
|
|
vector vel;
|
|
char movement_type;
|
|
|
|
if (memory->mode == SICKLE_LANDED) {
|
|
Obj_Value(me, VF_GET, OBJV_V_VELOCITY, &vel);
|
|
vel.y -= (8.0f + ((float)rand() / (float)RAND_MAX) * 15.0f);
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
}
|
|
|
|
// Melee Mode
|
|
flags = PF_LOCK_MASK | PF_POINT_COLLIDE_WALLS;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
flags = PF_LEVELING;
|
|
Obj_Value(me, VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
flags = AIF_FORCE_AWARENESS | AIF_AUTO_AVOID_FRIENDS | AIF_PERSISTANT;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
movement_type = MT_PHYSICS;
|
|
Obj_Value(me, VF_SET, OBJV_C_MOVEMENT_TYPE, &movement_type);
|
|
|
|
AI_SetType(me, AIT_MELEE1);
|
|
} break;
|
|
default:
|
|
mprintf(0, "Sickle is all messed up!\n");
|
|
}
|
|
|
|
memory->mode = mode;
|
|
memory->mode_time = 0.0f;
|
|
}
|
|
|
|
void Sickle::DoInit(int me) {
|
|
int flags;
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(sickle_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (sickle_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
AI_SetType(me, AIT_AIS);
|
|
flags = AIF_FORCE_AWARENESS | AIF_AUTO_AVOID_FRIENDS | AIF_PERSISTANT;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &memory->home_pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &memory->home_room);
|
|
memory->done_turning = false;
|
|
memory->done_moving = false;
|
|
|
|
flags = PF_LEVELING;
|
|
Obj_Value(me, VF_SET_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
flags = PF_POINT_COLLIDE_WALLS;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
memory->home_fvec = orient.fvec;
|
|
memory->home_fvec.y = 0.0f;
|
|
vm_VectorNormalize(&memory->home_fvec);
|
|
|
|
memory->mode_time = 0.0f;
|
|
|
|
SetMode(me, SICKLE_MELEE);
|
|
}
|
|
|
|
void Sickle::DoFrame(int me) {
|
|
vector uvec;
|
|
vector start_pos;
|
|
vector end_pos;
|
|
int flags;
|
|
int fate;
|
|
int start_room;
|
|
int ceiling_room;
|
|
float anim_frame;
|
|
int new_mode;
|
|
vector vel;
|
|
float last_see_time;
|
|
float last_hear_time;
|
|
char movement_type;
|
|
|
|
float temp_time;
|
|
float temp_time2;
|
|
AI_Value(me, VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &temp_time);
|
|
AI_Value(me, VF_GET, AIV_F_LAST_HEAR_TARGET_TIME, &temp_time2);
|
|
|
|
last_hear_time = Game_GetTime() - temp_time2;
|
|
last_see_time = Game_GetTime() - temp_time;
|
|
|
|
if (last_hear_time < last_see_time || memory->mode == SICKLE_LANDED) {
|
|
last_see_time = last_hear_time;
|
|
}
|
|
|
|
memory->mode_time += Game_GetTime();
|
|
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &anim_frame);
|
|
|
|
new_mode = memory->mode;
|
|
|
|
if ((anim_frame >= 50 && anim_frame <= 55) || (last_see_time > 7.0)) {
|
|
if (memory->mode == SICKLE_MELEE) {
|
|
new_mode = SICKLE_GOING_HOME;
|
|
}
|
|
} else {
|
|
new_mode = SICKLE_MELEE;
|
|
}
|
|
|
|
if (memory->mode != new_mode) {
|
|
SetMode(me, new_mode);
|
|
}
|
|
|
|
if (memory->mode == SICKLE_GOING_HOME) {
|
|
mprintf(0, "Sickle going home\n");
|
|
|
|
if (memory->done_moving == true) {
|
|
SetMode(me, SICKLE_LAND_ON_CEILING);
|
|
}
|
|
} else if (memory->mode == SICKLE_LAND_ON_CEILING) {
|
|
uvec.x = 0.0;
|
|
uvec.y = -1.0;
|
|
uvec.z = 0.0;
|
|
|
|
memory->done_turning = AI_TurnTowardsVectors(me, &memory->home_fvec, &uvec) != 0;
|
|
|
|
// mprintf(0, "Sickle Land on Ceiling %d, %d\n", memory->done_moving?1:0, memory->done_turning?1:0);
|
|
vector pos;
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
bool f_used;
|
|
AI_GoalValue(me, 2, VF_GET, AIGV_B_USED, &f_used);
|
|
|
|
mprintf(0, "%d Sickle Land on Ceiling %f away\n", f_used ? 1 : 0, vm_VectorDistance(&memory->ceiling_pos, &pos));
|
|
|
|
// mprintf(0, "In mode 10\n");
|
|
int flags = PF_LEVELING;
|
|
Obj_Value(me, VF_CLEAR_FLAGS, OBJV_I_PHYSICS_FLAGS, &flags);
|
|
|
|
if (memory->done_turning == true && memory->done_moving == true) {
|
|
SetMode(me, SICKLE_LANDED);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Sickle::DoNotify(int me_handle, tOSIRISEVTAINOTIFY *notify) {
|
|
if ((IsGoalFinishedNotify(notify->notify_type)) &&
|
|
(memory->mode == SICKLE_GOING_HOME || memory->mode == SICKLE_LAND_ON_CEILING) && (notify->goal_num == 2)) {
|
|
|
|
memory->done_moving = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int16_t Sickle::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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->evt_ai_notify) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (sickle_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// FireAtDist
|
|
//---------------
|
|
|
|
void FireAtDist::DoInit(int me) {
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(fireatdist_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (fireatdist_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->last_test_time = Game_GetTime();
|
|
memory->last_f_fire = false;
|
|
memory->me = me;
|
|
|
|
AI_Value(me, VF_GET, AIV_F_CIRCLE_DIST, &memory->fire_dist);
|
|
}
|
|
|
|
void FireAtDist::DoFrame(int me) {
|
|
if (memory->last_test_time + 3.0f < Game_GetTime()) {
|
|
vector pos;
|
|
int room;
|
|
int scan_objs[200];
|
|
int n_scan;
|
|
int i;
|
|
|
|
float dist;
|
|
|
|
memory->last_test_time = Game_GetTime();
|
|
|
|
Obj_Value(memory->me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
AI_Value(memory->me, VF_GET, AIV_F_CIRCLE_DIST, &dist);
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, dist, scan_objs, 200, false, true, false, true);
|
|
|
|
memory->last_f_fire = false;
|
|
|
|
for (i = 0; i < n_scan; i++) {
|
|
int type;
|
|
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_PLAYER) {
|
|
vector p_pos;
|
|
float cur_dist;
|
|
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_V_POS, &p_pos);
|
|
|
|
cur_dist = vm_VectorDistance(&pos, &p_pos);
|
|
|
|
if (cur_dist <= memory->fire_dist) {
|
|
memory->last_f_fire = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int flags = AISR_RANGED_ATTACK;
|
|
if (memory->last_f_fire != false) {
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_STATUS_REG, &flags);
|
|
} else {
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_STATUS_REG, &flags);
|
|
}
|
|
}
|
|
|
|
int16_t FireAtDist::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (fireatdist_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// HatePTMC
|
|
//---------------
|
|
|
|
void HatePTMC::DoInit(int me) {
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(hateptmc_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (hateptmc_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->time_till_pulse = (float)rand() / (float)RAND_MAX + 2.0f;
|
|
memory->me = me;
|
|
}
|
|
|
|
void HatePTMC::DoFrame(int me) {
|
|
int flags = AIF_TEAM_PTMC;
|
|
|
|
AI_Value(me, VF_GET, AIV_I_FLAGS, &flags);
|
|
|
|
if ((flags & AIF_TEAM_MASK) == AIF_TEAM_REBEL) {
|
|
memory->time_till_pulse -= Game_GetFrameTime();
|
|
|
|
if (memory->time_till_pulse <= 0.0f) {
|
|
memory->time_till_pulse = (float)rand() / (float)RAND_MAX + 2.0f;
|
|
|
|
vector pos;
|
|
int room;
|
|
int scan_objs[50];
|
|
int n_scan;
|
|
int i;
|
|
|
|
float dist = 200.0f;
|
|
|
|
Obj_Value(memory->me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(memory->me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, dist, scan_objs, 50, false, true, false, true);
|
|
|
|
for (i = 0; i < n_scan; i++) {
|
|
char ctype;
|
|
int num_wbs;
|
|
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_C_CONTROL_TYPE, &ctype);
|
|
Obj_WBValue(scan_objs[i], 0, VF_GET, WBV_C_NUM_WBS, &num_wbs);
|
|
|
|
if (num_wbs > 0 && ctype == CT_AI) {
|
|
int flags;
|
|
int target;
|
|
float last_see_target_time;
|
|
int t_type = OBJ_NONE;
|
|
|
|
AI_Value(scan_objs[i], VF_GET, AIV_I_FLAGS, &flags);
|
|
AI_Value(scan_objs[i], VF_GET, AIV_I_TARGET_HANDLE, &target);
|
|
AI_Value(scan_objs[i], VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &last_see_target_time);
|
|
|
|
if (target != OBJECT_HANDLE_NONE) {
|
|
Obj_Value(target, VF_GET, OBJV_I_TYPE, &t_type);
|
|
}
|
|
|
|
// If it is a PTMC bot
|
|
if (((flags & AIF_TEAM_MASK) == AIF_TEAM_PTMC) &&
|
|
!(t_type == OBJ_PLAYER && Game_GetTime() - last_see_target_time < 7.0f)) {
|
|
float fov;
|
|
AI_Value(scan_objs[i], VF_GET, AIV_F_FOV, &fov);
|
|
|
|
vector p_pos;
|
|
int p_room;
|
|
matrix orient;
|
|
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_V_POS, &p_pos);
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_I_ROOMNUM, &p_room);
|
|
|
|
vector dir = p_pos - pos;
|
|
float dist = vm_VectorNormalize(&dir);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
if (p_room == room || dist < 60.0f || orient.fvec * dir >= fov) {
|
|
ray_info ray;
|
|
int flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS | FQ_IGNORE_MOVING_OBJECTS |
|
|
FQ_IGNORE_NON_LIGHTMAP_OBJECTS;
|
|
int fate = FVI_RayCast(me, &pos, &p_pos, room, 0.0f, flags, &ray);
|
|
|
|
if (fate == HIT_NONE) {
|
|
float awareness;
|
|
AI_Value(scan_objs[i], VF_GET, AIV_F_AWARENESS, &awareness);
|
|
if (awareness < AWARE_MOSTLY) {
|
|
awareness = AWARE_MOSTLY;
|
|
AI_Value(scan_objs[i], VF_SET, AIV_F_AWARENESS, &awareness);
|
|
}
|
|
AI_Value(scan_objs[i], VF_SET, AIV_I_TARGET_HANDLE, &me);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int16_t HatePTMC::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_INTERVAL:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (hateptmc_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Tubbs
|
|
//---------------
|
|
|
|
void Tubbs::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(tubbs_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (tubbs_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
int diff = Game_GetDiffLevel();
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
|
|
switch (diff) {
|
|
case DIFFICULTY_TRAINEE:
|
|
memory->max_speed = memory->base_speed * 1.2f;
|
|
memory->max_acc = memory->base_acc * 1.1f;
|
|
memory->full_anger_time = 22.0f;
|
|
break;
|
|
case DIFFICULTY_ROOKIE:
|
|
memory->max_speed = memory->base_speed * 1.3f;
|
|
memory->max_acc = memory->base_acc * 1.5f;
|
|
memory->full_anger_time = 20.0f;
|
|
break;
|
|
case DIFFICULTY_HOTSHOT:
|
|
memory->max_speed = memory->base_speed * 1.7f;
|
|
memory->max_acc = memory->base_acc * 2.5f;
|
|
memory->full_anger_time = 18.0f;
|
|
break;
|
|
case DIFFICULTY_ACE:
|
|
memory->max_speed = memory->base_speed * 1.75f;
|
|
memory->max_acc = memory->base_acc * 2.8f;
|
|
memory->full_anger_time = 15.0f;
|
|
break;
|
|
case DIFFICULTY_INSANE:
|
|
memory->max_speed = memory->base_speed * 1.8f;
|
|
memory->max_acc = memory->base_acc * 3.0f;
|
|
memory->full_anger_time = 12.0f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Tubbs::DoFrame(int me) {
|
|
float see_time;
|
|
AI_Value(me, VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &see_time);
|
|
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (see_time + 4.0f > Game_GetTime() && !used) {
|
|
float speed;
|
|
float acc;
|
|
float ft;
|
|
|
|
ft = Game_GetFrameTime();
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &speed);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &acc);
|
|
|
|
speed += (memory->max_speed - memory->base_speed) * (ft / memory->full_anger_time);
|
|
acc += (memory->max_acc - memory->base_acc) * (ft / memory->full_anger_time);
|
|
|
|
if (speed > memory->max_speed)
|
|
speed = memory->max_speed;
|
|
if (acc > memory->max_acc)
|
|
acc = memory->max_acc;
|
|
|
|
// mprintf(0, "Max Speed is %f\n", speed);
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &acc);
|
|
} else {
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
}
|
|
}
|
|
|
|
bool Tubbs::DoNotify(int me, tOSIRISEVTAINOTIFY *notify) {
|
|
int room;
|
|
vector pos;
|
|
int weapon_id;
|
|
|
|
if (notify->notify_type == AIN_MELEE_ATTACK_FRAME) {
|
|
if (notify->attack_num == 0) {
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_GetGunPos(me, 0, &pos);
|
|
|
|
weapon_id = Wpn_FindID("TubbsHitBlast");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, NULL, me);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int16_t Tubbs::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return (DoNotify(data->me_handle, &data->evt_ai_notify) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (tubbs_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------
|
|
// Old Scratch
|
|
//---------------
|
|
|
|
bool OldScratch::DoSteal(int me, int it) {
|
|
int max_tries = numSuperThiefableItems;
|
|
float gen_perc, perc_chance;
|
|
;
|
|
int i;
|
|
int count_max, count_num;
|
|
bool *attempted_steals;
|
|
|
|
attempted_steals = (bool *)malloc(numSuperThiefableItems * sizeof(bool));
|
|
if (!attempted_steals)
|
|
return false;
|
|
|
|
float ftime = Game_GetFrameTime();
|
|
if (ftime == 0)
|
|
ftime = 1.0f;
|
|
srand(Game_GetTime() / ftime);
|
|
|
|
for (i = 0; i < numSuperThiefableItems; i++) {
|
|
attempted_steals[i] = false;
|
|
}
|
|
|
|
while (max_tries--) {
|
|
i = rand() % numSuperThiefableItems;
|
|
|
|
if (attempted_steals[i]) {
|
|
// we already tried this one, try another
|
|
max_tries++;
|
|
continue;
|
|
}
|
|
|
|
attempted_steals[i] = true;
|
|
|
|
{
|
|
count_max = MAX_STOLEN_WEAPONS;
|
|
|
|
// mprintf(0,"THIEF: Checking %s\n",TXT(SuperThiefableItems[i].name_idx));
|
|
|
|
bool can_take = false;
|
|
int amount = 0;
|
|
char message[256];
|
|
|
|
switch (SuperThiefableItems[i].type) {
|
|
case THIEFABLEITEM_PRIMARY:
|
|
// check to see if we can steal the primary
|
|
Player_Value(it, VF_GET, PLYSV_I_WEAPON, &amount, SuperThiefableItems[i].index);
|
|
if (SuperThiefableItems[i].index != 0 && amount) {
|
|
can_take = true;
|
|
snprintf(message, sizeof(message), TXT_THIEFSTEAL, TXT(SuperThiefableItems[i].name_idx));
|
|
}
|
|
break;
|
|
|
|
case THIEFABLEITEM_SECONDARY:
|
|
// check to see if we can steal the secondary
|
|
{
|
|
Player_Value(it, VF_GET, PLYSV_I_WEAPON, &amount, SuperThiefableItems[i].index);
|
|
|
|
if (amount) {
|
|
int max;
|
|
|
|
if (SuperThiefableItems[i].index > 14) {
|
|
// 2nd selection group (black shark, etc)
|
|
max = (19 - SuperThiefableItems[i].index) / 2.5f + 1;
|
|
} else {
|
|
// 1st selection group (mega, etc)
|
|
max = (14 - SuperThiefableItems[i].index) / 2.5f + 1;
|
|
}
|
|
|
|
int new_amount = (rand() / (float)(RAND_MAX + 1)) * max + 1;
|
|
|
|
if (new_amount < amount)
|
|
amount = new_amount;
|
|
|
|
can_take = true;
|
|
|
|
if (amount == 1) {
|
|
snprintf(message, sizeof(message), TXT_STHIEFSTEAL, TXT(SuperThiefableItems[i].name_idx));
|
|
} else {
|
|
snprintf(message, sizeof(message), TXT_STHIEFSTEAL2, amount, TXT(SuperThiefableItems[i].name_idx));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!can_take)
|
|
continue;
|
|
|
|
{
|
|
int powerup_handle;
|
|
int room;
|
|
vector pos;
|
|
int j;
|
|
uint16_t id;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
for (j = 0; j < amount; j++) {
|
|
float speed = rand() / (float)RAND_MAX * 20.0f + 5.0f;
|
|
vector dir;
|
|
|
|
dir.x = rand() / (float)RAND_MAX - 0.5f;
|
|
dir.y = rand() / (float)RAND_MAX - 0.5f;
|
|
dir.z = rand() / (float)RAND_MAX - 0.5f;
|
|
|
|
vm_VectorNormalize(&dir);
|
|
dir *= speed;
|
|
|
|
Player_Value(it, VF_GET, PLYSV_US_WEAPON_POWERUP_ID, &id, SuperThiefableItems[i].index);
|
|
powerup_handle = Obj_Create(OBJ_POWERUP, id, room, &pos, NULL, it, &dir);
|
|
}
|
|
}
|
|
|
|
if (SuperThiefableItems[i].type == THIEFABLEITEM_PRIMARY) {
|
|
amount = 0;
|
|
} else {
|
|
amount *= -1;
|
|
}
|
|
|
|
Player_Value(it, VF_SET, PLYSV_I_WEAPON, &amount, SuperThiefableItems[i].index);
|
|
|
|
free(attempted_steals);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
mprintf(0, "Super Thief: Not taking anything\n");
|
|
free(attempted_steals);
|
|
return false; // we didn't take anything this try
|
|
}
|
|
|
|
void OldScratch::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(oldscratch_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (oldscratch_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
int diff = Game_GetDiffLevel();
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
|
|
switch (diff) {
|
|
case DIFFICULTY_TRAINEE:
|
|
memory->max_speed = memory->base_speed * 1.05f;
|
|
memory->max_acc = memory->base_acc * 1.05f;
|
|
memory->full_anger_time = 22.0f;
|
|
break;
|
|
case DIFFICULTY_ROOKIE:
|
|
memory->max_speed = memory->base_speed * 1.075f;
|
|
memory->max_acc = memory->base_acc * 1.2f;
|
|
memory->full_anger_time = 20.0f;
|
|
break;
|
|
case DIFFICULTY_HOTSHOT:
|
|
memory->max_speed = memory->base_speed * 1.1f;
|
|
memory->max_acc = memory->base_acc * 1.3f;
|
|
memory->full_anger_time = 18.0f;
|
|
break;
|
|
case DIFFICULTY_ACE:
|
|
memory->max_speed = memory->base_speed * 1.125f;
|
|
memory->max_acc = memory->base_acc * 1.4f;
|
|
memory->full_anger_time = 15.0f;
|
|
break;
|
|
case DIFFICULTY_INSANE:
|
|
memory->max_speed = memory->base_speed * 1.2f;
|
|
memory->max_acc = memory->base_acc * 1.5f;
|
|
memory->full_anger_time = 12.0f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void OldScratch::DoFrame(int me) {
|
|
float see_time;
|
|
AI_Value(me, VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &see_time);
|
|
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (see_time + 4.0f > Game_GetTime() && !used) {
|
|
float speed;
|
|
float acc;
|
|
float ft;
|
|
|
|
ft = Game_GetFrameTime();
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &speed);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &acc);
|
|
|
|
speed += (memory->max_speed - memory->base_speed) * (ft / memory->full_anger_time);
|
|
acc += (memory->max_acc - memory->base_acc) * (ft / memory->full_anger_time);
|
|
|
|
if (speed > memory->max_speed)
|
|
speed = memory->max_speed;
|
|
if (acc > memory->max_acc)
|
|
acc = memory->max_acc;
|
|
|
|
// mprintf(0, "Max Speed is %f\n", speed);
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &acc);
|
|
} else {
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
}
|
|
}
|
|
|
|
bool OldScratch::DoNotify(int me, tOSIRISEVTAINOTIFY *notify) {
|
|
if (notify->notify_type == AIN_MELEE_HIT) {
|
|
int target_handle;
|
|
int target_type;
|
|
int target_flags;
|
|
|
|
AI_Value(me, VF_GET, AIV_I_TARGET_HANDLE, &target_handle);
|
|
Obj_Value(target_handle, VF_GET, OBJV_I_TYPE, &target_type);
|
|
Obj_Value(target_handle, VF_GET, OBJV_I_FLAGS, &target_flags);
|
|
|
|
int diff = Game_GetDiffLevel();
|
|
float chance = (float)rand() / (float)RAND_MAX;
|
|
|
|
if (diff == 0 && chance < 0.60f)
|
|
return true;
|
|
|
|
if (diff == 1 && chance < 0.40f)
|
|
return true;
|
|
|
|
if (target_type == OBJ_PLAYER && (target_flags & OF_DESTROYABLE)) {
|
|
bool f_success;
|
|
f_success = DoSteal(me, target_handle);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int16_t OldScratch::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return (DoNotify(data->me_handle, &data->evt_ai_notify) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (oldscratch_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//---------------------
|
|
// BarnSwallow
|
|
//---------------------
|
|
|
|
void BarnSwallow::ComputeNextNestPnt(int me, vector *pos) {
|
|
pos->x = (0.5f - (float)rand() / (float)RAND_MAX);
|
|
pos->y = (0.5f - (float)rand() / (float)RAND_MAX);
|
|
pos->z = (0.5f - (float)rand() / (float)RAND_MAX);
|
|
|
|
pos->y *= 0.3f;
|
|
|
|
vm_VectorNormalize(pos);
|
|
|
|
*pos = *pos * memory->nest_rad;
|
|
*pos += memory->nest_center;
|
|
}
|
|
|
|
// #define BSCOMM_FOLLOW_ME 0 // send a percent chance so we
|
|
// can have a line sometimes #define BSCOMM_FOLLOW_CANCEL_FROM_FOLLOWER 2 #define
|
|
// BSCOMM_FOLLOW_CANCEL_FROM_LEADER 3
|
|
// #define BSCOMM_ATTACK_MY_TARGET 4
|
|
// BSCOMM_ARE_YOU_IN_MODE
|
|
|
|
bool BarnSwallow::ReceiveCommand(int me, int it, gb_com *command) {
|
|
bool f_ok = true;
|
|
|
|
switch (command->action) {
|
|
case BSCOMM_FOLLOW_ME:
|
|
SetMode(me, BSM_FOLLOW, it);
|
|
break;
|
|
case BSCOMM_FOLLOW_CANCEL_FROM_FOLLOWER:
|
|
memory->follower = OBJECT_HANDLE_NONE;
|
|
break;
|
|
case BSCOMM_FOLLOW_CANCEL_FROM_LEADER: {
|
|
SetMode(me, BSM_RETURN_TO_NEST);
|
|
} break;
|
|
case BSCOMM_ATTACK_MY_TARGET: {
|
|
int flags;
|
|
int room;
|
|
int me_room;
|
|
|
|
Obj_Value(it, VF_GET, OBJV_I_FLAGS, &flags);
|
|
Obj_Value(it, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_I_FLAGS, &me_room);
|
|
|
|
if ((flags & OF_ATTACHED) && rand() % 10 > 3) {
|
|
SetMode(me, BSM_ATTACK, it);
|
|
} else if (me_room == room) {
|
|
SetMode(me, BSM_ATTACK, it);
|
|
} else if (rand() % 10 > 6) {
|
|
SetMode(me, BSM_ATTACK, it);
|
|
} else {
|
|
f_ok = false;
|
|
}
|
|
} break;
|
|
case BSCOMM_ARE_YOU_IN_MODE: {
|
|
f_ok = (memory->mode == command->index);
|
|
} break;
|
|
case BSCOMM_ARE_YOU_AFTER_POWERUP: {
|
|
f_ok = (memory->powerup_id == command->index);
|
|
}
|
|
}
|
|
|
|
return f_ok;
|
|
}
|
|
|
|
bool BarnSwallow::SendCommand(int me, int it, char action, int value) {
|
|
bool f_ok;
|
|
tOSIRISEventInfo data;
|
|
gb_com command;
|
|
|
|
data.me_handle = it;
|
|
data.evt_ai_notify.it_handle = me;
|
|
|
|
command.action = action;
|
|
command.index = value;
|
|
|
|
data.extra_info = (void *)&command;
|
|
data.evt_ai_notify.notify_type = AIN_USER_DEFINED;
|
|
|
|
f_ok = Obj_CallEvent(it, EVT_AI_NOTIFY, &data);
|
|
if (f_ok || command.action == BSCOMM_FOLLOW_CANCEL_FROM_LEADER) {
|
|
switch (command.action) {
|
|
case BSCOMM_FOLLOW_ME:
|
|
memory->follower = it;
|
|
break;
|
|
case BSCOMM_FOLLOW_CANCEL_FROM_FOLLOWER:
|
|
memory->follower = OBJECT_HANDLE_NONE;
|
|
break;
|
|
case BSCOMM_FOLLOW_CANCEL_FROM_LEADER:
|
|
memory->follower = OBJECT_HANDLE_NONE;
|
|
break;
|
|
}
|
|
}
|
|
return f_ok;
|
|
}
|
|
|
|
void BarnSwallow::DoInit(int me) {
|
|
int i;
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(barnswallow_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (barnswallow_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->follower = OBJECT_HANDLE_NONE;
|
|
|
|
ComputeNest(me);
|
|
|
|
memory->next_friend_update_time = Game_GetTime() + 5.0f + ((float)rand() / (float)RAND_MAX) * 3.0f;
|
|
memory->next_attack_time = Game_GetTime();
|
|
memory->next_powerup_time = Game_GetTime() + 5.0f + ((float)rand() / (float)RAND_MAX) * 2.0f;
|
|
|
|
memory->last_non_attack_mode = memory->mode = BSM_NEST;
|
|
memory->mode_time = 0.0f;
|
|
|
|
memory->flags = 0;
|
|
|
|
SetMode(me, BSM_NEST);
|
|
}
|
|
|
|
bool BarnSwallow::DoNotify(int me, tOSIRISEventInfo *data) {
|
|
if (IsGoalFinishedNotify(data->evt_ai_notify.notify_type)) // chrishack -- handle invalid goal cases if I should
|
|
{
|
|
switch (memory->mode) {
|
|
case BSM_NEST: {
|
|
if (data->evt_ai_notify.goal_num == 1) {
|
|
SetMode(me, BSM_NEST);
|
|
return false;
|
|
}
|
|
} break;
|
|
case BSM_GET_POWERUP: {
|
|
if (data->evt_ai_notify.goal_num == 1) {
|
|
SetMode(me, BSM_RETURN_TO_NEST);
|
|
return false;
|
|
}
|
|
} break;
|
|
case BSM_RETURN_TO_NEST: {
|
|
if (data->evt_ai_notify.goal_num == 1) {
|
|
SetMode(me, BSM_NEST);
|
|
return false;
|
|
}
|
|
} break;
|
|
case BSM_ATTACK:
|
|
if (data->evt_ai_notify.goal_num == 1) {
|
|
SetMode(me, BSM_ATTACK_CIRCLE_BACK);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
} else if (data->evt_ai_notify.notify_type == AIN_USER_DEFINED) {
|
|
return ReceiveCommand(me, data->evt_ai_notify.it_handle, (gb_com *)data->extra_info);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BarnSwallow::ComputeNest(int me) {
|
|
memory->num_friends = 0;
|
|
int scan_objs[100];
|
|
int n_scan;
|
|
int i;
|
|
vector pos;
|
|
int room;
|
|
int type;
|
|
uint16_t id;
|
|
|
|
memory->num_friends = 0;
|
|
|
|
for (i = 0; i < BS_MAX_FRIENDS; i++)
|
|
memory->friends[i] = OBJECT_HANDLE_NONE;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_I_TYPE, &type);
|
|
Obj_Value(me, VF_GET, OBJV_US_ID, &id);
|
|
|
|
memory->home_room = room;
|
|
memory->nest_center =
|
|
pos; // Each robot will have a different center point becuase thier initial positions are weighted twice
|
|
memory->nest_rad = 0.0f;
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, 1000.0f, scan_objs, 100, false, true, false, true);
|
|
|
|
for (i = 0; i < n_scan; i++) {
|
|
if (scan_objs[i] != me) {
|
|
int c_type;
|
|
uint16_t c_id;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_I_TYPE, &c_type);
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_US_ID, &c_id);
|
|
|
|
// See if we are the same species
|
|
if (type == c_type && id == c_id) {
|
|
vector c_pos;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_V_POS, &c_pos);
|
|
|
|
memory->nest_center += c_pos;
|
|
|
|
memory->friends[memory->num_friends++] = scan_objs[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (memory->num_friends)
|
|
memory->nest_center /= memory->num_friends + 1;
|
|
|
|
// Determine the farthest distance from the center of the nest
|
|
for (i = 0; i < memory->num_friends; i++) {
|
|
vector c_pos;
|
|
float dist;
|
|
Obj_Value(memory->friends[i], VF_GET, OBJV_V_POS, &c_pos);
|
|
dist = vm_VectorDistance(&memory->nest_center, &c_pos);
|
|
|
|
if (dist * 0.9f > memory->nest_rad)
|
|
memory->nest_rad = dist * 0.9f;
|
|
}
|
|
|
|
if (memory->nest_rad < 5.0f)
|
|
memory->nest_rad = 5.0f;
|
|
}
|
|
|
|
void BarnSwallow::UpdateFriendList(int me) {
|
|
int i;
|
|
int j;
|
|
|
|
for (i = 0; i < memory->num_friends; i++) {
|
|
int type;
|
|
Obj_Value(memory->friends[i], VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_NONE) {
|
|
for (j = i; j < memory->num_friends - 1; j++) {
|
|
memory->friends[j] = memory->friends[j + 1];
|
|
}
|
|
|
|
i--; // Retest as the slot is now being used by what was the next item
|
|
memory->num_friends--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BarnSwallow::DoFrame(int me) {
|
|
if (memory->next_friend_update_time < Game_GetTime()) {
|
|
memory->next_friend_update_time = Game_GetTime() + 5.0f + ((float)rand() / (float)RAND_MAX) * 3.0f;
|
|
|
|
UpdateFriendList(me);
|
|
}
|
|
|
|
switch (memory->mode) {
|
|
case BSM_NEST: {
|
|
if (Game_GetTime() > memory->next_mode_time) {
|
|
int target;
|
|
int room;
|
|
int me_room;
|
|
|
|
AI_Value(me, VF_GET, AIV_I_TARGET_HANDLE, &target);
|
|
|
|
if (target != OBJECT_HANDLE_NONE) {
|
|
Obj_Value(target, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &me_room);
|
|
}
|
|
|
|
if (target != OBJECT_HANDLE_NONE && room == me_room)
|
|
SetMode(me, BSM_ATTACK);
|
|
else
|
|
SetMode(me, BSM_NEST);
|
|
}
|
|
|
|
if (Game_GetTime() > memory->next_powerup_time) {
|
|
int rand_val = rand() % 100;
|
|
|
|
memory->next_powerup_time = Game_GetTime() + 5.0f + ((float)rand() / (float)RAND_MAX) * 2.0f;
|
|
|
|
if (rand_val < 20) // Play with local powerups
|
|
{
|
|
int scan_objs[50];
|
|
int n_scan;
|
|
int i;
|
|
int n_powerups = 0;
|
|
int powerups[50];
|
|
int room;
|
|
vector pos;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, 30.0f, scan_objs, 50, false, false, false, true);
|
|
|
|
for (i = 0; i < n_scan; i++) {
|
|
int type;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_I_TYPE, &type);
|
|
if (type == OBJ_POWERUP) {
|
|
powerups[n_powerups++] = scan_objs[i];
|
|
}
|
|
}
|
|
|
|
if (n_powerups) {
|
|
int index = rand() % n_powerups;
|
|
SetMode(me, BSM_GET_POWERUP, powerups[index]);
|
|
}
|
|
}
|
|
if (rand_val > 95) // Find a remote powerup and go get it
|
|
{
|
|
int i;
|
|
bool f_ok = true;
|
|
|
|
// for(i = 0; i < memory->num_friends; i++)
|
|
// {
|
|
// int room = memory->home_room; // Acounts for dead friends
|
|
// }
|
|
|
|
int p_handle = AI_FindObjOfType(me, OBJ_POWERUP, -1, true);
|
|
|
|
for (i = 0; i < memory->num_friends; i++) {
|
|
if (SendCommand(me, memory->friends[i], BSCOMM_ARE_YOU_AFTER_POWERUP, p_handle)) {
|
|
p_handle = OBJECT_HANDLE_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (p_handle != OBJECT_HANDLE_NONE) {
|
|
SetMode(me, BSM_GET_POWERUP, p_handle);
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
case BSM_FOLLOW: {
|
|
} break;
|
|
case BSM_GET_POWERUP: {
|
|
if (memory->mode_time > memory->max_time_on_road) {
|
|
SetMode(me, BSM_RETURN_TO_NEST);
|
|
}
|
|
} break;
|
|
case BSM_ATTACK: {
|
|
float last_see_target_time;
|
|
float last_hear_target_time;
|
|
AI_Value(me, VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &last_see_target_time);
|
|
AI_Value(me, VF_GET, AIV_F_LAST_HEAR_TARGET_TIME, &last_hear_target_time);
|
|
|
|
if (Game_GetTime() > last_see_target_time + 7.0f && Game_GetTime() > last_hear_target_time + 7.0f)
|
|
SetMode(me, memory->last_non_attack_mode);
|
|
float dist;
|
|
AI_GoalValue(me, 1, VF_GET, AIGV_F_DIST_TO_GOAL, &dist);
|
|
} break;
|
|
case BSM_ATTACK_CIRCLE_BACK: {
|
|
float last_see_target_time;
|
|
float last_hear_target_time;
|
|
AI_Value(me, VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &last_see_target_time);
|
|
AI_Value(me, VF_GET, AIV_F_LAST_HEAR_TARGET_TIME, &last_hear_target_time);
|
|
|
|
if (Game_GetTime() > last_see_target_time + 7.0f && Game_GetTime() > last_hear_target_time + 7.0f)
|
|
SetMode(me, memory->last_non_attack_mode);
|
|
|
|
if (memory->mode_time > 5.0f)
|
|
SetMode(me, BSM_ATTACK);
|
|
} break;
|
|
case BSM_FLEE: {
|
|
} break;
|
|
case BSM_RETURN_TO_NEST: {
|
|
|
|
} break;
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
bool BarnSwallow::SetMode(int me, char mode, int it) {
|
|
// if(mode == BSM_ATTACK && memory->next_attack_time < Game_GetTime())
|
|
// return false;
|
|
int f_attack_flags = (AIF_FIRE | AIF_AUTO_AVOID_FRIENDS);
|
|
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
|
|
if (memory->mode != BSM_ATTACK && memory->mode != BSM_ATTACK_CIRCLE_BACK) {
|
|
memory->last_non_attack_mode = memory->mode;
|
|
|
|
if (mode != BSM_ATTACK && mode != BSM_ATTACK_CIRCLE_BACK)
|
|
memory->powerup_id = OBJECT_HANDLE_NONE;
|
|
} else {
|
|
if (mode == BSM_GET_POWERUP)
|
|
it = memory->powerup_id;
|
|
}
|
|
|
|
// If we are not just floating around in the nest, then reset the next_powerup_time
|
|
if (mode == BSM_NEST && memory->mode != BSM_NEST)
|
|
memory->next_powerup_time = Game_GetTime() + 5.0f + ((float)rand() / (float)RAND_MAX) * 2.0f;
|
|
|
|
if (memory->mode == BSM_RETURN_TO_NEST) {
|
|
Obj_UnattachChildren(me);
|
|
memory->powerup_id = OBJECT_HANDLE_NONE;
|
|
}
|
|
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
switch (mode) {
|
|
case BSM_NEST: {
|
|
int room;
|
|
float max_speed;
|
|
vector pos;
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &max_speed);
|
|
float update_time = memory->nest_rad / (max_speed) + 0.3f - ((float)rand() / (float)RAND_MAX);
|
|
if (update_time < .6f)
|
|
update_time = .6f;
|
|
|
|
memory->next_mode_time = Game_GetTime() + update_time;
|
|
|
|
ComputeNextNestPnt(me, &pos);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 1, 1.0, -1, GF_ORIENT_VELOCITY | GF_USE_BLINE_IF_SEES_GOAL, &pos, memory->home_room);
|
|
AI_SetGoalCircleDist(me, 1, 10.0f);
|
|
} break;
|
|
case BSM_FOLLOW: {
|
|
} break;
|
|
case BSM_GET_POWERUP: {
|
|
float dist = 20.0f;
|
|
|
|
AI_AddGoal(me, AIG_ATTACH_TO_OBJ, 1, 1.0f, -1, GF_ORIENT_VELOCITY, it, 0, 0, 0.9f, 0, 1);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
// Make the barnswallow return home after awhile -- chrishack -- check vis before returning!
|
|
memory->max_time_on_road = 12.0f + (float)rand() / (float)RAND_MAX * 20.0f;
|
|
memory->powerup_id = it;
|
|
} break;
|
|
case BSM_ATTACK: {
|
|
float dist = 40.0f + ((float)rand() / (float)RAND_MAX) * 10.0f;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
|
|
memory->next_attack_time = Game_GetTime() + 10.0 * ((float)rand() / (float)RAND_MAX);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1,
|
|
GF_SPEED_ATTACK | GF_ORIENT_VELOCITY | GF_OBJ_IS_TARGET | GF_USE_BLINE_IF_SEES_GOAL, OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
dist = 40.0f;
|
|
int g_index = AI_AddGoal(me, AIG_GET_AROUND_OBJ, ACTIVATION_BLEND_LEVEL, 1.0f, -1,
|
|
GF_ORIENT_VELOCITY | GF_OBJ_IS_TARGET, OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, g_index, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
} break;
|
|
case BSM_ATTACK_CIRCLE_BACK: {
|
|
vector pos;
|
|
int room;
|
|
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
AI_AddGoal(me, AIG_WANDER_AROUND, 1, 1.0f, -1,
|
|
GF_SPEED_FLEE | GF_ORIENT_VELOCITY | GF_NOTIFIES | GF_KEEP_AT_COMPLETION, &pos, room);
|
|
|
|
float dist = 40.0f;
|
|
int g_index = AI_AddGoal(me, AIG_GET_AROUND_OBJ, ACTIVATION_BLEND_LEVEL, 1.0f, -1,
|
|
GF_ORIENT_VELOCITY | GF_OBJ_IS_TARGET, OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, g_index, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
} break;
|
|
case BSM_FLEE: {
|
|
} break;
|
|
case BSM_RETURN_TO_NEST: {
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 1, 1.0, -1, GF_SPEED_FLEE | GF_ORIENT_VELOCITY | GF_USE_BLINE_IF_SEES_GOAL,
|
|
&memory->nest_center, memory->home_room);
|
|
AI_SetGoalCircleDist(me, 1, 5.0f);
|
|
} break;
|
|
}
|
|
|
|
memory->mode = mode;
|
|
|
|
memory->mode_time = 0.0f;
|
|
return true;
|
|
}
|
|
|
|
int16_t BarnSwallow::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_NOTIFY:
|
|
return (DoNotify(data->me_handle, data) != false) ? CONTINUE_CHAIN | CONTINUE_DEFAULT : 0;
|
|
break;
|
|
case EVT_DESTROY:
|
|
break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (barnswallow_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//===================
|
|
// GBPowerup
|
|
//===================
|
|
|
|
void GBPowerup::DoInit(int me) {
|
|
int i;
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(gbpowerup_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (gbpowerup_data *)Scrpt_MemAlloc(&ch);
|
|
memory->next_check_time = Game_GetTime() + (float)rand() / (float)RAND_MAX; // Sead the powerups differently :)
|
|
memory->time_till_next_hud_message = 0.0f;
|
|
|
|
uint16_t short_id[6];
|
|
|
|
short_id[0] = Obj_FindID("Buddyextinguisher");
|
|
short_id[1] = Obj_FindID("buddywingnut");
|
|
short_id[2] = Obj_FindID("buddycontrol");
|
|
short_id[3] = Obj_FindID("buddyassist");
|
|
short_id[4] = Obj_FindID("buddyantivirus");
|
|
short_id[5] = Obj_FindID("buddyspeed");
|
|
|
|
uint16_t id;
|
|
Obj_Value(me, VF_GET, OBJV_US_ID, &id);
|
|
|
|
memory->type = 5; // Forces it to the speed powerup if it didn't know what it was...
|
|
for (i = 0; i < 6; i++) {
|
|
if (id == short_id[i]) {
|
|
memory->type = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GBPowerup::DoFrame(int me) {
|
|
if (memory->next_check_time <= Game_GetTime()) {
|
|
vector pos;
|
|
int room;
|
|
int scan_objs[25];
|
|
int n_scan;
|
|
int i;
|
|
|
|
memory->next_check_time = Game_GetTime() + .2f + ((float)rand() / (float)RAND_MAX) * .3f;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, 5.0f, scan_objs, 25, false, true, false, true);
|
|
|
|
for (i = 0; i < n_scan; i++) {
|
|
int type;
|
|
int16_t id;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_I_TYPE, &type);
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_US_ID, &id);
|
|
|
|
if (type == OBJ_ROBOT && (id == ROBOT_GUIDEBOT || id == ROBOT_GUIDEBOTRED)) {
|
|
vector g_pos;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_V_POS, &g_pos);
|
|
|
|
float dist = vm_VectorDistance(&pos, &g_pos);
|
|
|
|
if (dist < 5.0f) {
|
|
gb_com command;
|
|
command.action = COM_POWERUP_PICKUP;
|
|
command.index = memory->type;
|
|
|
|
tOSIRISEventInfo data;
|
|
data.me_handle = scan_objs[i];
|
|
data.evt_ai_notify.it_handle = me;
|
|
|
|
data.extra_info = (void *)&command;
|
|
data.evt_ai_notify.notify_type = AIN_USER_DEFINED;
|
|
|
|
Obj_CallEvent(scan_objs[i], EVT_AI_NOTIFY, &data);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int16_t GBPowerup::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_INTERVAL:
|
|
memory->time_till_next_hud_message -= Game_GetFrameTime();
|
|
break;
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_AI_FRAME:
|
|
DoFrame(data->me_handle);
|
|
break;
|
|
case EVT_COLLIDE: {
|
|
if (memory->time_till_next_hud_message <= 0.0f) {
|
|
int type;
|
|
|
|
memory->time_till_next_hud_message = 1.0f;
|
|
Obj_Value(data->evt_collide.it_handle, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_PLAYER) {
|
|
Player_AddHudMessage(data->evt_collide.it_handle, TXT_GB_POWERUP);
|
|
}
|
|
}
|
|
} break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (gbpowerup_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Sparky class
|
|
//------------------
|
|
|
|
bool Sparky::DoNotify(int me_handle, tOSIRISEventInfo *data) {
|
|
// NOTE: I am also doing the rotational stuff here... So, no need to make a seperate 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_handle, VF_GET, OBJV_V_ROTVELOCITY, &rvel);
|
|
|
|
if (memory->spin_dir == SPARKY_ROT_LEFT) {
|
|
rvel.y -= DEATH_ROT_ACC * Game_GetFrameTime();
|
|
} else {
|
|
rvel.y += DEATH_ROT_ACC * Game_GetFrameTime();
|
|
}
|
|
|
|
Obj_Value(me_handle, VF_SET, OBJV_V_ROTVELOCITY, &rvel);
|
|
|
|
vector dir = Zero_vector;
|
|
AI_Value(me_handle, VF_SET, AIV_V_MOVEMENT_DIR, &dir);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Sparky::SetMode(int me, char mode) {
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
switch (mode) {
|
|
case SPARKY_NORMAL: {
|
|
Obj_Value(me, VF_SET, OBJV_F_ROTDRAG, &memory->drag);
|
|
AI_SetType(me, AIT_MELEE1);
|
|
} break;
|
|
case SPARKY_DYING: {
|
|
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);
|
|
} break;
|
|
}
|
|
|
|
memory->last_spark_time = Game_GetTime();
|
|
memory->spin_time = 0.0f;
|
|
memory->mode_time = 0.0f;
|
|
memory->mode = mode;
|
|
}
|
|
|
|
void Sparky::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(sparky_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (sparky_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->f_rotation_death_enabled = true;
|
|
memory->matcen_id = -1;
|
|
memory->mode = SPARKY_NORMAL;
|
|
memory->mode_time = 0.0f;
|
|
memory->spin_dir = SPARKY_NO_ROT;
|
|
memory->spin_time = 0.0f;
|
|
|
|
Obj_Value(me, VF_SET, OBJV_F_ROTDRAG, &memory->drag);
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &memory->orient);
|
|
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = me;
|
|
mstruct.g1 = 0;
|
|
mstruct.g2 = 1;
|
|
mstruct.lifetime = 1000000000.0f;
|
|
mstruct.size = 5.1f;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &mstruct.roomnum);
|
|
Obj_GetGunPos(me, 0, &mstruct.pos);
|
|
Obj_GetGunPos(me, 1, &mstruct.pos2);
|
|
|
|
mstruct.state = 0;
|
|
mstruct.interval = 0.5f;
|
|
mstruct.count = 2;
|
|
mstruct.index = 2;
|
|
mstruct.texnum = Scrpt_FindTextureName("FunkyEffect2");
|
|
// rbg
|
|
mstruct.color = ((128 >> 3) << 10) | ((128 >> 3) << 5) | (128 >> 3);
|
|
mstruct.flags = 1;
|
|
MSafe_CallFunction(MSAFE_WEATHER_LIGHTNING_BOLT, &mstruct);
|
|
|
|
memory->last_spark_time = Game_GetTime();
|
|
memory->spin_time = 0.0f;
|
|
memory->mode_time = 0.0f;
|
|
}
|
|
|
|
void Sparky::DoFrame(int me) {
|
|
|
|
if (memory->mode == SPARKY_NORMAL) {
|
|
char spin_dir;
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
float dot = orient.rvec * memory->orient.rvec;
|
|
if (dot < -1.0f)
|
|
dot = -1.0f;
|
|
else if (dot > 1.0f)
|
|
dot = 1.0f;
|
|
|
|
float ang = acos(dot);
|
|
|
|
float aps = (ang * 65535.0f) / (Game_GetFrameTime() * (2.0f * PI));
|
|
|
|
if (aps >= MIN_MALF_SPEED) {
|
|
float tdot = memory->orient.rvec * orient.fvec;
|
|
if (tdot < 0.0f)
|
|
spin_dir = SPARKY_ROT_LEFT;
|
|
else if (tdot > 0.0f)
|
|
spin_dir = SPARKY_ROT_RIGHT;
|
|
else
|
|
spin_dir = SPARKY_NO_ROT;
|
|
} else {
|
|
spin_dir = SPARKY_NO_ROT;
|
|
}
|
|
|
|
if (spin_dir == memory->spin_dir) {
|
|
if (spin_dir == SPARKY_NO_ROT)
|
|
memory->spin_time = 0.0f;
|
|
else
|
|
memory->spin_time += Game_GetFrameTime();
|
|
} else {
|
|
memory->spin_dir = spin_dir;
|
|
memory->spin_time = 0.0f;
|
|
}
|
|
|
|
memory->spin_dir = spin_dir;
|
|
memory->orient = orient;
|
|
|
|
// mprintf(0, "spin time %f\n", memory->spin_time);
|
|
|
|
if (memory->spin_time > MALF_TIME) {
|
|
SetMode(me, SPARKY_DYING);
|
|
}
|
|
} else {
|
|
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(5, &pos, room);
|
|
}
|
|
|
|
if (memory->mode_time < 2.5 && memory->mode_time + Game_GetFrameTime() >= 2.5) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.random = 21;
|
|
mstruct.is_real = 0;
|
|
mstruct.objhandle = me;
|
|
mstruct.gunpoint = 1;
|
|
mstruct.effect_type = MED_SMOKE_INDEX;
|
|
mstruct.phys_info = 0;
|
|
mstruct.drag = .001f;
|
|
mstruct.mass = .001f;
|
|
mstruct.interval = .05f;
|
|
mstruct.longevity = 100000.0f;
|
|
mstruct.lifetime = 1.0;
|
|
mstruct.size = 8.0f;
|
|
mstruct.speed = 120.0;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_START_SPEW, &mstruct);
|
|
}
|
|
|
|
if (memory->mode_time > DEATH_TIME) {
|
|
Obj_Kill(me, OBJECT_HANDLE_NONE, 1000.0f,
|
|
DF_BLAST_RING | DF_LOSES_ANTIGRAV | DF_EXPL_MEDIUM | DF_FIREBALL | DF_BREAKS_APART | DF_DEBRIS_SMOKES,
|
|
0.0f, 0.0f);
|
|
}
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
int16_t Sparky::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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_MEMRESTORE: {
|
|
memory = (sparky_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Hellion class
|
|
//------------------
|
|
|
|
bool Hellion::DoNotify(int me, tOSIRISEventInfo *data) {
|
|
switch (memory->mode) {
|
|
case HELLION_WAIT: {
|
|
if (data->evt_ai_notify.notify_type == AIN_MOVIE_START) {
|
|
SetMode(me, HELLION_INTRO);
|
|
}
|
|
} break;
|
|
case HELLION_INTRO: {
|
|
if (data->evt_ai_notify.notify_type == AIN_MOVIE_END) {
|
|
SetMode(me, HELLION_MATTER);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Hellion::RemapAlert(int me, float start, float end, float time) {
|
|
memory->alert_start = start;
|
|
memory->alert_end = end;
|
|
memory->alert_time = time;
|
|
|
|
Obj_Value(me, VF_SET, OBJSV_F_ANIM_START, &start, AS_ALERT);
|
|
Obj_Value(me, VF_SET, OBJSV_F_ANIM_END, &end, AS_ALERT);
|
|
Obj_Value(me, VF_SET, OBJSV_F_ANIM_TIME, &time, AS_ALERT);
|
|
}
|
|
|
|
void Hellion::RemapWB(int me, float start, float fire, int fire_sound, float end, float time, float latency, int index,
|
|
uint16_t w_id, char f_mask) {
|
|
memory->start = start;
|
|
memory->fire = fire;
|
|
memory->end = end;
|
|
memory->time = time;
|
|
memory->latency = latency;
|
|
memory->index = index;
|
|
memory->f_mask = f_mask;
|
|
|
|
Obj_WBValue(me, 0, VF_SET, WBSV_F_ANIM_START, &start, index);
|
|
Obj_WBValue(me, 0, VF_SET, WBSV_F_ANIM_FIRE, &fire, index);
|
|
Obj_WBValue(me, 0, VF_SET, WBSV_I_FIRE_SOUND, &fire_sound, index);
|
|
Obj_WBValue(me, 0, VF_SET, WBSV_F_ANIM_END, &end, index);
|
|
Obj_WBValue(me, 0, VF_SET, WBSV_F_ANIM_TIME, &time, index);
|
|
Obj_WBValue(me, 0, VF_SET, WBSV_F_LATENCY, &latency, index);
|
|
Obj_WBValue(me, 0, VF_SET, WBSV_C_MASK, &f_mask, 0);
|
|
|
|
char num_gps;
|
|
Obj_WBValue(me, 0, VF_GET, WBV_C_NUM_GUNPTS, &num_gps);
|
|
|
|
int i;
|
|
for (i = 0; i < num_gps; i++) {
|
|
Obj_WBValue(me, 0, VF_SET, WBSV_US_GUNPT_WEAPON_ID, &w_id, i);
|
|
}
|
|
}
|
|
|
|
void Hellion::SetMode(int me, char mode) {
|
|
AI_SetType(me, AIT_EVADER1);
|
|
|
|
switch (memory->mode) {
|
|
case HELLION_MATTER:
|
|
case HELLION_ENERGY:
|
|
case HELLION_FIRE:
|
|
case HELLION_SLEEP:
|
|
memory->next_mode = mode;
|
|
mode = HELLION_BETWEEN_MODE;
|
|
break;
|
|
}
|
|
|
|
switch (mode) {
|
|
case HELLION_BETWEEN_MODE: {
|
|
RemapAlert(me, 1.0f, 10.0f, 1.0f);
|
|
|
|
int flags = AIF_DISABLE_FIRING;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
switch (memory->mode) {
|
|
case HELLION_MATTER:
|
|
Obj_SetCustomAnim(me, 120.0f, 140.0f, 2.5f, AIAF_NOTIFY, Sound_FindId("RbtHellionMatterToAlert"), AS_ALERT);
|
|
break;
|
|
case HELLION_ENERGY:
|
|
Obj_SetCustomAnim(me, 200.0f, 240.0f, 5.0f, AIAF_NOTIFY, Sound_FindId("RbtHellionEnergyToAlert"), AS_ALERT);
|
|
break;
|
|
case HELLION_FIRE:
|
|
Obj_SetCustomAnim(me, 62.0f, 80.0f, 2.3f, AIAF_NOTIFY, Sound_FindId("RbtHellionNapalmToAlert"), AS_ALERT);
|
|
break;
|
|
case HELLION_SLEEP:
|
|
Obj_SetCustomAnim(me, 338.0f, 353.0f, 1.7f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
break;
|
|
}
|
|
} break;
|
|
|
|
case HELLION_MATTER:
|
|
RemapAlert(me, 375.0f, 384.0f, 1.0f);
|
|
RemapWB(me, 100.0f, 120.0f, Sound_FindId("fragA2"), 120.0f, 2.5f, 0.5f, 0, Wpn_FindID("HellionFrag"),
|
|
(1 << 1) | (1 << 2) | (1 << 3) | (1 << 4));
|
|
strcpy(memory->sound, "fragA2");
|
|
strcpy(memory->weapon, "HellionFrag");
|
|
Obj_SetCustomAnim(me, 80.0f, 100.0f, 2.7f, AIAF_NOTIFY, Sound_FindId("RbtHellionAlertToMatter"), AS_ALERT);
|
|
break;
|
|
case HELLION_FIRE:
|
|
RemapAlert(me, 360.0f, 369.0f, 1.0f);
|
|
RemapWB(me, 48.0f, 62.0f, -1, 62.0f, 2.0f, 0.1f, 0, -1, 0);
|
|
Obj_SetCustomAnim(me, 10.0f, 48.0f, 5.0f, AIAF_NOTIFY, Sound_FindId("RbtHellionAlertToNapalm"), AS_ALERT);
|
|
break;
|
|
case HELLION_ENERGY:
|
|
RemapAlert(me, 390.0f, 400.0f, 1.0f);
|
|
RemapWB(me, 179.0f, 200.0f, Sound_FindId("Mega missile fire"), 200.0f, 2.7f, 0.9f, 0, Wpn_FindID("HellionMega"),
|
|
(1 << 1) | (1 << 2) | (1 << 3) | (1 << 4));
|
|
strcpy(memory->sound, "Mega missile fire");
|
|
strcpy(memory->weapon, "HellionMega");
|
|
Obj_SetCustomAnim(me, 140.0f, 179.0f, 5.0f, AIAF_NOTIFY, Sound_FindId("RbtHellionAlertToEnergy"), AS_ALERT);
|
|
break;
|
|
case HELLION_SLEEP:
|
|
RemapAlert(me, 271.0f, 289.0f, 2.5);
|
|
Obj_SetCustomAnim(me, 240.0f, 271.0f, 4.0f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
break;
|
|
case HELLION_BIRTHING:
|
|
Obj_SetCustomAnim(me, 289.0f, 338.0f, 6.5f, AIAF_NOTIFY, -1, AS_ALERT);
|
|
break;
|
|
case HELLION_DEATH: {
|
|
int flags = AIF_DISABLE_FIRING | AIF_DISABLE_MELEE;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
flags = AIF_FIRE;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
vector vel = {0.0f, 0.0f, 0.0f};
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
|
|
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("RbtHellionDeath"), -1);
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
memory->mode_time = 0.0f;
|
|
memory->mode = mode;
|
|
}
|
|
|
|
void Hellion::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(hellion_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
int i;
|
|
|
|
memory = (hellion_data *)Scrpt_MemAlloc(&ch);
|
|
memory->flags = 0;
|
|
memory->mode = HELLION_WAIT;
|
|
|
|
memory->head_object = CreateAndAttach(me, "Hellionhead", OBJ_ROBOT, 0, 0, true, true);
|
|
memory->turret_object = CreateAndAttach(me, "Hellionturret", OBJ_ROBOT, 1, 0, true, true);
|
|
RemapAlert(me, 1.0f, 10.0f, 1.0f);
|
|
|
|
int proom;
|
|
vector ppos;
|
|
matrix porient;
|
|
|
|
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);
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
memory->camera[i] = Obj_Create(OBJ_POWERUP, Obj_FindID("Invisiblepowerup"), proom, &ppos, &porient, me);
|
|
msafe_struct mstruct;
|
|
mstruct.objhandle = memory->camera[i];
|
|
MSafe_CallFunction(MSAFE_OBJECT_NO_RENDER, &mstruct);
|
|
|
|
memory->emitter[i] = CreateAndAttach(me, "STEmitter", OBJ_ROBOT, 2 + i, 0, true, true);
|
|
}
|
|
|
|
memory->laser_sound = -1;
|
|
memory->last_frame = 0.0f;
|
|
memory->laser_time_left = 0.0f;
|
|
|
|
// Save and restore information
|
|
Obj_Value(me, VF_GET, OBJSV_F_ANIM_START, &memory->alert_start, AS_ALERT);
|
|
Obj_Value(me, VF_GET, OBJSV_F_ANIM_END, &memory->alert_end, AS_ALERT);
|
|
Obj_Value(me, VF_GET, OBJSV_F_ANIM_TIME, &memory->alert_time, AS_ALERT);
|
|
|
|
Obj_WBValue(me, 0, VF_GET, WBSV_F_ANIM_START, &memory->start, 0);
|
|
Obj_WBValue(me, 0, VF_GET, WBSV_F_ANIM_FIRE, &memory->fire, 0);
|
|
Obj_WBValue(me, 0, VF_GET, WBSV_F_ANIM_END, &memory->end, 0);
|
|
Obj_WBValue(me, 0, VF_GET, WBSV_F_ANIM_TIME, &memory->time, 0);
|
|
Obj_WBValue(me, 0, VF_GET, WBSV_F_LATENCY, &memory->latency, 0);
|
|
Obj_WBValue(me, 0, VF_GET, WBSV_C_MASK, &memory->f_mask, 0);
|
|
memory->index = 0;
|
|
|
|
strcpy(memory->sound, "fragA2");
|
|
strcpy(memory->weapon, "HellionFrag");
|
|
// end of save and restore info
|
|
|
|
int flags = AIF_DETERMINE_TARGET;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
SetMode(me, HELLION_WAIT);
|
|
}
|
|
|
|
void Hellion::DoFrame(int me) {
|
|
int flags;
|
|
float frame;
|
|
float shields;
|
|
|
|
char anim_type;
|
|
AI_Value(me, VF_GET, AIV_C_ANIMATION_TYPE, &anim_type);
|
|
|
|
bool f_ok_to_animate = (anim_type != AS_RANGED_ATTACK && anim_type != AS_MELEE1 && anim_type != AS_MELEE2);
|
|
|
|
Obj_Value(memory->head_object, VF_GET, OBJV_I_FLAGS, &flags);
|
|
Obj_Value(me, VF_GET, OBJV_F_ANIM_FRAME, &frame);
|
|
Obj_Value(memory->head_object, VF_GET, OBJV_F_SHIELDS, &shields);
|
|
|
|
// Matter fire animation sound
|
|
if ((memory->last_frame < 100.0f || memory->last_frame > 120.0f) && frame >= 100.0f && frame <= 120.0f) {
|
|
Sound_Play3d(me, Sound_FindId("RbtHellionMatterAttack"));
|
|
}
|
|
|
|
if (memory->laser_sound != -1 && (frame < 48.0f || frame > 60.0f)) {
|
|
memory->laser_sound = -1;
|
|
}
|
|
|
|
// Laser firing sound
|
|
if (frame >= 48.0f && frame <= 60.0f && memory->laser_sound == -1) {
|
|
memory->laser_sound = Sound_Play3d(me, Sound_FindId("RbtHellionLaser"));
|
|
}
|
|
|
|
int cur_mtype;
|
|
Obj_Value(me, VF_GET, OBJV_C_MOVEMENT_TYPE, &cur_mtype);
|
|
|
|
if (cur_mtype == MT_PHYSICS && frame >= 140.0 && frame <= 179.0f) {
|
|
char mtype = MT_NONE;
|
|
Obj_Value(me, VF_SET, OBJV_C_MOVEMENT_TYPE, &mtype);
|
|
} else if (cur_mtype == MT_NONE) {
|
|
char mtype = MT_PHYSICS;
|
|
Obj_Value(me, VF_SET, OBJV_C_MOVEMENT_TYPE, &mtype);
|
|
}
|
|
|
|
if (memory->last_frame >= 140.0f && memory->last_frame <= 179.0f && memory->last_frame < 146.0f && frame >= 146.0f) {
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
int room;
|
|
vector pos;
|
|
int weapon_id;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
pos += orient.uvec * 10.0f;
|
|
|
|
matrix new_orient;
|
|
|
|
new_orient.fvec = -orient.uvec;
|
|
new_orient.rvec = orient.rvec;
|
|
new_orient.uvec = orient.fvec;
|
|
|
|
weapon_id = Wpn_FindID("HellionBShark");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, &new_orient, OBJECT_HANDLE_NONE);
|
|
}
|
|
|
|
if (memory->last_frame >= 140.0f && memory->last_frame <= 179.0f && memory->last_frame < 160.0f && frame >= 160.0f) {
|
|
// shake players
|
|
msafe_struct mstruct;
|
|
mstruct.amount = 65.0f;
|
|
MSafe_CallFunction(MSAFE_OBJECT_VIEWER_SHAKE, &mstruct);
|
|
}
|
|
|
|
if (memory->laser_time_left > 0.0f) {
|
|
int i;
|
|
ray_info ray;
|
|
|
|
memory->laser_time_left -= Game_GetFrameTime();
|
|
if (memory->laser_time_left < 0.0f) {
|
|
memory->laser_time_left = 0.0f;
|
|
}
|
|
|
|
vector normal;
|
|
vector pos;
|
|
int room;
|
|
vector end_pos;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
int my_room;
|
|
vector my_pos;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &my_pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &my_room);
|
|
Obj_GetGunPos(me, i + 1, &pos, &normal);
|
|
|
|
int rfate = FVI_RayCast(me, &my_pos, &pos, my_room, 0.0f, 0, &ray);
|
|
room = ray.hit_room;
|
|
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
// Determine real start pos - room
|
|
vector end_pos = pos;
|
|
end_pos += normal * 2000.0f;
|
|
|
|
int fvi_flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS | FQ_IGNORE_WEAPONS;
|
|
int fate = FVI_RayCast(me, &pos, &end_pos, room, 0.0f, fvi_flags, &ray);
|
|
|
|
Obj_Value(memory->camera[i], VF_SET, OBJV_V_POS, &ray.hit_point);
|
|
Obj_Value(memory->camera[i], VF_SET, OBJV_I_ROOMNUM, &ray.hit_room);
|
|
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.random = 21;
|
|
mstruct.is_real = 0;
|
|
mstruct.objhandle = memory->camera[i];
|
|
mstruct.gunpoint = -1;
|
|
mstruct.effect_type = SNOWFLAKE_INDEX;
|
|
mstruct.phys_info = 0;
|
|
mstruct.drag = .001f;
|
|
mstruct.mass = .001f;
|
|
mstruct.interval = .05f;
|
|
mstruct.longevity = .1f;
|
|
mstruct.lifetime = .1f;
|
|
mstruct.size = 5.0f;
|
|
mstruct.speed = 80.0;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_START_SPEW, &mstruct);
|
|
|
|
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) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = ray.hit_object;
|
|
mstruct.killer_handle = me;
|
|
mstruct.damage_type = PD_ENERGY_WEAPON;
|
|
mstruct.amount = 30.0f * Game_GetFrameTime();
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start up the beem!
|
|
if (frame >= 49.0f && frame <= 55.0f && memory->laser_time_left == 0.0f) {
|
|
memory->laser_time_left = 1.8f;
|
|
int i;
|
|
msafe_struct mstruct;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
Obj_Value(memory->emitter[i], VF_GET, OBJV_I_ROOMNUM, &mstruct.roomnum);
|
|
Obj_Value(memory->emitter[i], VF_GET, OBJV_V_POS, &mstruct.pos);
|
|
Obj_Value(memory->camera[i], VF_GET, OBJV_V_POS, &mstruct.pos2);
|
|
|
|
mstruct.objhandle = memory->emitter[i];
|
|
mstruct.ithandle = memory->camera[i];
|
|
mstruct.lifetime = memory->laser_time_left;
|
|
mstruct.size = 3.0f;
|
|
|
|
mstruct.interval = 1.0f;
|
|
mstruct.count = 2;
|
|
mstruct.index = 1;
|
|
mstruct.texnum = Scrpt_FindTextureName("HellionBeam");
|
|
mstruct.color = ((128 >> 3) << 10) | ((128 >> 3) << 5) | (128 >> 3);
|
|
|
|
mstruct.state = 1;
|
|
mstruct.flags = 0;
|
|
|
|
MSafe_CallFunction(MSAFE_WEATHER_LIGHTNING_BOLT, &mstruct);
|
|
|
|
mstruct.random = 21;
|
|
mstruct.is_real = 0;
|
|
mstruct.objhandle = me;
|
|
mstruct.gunpoint = 1 + i;
|
|
mstruct.effect_type = MED_SMOKE_INDEX;
|
|
mstruct.phys_info = 0;
|
|
mstruct.drag = .001f;
|
|
mstruct.mass = .001f;
|
|
mstruct.interval = .05f;
|
|
mstruct.longevity = memory->laser_time_left;
|
|
mstruct.lifetime = 1.0;
|
|
mstruct.size = 5.0f;
|
|
mstruct.speed = 80.0;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_START_SPEW, &mstruct);
|
|
}
|
|
}
|
|
|
|
if ((memory->mode != HELLION_DEATH) && (flags & OF_AI_DEATH)) {
|
|
if (f_ok_to_animate) {
|
|
SetMode(me, HELLION_DEATH);
|
|
}
|
|
}
|
|
|
|
if (memory->mode == HELLION_WAIT) {
|
|
} else if (memory->mode == HELLION_INTRO) {
|
|
if (memory->mode_time < 0.5f && memory->mode_time + Game_GetFrameTime() >= 0.5f) {
|
|
int 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);
|
|
|
|
aDestroyAllRobotsInit();
|
|
aDestroyAllRobotsSpareHandle(me);
|
|
aDestroyAllRobotsSpareHandle(memory->turret_object);
|
|
aDestroyAllRobotsSpareHandle(memory->head_object);
|
|
aDestroyAllRobotsSpareHandle(memory->emitter[0]);
|
|
aDestroyAllRobotsSpareHandle(memory->emitter[1]);
|
|
aDestroyAllRobotsEnd();
|
|
}
|
|
} else if (memory->mode == HELLION_BETWEEN_MODE) {
|
|
if (frame >= 0.0f && frame <= 10.0f) {
|
|
SetMode(me, memory->next_mode);
|
|
}
|
|
} else if (memory->mode == HELLION_DEATH) {
|
|
if (memory->mode_time < 10.0f && memory->mode_time + Game_GetFrameTime() >= 10.0f) {
|
|
Obj_Burning(me, 1000000.0f, 1.0f);
|
|
}
|
|
|
|
if (frame == 650.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);
|
|
}
|
|
} else if (memory->mode == HELLION_MATTER) {
|
|
if (frame >= 375.0f && frame <= 384.0f) {
|
|
int flags = AIF_DETERMINE_TARGET;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
flags = AIF_DISABLE_FIRING;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
}
|
|
|
|
if (shields < 4000.0f) {
|
|
if (f_ok_to_animate) {
|
|
SetMode(me, HELLION_FIRE);
|
|
}
|
|
}
|
|
} else if (memory->mode == HELLION_FIRE) {
|
|
if (frame >= 360.0f && frame <= 369.0f) {
|
|
int flags = AIF_DISABLE_FIRING;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
}
|
|
|
|
if (shields < 2100.0f) {
|
|
if (f_ok_to_animate) {
|
|
SetMode(me, HELLION_ENERGY);
|
|
}
|
|
}
|
|
} else if (memory->mode == HELLION_ENERGY) {
|
|
if (frame >= 390.0f && frame <= 400.0f) {
|
|
int test_flags;
|
|
AI_Value(me, VF_GET, AIV_I_FLAGS, &test_flags);
|
|
|
|
if (test_flags & AIF_DISABLE_FIRING) {
|
|
int flags = AIF_DISABLE_FIRING;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
/* int room;
|
|
vector pos;
|
|
int weapon_id;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
weapon_id = Wpn_FindID("HellionBShark");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, NULL, NULL);*/
|
|
}
|
|
}
|
|
}
|
|
// else if(memory->mode == HELLION_SLEEP)
|
|
// {
|
|
// int offset = (memory->mode - HELLION_MATTER + 1)%4;
|
|
// SetMode(me, HELLION_MATTER + offset);
|
|
// }
|
|
else if (memory->mode_time > 20.0f) {
|
|
if (memory->mode == HELLION_MATTER || memory->mode == HELLION_ENERGY || memory->mode == HELLION_FIRE ||
|
|
memory->mode == HELLION_SLEEP) {
|
|
int offset = (memory->mode - HELLION_MATTER + 1) % 4;
|
|
SetMode(me, HELLION_MATTER + offset);
|
|
}
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
memory->last_frame = frame;
|
|
}
|
|
|
|
int16_t Hellion::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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->head_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);
|
|
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;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
mo.objhandle = memory->camera[i];
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
|
|
mo.objhandle = memory->emitter[i];
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
}
|
|
} break;
|
|
case EVT_MEMRESTORE: {
|
|
memory = (hellion_data *)data->evt_memrestore.memory_ptr;
|
|
|
|
uint16_t wpn = Wpn_FindID(memory->weapon);
|
|
int snd = Sound_FindId(memory->sound);
|
|
|
|
RemapAlert(data->me_handle, memory->alert_start, memory->alert_end, memory->alert_time);
|
|
RemapWB(data->me_handle, memory->start, memory->fire, snd, memory->end, memory->time, memory->latency,
|
|
memory->index, wpn, memory->f_mask);
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// MantaRay class
|
|
//------------------
|
|
|
|
bool MantaRay::SendCommand(int me, int it, char command, void *ptr) {
|
|
gb_com com;
|
|
|
|
com.action = command;
|
|
com.ptr = ptr;
|
|
|
|
tOSIRISEventInfo ei;
|
|
|
|
ei.me_handle = it;
|
|
ei.extra_info = (void *)&com;
|
|
ei.evt_ai_notify.notify_type = AIN_USER_DEFINED;
|
|
ei.evt_ai_notify.it_handle = me;
|
|
|
|
return Obj_CallEvent(it, EVT_AI_NOTIFY, &ei);
|
|
}
|
|
|
|
bool MantaRay::ReceiveCommand(int me, int it, char command, void *ptr) {
|
|
switch (command) {
|
|
case MRC_JOIN_ME: {
|
|
if (memory->flags == 0) {
|
|
memory->leader_handle = it;
|
|
memory->flags |= MRF_SQUADIE;
|
|
|
|
SetMode(me, memory->mode);
|
|
|
|
return true;
|
|
}
|
|
} break;
|
|
case MRC_LEAVE_YOU:
|
|
break;
|
|
case MRC_GET_GOAL_POS: {
|
|
int i;
|
|
for (i = 0; i < memory->num_teammates; i++) {
|
|
if (memory->teammate[i] == it) {
|
|
vector goal_pos;
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &goal_pos);
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
switch (i) {
|
|
case 0:
|
|
goal_pos -= orient.fvec * MRO_FVEC;
|
|
goal_pos -= orient.rvec * MRO_RVEC;
|
|
break;
|
|
case 1:
|
|
goal_pos -= orient.fvec * MRO_FVEC;
|
|
goal_pos += orient.rvec * MRO_RVEC;
|
|
break;
|
|
case 2:
|
|
goal_pos -= orient.fvec * MRO_FVEC * 2.0f;
|
|
goal_pos -= orient.rvec * MRO_RVEC * 2.0f;
|
|
break;
|
|
case 3:
|
|
goal_pos -= orient.fvec * MRO_FVEC * 2.0f;
|
|
goal_pos += orient.rvec * MRO_RVEC * 2.0f;
|
|
break;
|
|
}
|
|
|
|
*(vector *)ptr = goal_pos;
|
|
return true;
|
|
}
|
|
}
|
|
} break;
|
|
case MRC_GET_GOAL_ROOM: {
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, (int *)ptr);
|
|
return true;
|
|
} break;
|
|
case MRC_GET_GOAL_ORIENT: {
|
|
return true;
|
|
} break;
|
|
}
|
|
|
|
// mprintf(0, "MantaRay action %d failed\n");
|
|
return false;
|
|
}
|
|
|
|
void MantaRay::UpdateSquadList(int me) {
|
|
int i;
|
|
|
|
for (i = 0; i < memory->num_teammates; i++) {
|
|
int type;
|
|
int j;
|
|
|
|
Obj_Value(memory->teammate[i], VF_GET, OBJV_I_TYPE, &type);
|
|
if (type == OBJ_NONE) {
|
|
for (j = i; j < memory->num_teammates - 1; j++) {
|
|
memory->teammate[j] = memory->teammate[j + 1];
|
|
}
|
|
|
|
memory->num_teammates--;
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MantaRay::UpdateSquad(int me) {
|
|
if (memory->flags & MRF_SQUADIE)
|
|
return;
|
|
|
|
if (memory->flags & MRF_LEADER) {
|
|
UpdateSquadList(me);
|
|
|
|
if (memory->num_teammates == 0)
|
|
memory->flags &= ~MRF_LEADER;
|
|
|
|
if (memory->num_teammates >= MR_MAX_TEAMMATES)
|
|
return;
|
|
}
|
|
|
|
int scan_objs[25];
|
|
int n_scan;
|
|
int room;
|
|
vector pos;
|
|
int i;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, 200.0f, scan_objs, 25, false, true, false, true);
|
|
|
|
for (i = 0; i < n_scan; i++) {
|
|
uint16_t id;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_US_ID, &id);
|
|
|
|
// this is more rare than the types matching; so, do it first
|
|
if (id == memory->mantaray_id) {
|
|
int type;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_I_TYPE, &type);
|
|
if (type == OBJ_ROBOT && scan_objs[i] != me) {
|
|
bool f_on_team = false;
|
|
if (memory->flags & MRF_LEADER) {
|
|
int j;
|
|
for (j = 0; j < memory->num_teammates; j++) {
|
|
if (scan_objs[i] == memory->teammate[j]) {
|
|
f_on_team = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!f_on_team) {
|
|
if (SendCommand(me, scan_objs[i], MRC_JOIN_ME, NULL)) {
|
|
memory->flags |= MRF_LEADER;
|
|
memory->teammate[memory->num_teammates++] = scan_objs[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (memory->num_teammates >= MR_MAX_TEAMMATES)
|
|
return;
|
|
}
|
|
}
|
|
|
|
void MantaRay::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(mantaray_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (mantaray_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
memory->attack_speed = 1.8f * memory->base_speed;
|
|
|
|
memory->flags = 0;
|
|
memory->leader_handle = OBJECT_HANDLE_NONE;
|
|
memory->num_teammates = 0;
|
|
|
|
// .1 to .6 seconds into the level, do the first matching
|
|
memory->next_update_squad_time = Game_GetTime() + ((float)rand() / (float)RAND_MAX) * .5f + 0.1f;
|
|
|
|
memory->mantaray_id = Obj_FindID("Manta Ray");
|
|
|
|
SetMode(me, MRM_NORMAL);
|
|
}
|
|
|
|
void MantaRay::SetMode(int me, char mode) {
|
|
int f_attack_flags = (AIF_FIRE | AIF_AUTO_AVOID_FRIENDS);
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
if (memory->flags & MRF_SQUADIE) {
|
|
// The leader is alive; so, get my new goal pos
|
|
SendCommand(me, memory->leader_handle, MRC_GET_GOAL_POS, &memory->goal_pos);
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_V_POS, &memory->goal_pos);
|
|
SendCommand(me, memory->leader_handle, MRC_GET_GOAL_ROOM, &memory->goal_room);
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_I_ROOMNUM, &memory->goal_room);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 2, 1.0f, -1, GF_ORIENT_VELOCITY | GF_KEEP_AT_COMPLETION, &memory->goal_pos,
|
|
memory->goal_room);
|
|
float cd = .1f;
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_F_CIRCLE_DIST, &cd);
|
|
} else {
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
}
|
|
|
|
switch (mode) {
|
|
case MRM_NORMAL: {
|
|
vector pos;
|
|
int room = 0;
|
|
float dist = 15.0f;
|
|
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
AI_AddGoal(me, AIG_WANDER_AROUND, 1, 1.0f, -1, GF_ORIENT_VELOCITY | GF_NOTIFIES | GF_KEEP_AT_COMPLETION, &pos,
|
|
room);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
} break;
|
|
|
|
case MRM_ATTACK: {
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
|
|
// memory->next_attack_time = Game_GetTime() + 10.0 * ((float)rand()/(float)RAND_MAX);
|
|
|
|
float dist = 60.0f + ((float)rand() / (float)RAND_MAX) * 10.0f;
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1,
|
|
GF_ORIENT_VELOCITY | GF_OBJ_IS_TARGET | GF_USE_BLINE_IF_SEES_GOAL | GF_KEEP_AT_COMPLETION |
|
|
GF_NONFLUSHABLE,
|
|
OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
dist = 10.0f;
|
|
int g_index =
|
|
AI_AddGoal(me, AIG_GET_AROUND_OBJ, ACTIVATION_BLEND_LEVEL, 1.0f, -1,
|
|
GF_ORIENT_VELOCITY | GF_OBJ_IS_TARGET | GF_KEEP_AT_COMPLETION | GF_NONFLUSHABLE, OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, g_index, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
} break;
|
|
|
|
case MRM_ATTACK_CIRCLE_BACK: {
|
|
vector pos;
|
|
int room = 0;
|
|
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
|
|
if (!(memory->flags & MRF_SQUADIE)) {
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->attack_speed);
|
|
}
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
float dist = 10.0f;
|
|
|
|
AI_AddGoal(me, AIG_WANDER_AROUND, 1, 1.0f, -1,
|
|
GF_NOTIFIES | GF_ORIENT_VELOCITY | GF_KEEP_AT_COMPLETION | GF_NONFLUSHABLE, &pos, room);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
int g_index =
|
|
AI_AddGoal(me, AIG_GET_AROUND_OBJ, ACTIVATION_BLEND_LEVEL, 1.0f, -1,
|
|
GF_ORIENT_VELOCITY | GF_OBJ_IS_TARGET | GF_KEEP_AT_COMPLETION | GF_NONFLUSHABLE, OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, g_index, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
} break;
|
|
}
|
|
|
|
memory->mode = mode;
|
|
memory->mode_time = 0.0f;
|
|
}
|
|
|
|
void MantaRay::DoSquadieFrame(int me) {
|
|
int type;
|
|
Obj_Value(memory->leader_handle, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
// Check if the leader is still alive
|
|
if (type == OBJ_NONE) {
|
|
// Reset the speed
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
|
|
memory->flags = 0;
|
|
memory->leader_handle = OBJECT_HANDLE_NONE;
|
|
|
|
SetMode(me, MRM_NORMAL);
|
|
return;
|
|
}
|
|
|
|
// The leader is alive; so, get my new goal pos
|
|
SendCommand(me, memory->leader_handle, MRC_GET_GOAL_POS, &memory->goal_pos);
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_V_POS, &memory->goal_pos);
|
|
SendCommand(me, memory->leader_handle, MRC_GET_GOAL_ROOM, &memory->goal_room);
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_I_ROOMNUM, &memory->goal_room);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 2, 1.0f, -1, GF_ORIENT_VELOCITY | GF_KEEP_AT_COMPLETION, &memory->goal_pos,
|
|
memory->goal_room);
|
|
float cd = .1f;
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_F_CIRCLE_DIST, &cd);
|
|
|
|
vector my_pos;
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &my_pos);
|
|
|
|
float dist = vm_VectorDistance(&my_pos, &memory->goal_pos);
|
|
|
|
if (dist <= MRO_CATCHUP_DIST) {
|
|
float catchup_speed;
|
|
AI_Value(memory->leader_handle, VF_GET, AIV_F_MAX_SPEED, &catchup_speed);
|
|
|
|
float scaled_speed = dist / MRO_CATCHUP_DIST * 0.23f * catchup_speed;
|
|
catchup_speed += scaled_speed;
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &catchup_speed);
|
|
} else if (dist > MRO_CATCHUP_DIST) {
|
|
float catchup_speed;
|
|
AI_Value(memory->leader_handle, VF_GET, AIV_F_MAX_SPEED, &catchup_speed);
|
|
|
|
float scaled_speed = 0.23f * catchup_speed;
|
|
catchup_speed += scaled_speed;
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &catchup_speed);
|
|
}
|
|
|
|
// Get the leaders mode
|
|
|
|
// Set my mode to the leaders mode... (Modes account for flags)
|
|
}
|
|
|
|
void MantaRay::DoFrame(int me) {
|
|
float last_see_target_time;
|
|
float last_hear_target_time;
|
|
AI_Value(me, VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &last_see_target_time);
|
|
AI_Value(me, VF_GET, AIV_F_LAST_HEAR_TARGET_TIME, &last_hear_target_time);
|
|
|
|
// Periodically update the squad information
|
|
if (memory->next_update_squad_time <= Game_GetTime()) {
|
|
// 3 to 6 seconds
|
|
memory->next_update_squad_time = Game_GetTime() + ((float)rand() / (float)RAND_MAX) * 3.0f + 3.0f;
|
|
UpdateSquad(me);
|
|
}
|
|
|
|
// If your a squadie, check for a leader
|
|
if (memory->flags & MRF_SQUADIE) {
|
|
DoSquadieFrame(me);
|
|
}
|
|
|
|
// if(memory->flags & MRF_LEADER)
|
|
// {
|
|
// vector pos;
|
|
// Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
// vector gpos;
|
|
// int groom;
|
|
|
|
// float cd;
|
|
// AI_GoalValue(me, 1, VF_GET, AIGV_I_ROOMNUM, &groom);
|
|
// AI_GoalValue(me, 1, VF_GET, AIGV_V_POS, &gpos);
|
|
// AI_GoalValue(me, 1, VF_GET, AIGV_F_CIRCLE_DIST, &cd);
|
|
// mprintf(0, "%f,%f,%f-%f,%f,%f-%f\n", XYZ(&gpos), XYZ(&pos), cd);
|
|
// }
|
|
|
|
switch (memory->mode) {
|
|
case MRM_ATTACK: {
|
|
// mprintf(0, "MODE ATTACK\n");
|
|
if (Game_GetTime() > last_see_target_time + 7.0f && Game_GetTime() > last_hear_target_time + 7.0f)
|
|
SetMode(me, MRM_NORMAL);
|
|
//
|
|
// float dist;
|
|
// AI_GoalValue(me, 1, VF_GET, AIGV_F_DIST_TO_GOAL, &dist);
|
|
} break;
|
|
|
|
case MRM_ATTACK_CIRCLE_BACK: {
|
|
// mprintf(0, "MODE CBACK\n");
|
|
if (Game_GetTime() > last_see_target_time + 7.0f && Game_GetTime() > last_hear_target_time + 7.0f)
|
|
SetMode(me, MRM_NORMAL);
|
|
|
|
if (memory->mode_time > 5.5f)
|
|
SetMode(me, MRM_ATTACK);
|
|
} break;
|
|
|
|
case MRM_NORMAL: {
|
|
// mprintf(0, "MODE NORMAL\n");
|
|
if (Game_GetTime() < last_see_target_time + 7.0f && Game_GetTime() < last_hear_target_time + 7.0f)
|
|
SetMode(me, MRM_ATTACK);
|
|
} break;
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
bool MantaRay::DoNotify(int me, tOSIRISEventInfo *data) {
|
|
if (IsGoalFinishedNotify(data->evt_ai_notify.notify_type)) {
|
|
switch (memory->mode) {
|
|
case MRM_ATTACK:
|
|
if (data->evt_ai_notify.goal_num == 1) {
|
|
SetMode(me, MRM_ATTACK_CIRCLE_BACK);
|
|
return false;
|
|
}
|
|
break;
|
|
case MRM_ATTACK_CIRCLE_BACK:
|
|
if (data->evt_ai_notify.goal_num == 1) {
|
|
SetMode(me, MRM_ATTACK);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
} else if (data->evt_ai_notify.notify_type == AIN_USER_DEFINED) {
|
|
gb_com *com = (gb_com *)data->extra_info;
|
|
|
|
return ReceiveCommand(me, data->evt_ai_notify.it_handle, com->action, com->ptr);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int16_t MantaRay::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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_MEMRESTORE: {
|
|
memory = (mantaray_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Skiff class
|
|
//------------------
|
|
|
|
bool Skiff::SendCommand(int me, int it, char command, void *ptr) {
|
|
gb_com com;
|
|
|
|
com.action = command;
|
|
com.ptr = ptr;
|
|
|
|
tOSIRISEventInfo ei;
|
|
|
|
ei.me_handle = it;
|
|
ei.extra_info = (void *)&command;
|
|
ei.evt_ai_notify.notify_type = AIN_USER_DEFINED;
|
|
ei.evt_ai_notify.it_handle = me;
|
|
|
|
return Obj_CallEvent(it, EVT_AI_NOTIFY, &ei);
|
|
}
|
|
|
|
bool Skiff::ReceiveCommand(int me, int it, char command, void *ptr) {
|
|
switch (command) {
|
|
case MRC_JOIN_ME: {
|
|
if (memory->flags == 0) {
|
|
memory->leader_handle = it;
|
|
memory->flags |= MRF_SQUADIE;
|
|
|
|
SetMode(me, memory->mode);
|
|
|
|
return true;
|
|
}
|
|
} break;
|
|
case MRC_LEAVE_YOU:
|
|
break;
|
|
case MRC_GET_GOAL_POS: {
|
|
int i;
|
|
for (i = 0; i < memory->num_teammates; i++) {
|
|
if (memory->teammate[i] == it) {
|
|
vector goal_pos;
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &goal_pos);
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
switch (i) {
|
|
case 0:
|
|
goal_pos -= orient.fvec * MRO_FVEC;
|
|
goal_pos -= orient.rvec * MRO_RVEC;
|
|
break;
|
|
case 1:
|
|
goal_pos -= orient.fvec * MRO_FVEC;
|
|
goal_pos += orient.rvec * MRO_RVEC;
|
|
break;
|
|
case 2:
|
|
goal_pos -= orient.fvec * MRO_FVEC * 2.0f;
|
|
goal_pos -= orient.rvec * MRO_RVEC * 2.0f;
|
|
break;
|
|
case 3:
|
|
goal_pos -= orient.fvec * MRO_FVEC * 2.0f;
|
|
goal_pos += orient.rvec * MRO_RVEC * 2.0f;
|
|
break;
|
|
}
|
|
|
|
*(vector *)ptr = goal_pos;
|
|
return true;
|
|
}
|
|
}
|
|
} break;
|
|
case MRC_GET_GOAL_ROOM: {
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, (int *)ptr);
|
|
return true;
|
|
} break;
|
|
case MRC_GET_GOAL_ORIENT: {
|
|
return true;
|
|
} break;
|
|
}
|
|
|
|
// mprintf(0, "Skiff action %d failed\n");
|
|
return false;
|
|
}
|
|
|
|
void Skiff::UpdateSquadList(int me) {
|
|
int i;
|
|
|
|
for (i = 0; i < memory->num_teammates; i++) {
|
|
int type;
|
|
int j;
|
|
|
|
Obj_Value(memory->teammate[i], VF_GET, OBJV_I_TYPE, &type);
|
|
if (type == OBJ_NONE) {
|
|
for (j = i; j < memory->num_teammates - 1; j++) {
|
|
memory->teammate[j] = memory->teammate[j + 1];
|
|
}
|
|
|
|
memory->num_teammates--;
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Skiff::UpdateSquad(int me) {
|
|
if (memory->flags & MRF_SQUADIE)
|
|
return;
|
|
|
|
if (memory->flags & MRF_LEADER) {
|
|
UpdateSquadList(me);
|
|
|
|
if (memory->num_teammates == 0)
|
|
memory->flags &= ~MRF_LEADER;
|
|
|
|
if (memory->num_teammates >= MR_MAX_TEAMMATES)
|
|
return;
|
|
}
|
|
|
|
int scan_objs[25];
|
|
int n_scan;
|
|
int room;
|
|
vector pos;
|
|
int i;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
n_scan = AI_GetNearbyObjs(&pos, room, 200.0f, scan_objs, 25, false, true, false, true);
|
|
|
|
for (i = 0; i < n_scan; i++) {
|
|
uint16_t id;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_US_ID, &id);
|
|
|
|
// this is more rare than the types matching; so, do it first
|
|
if (id == memory->skiff_id) {
|
|
int type;
|
|
Obj_Value(scan_objs[i], VF_GET, OBJV_I_TYPE, &type);
|
|
if (type == OBJ_ROBOT && scan_objs[i] != me) {
|
|
bool f_on_team = false;
|
|
if (memory->flags & MRF_LEADER) {
|
|
int j;
|
|
for (j = 0; j < memory->num_teammates; j++) {
|
|
if (scan_objs[i] == memory->teammate[j]) {
|
|
f_on_team = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!f_on_team) {
|
|
if (SendCommand(me, scan_objs[i], MRC_JOIN_ME, NULL)) {
|
|
memory->flags |= MRF_LEADER;
|
|
memory->teammate[memory->num_teammates++] = scan_objs[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (memory->num_teammates >= MR_MAX_TEAMMATES)
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Skiff::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(skiff_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (skiff_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
memory->attack_speed = 1.6f * memory->base_speed;
|
|
|
|
memory->flags = 0;
|
|
memory->leader_handle = OBJECT_HANDLE_NONE;
|
|
memory->num_teammates = 0;
|
|
|
|
// .1 to .6 seconds into the level, do the first matching
|
|
memory->next_update_squad_time = Game_GetTime() + ((float)rand() / (float)RAND_MAX) * .5f + 0.1f;
|
|
|
|
memory->skiff_id = Obj_FindID("Skiff");
|
|
|
|
SetMode(me, MRM_NORMAL);
|
|
}
|
|
|
|
void Skiff::SetMode(int me, char mode) {
|
|
int f_attack_flags = (AIF_FIRE | AIF_AUTO_AVOID_FRIENDS);
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
|
|
switch (mode) {
|
|
case MRM_NORMAL: {
|
|
vector pos;
|
|
int room = 0;
|
|
float dist = 15.0f;
|
|
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
AI_AddGoal(me, AIG_WANDER_AROUND, 1, 1.0f, -1, GF_ORIENT_VELOCITY | GF_NOTIFIES | GF_KEEP_AT_COMPLETION, &pos,
|
|
room);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
} break;
|
|
|
|
case MRM_ATTACK: {
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
|
|
// memory->next_attack_time = Game_GetTime() + 10.0 * ((float)rand()/(float)RAND_MAX);
|
|
|
|
float dist = 60.0f + ((float)rand() / (float)RAND_MAX) * 10.0f;
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1,
|
|
GF_ORIENT_VELOCITY | GF_OBJ_IS_TARGET | GF_USE_BLINE_IF_SEES_GOAL | GF_KEEP_AT_COMPLETION |
|
|
GF_NONFLUSHABLE,
|
|
OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
dist = 10.0f;
|
|
int g_index =
|
|
AI_AddGoal(me, AIG_GET_AROUND_OBJ, ACTIVATION_BLEND_LEVEL, 1.0f, -1,
|
|
GF_ORIENT_VELOCITY | GF_OBJ_IS_TARGET | GF_KEEP_AT_COMPLETION | GF_NONFLUSHABLE, OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, g_index, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
} break;
|
|
|
|
case MRM_ATTACK_CIRCLE_BACK: {
|
|
vector pos;
|
|
int room = 0;
|
|
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
|
|
float dist = 10.0f;
|
|
|
|
AI_AddGoal(me, AIG_WANDER_AROUND, 1, 1.0f, -1,
|
|
GF_NOTIFIES | GF_ORIENT_VELOCITY | GF_KEEP_AT_COMPLETION | GF_NONFLUSHABLE, &pos, room);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
int g_index =
|
|
AI_AddGoal(me, AIG_GET_AROUND_OBJ, ACTIVATION_BLEND_LEVEL, 1.0f, -1,
|
|
GF_ORIENT_VELOCITY | GF_OBJ_IS_TARGET | GF_KEEP_AT_COMPLETION | GF_NONFLUSHABLE, OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, g_index, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->attack_speed);
|
|
} break;
|
|
}
|
|
|
|
if (memory->flags & MRF_SQUADIE) {
|
|
// The leader is alive; so, get my new goal pos
|
|
SendCommand(me, memory->leader_handle, MRC_GET_GOAL_POS, &memory->goal_pos);
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_V_POS, &memory->goal_pos);
|
|
SendCommand(me, memory->leader_handle, MRC_GET_GOAL_ROOM, &memory->goal_room);
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_I_ROOMNUM, &memory->goal_room);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_POS, 2, 4.0f, -1, GF_ORIENT_VELOCITY | GF_KEEP_AT_COMPLETION | GF_NONFLUSHABLE,
|
|
&memory->goal_pos, memory->goal_room);
|
|
}
|
|
|
|
memory->mode = mode;
|
|
memory->mode_time = 0.0f;
|
|
}
|
|
|
|
void Skiff::DoSquadieFrame(int me) {
|
|
int type;
|
|
Obj_Value(memory->leader_handle, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
// Check if the leader is still alive
|
|
if (type == OBJ_NONE) {
|
|
if (memory->flags & MRF_CATCHUP) {
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
}
|
|
|
|
memory->flags = 0;
|
|
memory->leader_handle = OBJECT_HANDLE_NONE;
|
|
|
|
SetMode(me, MRM_NORMAL);
|
|
return;
|
|
}
|
|
|
|
// The leader is alive; so, get my new goal pos
|
|
SendCommand(me, memory->leader_handle, MRC_GET_GOAL_POS, &memory->goal_pos);
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_V_POS, &memory->goal_pos);
|
|
SendCommand(me, memory->leader_handle, MRC_GET_GOAL_ROOM, &memory->goal_room);
|
|
AI_GoalValue(me, 2, VF_SET, AIGV_I_ROOMNUM, &memory->goal_room);
|
|
|
|
vector my_pos;
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &my_pos);
|
|
|
|
float dist = vm_VectorDistance(&my_pos, &memory->goal_pos);
|
|
|
|
if (dist <= MRO_CATCHUP_DIST) {
|
|
float scaled_speed = dist / MRO_CATCHUP_DIST * 0.18f * memory->base_speed;
|
|
float speed;
|
|
|
|
AI_Value(memory->leader_handle, VF_GET, AIV_F_MAX_SPEED, &speed);
|
|
|
|
speed += scaled_speed;
|
|
|
|
memory->flags &= ~MRF_CATCHUP;
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
} else if (!(memory->flags & MRF_CATCHUP) && dist > MRO_CATCHUP_DIST) {
|
|
// Between 18% and 23% speed up to get into formation
|
|
float catchup_speed;
|
|
AI_Value(memory->leader_handle, VF_GET, AIV_F_MAX_SPEED, &catchup_speed);
|
|
catchup_speed += (catchup_speed * .18f) + (catchup_speed * .05f * ((float)rand() / (float)RAND_MAX));
|
|
|
|
memory->flags |= MRF_CATCHUP;
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &catchup_speed);
|
|
}
|
|
|
|
// Get the leaders mode
|
|
|
|
// Set my mode to the leaders mode... (Modes account for flags)
|
|
}
|
|
|
|
void Skiff::DoFrame(int me) {
|
|
float last_see_target_time;
|
|
float last_hear_target_time;
|
|
AI_Value(me, VF_GET, AIV_F_LAST_SEE_TARGET_TIME, &last_see_target_time);
|
|
AI_Value(me, VF_GET, AIV_F_LAST_HEAR_TARGET_TIME, &last_hear_target_time);
|
|
|
|
// Periodically update the squad information
|
|
if (memory->next_update_squad_time <= Game_GetTime()) {
|
|
// 3 to 6 seconds
|
|
memory->next_update_squad_time = Game_GetTime() + ((float)rand() / (float)RAND_MAX) * 3.0f + 3.0f;
|
|
// UpdateSquad(me);
|
|
}
|
|
|
|
// If your a squadie, check for a leader
|
|
if (memory->flags & MRF_SQUADIE) {
|
|
DoSquadieFrame(me);
|
|
}
|
|
|
|
switch (memory->mode) {
|
|
case MRM_ATTACK: {
|
|
// mprintf(0, "MODE ATTACK\n");
|
|
if (Game_GetTime() > last_see_target_time + 7.0f && Game_GetTime() > last_hear_target_time + 7.0f)
|
|
SetMode(me, MRM_NORMAL);
|
|
//
|
|
// float dist;
|
|
// AI_GoalValue(me, 1, VF_GET, AIGV_F_DIST_TO_GOAL, &dist);
|
|
} break;
|
|
|
|
case MRM_ATTACK_CIRCLE_BACK: {
|
|
// mprintf(0, "MODE CBACK\n");
|
|
if (Game_GetTime() > last_see_target_time + 7.0f && Game_GetTime() > last_hear_target_time + 7.0f)
|
|
SetMode(me, MRM_NORMAL);
|
|
|
|
if (memory->mode_time > 4.0f)
|
|
SetMode(me, MRM_ATTACK);
|
|
} break;
|
|
|
|
case MRM_NORMAL: {
|
|
// mprintf(0, "MODE NORMAL\n");
|
|
if (Game_GetTime() < last_see_target_time + 7.0f && Game_GetTime() < last_see_target_time + 7.0f)
|
|
SetMode(me, MRM_ATTACK);
|
|
} break;
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
bool Skiff::DoNotify(int me, tOSIRISEventInfo *data) {
|
|
if (IsGoalFinishedNotify(data->evt_ai_notify.notify_type)) {
|
|
switch (memory->mode) {
|
|
case MRM_ATTACK:
|
|
if (data->evt_ai_notify.goal_num == 1) {
|
|
SetMode(me, MRM_ATTACK_CIRCLE_BACK);
|
|
return false;
|
|
}
|
|
break;
|
|
case MRM_ATTACK_CIRCLE_BACK:
|
|
if (data->evt_ai_notify.goal_num == 1) {
|
|
SetMode(me, MRM_ATTACK);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
} else if (data->evt_ai_notify.notify_type == AIN_USER_DEFINED) {
|
|
gb_com *com = (gb_com *)data->extra_info;
|
|
|
|
return ReceiveCommand(me, data->evt_ai_notify.it_handle, com->action, com->ptr);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int16_t Skiff::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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_MEMRESTORE: {
|
|
memory = (skiff_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// SpyHunter class
|
|
//------------------
|
|
|
|
void SpyHunter::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(spyhunter_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (spyhunter_data *)Scrpt_MemAlloc(&ch);
|
|
memory->last_attack_mode = SHM_ATTACK;
|
|
memory->f_hit_by_emd = false;
|
|
memory->emd_id = Wpn_FindID("EMDblob");
|
|
memory->tick_sound = Sound_FindId("WpnBmbTickElectricalA");
|
|
memory->last_tick_time = Game_GetTime();
|
|
|
|
SetMode(me, SHM_NORMAL);
|
|
}
|
|
|
|
void SpyHunter::SetMode(int me, char mode) {
|
|
AI_SetType(me, AIT_AIS);
|
|
|
|
int f_attack_flags = (AIF_FIRE | AIF_AUTO_AVOID_FRIENDS);
|
|
|
|
if (memory->mode == SHM_ATTACK || memory->mode == SHM_BOMB_ATTACK) {
|
|
memory->last_attack_mode = memory->mode;
|
|
}
|
|
|
|
switch (mode) {
|
|
case SHM_NORMAL: {
|
|
vector pos;
|
|
int room = 0;
|
|
float dist = 5.0f;
|
|
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
|
|
// If last see target time > 7.0 orient velocity
|
|
AI_AddGoal(me, AIG_WANDER_AROUND, 1, 1.0f, -1,
|
|
GF_ORIENT_TARGET | GF_KEEP_AT_COMPLETION | GF_FORCE_AWARENESS | GF_NOTIFIES, &pos, room);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
} break;
|
|
case SHM_ATTACK: {
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
AI_SetType(me, AIT_EVADER1);
|
|
} break;
|
|
case SHM_BOMB_ATTACK: {
|
|
float dist = -1000.0f;
|
|
|
|
f_attack_flags |= AIF_DODGE; // Bombs don't dodge
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &f_attack_flags);
|
|
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1,
|
|
GF_ORIENT_VELOCITY | GF_KEEP_AT_COMPLETION | GF_OBJ_IS_TARGET | GF_FORCE_AWARENESS, OBJECT_HANDLE_NONE);
|
|
AI_GoalValue(me, 1, VF_SET, AIGV_F_CIRCLE_DIST, &dist);
|
|
} break;
|
|
}
|
|
|
|
memory->mode = mode;
|
|
memory->mode_time = 0.0f;
|
|
memory->last_tick_time = Game_GetTime();
|
|
memory->next_summon_time = Game_GetTime() + 5.0f + (float)rand() / (float)RAND_MAX;
|
|
}
|
|
|
|
bool SpyHunter::DoNotify(int me, tOSIRISEventInfo *data) {
|
|
tOSIRISEVTAINOTIFY *notify = &data->evt_ai_notify;
|
|
|
|
if ((!memory->f_hit_by_emd) && notify->notify_type == AIN_HIT_BY_WEAPON) {
|
|
int type;
|
|
Obj_Value(notify->it_handle, VF_GET, OBJV_I_TYPE, &type);
|
|
|
|
if (type == OBJ_WEAPON) {
|
|
uint16_t id;
|
|
|
|
Obj_Value(notify->it_handle, VF_GET, OBJV_US_ID, &id);
|
|
if (id == memory->emd_id) {
|
|
memory->f_hit_by_emd = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (memory->mode == SHM_NORMAL || (memory->mode == SHM_ATTACK && memory->mode_time > 10.0f)) {
|
|
if ((notify->notify_type == AIN_OBJ_FIRED) || (notify->notify_type == AIN_HIT_BY_WEAPON) ||
|
|
(notify->notify_type == AIN_BUMPED_OBJ)) {
|
|
if (memory->mode == SHM_NORMAL) {
|
|
SetMode(me, SHM_ATTACK);
|
|
return true;
|
|
} else {
|
|
SetMode(me, SHM_BOMB_ATTACK);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (memory->mode == SHM_BOMB_ATTACK && notify->notify_type == AIN_BUMPED_OBJ) {
|
|
int room;
|
|
vector pos;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_GetGunPos(me, 0, &pos);
|
|
|
|
if (!memory->f_hit_by_emd) {
|
|
vector velocity;
|
|
Obj_Value(me, VF_GET, OBJV_V_VELOCITY, &velocity);
|
|
|
|
int weapon_id = Wpn_FindID("FragBarrel");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, NULL, me, &velocity);
|
|
}
|
|
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = me;
|
|
mstruct.killer_handle = OBJECT_HANDLE_NONE;
|
|
mstruct.damage_type = GD_SCRIPTED;
|
|
mstruct.amount = 1000.0f;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SpyHunter::DoFrame(int me) {
|
|
// mprintf(0, "Current goal is %d and the ", AI_GetCurGoalIndex(me));
|
|
|
|
switch (memory->mode) {
|
|
case SHM_NORMAL: {
|
|
// int gt = -1;
|
|
//
|
|
// if(AI_GetCurGoalIndex(me) >= 0)
|
|
// AI_GoalValue(me, AI_GetCurGoalIndex(me), VF_GET, AIGV_I_TYPE, >);
|
|
//
|
|
// mprintf(0, "current Mode is Normal and GT %X\n", gt);
|
|
} break;
|
|
case SHM_ATTACK: {
|
|
// mprintf(0, "current Mode is Attack\n");
|
|
// if(memory->mode_time > 10.0f)
|
|
// {
|
|
// SetMode(me, SHM_BOMB_ATTACK);
|
|
// }
|
|
} break;
|
|
case SHM_BOMB_ATTACK: {
|
|
float tick_interval;
|
|
// mprintf(0, "current Mode is Bomb Attack\n");
|
|
|
|
if (memory->mode_time < SH_BOMB_TICK_RAMP_TIME) {
|
|
float p_explode = (SH_BOMB_TICK_RAMP_TIME - memory->mode_time) / SH_BOMB_TICK_RAMP_TIME;
|
|
|
|
tick_interval = (SH_MAX_TICK_INTERVAL * p_explode) + (SH_MIN_TICK_INTERVAL * (1.0f - p_explode));
|
|
} else {
|
|
tick_interval = SH_MIN_TICK_INTERVAL;
|
|
}
|
|
|
|
if (memory->last_tick_time + tick_interval < Game_GetTime()) {
|
|
Sound_Play3d(me, memory->tick_sound);
|
|
memory->last_tick_time = Game_GetTime();
|
|
}
|
|
|
|
if (memory->mode_time < SH_MAX_BOMB_TIME) {
|
|
float max_speed;
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &max_speed);
|
|
max_speed += SH_BOMB_ACCELERATION * Game_GetFrameTime();
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &max_speed);
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_TURN_RATE, &max_speed);
|
|
max_speed += SH_BOMB_ROT_ACC * Game_GetFrameTime();
|
|
AI_Value(me, VF_SET, AIV_F_MAX_TURN_RATE, &max_speed);
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &max_speed);
|
|
max_speed += SH_BOMB_ACCELERATION * Game_GetFrameTime();
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &max_speed);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
int16_t SpyHunter::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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_MEMRESTORE: {
|
|
memory = (spyhunter_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Sniper class
|
|
//------------------
|
|
|
|
bool Sniper::DoNotify(int me_handle, tOSIRISEventInfo *data) { return true; }
|
|
|
|
void Sniper::SetMode(int me, char mode) {
|
|
switch (mode) {
|
|
case SNIPER_SNIPE: {
|
|
vector pos;
|
|
int room = 0;
|
|
|
|
int flags = GF_NOTIFIES;
|
|
|
|
if ((rand() % 100) > 50) {
|
|
flags |= GF_ORIENT_TARGET;
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 0, 1.0f, -1, GF_SPEED_ATTACK | GF_KEEP_AT_COMPLETION | GF_OBJ_IS_TARGET,
|
|
NULL, GST_NEG_FVEC);
|
|
memory->time_till_next_mode = 4.0f * (float)rand() / (float)RAND_MAX + 2.0f;
|
|
} else {
|
|
float timescaler = 1.0f;
|
|
|
|
if ((rand() % 100) >= 94) {
|
|
flags |= GF_ORIENT_VELOCITY | GF_SPEED_FLEE;
|
|
timescaler = 2.0f;
|
|
} else {
|
|
flags |= GF_SPEED_ATTACK;
|
|
}
|
|
|
|
AI_AddGoal(me, AIG_WANDER_AROUND, 3, 1.0f, -1, flags, &pos, room);
|
|
|
|
float speed = memory->base_speed + memory->base_speed * (float)rand() / (float)RAND_MAX * .5;
|
|
float delta_vel = memory->base_acc + memory->base_acc * (float)rand() / (float)RAND_MAX * .5;
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &delta_vel);
|
|
|
|
memory->time_till_next_mode = 4.0f * (float)rand() / (float)RAND_MAX + 2.0f;
|
|
memory->time_till_next_mode *= timescaler;
|
|
}
|
|
|
|
} break;
|
|
case SNIPER_BASE: {
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (used) {
|
|
int flags;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_I_FLAGS, &flags);
|
|
|
|
if (!(flags & GF_NONFLUSHABLE)) {
|
|
AI_ClearGoal(me, 3);
|
|
}
|
|
}
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
memory->time_till_next_mode = 5.0f * (float)rand() / (float)RAND_MAX;
|
|
+6.0f;
|
|
} break;
|
|
}
|
|
|
|
memory->mode_time = 0.0f;
|
|
memory->mode = mode;
|
|
}
|
|
|
|
void Sniper::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(sniper_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (sniper_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
|
|
SetMode(me, SNIPER_BASE);
|
|
}
|
|
|
|
void Sniper::DoFrame(int me) {
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
MSafe_GetValue(MSAFE_OBJECT_ID, &m);
|
|
int orbot_id = Obj_FindID("d3bot20");
|
|
|
|
if (m.id != orbot_id) {
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (used) {
|
|
return;
|
|
}
|
|
|
|
/* float awareness;
|
|
AI_Value(me, VF_GET, AIV_F_AWARENESS, &awareness);
|
|
|
|
if(awareness <= AWARE_BARELY)
|
|
{
|
|
if(memory->mode != SNIPER_BASE)
|
|
{
|
|
SetMode(me, SNIPER_BASE);
|
|
}
|
|
|
|
return;
|
|
}*/
|
|
}
|
|
|
|
if (memory->time_till_next_mode < memory->mode_time) {
|
|
if (memory->mode == SNIPER_BASE)
|
|
SetMode(me, SNIPER_SNIPE);
|
|
else
|
|
SetMode(me, SNIPER_BASE);
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
int16_t Sniper::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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_MEMRESTORE: {
|
|
memory = (sniper_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// SniperNoRun class
|
|
//------------------
|
|
|
|
bool SniperNoRun::DoNotify(int me_handle, tOSIRISEventInfo *data) { return true; }
|
|
|
|
void SniperNoRun::SetMode(int me, char mode) {
|
|
switch (mode) {
|
|
case SNIPER_SNIPE: {
|
|
vector pos;
|
|
int room = 0;
|
|
|
|
int flags = GF_NOTIFIES;
|
|
|
|
if ((rand() % 100) > 50) {
|
|
flags |= GF_ORIENT_TARGET;
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 0, 1.0f, -1, GF_SPEED_ATTACK | GF_KEEP_AT_COMPLETION | GF_OBJ_IS_TARGET,
|
|
NULL, GST_NEG_FVEC);
|
|
memory->time_till_next_mode = 4.0f * (float)rand() / (float)RAND_MAX + 2.0f;
|
|
} else {
|
|
float timescaler = 1.0f;
|
|
|
|
flags |= GF_SPEED_ATTACK;
|
|
AI_AddGoal(me, AIG_WANDER_AROUND, 3, 1.0f, -1, flags, &pos, room);
|
|
|
|
float speed = memory->base_speed + memory->base_speed * (float)rand() / (float)RAND_MAX * .5;
|
|
float delta_vel = memory->base_acc + memory->base_acc * (float)rand() / (float)RAND_MAX * .5;
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &delta_vel);
|
|
|
|
memory->time_till_next_mode = 2.0f * (float)rand() / (float)RAND_MAX + 2.0f;
|
|
memory->time_till_next_mode *= timescaler;
|
|
}
|
|
|
|
} break;
|
|
case SNIPER_BASE: {
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (used) {
|
|
int flags;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_I_FLAGS, &flags);
|
|
|
|
if (!(flags & GF_NONFLUSHABLE)) {
|
|
AI_ClearGoal(me, 3);
|
|
}
|
|
}
|
|
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
memory->time_till_next_mode = 5.0f * (float)rand() / (float)RAND_MAX;
|
|
+4.0f;
|
|
} break;
|
|
}
|
|
|
|
memory->mode_time = 0.0f;
|
|
memory->mode = mode;
|
|
}
|
|
|
|
void SniperNoRun::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(snipernorun_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (snipernorun_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
|
|
SetMode(me, SNIPER_BASE);
|
|
}
|
|
|
|
void SniperNoRun::DoFrame(int me) {
|
|
msafe_struct m;
|
|
m.objhandle = me;
|
|
MSafe_GetValue(MSAFE_OBJECT_ID, &m);
|
|
int orbot_id = Obj_FindID("d3bot20");
|
|
|
|
if (m.id != orbot_id) {
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (used) {
|
|
return;
|
|
}
|
|
|
|
/* float awareness;
|
|
AI_Value(me, VF_GET, AIV_F_AWARENESS, &awareness);
|
|
|
|
if(awareness <= AWARE_BARELY)
|
|
{
|
|
if(memory->mode != SNIPER_BASE)
|
|
{
|
|
SetMode(me, SNIPER_BASE);
|
|
}
|
|
|
|
return;
|
|
}*/
|
|
}
|
|
|
|
if (memory->time_till_next_mode < memory->mode_time) {
|
|
if (memory->mode == SNIPER_BASE)
|
|
SetMode(me, SNIPER_SNIPE);
|
|
else
|
|
SetMode(me, SNIPER_BASE);
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
int16_t SniperNoRun::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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_MEMRESTORE: {
|
|
memory = (snipernorun_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Evader Mod A class
|
|
//------------------
|
|
|
|
#define EMA_NORMAL 0
|
|
#define EMA_GET_BEHIND 1
|
|
|
|
bool EvaderModA::DoNotify(int me_handle, tOSIRISEventInfo *data) { return true; }
|
|
|
|
void EvaderModA::SetMode(int me, char mode) {
|
|
switch (mode) {
|
|
case EMA_GET_BEHIND: {
|
|
vector pos;
|
|
int vec;
|
|
|
|
int flags = GF_NOTIFIES | GF_ORIENT_TARGET;
|
|
|
|
if ((rand() % 100) > 50) {
|
|
vec = GST_NEG_FVEC;
|
|
memory->time_till_next_mode = 7.0f * (float)rand() / (float)RAND_MAX + 2.0f;
|
|
} else {
|
|
if ((rand() % 100) > 50) {
|
|
vec = GST_NEG_RVEC;
|
|
} else {
|
|
vec = GST_RVEC;
|
|
}
|
|
memory->time_till_next_mode = 4.0f * (float)rand() / (float)RAND_MAX + 2.0f;
|
|
}
|
|
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 0, 1.0f, -1, GF_SPEED_ATTACK | GF_KEEP_AT_COMPLETION | GF_OBJ_IS_TARGET,
|
|
NULL, vec);
|
|
} break;
|
|
case EMA_NORMAL: {
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (used) {
|
|
int flags;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_I_FLAGS, &flags);
|
|
|
|
if (!(flags & GF_NONFLUSHABLE)) {
|
|
AI_ClearGoal(me, 3);
|
|
}
|
|
}
|
|
|
|
memory->time_till_next_mode = 4.0f * (float)rand() / (float)RAND_MAX;
|
|
+6.0f;
|
|
} break;
|
|
}
|
|
|
|
memory->mode_time = 0.0f;
|
|
memory->mode = mode;
|
|
}
|
|
|
|
void EvaderModA::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(evadermoda_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (evadermoda_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
|
|
SetMode(me, EMA_NORMAL);
|
|
}
|
|
|
|
void EvaderModA::DoFrame(int me) {
|
|
float awareness;
|
|
AI_Value(me, VF_GET, AIV_F_AWARENESS, &awareness);
|
|
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (used) {
|
|
return;
|
|
}
|
|
|
|
if (awareness <= AWARE_BARELY) {
|
|
if (memory->mode != EMA_NORMAL) {
|
|
SetMode(me, EMA_NORMAL);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (memory->time_till_next_mode < memory->mode_time) {
|
|
if (memory->mode == EMA_NORMAL)
|
|
SetMode(me, EMA_GET_BEHIND);
|
|
else
|
|
SetMode(me, EMA_NORMAL);
|
|
}
|
|
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
int16_t EvaderModA::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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_MEMRESTORE: {
|
|
memory = (evadermoda_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// FlameRAS
|
|
//------------------
|
|
|
|
// Based off EvaderModA
|
|
|
|
bool FlameRAS::DoNotify(int me_handle, tOSIRISEventInfo *data) { return true; }
|
|
|
|
void FlameRAS::SetMode(int me, char mode) {
|
|
switch (mode) {
|
|
case EMA_GET_BEHIND: {
|
|
vector pos;
|
|
int vec;
|
|
|
|
int flags = GF_NOTIFIES | GF_ORIENT_TARGET;
|
|
|
|
if ((rand() % 100) > 50) {
|
|
vec = GST_NEG_FVEC;
|
|
memory->time_till_next_mode = 7.0f * (float)rand() / (float)RAND_MAX + 2.0f;
|
|
} else {
|
|
if ((rand() % 100) > 50) {
|
|
vec = GST_NEG_RVEC;
|
|
} else {
|
|
vec = GST_RVEC;
|
|
}
|
|
memory->time_till_next_mode = 4.0f * (float)rand() / (float)RAND_MAX + 2.0f;
|
|
}
|
|
|
|
AI_AddGoal(me, AIG_MOVE_RELATIVE_OBJ_VEC, 0, 1.0f, -1, GF_SPEED_ATTACK | GF_KEEP_AT_COMPLETION | GF_OBJ_IS_TARGET,
|
|
NULL, vec);
|
|
} break;
|
|
case EMA_NORMAL: {
|
|
AI_Value(me, VF_SET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_SET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (used) {
|
|
int flags;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_I_FLAGS, &flags);
|
|
|
|
if (!(flags & GF_NONFLUSHABLE)) {
|
|
AI_ClearGoal(me, 3);
|
|
}
|
|
}
|
|
|
|
memory->time_till_next_mode = 4.0f * (float)rand() / (float)RAND_MAX;
|
|
+6.0f;
|
|
} break;
|
|
}
|
|
|
|
memory->mode_time = 0.0f;
|
|
memory->mode = mode;
|
|
}
|
|
|
|
void FlameRAS::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(flameras_data);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (flameras_data *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->time_till_fire_toggle = 3.0f + (float)rand() / (float)RAND_MAX + (float)Game_GetDiffLevel();
|
|
memory->f_firing_ok = true;
|
|
|
|
AI_Value(me, VF_GET, AIV_F_MAX_SPEED, &memory->base_speed);
|
|
AI_Value(me, VF_GET, AIV_F_MAX_DELTA_SPEED, &memory->base_acc);
|
|
|
|
SetMode(me, EMA_NORMAL);
|
|
}
|
|
|
|
void FlameRAS::DoFrame(int me) {
|
|
float awareness;
|
|
AI_Value(me, VF_GET, AIV_F_AWARENESS, &awareness);
|
|
|
|
bool used;
|
|
AI_GoalValue(me, 3, VF_GET, AIGV_B_USED, &used);
|
|
|
|
if (memory->time_till_fire_toggle <= 0.0f) {
|
|
if (memory->f_firing_ok) {
|
|
int flags = AIF_DISABLE_FIRING;
|
|
AI_Value(me, VF_SET_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
memory->time_till_fire_toggle = 5.0f + (float)rand() / (float)RAND_MAX + ((float)Game_GetDiffLevel() - 3.0f);
|
|
memory->f_firing_ok = false;
|
|
} else {
|
|
int flags = AIF_DISABLE_FIRING;
|
|
AI_Value(me, VF_CLEAR_FLAGS, AIV_I_FLAGS, &flags);
|
|
|
|
memory->time_till_fire_toggle = 4.0f + (float)rand() / (float)RAND_MAX + (float)Game_GetDiffLevel();
|
|
memory->f_firing_ok = true;
|
|
}
|
|
}
|
|
|
|
if (used) {
|
|
return;
|
|
}
|
|
|
|
if (awareness <= AWARE_BARELY) {
|
|
if (memory->mode != EMA_NORMAL) {
|
|
SetMode(me, EMA_NORMAL);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (memory->time_till_next_mode < memory->mode_time) {
|
|
if (memory->mode == EMA_NORMAL)
|
|
SetMode(me, EMA_GET_BEHIND);
|
|
else
|
|
SetMode(me, EMA_NORMAL);
|
|
}
|
|
|
|
memory->time_till_fire_toggle -= Game_GetFrameTime();
|
|
memory->mode_time += Game_GetFrameTime();
|
|
}
|
|
|
|
int16_t FlameRAS::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
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_MEMRESTORE: {
|
|
memory = (flameras_data *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Seeker class
|
|
//------------------
|
|
|
|
void Seeker::DoInit(int me) {
|
|
AI_SetType(me, AIT_AIS);
|
|
AI_AddGoal(me, AIG_GET_TO_OBJ, 1, 1.0f, -1, GF_OBJ_IS_TARGET | GF_USE_BLINE_IF_SEES_GOAL, OBJECT_HANDLE_NONE);
|
|
|
|
uint16_t id;
|
|
uint16_t humon_seeker_id;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_US_ID, &id);
|
|
humon_seeker_id = Obj_FindID("HumonSeeker");
|
|
|
|
if (humon_seeker_id == id) {
|
|
vector velocity;
|
|
matrix orient;
|
|
Obj_Value(me, VF_GET, OBJV_V_VELOCITY, &velocity);
|
|
Obj_Value(me, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
velocity += orient.fvec * 50.0f;
|
|
|
|
Obj_Value(me, VF_SET, OBJV_V_VELOCITY, &velocity);
|
|
}
|
|
|
|
int parent = GetObjectParent(me);
|
|
int sound_id = Sound_FindId("Drop bomb");
|
|
int p_type = GetObjectType(parent);
|
|
|
|
if (sound_id >= 0 && p_type == OBJ_PLAYER) {
|
|
Sound_Play3d(parent, sound_id);
|
|
}
|
|
}
|
|
|
|
void Seeker::DoCollide(int me) {
|
|
int room;
|
|
vector pos;
|
|
int weapon_id;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
weapon_id = Wpn_FindID("SeekerExplosion");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, NULL, me);
|
|
|
|
msafe_struct mo;
|
|
mo.objhandle = me;
|
|
mo.playsound = 0;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
}
|
|
|
|
int16_t Seeker::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_COLLIDE:
|
|
DoCollide(data->me_handle);
|
|
break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
// Old Osiris functions converted to make porting easier
|
|
int GetObjectParent(int object) {
|
|
msafe_struct ms;
|
|
ms.objhandle = object;
|
|
|
|
MSafe_GetValue(MSAFE_OBJECT_PARENT, &ms);
|
|
|
|
return ms.objhandle;
|
|
}
|
|
|
|
int GetObjectType(int object) {
|
|
msafe_struct ms;
|
|
ms.objhandle = object;
|
|
MSafe_GetValue(MSAFE_OBJECT_TYPE, &ms);
|
|
|
|
return ms.type;
|
|
}
|
|
|
|
void ObjectDamage(int object, float amount, int killer = OBJECT_HANDLE_NONE);
|
|
void ObjectDamage(int object, float amount, int killer) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = object;
|
|
mstruct.killer_handle = killer;
|
|
mstruct.damage_type = GD_SCRIPTED;
|
|
mstruct.amount = amount;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_DAMAGE_OBJECT, &mstruct);
|
|
}
|
|
|
|
float GetObjectShields(int object) {
|
|
msafe_struct mstruct;
|
|
|
|
mstruct.objhandle = object;
|
|
MSafe_GetValue(MSAFE_OBJECT_SHIELDS, &mstruct);
|
|
|
|
return mstruct.shields;
|
|
}
|
|
|
|
void ObjectRemove(int object) {
|
|
msafe_struct ms;
|
|
ms.objhandle = object;
|
|
ms.playsound = 0;
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &ms);
|
|
}
|
|
|
|
//------------------
|
|
// Bouncing Betty class
|
|
//------------------
|
|
|
|
int16_t BettyBomb::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_MEMRESTORE: {
|
|
memory = (tBettyBombInfo *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
|
|
case EVT_CREATED: {
|
|
// create our memory
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(tBettyBombInfo);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = data->me_handle;
|
|
|
|
memory = (tBettyBombInfo *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->explode = false;
|
|
|
|
// Create and release our Betty children
|
|
int parent = GetObjectParent(data->me_handle);
|
|
int child_id = Obj_FindID("Betty");
|
|
|
|
if (parent != OBJECT_HANDLE_NONE && child_id != -1) {
|
|
int child_handle;
|
|
matrix orient, o, otemp, o1;
|
|
vector vel, pos, v, vtemp;
|
|
int room;
|
|
|
|
Obj_Value(parent, VF_GET, OBJV_M_ORIENT, &orient);
|
|
Obj_Value(parent, VF_GET, OBJV_V_VELOCITY, &vel);
|
|
Obj_Value(parent, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(parent, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
// v = -vel;
|
|
/*
|
|
v = vel;
|
|
float mag = vm_GetMagnitude(&v);
|
|
if(mag < 30.0f )
|
|
{
|
|
if(mag==0)
|
|
mag = 0.001f;
|
|
|
|
v /= mag;
|
|
v *= 30.0f;
|
|
}
|
|
*/
|
|
|
|
v = -orient.fvec + orient.rvec - orient.uvec;
|
|
vm_VectorNormalize(&v);
|
|
v *= 30.0f;
|
|
|
|
// give drop bomb sound
|
|
int type = GetObjectType(parent);
|
|
|
|
if (type == OBJ_PLAYER || type == OBJ_GHOST) {
|
|
int sound_id = Sound_FindId("Drop bomb");
|
|
|
|
if (sound_id >= 0) {
|
|
Sound_Play3d(parent, sound_id);
|
|
}
|
|
}
|
|
|
|
// First create middle Betty
|
|
child_handle = Obj_Create(OBJ_ROBOT, child_id, room, &pos, &orient, parent, &v);
|
|
|
|
// Now create left Betty
|
|
v = -orient.fvec + (0.3f * orient.rvec) - orient.uvec;
|
|
vm_VectorNormalize(&v);
|
|
v *= 30.0f;
|
|
child_handle = Obj_Create(OBJ_ROBOT, child_id, room, &pos, &orient, parent, &v);
|
|
|
|
// Now create right Betty
|
|
v = -orient.fvec - (0.3f * orient.rvec) - orient.uvec;
|
|
vm_VectorNormalize(&v);
|
|
v *= 30.0f;
|
|
child_handle = Obj_Create(OBJ_ROBOT, child_id, room, &pos, &orient, parent, &v);
|
|
}
|
|
|
|
memory->explode = true;
|
|
|
|
} break;
|
|
case EVT_INTERVAL: {
|
|
if (memory && memory->explode) {
|
|
ObjectRemove(data->me_handle);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
void BettyScript::DoInit(int me) {
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(tBettyInfo);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = me;
|
|
|
|
memory = (tBettyInfo *)Scrpt_MemAlloc(&ch);
|
|
|
|
memory->explode = false;
|
|
memory->lasttime = 0;
|
|
}
|
|
|
|
int16_t BettyScript::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_MEMRESTORE: {
|
|
memory = (tBettyInfo *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
|
|
case EVT_CREATED: {
|
|
// create our memory
|
|
DoInit(data->me_handle);
|
|
|
|
int parent, type;
|
|
vector vel;
|
|
|
|
parent = GetObjectParent(data->me_handle);
|
|
|
|
if (parent != OBJECT_HANDLE_NONE) {
|
|
// Give it velocity
|
|
Obj_Value(parent, VF_GET, OBJV_V_VELOCITY, &vel);
|
|
vel.x = vel.x * -0.5;
|
|
vel.y = vel.y * -0.5;
|
|
vel.z = vel.z * -0.5;
|
|
Obj_Value(data->me_handle, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
}
|
|
|
|
tOSIRISTIMER timer_info;
|
|
timer_info.flags = 0;
|
|
timer_info.repeat_count = 0;
|
|
timer_info.object_handle = data->me_handle;
|
|
timer_info.object_handle_detonator = OBJECT_HANDLE_NONE;
|
|
timer_info.timer_interval = 15.0f;
|
|
timer_info.id = CREATE_TIMER_ID(0x1A);
|
|
Scrpt_CreateTimer(&timer_info);
|
|
|
|
} break;
|
|
case EVT_COLLIDE: {
|
|
float velmag;
|
|
vector vel;
|
|
|
|
if (GetObjectType(data->evt_collide.it_handle) == OBJ_PLAYER) {
|
|
|
|
if (!memory->explode) {
|
|
memory->explode = true;
|
|
|
|
matrix orient;
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_V_VELOCITY, &vel);
|
|
velmag = vm_GetMagnitude(&vel);
|
|
velmag = velmag / 4;
|
|
|
|
int room;
|
|
vector pos;
|
|
int weapon_id;
|
|
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
weapon_id = Wpn_FindID("TubbsHitBlast");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, &orient, GetObjectParent(data->me_handle));
|
|
|
|
ObjectDamage(data->evt_collide.it_handle, velmag, data->me_handle);
|
|
}
|
|
}
|
|
} break;
|
|
case EVT_INTERVAL: {
|
|
float time, velmag, scale;
|
|
vector vel;
|
|
|
|
time = data->evt_interval.game_time;
|
|
|
|
if (time > memory->lasttime + 0.10) {
|
|
// check to see if we need to limit speed
|
|
memory->lasttime = time;
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_V_VELOCITY, &vel);
|
|
velmag = vm_GetMagnitude(&vel);
|
|
|
|
if (velmag > 120.0f) {
|
|
// limit the velocity
|
|
scale = 120.0f / velmag;
|
|
vel.x = scale * vel.x;
|
|
vel.y = scale * vel.y;
|
|
vel.z = scale * vel.z;
|
|
Obj_Value(data->me_handle, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
}
|
|
}
|
|
|
|
if (memory && memory->explode) {
|
|
ObjectRemove(data->me_handle);
|
|
}
|
|
} break;
|
|
|
|
case EVT_TIMER: {
|
|
float amount;
|
|
amount = GetObjectShields(data->me_handle) + 10.0f;
|
|
ObjectDamage(data->me_handle, amount);
|
|
} break;
|
|
}
|
|
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Chaff class
|
|
//------------------
|
|
|
|
void ChaffScript::DoInit(int handle) {
|
|
vector vel, v, pos;
|
|
|
|
int count, chaffcount;
|
|
int chunk_id, room;
|
|
int parent, ch, type;
|
|
|
|
// Allocate the memory for the script
|
|
tOSIRISMEMCHUNK mem;
|
|
mem.id = 4;
|
|
mem.size = sizeof(tChaffInfo);
|
|
mem.my_id.type = OBJECT_SCRIPT;
|
|
mem.my_id.objhandle = handle;
|
|
|
|
memory = (tChaffInfo *)Scrpt_MemAlloc(&mem);
|
|
memory->killme = false;
|
|
|
|
parent = GetObjectParent(handle);
|
|
chunk_id = Obj_FindID("ChaffChunk");
|
|
|
|
if (parent != OBJECT_HANDLE_NONE && chunk_id != -1) {
|
|
matrix orient;
|
|
Obj_Value(handle, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
Obj_Value(parent, VF_GET, OBJV_V_VELOCITY, &vel);
|
|
Obj_Value(handle, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(handle, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
chaffcount = 10;
|
|
for (count = 0; count < chaffcount; count++) {
|
|
v = -vel;
|
|
v.x += (rand() % 30) - 15;
|
|
v.y += (rand() % 30) - 15;
|
|
v.z += (rand() % 30) - 15;
|
|
|
|
ch = Obj_Create(OBJ_ROBOT, chunk_id, room, &pos, &orient, parent, &v);
|
|
}
|
|
|
|
type = GetObjectType(parent);
|
|
if (type == OBJ_PLAYER || type == OBJ_GHOST) {
|
|
int sound_id = Sound_FindId("Drop bomb");
|
|
if (sound_id >= 0) {
|
|
Sound_Play3d(parent, sound_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
memory->killme = true;
|
|
}
|
|
|
|
void ChaffScript::DoInterval(int handle) {
|
|
if (memory && memory->killme) {
|
|
ObjectRemove(handle);
|
|
}
|
|
}
|
|
|
|
int16_t ChaffScript::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_MEMRESTORE: {
|
|
memory = (tChaffInfo *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
case EVT_CREATED: {
|
|
DoInit(data->me_handle);
|
|
} break;
|
|
case EVT_INTERVAL: {
|
|
DoInterval(data->me_handle);
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
void ChaffChunkScript::DoInit(int handle) {
|
|
// Allocate the memory for the script
|
|
tOSIRISMEMCHUNK ch;
|
|
ch.id = 4;
|
|
ch.size = sizeof(tChaffChunkInfo);
|
|
ch.my_id.type = OBJECT_SCRIPT;
|
|
ch.my_id.objhandle = handle;
|
|
|
|
memory = (tChaffChunkInfo *)Scrpt_MemAlloc(&ch);
|
|
memory->killme = false;
|
|
memory->lifeleft = 30.0f;
|
|
|
|
int parent;
|
|
vector vel;
|
|
parent = GetObjectParent(handle);
|
|
|
|
if (parent != OBJECT_HANDLE_NONE) {
|
|
Obj_Value(parent, VF_GET, OBJV_V_VELOCITY, &vel);
|
|
Obj_Value(handle, VF_SET, OBJV_V_VELOCITY, &vel);
|
|
}
|
|
}
|
|
|
|
void ChaffChunkScript::DoInterval(int handle, float frametime) {
|
|
if (!memory)
|
|
return;
|
|
|
|
memory->lifeleft -= frametime;
|
|
if (!memory->killme && memory->lifeleft < 0) {
|
|
int weapon_id;
|
|
matrix orient;
|
|
|
|
memory->killme = true;
|
|
|
|
Obj_Value(handle, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
int room;
|
|
vector pos;
|
|
|
|
Obj_Value(handle, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(handle, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
weapon_id = Wpn_FindID("ChaffSpark");
|
|
|
|
int parent = GetObjectParent(handle);
|
|
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, &orient, parent);
|
|
}
|
|
|
|
if (memory->killme) {
|
|
ObjectRemove(handle);
|
|
}
|
|
}
|
|
|
|
void ChaffChunkScript::DoCollide(tOSIRISEventInfo *data) {
|
|
int weapon_id;
|
|
matrix orient;
|
|
|
|
if (!memory->killme) {
|
|
memory->killme = true;
|
|
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_M_ORIENT, &orient);
|
|
|
|
int room;
|
|
vector pos;
|
|
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(data->me_handle, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
weapon_id = Wpn_FindID("ChaffSpark");
|
|
|
|
int parent = GetObjectParent(data->me_handle);
|
|
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, &orient, parent);
|
|
}
|
|
}
|
|
|
|
int16_t ChaffChunkScript::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_MEMRESTORE: {
|
|
memory = (tChaffChunkInfo *)data->evt_memrestore.memory_ptr;
|
|
} break;
|
|
case EVT_CREATED: {
|
|
DoInit(data->me_handle);
|
|
} break;
|
|
case EVT_INTERVAL: {
|
|
DoInterval(data->me_handle, data->evt_interval.frame_time);
|
|
} break;
|
|
case EVT_COLLIDE: {
|
|
DoCollide(data);
|
|
} break;
|
|
case EVT_AI_INIT: {
|
|
Obj_SetCustomAnim(data->me_handle, 0.0f, 4.0f, 1.0f, 0);
|
|
} break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// ProxMine class
|
|
//------------------
|
|
|
|
void ProxMine::DoInit(int me) {
|
|
int parent = GetObjectParent(me);
|
|
int sound_id = Sound_FindId("Drop bomb");
|
|
if (sound_id >= 0) {
|
|
Sound_Play3d(parent, sound_id);
|
|
}
|
|
}
|
|
|
|
void ProxMine::DoCollide(int me) {
|
|
int room;
|
|
vector pos;
|
|
int weapon_id;
|
|
|
|
Obj_Value(me, VF_GET, OBJV_I_ROOMNUM, &room);
|
|
Obj_Value(me, VF_GET, OBJV_V_POS, &pos);
|
|
|
|
weapon_id = Wpn_FindID("TubbsHitBlast");
|
|
Obj_Create(OBJ_WEAPON, weapon_id, room, &pos, NULL, me);
|
|
|
|
msafe_struct mo;
|
|
mo.objhandle = me;
|
|
|
|
MSafe_CallFunction(MSAFE_OBJECT_REMOVE, &mo);
|
|
}
|
|
|
|
int16_t ProxMine::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_AI_INIT:
|
|
DoInit(data->me_handle);
|
|
break;
|
|
case EVT_COLLIDE:
|
|
DoCollide(data->me_handle);
|
|
break;
|
|
}
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|
|
|
|
//------------------
|
|
// Gunboy class
|
|
//------------------
|
|
int16_t Gunboy::CallEvent(int event, tOSIRISEventInfo *data) {
|
|
switch (event) {
|
|
case EVT_CREATED: {
|
|
int parent = GetObjectParent(data->me_handle);
|
|
int sound_id = Sound_FindId("Drop bomb");
|
|
if (sound_id >= 0) {
|
|
Sound_Play3d(parent, sound_id, 1);
|
|
}
|
|
|
|
tOSIRISTIMER timer_info;
|
|
timer_info.flags = 0;
|
|
timer_info.repeat_count = 0;
|
|
timer_info.object_handle = data->me_handle;
|
|
timer_info.object_handle_detonator = OBJECT_HANDLE_NONE;
|
|
timer_info.timer_interval = 160.0f;
|
|
timer_info.id = CREATE_TIMER_ID(0x2D);
|
|
Scrpt_CreateTimer(&timer_info);
|
|
|
|
} break;
|
|
|
|
case EVT_TIMER: {
|
|
float amount;
|
|
amount = GetObjectShields(data->me_handle) + 60.0f;
|
|
ObjectDamage(data->me_handle, amount);
|
|
} break;
|
|
}
|
|
|
|
return CONTINUE_CHAIN | CONTINUE_DEFAULT;
|
|
}
|