diff -urN sourceold/scripts/BeaconGameObj.h source/scripts/BeaconGameObj.h --- sourceold/scripts/BeaconGameObj.h 2018-11-17 10:23:56.961600000 +1000 +++ source/scripts/BeaconGameObj.h 2021-07-27 14:44:22.784009600 +1000 @@ -75,6 +75,10 @@ SoldierGameObj* Restore_Owner(); virtual BeaconGameObj* As_BeaconGameObj() { return this; } PlayerDataClass *Get_Player_Data() {return Player;} + int Get_State() { return State; } + float Get_Arm_Time() { return ArmTime; } + float Get_Detonate_Time() { return DetonateTime; } + const WeaponDefinitionClass *Get_WeaponDef() { return WeaponDef; } }; // 07F8 #endif \ No newline at end of file diff -urN sourceold/scripts/DBIKScripts.cpp source/scripts/DBIKScripts.cpp --- sourceold/scripts/DBIKScripts.cpp 2019-06-22 07:31:28.784400000 +1000 +++ source/scripts/DBIKScripts.cpp 2021-07-27 14:44:22.785005900 +1000 @@ -1319,7 +1319,11 @@ { GameObject *turret = Commands->Find_Object(turretId); if (turret) - Commands->Destroy_Object(turret); + { + Commands->Set_Health(turret,0); + Commands->Set_Shield_Strength(turret,0); + Commands->Apply_Damage(obj, 100.00, "BlamoKiller", killer); + } } void DB_Turret_Spawn::Destroyed(GameObject *obj) { @@ -1803,6 +1807,9 @@ Slot=9; Alt=0; } + + + PurchaseSettingsDefClass *PT = PurchaseSettingsDefClass::Find_Definition((PurchaseSettingsDefClass::TYPE)Type,(PurchaseSettingsDefClass::TEAM)Team); if(PT) { @@ -1843,4 +1850,115 @@ #endif // DRAGONADE } -ScriptRegistrant DB_Set_PT_Slot_Registrant("DB_Set_PT_Slot","Team=0:int,Type=0:int,Slot=0:int,Alt=-1:int,Preset=CnC_Nod_Light_Tank:String"); \ No newline at end of file +ScriptRegistrant DB_Set_PT_Slot_Registrant("DB_Set_PT_Slot","Team=0:int,Type=0:int,Slot=0:int,Alt=-1:int,Preset=CnC_Nod_Light_Tank:String"); + +void DB_Spawn_When_Killed::Killed(GameObject *obj,GameObject *killer) +{ + if(Find_Named_Definition(Get_Parameter("PresetName"))) + { + float facing = Commands->Get_Facing(obj); + Vector3 position = Commands->Get_Position(obj); + GameObject *newobj; + position.Z += Get_Float_Parameter("DropHeight"); + newobj = Commands->Create_Object(Get_Parameter("PresetName"),position); + if(newobj) + { + if (Get_Int_Parameter("SameFacing")) + { + Commands->Set_Facing(newobj,facing); + } + if (Get_Int_Parameter("MatchObjectTeam")) + { + Set_Object_Type(newobj,Get_Object_Type(obj)); + } + float maxhealth = Commands->Get_Max_Health(newobj); + float maxarmor = Commands->Get_Max_Shield_Strength(newobj); + if (maxhealth) Commands->Set_Health(newobj,maxhealth*Get_Float_Parameter("HealthPercent") / 100); + if (maxarmor) Commands->Set_Shield_Strength(newobj,maxarmor*Get_Float_Parameter("ArmorPercent") / 100); + Update_Network_Object(newobj); + Force_Position_Update(newobj); + #ifdef DRAGONADE + if(obj->As_SoldierGameObj()) + { + obj->As_PhysicalGameObj()->Set_Collision_Group(TERRAIN_ONLY_COLLISION_GROUP); + } + Fix_Stuck_Objects(position,10,15,false); + #endif // DRAGONADE + } + } +} + +void DB_Replace_When_Repaired::Damaged(GameObject *obj,GameObject *damager,float amount) +{ + if (amount >= 0 || Commands->Get_Health(obj) < Commands->Get_Max_Health(obj) || Commands->Get_Shield_Strength(obj) < Commands->Get_Max_Shield_Strength(obj)) + { + return; + } + + if(Find_Named_Definition(Get_Parameter("PresetName"))) + { + float facing = Commands->Get_Facing(obj); + Vector3 position = Commands->Get_Position(obj); + GameObject*newobj; + Commands->Destroy_Object(obj); + position.Z += Get_Float_Parameter("DropHeight"); + newobj = Commands->Create_Object(Get_Parameter("PresetName"),position); + if(newobj) + { + if (Get_Int_Parameter("SameFacing")) + { + Commands->Set_Facing(newobj,facing); + } + if (Get_Int_Parameter("MatchRepairTeam") && damager) + { + Set_Object_Type(newobj,Get_Object_Type(damager)); + } + float maxhealth = Commands->Get_Max_Health(newobj); + float maxarmor = Commands->Get_Max_Shield_Strength(newobj); + if (maxhealth) Commands->Set_Health(newobj,maxhealth*Get_Float_Parameter("HealthPercent") / 100); + if (maxarmor) Commands->Set_Shield_Strength(newobj,maxarmor*Get_Float_Parameter("ArmorPercent") / 100); + Update_Network_Object(newobj); + Force_Position_Update(newobj); + #ifdef DRAGONADE + Fix_Stuck_Objects(position,10,15,false); + #endif // DRAGONADE + } + } +} + +ScriptRegistrant DB_Spawn_When_Killed_Registrant("DB_Spawn_When_Killed","PresetName:string,DropHeight=0:float,SameFacing=1:int,HealthPercent=100.0000:float,ArmorPercent=100.0000:float,MatchObjectTeam=0:int"); +ScriptRegistrant DB_Replace_When_Repaired_Registrant("DB_Replace_When_Repaired","PresetName:string,DropHeight=0:float,SameFacing=1:int,HealthPercent=100.0000:float,ArmorPercent=100.0000:float,MatchRepairTeam=0:int"); + +void DB_Force_Fire::Created(GameObject *obj) +{ + if(obj && obj->As_SmartGameObj()) + { + ActionParamsStruct action; + action.AttackCheckBlocked = false; + action.AttackForceFire = true; + action.AttackLocation = obj->As_SmartGameObj()->Get_Targeting_Pos(); + action.AttackRange = 10000000; + action.Priority = 100; + action.AttackPrimaryFire = !Get_Int_Parameter("Use_Secondary_Fire"); + Commands->Action_Attack(obj,action); + float time = Get_Float_Parameter("ResetTime"); + if(time) + Commands->Start_Timer(obj,this,time,1); + } +} + +void DB_Force_Fire::Timer_Expired(GameObject *obj, int number) +{ + if(number == 1) + Commands->Action_Reset(obj,101); +} + +void DB_Force_Fire::Detach(GameObject *obj) +{ + if(Exe != 4) + { + Commands->Action_Reset(obj,101); + } +} + +ScriptRegistrantDB_Force_Fire_Registrant("DB_Force_Fire","UseSecondaryFire=0:int,ResetTime=0:float"); \ No newline at end of file diff -urN sourceold/scripts/DBIKScripts.h source/scripts/DBIKScripts.h --- sourceold/scripts/DBIKScripts.h 2018-11-11 08:30:08.520600000 +1000 +++ source/scripts/DBIKScripts.h 2021-07-27 14:44:22.786003000 +1000 @@ -264,4 +264,18 @@ class DB_Set_PT_Slot : public ScriptImpClass { void Created(GameObject *obj); void Timer_Expired(GameObject *obj,int number); +}; + +class DB_Spawn_When_Killed : public ScriptImpClass { + void Killed (GameObject *obj,GameObject *killer); +}; + +class DB_Replace_When_Repaired : public ScriptImpClass { + void Damaged (GameObject *obj,GameObject *damager,float amount); +}; + +class DB_Force_Fire : public ScriptImpClass { + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj, int number); + void Detach(GameObject *obj); }; \ No newline at end of file diff -urN sourceold/scripts/DBScripts.cpp source/scripts/DBScripts.cpp --- sourceold/scripts/DBScripts.cpp 2020-09-26 08:59:57.383168900 +1000 +++ source/scripts/DBScripts.cpp 2021-07-27 14:44:22.786003000 +1000 @@ -1154,6 +1154,235 @@ } }; +class DB_Beacon_fix : public ScriptImpClass { + + int beacontimerID; + void DB_Beacon_fix::Created(GameObject *obj) + { + + + } + + void DB_Beacon_fix::Custom(GameObject *obj,int type,int param,GameObject *sender) + { + if( type == CUSTOM_EVENT_BEACON_DEPLOYED && obj->As_PhysicalGameObj() && obj->As_PhysicalGameObj()->As_BeaconGameObj()) + { + float det_time = obj->As_PhysicalGameObj()->As_BeaconGameObj()->Get_Detonate_Time(); + //Console_InputF("msg beacon deployed: %f seconds",det_time); + Vector3 droploc = Commands->Get_Position(obj); + GameObject *beacontimer = Commands->Create_Object("Invisible_Object",droploc); + beacontimerID = beacontimer->Get_ID(); + Attach_Script_Once_V(beacontimer,"DB_Beacon_fix2","%i,%f",obj->Get_ID(),det_time); + } + } + + void DB_Beacon_fix::Destroyed(GameObject *obj) + { + if(beacontimerID) + Commands->Destroy_Object(Commands->Find_Object(beacontimerID)); + } +}; + +class DB_Beacon_fix2 : public ScriptImpClass { + + int BeaconID; + int CountDown; + void DB_Beacon_fix2::Created(GameObject *obj) + { + float DetTime = Get_Float_Parameter("DetTime"); + BeaconID = Get_Int_Parameter("BeaconID"); + GameObject *beacon = Commands->Find_Object(BeaconID); + if(beacon && beacon->As_PhysicalGameObj()->As_BeaconGameObj()) + { + int IsNuke = beacon->As_PhysicalGameObj()->As_BeaconGameObj()->Get_Definition().Is_Nuke(); + if(DetTime>52) + { + Commands->Start_Timer(obj,this,DetTime-(float)67,3575); // countdown initiated + if(IsNuke) + { + if(DetTime>62) + { + Commands->Start_Timer(obj,this,DetTime-(float)52.5,3585); // Nuclear Strike in 50 seconds + if(DetTime>72) + { + Commands->Start_Timer(obj,this,DetTime-(float)62.5,3583); // Nuclear Strike in 60 seconds + } + } + Commands->Start_Timer(obj,this,DetTime-(float)42.5,3587); // Nuclear Strike in 40 seconds + Commands->Start_Timer(obj,this,DetTime-(float)32.5,3589); // Nuclear Strike in 30 seconds + } + else + { + if(DetTime>62) + { + Commands->Start_Timer(obj,this,DetTime-(float)52.5,3586); // Nuclear Strike in 50 seconds + if(DetTime>72) + { + Commands->Start_Timer(obj,this,DetTime-(float)62.5,3584); // Nuclear Strike in 60 seconds + } + } + Commands->Start_Timer(obj,this,DetTime-(float)42.5,3588); // Ion Cannon Strike in 40 seconds + Commands->Start_Timer(obj,this,DetTime-(float)32.5,3590); // Ion Cannon Strike in 30 seconds + } + Commands->Start_Timer(obj,this,DetTime-25,3579); + Commands->Start_Timer(obj,this,DetTime-20,3580); + Commands->Start_Timer(obj,this,DetTime-15,3581); + } + + else if(DetTime>25) + { + Commands->Start_Timer(obj,this,DetTime-(float)37,3575); // countdown initiated + Commands->Start_Timer(obj,this,DetTime-(float)33.9,3574); // you have GDI + Commands->Start_Timer(obj,this,DetTime-(float)33.8,3576); // you have Nod + Commands->Start_Timer(obj,this,DetTime-33,3577); // 30 + Commands->Start_Timer(obj,this,DetTime-(float)32.4,3578); // Seconds to reach minimum safe distance + Commands->Start_Timer(obj,this,DetTime-25,3579); + Commands->Start_Timer(obj,this,DetTime-20,3580); + Commands->Start_Timer(obj,this,DetTime-15,3581); + } + else if(DetTime>10) + { + Commands->Start_Timer(obj,this,DetTime-(float)14.9,3574); // you have GDI + Commands->Start_Timer(obj,this,DetTime-(float)14.8,3576); // you have Nod + Commands->Start_Timer(obj,this,DetTime-14,3582); // 10 + Commands->Start_Timer(obj,this,DetTime-(float)13.4,3578); // Seconds to reach minimum safe distance + } + //Commands->Start_Timer(obj,this,DetTime-10,3582); + CountDown = 1; + } + } + + void Timer_Expired(GameObject *obj,int number) + { + if(number==3575) + { + //Console_Input("msg Beacon will detonate in 31 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("m00evan_dsgn0102i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("m00evag_dsgn0096i1evag_snd.wav",1); + } + + if(number==3574) + { + //Console_Input("msg Beacon will detonate in 31 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("m00evag_dsgn0097i1evag_snd.wav",1); + } + + if(number==3576) + { + //Console_Input("msg Beacon will detonate in 31 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("m00evan_dsgn0103i1evan_snd.wav",0); + } + + if(number==3577) + { + //Console_Input("msg Beacon will detonate in 30 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("m00evan_dsgn0079i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("m00evag_dsgn0073i1evag_snd.wav",1); + } + + if(number==3578) + { + //Console_Input("msg Beacon will detonate in 29 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("m00evan_dsgn0104i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("m00evag_dsgn0098i1evag_snd.wav",1); + } + + if(number==3579) + { + //Console_Input("msg Beacon will detonate in 25 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("m00evan_dsgn0080i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("m00evag_dsgn0074i1evag_snd.wav",1); + } + + if(number==3580) + { + //Console_Input("msg Beacon will detonate in 20 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("m00evan_dsgn0081i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("m00evag_dsgn0075i1evag_snd.wav",1); + } + + if(number==3581) + { + //Console_Input("msg Beacon will detonate in 15 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("m00evan_dsgn0082i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("m00evag_dsgn0076i1evag_snd.wav",1); + } + + if(number==3582) + { + //Console_InputF("msg Beacon will detonate in %i Seconds",11-CountDown); + StringClass SoundNod; + StringClass SoundGDI; + SoundNod.Format("m00evan_dsgn00%ii1evan_snd.wav",82+CountDown); + SoundGDI.Format("m00evag_dsgn00%ii1evag_snd.wav",76+CountDown); + Create_2D_WAV_Sound_Team_Dialog(SoundNod,0); + Create_2D_WAV_Sound_Team_Dialog(SoundGDI,1); + //CountDown++; + //if(CountDown<=11) + //Commands->Start_Timer(obj,this,1,3582); + //else + //Commands->Destroy_Object(obj); + } + + if(number==3583) + { + //Console_Input("msg Beacon will detonate in 60 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0033i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0035i1evag_snd.wav",1); + } + + if(number==3584) + { + //Console_Input("msg Beacon will detonate in 60 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0023i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0024i1evag_snd.wav",1); + } + + if(number==3585) + { + //Console_Input("msg Beacon will detonate in 50 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0032i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0034i1evag_snd.wav",1); + } + + if(number==3586) + { + //Console_Input("msg Beacon will detonate in 50 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0022i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0023i1evag_snd.wav",1); + } + + if(number==3587) + { + //Console_Input("msg Beacon will detonate in 40 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0031i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0033i1evag_snd.wav",1); + } + + if(number==3588) + { + //Console_Input("msg Beacon will detonate in 40 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0021i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0022i1evag_snd.wav",1); + } + + if(number==3589) + { + //Console_Input("msg Beacon will detonate in 30 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0030i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0032i1evag_snd.wav",1); + } + + if(number==3590) + { + //Console_Input("msg Beacon will detonate in 30 Seconds"); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0020i1evan_snd.wav",0); + Create_2D_WAV_Sound_Team_Dialog("mxxdsgn_dsgn0021i1evag_snd.wav",1); + } + } + +}; + class DB_Enter_Teleport_Random : public ScriptImpClass { void Entered(GameObject *obj, GameObject *enter) { @@ -1276,7 +1505,6 @@ }; ScriptRegistrant DB_Face_Forward_Registrant("DB_Face_Forward", "Duration=1:float"); - ScriptRegistrantDB_Captureable_Silo_Registrant("DB_Captureable_Silo","Team=-2:int,Amount=2:float,UpgradeAmount=3:float,UpgradeID=0:int"); ScriptRegistrant DB_Capturable_Helipad_Registrant("DB_Capturable_Helipad","Team=-2:int,ReloadZoneID=0:int"); ScriptRegistrant DB_Powerup_Buy_Poke_Sound_Registrant("DB_Powerup_Buy_Poke_Sound", "Team=0:int,BuildingID=0:int,Preset=bla:string,Cost=10000:float,SoundGranted=bla:string,SoundDenied=bla:string"); @@ -1292,5 +1520,6 @@ ScriptRegistrant GDI_Guard_Tower_DeathSound_Registrant("GDI_Guard_Tower_DeathSound", ""); ScriptRegistrant Nod_SamSite_DeathSound_Registrant("Nod_SamSite_DeathSound", ""); ScriptRegistrant DB_Power_Plant_fix_Registrant("DB_Power_Plant_fix", ""); -ScriptRegistrant DB_Enter_Teleport_Random_Registrant("DB_Enter_Teleport_Random","Object_ID1=1:int,Object_ID2=1:int,Object_ID3=1:int,Object_ID4=1:int,Object_ID5=1:int"); - +ScriptRegistrant DB_Beacon_fix_Registrant("DB_Beacon_fix", ""); +ScriptRegistrant DB_Beacon_fix2_Registrant("DB_Beacon_fix2", "BeaconID=0:int,DetTime:0:float"); +ScriptRegistrant DB_Enter_Teleport_Random_Registrant("DB_Enter_Teleport_Random","Object_ID1=1:int,Object_ID2=1:int,Object_ID3=1:int,Object_ID4=1:int,Object_ID5=1:int"); \ No newline at end of file diff -urN sourceold/scripts/anticheat.ini source/scripts/anticheat.ini --- sourceold/scripts/anticheat.ini 2020-09-26 08:59:57.386159600 +1000 +++ source/scripts/anticheat.ini 2021-07-27 14:44:12.875621500 +1000 @@ -134,9 +134,6 @@ 185315cb0070e52ab9c89d95d06f05e5=C&C_Complex.ldd 11fc8b1d6d70fbacbb18181618fc3964=c&c_field.mix b94a4486508afdc014c1ff3ea4f3d455=c&c_field.ldd -b3b5a7faa502f747d3bc8ff5c63a99cb=M00_Tutorial.mix -5da07b82073c698031b3bacdf286709c=M02.mix -ceb5f647bec120fedffc9d9fc70f9ae6=M04.mix 77e35ed03742c2a3916cae9baf6b119a=M07.mix 72d92296beb9b14c1cd227e8ee592e19=M08.mix b3a1e43b578cd1f1899d920d9502b4e3=always.dat diff -urN sourceold/scripts/engine_io.cpp source/scripts/engine_io.cpp --- sourceold/scripts/engine_io.cpp 2020-09-10 09:08:27.953899600 +1000 +++ source/scripts/engine_io.cpp 2021-07-27 14:44:22.787000100 +1000 @@ -429,6 +429,34 @@ } return string; } + +StringClass &INIClass::Get_String_Advanced(StringClass& string, const char* section, const char* entry, const char *defaultvalue, bool updateIfNotFound) const +{ + const char *value = defaultvalue; + if (!section || !entry) + { + string = ""; + } + INIEntry *Entry = Find_Entry(section,entry); + if (Entry) + { + value = Entry->Value; + } + else if (!updateIfNotFound) + { + return string; + } + + if (value) + { + string = value; + } + else + { + string = ""; + } + return string; +} INISection *INIClass::Find_Section(const char* section) const { if (section) diff -urN sourceold/scripts/engine_io.h source/scripts/engine_io.h --- sourceold/scripts/engine_io.h 2020-09-10 09:08:27.954910800 +1000 +++ source/scripts/engine_io.h 2021-07-27 14:44:22.787000100 +1000 @@ -134,6 +134,7 @@ int Get_String(char const *section,char const *entry,char const *defaultvalue,char *result,int size) const; int Get_String_Advanced(char const *section,char const *entry,char const *defaultvalue,char *result,int size,bool writeIfNotFound) const; StringClass &Get_String(StringClass &str, const char* section, const char* entry, const char* default = 0) const; + StringClass& Get_String_Advanced(StringClass& string, const char* section, const char* entry, const char* defaultvalue, bool updateIfNotFound) const; WideStringClass &Get_Wide_String(WideStringClass &,char const*,char const*,wchar_t const*) const; bool Put_Wide_String(const char* section, const char* entry, const wchar_t* string); bool Put_String(const char* section, const char* entry, const char* string); diff -urN sourceold/scripts/jmgBearHunter.cpp source/scripts/jmgBearHunter.cpp --- sourceold/scripts/jmgBearHunter.cpp 2020-09-26 08:59:57.398127800 +1000 +++ source/scripts/jmgBearHunter.cpp 2021-07-27 14:44:22.790989600 +1000 @@ -614,6 +614,20 @@ Commands->Start_Timer(obj,this,60.0f,13); Commands->Start_Timer(obj,this,0.6f,14); Commands->Start_Timer(obj,this,1.0f,18); + for (int x = 0;x < 7;x++) + { + GameObject *pumpJack = Commands->Find_Object(JMG_Bear_Hunter_Game_Control::pumpJackIds[x]); + if (!pumpJack) + continue; + pumpJackPos[x] = Commands->Get_Position(pumpJack); + } + for (int x = 0;x < 3;x++) + { + GameObject *pumpJack = Commands->Find_Object(JMG_Bear_Hunter_Game_Control::damagedPumpJackIds[x]); + if (!pumpJack) + continue; + damagedPumpJackPos[x] = Commands->Get_Position(pumpJack); + } Commands->Attach_Script(obj,"JMG_Bear_Hunter_Wolf_Pack_Definition","1,Wild_Wolf_Twiddler,5,218.230 180.027 0.022,10.0,120,30,60.0,15.0,0.5,0"); Commands->Attach_Script(obj,"JMG_Bear_Hunter_Wolf_Pack_Definition","2,Wild_Wolf_Twiddler,10,420.0 -1.14 0.59,50.0,240,30,60.0,15.0,1.5,1"); Commands->Attach_Script(obj,"JMG_Bear_Hunter_Wolf_Pack_Definition","3,Wild_Wolf_Twiddler,3,-400.0 230.0 -0.11,50.0,120,30,60.0,15.0,1.5,1"); @@ -8418,6 +8432,17 @@ *facing = node->facing; return true; } +bool JMG_Utility_Custom_Teleport_Sender_Wanderpoint::Get_A_Defense_Point(Vector3 *position,float *facing) +{ + Rp2SimplePositionSystem::SimplePositionNode *node = NULL; + if (wanderPointGroup != -1) + node = JMG_Wandering_AI_Controller::wanderPoints.GetRandomFromGroup(wanderPointGroup); + if (!node) + return false; + *position = node->position; + *facing = node->facing; + return true; +} bool JMG_Utility_AI_Goto_Player::GetRandomPosition(Vector3 *position) { Rp2SimplePositionSystem::SimplePositionNode *node = NULL; @@ -8923,6 +8948,12 @@ JMG_Wandering_AI_Controller::wanderPoints += node; return node; } +Rp2SimplePositionSystem::SimplePositionNode *JMG_Utility_Control_Point_Team_Target_Wander_Point::AddAndReturnWanderpoint(GameObject *obj) +{ + Rp2SimplePositionSystem::SimplePositionNode *node = new Rp2SimplePositionSystem::SimplePositionNode(obj); + JMG_Wandering_AI_Controller::wanderPoints += node; + return node; +} void JMG_Bear_Hunter_PumpJack_Beacon::Created(GameObject *obj) { GameObject *Player = NULL; @@ -9075,30 +9106,24 @@ float JMG_Bear_Hunter_PumpJack_Detect_Oil::FindClosestPumpjack(GameObject *obj) { Vector3 pos = Commands->Get_Position(obj); - GameObject *closest = NULL; + int closest = -1; float dist = -1; for (int x = 0;x < 7;x++) { - GameObject *pumpJack = Commands->Find_Object(JMG_Bear_Hunter_Game_Control::pumpJackIds[x]); - if (!pumpJack) - continue; - float tempDist = JmgUtility::SimpleDistance(pos,Commands->Get_Position(pumpJack)); - if (!closest || tempDist < dist) + float tempDist = JmgUtility::SimpleDistance(pos,JMG_Bear_Hunter_Game_Control::pumpJackPos[x]); + if (closest == -1 || tempDist < dist) { dist = tempDist; - closest = pumpJack; + closest = x; } } for (int x = 0;x < 3;x++) { - GameObject *pumpJack = Commands->Find_Object(JMG_Bear_Hunter_Game_Control::damagedPumpJackIds[x]); - if (!pumpJack) - continue; - float tempDist = JmgUtility::SimpleDistance(pos,Commands->Get_Position(pumpJack)); - if (!closest || tempDist < dist) + float tempDist = JmgUtility::SimpleDistance(pos,JMG_Bear_Hunter_Game_Control::damagedPumpJackPos[x]); + if (closest == -1 || tempDist < dist) { dist = tempDist; - closest = pumpJack; + closest = x; } } return dist; @@ -9262,6 +9287,86 @@ return; } } +Rp2SimplePositionSystem::SimplePositionNode *JMG_Utility_AI_Control_Point::Get_Capture_Point() +{ + if (captureCpGroupId != -1) + return JMG_Wandering_AI_Controller::wanderPoints.GetRandomFromGroup(captureCpGroupId); + Console_Input("MSG JMG_Utility_AI_Control_Point ERROR: Capture Point not found!"); + return NULL; +} +Rp2SimplePositionSystem::SimplePositionNode *JMG_Utility_AI_Control_Point::Get_Defense_Point(GameObject *obj) +{ + Vector3 pos = Commands->Get_Position(obj); + Rp2SimplePositionSystem::SimplePositionNode *node = NULL; + if (defendCpGroupId != -1) + { + if (Commands->Get_Random(0.0f,1.0f) < chooseFarDefendChance) + node = JMG_Wandering_AI_Controller::wanderPoints.GetRandomInsideOfRangeInGroup(defendCpGroupId,closeDefendDistanceSquared,pos); + else + node = JMG_Wandering_AI_Controller::wanderPoints.GetRandomOutsideOfRangeInGroup(defendCpGroupId,closeDefendDistanceSquared,pos); + if (node) + return node; + node = JMG_Wandering_AI_Controller::wanderPoints.GetRandomFromGroup(defendCpGroupId); + if (node) + return node; + } + Console_Input("MSG JMG_Utility_AI_Control_Point ERROR: Defend Point not found!"); + return NULL; +} +void JMG_Wandering_AI_Wander_Point_Follow_Weapon_Or_Obj::Created(GameObject *obj) +{ + if (!JMG_Wandering_AI_Controller::setup) + { + Console_Input("msg JMG_Wandering_AI_Wander_Point_Follow_Weapon_Or_Obj ERROR:: Make sure you have the script JMG_Wandering_AI_Controller placed on the map!"); + return; + } + followObjectId = 0; + sprintf(presetName,"%s",Get_Parameter("PresetName")); + sprintf(weaponName,"%s",Get_Parameter("WeaponName")); + Commands->Start_Timer(obj,this,0.25f,1); +} +void JMG_Wandering_AI_Wander_Point_Follow_Weapon_Or_Obj::Timer_Expired(GameObject *obj,int number) +{ + if (number == 1) + { + node = new Rp2SimplePositionSystem::SimplePositionNode(obj); + node->value = Get_Int_Parameter("GroupId"); + JMG_Wandering_AI_Controller::wanderPoints += node; + Commands->Start_Timer(obj,this,0.1f,2); + } + if (number == 2) + { + if (!JMG_Wandering_AI_Controller::setup) + { + Destroy_Script(); + return; + } + GameObject *target = Commands->Find_Object(followObjectId); + if (!target || !(Has_Weapon(target,weaponName) || !_stricmp(Commands->Get_Preset_Name(target),presetName))) + { + target = FindTargetObject(obj); + followObjectId = Commands->Get_ID(target); + } + if (target) + { + node->position = Commands->Get_Position(target); + node->facing = Commands->Get_Facing(target); + } + Commands->Start_Timer(obj,this,1.0f,2); + } +} +GameObject *JMG_Wandering_AI_Wander_Point_Follow_Weapon_Or_Obj::FindTargetObject(GameObject *obj) +{ + for (SLNode *current = GameObjManager::GameObjList.Head();current;current = current->Next()) + { + GameObject *o = current->Data()->As_ScriptableGameObj(); + if (!o || !Commands->Get_Health(o)) + continue; + if (Has_Weapon(o,weaponName) || !_stricmp(Commands->Get_Preset_Name(o),presetName)) + return o; + } + return NULL; +} ScriptRegistrant JMG_Bear_Hunter_Player_Soldier_Registrant("JMG_Bear_Hunter_Player_Soldier",""); ScriptRegistrant JMG_Rp2_Dedicated_Server_Sound_Emulator_Registrant("JMG_Rp2_Dedicated_Server_Sound_Emulator",""); ScriptRegistrant JMG_Bear_Hunter_Game_Control_Registrant("JMG_Bear_Hunter_Game_Control","PositionFile=DhSwampDeerPositions.ecw:string"); @@ -9363,4 +9468,5 @@ ScriptRegistrant JMG_Bear_Hunter_Milk_Drink_Registrant("JMG_Bear_Hunter_Milk_Drink","WholeMilk:int"); ScriptRegistrant JMG_Bear_Hunter_AI_Guardian_Generic_Registrant("JMG_Bear_Hunter_AI_Guardian_Generic","WanderingAIGroupID:int,WanderSpeed=1.0:float,FireRange=-1.0:float,FaceTarget=1:int,CheckBlocked=1:int,ArriveDistance=1.0:float,FlightHeight=0.0:float,TurnOffEngineOnArrival=1:int,UseSecondaryAttack=0:int,StealthModeOverride=0:int"); ScriptRegistrant JMG_Bear_Hunter_Give_AI_Cash_For_Kills_Registrant("JMG_Bear_Hunter_Give_AI_Cash_For_Kills",""); +ScriptRegistrant JMG_Wandering_AI_Wander_Point_Follow_Weapon_Or_Obj_Registrant("JMG_Wandering_AI_Wander_Point_Follow_Weapon_Or_Obj","GroupId:int,PresetName:string,WeaponName:string"); \ No newline at end of file diff -urN sourceold/scripts/jmgBearHunter.h source/scripts/jmgBearHunter.h --- sourceold/scripts/jmgBearHunter.h 2020-09-26 08:59:57.399125500 +1000 +++ source/scripts/jmgBearHunter.h 2021-07-27 14:44:22.791987200 +1000 @@ -1037,6 +1037,8 @@ static int bonusObjectiveCount; static int pumpJackIds[7]; static int damagedPumpJackIds[3]; + static Vector3 pumpJackPos[7]; + static Vector3 damagedPumpJackPos[3]; }; GameObject *JMG_Bear_Hunter_Game_Control::myObject = NULL; int JMG_Bear_Hunter_Game_Control::mutantTargetId = 600172; @@ -1082,6 +1084,8 @@ int JMG_Bear_Hunter_Game_Control::bonusObjectiveCount = 0; int JMG_Bear_Hunter_Game_Control::pumpJackIds[7] = {601041,601038,601044,601040,601045,601039,601043}; int JMG_Bear_Hunter_Game_Control::damagedPumpJackIds[3] = {601061,601062,601063}; +Vector3 JMG_Bear_Hunter_Game_Control::pumpJackPos[7]; +Vector3 JMG_Bear_Hunter_Game_Control::damagedPumpJackPos[3]; struct BearHunterScoreSystem { @@ -1542,13 +1546,14 @@ } FILE *SaveScores; FILE *SaveScores2; - char tempPath[512],textPath[512],realPath[512]; + char tempPath[512],textTmpPath[512],realPath[512],textPath[512]; _mkdir(savePath); sprintf(tempPath,"%sBearHunterPlayerRecords.tmp",savePath); sprintf(realPath,"%sBearHunterPlayerRecords.dat",savePath); + sprintf(textTmpPath,"%sBearHunterPlayerRecordsTmp.txt",savePath); sprintf(textPath,"%sBearHunterPlayerRecords.txt",savePath); SaveScores = fopen(tempPath,"w"); - SaveScores2 = fopen(textPath,"w"); + SaveScores2 = fopen(textTmpPath,"w"); CalculateRank(); int currentRank = 0; BHScoreNode *Current = BHScoreNodeList; @@ -1588,6 +1593,8 @@ fclose(SaveScores2); remove(realPath); rename(tempPath,realPath); + remove(textPath); + rename(textTmpPath,textPath); } void LoadData() { @@ -1779,7 +1786,7 @@ case 118: return EveluateHighestScore(High->PortablePumpJacksPlaced,Current->PortablePumpJacksPlaced,High,Current); case 119: return EveluateHighestScore(High->PortablePumpJacksLost,Current->PortablePumpJacksLost,High,Current); case 120: return EveluateHighestScore(High->PumpJackMoney,Current->PumpJackMoney,High,Current); - case 121: return EveluateHighestScore(High->PumpJackMoney,Current->PumpJackMoney,High,Current); + case 121: return EveluateHighestScore(High->MobilePumpJackMoney,Current->MobilePumpJackMoney,High,Current); default: return High; } } @@ -3669,4 +3676,23 @@ class JMG_Bear_Hunter_Give_AI_Cash_For_Kills : public ScriptImpClass { void Killed(GameObject *obj,GameObject *killer); +}; + + +/*! +* \brief Makes a wander point follow an object on the map +* \GroupId - ID of the group the point belongs to +* \PresetName - Preset to follow by object +* \WeaponName - Object with weapon to follow +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Wandering_AI_Wander_Point_Follow_Weapon_Or_Obj : public ScriptImpClass { + Rp2SimplePositionSystem::SimplePositionNode *node; + int followObjectId; + char presetName[128]; + char weaponName[128]; + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj,int number); + GameObject *FindTargetObject(GameObject *obj); }; \ No newline at end of file diff -urN sourceold/scripts/jmgMetroid.cpp source/scripts/jmgMetroid.cpp --- sourceold/scripts/jmgMetroid.cpp 2020-04-02 12:02:44.440356300 +1000 +++ source/scripts/jmgMetroid.cpp 2021-07-27 14:44:22.795007700 +1000 @@ -44,6 +44,12 @@ NumberOfDeactivatedTerminals = 0; BossID = 0; BossMode = 0; + if (!winMusicId) + { + GameObject *winMusic = Commands->Create_Object("Daves Arrow",Vector3(353.3f,-406.2f,2.17f)); + Commands->Set_Model(winMusic,"s_victory"); + winMusicId = Commands->Get_ID(winMusic); + } for (int x = 0;x < 3;x++) { GameObject *Terminal = Commands->Find_Object(MineTerminalID[x]); @@ -279,7 +285,9 @@ if (The_Game()->Is_Game_Over()) { SavePlayerPerkData(); + SaveMetroidHighScores(); Commands->Destroy_Object(obj); + return; } if (MetroidPSSControl.PlayerSettings[0]->IsInTeleport) MetroidPSSControl.PlayerSettings[0]->IsInTeleport--; @@ -1077,8 +1085,11 @@ { for (int x = 0;x < 3;x++) { - MetroidObjectiveSystemControl.Add_Objective(30+x,NewObjectiveSystem::Primary,NewObjectiveSystem::Pending,12810+x,"",12810+x,Commands->Get_Position(Commands->Find_Object(LockDownButtonIDs[x]))); - JmgUtility::SetHUDHelpText(12810,Vector3(0,1,0)); + if (MetroidObjectiveSystemControl.Get_Objective_Status(30+x) == NewObjectiveSystem::NotDefined) + { + MetroidObjectiveSystemControl.Add_Objective(30+x,NewObjectiveSystem::Primary,NewObjectiveSystem::Pending,12810+x,"",12810+x,Commands->Get_Position(Commands->Find_Object(LockDownButtonIDs[x]))); + JmgUtility::SetHUDHelpText(12810,Vector3(0,1,0)); + } } } if (message == 403025) @@ -1148,8 +1159,11 @@ } if (message == 123425) { - MetroidObjectiveSystemControl.Add_Objective(33,NewObjectiveSystem::Primary,NewObjectiveSystem::Pending,12813,"",12813,Commands->Get_Position(Commands->Find_Object(406329))); - JmgUtility::SetHUDHelpText(12813,Vector3(0,1,0)); + if (MetroidObjectiveSystemControl.Get_Objective_Status(32) == NewObjectiveSystem::NotDefined) + { + MetroidObjectiveSystemControl.Add_Objective(33,NewObjectiveSystem::Primary,NewObjectiveSystem::Pending,12813,"",12813,Commands->Get_Position(Commands->Find_Object(406329))); + JmgUtility::SetHUDHelpText(12813,Vector3(0,1,0)); + } MetroidGameControl.MainElevatorDown = true; } if (message == 406919) @@ -1677,7 +1691,7 @@ {// Crates or player equipment GameObject *Boss = Commands->Find_Object(MetroidGameControl.BossID); if (Boss) - Commands->Apply_Damage(Boss,-(10.0f+Get_Player_Count()),"None",enter); + Commands->Apply_Damage(Boss,-(5.0f+Get_Player_Count()),"None",enter); Commands->Apply_Damage(enter,9999.9f,"Blamokiller",0); return; } @@ -1858,15 +1872,21 @@ } if (message == 406919) { - MetroidObjectiveSystemControl.Add_Objective(29,NewObjectiveSystem::Primary,NewObjectiveSystem::Pending,12813,"",12813,Commands->Get_Position(Commands->Find_Object(LockDownButtonID))); + if (MetroidObjectiveSystemControl.Get_Objective_Status(29) == NewObjectiveSystem::NotDefined) + { + MetroidObjectiveSystemControl.Add_Objective(29,NewObjectiveSystem::Primary,NewObjectiveSystem::Pending,12813,"",12813,Commands->Get_Position(Commands->Find_Object(LockDownButtonID))); JmgUtility::SetHUDHelpText(12813,Vector3(0,1,0)); + } } if (message == 406920) { for (int x = 0;x < 3;x++) { - MetroidObjectiveSystemControl.Add_Objective(34+x,NewObjectiveSystem::Primary,NewObjectiveSystem::Pending,12814+x,"",12814+x,Commands->Get_Position(Commands->Find_Object(MetroidGameControl.MineTerminalID[x]))); - JmgUtility::SetHUDHelpText(12814+x,Vector3(0,1,0)); + if (MetroidObjectiveSystemControl.Get_Objective_Status(34+x) == NewObjectiveSystem::NotDefined) + { + MetroidObjectiveSystemControl.Add_Objective(34+x,NewObjectiveSystem::Primary,NewObjectiveSystem::Pending,12814+x,"",12814+x,Commands->Get_Position(Commands->Find_Object(MetroidGameControl.MineTerminalID[x]))); + JmgUtility::SetHUDHelpText(12814+x,Vector3(0,1,0)); + } } } } @@ -2369,7 +2389,7 @@ Commands->Set_Model(obj,"jGenerator"); GameObject *aBeacon = Commands->Find_Object(Get_Int_Parameter("BeaconID")); if (aBeacon) - Commands->Apply_Damage(aBeacon,9999.9f,"BlamoKiller",0); + Commands->Destroy_Object(aBeacon); MetroidScoreControl.MetroidPlayerHighScoreNodes[PlayerID]->PlacedRechargers++; Commands->Start_Timer(obj,this,0.1f,1); Commands->Start_Timer(obj,this,2.5f,2); @@ -3072,7 +3092,7 @@ DefenseModeSystemControl.DefensePlayerDataNodes[PlayerID].DefGunID = Commands->Get_ID(obj); GameObject *aBeacon = Commands->Find_Object(Get_Int_Parameter("BeaconID")); if (aBeacon) - Commands->Apply_Damage(aBeacon,9999.9f,"BlamoKiller",0); + Commands->Destroy_Object(aBeacon); Commands->Attach_Script(obj,"JMG_Metroid_Camera_Behavior","90.0,0,1,0.0,0,Beep,0.5"); MetroidScoreControl.MetroidPlayerHighScoreNodes[PlayerID]->PlacedTurrets++; return; @@ -3090,7 +3110,7 @@ { GameObject *aBeacon = Commands->Find_Object(Get_Int_Parameter("BeaconID")); if (aBeacon) - Commands->Apply_Damage(aBeacon,9999.9f,"BlamoKiller",0); + Commands->Destroy_Object(aBeacon); DefenseModeSystemControl.DefensePlayerDataNodes[PlayerID].DefGunID = 0; MetroidEquipmentSystemControl -= obj; } @@ -3147,10 +3167,10 @@ DefenseModeSystemControl.AddTelepad(obj,PlayerID); DefenseModeSystemControl.DefensePlayerDataNodes[PlayerID].TelepadID = Commands->Get_ID(obj); Commands->Set_Animation(obj,"DefModeTelepad.DefModeTelepad",false,0,1,1,false); - GameObject *aBeacon = Commands->Find_Object(Get_Int_Parameter("BeaconID")); MetroidScoreControl.MetroidPlayerHighScoreNodes[PlayerID]->PlacedTeleports++; + GameObject *aBeacon = Commands->Find_Object(Get_Int_Parameter("BeaconID")); if (aBeacon) - Commands->Apply_Damage(aBeacon,9999.9f,"BlamoKiller",0); + Commands->Destroy_Object(aBeacon); Commands->Start_Timer(obj,this,1.0f,1); return; } @@ -3167,7 +3187,7 @@ { GameObject *aBeacon = Commands->Find_Object(Get_Int_Parameter("BeaconID")); if (aBeacon) - Commands->Apply_Damage(aBeacon,9999.9f,"BlamoKiller",0); + Commands->Destroy_Object(aBeacon); else { DefenseModeSystemControl.RemoveTelepad(obj); @@ -3179,7 +3199,7 @@ { if (!enter->As_SoldierGameObj() || MetroidPSSControl.PlayerSettings[JmgUtility::JMG_Get_Player_ID(enter)]->IsInTeleport) return; - DefenseModeSystem::TelepadNode *Node = DefenseModeSystemControl.FindNextTelepad(Get_Int_Parameter("PlayerID")); + DefenseModeSystem::TelepadNode *Node = DefenseModeSystemControl.FindNextTelepad(Get_Int_Parameter("PadID")); if (!Node) return; Vector3 TargetPos = Node->Pos; @@ -3883,7 +3903,8 @@ Commands->Set_Position(obj,Vector3(-1295.915f,-959.524f,2.195f)); GameObject *Banshee = Commands->Create_Object("Alien_Strike_Craft",Vector3(0.0f,0.0f,0.0f)); Commands->Attach_To_Object_Bone(Banshee,obj,"Banshee"); - Commands->Attach_Script(Banshee,"JMG_Metroid_Base_Defense","0.0,200.0,3"); + Commands->Attach_Script(Banshee,"M00_Base_Defense","0.0,300.0,3"); + Commands->Attach_Script(Banshee,"JMG_Utility_Timer_Trigger_Enemy_Seen","0.1"); BansheeID = Commands->Get_ID(Banshee); Animation_Complete(obj,""); } @@ -4701,6 +4722,11 @@ MetroidGameControl.MineTerminalReset[TerminalNumber]--; if (!MetroidGameControl.MineTerminalReset[TerminalNumber]) { + if (MetroidObjectiveSystemControl.Get_Objective_Status(34+TerminalNumber) == NewObjectiveSystem::Accomplished) + { + MetroidObjectiveSystemControl.Remove_Objective(34+TerminalNumber); + MetroidObjectiveSystemControl.Add_Objective(34+TerminalNumber,NewObjectiveSystem::Primary,NewObjectiveSystem::Pending,12814+TerminalNumber,"",12814+TerminalNumber,Commands->Get_Position(Commands->Find_Object(MetroidGameControl.MineTerminalID[TerminalNumber]))); + } Commands->Enable_HUD_Pokable_Indicator(obj,true); MetroidGameControl.NumberOfDeactivatedTerminals--; MetroidGameControl.MineTerminalDeactivated[TerminalNumber] = false; @@ -5373,7 +5399,7 @@ ScriptRegistrant JMG_Metroid_Defense_Gun_Powerup_Registrant("JMG_Metroid_Defense_Gun_Powerup","BeaconID:int,PlayerID:int"); ScriptRegistrant JMG_Metroid_Defense_Telepad_Beacon_Registrant("JMG_Metroid_Defense_Telepad_Beacon",""); ScriptRegistrant JMG_Metroid_Defense_Telepad_Powerup_Registrant("JMG_Metroid_Defense_Telepad_Powerup","BeaconID:int,PlayerID:int"); -ScriptRegistrant JMG_Metroid_Defense_Telepad_Zone_Registrant("JMG_Metroid_Defense_Telepad_Zone","PlayerID:int"); +ScriptRegistrant JMG_Metroid_Defense_Telepad_Zone_Registrant("JMG_Metroid_Defense_Telepad_Zone","PlayerID:int,PadID:int"); ScriptRegistrant JMG_Metroid_Preplaced_Player_Telepad_Registrant("JMG_Metroid_Preplaced_Player_Telepad",""); ScriptRegistrant JMG_Metroid_Player_Turret_Damage_Registrant("JMG_Metroid_Player_Turret_Damage",""); ScriptRegistrant JMG_Metroid_Screen_Fade_Red_On_Damage_Registrant("JMG_Metroid_Screen_Fade_Red_On_Damage",""); diff -urN sourceold/scripts/jmgMetroid.h source/scripts/jmgMetroid.h --- sourceold/scripts/jmgMetroid.h 2019-08-20 10:11:06.811000100 +1000 +++ source/scripts/jmgMetroid.h 2021-07-27 14:44:22.796004200 +1000 @@ -492,13 +492,14 @@ saveInProgress = true; FILE *SaveScores; FILE *SaveScores2; - char tempFileName[256],finalFileName[256],textFileName[256]; + char tempFileName[256],finalFileName[256],textFileName[256],textTmpFileName[256]; _mkdir(metroidSavePath); sprintf(finalFileName,"%sBetaMetroidPlayerRecords.Rp2",metroidSavePath); sprintf(textFileName,"%sMetroidPlayerRecords.txt",metroidSavePath); + sprintf(textTmpFileName,"%sMetroidPlayerRecordsTmp.txt",metroidSavePath); sprintf(tempFileName,"%sBetaMetroidPlayerRecords.tmp",metroidSavePath); SaveScores = fopen(tempFileName,"w"); - SaveScores2 = fopen(textFileName,"w"); + SaveScores2 = fopen(textTmpFileName,"w"); MetroidHighScoresNode *Current = MetroidHighScoresNodeList; while (Current) { @@ -519,6 +520,8 @@ fclose(SaveScores2); remove(finalFileName); rename(tempFileName,finalFileName); + remove(textFileName); + rename(textTmpFileName,textFileName); saveInProgress = false; } void LoadData() @@ -2130,6 +2133,7 @@ bool cityShieldLowered; bool electrifiedWaterTurnedOff; bool tunnelEntryLowered; + int winMusicId; MetroidGame() { LastPlayerCount = 0; @@ -2140,6 +2144,7 @@ BossMode = 0; BossDefeated = false; BossDefeatedMusicID = 0; + winMusicId = 0; for (int x = 0;x < 3;x++) { MineTerminalID[x] = 0; @@ -2705,6 +2710,19 @@ LastFired = 0; MedicalPackNodeList = NULL; } + int FindFirstID() + { + if (!MedicalPackNodeList) + return 0; + MedicalPackNode *Current = MedicalPackNodeList; + while (Current) + { + if (Current->ID) + return Current->ID; + Current = Current->next; + } + return 0; + } void AddMedicalPack(GameObject *obj) { MedicalPackCount++; @@ -2722,7 +2740,7 @@ } if (MedicalPackCount > 3) { - GameObject *object = Commands->Find_Object(MedicalPackNodeList->ID); + GameObject *object = Commands->Find_Object(FindFirstID()); if (object) Commands->Destroy_Object(object); } @@ -2736,13 +2754,7 @@ if (Current->ID == ID) { MedicalPackCount--; - if (!Prev) - MedicalPackNodeList = MedicalPackNodeList->next; - else if (Current->next) - Prev->next = Current->next; - else - Prev->next = NULL; - delete Current; + Current->ID = 0; return; } Prev = Current; @@ -3671,7 +3683,7 @@ OBBoxClass TheBox(Pos,Vector3(0.5f,0.5f,0.5f),rotation); GameObject *theZone = Create_Zone("Script_Zone_All",TheBox); char params[32]; - sprintf(params,"%d",PlayerID); + sprintf(params,"%d,%d",PlayerID,ID); Commands->Attach_Script(theZone,"JMG_Metroid_Defense_Telepad_Zone",params); ZoneID = Commands->Get_ID(theZone); playerDeathCount = 0; @@ -3710,9 +3722,6 @@ GameObject *OldPad = Commands->Find_Object(Current->ID); if (OldPad) Commands->Destroy_Object(OldPad); - Current->ID = Commands->Get_ID(TelePad); - Current->Pos = Commands->Get_Position(TelePad); - break; } if (!Current->next) { @@ -3721,17 +3730,14 @@ } Current = Current->next; } - if (TelepadNodeList->next) - { - GameObject *Telepad = Commands->Find_Object(TelepadNodeList->ID); - if (Telepad) - Commands->Set_Animation(Telepad,"DefModeTelepad.DefModeTelepad",false,0,1,1,false); - } + GameObject *Telepad = Commands->Find_Object(TelepadNodeList->ID); + if (Telepad) + Commands->Set_Animation(Telepad,"DefModeTelepad.DefModeTelepad",false,0,1,1,false); } void RemoveTelepad(GameObject *telepad) { int ID = Commands->Get_ID(telepad); - TelepadNode *Current = TelepadNodeList,*Prev = NULL; + TelepadNode *Current = TelepadNodeList; while (Current) { if (Current->ID == ID) @@ -3739,53 +3745,65 @@ GameObject *Zone = Commands->Find_Object(Current->ZoneID); if (Zone) Commands->Destroy_Object(Zone); - if (!Prev) - TelepadNodeList = Current->next; - else - Prev->next = Current->next; - delete Current; - if (TelepadNodeList && !TelepadNodeList->next) - { - GameObject *Telepad = Commands->Find_Object(TelepadNodeList->ID); - if (Telepad) - Commands->Set_Animation(Telepad,"DefModeTelepad.DefModeTelepad",false,0,0,0,false); - } - return; + Current->ID = 0; + Current->PlayerID = 0; + Current->ZoneID = 0; + break; + } + Current = Current->next; + } + bool activeTeleporter = false; + Current = TelepadNodeList; + while (Current) + { + if (Current->ID && TelepadNodeList->ID != Current->ID) + { + activeTeleporter = true; + break; } - Prev = Current; Current = Current->next; } + if (activeTeleporter) + return; + if (TelepadNodeList) + { + GameObject *Telepad = Commands->Find_Object(TelepadNodeList->ID); + if (Telepad) + Commands->Set_Animation(Telepad,"DefModeTelepad.DefModeTelepad",false,0,0,0,false); + } } void RemoveAllButSpecial() { - TelepadNode *Current = TelepadNodeList,*die = NULL; + TelepadNode *Current = TelepadNodeList; if (Current) Current = Current->next; while (Current) { - die = Current; - Current = Current->next; - GameObject *Zone = Commands->Find_Object(die->ZoneID); + GameObject *Zone = Commands->Find_Object(Current->ZoneID); if (Zone) Commands->Destroy_Object(Zone); - delete die; + Current->ID = 0; + Current = Current->next; } GameObject *Telepad = Commands->Find_Object(TelepadNodeList->ID); if (Telepad) Commands->Set_Animation(Telepad,"DefModeTelepad.DefModeTelepad",false,0,0,0,false); } - TelepadNode *FindNextTelepad(int TelepadPlayerID) + TelepadNode *FindNextTelepad(int TelepadID) { if (!TelepadNodeList || !TelepadNodeList->next) return NULL; + bool goodToGo = false; TelepadNode *Current = TelepadNodeList; while (Current) { - if (Current->PlayerID == TelepadPlayerID) - return Current->next ? Current->next : TelepadNodeList; + if (goodToGo && Current->ID) + return Current; + if (Current->ID == TelepadID) + goodToGo = true; Current = Current->next; } - return NULL; + return TelepadNodeList->ID == TelepadID ? NULL : TelepadNodeList; } TelepadNode *FindPlayerTelepad(int TelepadPlayerID) { @@ -3885,13 +3903,7 @@ { if (Current->ID == ID) { - if (!Prev) - EquipmentNodeList = EquipmentNodeList->next; - else if (Current->next) - Prev->next = Current->next; - else - Prev->next = NULL; - delete Current; + Current->ID = 0; break; } Prev = Current; @@ -3945,6 +3957,11 @@ EquipmentNode *Current = EquipmentNodeList,*Nearest = NULL; while (Current) { + if (!Current->ID) + { + Current = Current->next; + continue; + } float Temp = JmgUtility::SimpleDistance(*Pos,Current->Pos); if ((!Nearest || Temp < Dist) && Current->ID != *EquipmentID) { @@ -4226,6 +4243,19 @@ UpgradePowerupCount = 0; UpgradePowerupNodeList = NULL; } + int GrabFirstID() + { + if (!UpgradePowerupNodeList) + return 0; + UpgradePowerupNode *Current = UpgradePowerupNodeList; + while (Current) + { + if (Current->ID) + return Current->ID; + Current = Current->next; + } + return 0; + } void AddUpgradePowerup(GameObject *obj) { UpgradePowerupCount++; @@ -4243,7 +4273,7 @@ } if (UpgradePowerupCount > 5) { - GameObject *object = Commands->Find_Object(UpgradePowerupNodeList->ID); + GameObject *object = Commands->Find_Object(GrabFirstID()); if (object) Commands->Destroy_Object(object); } @@ -4257,13 +4287,7 @@ if (Current->ID == ID) { UpgradePowerupCount--; - if (!Prev) - UpgradePowerupNodeList = UpgradePowerupNodeList->next; - else if (Current->next) - Prev->next = Current->next; - else - Prev->next = NULL; - delete Current; + Current->ID = 0; return; } Prev = Current; diff -urN sourceold/scripts/jmgUtility.cpp source/scripts/jmgUtility.cpp --- sourceold/scripts/jmgUtility.cpp 2020-09-10 09:08:27.970221800 +1000 +++ source/scripts/jmgUtility.cpp 2021-07-27 14:44:22.798967900 +1000 @@ -1517,11 +1517,11 @@ sprintf(JMG_Utility_Play_Music_On_Join_Controller::musicFileName,"%s",Get_Parameter("Music")); for (int x = 1;x < 128;x++) playingMusic[x] = false; - Commands->Start_Timer(obj,this,1.0,1); + Commands->Start_Timer(obj,this,1.0,23453451); } void JMG_Utility_Play_Music_On_Join_Controller::Timer_Expired(GameObject *obj,int number) { - if (number == 1) + if (number == 23453451) { for (int x = 1;x < 128;x++) { @@ -1534,7 +1534,7 @@ if (!player && playingMusic[x]) playingMusic[x] = false; } - Commands->Start_Timer(obj,this,1.0,1); + Commands->Start_Timer(obj,this,1.0,23453451); } } void JMG_Utility_Play_Music_On_Join_Controller::Destroyed(GameObject *obj) @@ -1543,6 +1543,8 @@ } void JMG_Utility_Play_Music_On_Join_Controller::Detach(GameObject *obj) { + if (Exe == 4) + return; Destroyed(obj); } void JMG_Utility_Play_Music_On_Join_Controller::Set_Music(const char *musicFilName,int fadeOut,int fadeIn) @@ -2227,7 +2229,7 @@ { if (!enter->As_SoldierGameObj()) return; - if (!Is_Script_Attached(enter,"JMG_Utility_Swimming_Infantry") && !Is_Script_Attached(enter,"JMG_Utility_Swimming_Infantry_Advanced")) + if (!Is_Script_Attached(enter,"JMG_Utility_Swimming_Infantry") && !Is_Script_Attached(enter,"JMG_Utility_Swimming_Infantry_Advanced") && !Is_Script_Attached(enter,"JMG_Utility_Swimming_Infantry_AI") && !Is_Script_Attached(enter,"JMG_Utility_Swimming_Infantry_Advanced_AI")) { Commands->Apply_Damage(enter,9999.9f,"Death",obj); if (Commands->Is_A_Star(enter) && !Commands->Get_Health(enter)) @@ -2258,6 +2260,14 @@ bool JMG_Utility_Swimming_Infantry::isInWater[128] = {false}; void JMG_Utility_Swimming_Infantry::Created(GameObject *obj) { + if (!Commands->Is_A_Star(obj)) + { + char params[1024]; + sprintf(params,"%s,%.2f,%.2f,%s,%s,%s,%.2f%,%.2f,%.2f,%s,%d,%d,%s,%s,%s",Get_Parameter("WeaponPreset"),Get_Float_Parameter("DrownTime"),Get_Float_Parameter("StartDrownSequence"),Get_Parameter("GaspForBreath"),Get_Parameter("PantingSoundEmitterModel"),Get_Parameter("HeartBeatSoundEmitterModel"),Get_Float_Parameter("DrownDamageRate"),Get_Float_Parameter("CatchBreathRate"),Get_Float_Parameter("WaterDamageAmount"),Get_Parameter("WaterDamageWarhead"),Get_Int_Parameter("WaterDamageDelayTime"),Get_Int_Parameter("WaterDamageDelayTimeRecover"),Get_Parameter("SwimmingSkin"),Get_Parameter("SwimmingArmor"),Get_Parameter("SwimmingModel")); + Commands->Attach_Script(obj,"JMG_Utility_Swimming_Infantry_AI",params); + Destroy_Script(); + return; + } lastDisplayTime = NULL; lastWaterZoneId = 0; heartBeatSoundId = 0; @@ -2433,6 +2443,17 @@ { DestroySoundEmitter(&heartBeatSoundId); DestroySoundEmitter(&pantSoundId); + if (!waterZoneCount) + return; + if (startedFadeRed) + { + Set_Screen_Fade_Color_Player(obj,1.0f,0.0f,0.0f,JmgUtility::MathClamp(Get_Float_Parameter("DrownTime")-drownTime,0,Get_Float_Parameter("StartDrownSequence"))); + CreateSoundEmitter(obj,Get_Parameter("HeartBeatSoundEmitterModel"),&heartBeatSoundId); + } + else + Set_Screen_Fade_Color_Player(obj,JMG_Utility_Swimming_Zone::waterNode[playerId].waterColor.X,JMG_Utility_Swimming_Zone::waterNode[playerId].waterColor.Y,JMG_Utility_Swimming_Zone::waterNode[playerId].waterColor.Z,0.0f); + Set_Screen_Fade_Opacity_Player(obj,JMG_Utility_Swimming_Zone::waterNode[playerId].waterColorOpacity,0.1f); + Set_Fog_Range_Player(obj,JMG_Utility_Swimming_Zone::waterNode[playerId].waterMinViewDistance,JMG_Utility_Swimming_Zone::waterNode[playerId].waterMaxViewDistance,0.1f); } void JMG_Utility_Swimming_Infantry::Destroyed(GameObject *obj) { @@ -3083,40 +3104,63 @@ } void JMG_Utility_AI_Goto_Player::Stuck_Check(GameObject *obj,Vector3 targetPos) { - if (!obj->As_VehicleGameObj()) - return; - Vector3 pos = Commands->Get_Position(obj); - if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) + if (obj->As_VehicleGameObj()) { - if (targetId && JmgUtility::SimpleDistance(pos,targetPos) <= attackArriveDistance*attackArriveDistance) + Vector3 pos = Commands->Get_Position(obj); + if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) + { + if (targetId && JmgUtility::SimpleDistance(pos,targetPos) <= attackArriveDistance*attackArriveDistance) + { + stuckTime = 0; + return; + } + if (!reverseTime) + { + stuckTime++; + if (stuckTime > 30) + { + moveBackward = true; + Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + reverseTime = Commands->Get_Random_Int(10,60); + stuckTime = 0; + } + } + } + else { stuckTime = 0; - return; + lastPosition = pos; } - if (!reverseTime) + if (reverseTime) { - stuckTime++; - if (stuckTime > 30) + reverseTime--; + if (!reverseTime) { - moveBackward = true; + moveBackward = false; Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); - reverseTime = Commands->Get_Random_Int(10,60); - stuckTime = 0; } } } else { - stuckTime = 0; - lastPosition = pos; - } - if (reverseTime) - { - reverseTime--; - if (!reverseTime) + Vector3 pos = Commands->Get_Position(obj); + if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) { - moveBackward = false; - Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + if (targetId && JmgUtility::SimpleDistance(pos,targetPos) <= attackArriveDistance*attackArriveDistance) + { + stuckTime = 0; + return; + } + stuckTime++; + if (stuckTime > 30) + Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + if (stuckTime > 60) + Attack_Move(obj,NULL,homeLocation,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + } + else + { + stuckTime = 0; + lastPosition = pos; } } } @@ -4170,6 +4214,7 @@ if (ammo) weaponRange = ammo->Range; fireRange = Get_Float_Parameter("FireRange") >= 0 ? Get_Float_Parameter("FireRange") : weaponRange; + Commands->Innate_Disable(obj); Commands->Enable_Enemy_Seen(obj,true); Commands->Enable_Engine(obj,true); Commands->Start_Timer(obj,this,1.0f,1); @@ -5693,40 +5738,63 @@ } void JMG_Utility_AI_Goto_Enemy::Stuck_Check(GameObject *obj,Vector3 targetPos) { - if (!obj->As_VehicleGameObj()) - return; - Vector3 pos = Commands->Get_Position(obj); - if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) + if (obj->As_VehicleGameObj()) { - if (targetId && JmgUtility::SimpleDistance(pos,targetPos) <= attackArriveDistance*attackArriveDistance) + Vector3 pos = Commands->Get_Position(obj); + if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) + { + if (targetId && JmgUtility::SimpleDistance(pos,targetPos) <= attackArriveDistance*attackArriveDistance) + { + stuckTime = 0; + return; + } + if (!reverseTime) + { + stuckTime++; + if (stuckTime > 30) + { + moveBackward = true; + Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + reverseTime = Commands->Get_Random_Int(10,60); + stuckTime = 0; + } + } + } + else { stuckTime = 0; - return; + lastPosition = pos; } - if (!reverseTime) + if (reverseTime) { - stuckTime++; - if (stuckTime > 30) + reverseTime--; + if (!reverseTime) { - moveBackward = true; + moveBackward = false; Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); - reverseTime = Commands->Get_Random_Int(10,60); - stuckTime = 0; } } } else { - stuckTime = 0; - lastPosition = pos; - } - if (reverseTime) - { - reverseTime--; - if (!reverseTime) + Vector3 pos = Commands->Get_Position(obj); + if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) { - moveBackward = false; - Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + if (targetId && JmgUtility::SimpleDistance(pos,targetPos) <= attackArriveDistance*attackArriveDistance) + { + stuckTime = 0; + return; + } + stuckTime++; + if (stuckTime > 30) + Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + if (stuckTime > 60) + Attack_Move(obj,NULL,homeLocation,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + } + else + { + stuckTime = 0; + lastPosition = pos; } } } @@ -6109,40 +6177,63 @@ } void JMG_Utility_AI_Goto_Enemy_Not_Star::Stuck_Check(GameObject *obj,Vector3 targetPos) { - if (!obj->As_VehicleGameObj()) - return; - Vector3 pos = Commands->Get_Position(obj); - if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) + if (obj->As_VehicleGameObj()) { - if (targetId && JmgUtility::SimpleDistance(pos,targetPos) <= attackArriveDistance*attackArriveDistance) + Vector3 pos = Commands->Get_Position(obj); + if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) + { + if (targetId && JmgUtility::SimpleDistance(pos,targetPos) <= attackArriveDistance*attackArriveDistance) + { + stuckTime = 0; + return; + } + if (!reverseTime) + { + stuckTime++; + if (stuckTime > 30) + { + moveBackward = true; + Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + reverseTime = Commands->Get_Random_Int(10,60); + stuckTime = 0; + } + } + } + else { stuckTime = 0; - return; + lastPosition = pos; } - if (!reverseTime) + if (reverseTime) { - stuckTime++; - if (stuckTime > 30) + reverseTime--; + if (!reverseTime) { - moveBackward = true; + moveBackward = false; Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); - reverseTime = Commands->Get_Random_Int(10,60); - stuckTime = 0; } } } else { - stuckTime = 0; - lastPosition = pos; - } - if (reverseTime) - { - reverseTime--; - if (!reverseTime) + Vector3 pos = Commands->Get_Position(obj); + if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) { - moveBackward = false; - Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + if (targetId && JmgUtility::SimpleDistance(pos,targetPos) <= attackArriveDistance*attackArriveDistance) + { + stuckTime = 0; + return; + } + stuckTime++; + if (stuckTime > 30) + Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + if (stuckTime > 60) + Attack_Move(obj,NULL,homeLocation,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + } + else + { + stuckTime = 0; + lastPosition = pos; } } } @@ -6464,6 +6555,7 @@ bool JMG_Utility_Send_Custom_On_Deaths_Controller::controllerPlaced = false; bool JMG_Utility_Send_Custom_On_Deaths_Controller::announceOnFirstDeath = false; bool JMG_Utility_Send_Custom_On_Deaths_Controller::onlyTrackPlayerDeaths = false; +bool JMG_Utility_Send_Custom_On_Deaths_Controller::recordAIDeaths = false; void JMG_Utility_Send_Custom_On_Deaths_Controller::Created(GameObject *obj) { if (controllerPlaced) @@ -6497,6 +6589,7 @@ requiresADeathToStartNoPlayerAdd = Get_Int_Parameter("RequiresADeathToStartNoPlayerAdd") ? true : false; announceOnFirstDeath = Get_Int_Parameter("AnnounceOnFirstDeath") ? true : false; onlyTrackPlayerDeaths = Get_Int_Parameter("OnlyTrackPlayerDeaths") ? true : false; + recordAIDeaths = Get_Int_Parameter("RecordAIDeaths") ? true : false; myId = Commands->Get_ID(obj); } void JMG_Utility_Send_Custom_On_Deaths_Controller::Timer_Expired(GameObject *obj,int number) @@ -6549,7 +6642,7 @@ { if (!controllerPlaced) return; - if (onlyTrackPlayerDeaths && !Commands->Is_A_Star(obj)) + if (onlyTrackPlayerDeaths && !(recordAIDeaths || Commands->Is_A_Star(obj))) return; deathCount++; int remainingLives = maxDeaths-deathCount; @@ -7984,6 +8077,7 @@ } void JMG_Utility_Custom_Send_Custom_On_Player_Count_HUD::Created(GameObject *obj) { + enabled = true; hudStringId = Get_Int_Parameter("HudStringId"); sprintf(hudMessage,"%s",Get_Translated_String(hudStringId)); hudStringColor = Get_Vector3_Parameter("HudStringColor"); @@ -7995,6 +8089,8 @@ } void JMG_Utility_Custom_Send_Custom_On_Player_Count_HUD::Custom(GameObject *obj,int message,int param,GameObject *sender) { + if (!enabled) + return; if (message == resetCustom) { runningCount = 0; @@ -8003,7 +8099,8 @@ { runningCount++; int requiredCount = baseCount+playerCount*Get_Player_Count(); - JmgUtility::SetHUDHelpText(hudStringId,formatHUDMessage(hudMessage,runningCount,requiredCount),hudStringColor); + if (runningCount <= requiredCount) + JmgUtility::SetHUDHelpText(hudStringId,formatHUDMessage(hudMessage,runningCount,requiredCount),hudStringColor); if (runningCount < requiredCount) return; int id = Get_Int_Parameter("ID"); @@ -8013,7 +8110,7 @@ int Param = Get_Int_Parameter("Param"); Commands->Send_Custom_Event(obj,object,Get_Int_Parameter("SendCustom"),Param == -1 ? param : Param,Get_Float_Parameter("Delay")); } - this->Destroy_Script(); + enabled = false; } } char *JMG_Utility_Custom_Send_Custom_On_Player_Count_HUD::formatHUDMessage(const char *format,...) @@ -9792,6 +9889,9 @@ } void JMG_Utility_Basic_Spawner_In_Radius_Attached::Created(GameObject *obj) { + if (sentCreateMessage) + return; + sentCreateMessage = true; GameObject *controller = Commands->Find_Object(Get_Int_Parameter("Controller_ID")); if (controller) Commands->Send_Custom_Event(obj,controller,6873523,Get_Int_Parameter("Script_ID"),0.0f); @@ -9799,6 +9899,9 @@ } void JMG_Utility_Basic_Spawner_In_Radius_Attached::Destroyed(GameObject *obj) { + if (sentDeathMessage) + return; + sentDeathMessage = true; GameObject *controller = Commands->Find_Object(Get_Int_Parameter("Controller_ID")); if (controller) Commands->Send_Custom_Event(obj,controller,6873524,Get_Int_Parameter("Script_ID"),0.0f); @@ -10774,6 +10877,14 @@ } void JMG_Utility_Swimming_Infantry_Advanced::Created(GameObject *obj) { + if (!Commands->Is_A_Star(obj)) + { + char params[1024]; + sprintf(params,"%d,%s,%d,%d,%.2f,%.2f,%.2f,%s,%s,%s,%.2f%,%.2f,%.2f,%s,%d,%d,%s,%s,%s,%.2f,%.2f",Get_Int_Parameter("WeaponsGroupID"),Get_Parameter("WeaponPreset"),Get_Int_Parameter("ForceDefinedWeapons"),Get_Int_Parameter("DefaultHoldStyle"),Get_Float_Parameter("DefaultSwimSpeed"),Get_Float_Parameter("DrownTime"),Get_Float_Parameter("StartDrownSequence"),Get_Parameter("GaspForBreath"),Get_Parameter("PantingSoundEmitterModel"),Get_Parameter("HeartBeatSoundEmitterModel"),Get_Float_Parameter("DrownDamageRate"),Get_Float_Parameter("CatchBreathRate"),Get_Float_Parameter("WaterDamageAmount"),Get_Parameter("WaterDamageWarhead"),Get_Int_Parameter("WaterDamageDelayTime"),Get_Int_Parameter("WaterDamageDelayTimeRecover"),Get_Parameter("SwimmingSkin"),Get_Parameter("SwimmingArmor"),Get_Parameter("SwimmingModel"),Get_Float_Parameter("SwimmingHeightScale"),Get_Float_Parameter("SwimmingWidthScale")); + Commands->Attach_Script(obj,"JMG_Utility_Swimming_Infantry_Advanced_AI",params); + Destroy_Script(); + return; + } lastDisplayTime = NULL; lastWaterZoneId = 0; heartBeatSoundId = 0; @@ -10962,7 +11073,8 @@ waterSpeedMultiplier = (param/100.0f); JMG_Utility_Swimming_Infantry::isInWater[playerId] = true; sprintf(enterWeapon,"%s",Get_Current_Weapon(obj) ? Get_Current_Weapon(obj) : ""); - Grant_Weapon(obj,defaultWeaponPreset,true,-1,1); + if (forceDefinedWeapons) + Grant_Weapon(obj,defaultWeaponPreset,true,-1,1); obj->As_SoldierGameObj()->Set_Can_Play_Damage_Animations(false); obj->As_SoldierGameObj()->Set_Movement_Loiters_Allowed(false); obj->As_SoldierGameObj()->Set_Human_Anim_Override(false); @@ -10992,6 +11104,17 @@ { DestroySoundEmitter(&heartBeatSoundId); DestroySoundEmitter(&pantSoundId); + if (!waterZoneCount) + return; + if (startedFadeRed) + { + Set_Screen_Fade_Color_Player(obj,1.0f,0.0f,0.0f,JmgUtility::MathClamp(defaultDrownTime-drownTime,0,startDrownSequence)); + CreateSoundEmitter(obj,heartBeatSoundEmitterModel,&heartBeatSoundId); + } + else + Set_Screen_Fade_Color_Player(obj,JMG_Utility_Swimming_Zone::waterNode[playerId].waterColor.X,JMG_Utility_Swimming_Zone::waterNode[playerId].waterColor.Y,JMG_Utility_Swimming_Zone::waterNode[playerId].waterColor.Z,0.0f); + Set_Screen_Fade_Opacity_Player(obj,JMG_Utility_Swimming_Zone::waterNode[playerId].waterColorOpacity,0.1f); + Set_Fog_Range_Player(obj,JMG_Utility_Swimming_Zone::waterNode[playerId].waterMinViewDistance,JMG_Utility_Swimming_Zone::waterNode[playerId].waterMaxViewDistance,0.1f); } void JMG_Utility_Swimming_Infantry_Advanced::Destroyed(GameObject *obj) { @@ -13259,12 +13382,22 @@ for (SLNode *current = JMG_Utility_Control_Point_Controller::wanderPoints.Head();current;current = current->Next()) { GameObject* o = current->Data(); - if (!o || !Is_Script_Attached(o,"JMG_Utility_Control_Point_Wander_Point")) - continue; - JMG_Utility_Control_Point_Wander_Point *cpScript = (JMG_Utility_Control_Point_Wander_Point*)Find_Script_On_Object(o,"JMG_Utility_Control_Point_Wander_Point"); - if (!cpScript) + if (!o) continue; - cpScript->UpdateWanderpointSettings(); + if (Is_Script_Attached(o,"JMG_Utility_Control_Point_Wander_Point")) + { + JMG_Utility_Control_Point_Wander_Point *cpScript = (JMG_Utility_Control_Point_Wander_Point*)Find_Script_On_Object(o,"JMG_Utility_Control_Point_Wander_Point"); + if (!cpScript) + continue; + cpScript->UpdateWanderpointSettings(); + } + if (Is_Script_Attached(o,"JMG_Utility_Control_Point_Team_Target_Wander_Point")) + { + JMG_Utility_Control_Point_Team_Target_Wander_Point *cpScript = (JMG_Utility_Control_Point_Team_Target_Wander_Point*)Find_Script_On_Object(o,"JMG_Utility_Control_Point_Team_Target_Wander_Point"); + if (!cpScript) + continue; + cpScript->UpdateWanderpointSettings(); + } } } void JMG_Utility_Control_Point_Setting_Override::Created(GameObject *obj) @@ -13304,6 +13437,10 @@ Commands->Start_Timer(obj,this,0.1f,1); } } +void JMG_Utility_Control_Point_Team_Member::Killed(GameObject *obj,GameObject *killer) +{ + setupComplete = false; +} int JMG_Utility_Control_Point_Assault_Mode::teamId = 0; int JMG_Utility_Control_Point_Assault_Mode::controllerId = 0; int JMG_Utility_Control_Point_Assault_Mode::frontLineGroup = 0; @@ -13363,9 +13500,9 @@ JMG_Utility_Control_Point *cpScript = (JMG_Utility_Control_Point*)Find_Script_On_Object(o,"JMG_Utility_Control_Point"); if (!cpScript || !cpScript->setupComplete || cpScript->cpGroupId < 0) continue; - if (cpScript->controllingTeamId != teamId || !cpScript->captured) + if (cpScript->controllingTeamId != teamId && cpScript->captured) enemyCount++; - if (cpScript->controllingTeamId == teamId || !cpScript->captured) + if (cpScript->controllingTeamId == teamId && cpScript->captured) friendlyCount++; if ((cpScript->controllingTeamId != teamId || !cpScript->captured) && cpScript->cpGroupId > maxEnemyGroup) maxEnemyGroup = cpScript->cpGroupId; @@ -13414,6 +13551,26 @@ frontLineGroup = minFriendlyGroup; spawnGroup = minSpawnGroup; enemySpawnGroup = maxEnemySpawnGroup; + for (SLNode *current = JMG_Utility_Control_Point_Controller::wanderPoints.Head();current;current = current->Next()) + { + GameObject* o = current->Data(); + if (!o) + continue; + if (Is_Script_Attached(o,"JMG_Utility_Control_Point_Wander_Point")) + { + JMG_Utility_Control_Point_Wander_Point *cpScript = (JMG_Utility_Control_Point_Wander_Point*)Find_Script_On_Object(o,"JMG_Utility_Control_Point_Wander_Point"); + if (!cpScript) + continue; + cpScript->UpdateWanderpointSettings(); + } + if (Is_Script_Attached(o,"JMG_Utility_Control_Point_Team_Target_Wander_Point")) + { + JMG_Utility_Control_Point_Team_Target_Wander_Point *cpScript = (JMG_Utility_Control_Point_Team_Target_Wander_Point*)Find_Script_On_Object(o,"JMG_Utility_Control_Point_Team_Target_Wander_Point"); + if (!cpScript) + continue; + cpScript->UpdateWanderpointSettings(); + } + } } void JMG_Utility_Control_Point_Assault_Mode::Destroyed(GameObject *obj) { @@ -14373,11 +14530,11 @@ sprintf(subobject,"%s",Get_Parameter("SubObject")); for (int x = 1;x < 128;x++) animating[x] = false; - Timer_Expired(obj,1); + Timer_Expired(obj,1024); } void JMG_Utility_Created_Animate_SubObject_On_Join::Timer_Expired(GameObject *obj,int number) { - if (number == 1) + if (number == 1024) { for (int x = 1;x < 128;x++) { @@ -14385,13 +14542,24 @@ if (player && !animating[x]) { animating[x] = true; - Set_Subobject_Animation_Player(player,obj,animation,true,subobject,0,-1,true); + TriggerAnimationForThePlayer(obj,x); + Commands->Start_Timer(obj,this,0.25f,x); } if (!player && animating[x]) animating[x] = false; } - Commands->Start_Timer(obj,this,0.1f,1); + Commands->Start_Timer(obj,this,0.1f,1024); } + if (number < 128) + TriggerAnimationForThePlayer(obj,number); + +} +void JMG_Utility_Created_Animate_SubObject_On_Join::TriggerAnimationForThePlayer(GameObject *obj,int playerId) +{ + GameObject *player = Get_GameObj(playerId); + if (!player) + return; + Set_Subobject_Animation_Player(player,obj,animation,true,subobject,0,-1,true); } void JMG_Utility_Custom_Play_Animation_Send_Custom_When_Complete::Created(GameObject *obj) { @@ -14599,6 +14767,1330 @@ } } } +void JMG_Utility_Custom_Enable_Spawners_Incrementally_In_Range::Created(GameObject *obj) +{ + currentId = Get_Int_Parameter("StartID"); + enable = Get_Int_Parameter("Enable") ? true : false; + custom = Get_Int_Parameter("Custom"); + endId = Get_Int_Parameter("EndID"); +} +void JMG_Utility_Custom_Enable_Spawners_Incrementally_In_Range::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == custom) + { + if (currentId > endId) + return; + Commands->Enable_Spawner(currentId,enable); + currentId++; + } +} +void JMG_Utility_Control_Point_Team_Target_Wander_Point::Created(GameObject *obj) +{ + controlPointId = Get_Int_Parameter("ControlPointID"); + teamId = Get_Int_Parameter("TeamID"); + targetGroupId = Get_Int_Parameter("TargetGroupID"); + nonTargetGroupId = Get_Int_Parameter("NonTargetGroupId"); + wanderPoint = AddAndReturnWanderpoint(obj); + wanderPoint->value = -999; + Commands->Set_Is_Rendered(obj,false); + Commands->Set_Is_Visible(obj,false); + Timer_Expired(obj,1); +} +void JMG_Utility_Control_Point_Team_Target_Wander_Point::Timer_Expired(GameObject *obj,int number) +{ + if (number == 1) + { + GameObject *controller = Commands->Find_Object(controlPointId); + if (!controller) + { + Commands->Start_Timer(obj,this,0.1f,1); + return; + } + cpScript = (JMG_Utility_Control_Point*)Find_Script_On_Object(controller,"JMG_Utility_Control_Point"); + if (!cpScript || !cpScript->setupComplete) + { + Commands->Start_Timer(obj,this,0.1f,1); + return; + } + cpScript->controlPointWanderPoints.Add_Tail(obj); + JMG_Utility_Control_Point_Controller::wanderPoints.Add_Tail(obj); + UpdateWanderpointSettings(); + } +} +void JMG_Utility_Control_Point_Team_Target_Wander_Point::UpdateWanderpointSettings() +{ + if (!cpScript || !cpScript->setupComplete || cpScript->cpGroupId < 0) + return; + if (targetGroupId != -999 && !cpScript->locked && (!cpScript->captured || cpScript->controllingTeamId != teamId) && (!cpScript->captured || + (cpScript->cpGroupId == JMG_Utility_Control_Point_Assault_Mode::spawnGroup && JMG_Utility_Control_Point_Assault_Mode::teamId != teamId) || + (cpScript->cpGroupId == JMG_Utility_Control_Point_Assault_Mode::enemySpawnGroup && JMG_Utility_Control_Point_Assault_Mode::teamId == teamId))) + wanderPoint->value = targetGroupId; + else + wanderPoint->value = nonTargetGroupId; +} +void JMG_Utility_AI_Control_Point::Created(GameObject *obj) +{ + state = IDLE; + moveBackward = false; + lastPosition = Commands->Get_Position(obj); + targetId = 0; + lastSeenTime = 0; + stuckTime = 0; + reverseTime = 0; + allowCaptureAttackDistract = false; + captureCpGroupId = Get_Int_Parameter("CaptureCpGroupId"); + captureCpChance = Get_Float_Parameter("CaptureCpChance"); + captureSpeed = Get_Float_Parameter("CaptureSpeed"); + captureDistance = Get_Float_Parameter("CaptureDistance"); + captureDistanceSquared = captureDistance*captureDistance; + defendCpGroupId = Get_Int_Parameter("DefendCpGroupId"); + defendSpeed = Get_Float_Parameter("DefendSpeed"); + defendDistance = Get_Float_Parameter("DefendDistance"); + defendDistanceSquared = defendDistance*defendDistance; + closeDefendDistanceSquared = Get_Float_Parameter("CloseDefendDistance"); + closeDefendDistanceSquared *= closeDefendDistanceSquared; + chooseFarDefendChance = Get_Float_Parameter("ChooseFarDefendChance"); + attackSpeed = Get_Float_Parameter("AttackSpeed"); + attackDistance = Get_Float_Parameter("AttackDistance"); + randomAttackDistance = Get_Float_Parameter("RandomAttackDistance"); + attackDistractFromCaptureChance = Get_Float_Parameter("AttackDistractFromCaptureChance"); + chanceToInvestigateLastSeenLocation = Get_Float_Parameter("ChanceToInvestigateLastSeenLocation"); + attackCheckBlocked = Get_Int_Parameter("AttackCheckBlocked") ? true : false; + canSeeStealth = Get_Int_Parameter("CanSeeStealth"); + shutdownEngineOnArrival = Get_Int_Parameter("ShutdownEngineOnArrival") ? true : false; + const AmmoDefinitionClass *ammo = Get_Weapon_Ammo_Definition(Get_Current_Weapon(obj),true); + if (ammo) + { + weaponRange = ammo->Range; + weaponEffectiveRange = ammo->Range < ammo->EffectiveRange ? ammo->Range : ammo->EffectiveRange; + } + attackArriveDistance = attackDistance >= 0 ? attackDistance : weaponEffectiveRange; + Commands->Enable_Enemy_Seen(obj,true); + Commands->Start_Timer(obj,this,1.0f,1); + Commands->Start_Timer(obj,this,1.0f,2); + if (randomAttackDistance) + Commands->Start_Timer(obj,this,0.0f,3); + Commands->Start_Timer(obj,this,10.0f,4); +} +void JMG_Utility_AI_Control_Point::Enemy_Seen(GameObject *obj,GameObject *seen) +{ + if (!Commands->Get_Health(seen)) + return; + if (Is_Script_Attached(seen,"JMG_Utility_AI_Goto_Enemy_Ignore_Object")) + return; + if (!_stricmp(Get_Skin(seen),"Blamo") || !_stricmp(Get_Shield_Type(seen),"Blamo")) + return; + if (canSeeStealth != 2 && seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed()) + { + if (!canSeeStealth) + return; + float dist = JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)); + if (seen->As_SoldierGameObj() && dist > seen->As_SoldierGameObj()->Get_Stealth_Fade_Distance()*seen->As_SoldierGameObj()->Get_Stealth_Fade_Distance()) + return; + else if (seen->As_VehicleGameObj() && dist > seen->As_VehicleGameObj()->Get_Stealth_Fade_Distance()*seen->As_VehicleGameObj()->Get_Stealth_Fade_Distance()) + return; + else if (dist > seen->As_SmartGameObj()->Get_Stealth_Fade_Distance()*seen->As_SmartGameObj()->Get_Stealth_Fade_Distance()) + return; + } + if (!targetId || !lastSeenTime || Choose_Target(obj,seen)) + TriggerAttack(obj,seen); + if (targetId == Commands->Get_ID(seen)) + lastSeenTime = Commands->Get_Random_Int(30,60); +} +void JMG_Utility_AI_Control_Point::Timer_Expired(GameObject *obj,int number) +{ + if (number == 1 && !The_Game()->Is_Game_Over()) + { + if ((state == CONTROL_POINT_ATTACK || state == CONTROL_POINT_DEFENSE || state == CHECKING_LOCATION) && !lastWanderPoint) + state = IDLE; + Vector3 targetPos = Commands->Get_Position(obj); + if (lastSeenTime) + { + lastSeenTime--; + if (!lastSeenTime) + { + if (state == CONTROL_POINT_ATTACK && lastWanderPoint) + Attack_Move(obj,NULL,lastWanderPoint->position,captureSpeed,captureDistance,false,false); + else + SelectNextMission(obj,ValidLastLocation(targetId)); + targetId = 0; + } + } + if (targetId) + { + GameObject *target = Commands->Find_Object(targetId); + targetPos = Commands->Get_Position(target); + if (!target || !Commands->Get_Health(target) || (Commands->Get_Player_Type(obj) != -2 && Commands->Get_Player_Type(target) == Commands->Get_Player_Type(obj)) || Is_Script_Attached(target,"JMG_Utility_AI_Goto_Enemy_Ignore_Object")) + { + lastSeenTime = 0; + if (state == CONTROL_POINT_ATTACK && lastWanderPoint) + Attack_Move(obj,NULL,lastWanderPoint->position,captureSpeed,captureDistance,false,false); + else + SelectNextMission(obj,ValidLastLocation(targetId)); + targetId = 0; + } + } + if (state == CONTROL_POINT_ATTACK && lastWanderPoint) + { + if (lastWanderPoint->value != captureCpGroupId) + SelectNextMission(obj,false); + } + if (state == CONTROL_POINT_DEFENSE && lastWanderPoint) + { + if (JmgUtility::SimpleDistance(lastWanderPoint->position,Commands->Get_Position(obj)) < defendDistanceSquared) + SelectNextMission(obj,false); + } + if (state == CHECKING_LOCATION && lastWanderPoint) + { + if (JmgUtility::SimpleDistance(lastWanderPoint->position,Commands->Get_Position(obj)) < (obj->As_VehicleGameObj() ? 25.0f : 1.0f)) + SelectNextMission(obj,false); + } + if (state == IDLE) + { + SelectNextMission(obj,false); + } + if (state == CHECKING_LOCATION) + if (JmgUtility::SimpleDistance(lastAction.location,Commands->Get_Position(obj)) <= (obj->As_VehicleGameObj() ? 25.0f : 1.0f)) + state = IDLE; + if (state == ACTION_BADPATH) + state = IDLE; + if (state != IDLE) + Stuck_Check(obj,targetPos); + Commands->Start_Timer(obj,this,0.1f,1); + } + if (number == 3) + { + attackArriveDistance = attackDistance >= 0 ? attackDistance : weaponEffectiveRange; + attackArriveDistance += Commands->Get_Random(randomAttackDistance,randomAttackDistance); + } + if (number == 4) + { + allowCaptureAttackDistract = Commands->Get_Random(0.0f,1.0f) < attackDistractFromCaptureChance; + Commands->Start_Timer(obj,this,10.0f,4); + } +} +void JMG_Utility_AI_Control_Point::Action_Complete(GameObject *obj,int action_id,ActionCompleteReason reason) +{ + if (reason == ACTION_COMPLETE_PATH_BAD_DEST) + { + if (state == CONTROL_POINT_ATTACK) + state = ACTION_BADPATH; + if (state == CONTROL_POINT_DEFENSE) + state = ACTION_BADPATH; + else if (state == CHECKING_LOCATION) + state = ACTION_BADPATH; + else if (state == ATTACKING_TARGET) + state = ACTION_BADPATH; + } +} +void JMG_Utility_AI_Control_Point::Damaged(GameObject *obj,GameObject *damager,float damage) +{ + if (damage <= 0.0f || Commands->Get_Player_Type(damager) == Commands->Get_Player_Type(obj)) + return; + if (!_stricmp(Get_Skin(damager),"Blamo") || !_stricmp(Get_Shield_Type(damager),"Blamo")) + return; + if (!targetId || !lastSeenTime || Choose_Target(obj,damager)) + TriggerAttack(obj,damager); +} +void JMG_Utility_AI_Control_Point::Attack_Move(GameObject *obj,GameObject *target,Vector3 location,float speed,float distance,bool attack,bool overrideLocation) +{ + Commands->Action_Reset(obj,100); + lastAction = LastAction(targetId,location,speed,distance,attack,overrideLocation); + ActionParamsStruct params; + params.ObserverID = this->Get_ID(); + if (target && !overrideLocation) + { + params.Set_Movement(target,speed,distance,false); + params.MoveFollow = true; + } + else + { + params.Set_Movement(location,speed,distance,false); + params.MoveFollow = false; + } + params.MoveBackup = moveBackward; + params.MovePathfind = true; + if (shutdownEngineOnArrival) + params.ShutdownEngineOnArrival = true; + Commands->Action_Goto(obj,params); + params.Set_Basic(this,100,100,AI_STATE_COMBAT); + params.AttackCheckBlocked = attackCheckBlocked; + if (target && attack) + { + params.Set_Attack(target,weaponRange,0.0f,true); + Commands->Action_Attack(obj,params); + } +} +void JMG_Utility_AI_Control_Point::SelectNextMission(GameObject *obj,ValidLastLocation goNearLastWanderPoint) +{ + state = IDLE; + Vector3 wanderPos; + if (goNearLastWanderPoint.valid && state != CONTROL_POINT_ATTACK && Commands->Get_Random(0.0f,1.0f) < chanceToInvestigateLastSeenLocation) + { + state = CHECKING_LOCATION; + Attack_Move(obj,NULL,goNearLastWanderPoint.location,attackSpeed,0.0f,false,false); + return; + } + GameObject *target = Commands->Find_Object(targetId); + if (Commands->Get_Random(0.0f,1.0f) < captureCpChance) + { + Rp2SimplePositionSystem::SimplePositionNode *node = Get_Capture_Point(); + if (node) + { + state = CONTROL_POINT_ATTACK; + lastWanderPoint = node; + Attack_Move(obj,target,lastWanderPoint->position,captureSpeed,captureDistance,false,true); + return; + } + } + Rp2SimplePositionSystem::SimplePositionNode *node = Get_Defense_Point(obj); + if (node) + { + state = CONTROL_POINT_DEFENSE; + lastWanderPoint = node; + Attack_Move(obj,target,lastWanderPoint->position,captureSpeed,0.0f,false,true); + return; + } +} +void JMG_Utility_AI_Control_Point::Stuck_Check(GameObject *obj,Vector3 targetPos) +{ + if (!obj->As_VehicleGameObj()) + { + Vector3 pos = Commands->Get_Position(obj); + if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) + { + if (state == ATTACKING_TARGET && targetId && JmgUtility::SimpleDistance(pos,Commands->Get_Position(Commands->Find_Object(targetId))) <= attackArriveDistance*attackArriveDistance) + { + stuckTime = 0; + return; + } + else if (state == CONTROL_POINT_ATTACK && lastWanderPoint &&JmgUtility::SimpleDistance(lastWanderPoint->position,pos) <= captureDistance) + { + stuckTime = 0; + return; + } + if (!reverseTime) + { + stuckTime++; + if (stuckTime > 30) + { + moveBackward = true; + Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + reverseTime = Commands->Get_Random_Int(10,60); + stuckTime = 0; + } + } + } + else + { + stuckTime = 0; + lastPosition = pos; + } + if (reverseTime) + { + reverseTime--; + if (!reverseTime) + { + moveBackward = false; + Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + } + } + } + else + { + Vector3 pos = Commands->Get_Position(obj); + if (JmgUtility::SimpleDistance(pos,lastPosition) < 1.0f) + { + if (state == ATTACKING_TARGET && targetId && JmgUtility::SimpleDistance(pos,Commands->Get_Position(Commands->Find_Object(targetId))) <= attackArriveDistance*attackArriveDistance) + { + stuckTime = 0; + return; + } + else if (state == CONTROL_POINT_ATTACK && lastWanderPoint && JmgUtility::SimpleDistance(lastWanderPoint->position,pos) <= captureDistance) + { + stuckTime = 0; + return; + } + stuckTime++; + if (stuckTime > 5) + { + Attack_Move(obj,Commands->Find_Object(lastAction.targetId),lastAction.location,lastAction.speed,lastAction.distance,lastAction.attack,lastAction.overrideLocation); + stuckTime = 0; + } + } + else + { + stuckTime = 0; + lastPosition = pos; + } + } +} +bool JMG_Utility_AI_Control_Point::Choose_Target(GameObject *obj,GameObject *target) +{ + Vector3 pos = Commands->Get_Position(obj); + GameObject *currentTarget = Commands->Find_Object(targetId); + if (!currentTarget) + return true; + if (JmgUtility::SimpleDistance(pos,Commands->Get_Position(target)) < JmgUtility::SimpleDistance(pos,Commands->Get_Position(currentTarget))) + return true; + return false; +} +JMG_Utility_AI_Control_Point::ValidLastLocation::ValidLastLocation(int enemyId) +{ + GameObject *target = Commands->Find_Object(enemyId); + if (!target || !Commands->Get_Health(target)) + { + this->valid = false; + return; + } + this->location = Commands->Get_Position(target); + this->valid = true; +} +void JMG_Utility_AI_Control_Point::TriggerAttack(GameObject *obj,GameObject *target) +{ + targetId = Commands->Get_ID(target); + lastSeenTime = Commands->Get_Random_Int(30,60); + if ((state == CONTROL_POINT_ATTACK && !allowCaptureAttackDistract)) + Attack_Move(obj,target,lastWanderPoint->position,captureSpeed,captureDistance,true,true); + else + { + state = ATTACKING_TARGET; + Attack_Move(obj,target,Vector3(),attackSpeed,attackArriveDistance,true,false); + } +} +void JMG_Utility_Objective_System_Set_Infantry_Attach_Bone::Created(GameObject *obj) +{ + Commands->Start_Timer(obj,this,1.0f,1); +} +void JMG_Utility_Objective_System_Set_Infantry_Attach_Bone::Timer_Expired(GameObject *obj,int number) +{ + if (1 == number) + { + sprintf(BasicObjectiveSystem.infantryAttachBone,"%s",Get_Parameter("InfantryAttachBone")); + } +} +void JMG_Utility_Security_System_Fixed_NumberPad_Control::Created(GameObject *obj) +{ + char presetName[220],boneName[220],params[225]; + const char *realPresetName = Commands->Get_Preset_Name(obj); + for (int x = 0;x < 10;x++) + { + sprintf(presetName,"%s_Key_%0d",realPresetName,x); + sprintf(boneName,"Number%0d",x); + GameObject *button = Commands->Create_Object(presetName,Commands->Get_Position(obj)); + Commands->Attach_To_Object_Bone(button,obj,boneName); + sprintf(params,"%d,4356341,%0d,null",Commands->Get_ID(obj),x); + Commands->Attach_Script(button,"JMG_Utility_Custom_Combination_Lock_Key",params); + buttonIds[x] = Commands->Get_ID(button); + } + sprintf(presetName,"%s_Key_Clear",realPresetName); + GameObject *button = Commands->Create_Object(presetName,Commands->Get_Position(obj)); + Commands->Attach_To_Object_Bone(button,obj,"Clear"); + sprintf(params,"%d,4356340,0,null",Commands->Get_ID(obj)); + Commands->Attach_Script(button,"JMG_Utility_Custom_Combination_Lock_Key",params); + buttonIds[10] = Commands->Get_ID(button); + + sprintf(presetName,"%s_Key_Enter",realPresetName); + button = Commands->Create_Object(presetName,Commands->Get_Position(obj)); + Commands->Attach_To_Object_Bone(button,obj,"Enter"); + sprintf(params,"%d,4356342,0,null",Commands->Get_ID(obj)); + Commands->Attach_Script(button,"JMG_Utility_Custom_Combination_Lock_Key",params); + buttonIds[11] = Commands->Get_ID(button); + + sprintf(params,"%d,%s,4356341,4356342,4356340,%d,%d,%d,%d,%d,%d,%d,%d,%d,%s",Commands->Get_ID(obj),Get_Parameter("KeyCode"),Get_Int_Parameter("Success_Custom"),Get_Int_Parameter("Partial_Failure_Custom"),Get_Int_Parameter("Failure_Custom"),Get_Int_Parameter("Disable_On_Success"),Get_Int_Parameter("Disable_On_Failure"),Get_Int_Parameter("Starts_Enabled"),Get_Int_Parameter("Enable_Custom"),Get_Int_Parameter("Failure_Safty"),Get_Int_Parameter("Max_Failures"),Get_Parameter("Sound_Name_Base")); + Commands->Attach_Script(obj,"JMG_Utility_Custom_Combination_Lock",params); +} +void JMG_Utility_Security_System_Fixed_NumberPad_Control::Destroyed(GameObject *obj) +{ + for (int x = 0;x < 12;x++) + { + GameObject *button = Commands->Find_Object(buttonIds[x]); + if (button) + Commands->Destroy_Object(button); + } +} +void JMG_Utility_Custom_Teleport_Sender_Wanderpoint::Created(GameObject *obj) +{ + customMsg = Get_Int_Parameter("Custom"); + retryOnFailure = Get_Int_Parameter("RetryOnFailure") ? true : false; + safeTeleportDistance = Get_Float_Parameter("SafeTeleportDistance"); + wanderPointGroup = Get_Int_Parameter("WanderingAIGroupID"); +} +void JMG_Utility_Custom_Teleport_Sender_Wanderpoint::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == customMsg) + { + Grab_Teleport_Spot(sender,5); + } +} +bool JMG_Utility_Custom_Teleport_Sender_Wanderpoint::Grab_Teleport_Spot(GameObject *enter,int attempts) +{ + if (The_Game()->Get_Game_Duration_S() < 1.0f) + { + char params[220]; + sprintf(params,"%d,%.2f",wanderPointGroup,safeTeleportDistance); + Commands->Attach_Script(enter,"JMG_Utility_Zone_Teleport_To_Random_Wander_Point_Attach",params); + return false; + } + if (!attempts) + { + if (retryOnFailure) + { + char params[220]; + sprintf(params,"%d,%.2f",wanderPointGroup,safeTeleportDistance); + Commands->Attach_Script(enter,"JMG_Utility_Zone_Teleport_To_Random_Wander_Point_Attach",params); + } + return false; + } + attempts--; + Vector3 targetPos; + float facing; + if (!Get_A_Defense_Point(&targetPos,&facing)) + { + char errormsg[220]; + sprintf(errormsg,"msg JMG_Utility_Zone_Teleport_To_Random_Wander_Point ERROR: No wander points found for group %d!",wanderPointGroup); + Console_Input(errormsg); + return false; + } + MoveablePhysClass *mphys = enter->As_PhysicalGameObj() ? enter->As_PhysicalGameObj()->Peek_Physical_Object()->As_MoveablePhysClass() : NULL; + if (mphys && mphys->Find_Teleport_Location(targetPos,safeTeleportDistance,&targetPos)) + { + Toggle_Fly_Mode(enter); + Commands->Set_Position(enter,targetPos); + Force_Position_Update(enter); + Toggle_Fly_Mode(enter); + char params[220]; + sprintf(params,"0.1,%.2f",facing); + Commands->Attach_Script(enter,"JMG_Utility_Delay_Then_Rotate_Camera",params); + return true; + } + else + { + Commands->Set_Position(enter,targetPos); + return Grab_Teleport_Spot(enter,attempts); + } +} +void JMG_Utility_Create_Set_Random_Model::Created(GameObject *obj) +{ + char model[32]; + sprintf(model,"%s%0d",Get_Parameter("BaseName"),Commands->Get_Random_Int(0,Get_Int_Parameter("FinalModelNumber")+1)); + Commands->Set_Model(obj,model); +} +void JMG_Utility_Custom_Destroy_Sender::Created(GameObject *obj) +{ + custom = Get_Int_Parameter("Custom"); +} +void JMG_Utility_Custom_Destroy_Sender::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == custom) + { + Commands->Destroy_Object(sender); + } +} +void JMG_Utility_Custom_Grant_Weapon_Destroy_Sender::Created(GameObject *obj) +{ + custom = Get_Int_Parameter("Custom"); + sprintf(weapon,"%s",Get_Parameter("Weapon")); + sprintf(fullAmmoString,"%s",Get_Parameter("FullAmmoString")); +} +void JMG_Utility_Custom_Grant_Weapon_Destroy_Sender::Custom(GameObject *obj,int message,int cparam,GameObject *sender) +{ + if (message == custom) + { + if (Has_Weapon(obj,weapon) && Get_Max_Total_Bullets(obj,weapon) == Get_Total_Bullets(obj,weapon)) + { + Set_HUD_Help_Text_Player(obj,Get_String_ID_By_Desc(fullAmmoString),Vector3(0.0f,1.0f,0.0f)); + return; + } + Grant_Weapon(obj,weapon,true,1,true); + Commands->Destroy_Object(sender); + } +} +void JMG_Utility_Remove_Script_While_Has_Weapon::Created(GameObject *obj) +{ + sprintf(weaponName,"%s",Get_Parameter("WeaponName")); + rate = Get_Float_Parameter("Rate"); + sprintf(script,"%s",Get_Parameter("Script")); + const char *parameters = Get_Parameter("Params"); + params = newstr(parameters); + char delim = Get_Parameter("Delim")[0]; + unsigned int length = strlen(parameters); + for (unsigned int x = 0;x < length;x++) + if (params[x] == delim) + params[x] = ','; + Commands->Start_Timer(obj,this,rate,1); +} +void JMG_Utility_Remove_Script_While_Has_Weapon::Timer_Expired(GameObject *obj,int number) +{ + if (number == 1) + { + if (!Has_Weapon(obj,weaponName)) + Attach_Script_Once(obj,script,params); + else if (Is_Script_Attached(obj,script)) + Remove_Script(obj,script); + Commands->Start_Timer(obj,this,rate,1); + } +} +void JMG_Utility_Swimming_Infantry_AI::Created(GameObject *obj) +{ + if (Commands->Is_A_Star(obj)) + { + Destroy_Script(); + return; + } + lastWaterZoneId = 0; + heartBeatSoundId = 0; + pantSoundId = 0; + underwater = false; + waterZoneCount = 0; + startedFadeRed = false; + drownTime = 0.0f; + defaultSpeed = obj->As_SoldierGameObj()->Get_Max_Speed(); + waterDamageDelayTime = remainingWaterDamageDelay = Get_Int_Parameter("WaterDamageDelayTime"); + waterDamageDelayTimeRecover = Get_Int_Parameter("WaterDamageDelayTimeRecover"); + sprintf(originalSkin,""); + sprintf(originalArmor,""); + sprintf(originalModel,""); + for (SLNode* node = GameObjManager::ScriptZoneGameObjList.Head();node;node = node->Next()) + { + GameObject *zone = (GameObject *)node->Data(); + if (Is_Script_Attached(zone,"JMG_Utility_Swimming_Zone") && IsInsideZone(zone,obj)) + Commands->Send_Custom_Event(obj,obj,347341,347341,0.25f); + } + Commands->Start_Timer(obj,this,0.1f,1); +} +void JMG_Utility_Swimming_Infantry_AI::Timer_Expired(GameObject *obj,int number) +{ + if (number == 1 && Commands->Get_Health(obj)) + { + if (obj->As_SoldierGameObj()->Is_Crouched() && !obj->As_SoldierGameObj()->Is_Airborne() && waterZoneCount) + { + if (!underwater) + { + underwater = true; + isUnderwater = true; + HideSoundEmitter(&pantSoundId); + } + drownTime += 0.1f; + if (!startedFadeRed && drownTime >= Get_Float_Parameter("DrownTime")-Get_Float_Parameter("StartDrownSequence")) + { + startedFadeRed = true; + CreateSoundEmitter(obj,Get_Parameter("HeartBeatSoundEmitterModel"),&heartBeatSoundId); + } + if (drownTime >= Get_Float_Parameter("DrownTime")) + Commands->Apply_Damage(obj,Get_Float_Parameter("DrownDamageRate"),"None",obj); + } + else + { + if (underwater) + { + underwater = false; + isUnderwater = false; + HideSoundEmitter(&heartBeatSoundId); + if (drownTime > 1.0f) + CreateSoundEmitter(obj,Get_Parameter("PantingSoundEmitterModel"),&pantSoundId); + if (drownTime > Get_Float_Parameter("DrownTime")) + Commands->Create_3D_Sound_At_Bone(Get_Parameter("GaspForBreath"),obj,"c head"); + } + if (drownTime) + { + drownTime -= Get_Float_Parameter("CatchBreathRate"); + if (startedFadeRed && drownTime < Get_Float_Parameter("DrownTime")-Get_Float_Parameter("StartDrownSequence")) + startedFadeRed = false; + if (drownTime <= 0) + { + drownTime = 0.0f; + HideSoundEmitter(&pantSoundId); + } + } + } + if (waterZoneCount) + { + const char *weap = Get_Current_Weapon(obj); + if (Has_Weapon(obj,Get_Parameter("WeaponPreset")) && (!weap || _stricmp(weap,Get_Parameter("WeaponPreset")))) + Commands->Select_Weapon(obj,Get_Parameter("WeaponPreset")); + if (remainingWaterDamageDelay) + remainingWaterDamageDelay--; + if (!remainingWaterDamageDelay && Get_Float_Parameter("WaterDamageAmount")) + Commands->Apply_Damage(obj,Get_Float_Parameter("WaterDamageAmount"),Get_Parameter("WaterDamageWarhead"),Commands->Find_Object(lastWaterZoneId)); + } + else if (!waterZoneCount && remainingWaterDamageDelay < waterDamageDelayTime) + { + remainingWaterDamageDelay += waterDamageDelayTimeRecover; + if (remainingWaterDamageDelay > waterDamageDelayTime) + remainingWaterDamageDelay = waterDamageDelayTime; + } + Commands->Start_Timer(obj,this,0.1f,1); + } +} +void JMG_Utility_Swimming_Infantry_AI::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == 347340) + { + Commands->Send_Custom_Event(sender,obj,347342,param,0.25f); + } + if (message == 347342) + { + if (obj->As_SoldierGameObj()->Is_Airborne()) + { + Commands->Send_Custom_Event(sender,obj,message,param,0.25f); + return; + } + waterZoneCount--; + if (!waterZoneCount) + { + obj->As_SoldierGameObj()->Set_Can_Play_Damage_Animations(true); + obj->As_SoldierGameObj()->Set_Movement_Loiters_Allowed(true); + if (Has_Weapon(obj,enterWeapon)) + Commands->Select_Weapon(obj,enterWeapon); + if (Has_Weapon(obj,Get_Parameter("WeaponPreset"))) + Remove_Weapon(obj,Get_Parameter("WeaponPreset")); + obj->As_SoldierGameObj()->Set_Max_Speed(defaultSpeed); + if (_stricmp(Get_Parameter("SwimmingSkin"),"")) + Set_Skin(obj,originalSkin); + if (_stricmp(Get_Parameter("SwimmingArmor"),"")) + Commands->Set_Shield_Type(obj,originalArmor); + if (_stricmp(Get_Parameter("SwimmingModel"),"")) + Commands->Set_Model(obj,originalModel); + } + } + if (message == 347341) + { + lastWaterZoneId = Commands->Get_ID(sender); + if (!waterZoneCount) + { + sprintf(enterWeapon,"%s",Get_Current_Weapon(obj) ? Get_Current_Weapon(obj) : ""); + Grant_Weapon(obj,Get_Parameter("WeaponPreset"),true,-1,1); + obj->As_SoldierGameObj()->Set_Can_Play_Damage_Animations(false); + obj->As_SoldierGameObj()->Set_Movement_Loiters_Allowed(false); + Commands->Set_Loiters_Allowed(obj,false); + if (_stricmp(Get_Parameter("SwimmingSkin"),"")) + { + sprintf(originalSkin,"%s",Get_Skin(obj)); + Set_Skin(obj,Get_Parameter("SwimmingSkin")); + } + if (_stricmp(Get_Parameter("SwimmingArmor"),"")) + { + sprintf(originalArmor,"%s",Get_Shield_Type(obj)); + Commands->Set_Shield_Type(obj,Get_Parameter("SwimmingArmor")); + } + if (_stricmp(Get_Parameter("SwimmingModel"),"")) + { + sprintf(originalModel,"%s",Get_Model(obj)); + Commands->Set_Model(obj,Get_Parameter("SwimmingModel")); + } + } + waterZoneCount++; + } +} +void JMG_Utility_Swimming_Infantry_AI::Killed(GameObject *obj,GameObject *killer) +{ + DestroySoundEmitter(&heartBeatSoundId); + DestroySoundEmitter(&pantSoundId); +} +void JMG_Utility_Swimming_Infantry_AI::Destroyed(GameObject *obj) +{ +} +void JMG_Utility_Swimming_Infantry_AI::Detach(GameObject *obj) +{ + if (Exe == 4) + return; + Destroyed(obj); +} +void JMG_Utility_Swimming_Infantry_AI::CreateSoundEmitter(GameObject *obj,const char *model,int *soundId) +{ + GameObject *soundEmitter = Commands->Find_Object(*soundId); + if (!soundEmitter) + { + soundEmitter = Commands->Create_Object("Daves Arrow",Commands->Get_Position(obj)); + *soundId = Commands->Get_ID(soundEmitter); + Commands->Attach_To_Object_Bone(soundEmitter,obj,"c head"); + } + Commands->Set_Model(soundEmitter,model); +} +void JMG_Utility_Swimming_Infantry_AI::HideSoundEmitter(int *soundId) +{ + GameObject *soundEmitter = Commands->Find_Object(*soundId); + if (soundEmitter) + Commands->Set_Model(soundEmitter,"null"); +} +void JMG_Utility_Swimming_Infantry_AI::DestroySoundEmitter(int *soundId) +{ + GameObject *soundEmitter = Commands->Find_Object(*soundId); + if (soundEmitter) + Commands->Destroy_Object(soundEmitter); + *soundId = 0; +} +void JMG_Utility_Damaged_Refund_Damage::Created(GameObject *obj) +{ + warhead = ArmorWarheadManager::Get_Warhead_Type(Get_Parameter("WarheadName")); +} +void JMG_Utility_Damaged_Refund_Damage::Damaged(GameObject *obj,GameObject *damager,float damage) +{ + if (Get_Damage_Warhead() != warhead) + return; + float healthDiff = Commands->Get_Max_Health(obj)-Commands->Get_Health(obj); + Commands->Set_Health(obj,JmgUtility::MathClamp(Commands->Get_Health(obj)+damage,0.0f,Commands->Get_Max_Health(obj))); + damage -= healthDiff; + if (damage > 0) + Commands->Set_Shield_Strength(obj,JmgUtility::MathClamp(Commands->Get_Shield_Strength(obj)+damage,0.0f,Commands->Get_Max_Shield_Strength(obj))); +} +bool JMG_Utility_Turret_Spawn_Global_Flag_Controller::AllowAttach = true; +void JMG_Utility_Turret_Spawn_Global_Flag_Controller::Created(GameObject *obj) +{ + custom = Get_Int_Parameter("Custom"); +} +void JMG_Utility_Turret_Spawn_Global_Flag_Controller::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == custom) + { + AllowAttach = param ? true : false; + } +} +void JMG_Utility_Turret_Spawn_Global_Flag::Created(GameObject *obj) +{ + if (!JMG_Utility_Turret_Spawn_Global_Flag_Controller::AllowAttach) + { + Destroy_Script(); + return; + } + GameObject *turret = Commands->Create_Object(Get_Parameter("Turret_Preset"),Vector3()); + Commands->Attach_To_Object_Bone(turret,obj,Get_Parameter("Bone_Name")); + turretId = Commands->Get_ID(turret); + if (turret->As_VehicleGameObj()) + turret->As_VehicleGameObj()->Set_Is_Scripts_Visible(false); + hasDriver = false; +} +void JMG_Utility_Turret_Spawn_Global_Flag::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == CUSTOM_EVENT_VEHICLE_ENTERED) + { + if (!hasDriver) + { + hasDriver = true; + GameObject *turret = Commands->Find_Object(turretId); + if (turret) + { + Commands->Set_Player_Type(turret,Commands->Get_Player_Type(sender)); + Commands->Action_Reset(turret,100); + } + } + } + if (message == CUSTOM_EVENT_VEHICLE_EXITED) + { + if (hasDriver && obj->As_VehicleGameObj() && !Get_Vehicle_Occupant_Count(obj)) + { + hasDriver = false; + GameObject *turret = Commands->Find_Object(turretId); + if (turret) + { + Commands->Set_Player_Type(turret,Commands->Get_Player_Type(obj)); + Commands->Action_Reset(turret,100); + } + } + } +} +void JMG_Utility_Turret_Spawn_Global_Flag::Killed(GameObject *obj,GameObject *killer) +{ + Destroyed(obj); +} +void JMG_Utility_Turret_Spawn_Global_Flag::Destroyed(GameObject *obj) +{ + GameObject *turret = Commands->Find_Object(turretId); + if (turret) + Commands->Destroy_Object(turret); +} +void JMG_Utility_Turret_Spawn_Global_Flag::Detach(GameObject *obj) +{ + if (Exe == 4) + return; + Destroyed(obj); +} +void JMG_Utility_Timer_Death_If_Not_Damaged_Over_Peroid::Created(GameObject *obj) +{ + time = originalTime = Get_Int_Parameter("Time"); + damage = Get_Float_Parameter("Damage"); + sprintf(warhead,"%s",Get_Parameter("Warhead")); + Timer_Expired(obj,1); +} +void JMG_Utility_Timer_Death_If_Not_Damaged_Over_Peroid::Timer_Expired(GameObject *obj,int number) +{ + if (number == 1) + { + if (time) + { + time--; + if (!time) + Commands->Apply_Damage(obj,damage,warhead,obj); + } + Commands->Start_Timer(obj,this,1.0f,1); + } +} +void JMG_Utility_Timer_Death_If_Not_Damaged_Over_Peroid::Damaged(GameObject *obj,GameObject *damager,float damage) +{ + if (damage <= 0.0f) + return; + time = originalTime; +} +int JMG_Utility_Global_Attach_Script_On_Flag_Controller::globalFlag = -1; +void JMG_Utility_Global_Attach_Script_On_Flag_Controller::Created(GameObject *obj) +{ + globalFlag = Get_Int_Parameter("GlobalFlag"); +} +void JMG_Utility_Global_Attach_Script_On_Flag_Custom::Created(GameObject *obj) +{ + custom = Get_Int_Parameter("Custom"); + globalFlag = Get_Int_Parameter("GlobalFlag"); +} +void JMG_Utility_Global_Attach_Script_On_Flag_Custom::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == custom) + { + JMG_Utility_Global_Attach_Script_On_Flag_Controller::globalFlag = globalFlag; + } +} +void JMG_Utility_Global_Attach_Script_On_Flag::Created(GameObject *obj) +{ + if (JMG_Utility_Global_Attach_Script_On_Flag_Controller::globalFlag != Get_Int_Parameter("GlobalFlag")) + { + Destroy_Script(); + return; + } + char script[128]; + sprintf(script,"%s",Get_Parameter("Script")); + const char *parameters = Get_Parameter("Params"); + char *params = newstr(parameters); + char delim = Get_Parameter("Delim")[0]; + unsigned int length = strlen(parameters); + for (unsigned int x = 0;x < length;x++) + if (params[x] == delim) + params[x] = ','; + Attach_Script_Once(obj,script,params); +} +void JMG_Utility_Custom_Attach_Script_To_Presets::Created(GameObject *obj) +{ + sprintf(preset,"%s",Get_Parameter("Preset")); + sprintf(script,"%s",Get_Parameter("Script")); + const char *parameters = Get_Parameter("Params"); + params = newstr(parameters); + char delim = Get_Parameter("Delim")[0]; + unsigned int length = strlen(parameters); + for (unsigned int x = 0;x < length;x++) + if (params[x] == delim) + params[x] = ','; + custom = Get_Int_Parameter("Custom"); +} +void JMG_Utility_Custom_Attach_Script_To_Presets::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == custom) + { + for (SLNode *current = GameObjManager::GameObjList.Head();current;current = current->Next()) + { + GameObject *target = current->Data()->As_ScriptableGameObj(); + if (target && target->As_ScriptableGameObj() && !_stricmp(preset,Commands->Get_Preset_Name(target))) + Commands->Attach_Script(target,script,params); + } + } +} +void JMG_Utility_Custom_Send_Custom_On_Player_Count_Animation::Created(GameObject *obj) +{ + controllerId = Get_Int_Parameter("ControllingID"); + sprintf(animation,"%s",Get_Parameter("Animation")); + maxFrame = Get_Float_Parameter("MaxFrame"); + lastCalculation = -1; + Timer_Expired(obj,1); +} +void JMG_Utility_Custom_Send_Custom_On_Player_Count_Animation::Timer_Expired(GameObject *obj,int number) +{ + if (number == 1 && controllerId) + { + GameObject *controller = Commands->Find_Object(controllerId); + if (controller) + { + JMG_Utility_Custom_Send_Custom_On_Player_Count_HUD *script = (JMG_Utility_Custom_Send_Custom_On_Player_Count_HUD*)Find_Script_On_Object(controller,"JMG_Utility_Custom_Send_Custom_On_Player_Count_HUD"); + if (script) + { + int requiredCount = script->baseCount+script->playerCount*Get_Player_Count(); + if (requiredCount) + { + float frame = JmgUtility::MathClamp((float)script->runningCount/(float)requiredCount*maxFrame,0.0f,maxFrame); + if (frame != lastCalculation && requiredCount) + { + Commands->Set_Animation(obj,animation,false,0,frame,frame,true); + lastCalculation = frame; + } + } + } + } + Commands->Start_Timer(obj,this,0.1f,1); + } +} +void JMG_Utility_Created_Destroy_If_Not_In_Pathfind::Created(GameObject *obj) +{ + Vector3 out; + if (!Get_Random_Pathfind_Spot(Commands->Get_Position(obj),0.0f,&out)) + Commands->Destroy_Object(obj); +} +void JMG_Utility_Teleport_If_Not_Moving_To_Location::Created(GameObject *obj) +{ + time = originalTime = (Get_Int_Parameter("Time")*10); + location = Get_Vector3_Parameter("Location"); + lastSpot = Commands->Get_Position(obj); + Commands->Start_Timer(obj,this,0.1f,1); +} +void JMG_Utility_Teleport_If_Not_Moving_To_Location::Timer_Expired(GameObject *obj,int number) +{ + if (number == 1) + { + Vector3 pos = Commands->Get_Position(obj); + if (JmgUtility::SimpleDistance(pos,lastSpot) <= 0.0f) + { + time--; + if (!time) + { + time = originalTime; + MoveablePhysClass *mphys = obj->As_PhysicalGameObj() ? obj->As_PhysicalGameObj()->Peek_Physical_Object()->As_MoveablePhysClass() : NULL; + if (mphys && mphys->Find_Teleport_Location(location,0.75f,&pos)) + Commands->Set_Position(obj,pos); + } + } + else + { + time = originalTime; + lastSpot = pos; + } + Commands->Start_Timer(obj,this,0.1f,1); + } +} +char JMG_Utility_Global_Set_Random_Model_Controller::extension[16]; +void JMG_Utility_Global_Set_Random_Model_Controller::Created(GameObject *obj) +{ + sprintf(JMG_Utility_Global_Set_Random_Model_Controller::extension,Get_Parameter("DefaultExtension")); +} +void JMG_Utility_Global_Set_Random_Model_Custom::Created(GameObject *obj) +{ + custom = Get_Int_Parameter("Custom"); + sprintf(extension,"%s",Get_Parameter("Extension")); +} +void JMG_Utility_Global_Set_Random_Model_Custom::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == custom) + { + sprintf(JMG_Utility_Global_Set_Random_Model_Controller::extension,extension); + for (SLNode *current = GameObjManager::GameObjList.Head();current;current = current->Next()) + { + GameObject *target = current->Data()->As_ScriptableGameObj(); + if (!target || !target->As_ScriptableGameObj()) + continue; + JMG_Utility_Global_Set_Random_Model *script = (JMG_Utility_Global_Set_Random_Model*)Find_Script_On_Object(target,"JMG_Utility_Global_Set_Random_Model"); + if (!script) + continue; + script->UpdateModel(target); + } + } +} +void JMG_Utility_Global_Set_Random_Model::Created(GameObject *obj) +{ + sprintf(baseName,"%s",Get_Parameter("BaseName")); + randomValue = Commands->Get_Random_Int(0,Get_Int_Parameter("FinalModelNumber")+1); + UpdateModel(obj); +} +void JMG_Utility_Global_Set_Random_Model::UpdateModel(GameObject *obj) +{ + char model[32]; + sprintf(model,"%s%0d%s",baseName,randomValue,JMG_Utility_Global_Set_Random_Model_Controller::extension); + Commands->Set_Model(obj,model); +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::Created(GameObject *obj) +{ + if (Commands->Is_A_Star(obj)) + { + Destroy_Script(); + return; + } + lastWaterZoneId = 0; + heartBeatSoundId = 0; + pantSoundId = 0; + underwater = false; + waterZoneCount = 0; + startedFadeRed = false; + drownTime = 0.0f; + defaultSpeed = obj->As_SoldierGameObj()->Get_Max_Speed(); + defaultHoldStyle = Get_Int_Parameter("DefaultHoldStyle"); + defaultSwimSpeedMultiplier = Get_Float_Parameter("DefaultSwimSpeed"); + waterDamageDelayTime = remainingWaterDamageDelay = Get_Int_Parameter("WaterDamageDelayTime"); + waterDamageDelayTimeRecover = Get_Int_Parameter("WaterDamageDelayTimeRecover"); + sprintf(originalSkin,""); + sprintf(originalArmor,""); + sprintf(originalModel,""); + waterSpeedMultiplier = 1.0f; + sprintf(defaultWeaponPreset,"%s",Get_Parameter("WeaponPreset")); + defaultDrownTime = Get_Float_Parameter("DrownTime"); + startDrownSequence = Get_Float_Parameter("StartDrownSequence"); + waterDamageAmount = Get_Float_Parameter("WaterDamageAmount"); + sprintf(waterDamageWarhead,"%s",Get_Parameter("WaterDamageWarhead")); + sprintf(swimmingSkin,"%s",Get_Parameter("SwimmingSkin")); + sprintf(swimmingArmor,"%s",Get_Parameter("SwimmingArmor")); + sprintf(swimmingModel,"%s",Get_Parameter("SwimmingModel")); + sprintf(heartBeatSoundEmitterModel,"%s",Get_Parameter("HeartBeatSoundEmitterModel")); + sprintf(pantingSoundEmitterModel,"%s",Get_Parameter("PantingSoundEmitterModel")); + sprintf(gaspForBreath,"%s",Get_Parameter("GaspForBreath")); + catchBreathRate = Get_Float_Parameter("CatchBreathRate"); + forceDefinedWeapons = Get_Int_Parameter("ForceDefinedWeapons") ? true : false; + weaponGroupId = Get_Int_Parameter("WeaponsGroupID"); + swimmingHeightScale = Get_Float_Parameter("SwimmingHeightScale"); + swimmingWidthScale = Get_Float_Parameter("SwimmingWidthScale"); + originalHeightScale = 0.0f; + originalWidthScale = 0.0f; + drownDamageRate = drownDamageRate; + weaponSwitchForward = true; + for (SLNode *node = GameObjManager::ScriptZoneGameObjList.Head();node;node = node->Next()) + { + GameObject *zone = (GameObject *)node->Data(); + if (Is_Script_Attached(zone,"JMG_Utility_Swimming_Zone") && IsInsideZone(zone,obj)) + Commands->Send_Custom_Event(obj,obj,347341,347341,0.25f); + } + Commands->Start_Timer(obj,this,0.1f,1); +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::Timer_Expired(GameObject *obj,int number) +{ + if (number == 1 && Commands->Get_Health(obj) && !The_Game()->Is_Game_Over()) + { + if (obj->As_SoldierGameObj()->Is_Crouched() && !obj->As_SoldierGameObj()->Is_Airborne() && waterZoneCount) + { + if (!underwater) + { + underwater = true; + HideSoundEmitter(&pantSoundId); + } + drownTime += 0.1f; + if (!startedFadeRed && drownTime >= defaultDrownTime-startDrownSequence) + { + startedFadeRed = true; + CreateSoundEmitter(obj,heartBeatSoundEmitterModel,&heartBeatSoundId); + } + if (drownTime >= defaultDrownTime) + Commands->Apply_Damage(obj,drownDamageRate,"None",obj); + } + else + { + if (underwater) + { + underwater = false; + HideSoundEmitter(&heartBeatSoundId); + if (drownTime > 1.0f) + CreateSoundEmitter(obj,pantingSoundEmitterModel,&pantSoundId); + if (drownTime > defaultDrownTime) + Commands->Create_3D_Sound_At_Bone(gaspForBreath,obj,"c head"); + } + if (drownTime) + { + drownTime -= catchBreathRate; + if (startedFadeRed && drownTime < defaultDrownTime-startDrownSequence) + startedFadeRed = false; + if (drownTime <= 0) + { + drownTime = 0.0f; + HideSoundEmitter(&pantSoundId); + } + } + } + if (waterZoneCount) + { + SwitchWeapon(obj); + if (remainingWaterDamageDelay) + remainingWaterDamageDelay--; + if (!remainingWaterDamageDelay && waterDamageAmount) + Commands->Apply_Damage(obj,waterDamageAmount,waterDamageWarhead,Commands->Find_Object(lastWaterZoneId)); + } + else if (!waterZoneCount && remainingWaterDamageDelay < waterDamageDelayTime) + { + remainingWaterDamageDelay += waterDamageDelayTimeRecover; + if (remainingWaterDamageDelay > waterDamageDelayTime) + remainingWaterDamageDelay = waterDamageDelayTime; + } + Commands->Start_Timer(obj,this,0.1f,1); + } +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::Custom(GameObject *obj,int message,int param,GameObject *sender) +{ + if (message == 347340) + { + Commands->Send_Custom_Event(sender,obj,347342,param,0.25f); + } + if (message == 347342) + { + if (obj->As_SoldierGameObj()->Is_Airborne()) + { + Commands->Send_Custom_Event(sender,obj,message,param,0.25f); + return; + } + waterZoneCount--; + if (!waterZoneCount) + { + obj->As_SoldierGameObj()->Set_Can_Play_Damage_Animations(true); + obj->As_SoldierGameObj()->Set_Movement_Loiters_Allowed(true); + obj->As_SoldierGameObj()->Set_Human_Anim_Override(true); + obj->As_SoldierGameObj()->Set_Override_Weapon_Hold_Style(-1); + if (forceDefinedWeapons && Has_Weapon(obj,enterWeapon)) + Commands->Select_Weapon(obj,enterWeapon); + if (Has_Weapon(obj,defaultWeaponPreset)) + Remove_Weapon(obj,defaultWeaponPreset); + obj->As_SoldierGameObj()->Set_Max_Speed(defaultSpeed); + if (_stricmp(swimmingSkin,"")) + Set_Skin(obj,originalSkin); + if (_stricmp(swimmingArmor,"")) + Commands->Set_Shield_Type(obj,originalArmor); + if (_stricmp(swimmingModel,"")) + Commands->Set_Model(obj,originalModel); + if (abs(swimmingHeightScale-999.99f) > 0.1f) + obj->As_SoldierGameObj()->Set_Skeleton_Height(originalHeightScale); + if (abs(swimmingWidthScale-999.99f) > 0.1f) + obj->As_SoldierGameObj()->Set_Skeleton_Width(originalWidthScale); + } + } + if (message == 347341) + { + lastWaterZoneId = Commands->Get_ID(sender); + if (!waterZoneCount) + { + if (abs(swimmingHeightScale-999.99f) > 0.1f) + { + originalHeightScale = obj->As_SoldierGameObj()->Get_Skeleton_Heigth(); + obj->As_SoldierGameObj()->Set_Skeleton_Height(swimmingHeightScale); + } + if (abs(swimmingWidthScale-999.99f) > 0.1f) + { + originalWidthScale = obj->As_SoldierGameObj()->Get_Skeleton_Width(); + obj->As_SoldierGameObj()->Set_Skeleton_Width(swimmingWidthScale); + } + defaultSpeed = obj->As_SoldierGameObj()->Get_Max_Speed(); + waterSpeedMultiplier = (param/100.0f); + sprintf(enterWeapon,"%s",Get_Current_Weapon(obj) ? Get_Current_Weapon(obj) : ""); + if (forceDefinedWeapons) + Grant_Weapon(obj,defaultWeaponPreset,true,-1,1); + obj->As_SoldierGameObj()->Set_Can_Play_Damage_Animations(false); + obj->As_SoldierGameObj()->Set_Movement_Loiters_Allowed(false); + obj->As_SoldierGameObj()->Set_Human_Anim_Override(false); + currentWeaponId = 1; + SwitchWeapon(obj); + Commands->Set_Loiters_Allowed(obj,false); + if (_stricmp(swimmingSkin,"")) + { + sprintf(originalSkin,"%s",Get_Skin(obj)); + Set_Skin(obj,swimmingSkin); + } + if (_stricmp(swimmingArmor,"")) + { + sprintf(originalArmor,"%s",Get_Shield_Type(obj)); + Commands->Set_Shield_Type(obj,swimmingArmor); + } + if (_stricmp(swimmingModel,"")) + { + sprintf(originalModel,"%s",Get_Model(obj)); + Commands->Set_Model(obj,swimmingModel); + } + } + waterZoneCount++; + } +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::Killed(GameObject *obj,GameObject *killer) +{ + DestroySoundEmitter(&heartBeatSoundId); + DestroySoundEmitter(&pantSoundId); +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::Destroyed(GameObject *obj) +{ + DestroySoundEmitter(&heartBeatSoundId); + DestroySoundEmitter(&pantSoundId); +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::Detach(GameObject *obj) +{ + if (Exe == 4) + return; + Destroyed(obj); +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::CreateSoundEmitter(GameObject *obj,const char *model,int *soundId) +{ + GameObject *soundEmitter = Commands->Find_Object(*soundId); + if (!soundEmitter) + { + soundEmitter = Commands->Create_Object("Daves Arrow",Commands->Get_Position(obj)); + *soundId = Commands->Get_ID(soundEmitter); + Commands->Attach_To_Object_Bone(soundEmitter,obj,"c head"); + } + Commands->Set_Model(soundEmitter,model); +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::HideSoundEmitter(int *soundId) +{ + GameObject *soundEmitter = Commands->Find_Object(*soundId); + if (soundEmitter) + Commands->Set_Model(soundEmitter,"null"); +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::DestroySoundEmitter(int *soundId) +{ + GameObject *soundEmitter = Commands->Find_Object(*soundId); + if (soundEmitter) + Commands->Destroy_Object(soundEmitter); + *soundId = 0; +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::SwitchWeapon(GameObject *obj) +{ + const WeaponDefinitionClass *weaponDef = Get_Current_Weapon_Definition(obj); + if ((weaponDef && currentWeaponId != weaponDef->Get_ID()) || (!weaponDef && currentWeaponId != 1)) + { + int currentPosition = GetWeaponPosition(obj,currentWeaponId); + GetWeaponId(weaponDef); + if (forceDefinedWeapons) + { + int newPosition = GetWeaponPosition(obj,currentWeaponId); + bool forward = newPosition > currentPosition; + int diff = abs(newPosition-currentPosition); + if ((diff <= 1 && forward) || (diff > 1 && weaponSwitchForward)) + currentWeapon = JMG_Utility_Swimming_Infantry_Advanced_Controller::getNext(obj,weaponGroupId,currentWeapon); + else + currentWeapon = JMG_Utility_Swimming_Infantry_Advanced_Controller::getPrev(obj,weaponGroupId,currentWeapon); + weaponSwitchForward = forward; + if (currentWeapon) + { + Commands->Select_Weapon(obj,currentWeapon->weaponName); + GetWeaponId(Get_Current_Weapon_Definition(obj)); + } + } + else + currentWeapon = JMG_Utility_Swimming_Infantry_Advanced_Controller::getWeapon(weaponGroupId,currentWeaponId); + if (currentWeapon) + { + obj->As_SoldierGameObj()->Set_Override_Weapon_Hold_Style(currentWeapon->holdStyle); + obj->As_SoldierGameObj()->Set_Max_Speed(defaultSpeed*currentWeapon->speed*waterSpeedMultiplier); + } + else + { + obj->As_SoldierGameObj()->Set_Override_Weapon_Hold_Style(defaultHoldStyle); + obj->As_SoldierGameObj()->Set_Max_Speed(defaultSpeed*defaultSwimSpeedMultiplier*waterSpeedMultiplier); + } + } +} +void JMG_Utility_Swimming_Infantry_Advanced_AI::GetWeaponId(const WeaponDefinitionClass *weaponDef) +{ + if (weaponDef) + currentWeaponId = weaponDef->Get_ID(); + else + currentWeaponId = 1; +} +int JMG_Utility_Swimming_Infantry_Advanced_AI::GetWeaponPosition(GameObject *obj,int weaponId) +{ + if (ArmedGameObj *armedGameObj = obj->As_PhysicalGameObj()->As_ArmedGameObj()) + { + WeaponBagClass *weaponBagClass = armedGameObj->Get_Weapon_Bag(); + int x = weaponBagClass->Get_Count(); + for (int i = 0;i < x;i++) + if (weaponBagClass->Peek_Weapon(i)) + if (weaponBagClass->Peek_Weapon(i)->Get_ID() == weaponId) + return i; + } + return -1; +} ScriptRegistrant JMG_Utility_Check_If_Script_Is_In_Library_Registrant("JMG_Utility_Check_If_Script_Is_In_Library","ScriptName:string,CppName:string"); ScriptRegistrant JMG_Send_Custom_When_Custom_Sequence_Matched_Registrant("JMG_Send_Custom_When_Custom_Sequence_Matched","Success_Custom=0:int,Correct_Step_Custom=0:int,Partial_Failure_Custom=0:int,Failure_Custom=0:int,Send_To_ID=0:int,Custom_0=0:int,Custom_1=0:int,Custom_2=0:int,Custom_3=0:int,Custom_4=0:int,Custom_5=0:int,Custom_6=0:int,Custom_7=0:int,Custom_8=0:int,Custom_9=0:int,Disable_On_Success=1:int,Disable_On_Failure=0:int,Starts_Enabled=1:int,Enable_Custom=0:int,Correct_Step_Saftey=0:int,Failure_Saftey=1:int,Max_Failures=1:int"); ScriptRegistrant JMG_Utility_Change_Model_On_Timer_Registrant("JMG_Utility_Change_Model_On_Timer","Model=null:string,Time=0:float"); @@ -14731,7 +16223,7 @@ ScriptRegistrant JMG_Utility_Custom_Damage_All_Soldiers_On_Team_Registrant("JMG_Utility_Custom_Damage_All_Soldiers_On_Team","Custom:int,Team:int,Damage:float,Warhead=None:string"); ScriptRegistrant JMG_Utility_AI_Guardian_Vehicle_Registrant("JMG_Utility_AI_Guardian_Vehicle","WanderingAIGroupID:int,WanderSpeed=1.0:float,FireRange=-1.0:float,FaceTarget=1:int,CheckBlocked=1:int,AimAtFeet=1:int,TurnOffEngineOnArrival=1:int,StealthModeOverride=0:int"); ScriptRegistrant JMG_Utility_Custom_Destroy_Closest_Model_To_Position_Registrant("JMG_Utility_Custom_Destroy_Closest_Model_To_Position","Custom:int,Model:string,Position:Vector3,MaxDistance=0.0:float"); -ScriptRegistrant JMG_Utility_Send_Custom_On_Deaths_Controller_Registrant("JMG_Utility_Send_Custom_On_Deaths_Controller","MaxDeaths=15:int,DeathReminder=5:int,UrgentDeathReminder=5:int,StringID=12628:int,ReminderMessage=%d %s^ only %d more %s allowed!:string,Delim=^:string,ReminderMessageOrder=0:int,DeathSingular=casualty:string,DeathPlural=casualties:string,RemainingSingular=death:string,RemainingPlural=deaths:string,ReminderColor=1.0 0.0 0.0:Vector3,ID=0:int,Custom=0:int,Param=0:int,Delay=0:int,ReminderCustom=0:int,AddDeathsWhenNoPlayers=10.0:float,NoPlayersAddDeathSaftyTime=150:int,RequiresADeathToStartNoPlayerAdd=1:int,AnnounceOnFirstDeath=1:int,OnlyTrackPlayerDeaths=1:int"); +ScriptRegistrant JMG_Utility_Send_Custom_On_Deaths_Controller_Registrant("JMG_Utility_Send_Custom_On_Deaths_Controller","MaxDeaths=15:int,DeathReminder=5:int,UrgentDeathReminder=5:int,StringID=12628:int,ReminderMessage=%d %s^ only %d more %s allowed!:string,Delim=^:string,ReminderMessageOrder=0:int,DeathSingular=casualty:string,DeathPlural=casualties:string,RemainingSingular=death:string,RemainingPlural=deaths:string,ReminderColor=1.0 0.0 0.0:Vector3,ID=0:int,Custom=0:int,Param=0:int,Delay=0:int,ReminderCustom=0:int,AddDeathsWhenNoPlayers=10.0:float,NoPlayersAddDeathSaftyTime=150:int,RequiresADeathToStartNoPlayerAdd=1:int,AnnounceOnFirstDeath=1:int,OnlyTrackPlayerDeaths=1:int,RecordAIDeaths=0:int"); ScriptRegistrant JMG_Utility_Send_Custom_On_Deaths_Reporter_Registrant("JMG_Utility_Send_Custom_On_Deaths_Reporter",""); ScriptRegistrant JMG_Utility_AI_Goto_Player_Ignore_Object_Registrant("JMG_Utility_AI_Goto_Player_Ignore_Object",""); ScriptRegistrant JMG_Utility_AI_Goto_Enemy_Ignore_Object_Registrant("JMG_Utility_AI_Goto_Enemy_Ignore_Object",""); @@ -14959,3 +16451,29 @@ ScriptRegistrant JMG_Utility_Global_Health_Custom_Registrant("JMG_Utility_Global_Health_Custom","Custom:int,MaxHealth:float,Health:float,UpdateAllObjects=1:int"); ScriptRegistrant JMG_Utility_Global_Health_Object_Registrant("JMG_Utility_Global_Health_Object",""); ScriptRegistrant JMG_Utility_Custom_Create_Random_Explosions_Registrant("JMG_Utility_Custom_Create_Random_Explosions","Custom:int,ExplosionPreset:string,Count:int,MaxDistance:float,KillerID:int"); +ScriptRegistrant JMG_Utility_Custom_Enable_Incrementally_Spawners_In_Range_Registrant("JMG_Utility_Custom_Enable_Spawners_Incrementally_In_Range","StartID:int,EndID:int,Custom:int,Enable=1:int"); +ScriptRegistrant JMG_Utility_Control_Point_Team_Target_Wander_Point_Registrant("JMG_Utility_Control_Point_Team_Target_Wander_Point","ControlPointID:int,TeamID:int,TargetGroupID:int,NonTargetGroupId:int"); +ScriptRegistrant JMG_Utility_AI_Control_Point_Registrant("JMG_Utility_AI_Control_Point","CaptureCpGroupId:int,CaptureCpChance=0.33:float,CaptureSpeed=1.0:float,CaptureDistance=5.0:float,DefendCpGroupId:int,DefendSpeed=1.0:float,DefendDistance=1.0:float,CloseDefendDistance=25.0:float,ChooseFarDefendChance=0.1:float,AttackSpeed=1.0:float,AttackDistance=-1.0:float,RandomAttackDistance=0.0:float,AttackDistractFromCaptureChance=0.25,ChanceToInvestigateLastSeenLocation=0.66:float,AttackCheckBlocked=1:int,CanSeeStealth=1:int,ShutdownEngineOnArrival=0:int"); +ScriptRegistrant JMG_Utility_Objective_System_Set_Infantry_Attach_Bone_Registrant("JMG_Utility_Objective_System_Set_Infantry_Attach_Bone","InfantryAttachBone:string"); +ScriptRegistrant JMG_Utility_Security_System_Fixed_NumberPad_Control_Registrant("JMG_Utility_Security_System_Fixed_NumberPad_Control","KeyCode:string,ID:int,Success_Custom=0:int,Partial_Failure_Custom=0:int,Failure_Custom=0:int,Disable_On_Success=1:int,Disable_On_Failure=0:int,Starts_Enabled=1:int,Enable_Custom=0:int,Failure_Safty=1:int,Max_Failures=1:int,Sound_Name_Base:string"); +ScriptRegistrant JMG_Utility_Custom_Teleport_Sender_Wanderpoint_Registrant("JMG_Utility_Custom_Teleport_Sender_Wanderpoint","Custom:int,WanderingAIGroupID=-1:int,SafeTeleportDistance=1.5:float,RetryOnFailure=0:int"); +ScriptRegistrant JMG_Utility_Create_Set_Random_Model_Registrant("JMG_Utility_Create_Set_Random_Model","BaseName:string,FinalModelNumber:int"); +ScriptRegistrant JMG_Utility_Custom_Destroy_Sender_Registrant("JMG_Utility_Custom_Destroy_Sender","Custom:int"); +ScriptRegistrant JMG_Utility_Custom_Grant_Weapon_Destroy_Sender_Registrant("JMG_Utility_Custom_Grant_Weapon_Destroy_Sender","Custom:int,Weapon:string,FullAmmoString=IDS_M00_AMMO_FULL:string"); +ScriptRegistrant JMG_Utility_Remove_Script_While_Has_Weapon_Registrant("JMG_Utility_Remove_Script_While_Has_Weapon","WeaponName:string,Script:string,Params:string,Delim=@:string,Rate=0.1:float"); +ScriptRegistrant JMG_Utility_Swimming_Infantry_AI_Registrant("JMG_Utility_Swimming_Infantry_AI","WeaponPreset=Weapon_Swimming_Animations:string,DrownTime=10.0:float,StartDrownSequence=3.0:float,GaspForBreath=SFX.SwimmingGaspForBreath:string,PantingSoundEmitterModel=s_panting:string,HeartBeatSoundEmitterModel=s_heartBeat:string,DrownDamageRate=2.5:float,CatchBreathRate=0.33:float,WaterDamageAmount=0.0:float,WaterDamageWarhead=None:string,WaterDamageDelayTime=0:int,WaterDamageDelayTimeRecover=0:int,SwimmingSkin:string,SwimmingArmor:string,SwimmingModel:string"); +ScriptRegistrant JMG_Utility_Damaged_Refund_Damage_Registrant("JMG_Utility_Damaged_Refund_Damage","WarheadName:string"); +ScriptRegistrant JMG_Utility_Turret_Spawn_Global_Flag_Controller_Registrant("JMG_Utility_Turret_Spawn_Global_Flag_Controller","Custom:int"); +ScriptRegistrant JMG_Utility_Turret_Spawn_Global_Flag_Registrant("JMG_Utility_Turret_Spawn_Global_Flag","Turret_Preset:string,Bone_Name=Tur_Mount:string"); +ScriptRegistrant JMG_Utility_Timer_Death_If_Not_Damaged_Over_Peroid_Registrant("JMG_Utility_Timer_Death_If_Not_Damaged_Over_Peroid","Time:int,Damage:float,Warhead:string"); +ScriptRegistrant JMG_Utility_Global_Attach_Script_On_Flag_Controller_Registrant("JMG_Utility_Global_Attach_Script_On_Flag_Controller","GlobalFlag:int"); +ScriptRegistrant JMG_Utility_Global_Attach_Script_On_Flag_Custom_Registrant("JMG_Utility_Global_Attach_Script_On_Flag_Custom","Custom:int,GlobalFlag:int"); +ScriptRegistrant JMG_Utility_Global_Attach_Script_On_Flag_Registrant("JMG_Utility_Global_Attach_Script_On_Flag","GlobalFlag:int,Script:string,Params:string,Delim=@:string"); +ScriptRegistrant JMG_Utility_Custom_Attach_Script_To_Presets_Registrant("JMG_Utility_Custom_Attach_Script_To_Presets","Custom:int,Preset:string,Script:string,Params:string,Delim=@:string"); +ScriptRegistrant JMG_Utility_Custom_Send_Custom_On_Player_Count_Animation_Registrant("JMG_Utility_Custom_Send_Custom_On_Player_Count_Animation","ControllingID:int,Animation:string,MaxFrame:float"); +ScriptRegistrant JMG_Utility_Created_Destroy_If_Not_In_Pathfind_Registrant("JMG_Utility_Created_Destroy_If_Not_In_Pathfind",""); +ScriptRegistrant JMG_Utility_Teleport_If_Not_Moving_To_Location_Registrant("JMG_Utility_Teleport_If_Not_Moving_To_Location","Time:int,Location:vector3"); +ScriptRegistrant JMG_Utility_Global_Set_Random_Model_Controller_Registrant("JMG_Utility_Global_Set_Random_Model_Controller","DefaultExtension:string"); +ScriptRegistrant JMG_Utility_Global_Set_Random_Model_Custom_Registrant("JMG_Utility_Global_Set_Random_Model_Custom","Custom:int,Extension:string"); +ScriptRegistrant JMG_Utility_Global_Set_Random_Model_Registrant("JMG_Utility_Global_Set_Random_Model","BaseName:string,FinalModelNumber:int"); +ScriptRegistrant JMG_Utility_Swimming_Infantry_Advanced_AI_Registrant("JMG_Utility_Swimming_Infantry_Advanced_AI","WeaponsGroupID=0:int,WeaponPreset=Weapon_Swimming_Animations:string,ForceDefinedWeapons=0:int,DefaultHoldStyle=4:int,DefaultSwimSpeed=1.0:float,DrownTime=10.0:float,StartDrownSequence=3.0:float,GaspForBreath=SFX.SwimmingGaspForBreath:string,PantingSoundEmitterModel=s_panting:string,HeartBeatSoundEmitterModel=s_heartBeat:string,DrownDamageRate=2.5:float,CatchBreathRate=0.33:float,WaterDamageAmount=0.0:float,WaterDamageDelayTime=0:int,WaterDamageDelayTimeRecover=0:int,WaterDamageWarhead=None:string,SwimmingSkin:string,SwimmingArmor:string,SwimmingModel:string,SwimmingHeightScale=999.99:float,SwimmingWidthScale=999.99:float"); diff -urN sourceold/scripts/jmgUtility.h source/scripts/jmgUtility.h --- sourceold/scripts/jmgUtility.h 2020-09-10 09:08:27.972483200 +1000 +++ source/scripts/jmgUtility.h 2021-07-27 14:44:22.800963900 +1000 @@ -913,6 +913,7 @@ enum Priority{Undefined=-1,Unknown,Primary,Secondary,Tertiary,Quaternary,Quinary,Senary,Septenary,Octonary,Nonary,Denary,Bonus}; enum Status{Removed=-2,NotDefined=-1,Pending,Accomplished,Failed,Hidden}; int controllerId; + char infantryAttachBone[32]; private: struct ObjectiveNode { @@ -1078,6 +1079,7 @@ public: NewObjectiveSystem(int team,bool showRadarStars = true,const char *primaryObjectiveModel = "null",const char *secondaryObjectiveModel = "null",const char *tertiaryObjectiveModel = "null") { + sprintf(infantryAttachBone,"c pelvis"); this->team = team; objectiveStringIdsLoaded = false; objectiveCounts = 0; @@ -1107,7 +1109,7 @@ GameObject *radarMarker = Create_Radar_Marker(Commands->Get_Position(blipUnit),priority,objectiveId,modelOverride); if (!radarMarker) return false; - Commands->Attach_To_Object_Bone(radarMarker,blipUnit,blipUnit->As_SoldierGameObj() ? "c pelvis" : "origin"); + Commands->Attach_To_Object_Bone(radarMarker,blipUnit,blipUnit->As_SoldierGameObj() ? infantryAttachBone : "origin"); return addObjective(objectiveId,priority,status,nameId,soundFilename,descriptionId,Commands->Get_ID(radarMarker),objectiveNumber); } bool Add_Objective(int objectiveId, Priority priority, Status status, unsigned long nameId, char *soundFilename, unsigned long descriptionId,Vector3 blipPosition,const char *modelOverride = NULL,int objectiveNumber = 0) @@ -1149,7 +1151,7 @@ GameObject *radarMarker = Create_Radar_Marker(Commands->Get_Position(blipUnit),current->priority,objectiveId,modelOverride); if (!radarMarker) return; - Commands->Attach_To_Object_Bone(radarMarker,blipUnit,blipUnit->As_SoldierGameObj() ? "c pelvis" : "origin"); + Commands->Attach_To_Object_Bone(radarMarker,blipUnit,blipUnit->As_SoldierGameObj() ? infantryAttachBone : "origin"); current->radarMarkerId = Commands->Get_ID(radarMarker); return; } @@ -2592,6 +2594,7 @@ /*! * \brief Used to trigger the simple swimming animation system this is to be used along side JMG_Utility_Swimming_Infantry, * \ any soldiers that enter the script zone without JMG_Utility_Swimming_Infantry attached will die. +* \ JMG_Utility_Swimming_Infantry_AI is now automatically attached to AI infantry 7.1 * \WaterColor[R|G|B] - Color to fade the screen while in this swimming zone * \WaterOpacity - How transparent the screen is while in this zone * \WaterMinViewDistance - The min distance that the fog shows up when underwater @@ -4794,6 +4797,7 @@ static bool controllerPlaced; static bool announceOnFirstDeath; static bool onlyTrackPlayerDeaths; + static bool recordAIDeaths; }; /*! @@ -5749,17 +5753,19 @@ * \ingroup JmgUtility */ class JMG_Utility_Custom_Send_Custom_On_Player_Count_HUD : public ScriptImpClass { + bool enabled; Vector3 hudStringColor; char hudMessage[220]; int hudStringId; int custom; int resetCustom; - int baseCount; - int playerCount; - int runningCount; void Created(GameObject *obj); void Custom(GameObject *obj,int message,int param,GameObject *sender); char *formatHUDMessage(const char *format,...); +public: + int runningCount; + int baseCount; + int playerCount; }; /*! @@ -6902,9 +6908,17 @@ * \ingroup JmgUtility */ class JMG_Utility_Basic_Spawner_In_Radius_Attached: public ScriptImpClass { + bool sentCreateMessage; + bool sentDeathMessage; void Created(GameObject *obj); void Destroyed(GameObject *obj); void AddSpawnedObjectToGroup(GameObject *spawned); +public: + JMG_Utility_Basic_Spawner_In_Radius_Attached() + { + sentCreateMessage = false; + sentDeathMessage = false; + } }; /*! @@ -7771,7 +7785,7 @@ * \ to stand on 1.466 meters below the water surface. The underwater[playerId] can be accessed from anywhere, allowing you to disable * \ screen fading when underwater from other scripts. * \WeaponsGroupID - ID to use to look up weapons specified by the weapons group controller -* \WeaponPreset - Weapon to lock the player to while swimming, make sure its type launcher to make use of my animations +* \WeaponPreset - Weapon to lock the player to while swimming, make sure its type launcher to make use of my animations, only used if ForceDefinedWeapons is on * \ForceDefinedWeapons - If 1 the player won't be able to select weapons that haven't been defined for the weapons group. * \DefaultHoldStyle - Default animation set used for weapons that haven't been defined. 0 = A, 1 = A, 2 = C, 3 = D, 4 = E, 5 = F, 6 = A, 7 = A, 8 = B, 9 = A, 10 = J, 11 = K, 12 = L, 13 = M * \DefaultSwimSpeed - Default swim speed for weapons that haven't overridden the swim speed. @@ -9576,6 +9590,7 @@ class JMG_Utility_Control_Point_Team_Member : public ScriptImpClass { void Created(GameObject *obj); void Timer_Expired(GameObject *obj,int number); + void Killed(GameObject *obj,GameObject *killer); public: int teamId; JMG_Utility_Control_Point_Controller::ControlPointTeamData *teamData; @@ -10124,6 +10139,7 @@ bool animating[128]; void Created(GameObject *obj); void Timer_Expired(GameObject *obj,int number); + void TriggerAnimationForThePlayer(GameObject *obj,int playerId); }; /*! @@ -10325,4 +10341,592 @@ char explosionPreset[256]; void Created(GameObject *obj); void Custom(GameObject *obj,int message,int param,GameObject *sender); +}; + +/*! +* \brief Used to enable or disable a spawner within an ID range of the script each time a custom is sent (adds one to the id to enable each time) +* \StartID - ID to start at +* \EndID - ID to stop at +* \Custom - Custom used to trigger the enable/disable +* \Enable - Enable or disable all the spawners +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Custom_Enable_Spawners_Incrementally_In_Range : public ScriptImpClass { + int currentId; + int custom; + int endId; + bool enable; + void Created(GameObject *obj); + void Custom(GameObject *obj,int message,int param,GameObject *sender); +}; + +/*! +* \brief Updates a wander point's group ID depending on if the attached control point can be spawned at by the specified team +* \ControlPointID - ID of the control point this wander point is connected to +* \TeamID - Team that this wander point updates for +* \TargetGroupID - GroupId the wander point should change to if the control point is the target of the team +* \NonTargetGroupId - GroupId the wander point should change to if the control point is not the target of the team +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Control_Point_Team_Target_Wander_Point : public ScriptImpClass { + int targetGroupId; + int nonTargetGroupId; + JMG_Utility_Control_Point *cpScript; + Rp2SimplePositionSystem::SimplePositionNode *wanderPoint; + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj,int number); + Rp2SimplePositionSystem::SimplePositionNode *AddAndReturnWanderpoint(GameObject *obj); +public: + JMG_Utility_Control_Point_Team_Target_Wander_Point() + { + cpScript = NULL; + } + int teamId; + int controlPointId; + void ControlPointChanged(); + void UpdateWanderpointSettings(); +}; + + +/*! +* \brief This script makes the AI seek out the nearest enemy, players are ignored, it works for infantry and vehicles +* \CaptureCpGroupId - Distance to scan for players to magically find and move to, -1 means infinite range +* \CaptureCpChance - 0.0-1.0 chance the AI will attempt to capture a control point instead of choosing to defend +* \CaptureSpeed - Speed at which the bot moves while trying to capture a control point +* \CaptureDistance - Distance to the wander point the bot will arrive +* \DefendCpGroupId - Group the AI will run between to defend CPs +* \DefendSpeed - Speed at which the bot moves while trying to defend a control point +* \DefendDistance - Distance to the wander point the bot will arrive +* \CloseDefendDistance - Distance in which defend points are considered close +* \ChooseFarDefendChance - 0.0-1.0 Chance that the bot will attempt to find a defend point that is further than the CloseDefendDistance +* \AttackSpeed - Speed to move at once an attack is begun +* \AttackDistance - Distance to arrive from a target when attacking, -1 uses the weapon effective distance +* \RandomAttackDistance - Random amount to add or subtract from the Attack distance +* \AttackDistractFromCaptureChance - Chance seeing an enemy will pull the AI away from charging at a control point to capture it +* \ChanceToInvestigateLastSeenLocation - The percent chance (0.0-1.0) of checking out the last spot an enemy/target was seen +* \AttackCheckBlocked - Defines whether they should check if they can actually hit the player before shooting +* \CanSeeStealth - Can the AI see stealth targets? 0 = No at all, 1 is within the ranges set in LE globals file, 2 means it can see stealth everywhere, think of stock AI. +* \ShutdownEngineOnArrival - Used for vehicles, turn on if you have issues with your vehicle rolling away from its move positions after it arrives +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_AI_Control_Point : public ScriptImpClass { + enum aiState{IDLE,CONTROL_POINT_ATTACK,CONTROL_POINT_DEFENSE,ATTACKING_TARGET,CHECKING_LOCATION,ACTION_BADPATH}; + struct LastAction + { + int targetId; + Vector3 location; + float speed; + float distance; + bool attack; + bool overrideLocation; + LastAction() + { + } + LastAction(int targetId,Vector3 location,float speed,float distance,bool attack,bool overrideLocation) + { + this->targetId = targetId; + this->location = location; + this->speed = speed; + this->distance = distance; + this->attack = attack; + this->overrideLocation = overrideLocation; + } + }; + struct ValidLastLocation + { + bool valid; + Vector3 location; + ValidLastLocation(bool valid) + { + this->valid = valid; + } + ValidLastLocation(int enemyId); + }; + LastAction lastAction; + aiState state; + Rp2SimplePositionSystem::SimplePositionNode *lastWanderPoint; + int targetId; + int lastSeenTime; + float weaponRange; + float weaponEffectiveRange; + float attackArriveDistance; + int stuckTime; + int reverseTime; + Vector3 lastPosition; + bool moveBackward; + float captureDistanceSquared; + float defendDistanceSquared; + bool allowCaptureAttackDistract; + + int captureCpGroupId; + float captureCpChance; + float captureSpeed; + float captureDistance; + int defendCpGroupId; + float defendSpeed; + float defendDistance; + float closeDefendDistanceSquared; + float chooseFarDefendChance; + float attackSpeed; + float attackDistance; + float randomAttackDistance; + float attackDistractFromCaptureChance; + float chanceToInvestigateLastSeenLocation; + bool attackCheckBlocked; + int canSeeStealth; + bool shutdownEngineOnArrival; + void Created(GameObject *obj); + void Enemy_Seen(GameObject *obj,GameObject *seen); + void Timer_Expired(GameObject *obj,int number); + void Action_Complete(GameObject *obj,int action_id,ActionCompleteReason reason); + void Damaged(GameObject *obj,GameObject *damager,float damage); + void Attack_Move(GameObject *obj,GameObject *target,Vector3 location,float speed,float distance,bool attack,bool overrideLocation); + void SelectNextMission(GameObject *obj,ValidLastLocation goNearLastWanderPoint); + void Stuck_Check(GameObject *obj,Vector3 targetPos); + bool Choose_Target(GameObject *obj,GameObject *target); + Rp2SimplePositionSystem::SimplePositionNode *Get_Capture_Point(); + Rp2SimplePositionSystem::SimplePositionNode *Get_Defense_Point(GameObject *obj); + void TriggerAttack(GameObject *obj,GameObject *target); +}; + + +/*! +* \brief Sets what bone the visible objective marker attaches to for the objective system +* \InfantryAttachBone - Bone to attach to +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Objective_System_Set_Infantry_Attach_Bone : public ScriptImpClass +{ + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj,int number); +}; + +/*! +* \brief Builds a keypad using a model with bones named Number(0-9), Clear, and Enter, the buttons it creates to attach to the bones are named PRESETNAME_Key_(0-9), PRESETNAME_Key_Enter, or PRESETNAME_Key_Clear +* \KeyCode - Code the numpad is looking for +* \ID - ID to send the messages to +* \Success_Custom - Message to send when the combination is matched +* \Partial_Failure_Custom - Sent until max_failures is meet +* \Failure_Custom - Message to send when max failures is meet, counter for partial fails is reset upon this +* \Disable_On_Success - Disable the keypad on a valid entry +* \Disable_On_Failure - If you completely fail should the keypad disable +* \Starts_Enabled - Whether the pad can be used from the get-go +* \Enable_Custom - Turn the number pad on/off +* \Failure_Safty - Allow partial fail customs to send +* \Max_Failures - As long as this value is less than the current failed attempt count a partial fail message is sent, once it matches a complete fail message is sent +* \Sound_Name_Base - Sound that should be played when buttons are pressed, a number 0-9 or ENTER or CLEAR is tacked on to the end +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Security_System_Fixed_NumberPad_Control : public ScriptImpClass { + int buttonIds[12]; + void Created(GameObject *obj); + void Destroyed(GameObject *obj); +}; + +/*! +* \brief Teleports the sender of a custom to a random wander point +* \Custom - Custom message to listen for +* \WanderingAIGroupID - Group of points to teleport to +* \SafeTeleportDistance - How far can infantry be moved if the spot is blocked +* \RetryOnFailure - If this is true a script will be attached that will continue to try to teleport the player until successful (Warning: Turning this on hides error messages) +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Custom_Teleport_Sender_Wanderpoint : public ScriptImpClass { + int customMsg; + bool retryOnFailure; + float safeTeleportDistance; + int wanderPointGroup; + void Created(GameObject *obj); + void Custom(GameObject *obj,int message,int param,GameObject *sender); + bool Get_A_Defense_Point(Vector3 *position,float *facing); + bool Grab_Teleport_Spot(GameObject *enter,int attempts); +}; + +/*! +* \brief Sets a random model on create +* \BaseName - Base model name in the random set to assign +* \FinalModelNumber - Latest number in the string of models, used to select the random model range 0-value (IE: BaseModel#) +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Create_Set_Random_Model : public ScriptImpClass { + void Created(GameObject *obj); +}; + +/*! +* \brief Destroys the sender on custom +* \Custom - custom to destroy the sender on +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Custom_Destroy_Sender : public ScriptImpClass { + int custom; + void Created(GameObject *obj); + void Custom(GameObject *obj,int message,int param,GameObject *sender); +}; + +/*! +* \brief Destroys the sender on custom +* \Custom - custom to destroy the sender on +* \Weapon - weapon to grant/add ammo to +* \FullAmmoString - HUD message to display when no more ammo can be carried +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Custom_Grant_Weapon_Destroy_Sender : public ScriptImpClass { + int custom; + char weapon[128]; + char fullAmmoString[128]; + void Created(GameObject *obj); + void Custom(GameObject *obj,int message,int param,GameObject *sender); +}; + + +/*! +* \brief Removes a script while a character has a weapon, gives it back when they lose the weapon +* \WeaponName - Name of the weapon to check for +* \Script - Name of the script to attach +* \Params - The parameters to use for the script +* \Delim - The character to use in place of a ',' swapped at script creation +* \Rate - Rate at which the script scans +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Remove_Script_While_Has_Weapon : public ScriptImpClass { + char weaponName[128]; + char *params; + char script[128]; + float rate; + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj,int number); +}; + +/*! +* \brief This script allows a AI soldier to swim when in a swimming zone. Attached by JMG_Utility_Swimming_Infantry +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Swimming_Infantry_AI : public ScriptImpClass { + int heartBeatSoundId; + int pantSoundId; + char enterWeapon[256]; + bool startedFadeRed; + float drownTime; + bool underwater; + int waterZoneCount; + int lastWaterZoneId; + float defaultSpeed; + int waterDamageDelayTime; + int waterDamageDelayTimeRecover; + int remainingWaterDamageDelay; + char originalSkin[128]; + char originalArmor[128]; + char originalModel[128]; + bool isUnderwater; + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj,int number); + void Custom(GameObject *obj,int message,int param,GameObject *sender); + void Killed(GameObject *obj,GameObject *killer); + void Destroyed(GameObject *obj); + void Detach(GameObject *obj); + void CreateSoundEmitter(GameObject *obj,const char *model,int *soundId); + void DestroySoundEmitter(int *soundId); + void HideSoundEmitter(int *soundId); +}; + +/*! +* \brief Refunds the damage if the warhead is a match +* \WarheadName - Warhead to refund the damage from +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Damaged_Refund_Damage : public ScriptImpClass { + WarheadType warhead; + void Created(GameObject *obj); + void Damaged(GameObject *obj,GameObject *damager,float damage); +public: + JMG_Utility_Damaged_Refund_Damage() + { + warhead = NULL; + } +}; + +/*! +* \brief Controls if JMG_Utility_Turret_Spawn_Global_Flag can attach turrets +* \Custom - Custom to flip if attaching is allowed, if param is 0 then its false +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Turret_Spawn_Global_Flag_Controller : public ScriptImpClass +{ + int custom; + void Created(GameObject *obj); + void Custom(GameObject *obj,int message,int param,GameObject *sender); +public: + JMG_Utility_Turret_Spawn_Global_Flag_Controller() + { + AllowAttach = true; + } + static bool AllowAttach; +}; + +/*! +* \brief Attaches turret as long as the global flag is true otherwise is ignored +* \Turret_Preset - Preset of the turret +* \Bone_Name - Bone to hook the turret to +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Turret_Spawn_Global_Flag : public ScriptImpClass +{ + int turretId; + bool hasDriver; + void Created(GameObject *obj); + void Custom(GameObject *obj,int message,int param,GameObject *sender); + void Killed(GameObject *obj,GameObject *killer); + void Destroyed(GameObject *obj); + void Detach(GameObject *obj); +}; + +/*! +* \brief Kills an object after a time period of not being damaged, damage resets the countdown +* \Time - How long in seconds before death +* \Damage - How much damage +* \Warhead - Warhead to use +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Timer_Death_If_Not_Damaged_Over_Peroid : public ScriptImpClass { + char warhead[128]; + float damage; + int time; + int originalTime; + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj,int number); + void Damaged(GameObject *obj,GameObject *damager,float damage); +}; + +/*! +* \brief Controls if JMG_Utility_Global_Attach_Script_On_Flags can attach scripts, default flag is -1 +* \Custom - Custom to flip if attaching is allowed, the param is what to set the flag to +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Global_Attach_Script_On_Flag_Controller : public ScriptImpClass +{ + void Created(GameObject *obj); +public: + JMG_Utility_Global_Attach_Script_On_Flag_Controller() + { + globalFlag = -1; + } + static int globalFlag; +}; + +/*! +* \brief Changes what the global flag is on custom +* \Custom - Custom to flip if attaching is allowed +* \GlobalFlag - What to set the global flag to +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Global_Attach_Script_On_Flag_Custom : public ScriptImpClass +{ + int custom; + int globalFlag; + void Created(GameObject *obj); + void Custom(GameObject *obj,int message,int param,GameObject *sender); +}; + +/*! +* \brief Attaches a script on create if the global flag matches +* \GlobalFlag - Value that must be matched +* \Script - Name of the script to attach +* \Params - The parameters to use for the script +* \Delim - The character to use in place of a ',' swapped at script creation +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Global_Attach_Script_On_Flag : public ScriptImpClass { + void Created(GameObject *obj); +}; + +/*! +* \brief Controls if JMG_Utility_Global_Attach_Script_On_Flags can attach scripts, default flag is -1 +* \Custom - Custom to flip if attaching is allowed, the param is what to set the flag to +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Custom_Attach_Script_To_Presets : public ScriptImpClass +{ + int custom; + char *params; + char script[128]; + char preset[128]; + void Created(GameObject *obj); + void Custom(GameObject *obj,int message,int param,GameObject *sender); +}; + +/*! +* \brief References an object with JMG_Utility_Custom_Send_Custom_On_Player_Count_HUD and uses the count to set the animation frame on this object +* \ (Math is (baseCount+playerCountMultiplier*CurrentPlayers)*MaxFrame +* \ControllingID - Object with JMG_Utility_Custom_Send_Custom_On_Player_Count_HUD +* \Animation - Animation to use +* \MaxFrame - Length of the animation +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Custom_Send_Custom_On_Player_Count_Animation : public ScriptImpClass { + char animation[32]; + float maxFrame; + int controllerId; + float lastCalculation; + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj,int number); +}; + +/*! +* \brief Destroys self if not in a pathfind sector +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Created_Destroy_If_Not_In_Pathfind : public ScriptImpClass { + void Created(GameObject *obj); +}; + +/*! +* \brief Teleports a unit to a different location if it hasn't moved for X seconds +* \Seconds - Time before it must be teleported +* \Location - Spot to teleport to +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Teleport_If_Not_Moving_To_Location : public ScriptImpClass { + int originalTime; + int time; + Vector3 location; + Vector3 lastSpot; + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj,int number); +}; + +/*! +* \brief Used to set the default extension for the global random model system +* \DefaultExtension - String to use +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Global_Set_Random_Model_Controller : public ScriptImpClass +{ + void Created(GameObject *obj); +public: + JMG_Utility_Global_Set_Random_Model_Controller() + { + sprintf(extension,""); + } + static char extension[16]; +}; + +/*! +* \brief Updates all objects with JMG_Utility_Global_Set_Random_Model attached to use the new global extension +* \Custom - Custom message to trigger the update on +* \Extension - Extension to use +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Global_Set_Random_Model_Custom : public ScriptImpClass +{ + int custom; + char extension[16]; + void Created(GameObject *obj); + void Custom(GameObject *obj,int message,int param,GameObject *sender); +}; + +/*! +* \brief Sets a random model on create, makes use of the global extension +* \BaseName - Base model name in the random set to assign +* \FinalModelNumber - Latest number in the string of models, used to select the random model range 0-value (IE: BaseModel#Extension) +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Global_Set_Random_Model : public ScriptImpClass { + int randomValue; + char baseName[16]; + void Created(GameObject *obj); +public: + void UpdateModel(GameObject *obj); +}; + +/*! +* \brief This script allows an AI soldier to swim when in a swimming zone. Attached by JMG_Utility_Swimming_Infantry_Advanced_AI +* \author jgray +* \ingroup JmgUtility +*/ +class JMG_Utility_Swimming_Infantry_Advanced_AI : public ScriptImpClass { + int heartBeatSoundId; + int pantSoundId; + char enterWeapon[256]; + bool startedFadeRed; + float drownTime; + bool underwater; + int waterZoneCount; + int lastWaterZoneId; + float defaultSpeed; + int waterDamageDelayTime; + int waterDamageDelayTimeRecover; + int remainingWaterDamageDelay; + char originalSkin[128]; + char originalArmor[128]; + char originalModel[128]; + int weaponGroupId; + unsigned int currentWeaponId; + int defaultHoldStyle; + float defaultSwimSpeedMultiplier; + float waterSpeedMultiplier; + char defaultWeaponPreset[128]; + float defaultDrownTime; + float startDrownSequence; + float waterDamageAmount; + char waterDamageWarhead[128]; + float drownDamageRate; + char swimmingSkin[128]; + char swimmingArmor[128]; + char swimmingModel[128]; + float swimmingHeightScale; + float swimmingWidthScale; + float originalHeightScale; + float originalWidthScale; + char heartBeatSoundEmitterModel[16]; + char pantingSoundEmitterModel[16]; + char gaspForBreath[128]; + float catchBreathRate; + bool forceDefinedWeapons; + int weaponSwitchForward; + JMG_Utility_Swimming_Infantry_Advanced_Controller::WeaponNode *currentWeapon; + void Created(GameObject *obj); + void Timer_Expired(GameObject *obj,int number); + void Custom(GameObject *obj,int message,int param,GameObject *sender); + void Killed(GameObject *obj,GameObject *killer); + void Destroyed(GameObject *obj); + void Detach(GameObject *obj); + void CreateSoundEmitter(GameObject *obj,const char *model,int *soundId); + void DestroySoundEmitter(int *soundId); + void HideSoundEmitter(int *soundId); + void SwitchWeapon(GameObject *obj); + void UpdateWeaponSwimming(GameObject *obj,const WeaponDefinitionClass *weaponDef); + void GetWeaponId(const WeaponDefinitionClass *weaponDef); + int GetWeaponPosition(GameObject *obj,int weaponId); +public: + JMG_Utility_Swimming_Infantry_Advanced_AI() + { + weaponGroupId = 0; + currentWeaponId = 0; + currentWeapon = NULL; + } }; \ No newline at end of file diff -urN sourceold/scripts/ms_ai.cpp source/scripts/ms_ai.cpp --- sourceold/scripts/ms_ai.cpp 2020-09-26 08:59:57.401120700 +1000 +++ source/scripts/ms_ai.cpp 2021-07-27 14:44:22.801960500 +1000 @@ -103,12 +103,18 @@ Non script-specific functions --------------------------*/ +static bool IsFirstDebugMessageOnMapSent = false; static bool DebugPathfindFailMessagesLiveEnabled = false; static bool DebugPathfindFailMessagesFileEnabled = false; void Print_Debug(const StringClass& text) { if (DebugPathfindFailMessagesFileEnabled) { + if (!IsFirstDebugMessageOnMapSent) + { + Write_File_Async("_debug_bots.txt", The_Game()->Get_Map_Name().Peek_Buffer()); + Write_File_Async("_debug_bots.txt", "\n"); + } Write_File_Async("_debug_bots.txt", text.Peek_Buffer()); Write_File_Async("_debug_bots.txt", "\n"); } @@ -116,6 +122,9 @@ if (DebugPathfindFailMessagesLiveEnabled) { M_Console_Input("msg " + text); } + + if (!IsFirstDebugMessageOnMapSent) + IsFirstDebugMessageOnMapSent = true; } // ------------------------------------------------------------------------------------------------- @@ -377,6 +386,8 @@ // Skill-independent personality traits float investigateChance; float chatChance; + float squishEvadeChance; + float squishEvadeDistMin, squishEvadeDistMax; float sympathyVarMin, sympathyVarMax; public: @@ -397,6 +408,8 @@ CROUCH_CHANCE_CALM, INVESTIGATE_CHANCE, CHAT_CHANCE, + SQUISH_EVADE_CHANCE, + SQUISH_EVADE_DISTANCE, }; float Get_Random(BotPersonality::INTERVAL intervalType) @@ -431,6 +444,10 @@ return FreeRandom.Get_Float() < investigateChance ? 1.0f : 0.0f; case CHAT_CHANCE: return FreeRandom.Get_Float() < chatChance ? 1.0f : 0.0f; + case SQUISH_EVADE_CHANCE: + return FreeRandom.Get_Float() < squishEvadeChance ? 1.0f : 0.0f; + case SQUISH_EVADE_DISTANCE: + return FreeRandom.Get_Float(squishEvadeDistMin, squishEvadeDistMax); } return 0.0f; } @@ -482,6 +499,9 @@ jumpChanceRegular = FreeRandom.Get_Float(0.00f, 0.02f); jumpChanceCombat = FreeRandom.Get_Float(0.04f, 0.12f); crouchChanceCalm = FreeRandom.Get_Float(0.10f, 0.20f); + squishEvadeChance = FreeRandom.Get_Float(0.50f, 0.75f); + squishEvadeDistMin = 30.0f; + squishEvadeDistMax = 45.0f; break; // Regular case 1: @@ -500,6 +520,9 @@ jumpChanceRegular = FreeRandom.Get_Float(0.03f, 0.06f); jumpChanceCombat = FreeRandom.Get_Float(0.20f, 0.40f); crouchChanceCalm = FreeRandom.Get_Float(0.20f, 0.40f); + squishEvadeChance = FreeRandom.Get_Float(0.80f, 0.90f); + squishEvadeDistMin = 40.0f; + squishEvadeDistMax = 50.0f; break; // Experienced case 2: @@ -518,6 +541,9 @@ jumpChanceRegular = FreeRandom.Get_Float(0.08f, 0.16f); jumpChanceCombat = FreeRandom.Get_Float(0.60f, 0.80f); crouchChanceCalm = FreeRandom.Get_Float(0.70f, 1.00f); + squishEvadeChance = FreeRandom.Get_Float(0.95f, 1.00f); + squishEvadeDistMin = 50.0f; + squishEvadeDistMax = 60.0f; break; } // Untied to fixed personalities @@ -820,7 +846,7 @@ * \author Daniel Szabo (daniel.szabo.715@gmail.com) * \ingroup scripts_ai * -* Place this script on vehicle presets to explicitly mark them as transport vehicles. +* Place this script on vehicle presets to explicitly mark them as transport vehicles. * It makes sure bots use it as a transport, even if the seat count is less than the one set * TransportVehicleMinimumSeatCount in the bot config file. * @@ -849,7 +875,7 @@ // ------------------------------------------------------------------------------------------------- -static DynamicVectorClass AIBuildings; +static DynamicVectorClass AIBuildings; /* Building debug stuff stored outside the globalcontroller because this script can exist without palyer logic bots */ static bool BuildingDebugInited = false; @@ -939,7 +965,7 @@ // Setup automatic repair needed triggers StringClass params; - params.Format("%d,%d,%d,%d,%d,%d,%d,%s", (int)(Commands->Get_Max_Health(obj) * ((float)(100-(Get_Int_Parameter("DamagePercentToRepair"))) / 100)), 0, CUSTOM_AI_REPAIR_NEEDED, 0, -1, 0, 0, "false"); + params.Format("%d,%d,%d,%d,%d,%d,%d,%s", (int)(Commands->Get_Max_Health(obj) * ((float)(100 - (Get_Int_Parameter("DamagePercentToRepair"))) / 100)), 0, CUSTOM_AI_REPAIR_NEEDED, 0, -1, 0, 0, "false"); Commands->Attach_Script(obj, "MS_Health_Send_Custom", params.Peek_Buffer()); params.Format("%d,%d,%d,%d,%d,%d,%d,%s", (int)(Commands->Get_Max_Health(obj)), -1, 0, 0, 0, CUSTOM_AI_REPAIR_COMPLETE, 0, "false"); Commands->Attach_Script(obj, "MS_Health_Send_Custom", params.Peek_Buffer()); @@ -1216,8 +1242,8 @@ Get_Closest_Pathfind_Spot_Size(boundingBox.Center, bldRadius + 50.0f, &soldierAttackObjectiveLocation, 0.5f); Vector3 helper; int count = 0; - while (count < 50 && - !M_Test_Raycast_Collision(soldierAttackObjectiveLocation, boundingBox.Center, false, &helper, obj) && + while (count < 50 && + !M_Test_Raycast_Collision(soldierAttackObjectiveLocation, boundingBox.Center, false, &helper, obj) && !M_Test_Raycast_Collision(soldierAttackObjectiveLocation, boundingBox.Center + Vector3(0, 0, boundingBox.Extent.Z), false, &helper, obj)) { count++; @@ -1285,7 +1311,7 @@ if (!obj->As_BuildingGameObj()) Commands->Attach_Script(obj, "dp88_AI_Marker_Building", ""); - + if (BuildingDebugLocationsDisplayEnabled) { StringClass debug; @@ -1470,7 +1496,7 @@ } } - Raycast_Target_Pair(obj, sg1, sg2, closestPoly1, closestPoly2); + Raycast_Target_Pair(obj, sg1, sg2, closestPoly1, closestPoly2); // Tall buildings const float heightAvg = (sg1.Z + sg2.Z) / 2; @@ -1671,7 +1697,6 @@ targetPos = Commands->Get_Position(damager); } Create_Timed_Objective(Get_Object_Type(obj), 8.0f, targetPos, 2, dp88_AI_Objective::TYPE_DEFENSIVE); - } void Cleanup(GameObject* obj) @@ -1684,10 +1709,10 @@ Commands->Destroy_Object(objectiveRepairTargetObj); Commands->Destroy_Object(centralTarget); Commands->Destroy_Object(centralTargetGround); - for (int i = 0; i < exteriorTargets.Count(); i++) + for (int i = 0; i < exteriorTargets.Count(); i++) Commands->Destroy_Object(exteriorTargets[i]); - for (int i = 0; i < objectiveVehicleAttackLocations.Count(); i++) + for (int i = 0; i < objectiveVehicleAttackLocations.Count(); i++) Commands->Destroy_Object(objectiveVehicleAttackLocations[i]); // TODO: keep the locations saved so no need to recalculate after reviving, just place them @@ -1763,6 +1788,7 @@ GOING_TO_EXIT_VEHICLE_POINT, GOING_TO_RALLY_POINT, PATROLLING, + DEAD, }; State state; @@ -1807,6 +1833,7 @@ // Squishing! bool isSquisherUnit; bool isSquishableUnit; + bool isSquishEvading; // Helping limited turret rotation vehicles bool isPrimaryAmmoTurning; @@ -1819,6 +1846,8 @@ float turretTurnMin; float turretTurnMax; + //int debugObjID = 0; + // Ignoring primary objectives when doing other stuff ReferencerClass overridingTask; //int overridingTaskID; @@ -1883,6 +1912,7 @@ isSquisherUnit = false; isSquishableUnit = false; + isSquishEvading = false; isPrimaryAmmoTurning = false; isSecondaryAmmoTurning = false; @@ -1987,14 +2017,15 @@ isLimitedAmmoUnit = Is_Script_Attached(obj, "MS_AI_Marker_Refiller_Vehicle"); // Fix bad AI definition settings - int weaponMaxRange = max(primary_maxRange, secondary_maxRange); - float defSightRange = obj->As_SmartGameObj()->Get_Definition().Get_Sight_Range(); - float neededSightRange = max( max(weaponMaxRange, defSightRange), 100 ) + 5; // +5: Sight range needs to be larger than the weapon max range + const int weaponMaxRange = max(primary_maxRange, secondary_maxRange); + const float defSightRange = obj->As_SmartGameObj()->Get_Definition().Get_Sight_Range(); + const float neededSightRange = max( max(weaponMaxRange + 5, defSightRange), 100 ); // +5: Sight range needs to be larger than the weapon max range if (defSightRange < neededSightRange) const_cast(obj->As_SmartGameObj()->Get_Definition()).Set_Sight_Range(neededSightRange); - float neededArc = obj->As_SoldierGameObj() ? 135.0f : 360.0f; - if (obj->As_SmartGameObj()->Get_Definition().Get_Sight_Arc() < neededArc) + const float neededArc = obj->As_SoldierGameObj() ? 135.0f : 360.0f; + const float currentArc = RAD2DEG(obj->As_SmartGameObj()->Get_Definition().Get_Sight_Arc()); + if (currentArc < neededArc) const_cast(obj->As_SmartGameObj()->Get_Definition()).Set_Sight_Arc(DEG2RAD(neededArc)); Commands->Attach_Script(obj, "MS_AI_Pathfind_Helper", ""); @@ -2165,14 +2196,20 @@ } else params.Set_Attack(target, (float)(maxRange), tempAttackError, m_bTargetPrimaryFire); + + params.AttackCheckBlocked = false; + + const Vector3 objPos = Commands->Get_Position(obj); + float currentDistance2 = Vector3::Distance(objPos, Commands->Get_Position(target)); // This makes units like Renegade MRLS/Stealth Tank work, their rockets turn after firing but the barrel cant point at their target - params.AttackForceFire = m_bTargetPrimaryFire ? isPrimaryAmmoTurning : isSecondaryAmmoTurning; + params.AttackForceFire = currentDistance2 < maxRange ? (m_bTargetPrimaryFire ? isPrimaryAmmoTurning : isSecondaryAmmoTurning) : false; // COPIED ORIGINAL FUNCTION, MODIFIED FROM HERE // Soldiers crouch with some probability bool moveCrouched = false; - if (bPersueTarget && obj->As_SoldierGameObj()) + // Ignore crouching during squishevasion, makes the soldier too slow to succeed + if (bPersueTarget && obj->As_SoldierGameObj() && !isSquishEvading) { if (FreeRandom.Get_Float() < 0.1f && !isDeployable) moveCrouched = true; @@ -2182,7 +2219,6 @@ } int preferredRange = GetPreferredAttackRange(obj, target); - float currentDistance2 = Vector3::Distance(Commands->Get_Position(obj), Commands->Get_Position(target)); // Evade-moving around if (evadePosition != Vector3()) @@ -2238,7 +2274,7 @@ // Only need correction if the turret cannot rotate there, unless the ammo is turning, in which case a little offness is acceptable since the ammo turn corrects it after some distance auto &boundingBox = obj->As_PhysicalGameObj()->Peek_Model()->Get_Bounding_Box(); const int radius = (int)(max(boundingBox.Extent.X, boundingBox.Extent.Y)); - if ((angle < turretTurnMin || angle > turretTurnMax) && (!(m_bTargetPrimaryFire ? isPrimaryAmmoTurning : isSecondaryAmmoTurning) || (abs(angle) > DEG2RAD(20) && Vector3::Distance(Commands->Get_Position(obj), targetPos) > radius + 10.0f))) + if ((angle < turretTurnMin || angle > turretTurnMax) && (!(m_bTargetPrimaryFire ? isPrimaryAmmoTurning : isSecondaryAmmoTurning) || (abs(angle) > DEG2RAD(20) && Vector3::Distance(objPos, targetPos) > radius + 10.0f))) needsRotating = true; } @@ -2268,7 +2304,7 @@ Get_Closest_Pathfind_Spot_Size(pos, min(20.0f, preferredRange), &pos, obj->As_SoldierGameObj() ? 0.5f : 5.0f); params.Set_Movement(pos, 1.0f, 1.0f, moveCrouched); } - else + else { if (canSquishTarget) { params.Set_Movement(target, 1.0f, 0); @@ -2286,7 +2322,6 @@ // "Turn towards" enemy if turret cannot turn on the enemy by moving closer to target else if (needsRotating && !isTargetFlying) { - Vector3 objPos = Commands->Get_Position(obj); targetPos = Commands->Get_Position(target); // "Turn" towards target by actually going towards it @@ -2332,7 +2367,7 @@ { params.MovePathfind = false; params.MoveLocation.Z += flyingHeight; - auto helperPos = Commands->Get_Position(obj); + auto helperPos = objPos; helperPos.Z -= 15; if (isBomber && Vector3::Distance(helperPos, Commands->Get_Position(target)) <= maxRange) { @@ -2550,7 +2585,6 @@ if (!M_Test_Raycast_Collision(Commands->Get_Position(obj) + Vector3(0, 0, 1.5f), Commands->Get_Position(taskTarget) + Vector3(0, 0, 1.5f), false)) Enemy_Seen(obj, taskTarget); } - break; } } @@ -2576,17 +2610,15 @@ void Timer_Expired(GameObject* obj, int number) override { + if (state == DEAD) + return; + if (number == TIMER_AI_POST_CREATED) { Post_Created(obj); } else if (number == TIMER_AI_THINK) { - if (Commands->Get_Health(obj) < 1.0f) - { - return; - } - thinkTime = personality->Get_Random(BotPersonality::THINK_TIME); dp88_AI_Tank_Offensive::Timer_Expired(obj, number); @@ -2736,7 +2768,7 @@ // Default evadePosition = Vector3(); // Don't deploy when busy, retry later - if (!(obj->As_SoldierGameObj()&& Is_Soldier_Busy(obj->As_SoldierGameObj()))) + if (!(obj->As_SoldierGameObj() && Is_Soldier_Busy(obj->As_SoldierGameObj()))) deployScript->KeyHook(); // Commands->Start_Timer(obj, this, 0.8f, TIMER_AI_REQUEST_DEPLOY); } @@ -2836,10 +2868,10 @@ // Move around randomly every second (x second for vehs) during battle if (!gettingAround && !deployOrderOverride) { - float distFromTarget = getDistance(obj, target); - float maxRange = (float)((m_bTargetPrimaryFire) ? primary_maxRange : secondary_maxRange); - bool inRange = maxRange > distFromTarget; - bool inPrefRange = GetPreferredAttackRange(obj, target) >= distFromTarget; + const float distFromTarget = getDistance(obj, target); + const float maxRange = (float)((m_bTargetPrimaryFire) ? primary_maxRange : secondary_maxRange); + const bool inRange = maxRange > distFromTarget; + const bool inPrefRange = GetPreferredAttackRange(obj, target) >= distFromTarget; bool hasWeapon = false; if (target->As_SmartGameObj()) @@ -2851,8 +2883,11 @@ // Reset evadePosition = Vector3(); + if (obj->As_SoldierGameObj()) + isSquishEvading = false; - bool canSquishTarget = isSquisherUnit && target->As_SoldierGameObj() && !Is_Unsquishable(target); + const bool canSquishTarget = isSquisherUnit && target->As_SoldierGameObj() && !Is_Unsquishable(target); + const bool targetCanSquishMe = isSquishableUnit && target->As_VehicleGameObj() && target->As_VehicleGameObj()->Get_Squish_Velocity() < 10; // Cancel evading in these cases if (!IsValidTarget(obj, target) || @@ -2883,15 +2918,91 @@ if (m_pCurrentObjective != nullptr) objectiveObj = m_pCurrentObjective->GetGameObject(); - if ((ShouldPursueTarget(obj, target) || M_Has_Timer(obj, this, TIMER_AI_UNDER_ATTACK)) && (targetType != BUILDING ? inPrefRange : inRange) && Get_Random_Pathfind_Spot_Around_Self(obj, minDist, maxDist, &pos, targetType != BUILDING ? target: nullptr)) - evadePosition = pos; - else if (objectiveObj && (m_pCurrentObjective->GetRange() + 1) < getDistance(obj, m_pCurrentObjective->GetGameObject())) - { - minDist = 0.5f; - maxDist = (float)m_pCurrentObjective->GetRange(); - auto objectivePos = Commands->Get_Position(objectiveObj); - if (Get_Random_Pathfind_Spot_Around_Position(obj, objectivePos, minDist, maxDist, &pos)) + if (targetCanSquishMe && personality->Get_Random(BotPersonality::SQUISH_EVADE_CHANCE)) { + const auto targetVeh = target->As_VehicleGameObj(); + + const auto model = targetVeh->Peek_Model(); + const auto& obj_box = model->Get_Bounding_Box(); + // Expecting most vehicles to be square-ish sized, epic long ships will be problematic here. + const float biggerSideHalf = max(obj_box.Extent.X, obj_box.Extent.Y); + const float minEvadeDistance = biggerSideHalf + personality->Get_Random(BotPersonality::SQUISH_EVADE_DISTANCE); + + // Find an evade position around the enemy vehicle + if (minEvadeDistance > distFromTarget) + { + + // TODO: move to side of vehicle ASAP if not there yet, move further away if already there as far as preferred range is, but keep that to the vehicle's side too + /* + * If Enemy can squish me: + * 1. Check if I'm epic close: + * - If yes, do evade and put objective on hold until we're safe or new target is around and done. + * 2. If not close: + * - Check if target should be pursued (group rushing and negative priorities are all NO pursue): + * - If yes, move to preferred distance away from the enemy's closest side. + * - If not, move towards objective, aka do nothing. + */ + + const Vector3 objPos = Commands->Get_Position(obj); + const auto facing = DEG2RAD(Commands->Get_Facing(target)); + const bool isTooClose = distFromTarget < (obj_box.Extent.Y * 2); + // const float XOffset = 0;// distFromTarget < 3 * obj_box.Extent.Y ? 0 : (obj_box.Extent.Y * (Is_Behind_Cone(Get_Transform(target), objPos, 0) ? -1 : 1)); + // Get out of the way if too close, otherwise go out of dangerous proximity + const float YOffset = obj_box.Extent.X + (isTooClose ? obj_box.Extent.Y * 3.0f : minEvadeDistance) + 3; + + Vector3 helpPos1 = Commands->Get_Position(target); + Vector3 helpPos2 = Commands->Get_Position(target); + const float facingCos = cos(facing); + const float facingSin = sin(facing); + //const float XOffsetCos = XOffset * facingCos; + //const float XOffsetSin = XOffset * facingSin; + const float YOffsetCos = YOffset * facingCos; + const float YOffsetSin = YOffset * facingSin; + //helpPos1.X += XOffsetCos; + //helpPos1.Y += XOffsetSin; + helpPos1.Y += YOffsetCos; + helpPos1.X -= YOffsetSin; + + //helpPos2.X += XOffsetCos; + //helpPos2.Y += XOffsetSin; + helpPos2.Y += -YOffsetCos; + helpPos2.X -= -YOffsetSin; + + const bool isWithinPathfindGrid1 = Get_Closest_Pathfind_Spot_Size(Vector3(helpPos1), 1.0f, &helpPos1, 2.0f); + const bool isWithinPathfindGrid2 = Get_Closest_Pathfind_Spot_Size(Vector3(helpPos2), 1.0f, &helpPos2, 2.0f); + if (isWithinPathfindGrid1 && isWithinPathfindGrid2) + { + // Choose the closer one + evadePosition = Vector3::Distance_Squared(objPos, helpPos1) < Vector3::Distance_Squared(objPos, helpPos2) ? helpPos1 : helpPos2; + } + else + { + evadePosition = isWithinPathfindGrid1 ? helpPos1 : helpPos2; + } + if (isWithinPathfindGrid1 || isWithinPathfindGrid2) + { + isSquishEvading = true; + Get_Random_Pathfind_Spot_Around_Position(obj, evadePosition, 0.5f, 2.0f, &evadePosition); + + //Commands->Destroy_Object(Commands->Find_Object(debugObjID)); + //const auto targetObj = Commands->Create_Object("Invisible_Object", evadePosition); + //Commands->Set_Model(targetObj, isTooClose ? "o_davesarrow" : "o_flagatt"); + //debugObjID = targetObj->Get_ID(); + } + } + } + + if (!isSquishEvading) + { + if ((ShouldPursueTarget(obj, target) || M_Has_Timer(obj, this, TIMER_AI_UNDER_ATTACK)) && (targetType != BUILDING ? inPrefRange : inRange) && Get_Random_Pathfind_Spot_Around_Self(obj, minDist, maxDist, &pos, targetType != BUILDING ? target : nullptr)) evadePosition = pos; + else if (objectiveObj && (m_pCurrentObjective->GetRange() + 1) < getDistance(obj, m_pCurrentObjective->GetGameObject())) + { + minDist = 0.5f; + maxDist = (float)m_pCurrentObjective->GetRange(); + const auto objectivePos = Commands->Get_Position(objectiveObj); + if (Get_Random_Pathfind_Spot_Around_Position(obj, objectivePos, minDist, maxDist, &pos)) + evadePosition = pos; + } } } @@ -3348,6 +3459,21 @@ // ------------------------------------------------------------------------------------------------- + void Killed(GameObject* obj, GameObject* killer) override + { + state = DEAD; + } + + // ------------------------------------------------------------------------------------------------- + + void Destroyed(GameObject* obj) override + { + state = DEAD; + //Commands->Destroy_Object(Commands->Find_Object(debugObjID)); + } + + // ------------------------------------------------------------------------------------------------- + void Action_Complete(GameObject *obj, int action_id, ActionCompleteReason complete_reason) override { if (action_id == ACTION_ID_MOVE_TO_OBJECTIVE) @@ -3549,7 +3675,7 @@ "Weapon_Heavy_Vehicle=1:int," "Priority_VTOL=5.0:float," "Weapon_VTOL=1:int," - "Priority_Building=12.0:float," + "Priority_Building=3.0:float," "Weapon_Building=1:int," "Max_Attack_Range=150:int," "Min_Attack_Range=0:int," @@ -3972,6 +4098,8 @@ DynamicVectorClass engineerUnitList; int transportVehicleSeatCount; float grantCreditsMultiplier; + bool chattingEnabled; + bool radioCommandsEnabled; bool killedRespEnabled; bool killerRespEnabled; bool debugBuyMessagesEnabled; @@ -4030,6 +4158,8 @@ baseDefObjectiveIDTeam[1] = 0; radarRadius = 0; transportVehicleSeatCount = 0; + chattingEnabled = true; + radioCommandsEnabled = true; killedRespEnabled = false; killerRespEnabled = false; debugBuyMessagesEnabled = false; @@ -4059,6 +4189,8 @@ // TODO: THIS IS A SMALL PROBLEM radarRadius = 50.0f; //Commands->Get_Radar_Radius(); + IsFirstDebugMessageOnMapSent = false; + int radarMode = RADAR_NOBODY; if (The_Game()) radarMode = The_Game()->Get_Radar_Mode(); @@ -4121,7 +4253,6 @@ Commands->Attach_Script(object, "MS_AI_Building", bldParams); } } - Create_Engineer_Unit_List(); } @@ -4304,6 +4435,8 @@ Radio_Message_Type::DEFEND_THAT_STRUCTURE = config->Get_Int("RadioCommands", "DEFEND_THAT_STRUCTURE", Radio_Message_Type::DEFEND_THAT_STRUCTURE); } + chattingEnabled = config->Get_Bool("General", "EnableChat", true); + radioCommandsEnabled = config->Get_Bool("General", "EnableRadioCommands", true); transportVehicleSeatCount = config->Get_Int("General", "TransportVehicleMinimumSeatCount", 3); grantCreditsMultiplier = config->Get_Float("General", "GrantCreditsMultiplier", 1.0f); debugBuyMessagesEnabled = config->Get_Bool("Debug", "EnableBuyMessages", false); @@ -5159,18 +5292,18 @@ nextObjectiveType = dp88_AI_Objective::TYPE_OFFENSIVE; team = Get_Int_Parameter("Team"); // Neutral is bad, actual team is needed by some scripts - Change_Team(obj, team); + Set_Object_Type(obj, team); // Let Global Controller know we exist MS_AI_Global_Controller::GetInstance().teamControllers[team] = obj; - Commands->Start_Timer(obj, this, 0.1f, TIMER_AI_THINK); - beaconPlacementVictoryAllowed = false; if (The_Cnc_Game()) beaconPlacementVictoryAllowed = The_Cnc_Game()->Beacon_Placement_Ends_Game(); else if (The_Skirmish_Game()) beaconPlacementVictoryAllowed = The_Skirmish_Game()->Beacon_Placement_Ends_Game(); + + Commands->Start_Timer(obj, this, 0.1f, TIMER_AI_THINK); } void Destroyed(GameObject *obj) override @@ -5215,7 +5348,7 @@ return defenders.Count() < defendersNeeded; } - GameObject *Find_Closest_AI_Building(const Vector3& position, int team) + GameObject *Find_Closest_AI_Building(const Vector3& position) { GameObject* bestBuilding = nullptr; float bestDistance = FLT_MAX; @@ -5258,7 +5391,7 @@ if (!isValidTarget) { Vector3 objPos = Commands->Get_Position(object); - auto bld = Find_Closest_AI_Building(objPos, team); + auto bld = Find_Closest_AI_Building(objPos); // This check is necessary as it can happen that only the other team has buildings, and this team does not if (!bld) continue; @@ -5320,7 +5453,7 @@ continue; Vector3 objPos = Commands->Get_Position(object); - auto bld = Find_Closest_AI_Building(objPos, team); + auto bld = Find_Closest_AI_Building(objPos); // This check is necessary as it can happen that only the other team has buildings, and this team does not if (!bld) continue; @@ -5343,7 +5476,6 @@ } } - public: int Signup_For_Next_Objective_Type(GameObject *botCtrl) { @@ -5632,7 +5764,7 @@ if (!checkedError.Is_Empty()) { - errorMessage.Format("%s", checkedError.Peek_Buffer()); + errorMessage = checkedError; valid = false; } @@ -6408,7 +6540,7 @@ if (!Is_Script_Attached(soldierObject, "MS_AI_Behaviour")) Activate_AI_Behaviour(obj, soldierObject); - else + else { Commands->Send_Custom_Event(obj, soldierObject, CUSTOM_AI_COOP_STOP_SECONDARY_OBJECTIVE, 0, 0.0f); Search_For_Vehicle(obj, 30); @@ -6669,7 +6801,7 @@ } } - soldier->As_SoldierGameObj()->Set_Bot_Tag(botName); + soldier->As_SoldierGameObj()->Set_Bot_Tag(botName.Peek_Buffer()); soldier->As_SoldierGameObj()->Set_Player_Type(team); soldier->As_SoldierGameObj()->Start_Observers(); Remove_Stock_AI_Behaviour(soldier); @@ -6855,7 +6987,10 @@ } for (int i = 0; i < fullList.Count(); i++) { - namedList.Add(DefinitionMgrClass::Find_Definition(fullList[i], false)->Get_Name()); + if (auto* def = DefinitionMgrClass::Find_Definition(fullList[i], false)) + { + namedList.Add(def->Get_Name()); + } } } @@ -6932,6 +7067,9 @@ // Make sure it's not zero or bigger than max if (preferredAttackRange == 0 || preferredAttackRange > maxAttackRange) preferredAttackRange = maxAttackRange; + // Long range units: snipers/artilleries, should not try to come as close + if (maxAttackRange >= 120) + preferredAttackRange = maxAttackRange - 10; } } @@ -7134,6 +7272,9 @@ void Send_Private_Message_String(char *msg, GameObject *recipient) { + if (!MS_AI_Global_Controller::GetInstance().chattingEnabled) { + return; + } unsigned int red, green, blue; Get_Private_Message_Color(&red, &green, &blue);