Test_GTH.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "scripts.h"
  19. #include <string.h>
  20. #include "toolkit.h"
  21. #define GTH_DEBUG 0
  22. #if (GTH_DEBUG)
  23. #define GTH_DEBUG_INT( x , format ) Commands->Display_Int( x, format )
  24. #define GTH_DEBUG_FLOAT( x , format ) Commands->Display_Float( x, format )
  25. #else
  26. #define GTH_DEBUG_INT( x , format )
  27. #define GTH_DEBUG_FLOAT( x , format )
  28. #endif
  29. /*
  30. ** GTH_Drop_Object_On_Death (verified)
  31. ** This script will create an object at the position of the object when it dies.
  32. **
  33. ** Params:
  34. ** Drop_Object - name of the preset to create an instance of
  35. ** Drop_Height - float meters to add to the Z coord of the original object when creating the drop obj
  36. ** Probability - int between 1 and 100, chance that the object will be created
  37. */
  38. DECLARE_SCRIPT(GTH_Drop_Object_On_Death, "Drop_Object=:string,Drop_Height=0.25:float,Probability=100:int")
  39. {
  40. void Killed( GameObject * obj, GameObject * killer )
  41. {
  42. const char * obj_name = Get_Parameter("Drop_Object");
  43. bool doit = false;
  44. float probability = Get_Int_Parameter("Probability");
  45. if (probability >= 100) {
  46. doit = true;
  47. } else {
  48. int random = Commands->Get_Random_Int(0,100);
  49. doit = random < probability;
  50. }
  51. if ((obj_name != NULL) && (doit)) {
  52. Vector3 spawn_location = Commands->Get_Position ( obj );
  53. spawn_location.Z = spawn_location.Z + Get_Float_Parameter("Drop_Height");
  54. Commands->Create_Object ( obj_name, spawn_location );
  55. }
  56. }
  57. };
  58. /*
  59. ** GTH_Drop_Object_On_Death_Zone (verified)
  60. ** This script is just like the other drop object on death except that it must also
  61. ** be activated by a custom message from another script. Use the GTH_Zone_Send_Custom
  62. ** to enable and disable this script.
  63. **
  64. ** Params:
  65. ** Custom_Message - message id that turns this script on or off, use message ID's greater than 10000!
  66. ** Drop_Object - name of the preset to create an instance of
  67. ** Drop_Height - float meters to add to the Z coord of the original object when creating the drop obj
  68. ** Probability - int between 1 and 100, chance that the object will be created
  69. */
  70. DECLARE_SCRIPT(GTH_Drop_Object_On_Death_Zone, "Custom_Message=20000:int,Drop_Object=:string,Drop_Height=0.25:float,Probability=100:int")
  71. {
  72. bool enabled;
  73. REGISTER_VARIABLES()
  74. {
  75. SAVE_VARIABLE(enabled, 1);
  76. }
  77. void Created( GameObject * obj )
  78. {
  79. enabled = false;
  80. }
  81. void Custom(GameObject * obj, int type, int param, GameObject * sender)
  82. {
  83. if (type == Get_Int_Parameter("Custom_Message")) {
  84. enabled = (param==1);
  85. GTH_DEBUG_INT(enabled,"GTH_Drop_Object_On_Death_Zone custom message: enable = %d\n");
  86. }
  87. }
  88. void Killed( GameObject * obj, GameObject * killer )
  89. {
  90. GTH_DEBUG_INT(0,"GTH_Drop_Object_On_Death_Zone Killed callback\n");
  91. if (!enabled) {
  92. GTH_DEBUG_INT(0,"not in zone\n");
  93. return;
  94. }
  95. const char * obj_name = Get_Parameter("Drop_Object");
  96. bool doit = false;
  97. float probability = Get_Int_Parameter("Probability");
  98. if (probability >= 100) {
  99. doit = true;
  100. } else {
  101. int random = Commands->Get_Random_Int(0,100);
  102. doit = random < probability;
  103. }
  104. if ((obj_name != NULL) && (doit)) {
  105. Vector3 spawn_location = Commands->Get_Position ( obj );
  106. spawn_location.Z = spawn_location.Z + Get_Float_Parameter("Drop_Height");
  107. GTH_DEBUG_INT(0,"Creating object ");
  108. GTH_DEBUG_INT(0,obj_name);
  109. GTH_DEBUG_FLOAT(spawn_location.X," x=%f, ");
  110. GTH_DEBUG_FLOAT(spawn_location.Y," y=%f, ");
  111. GTH_DEBUG_FLOAT(spawn_location.Z," z=%f, ");
  112. Commands->Create_Object ( obj_name, spawn_location );
  113. }
  114. }
  115. };
  116. /*
  117. ** GTH_Zone_Send_Custom (verified)
  118. ** This script lets you send a custom message to an object on enter and exit of a zone. To talk
  119. ** to the "drop in death zone" script, send the same custom message with 1 for Enter_Param and
  120. ** 0 for Exit_Param...
  121. **
  122. ** Params:
  123. ** Enter_Message = message id to send when an object enters this zone
  124. ** Enter_Param = message parameter to send when an object enters
  125. ** Exit_Message = message id to send when an object exits
  126. ** Exit_Param = message id to send when and object exits
  127. */
  128. DECLARE_SCRIPT(GTH_Zone_Send_Custom, "Enter_Message=20000:int,Enter_Param=1:int,Exit_Message=20000:int,Exit_Param=0:int")
  129. {
  130. void Entered( GameObject * obj, GameObject * enterer )
  131. {
  132. int message = Get_Int_Parameter("Enter_Message");
  133. GTH_DEBUG_INT(message,"GTH_Zone_Send_Custom sending enter message %d\n");
  134. if (message != 0) {
  135. Commands->Send_Custom_Event(obj,enterer,message,Get_Int_Parameter("Enter_Param"));
  136. }
  137. }
  138. void Exited( GameObject * obj, GameObject * exiter )
  139. {
  140. int message = Get_Int_Parameter("Exit_Message");
  141. GTH_DEBUG_INT(message,"GTH_Zone_Send_Custom sending exit message %d\n");
  142. if (message != 0) {
  143. Commands->Send_Custom_Event(obj,exiter,message,Get_Int_Parameter("Exit_Param"));
  144. }
  145. }
  146. };
  147. /*
  148. ** GTH_Create_Object_On_Enter (verified)
  149. ** This script will create an object when a script zone is entered by a game object. Use it
  150. ** to fire off cinematics for example...
  151. **
  152. ** Params:
  153. ** Create_Object - name of the preset to create an instance of
  154. ** Position - world space position to create the object at
  155. ** Min_Delay - amount of time to wait before re-enabling the script once it has fired
  156. ** Max_Creations - maximum number of times the script should create an object
  157. ** Probability - integer between 1 and 100, chance on any given "Enter" that the object will be created
  158. ** Player_Type - type of player that can trigger integer, 0 = Nod, 1 = GDI, 2 = any
  159. */
  160. DECLARE_SCRIPT(GTH_Create_Object_On_Enter, "Create_Object=:string,Position:vector3,Min_Delay=10:int,Max_Creations=1:int,Probability=100:int,Player_Type=2:int")
  161. {
  162. bool script_enabled;
  163. int trigger_count;
  164. enum { TIMER_ID_REENABLE = 0 };
  165. public:
  166. GTH_Create_Object_On_Enter(void) :
  167. script_enabled(true),
  168. trigger_count(0)
  169. {
  170. }
  171. protected:
  172. REGISTER_VARIABLES()
  173. {
  174. SAVE_VARIABLE(script_enabled, 1);
  175. SAVE_VARIABLE(trigger_count, 2);
  176. }
  177. void Entered( GameObject * obj, GameObject * enterer )
  178. {
  179. if (script_enabled == false) {
  180. return;
  181. }
  182. // Check player type, if it doesn't match, just return
  183. int our_player_type = Get_Int_Parameter("Player_Type");
  184. if (our_player_type != 2) {
  185. int enter_player_type = Commands->Get_Player_Type(enterer);
  186. if (enter_player_type != our_player_type) {
  187. return;
  188. }
  189. }
  190. // Check probability, if the check fails, just return
  191. float probability = Get_Int_Parameter("Probability");
  192. if (probability < 100) {
  193. int random = Commands->Get_Random_Int(0,100);
  194. if (random > probability) {
  195. return;
  196. }
  197. }
  198. GTH_DEBUG_INT( 0, "Enter callback in GTH_Create_Object_On_Enter" );
  199. // Inc our trigger count, if we're allowed to fire again, set a timer to re-enable the script
  200. trigger_count++;
  201. script_enabled = false;
  202. if (trigger_count < Get_Int_Parameter("Max_Creations")) {
  203. Commands->Start_Timer(obj, this, Get_Float_Parameter("Min_Delay"), TIMER_ID_REENABLE);
  204. }
  205. // Ok, create the object
  206. const char * obj_name = Get_Parameter("Create_Object");
  207. Vector3 obj_pos = Get_Vector3_Parameter("Position");
  208. if (obj_name != NULL) {
  209. GTH_DEBUG_INT( 0, obj_name );
  210. GTH_DEBUG_INT( obj_pos.X, " x: %f, " );
  211. GTH_DEBUG_INT( obj_pos.Y, "y: %f, " );
  212. GTH_DEBUG_INT( obj_pos.Z, "z: %f\n" );
  213. Commands->Create_Object ( obj_name, obj_pos );
  214. }
  215. }
  216. void Timer_Expired( GameObject * obj, int timer_id )
  217. {
  218. if (timer_id == TIMER_ID_REENABLE) {
  219. script_enabled = true;
  220. }
  221. }
  222. };
  223. #if 0
  224. /*
  225. ** GTH_Speed_Controlled_Anim
  226. ** NOTE: THIS PROBABLY WON'T WORK IN A NET GAME ANYWAY.... DELETE ME
  227. ** Stop_Speed - float, maximum speed where the object is considered "stopped"
  228. ** Stop_Anim - name of the anim to play when the unit is "stopped"
  229. ** Walk_Speed - float, max speed where the object is considered "walking"
  230. ** Walk_Anim - name of the anim to play when the unit is "walking"
  231. ** Run_Anim - name of anim to play when the unit is moving faster than "walking"
  232. ** Update_Delay - delay between re-evaluating the state of the object (0.1 = 10 times a second)
  233. */
  234. DECLARE_SCRIPT(GTH_Speed_Controlled_Anim,"Stop_Speed=0.1:float,StopAnim=none:string,Walk_Speed=5.0:float,Walk_Anim=none:string,Run_Anim=none:string,Update_Rate=0.1:float")
  235. {
  236. enum { STOPPED = 0, WALKING, RUNNING };
  237. enum { TIMER_ID_TICK = 0xbeef };
  238. int cur_state;
  239. Vector3 last_pos;
  240. REGISTER_VARIABLES()
  241. {
  242. SAVE_VARIABLE(cur_state, 1);
  243. SAVE_VARIABLE(last_pos, 2);
  244. }
  245. void Created( GameObject * obj )
  246. {
  247. // initialize the state
  248. cur_state = -1;
  249. last_pos = Commands->Get_Position( obj );
  250. update_state(obj,eval_speed(obj));
  251. // start up our update timer
  252. Commands->Start_Timer(obj, this, Get_Float_Parameter("Update_Rate"), TIMER_ID_TICK);
  253. }
  254. void ( * Set_Animation )( GameObject * obj, const char * anim_name, bool looping, const char * sub_obj_name = NULL, float start_frame = 0.0F, float end_frame = -1.0F, bool is_blended = false );
  255. void Timer_Expired( GameObject * obj, int timer_id )
  256. {
  257. if (timer_id == TIMER_ID_TICK) {
  258. update_state(obj,eval_speed(obj));
  259. Commands->Start_Timer(obj, this, get_update_rate(), TIMER_ID_TICK);
  260. }
  261. }
  262. float get_update_rate( void )
  263. {
  264. float update_rate = Get_Float_Parameter("Update_Rate");
  265. if (update_rate <= 0.01f) {
  266. update_rate = 0.01f;
  267. }
  268. return update_rate;
  269. }
  270. int eval_speed( GameObject * obj )
  271. {
  272. // This function evaluates the object's current speed and returns the state
  273. Vector3 cur_pos = Commands->Get_Position(obj);
  274. Vector3 vel = (cur_pos - last_pos) / get_update_rate();
  275. last_pos = cur_pos;
  276. float speed = vel.Length();
  277. if (speed <= Get_Float_Parameter("Stop_Speed")) {
  278. return STOPPED;
  279. }
  280. if (speed <= Get_Float_Parameter("Walk_Speed")) {
  281. return WALKING;
  282. }
  283. return RUNNING;
  284. }
  285. void update_state( GameObject * obj, int new_state )
  286. {
  287. // This function plugs in the animation depending on the current state
  288. if (new_state != cur_state) {
  289. cur_state = new_state;
  290. const char * anim = NULL;
  291. switch( cur_state )
  292. {
  293. case STOPPED:
  294. {
  295. anim = Get_Parameter("Stop_Anim");
  296. }
  297. break;
  298. case WALKING:
  299. {
  300. anim = Get_Parameter("Walk_Anim");
  301. }
  302. break;
  303. case RUNNING:
  304. {
  305. anim = Get_Parameter("Run_Anim");
  306. }
  307. break;
  308. };
  309. if (anim != NULL) {
  310. Commands->Set_Animation( obj, anim, true );
  311. }
  312. }
  313. }
  314. };
  315. #endif
  316. /*
  317. ** GTH_On_Enter_Mission_Complete (verified)
  318. ** When you enter a zone with this script on it, the mission is complete. NOTE, this only
  319. ** works in single player levels
  320. **
  321. ** Parameters:
  322. ** Success - 0 = mission failed, 1 = missuion succeeded
  323. ** Player_Type - type of player allowed to trigger, 0=nod, 1=gdi, 2=any
  324. */
  325. DECLARE_SCRIPT(GTH_On_Enter_Mission_Complete, "Success=1:int, Player_Type=2:int" )
  326. {
  327. void Entered( GameObject * obj, GameObject * enterer )
  328. {
  329. GTH_DEBUG_INT(0,"GTH_On_Enter_Mission_Complete::Entered\n");
  330. // check the player type
  331. int our_player_type = Get_Int_Parameter("Player_Type");
  332. GTH_DEBUG_INT(our_player_type,"Script desired player type: %d\n");
  333. if (our_player_type != 2) {
  334. int enter_player_type = Commands->Get_Player_Type(enterer);
  335. GTH_DEBUG_INT(enter_player_type,"Enterer player type: %d\n");
  336. if (enter_player_type != our_player_type) {
  337. return;
  338. }
  339. }
  340. // if we get here, complete the mission
  341. int success = Get_Int_Parameter("Success");
  342. GTH_DEBUG_INT(success,"Calling Mission_Complete(%d)\n");
  343. if (success == 0) {
  344. Commands->Mission_Complete(false);
  345. } else {
  346. Commands->Mission_Complete(true);
  347. }
  348. }
  349. };
  350. /*
  351. ** GTH_On_Death_Mission_Complete (verified)
  352. ** When you kill something this script on it, the mission is complete. NOTE this
  353. ** only works in single-player levels.
  354. **
  355. ** Parameters:
  356. ** Success - 0 = mission failed, 1 = missuion succeeded
  357. ** Player_Type - type of player allowed to trigger, 0=nod, 1=gdi, 2=any
  358. */
  359. DECLARE_SCRIPT(GTH_On_Killed_Mission_Complete, "Success=1:int, Player_Type=2:int" )
  360. {
  361. void Killed( GameObject * obj, GameObject * killer )
  362. {
  363. GTH_DEBUG_INT(0,"GTH_On_Killed_Mission_Complete::Killed\n");
  364. // check the player type
  365. int our_player_type = Get_Int_Parameter("Player_Type");
  366. GTH_DEBUG_INT(our_player_type,"Script desired player type: %d\n");
  367. if (our_player_type != 2) {
  368. int enter_player_type = Commands->Get_Player_Type(killer);
  369. GTH_DEBUG_INT(enter_player_type,"Enterer player type: %d\n");
  370. if (enter_player_type != our_player_type) {
  371. return;
  372. }
  373. }
  374. // if we get here, complete the mission
  375. int success = Get_Int_Parameter("Success");
  376. GTH_DEBUG_INT(success,"Calling Mission_Complete(%d)\n");
  377. if (success == 0) {
  378. Commands->Mission_Complete(false);
  379. } else {
  380. Commands->Mission_Complete(true);
  381. }
  382. }
  383. };
  384. /*
  385. ** GTH_Create_Objective
  386. ** Adds an objective to the mission when the specified action (create, enter, poke, or kill)
  387. ** happens to the object with this script on it.
  388. **
  389. ** params:
  390. ** Creation_Type - 0=Create, 1=Entered, 2=Poked, 3=Killed
  391. ** Objective_ID - id of the objective, match this with the "GTH_Objective_Complete" script
  392. ** Objective_Type - 0=PRIMARY, 1=SECONDARY
  393. ** Short_Desc_ID - string id for short description
  394. ** Long_Desc_ID - string id for long description
  395. ** Priority - priority of this objective
  396. ** Position - 3d position of the objective
  397. ** Pog_Texture - tga file for the objective pog
  398. ** Pog_Text_ID - string id for the pog text (usually something like IDS_POG_DESTROY)
  399. **
  400. */
  401. DECLARE_SCRIPT(GTH_Create_Objective,"Creation_Type=0:int,Objective_ID=0:int,Objective_Type=0:int,Short_Desc_ID=0:int,Long_Desc_ID=0:int,Priority=90:float,Position:vector3,Pog_Texture:string")
  402. {
  403. bool already_triggered;
  404. REGISTER_VARIABLES()
  405. {
  406. SAVE_VARIABLE(already_triggered, 1);
  407. }
  408. void Created( GameObject * obj ) { if (Get_Int_Parameter("Creation_Type") == 0) create_objective(); }
  409. void Entered( GameObject * obj, GameObject * enterer ) { if (Get_Int_Parameter("Creation_Type") == 1) create_objective(); }
  410. void Poked( GameObject * obj, GameObject * poker ) { if (Get_Int_Parameter("Creation_Type") == 2) create_objective(); }
  411. void Killed( GameObject * obj, GameObject * killer ) { if (Get_Int_Parameter("Creation_Type") == 3) create_objective(); }
  412. void create_objective(void)
  413. {
  414. if (already_triggered) {
  415. return;
  416. }
  417. // create the objective
  418. int id = Get_Int_Parameter("Objective_ID");
  419. int type;
  420. if (Get_Int_Parameter("Objective_Type") == 0) {
  421. type = OBJECTIVE_TYPE_PRIMARY;
  422. } else {
  423. type = OBJECTIVE_TYPE_SECONDARY;
  424. }
  425. int short_desc_id = Get_Int_Parameter("Short_Desc_ID");
  426. int long_desc_id = Get_Int_Parameter("Long_Desc_ID");
  427. Commands->Add_Objective(id,type,OBJECTIVE_STATUS_PENDING,short_desc_id, NULL, long_desc_id );
  428. // set up the radar blip
  429. Vector3 objective_pos = Get_Vector3_Parameter("Position");
  430. Commands->Set_Objective_Radar_Blip(id, objective_pos);
  431. // set up the hud stuff
  432. const char * pog = Get_Parameter("Pog_Texture");
  433. float priority = Get_Float_Parameter("Priority");
  434. int pog_text_id = Get_Int_Parameter("Pog_Text_ID");
  435. Commands->Set_Objective_HUD_Info_Position(id, priority, pog, pog_text_id, objective_pos);
  436. }
  437. };
  438. /*
  439. ** GTH_Objective_Complete
  440. ** Ends an objective with either success or failure. All of the following things
  441. ** cause the objective to complete: "Entered", "Killed", or "Poked"
  442. **
  443. ** param
  444. ** Objective_ID - id of the objective
  445. ** Success - 0 or 1, success or failure
  446. ** Player_Type - player type allowed to trigger this. 0=nod, 1=gdi, 2=any
  447. */
  448. DECLARE_SCRIPT(GTH_Objective_Complete_Enter_Kill_Poke, "Objective_ID=0:int, Success=1:int, Player_Type=2:int" )
  449. {
  450. bool already_triggered;
  451. REGISTER_VARIABLES()
  452. {
  453. SAVE_VARIABLE(already_triggered, 1);
  454. }
  455. void Created( GameObject * obj ) { already_triggered = false; }
  456. void Poked( GameObject * obj, GameObject * poker ) { trigger(poker); }
  457. void Killed( GameObject * obj, GameObject * killer ) { trigger(killer); }
  458. void Entered( GameObject * obj, GameObject * enterer ) { trigger(enterer); }
  459. void trigger(GameObject * obj)
  460. {
  461. // check if we've already triggered
  462. if (already_triggered) {
  463. return;
  464. }
  465. // check the player type
  466. int our_player_type = Get_Int_Parameter("Player_Type");
  467. if (our_player_type != 2) {
  468. int other_player_type = Commands->Get_Player_Type(obj);
  469. if (other_player_type != our_player_type) {
  470. return;
  471. }
  472. }
  473. // if we get here, complete the mission
  474. int success = Get_Int_Parameter("Success");
  475. int id = Get_Int_Parameter("Objective_ID");
  476. if (success == 0) {
  477. Commands->Set_Objective_Status(id, 0);
  478. } else {
  479. Commands->Set_Objective_Status(id, 1);
  480. }
  481. already_triggered = true;
  482. }
  483. };
  484. /*
  485. ** GTH_User_Controllable_Base_Defense (verified)
  486. ** Just like M00_Base_Defense except that if a player enters, he can control the object
  487. **
  488. ** params:
  489. ** MinAttackDistance - min range for auto attack
  490. ** MaxAttackDistance - max range for auto attack
  491. ** AttackTimer - amount of time to continue tracking after last "enemy seen"
  492. */
  493. DECLARE_SCRIPT (GTH_User_Controllable_Base_Defense, "MinAttackDistance=0:int, MaxAttackDistance=300:int, AttackTimer=10:int")
  494. {
  495. int token_01_id;
  496. int token_02_id;
  497. int token_03_id;
  498. int player_type;
  499. bool occupied;
  500. REGISTER_VARIABLES()
  501. {
  502. SAVE_VARIABLE (token_01_id, 1);
  503. SAVE_VARIABLE (token_02_id, 2);
  504. SAVE_VARIABLE (token_03_id, 3);
  505. SAVE_VARIABLE (player_type, 4);
  506. SAVE_VARIABLE (occupied, 5);
  507. }
  508. void Created (GameObject * obj)
  509. {
  510. occupied = false;
  511. // find out what my team preset is.
  512. player_type = Commands->Get_Player_Type( obj );
  513. Commands->Debug_Message( "***** Player Type Saved *****\n" );
  514. Commands->Enable_Hibernation (obj, false);
  515. Commands->Innate_Enable (obj);
  516. Commands->Enable_Enemy_Seen (obj, true);
  517. Vector3 my_position = Commands->Get_Position (obj);
  518. Vector3 token_01_pos = my_position;
  519. Vector3 token_02_pos = my_position;
  520. Vector3 token_03_pos = my_position;
  521. token_01_pos.X -= 10.0f;
  522. token_01_pos.Y -= 10.0f;
  523. token_01_pos.Z += 2.0f;
  524. token_02_pos.X += 10.0f;
  525. token_02_pos.Z += 2.0f;
  526. token_03_pos.X += 10.0f;
  527. token_03_pos.Y -= 10.0f;
  528. token_03_pos.Z += 2.0f;
  529. GameObject * token_01 = Commands->Create_Object ("Invisible_Object", token_01_pos);
  530. if (token_01)
  531. {
  532. token_01_id = Commands->Get_ID (token_01);
  533. }
  534. token_01 = Commands->Create_Object ("Invisible_Object", token_02_pos);
  535. if (token_01)
  536. {
  537. token_02_id = Commands->Get_ID (token_01);
  538. }
  539. token_01 = Commands->Create_Object ("Invisible_Object", token_03_pos);
  540. if (token_01)
  541. {
  542. token_03_id = Commands->Get_ID (token_01);
  543. }
  544. Commands->Start_Timer (obj, this, 10.0f, 1);
  545. }
  546. void Timer_Expired (GameObject * obj, int timer_id)
  547. {
  548. if (timer_id == 1)
  549. {
  550. int rnd_num = Get_Int_Random(0,2);
  551. if (occupied == false) {
  552. GTH_DEBUG_INT(rnd_num,"GTH_User_Controllable_Base_Defense aiming at target %d\n");
  553. switch (rnd_num)
  554. {
  555. case (0):
  556. {
  557. GameObject * token_01 = Commands->Find_Object (token_01_id);
  558. if (token_01)
  559. {
  560. ActionParamsStruct params;
  561. params.Set_Basic(this, 70, 1);
  562. params.Set_Attack(token_01, 0.0f, 0.0f, true);
  563. Commands->Action_Attack(obj, params);
  564. }
  565. break;
  566. }
  567. case (1):
  568. {
  569. GameObject * token_02 = Commands->Find_Object (token_02_id);
  570. if (token_02)
  571. {
  572. ActionParamsStruct params;
  573. params.Set_Basic(this, 70, 1);
  574. params.Set_Attack(token_02, 0.0f, 0.0f, true);
  575. Commands->Action_Attack(obj, params);
  576. }
  577. break;
  578. }
  579. default:
  580. {
  581. GameObject * token_03 = Commands->Find_Object (token_03_id);
  582. if (token_03)
  583. {
  584. ActionParamsStruct params;
  585. params.Set_Basic(this, 70, 1);
  586. params.Set_Attack(token_03, 0.0f, 0.0f, true);
  587. Commands->Action_Attack(obj, params);
  588. }
  589. }
  590. }
  591. }
  592. Commands->Start_Timer (obj, this, 10.0f, 1);
  593. }
  594. else if (timer_id == 2)
  595. {
  596. Commands->Action_Reset (obj, 100.0f);
  597. }
  598. }
  599. void Enemy_Seen (GameObject * obj, GameObject * enemy)
  600. {
  601. GTH_DEBUG_INT(occupied,"GTH_User_Controllable_Base_Defense::Enemy_Seen, occupied = %d\n");
  602. if (occupied == false) {
  603. Vector3 my_loc = Commands->Get_Position (obj);
  604. Vector3 enemy_loc = Commands->Get_Position (enemy);
  605. float distance = Commands->Get_Distance (my_loc, enemy_loc);
  606. if (distance > Get_Int_Parameter("MinAttackDistance") )
  607. {
  608. ActionParamsStruct params;
  609. params.Set_Basic(this, 100, 2);
  610. params.Set_Attack(enemy, Get_Int_Parameter("MaxAttackDistance"), 0.0f, true);
  611. params.AttackCheckBlocked = false;
  612. Commands->Action_Attack(obj, params);
  613. Commands->Start_Timer (obj, this, Get_Int_Parameter( "AttackTimer"), 2);
  614. }
  615. }
  616. }
  617. void Action_Complete (GameObject * obj, int action_id, ActionCompleteReason complete_reason)
  618. {
  619. if (action_id == 2)
  620. {
  621. Commands->Action_Reset (obj, 100.0f);
  622. }
  623. }
  624. void Custom(GameObject * obj, int type, int param, GameObject * sender)
  625. {
  626. ActionParamsStruct params;
  627. switch (type)
  628. {
  629. case CUSTOM_EVENT_VEHICLE_ENTERED:
  630. occupied = true;
  631. params.Set_Basic(this, 100, 3);
  632. Commands->Action_Follow_Input( obj, params );
  633. break;
  634. case CUSTOM_EVENT_VEHICLE_EXITED:
  635. occupied = false;
  636. // set team back to my preset.
  637. Commands->Set_Player_Type( obj, player_type );
  638. Commands->Action_Reset (obj, 100.0f);
  639. break;
  640. }
  641. GTH_DEBUG_INT(occupied,"GTH_User_Controllable_Base_Defense occupied = %d\n");
  642. }
  643. };
  644. /*
  645. ** GTH_Credit_Trickle
  646. ** This script will give an amount money to its team at a regular interval. You can use it to
  647. ** create silos that give money as long as they're alive.
  648. ** NOTE: this won't work on buildings, only things like turrets, characters, or vehicles so make your
  649. ** "silos" as a weaponless vehcile set up like the nod-turret for example.
  650. **
  651. ** Params:
  652. ** Credits - number of credits to give
  653. ** Delay - time between credit grants
  654. */
  655. DECLARE_SCRIPT(GTH_Credit_Trickle, "Credits=1:int,Delay=2.0:float")
  656. {
  657. enum { TRICKLE_TIMER = 667 };
  658. void Created (GameObject * obj)
  659. {
  660. // start the trickle timer
  661. Commands->Start_Timer (obj, this, Get_Float_Parameter("Delay"), TRICKLE_TIMER);
  662. }
  663. void Timer_Expired (GameObject * obj, int timer_id)
  664. {
  665. if (timer_id == TRICKLE_TIMER)
  666. {
  667. Commands->Give_Money( obj, Get_Int_Parameter("Credits"), true );
  668. Commands->Start_Timer (obj, this, Get_Int_Parameter("Delay"), TRICKLE_TIMER);
  669. }
  670. }
  671. };
  672. /*
  673. ** GTH_Enable_Spawner_On_Enter
  674. ** This script will enable or disable a spawner when its zone is entered
  675. **
  676. ** Params:
  677. ** SpawnerID - id of the spawner
  678. ** Player_Type - type of player that can trigger integer, 0 = Nod, 1 = GDI, 2 = any
  679. ** Enable - enable or disable the spawner (1=enable, 0=disable)
  680. */
  681. DECLARE_SCRIPT(GTH_Enable_Spawner_On_Enter, "SpawnerID=0:int,Player_Type=2:int,Enable=1:int")
  682. {
  683. void Entered( GameObject * obj, GameObject * enterer )
  684. {
  685. // Check player type, if it doesn't match, just return
  686. int our_player_type = Get_Int_Parameter("Player_Type");
  687. if (our_player_type != 2) {
  688. int enter_player_type = Commands->Get_Player_Type(enterer);
  689. if (enter_player_type != our_player_type) {
  690. return;
  691. }
  692. }
  693. // Now enable the spawner
  694. bool enable = (Get_Int_Parameter("Enable") != 0);
  695. Commands->Enable_Spawner( Get_Int_Parameter("SpawnerID"),enable);
  696. }
  697. };
  698. /*
  699. ** GTH_CTF_Object
  700. ** This script will make the object it is attached to behave kind of like a CTF "flag" by
  701. ** attaching to the opposing player who pokes it. If its position gets within a
  702. ** certain distance of the "enemy home" an internal counter is incremented. Once the counter
  703. ** reaches a desired number, an object in the level is destroyed. This object should be the
  704. ** only building owned by the flag's team so that they immediately lose.
  705. **
  706. ** You should use this script on an object that has "projectile" collision only. Make a model
  707. ** of your "flag", give it projectile collision and make a preset for it similar to the "Marker_Flag"
  708. ** in LevelEdit (you can find "Marker_Flag" under Object->Simple).
  709. **
  710. ** CTF Rules as implemented:
  711. ** - this script controls how many flag captures cause a win
  712. ** - enemy entering the pickup radius of the flag attaches it to him
  713. ** - enemy teammates can take the flag from each other
  714. ** - friend poking the flag sends it back to its initial position (only if its not carried by an enemy)
  715. ** - enemy getting the flag to his home position increments an internal counter
  716. ** - when win count is reached, all of the "Win_Object_To_Kill" objects are destroyed (make sure
  717. ** this destroys all of the enemy team's buildings
  718. **
  719. ** Params:
  720. ** Update_Delay - how many times per second to update (this will *always* be laggy though...)
  721. ** Enemy_Player_Type - type of player that wants to grab this flag (0=Nod,1=GDI)
  722. ** Enemy_Home_Position - when flag gets here, capture count increments!
  723. ** Pickup_Radius - how close to flag we need to pickup flag
  724. ** Home_Radius - how close to enemy home position we need to get to count
  725. ** Capture_Respawn_Timer - When flag is dropped respawn back at home position after x seconds.
  726. ** x < 0 means flag MUST be poked for it to be returned.
  727. ** Captures_Needed_To_Win - after this many captures, we destroy the token "building" for the win
  728. ** Flag_Stolen_Event_ID - custom event id to send when the flag is stolen
  729. ** Flag_Stolen_Object_ID - Object that receives the event
  730. ** Flag_Lost_Event_ID - custom event id to send when enemy team gets flag back to Enemy_Home_Position :-(
  731. ** Flag_Lost_Object_ID - Object that receives the event
  732. ** Flag_Saved_Event_ID - custom event id to send when flag carrier is killed
  733. ** Flag_Saved_Object_ID - Object that receives the event
  734. ** Flag_Returned_Event_ID - custom event id to send when flag has been returned to home position.
  735. ** Flag_Returned_Object_ID - Object that receives the event
  736. ** Captures_Exceeded_Event_ID - custom event id to send when flag has been captured "Captures_Needed_To_Win" times.
  737. ** Captures_Exceeded_Object_ID - Object that receives the event
  738. ** Win_Object_To_Kill0 - object that we destroy when the capture count is reached
  739. ** Win_Object_To_Kill1 - object that we destroy when the capture count is reached
  740. ** Win_Object_To_Kill2 - object that we destroy when the capture count is reached
  741. ** Win_Object_To_Kill3 - object that we destroy when the capture count is reached
  742. ** Win_Object_To_Kill4 - object that we destroy when the capture count is reached
  743. **
  744. */
  745. DECLARE_SCRIPT(GTH_CTF_Object2, "Update_Delay=0.05:float,Enemy_Player_Type=0:int,Enemy_Home_Position:vector3,"
  746. "Pickup_Radius=5.0:float,Home_Radius=5.0:float,Capture_Respawn_Timer=30.0:float,"
  747. "Captures_Needed_To_Win=5:int,"
  748. "Flag_Stolen_Event_ID=25000:int,Flag_Stolen_Object_ID=0:int,Flag_Lost_Event_ID=25001:int,"
  749. "Flag_Lost_Object_ID=0:int,Flag_Saved_Event_ID=25002:int,Flag_Saved_Object_ID=0:int,"
  750. "Flag_Returned_Event_ID=25003:int,Flag_Returned_Object_ID=0:int,"
  751. "Captures_Exceeded_Event_ID=25004:int,Captures_Exceeded_Object_ID=0:int,"
  752. "Win_Object_To_Kill0=0:int,Win_Object_To_Kill1=0:int,Win_Object_To_Kill2=0:int,"
  753. "Win_Object_To_Kill3=0:int,Win_Object_To_Kill4=0:int")
  754. {
  755. enum { CTF_UPDATE_TIMER = 0x10211131 };
  756. int capture_count;
  757. int captured_by_id;
  758. Vector3 home_position;
  759. float capture_timer;
  760. REGISTER_VARIABLES()
  761. {
  762. SAVE_VARIABLE (capture_count, 1);
  763. SAVE_VARIABLE (captured_by_id, 2);
  764. SAVE_VARIABLE (home_position, 3);
  765. SAVE_VARIABLE (capture_timer, 4);
  766. }
  767. void Created (GameObject * obj)
  768. {
  769. // Initialize everything
  770. capture_count = 0;
  771. captured_by_id = 0;
  772. capture_timer = 0.0;
  773. home_position = Commands->Get_Position(obj);
  774. GTH_DEBUG_FLOAT(home_position.X,"Flag x: %f");
  775. GTH_DEBUG_FLOAT(home_position.Y,"Flag y: %f");
  776. GTH_DEBUG_FLOAT(home_position.Z,"Flag z: %f\n");
  777. // Start the update timer!
  778. Commands->Start_Timer (obj, this, Get_Float_Parameter("Update_Delay"), CTF_UPDATE_TIMER);
  779. }
  780. void Damaged( GameObject * obj, GameObject * damager, float amount )
  781. {
  782. Grab_Flag(obj,damager);
  783. }
  784. // void Poked( GameObject * obj, GameObject * poker )
  785. // {
  786. // Grab_Flag(obj,poker);
  787. // }
  788. void Grab_Flag( GameObject * obj, GameObject * grabber )
  789. {
  790. // If we're already captured, bail
  791. if (captured_by_id != 0) {
  792. return;
  793. }
  794. // Check distance, if the damager is too far away, bail
  795. Vector3 my_pos = Commands->Get_Position(obj);
  796. Vector3 delta = my_pos - Commands->Get_Position(grabber);
  797. if (delta.Length() > Get_Float_Parameter("Pickup_Radius")) {
  798. GTH_DEBUG_FLOAT(delta.Length(),"Flag too far away, dist = %f\n");
  799. return;
  800. }
  801. // Check player type, if it doesn't match, warp back to our home position
  802. int capture_player_type = Get_Int_Parameter("Enemy_Player_Type");
  803. int poke_player_type = Commands->Get_Player_Type(grabber);
  804. // Someone poked the flag...see if it needs to respawn back at home.
  805. if (capture_player_type != 2) {
  806. if (poke_player_type != capture_player_type) {
  807. // if we're not being carried and the flag isn't on a timer, warp back
  808. if (captured_by_id == 0 && Get_Float_Parameter("Capture_Respawn_Timer") < 0) {
  809. Commands->Set_Position(obj,home_position);
  810. GTH_DEBUG_INT(poke_player_type,"Flag recovered back by team: %d\n");
  811. // Play flag returned sound
  812. int cust_id = Get_Int_Parameter("Flag_Returned_Event_ID");
  813. GameObject * event_obj = Commands->Find_Object(Get_Int_Parameter("Flag_Returned_Object_ID"));
  814. if (event_obj == NULL) event_obj = obj;
  815. Commands->Send_Custom_Event(obj,event_obj,cust_id, 1);
  816. }
  817. return;
  818. }
  819. }
  820. // The player type matches, Attach to this dude!
  821. captured_by_id = Commands->Get_ID(grabber);
  822. // attach to bone 0 (root)
  823. Commands->Attach_To_Object_Bone(obj,grabber,"ROOTTRANSFORM");
  824. GTH_DEBUG_INT(poke_player_type,"Flag captured by team: %d\n");
  825. // Play picked up sound
  826. int cust_id = Get_Int_Parameter("Flag_Stolen_Event_ID");
  827. GameObject * event_obj = Commands->Find_Object(Get_Int_Parameter("Flag_Stolen_Object_ID"));
  828. if (event_obj == NULL) event_obj = obj;
  829. Commands->Send_Custom_Event(obj,event_obj,cust_id, 1);
  830. }
  831. void Timer_Expired (GameObject * obj, int timer_id)
  832. {
  833. if (timer_id == CTF_UPDATE_TIMER)
  834. {
  835. // Search for a player around the flag and let them auto-grab it.
  836. // BMH 12/7/02 - Get_A_Star only returns the closest to the position. So if someone just sat
  837. // on top of the flag that's the only star we'd ever check. So check from a random position
  838. // within the pickup radius
  839. Vector3 my_pos = Commands->Get_Position(obj);
  840. Vector3 find_pos = my_pos;
  841. float pick_rad = Get_Float_Parameter("Pickup_Radius");
  842. float rval = Commands->Get_Random(-pick_rad, pick_rad);
  843. find_pos.X += rval;
  844. rval = Commands->Get_Random(-pick_rad, pick_rad);
  845. find_pos.Y += rval;
  846. rval = Commands->Get_Random(-pick_rad, pick_rad);
  847. find_pos.Z += rval;
  848. GameObject *star_obj = Commands->Get_A_Star(find_pos);
  849. Grab_Flag(obj,star_obj);
  850. // check if we've been captured enough to end the game
  851. if (capture_count >= Get_Int_Parameter("Captures_Needed_To_Win")) {
  852. // Play Win Sound!
  853. int cust_id = Get_Int_Parameter("Captures_Exceeded_Event_ID");
  854. GameObject * event_obj = Commands->Find_Object(Get_Int_Parameter("Captures_Exceeded_Object_ID"));
  855. if (event_obj == NULL) event_obj = obj;
  856. Commands->Send_Custom_Event(obj,event_obj,cust_id, 1);
  857. // win!
  858. GameObject * win_obj = Commands->Find_Object(Get_Int_Parameter("Win_Object_To_Kill0"));
  859. if (win_obj != NULL) {
  860. Commands->Destroy_Object( win_obj );
  861. }
  862. win_obj = Commands->Find_Object(Get_Int_Parameter("Win_Object_To_Kill1"));
  863. if (win_obj != NULL) {
  864. Commands->Destroy_Object( win_obj );
  865. }
  866. win_obj = Commands->Find_Object(Get_Int_Parameter("Win_Object_To_Kill2"));
  867. if (win_obj != NULL) {
  868. Commands->Destroy_Object( win_obj );
  869. }
  870. win_obj = Commands->Find_Object(Get_Int_Parameter("Win_Object_To_Kill3"));
  871. if (win_obj != NULL) {
  872. Commands->Destroy_Object( win_obj );
  873. }
  874. win_obj = Commands->Find_Object(Get_Int_Parameter("Win_Object_To_Kill4"));
  875. if (win_obj != NULL) {
  876. Commands->Destroy_Object( win_obj );
  877. }
  878. GTH_DEBUG_INT(0,"Capture count exceeded\n");
  879. return;
  880. }
  881. // check if we're in our enemies home position
  882. Vector3 delta = my_pos - Get_Vector3_Parameter("Enemy_Home_Position");
  883. float dist = delta.Length();
  884. if (dist < Get_Float_Parameter("Home_Radius")) {
  885. // Increment the capture count, detatch from our carrier and warp home
  886. capture_count++;
  887. captured_by_id = 0;
  888. Commands->Attach_To_Object_Bone(obj,NULL,NULL);
  889. Commands->Set_Position(obj,home_position);
  890. GTH_DEBUG_INT(capture_count,"Flag stolen, counter now %d, returning to home!\n");
  891. // Play flag capture sound
  892. int cust_id = Get_Int_Parameter("Flag_Lost_Event_ID");
  893. GameObject * event_obj = Commands->Find_Object(Get_Int_Parameter("Flag_Lost_Object_ID"));
  894. if (event_obj == NULL) event_obj = obj;
  895. Commands->Send_Custom_Event(obj,event_obj,cust_id, 1);
  896. }
  897. // check if we have a capturer and need to update
  898. if (captured_by_id != 0) {
  899. GameObject * capturer = Commands->Find_Object(captured_by_id);
  900. if (capturer != NULL) {
  901. // Don't do this cause we're using the attach to bone method!
  902. //Vector3 cap_position = Commands->Get_Position(capturer);
  903. //Commands->Set_Position(obj,cap_position);
  904. } else {
  905. captured_by_id = 0;
  906. // start the flag respawn timer.
  907. capture_timer = (((float)Commands->Get_Sync_Time()) / 1000.0);
  908. Commands->Attach_To_Object_Bone(obj,NULL,NULL);
  909. GTH_DEBUG_INT(0,"Capturer killed!\n");
  910. // Play flag saved sound
  911. int cust_id = Get_Int_Parameter("Flag_Saved_Event_ID");
  912. GameObject * event_obj = Commands->Find_Object(Get_Int_Parameter("Flag_Saved_Object_ID"));
  913. if (event_obj == NULL) event_obj = obj;
  914. Commands->Send_Custom_Event(obj,event_obj,cust_id, 1);
  915. }
  916. }
  917. // Check if the flag has been sitting idle for too long and return it if need be.
  918. float ctimer = Get_Float_Parameter("Capture_Respawn_Timer");
  919. if (captured_by_id == 0 && ctimer > 0) {
  920. // Make sure the flag isn't already at the base
  921. delta = my_pos - home_position;
  922. dist = delta.Length();
  923. if (dist > Get_Float_Parameter("Pickup_Radius")) {
  924. // has the Respawn_Timer expired
  925. if (((((float)Commands->Get_Sync_Time()) / 1000.0) - capture_timer) > ctimer) {
  926. // Return the flag
  927. Commands->Set_Position(obj,home_position);
  928. GTH_DEBUG_INT(0,"Flag timeout has been hit, respawning flag back at home.\n");
  929. // Play flag returned sound
  930. int cust_id = Get_Int_Parameter("Flag_Returned_Event_ID");
  931. GameObject * event_obj = Commands->Find_Object(Get_Int_Parameter("Flag_Returned_Object_ID"));
  932. if (event_obj == NULL) event_obj = obj;
  933. Commands->Send_Custom_Event(obj,event_obj,cust_id, 1);
  934. }
  935. }
  936. }
  937. // register for another update!
  938. Commands->Start_Timer (obj, this, Get_Float_Parameter("Update_Delay"), CTF_UPDATE_TIMER);
  939. }
  940. }
  941. };