From 407a70dd730b42f86bfe71be1a637cad42d8dcc2 Mon Sep 17 00:00:00 2001 From: inkoalawetrust <56005600+inkoalawetrust@users.noreply.github.com> Date: Thu, 16 Jun 2022 02:57:41 +0300 Subject: [PATCH] New marine squad system, improvements, and easter eggs. - Added a brand new squad system, now on top of marines being able to do certain things when near other marines, they can also be made to rally around a designated leader, for more information, check the updated documentation. - Increased the default amount of time the marines spend looking for a target from 300 to 500. - Added a new dark red color for the marines. - Removed user_meleechance, it just took up space on the marines' custom tab for little value, as the marines are intentionally terrible at melee, and so have no reason NOT to avoid it, unless they have a berserk powerup in their inventory. - Marine command icons will no longer appear on the map. - Fixed a bug with the updated commands that made marines go back to staying still by default. - Fixed a bug in SM_AlertNearbyMarines(), which ran checks on the calling marine, instead of the ally that the calling marine is trying to alert. - The marine alerting functions can now also alert marines in the middle of fleeing from a grenade, by giving them a target to begin hunting after they stop running, if they didn't have one already. - Removed even more YandereDev code. - Added more easter eggs. - Updated readme and documentation. --- Design Document.txt | 2 +- Document.txt | 46 ++++- MAPINFO | 2 +- MarineFunctions.zsc | 41 ++-- Marine_Deaths.zsc | 76 ++++++-- Marine_OtherActors.zsc | 54 +++++- Marine_Squads.zsc | 410 +++++++++++++++++++++++++++++++++++++++ Marine_Turret.zsc | 4 +- Marine_UserVariables.zsc | 45 ++--- SNDINFO | 4 +- Sounds/AltAlert.ogg | Bin 0 -> 15337 bytes Sounds/AltVanish.ogg | Bin 0 -> 12896 bytes Sprites/MARCE0.png | Bin 0 -> 186 bytes Sprites/MARCF0.png | Bin 0 -> 247 bytes Sprites/MARCZ0.png | Bin 1042 -> 1042 bytes TRNSLATE | 1 + ZScript.zsc | 88 ++++++--- readme.md | 2 + 18 files changed, 692 insertions(+), 83 deletions(-) create mode 100644 Marine_Squads.zsc create mode 100644 Sounds/AltAlert.ogg create mode 100644 Sounds/AltVanish.ogg create mode 100644 Sprites/MARCE0.png create mode 100644 Sprites/MARCF0.png diff --git a/Design Document.txt b/Design Document.txt index cbb2087..235c96d 100644 --- a/Design Document.txt +++ b/Design Document.txt @@ -63,7 +63,7 @@ x===Worst case scenario I have to make them just keep facing the turret and walk =MAKE A MARINE WITH A ROCKET LAUNCHER. ==Make firing states for the rocket launcher with fire coming out. ==Don't forget to crop out and add his pain frames in the file, once I make the firing sprites though, and know what frame name to give to his pain sprites. -==Make a custom rocket model, yes me, that's right, I'll try to learn Blender to make a rocket with different pitches. +*==Make a custom rocket model, yes me, that's right, I'll try to learn Blender to make a rocket with different pitches. *==Figure out how to make the back facing sprites of the marine actually show the rocket launcher.| Done, TG5 redid all the sprites. ==Make the rocket launcher into a standalone sprite that can be picked up, and which rolls up in the air after the marine is killed.| Sprite made by me. diff --git a/Document.txt b/Document.txt index 5df465c..bdba9c2 100644 --- a/Document.txt +++ b/Document.txt @@ -23,6 +23,7 @@ The editor name of their No Crouch Zone map spots (More info below) is "No Crouc - Run away from the grenades they throw, if those grenades are about to explode. - Intelligently dodge enemy or extremely harmful projectiles heading their way. More information below. - Dynamically take cover behind sufficiently tall level geometry or shootable actors when attacking enemies. More information below. +- Can be set up into squads in which they stick together and follow a designated leader. And can change leaders if the previous one died. - They have multiple death animations that can play randomly. - Reload their rifles for every 20 rounds they fire in total. - They first try a few times to run away from their target until out of sight, to not reload while vulnerable. @@ -44,6 +45,7 @@ The editor name of their No Crouch Zone map spots (More info below) is "No Crouc - Orange - Pink - Dark Green + - Dark Red - Random (Randomly pick a color.) - Default (Use the default shade of green. Similar to just leaving User_Color empty.) - Most of the marines' behaviour can be configured on a per actor basis using user variables. @@ -147,12 +149,46 @@ Friendly marines can be commanded to do different things. You can press use on them to cycle them through the following orders: Wander: The marine will randomly wander around without following the player, and sometimes stay in place to look around. -Follow: The marine will follow the player that pressed use on him (If the player is friendly). Marines following a player are faster than when wandering, and will not stay in place. +Follow: The marine will follow the player that pressed use on him (If the player is friendly). Marines following a player move faster than when wandering, and will not stop to look around. Stand Still: The marine will stay in place, like when first spawned. After they finish searching for enemies, they go back to standing still instead of wandering. Exit Turret: Only applicable for marines on turrets, makes the marine exit the turret. Pressing use on a hostile marine will cause him to alert other nearby hostile marines. +================|SQUAD BEHAVIOR|================ +Marines can be optionally put into squads, in which they will follow the squads' leader around. +Marines in squads can also alert each other of enemies, this works similarly to how marines outside of squads alert each other, except there is no distance limit. + +HOW TO SET UP: +To form a squad on your map, you need to give 2 or more marines a User_SquadName, this is the name of the squad the marines will be part of. +Then you need to give the squad a leader for them to rally around, to do that, just pick or make a marine that shares the same squad name, +and turn on the User_Leader variable, which will assign him as the squads' leader. + +NOTE: You cannot have a squad with more than one leader, you also cannot make two opposing squads, that both share the same name. + +LEADERSHIP CHANGES: +Marine squads can change their leadership around if their leader is killed. If the squads' leader is killed or entirely removed from the game, +then the squad will assign its' healthiest marine as their new leader. + +In addition, marines, including the former leader, are not actually removed from their squad when killed, as there is still a chance for them +to be resurrected. However marines WILL be entirely removed from their squad if they are gibbed (Cannot be resurrected), or are entirely erased +from existance. If all the members are gibbed or erased, the squad is dissipated entirely. + +NOTE: If an entire squad has been killed, and then one of the dead marines in it is resurrected. The surviving marine will set himself as the leader. + +SQUAD MOVEMENT & BEHAVIOR: +When in a squad, marines will follow their leader around, and try to stay within range of him. +Generally, marine squads stay the closest together when fighting, but squad members whose current target is very powerful, will keep more distance +from their leader. To avoid all getting too close together and then being killed by whichever powerful enemy. When wandering around looking for enemies, +squads are at their most lax, and squad members are allowed to be as far as 1024 map units away from their leader without needing to head back to him. + +Friendly marines that are part of a squad, but are not the squads' leader, will not follow the players' commands. +Instead, to command the members of a squad you have to give commands to the leader, which the rest of the squad +will then generally follow, such as making a squad stand still by ordering their leader to do so. + +Note: Friendly marines in squads have little icons that appear over their heads for distinction, the icon with 4 human figures indicates that the marine +is a normal member. The star icon indicates that the marine is the squads' leader. Unfriendly marines do not have these icons over their heads. + ================|USER VARIABLES|================ User variables allow an actor to be configured by the mapper through the editor on a per-actor basis, without requiring to modify the source code of the actor in any way. If you don't know how to use and/or change the user variables of an actor, then click on this Imgur album to see how: https://imgur.com/a/IF9Ezo2 @@ -170,20 +206,20 @@ User_RetreatAttempts: The amount of times the marine tries to run out of sight o - Default is 10. -1 makes the marine reload in the open, without trying to get behind cover at all. User_AlertRange: The distance around which a marine will alert other marines on his side of a target. - Default is 384 map units. -1 disables alerting entirely for this marine, but still allows them to hear the alerts of other marines. +User_Leader: If the marine also has a squad name specified, this tells the game that this marine should be the leader of that squad. Otherwise, it does nothing. +User_SquadName: The name of the squad the marine is in, if the squad has a leader, the marine will follow him around. User_DodgeRange: The maximum range in which marines react to incoming projectiles. - Default is 384 map units. -1 disables dodging entirely for this marine. User_EnemyAlertHearingRange: The distance around which a marine can hear the alerts of other, enemy marines. - Default is 1024 map units. -1 disables hearing enemy alerts entirely, but the marine can still be alerted by other marines on his side. -User_MeleeChance: The chance of the marine to try changing position after smacking something once. -- Default is 96, a value of 255 means that the marine will only smack a target when he gets into melee once, then change position. This variable ONLY uses a number range from 0 to 255. User_TurretThreshold: How likely the marine is to use a turret near him. Marines are much more likely to use turrets, if they have friendly players and/or NPCs around to cover them. - Default is 100, -1 means that the marine will never use a turret. Lower values increase the use chance. User_FearDistance: The distance that the marine runs away to, if his target is scary enough. - Default is 2048 map units, a value of 0 or less makes them fearless. (Within reason, so they still avoid grenades for example.) User_Color: Can be used to give the marines different armor colors. -- Default is no translation, the available colors are Default, Gray, Red, White, Black, Blue, Yellow, Orange, Pink, Dark Green, and Random, which randomly picks any of the afformentioned colors. +- Default is no translation, the available colors are Default, Gray, Red, White, Black, Blue, Yellow, Orange, Pink, Dark Green, Dark Red, and Random, which randomly picks any of the afformentioned colors. User_RandomPersonality: When on, it randomizes several user variables that could be considered aspects of the marines' personality, such as: -- User_SearchTime, User_GrenadeThreshold, User_TurretThreshold, User_RetreatAttempts, User_MeleeChance, User_FearDistance, and User_Color. +- User_SearchTime, User_GrenadeThreshold, User_TurretThreshold, User_RetreatAttempts, User_FearDistance, and User_Color. - If any of those variables have any values besides their defaults, then they won't be randomized, so you can still for example have marines with random grenade and turret thresholds, but the same melee chance. - The variable can be set to true again in-game using SetUserVariable() in ACS, allowing you to randomize a marines' personality mid-game, if you want to do that for some reason. diff --git a/MAPINFO b/MAPINFO index a334b9e..328cd22 100644 --- a/MAPINFO +++ b/MAPINFO @@ -9,5 +9,5 @@ DoomEdNums GameInfo { - AddEventHandlers = "SM_ProjectileHandler", "SM_LiquidTextureNameList" + AddEventHandlers = "SM_ProjectileHandler", "SM_LiquidTextureNameList", "SM_SquadHandler" } \ No newline at end of file diff --git a/MarineFunctions.zsc b/MarineFunctions.zsc index 7fa33ce..873d061 100644 --- a/MarineFunctions.zsc +++ b/MarineFunctions.zsc @@ -164,6 +164,12 @@ Mixin Class MarineFunctions Return (!Other.bNoTarget && !Other.bNeverTarget && !(Other.Player && Other.Player.Cheats & CF_NOTARGET)); } + //Returns true if the marine is busy with something, mostly to prevent those stupid delicate state sequences from breaking by an abrupt state change. + Bool SM_IsBusy() + { + Return (GoingToTurret || OnTurret || RunningToReload || Evading || Crouching || SM_IsInState ("RunAway")); + } + Bool GrenadeAttackDecision (Double BlastRadius = 192, Double ThrowDistance = 1024, Double DangerDistance = 224) { Int ThrowChance; @@ -336,7 +342,7 @@ Mixin Class MarineFunctions //Check the list of water textures. For (Int I = 0; I < LiquidList.WaterTextures.Size(); I++) { - //Did we hit a solid sector or 3D floor, if yes, does its' texture much any of the ones on the list ? + //Did we hit a solid sector or 3D floor, if yes, does its' texture match any of the ones on the list ? If (LiquidCheck.HitTexture == TexMan.CheckForTexture(LiquidList.WaterTextures[I])) { Splash = Spawn ("SM_BulletSplash",LiquidCheck.HitLocation); @@ -348,7 +354,7 @@ Mixin Class MarineFunctions //Check the list of nukage textures. For (Int I = 0; I < LiquidList.NukageTextures.Size(); I++) { - //Did we hit a solid sector or 3D floor, if yes, does its' texture much any of the ones on the list ? + //Did we hit a solid sector or 3D floor, if yes, does its' texture match any of the ones on the list ? If (LiquidCheck.HitTexture == TexMan.CheckForTexture(LiquidList.NukageTextures[I])) { Splash = Spawn ("SM_BulletSplash",LiquidCheck.HitLocation); @@ -361,7 +367,7 @@ Mixin Class MarineFunctions //Check the list of blood textures. For (Int I = 0; I < LiquidList.BloodTextures.Size(); I++) { - //Did we hit a solid sector or 3D floor, if yes, does its' texture much any of the ones on the list ? + //Did we hit a solid sector or 3D floor, if yes, does its' texture match any of the ones on the list ? If (LiquidCheck.HitTexture == TexMan.CheckForTexture(LiquidList.BloodTextures[I])) { Splash = Spawn ("SM_BulletSplash",LiquidCheck.HitLocation); @@ -374,7 +380,7 @@ Mixin Class MarineFunctions //Check the list of slime textures. For (Int I = 0; I < LiquidList.SlimeTextures.Size(); I++) { - //Did we hit a solid sector or 3D floor, if yes, does its' texture much any of the ones on the list ? + //Did we hit a solid sector or 3D floor, if yes, does its' texture match any of the ones on the list ? If (LiquidCheck.HitTexture == TexMan.CheckForTexture(LiquidList.SlimeTextures[I])) { Splash = Spawn ("SM_BulletSplash",LiquidCheck.HitLocation); @@ -387,7 +393,7 @@ Mixin Class MarineFunctions //Check the list of lava textures. For (Int I = 0; I < LiquidList.LavaTextures.Size(); I++) { - //Did we hit a solid sector or 3D floor, if yes, does its' texture much any of the ones on the list ? + //Did we hit a solid sector or 3D floor, if yes, does its' texture match any of the ones on the list ? If (LiquidCheck.HitTexture == TexMan.CheckForTexture(LiquidList.LavaTextures[I])) { Splash = Spawn ("SM_BulletSplash",LiquidCheck.HitLocation); @@ -569,7 +575,7 @@ Mixin Class MarineFunctions { return (((A - B) cross (C - B))).length() / (C - B).length(); } - + //Alerts nearby marines friendly to the calling one of the callers' target. Void SM_AlertNearbyMarines (Double Range = 256, Bool SkipCheck = False) { @@ -588,23 +594,34 @@ Mixin Class MarineFunctions { PotentialMarine = AlertNearbyMarines.Thing; - If (!PotentialMarine.bDormant && PotentialMarine.GetClassName() == "SmartMarine" && Distance3DSquared (PotentialMarine) <= Range*Range && PotentialMarine.Health > 0 && !IsHostile (PotentialMarine) && !(PotentialMarine.Target)) //If the actor is not dormant, a marine, in range, is not hostile to you, and has no target already. + If (PotentialMarine != Self && !PotentialMarine.bDormant && PotentialMarine.GetClassName() == "SmartMarine" && Distance3DSquared (PotentialMarine) <= Range*Range && PotentialMarine.Health > 0 && !IsHostile (PotentialMarine)) //If the actor is not dormant, a marine, in range, is not hostile to you, and has no target already. { - //Make sure the marine isn't in its' RunAway state or crouching, that state must NEVER be broken by being changed or having the marines' target changed during it. - If (!SM_IsInState ("RunAway") && !Crouching) + //If the marine isn't running away from a grenade and has no target currently. + If (!SmartMarine(PotentialMarine).SM_IsInState ("RunAway") && !PotentialMarine.Target) { - PotentialMarine.Target = PotentialMarine.LastHeard = Target; //Have the marine share your target. + PotentialMarine.LastHeard = PotentialMarine.Target = Target; //Have the marine share your target. FoundMarines = True; //A marine has been found. - If (!SM_IsInState ("See")) //If the marine isn't in its' see state already, to chase the target. + If (!SmartMarine(PotentialMarine).SM_IsInState ("See") && !SmartMarine(PotentialMarine).SM_IsBusy()) //Put the marine in his see state to begin chasing the target, if he isn't only it already and isn't busy. { PotentialMarine.SetStateLabel ("See"); //Then send them to it. } } + //If he IS running away from a grenade. But has no target that he was attacking before running away from the grenade. + Else If (SmartMarine(PotentialMarine).SM_IsInState ("RunAway") && !SmartMarine(PotentialMarine).OriginalTarget) + { + SmartMarine(PotentialMarine).OriginalTarget = PotentialMarine.LastHeard = Target; //Have the marine share your target. + FoundMarines = True; //A marine has been found. + } } } If (FoundMarines) //If at least one marine was found to alert. - A_StartSound ("Marine/AlertAllies",CHAN_VOICE,attenuation:0.75); + { + If (Random (0,256) == Random (0,256)) + A_StartSound ("Marine/AlertAlliesAlt",CHAN_VOICE,attenuation:0.75); + Else + A_StartSound ("Marine/AlertAllies",CHAN_VOICE,attenuation:0.75); + } } } diff --git a/Marine_Deaths.zsc b/Marine_Deaths.zsc index ed08861..c825444 100644 --- a/Marine_Deaths.zsc +++ b/Marine_Deaths.zsc @@ -8,12 +8,36 @@ Extend Class SmartMarine { LeaveTurret(); Super.Die (Source, Inflictor, DmgFlags, MeansOfDeath); + If (Icon) + Icon.Alpha = 0.0; + //If you were a squad leader. Then stay on the list still in case you are resurrected, but assign a new leader. + If (Squad) + { + //Remove yourself from the member list if you were gibbed. + If (Health <= GetGibHealth()) + Squad.LeaveSquad(Self); + + Squad.AssignNewSquadLeader(Self); + } + Else If (Master && SmartMarine(Master).Squad && Health <= GetGibHealth()) + SmartMarine(Master).Squad.LeaveSquad (Self); } Override Void OnDestroy() //I'd consider this to be a form of death. { LeaveTurret(); + //If you were a squad leader before erasure, remove your entry from the members array, so there are no null entries. And try assigning a new leader. + If (Squad) + { + Squad.Members.Delete(Squad.Members.Find(Self)); + Squad.AssignNewSquadLeader(Self); + } + + //If you were in a squad, access the squad class from your leader to remove yourself from the member list. + If (Master && SmartMarine(Master).Squad) + SmartMarine(Master).Squad.Members.Delete(SmartMarine(Master).Squad.Members.Find(Self)); + If (!bDormant && Random (0,512) == Random (0,512)) { Tracer = Spawn ("SmartMarineErased",Pos); @@ -107,19 +131,47 @@ Extend Class SmartMarine Raise: TNT1 A 0 { + If (Icon) + Icon.Alpha = 1.0; + Else If (!Icon && Master) + { + Icon = Spawn ("SmartMarineSquadIcon",(Pos.X,Pos.Y,Pos.Z+Height)); + If (Icon) + Icon.Master = Self; + } + Else If (!Icon && Squad && Squad.Leader == Self) + {; + Icon = Spawn ("SmartMarineLeaderIcon",(Pos.X,Pos.Y,Pos.Z+Height)); + If (Icon) + Icon.Master = Self; + } + + //Leave the squad if it is now hostile to you. + If (SmartMarine(Master) && SmartMarine(Master).Squad && IsHostile(Master)) + SmartMarine(Master).Squad.LeaveSquad(Self); + + //Make yourself the squad leader if after being resurrected, you find out that you are the only living squad member. + If (SmartMarine(Master) && SmartMarine(Master).Squad && SmartMarine(Master).Squad.IsEveryoneDead(Self)) + SmartMarine(Master).Squad.MakeSquadLeader (Self); + //I could shorten this using String.Format(), if strings/names could even be converted to state labels that is. - If (DeathAnimPlayed == 0 || DeathAnimPlayed == 1) - Return ResolveState ("RaiseAnim1"); - Else If (DeathAnimPlayed == 2) - Return ResolveState ("RaiseAnim2"); - Else If (DeathAnimPlayed == 3) - Return ResolveState ("RaiseAnim3"); - Else If (DeathAnimPlayed == 4) - Return ResolveState ("RaiseAnim4"); - Else If (DeathAnimPlayed == 5) - Return ResolveState ("RaiseAnim5"); - Else If (DeathAnimPlayed == 6) - Return ResolveState ("CrouchRaise"); + Switch (DeathAnimPlayed) + { + Case 0: + Return ResolveState ("RaiseAnim1"); + Case 1: + Return ResolveState ("RaiseAnim1"); + Case 2: + Return ResolveState ("RaiseAnim2"); + Case 3: + Return ResolveState ("RaiseAnim3"); + Case 4: + Return ResolveState ("RaiseAnim4"); + Case 5: + Return ResolveState ("RaiseAnim5"); + Case 6: + Return ResolveState ("CrouchRaise"); + } Return ResolveState (Null); } diff --git a/Marine_OtherActors.zsc b/Marine_OtherActors.zsc index 33abb8e..0212b69 100644 --- a/Marine_OtherActors.zsc +++ b/Marine_OtherActors.zsc @@ -83,6 +83,7 @@ Class SmartMarineStandingMessage : Actor { Scale 0.25; +NoInteraction; + +NotOnAutomap; +NoGravity; +Bright; } @@ -128,6 +129,53 @@ Class SmartMarineFollowingMessage : SmartMarineStandingMessage } } +Class SmartMarineSquadIcon : Actor +{ + Default + { + Scale 0.75; + RenderStyle "Translucent"; + +NoInteraction; + +NoGravity; + +NotOnAutomap; + } + States + { + Spawn: + MARC E 1 NoDelay + { + If (Master) + SetOrigin ((Master.Pos.X,Master.Pos.Y,Master.Pos.Z+Master.Height+4),True); + Else + Destroy(); + + If (Master.Health <= 0) + Alpha = 0; + } + Loop; + } +} + +Class SmartMarineLeaderIcon : SmartMarineSquadIcon +{ + Default{Scale 0.5;} + States + { + Spawn: + MARC F 1 NoDelay + { + If (Master) + SetOrigin ((Master.Pos.X,Master.Pos.Y,Master.Pos.Z+Master.Height+4),True); + Else + Destroy(); + + If (Master.Health <= 0) + Alpha = 0; + } + Loop; + } +} + Class SM_Grenade : Actor { Default @@ -354,9 +402,13 @@ Class SmartMarineErased : Actor States { Spawn: - PLAY G 4 NoDelay A_StartSound ("Marine/Gone",CHAN_VOICE); + TNT1 A 0 NoDelay A_Jump (255/2,"Alt"); + PLAY G 4 A_StartSound ("Marine/Gone",CHAN_VOICE); TNT1 A 35; Stop; + Alt: + TNT1 A 75 A_StartSound ("Marine/GoneAlt",CHAN_VOICE); + Stop; } } diff --git a/Marine_Squads.zsc b/Marine_Squads.zsc new file mode 100644 index 0000000..0f6c380 --- /dev/null +++ b/Marine_Squads.zsc @@ -0,0 +1,410 @@ +Extend Class SmartMarine +{ + //Sets up a squad leaders' squad during PostBeginplay(). + Void InitializeSquad() + { + //If you are a squad leader. + If (User_Leader && User_SquadName) + { + Let SquadList = SM_SquadHandler(EventHandler.Find("SM_SquadHandler")); + //Check if you are the leader of an existing squad, which of course already has a leader. + If (!SquadList.IsSquadNameUsed (User_SquadName)) + { + Squad = New('SM_MarineSquad'); //Create the container class. + SquadList.MarineSquads.Push(Squad); //Add the new squad to the squad list. + + Squad.Leader = Self; //Assign yourself as the squad leader. + + Squad.Name = User_SquadName; //Set the squads' name to the one in your user variable. + + Squad.Members.Push(Self); //Add yourself to the member list. + + //Give yourself an icon if friendly to any player. + If (bFriendly) + { + Icon = Spawn ("SmartMarineLeaderIcon",(Pos.X,Pos.Y,Pos.Z+Height)); + If (Icon) + Icon.Master = Self; + } + } + Else + { + Console.Printf ("\ckWARNING: The squad called \"%s\" already has a leader !",User_SquadName); + User_Leader = False; //Can't have a squad with multiple leaders. + SM_FindSquadmates(); //Now join the squad as a normal member instead. + } + } + } + //This runs rougly every second when a marine has a squad he should be in, and is not a squad leader himself. So that he can join said squad. + Void SM_FindSquadmates () + { + If (!User_SquadName || User_SquadName ~== "") + Return; + + Let SquadList = SM_SquadHandler(EventHandler.Find("SM_SquadHandler")); + SM_MarineSquad DaSquad; + + For (Int I = SquadList.MarineSquads.Size()-1; I >= 0; I--) + { + DaSquad = SquadList.MarineSquads[I]; + + If (DaSquad && DaSquad.Name ~== User_SquadName) + { + If (!DaSquad.IsInSquad(Self)) + { + //First, check if the squad is hostile to you. + If (IsHostile(Actor(DaSquad.Leader))) + { + Console.Printf ("\ckWARNING: There are hostile and friendly marines that share the \"%s\" squad name !",User_SquadName); + User_SquadName = ""; //Stop trying to join an enemy squad. + Return; + } + + //If at least someone in your new squad is alive. + If (!DaSquad.IsEveryoneDead()) + { + Master = DaSquad.Leader; //Then make the squads' leader your boss. + CurrentMarineOrder = ORDER_NONE; //And ignore the players' commands if friendly. + + //Give yourself an icon if friendly to any player. + If (bFriendly) + { + Icon = Spawn ("SmartMarineSquadIcon",(Pos.X,Pos.Y,Pos.Z+Height)); + If (Icon) + Icon.Master = Self; + } + } + //Otherwise, if everyone in the squad is dead already, make yourself the squad leader, so you don't follow around a corpse. + Else + DaSquad.MakeSquadLeader(Self); + DaSquad.Members.Push(Self); //Add yourself to the members array. + } + } + } + } + + //Makes marines in squads and their leaders alert the rest of their squad of an enemy. + Void SM_AlertSquadmates () + { + If (Master && SmartMarine(Master).Squad) + SmartMarine(Master).Squad.AlertSquad(Self); + Else If (Squad && Squad.Leader == Self) + Squad.AlertSquad(Self); + } + + //Doesn't really belong in MarineFunctions, because it only handles the marines' movement when they are in a squad. + Void SM_Chase () + { + Bool FollowPlayers; //The value of DontFollowPlayers before the function modifies it. + FollowPlayers = bDontFollowPlayers; + + //Don't run this if the marine doesn't have a squad leader, or has a patrol point. + If (Master && SmartMarine(Master).Squad && !Goal) + { + bDontFollowPlayers = True; //Don't follow the player when in a squad, only your leader. + //If you have a target, then follow your leader if he is further than 640 map units, but don't get too close. + If (Target && Target != Goal) + { + //While in combat, follow your leader if you are too far away, but also don't get right up in his face all the time. + //If your target is something scary, then keep more distance from your leader, to avoid getting your squad killed by the powerful foe. + //Also, you don't need to follow your leader when he is too far if he is simply running to reload or to avoid a grenade. + If (!SM_LeaderRunning() && !bFrightened && Distance3DSquared(Master) >= 640*640 && !(Distance3DSquared(Master) <= 304*304) || + !SM_LeaderRunning() && bFrightened && Distance3DSquared(Master) >= 1024*1024 && !(Distance3DSquared(Master) <= 400*400)) + { + SM_MoveTowards (Master); + A_Chase ("RifleSmack","DecideAttack",CHF_DONTIDLE|CHF_DONTMOVE); + bDontFollowPlayers = FollowPlayers; + Return; + } + //If you are too close to your leader or close enough to him, then go back to normal A_Chase. + Else + { + A_Chase ("RifleSmack","DecideAttack",CHF_DONTIDLE); + bDontFollowPlayers = FollowPlayers; + Return; + } + } + //If you have no target currently, but are still in your See state. + Else If (!Target && !Goal) + { + If (Distance3DSquared(Master) >= 512*512) + { + SM_MoveTowards (Master); + A_Chase ("RifleSmack","DecideAttack",CHF_DONTIDLE|CHF_DONTMOVE); + bDontFollowPlayers = FollowPlayers; + Return; + } + Else + { + A_Chase ("RifleSmack","DecideAttack",CHF_DONTIDLE); + bDontFollowPlayers = FollowPlayers; + Return; + } + } + } + //If you are not even in a squad, then just use standard A_Chase. + Else + A_Chase ("RifleSmack","DecideAttack",CHF_DONTIDLE); + } + + //Ditto + Void SM_Wander () + { + Bool FollowPlayers; + FollowPlayers = bDontFollowPlayers; + //If you are in a squad. + If (Master && SmartMarine(Master).Squad) + { + bDontFollowPlayers = True; + //Move towards your squad leader if you are too far away from him. + If (Distance3DSquared(Master) >= 1024*1024) + { + SM_MoveTowards (Master); + bDontFollowPlayers = FollowPlayers; + Return; + } + //If you are not too far away, or you are TOO close to him, then wander normaly. + Else + { + A_Wander(); + bDontFollowPlayers = FollowPlayers; + Return; + } + } + + A_Wander(); //Wander normally if not in a squad. + } + + //Temporarily changes the marines' target to the other actor, so that he will move towards it without chasing it. + Void SM_MoveTowards (Actor Other) + { + If (!Other) Return; + + OriginalTarget = Target; + Target = Other; + NewChaseDir(); + FaceMovementDirection(); + Target = OriginalTarget; + OriginalTarget = Null; + } + + Bool SM_LeaderRunning() + { + Return (SmartMarine(Master).SM_IsInState ("RunAway") || RunningToReload); + } + +} + +/*====================================================================================================== +This class is the data container each squad in the game uses, the squads' leader always has a pointer to +it, and other members of the squad can also access this container by doing "SmartMarine(Master).Squad", +because marines have their squad leader set as their master. +======================================================================================================*/ +Class SM_MarineSquad Play +{ + String Name; //The name of the squad. + Array Members; //Me and the boys. + SmartMarine Leader; //A pointer to the squads' leader in particular. + + //Called when the marine is killed or removed, if the marine is the leader of a squad, then the healthiest member of the squad will become the new leader. + Void AssignNewSquadLeader (SmartMarine Caller) + { + Let SquadList = SM_SquadHandler(EventHandler.Find("SM_SquadHandler")); + + //Non-leaders can't mess with who runs the squad. + If (!Caller.User_Leader || !Caller) + Return; + + //You are the last member of the squad left. So remove the squad. + If (Members.Size() == 0) + { + If (Caller.Squad) + SquadList.RemoveSquad (Caller.Squad); + Caller.Squad = Null; + If (Caller.Icon) + Caller.Icon.Destroy(); + Destroy(); + Return; + } + + //Iterate through the whole squad, and keep assigning the marine with the highest health you found as the next leader. + SmartMarine NextLeader = Members[0]; + + For (Int I = Members.Size()-1; I >= 0; I--) + { + //This marine is gibbed, so he cannot be possibly resurrected. + If (Members[I] && NextLeader && Members[I].Health <= Members[I].GetGibHealth()) + LeaveSquad(Members[I]); //So just remove him from the squad. + Else If (Members[I] && NextLeader && Members[I].Health > NextLeader.Health) + NextLeader = Members[I]; + } + + //If the above search didn't return a single marine who hasn't been gibbed already. Then remove the squad. + If (!NextLeader) + { + + If (Caller.Squad) + SquadList.RemoveSquad (Caller.Squad); + Caller.Squad = Null; + If (Caller.Icon) + Caller.Icon.Destroy(); + Destroy(); + Return; + } + + MakeSquadLeader(NextLeader); //https://www.youtube.com/watch?v=dvA-mimf2yg + } + + //Checks if the specified marine is the squads' leader or a part of the members array. + Bool IsInSquad (SmartMarine Marine) + { + Return (Marine == Leader || Members.Find(Marine) != Members.Size()); + } + + //Checks if everyone in the squad is dead, returns false if at least one marine is alove. + //The exception parameter is for resurrected marines to assign themselves as leader if everyone besides them is dead. + Bool IsEveryoneDead (SmartMarine Exception = Null) + { + If (!Exception) + { + For (Int I = Members.Size()-1; I >= 0; I--) + { + If (Members[I] && Members[I].Health > 0) + Return False; //A live marine was found. + } + } + Else + { + For (Int I = Members.Size()-1; I >= 0; I--) + { + If (Members[I] && Members[I] != Exception && Members[I].Health > 0) + Return False; //A live marine was found. + } + } + + Return True; + } + + //Assign the specified marine as the new squad leader. + Void MakeSquadLeader (SmartMarine Marine) + { + If (!Marine) + { + Console.Printf ("\ckWARNING: Tried to assign a non-existent marine as the leader of a squad."); + Return; + } + + //Demote the current leader. + Leader.User_Leader = False; + Leader.Squad = Null; + If (Leader.Icon) + Leader.Icon.Destroy(); + Leader = Null; + + //And make the specified marine the leader. + Marine.Master = Null; + Marine.Squad = Self; + Marine.User_Leader = True; + Leader = Marine; + + //If the marine already had an icon beforehand. + If (Marine.Icon) + Marine.Icon.Destroy(); //Destroy it. + + Marine.Icon = Actor.Spawn ("SmartMarineLeaderIcon",(Marine.Pos.X,Marine.Pos.Y,Marine.Pos.Z+Marine.Height)); + Marine.Icon.Master = Marine; + If (Marine.Health <= 0) //Make the icon invisible if the new squad leader is already dead. + Marine.Icon.Alpha = 0.0; + + //Go through the rest of the squad and assign the new marine as their leader. + For (Int I = Members.Size()-1; I >= 0; I--) + { + If (Members[I] && Members[I] != Marine) + Members[I].Master = Marine; + } + } + + //Makes a specific marine leave the squad. + Void LeaveSquad (SmartMarine Marine) + { + If (!Marine) Return; + + Members.Delete(Members.Find(Marine)); + Marine.User_SquadName = ""; + Marine.Master = Null; + + If (Marine.Icon) + Marine.Icon.Destroy(); + } + + //Used by friendly squad leaders who are given the stand still command by players, so that the command will be transfered over to the rest of the squad. + Void StandDownSquad(SmartMarine Caller, Bool RiseUp = False) + { + For (Int I = Members.Size()-1; I >= 0; I--) + { + If (Members[I] && Members[I] != Caller && Members[I].Health > 0) + If (!RiseUp) + { + Members[I].CurrentMarineOrder = 3; //3 is ORDER_STANDSTILL + Members[I].TimeSearching = 0; + } + Else + Members[I].CurrentMarineOrder = 0; + } + } + + //Alert the squad of the calling marines' target, but only alert squad members who don't already have a target. + //Unlike SM_AlertNearbyMarines, this alerts squadmates regardless of distance. + Void AlertSquad (SmartMarine Caller) + { + Bool Alerted; + For (Int I = Members.Size()-1; I >= 0; I--) + { + If (Members[I] && Members[I] != Caller && Members[I].Health > 0) + { + If (Members[I].SM_IsInState ("RunAway") && Members[I].OriginalTarget == Null) + Members[I].OriginalTarget = Caller.Target; //If running away from a grenade, the OriginalTarget variable is what needs changing. + + Else If (!Members[I].SM_IsInState ("RunAway") && Members[I].Target == Null) + { + Members[I].Target = Caller.Target; + If (!Members[I].SM_IsInState ("See") && !Members[I].SM_IsBusy()) + Members[I].SetStateLabel ("See"); + } + + Alerted = True; + } + } + + //If at least one other marine in the squad was alerted. + If (Alerted) + If (Random (0,256) == Random (0,256)) + Caller.A_StartSound ("Marine/AlertAlliesAlt",CHAN_VOICE,attenuation:0.75); + Else + Caller.A_StartSound ("Marine/AlertAllies",CHAN_VOICE,attenuation:0.75); + } +} + +//This is the event handler that stores pointers to all the SM_MarineSquad classes. So that marines can link up with or create their squad. +Class SM_SquadHandler : EventHandler +{ + Array MarineSquads; + + //Used to prevent identical squads from forming, or squads from trying to have more than one leader. + Bool IsSquadNameUsed (String SquadName) + { + For (Int I = 0; I < MarineSquads.Size(); I++) + { + If (SquadName ~== MarineSquads[I].Name) + Return True; + } + Return False; + } + + //Removes a particular squad from the global squad list. + Void RemoveSquad (SM_MarineSquad Squad) + { + If (Squad) + MarineSquads.Delete(MarineSquads.Find(Squad)); + } +} \ No newline at end of file diff --git a/Marine_Turret.zsc b/Marine_Turret.zsc index 4a572d1..47280ed 100644 --- a/Marine_Turret.zsc +++ b/Marine_Turret.zsc @@ -167,9 +167,9 @@ Class TurretMarine : Actor //Handles the random color choice that is performed if the color name given is "Random". If (User_Color ~== "Random") { - Static Const String ColorArray[] = {"Red","Gray","White","Black","Blue","Yellow","Orange","Pink","DarkGreen","Default"}; //Contains all the valid color names. + Static Const String ColorArray[] = {"Red","Gray","White","Black","Blue","Yellow","Orange","Pink","DarkGreen","DarkRed","Default"}; //Contains all the valid color names. - User_Color = ColorArray[Random(0,9)]; //Randomly picks a color string. + User_Color = ColorArray[Random(0,ColorArray.Size()-1)]; //Randomly picks a color string. } A_SetTranslation (String.Format("Marine_%s", User_Color)); diff --git a/Marine_UserVariables.zsc b/Marine_UserVariables.zsc index fcb2da9..77b6d20 100644 --- a/Marine_UserVariables.zsc +++ b/Marine_UserVariables.zsc @@ -1,26 +1,7 @@ //Code related to the marines' user variables goes here. Extend Class SmartMarine -{ - Override Void PostBeginPlay() - { - Super.PostBeginPlay(); - - //The //$UserDefaultValue editor key is buggy, so this code still needs to be used. - If (User_SearchTime == 0) User_SearchTime = 300; //The amount of time the marine tries to chase it's target, after it has died or gone out of sight. - If (User_GrenadeThreshold == 0) User_GrenadeThreshold = 240; //Default threshold that needs to be reached or surpassed, for the marine to throw a grenade. - If (User_RetreatAttempts == 0) User_RetreatAttempts = 10; //Default amount of attempts the marine makes to run for cover to reload. - If (User_AlertRange == 0) User_AlertRange = 384; //Default range in which marines alert each other of enemies. - If (User_EnemyAlertHearingRange == 0) User_EnemyAlertHearingRange = 1024; //Default range in which marines can hear the alerts of enemy marines. - If (User_DodgeRange == 0) User_DodgeRange = 384; //Default range in which marines dodge projectiles. - If (User_MeleeChance == 0) User_MeleeChance = 96; //Default chance for the marine to try running away after hitting something with his rifles' stock. - If (User_TurretThreshold == 0) User_TurretThreshold = 100; //Default chance for the marine to use a turret. - If (User_FearDistance == 0) User_FearDistance = 2048; //The default distance the marines keeps from powerful enemies. - - HandleRandomPersonalityMidGame(); - HandleMarineColoring(); - } - +{ //Handles the coloring of the marines. Void HandleMarineColoring() { @@ -29,24 +10,36 @@ Extend Class SmartMarine //Handles the random color choice that is performed if the color name given is "Random". If (User_Color ~== "Random") { - Static Const String ColorArray[] = {"Red","Gray","White","Black","Blue","Yellow","Orange","Pink","DarkGreen","Default"}; //Contains all the valid color names. + Static Const String ColorArray[] = {"Red","Gray","White","Black","Blue","Yellow","Orange","Pink","DarkGreen","DarkRed","Default"}; //Contains all the valid color names. - User_Color = ColorArray[Random(0,9)]; //Randomly picks a color string. + User_Color = ColorArray[Random(0,ColorArray.Size()-1)]; //Randomly picks a color string. } A_SetTranslation (String.Format("Marine_%s", User_Color)); } + Void SetUserVariableDefaults() + { + //The //$UserDefaultValue editor key is buggy, so this code still needs to be used. + If (User_SearchTime == 0) User_SearchTime = 500; //The amount of time the marine tries to chase it's target, after it has died or gone out of sight. + If (User_GrenadeThreshold == 0) User_GrenadeThreshold = 240; //Default threshold that needs to be reached or surpassed, for the marine to throw a grenade. + If (User_RetreatAttempts == 0) User_RetreatAttempts = 10; //Default amount of attempts the marine makes to run for cover to reload. + If (User_AlertRange == 0) User_AlertRange = 384; //Default range in which marines alert each other of enemies. + If (User_EnemyAlertHearingRange == 0) User_EnemyAlertHearingRange = 1024; //Default range in which marines can hear the alerts of enemy marines. + If (User_DodgeRange == 0) User_DodgeRange = 384; //Default range in which marines dodge projectiles. + If (User_TurretThreshold == 0) User_TurretThreshold = 100; //Default chance for the marine to use a turret. + If (User_FearDistance == 0) User_FearDistance = 2048; //The default distance the marines keeps from powerful enemies. + } + Void HandleRandomPersonalityMidGame() { If (User_RandomPersonality) { - If (!User_SearchTime) User_SearchTime = 300*FRandom (-0.6,3.0); + If (!User_SearchTime) User_SearchTime = 500*FRandom (-0.6,3.0); If (!User_GrenadeThreshold) User_GrenadeThreshold = 240*FRandom (-0.75,2.0); If (!User_RetreatAttempts) User_RetreatAttempts = Random (7,15); If (!User_FearDistance) User_FearDistance = 2048*FRandom (0.75,2.0); If (!User_TurretThreshold) User_TurretThreshold = 100*Frandom (0.5,1.5); - If (!User_MeleeChance) User_MeleeChance = 96*FRandom (0.5,2.5); If (!User_Color || User_Color ~== "") User_Color = "Random"; User_RandomPersonality = False; } @@ -56,7 +49,6 @@ Extend Class SmartMarine Int User_GrenadeThreshold; Int User_RetreatAttempts; Int User_TurretThreshold; - UInt8 User_MeleeChance; //This is a UInt8 because it's plugged directly into A_Jump calls, which already don't support values beyond 0-255. Double User_AlertRange; Double User_EnemyAlertHearingRange; Double User_FearDistance; @@ -67,5 +59,6 @@ Extend Class SmartMarine Bool User_RandomPersonality; Bool User_NoRifleDrop; String User_Color; - + String User_SquadName; + Bool User_Leader; } \ No newline at end of file diff --git a/SNDINFO b/SNDINFO index e038676..894f9a0 100644 --- a/SNDINFO +++ b/SNDINFO @@ -3,9 +3,11 @@ Marine/Death1 DSPLDETH Marine/Death2 DSPDIEHI Marine/XDeath DSSLOP Marine/Gone "Sounds/Vanish.ogg" //Gone, reduced to atoms. +Marine/GoneAlt "Sounds/AltVanish.ogg" //🏱︎☟︎⚐︎❄︎⚐︎☠︎ ☼︎☜︎✌︎👎︎✋︎☠︎☝︎💧︎ ☠︎☜︎☝︎✌︎❄︎✋︎✞︎☜︎ Marine/Pain DSPLPAIN Marine/AlertAllies "Sounds/Alert.ogg" -Marine/Throw DSNOWAY //https://www.youtube.com/watch?v=UhVHp1cFTTc +Marine/AlertAlliesAlt "Sounds/AltAlert.ogg" +Marine/Throw DSNOWAY //https://www.youtube.com/watch?v=UhVHp1cFTTc Marine/Wahoo "Sounds/AltJump.ogg" //Super Doomguy Bros Marine/Fire "Sounds/RifleFire.ogg" Marine/Reload "Sounds/RifleReload.ogg" diff --git a/Sounds/AltAlert.ogg b/Sounds/AltAlert.ogg new file mode 100644 index 0000000000000000000000000000000000000000..cd13b2f3899c51433f65edd819617381fff76473 GIT binary patch literal 15337 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 UMBv!ppSg9$!e%%4@^9&X0AMfHApigX literal 0 HcmV?d00001 diff --git a/Sounds/AltVanish.ogg b/Sounds/AltVanish.ogg new file mode 100644 index 0000000000000000000000000000000000000000..188d20136188929a8033ca737f0d0ce80d5cbfbd GIT binary patch literal 12896 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 I_EPx%7c$ocP5=M^ literal 0 HcmV?d00001 diff --git a/Sprites/MARCE0.png b/Sprites/MARCE0.png new file mode 100644 index 0000000000000000000000000000000000000000..81d34dbc97e99f570741d5127c2dc520c73a62f8 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^QXn=HGmt#>eOeum;z%!YOacjlFw5MlSwNaIz$e5N zNdN!;A4nD-Yc2#*j3q&S!3+-1ZlnP@-kvUwAr*1S2~h_b*n9*9qO?~r`snkQkOEon+2bn^0wOc54@B3Lit; X9=Xf8Qj4Yljb-q3^>bP0l+XkK_$D_N literal 0 HcmV?d00001 diff --git a/Sprites/MARCF0.png b/Sprites/MARCF0.png new file mode 100644 index 0000000000000000000000000000000000000000..fda3cf1904fc9cfe7c75b4a419a256a1d4759cbd GIT binary patch literal 247 zcmeAS@N?(olHy`uVBq!ia0vp^N+8U{3?$t(9|r;{j`SkOBp@valHiJ&v;jzS2Ka=y z0_p$%{{zY5W6gy?im@cfFPOpM*^M+Hr^D05F{C1{_Pin2VFRAlhwa;c$w@x?eS*bb zuG#;g_m0hC)5Dgp*}$U9`^8uOkAgtSL(X`Mf0|AsNh;1SIi~p^FkU^$J!9L8nbXZe|G!wy7y5Tn?94LNfVzu`S%o_!iZ!40?On59 kTHw7m&x4Iw;i?aKTeU2|SRFUf2fB&D)78&qol`;+0Qad_G5`Po literal 0 HcmV?d00001 diff --git a/Sprites/MARCZ0.png b/Sprites/MARCZ0.png index 0f6803d512393aea8199aa8f0965f4202d9d1131..3936bcb51d895d8ad87d95f18b038ba31d819d98 100644 GIT binary patch delta 629 zcmV-*0*d{T2$BepPaa24GdFW9KZ7qLBO|Yuf1ihMn`1k_r;lGlBQIkkk!?JYuO0?Z zPfweFYmv=A1c&ncMUxN#kAKe=wlg?a!~H8BosSmY_)G;0QNp!j+aAPLz+zRxsNSfz z24lxt$WSU*!p7CZ&5upX8#tcB0^?x8f|anoy?K7V*?Q}NJq`@>Vlt8TmgP!#Uf=L~ zjyGEW`fix!eDd<8YIDJ?Ws8TyZ;P`B$Mc-g-6Ml?z`necaMvxDt$(uO2x@tQySvAT ztpej>KAFtt`wo~D)!`FuiwL+LJYChJ2qAEWIiFvcI|U=lvcN4Ho6WXSe+`FEkriQt z7{;&E*l|lpt+rfRmzS5V=r!UQn@t1^x$RCDF}RFCzHB%W3r?g7U#r)#ARJ=MP_NUz z1=vRnA#E25-W1`Y(|-ooY;xG?xWw&sT(|2F5!S0*5x?Jcd+j1Mo?bWXcDwz)Kh$Y> z*a74cnrq+Q3h0us@B70cisR(4gIo*h_8(u6hXRX&Q!9=g=_k?|)Y-hSZKR6zP=eF(IO2 z0F2=sR|#Pkq3JB8lq4JiLlTo6-$p`cloFC44jIZcwSzVhz%bNx9b*BrER&oB5p;zK zrFuvcoaY&bi5|{MR_QQ8;I1eQ(`1VCEK4}lX{dC-rsJ_q^(4x(BuV0;V(BX36a!m~ z?K9CPFnBl);~jkluD0iX+V%|2Q_nMTB9wBtw^c4Ug-e*tLPGSDxC2HMfp>em??q^Q P00000NkvXXu0mjfws#Zx<9W{L?vX(`U|(KJxa-#IR)1M>1hu@u-Q8ov zHo9@KoXwWYeFw~n>hOssMFiXqp04Uqgb+BxTrMxnoq~~NS>V=U@}CA0W)+aJ2~^A z@B0FZVy0>6oA)?O7pp~@a;TeM&FgFZUG)$O(liWdz@cH@-+ym545=MsC<-VIVnRek z9T>wqt`fp7LeoV`DM>g4h9o9CzKw*?C?zC895R$?Y6op1fMFN}0mcGmStdCNBIpVe zN`sIlIL|WNs`1x#nM&6IR>_x z+GEisFnBl);~jkluD0iX+V%p@GsrV>B9wBtw^c4Uhf7#2LPCO*xC2HMMW}nKD@D3N P00000NkvXXu0mjfrs5>T diff --git a/TRNSLATE b/TRNSLATE index 219a623..b25c6f2 100644 --- a/TRNSLATE +++ b/TRNSLATE @@ -9,6 +9,7 @@ Marine_Yellow = "112:127=%[0.00,0.00,0.00]:[1.77,1.77,0.00]" Marine_Orange = "112:127=%[0.00,0.00,0.00]:[2.00,1.01,0.00]" Marine_Pink = "112:127=%[0.00,0.00,0.00]:[2.00,1.01,1.51]" Marine_DarkGreen = "112:127=%[0.00,0.00,0.00]:[0.00,1.01,0.50]" +Marine_DarkRed = "112:127=#[192,0,0]" Marine_Default = "0:0=0:0" //Small liquid splash colors. diff --git a/ZScript.zsc b/ZScript.zsc index be1c531..89df474 100644 --- a/ZScript.zsc +++ b/ZScript.zsc @@ -7,6 +7,7 @@ Version "4.8.0" #Include "Marine_OtherActors.zsc" //Other actors related to the marine. Like the grenades and empty magazines. #Include "Marine_Turret.zsc" //Handles the code for the marines' turret, and their turret states. #Include "Marine_UserVariables.zsc" //Has the code related to the marines' user variables. +#Include "Marine_Squads.zsc" //Has the code that handles the marines' squad mechanics. //To do: /*HATE. LET ME TELL YOU HOW MUCH I'VE COME TO HATE YOU SINCE I BEGAN TO WORK ON YOU. @@ -61,15 +62,32 @@ Class SmartMarine : Actor +AvoidHazards; //This ONLY works for crushing ceilings and NOTHING ELSE. So it's largely useless but I'm still adding it. +SeeFriendlyMonsters; //No more ZScript code hacks. } + + Override Void PostBeginPlay() + { + Super.PostBeginPlay(); + + InitializeSquad(); + SetUserVariableDefaults(); + HandleRandomPersonalityMidGame(); + HandleMarineColoring(); + } Override Void Tick() { Super.Tick(); - //Don't run the below code if frozen in time or dead. If (IsFrozen() || Health <= 0) Return; + //This is done so marines can join squads right after the map starts. + If (!User_Leader && GetAge() == 2) + SM_FindSquadmates(); + + //Rougly every second. Look for a squad to join if you currently have no master, and are not a squad leader yourself. + If (!Master && !(Squad && Squad.Leader == Self) && GetAge() % TICRATE/FRandom(0.2,-0.2) == 0) + SM_FindSquadmates(); + If (CrouchDelay > 0) CrouchDelay--; If (GrenadeDelay > 0) GrenadeDelay--; If (DodgeDelay > 0) DodgeDelay--; @@ -100,7 +118,22 @@ Class SmartMarine : Actor { Super.Used (User); - If (User_DisobeyCommands || Goal) Return False; //Ignore all this when patrolling. Or if it is disabled. + //Don't follow any player commands if in a squad. Unless you are the leader. + If (Master && SmartMarine(Master).Squad) + Return False; + + //Trying to command an enemy marine just makes them attack you. + If (!bFriendly && !bDormant && IsHostile (User) && SM_CanBeTargeted(User) && (SM_IsInState ("Spawn") || SM_IsInState ("Idle") || SM_IsInState ("StandStill"))) //If you are hostile and not dormant, and a player tried using you while in your Spawn, Idle, or StandStill state. + { + Target = LastHeard = User; + + SM_AlertNearbyMarines (User_AlertRange,True); + SM_AlertSquadmates(); + SetStateLabel ("See"); + Return True; + } + + If (User_DisobeyCommands || Goal || Master) Return False; //Ignore all this when patrolling, if it is disabled. Or if you are in a squad. If (OnTurret && IsFriend (User) && !Target) //If you are on a turret, your user was friendly, and you have no target currently. { @@ -118,12 +151,15 @@ Class SmartMarine : Actor //Increment the different orders by one. And loop around to standing still if it exceeds the maximum number of possible orders. CurrentMarineOrder += 1; - If (CurrentMarineOrder > 2) - CurrentMarineOrder = 0; + If (CurrentMarineOrder > 3) + CurrentMarineOrder = 0; //Ordered to wander around randomly. If (CurrentMarineOrder == ORDER_WANDER) { + //Tell the other marines in your squad to no longer stay still instead of wandering. + If (Squad && Squad.Leader == Self) + Squad.StandDownSquad(Self,True); Spawn ("SmartMarineWanderingMessage",(Pos.X,Pos.Y,Pos.Z+Height+4)); A_StartSound ("Misc/Chat",CHAN_VOICE,CHANF_OVERLAP,0.25,ATTN_STATIC); SetStateLabel ("Idle"); //Then go to the Idle state. @@ -144,6 +180,9 @@ Class SmartMarine : Actor } Else If (CurrentMarineOrder == ORDER_STANDSTILL) { + //If you are a squad leader, order the rest of the squad to behave the same. + If (Squad && Squad.Leader == Self) + Squad.StandDownSquad(Self); Spawn ("SmartMarineStandingMessage",(Pos.X,Pos.Y,Pos.Z+Height+4)); A_StartSound ("Misc/Chat",CHAN_VOICE,CHANF_OVERLAP,0.25,ATTN_STATIC); TimeSearching = 0; //Reset the time the marine has spent searching for targets. In case the marine was used while in the See state. @@ -151,16 +190,7 @@ Class SmartMarine : Actor Return True; } } - - If (!bFriendly && !bDormant && IsHostile (User) && SM_CanBeTargeted(User) && (SM_IsInState ("Spawn") || SM_IsInState ("Idle") || SM_IsInState ("StandStill"))) //If you are hostile and not dormant, and a player tried using you while in your Spawn, Idle, or StandStill state. - { - Target = LastHeard = User; - - SM_AlertNearbyMarines (User_AlertRange,True); //And alert all marines around you of the player. - SetStateLabel ("See"); - Return True; - } - + Return False; } @@ -177,12 +207,17 @@ Class SmartMarine : Actor Override Bool OkayToSwitchTarget(Actor Other) { If ((Other.bIsMonster || Other.Player) && IsHostile(Other) && Other.Health > 0) + { SM_AlertNearbyMarines(User_AlertRange,True); + SM_AlertSquadmates(); + } Return Super.OkayToSwitchTarget(Other); } Mixin MarineFunctions; + SM_MarineSquad Squad; //A reference to the squad the marine is in. + Actor Icon; //The icon that appears above the marine when he is in a squad. Actor NearbyProjectile; //Stores a pointer to the first projectile the marine finds near him. Actor PreviousProjectile; //The previous nearby projectile the marine had found. Actor OriginalTarget; //Temporarily stores the marines' target while running from a grenade. @@ -217,9 +252,10 @@ Class SmartMarine : Actor Int CurrentMarineOrder; Enum MarineOrders { - ORDER_STANDSTILL = 0, - ORDER_WANDER = 1, - ORDER_FOLLOW = 2 + ORDER_NONE, + ORDER_FOLLOW, + ORDER_WANDER, + ORDER_STANDSTILL }; States @@ -230,6 +266,13 @@ Class SmartMarine : Actor If (SM_FindNearbyGrenade()) Return ResolveState ("RunAway"); + //If you are in a squad, and your leader is too far away from you, start following him. + If (Master && SmartMarine(Master).Squad && Distance3DSquared (Master) >= 512*512 && CurrentMarineOrder != ORDER_STANDSTILL) + Return ResolveState ("Idle"); + + If (Master && SmartMarine(Master).Squad && SmartMarine(Master).CurrentMarineOrder != ORDER_STANDSTILL) + CurrentMarineOrder = ORDER_NONE; + bStandStill = True; //Prevents friendly marines from moving because they heard a friendly player. A_LookEx (0,0,8192,4096,160,"AlertOtherMarines"); bStandStill = False; @@ -337,7 +380,7 @@ Class SmartMarine : Actor Return ResolveState ("Crouch"); SM_ShouldBeScared(); - A_Chase ("RifleSmack","DecideAttack",CHF_DONTIDLE); + SM_Chase(); Return State (Null); } @@ -381,7 +424,7 @@ Class SmartMarine : Actor } } - A_Wander(); + SM_Wander(); A_LookEx (0,0,8192,4096,160,"AlertOtherMarines"); SM_ListenForEnemyAlerts(User_EnemyAlertHearingRange); @@ -399,6 +442,7 @@ Class SmartMarine : Actor Loop; AlertOtherMarines: //Alerts other marines of the target that the marine just acquired after calling A_LookEx. TNT1 A 0 SM_AlertNearbyMarines(User_AlertRange); + TNT1 A 0 SM_AlertSquadmates(); TNT1 A 0 A_JumpIf (OnTurret,"TurretSee"); TNT1 A 0 A_JumpIf (Crouching,"Crouch"); Goto See; @@ -423,7 +467,7 @@ Class SmartMarine : Actor TNT1 A 0 {Maneuvering = False;} TNT1 A 0 { - If (CheckMeleeRange() && Random[pr_cajump](0,255) < User_MeleeChance) Return ResolveState ("RifleSmack"); + If (CheckMeleeRange() && Random[pr_cajump](0,255) < 96) Return ResolveState ("RifleSmack"); If (Random[pr_cajump](0,255) < 32) Return ResolveState ("DecideAttack"); Return State (Null); } @@ -770,11 +814,11 @@ Class SmartMarine : Actor MARM A 3; TNT1 A 0 A_JumpIf (CountInv("PowerStrength") > 0,3); MARM B 8 A_CustomMeleeAttack (Random(4,8),"Marine/Melee","Marine/MeleeMiss",'Melee',False); //Blunt force attack, so no blood. - MARM A 0 A_Jump (User_MeleeChance,"ChangePosition"); //If you are in melee range of your enemy, you are probably fucked. + MARM A 0 A_Jump (96,"ChangePosition"); //If you are in melee range of your enemy, you are probably fucked. Goto See; MARM B 6 A_CustomMeleeAttack (Random(16,32),"Marine/PowerMelee","Marine/MeleeMiss",'Melee'); //Powerful blunt force attack, so YES blood. - MARM A 0 A_Jump (User_MeleeChance/2,"ChangePosition"); //If you are in melee range of your enemy with a berserk pack, you are probably not THAT fucked. + MARM A 0 A_Jump (96/2,"ChangePosition"); //If you are in melee range of your enemy with a berserk pack, you are probably not THAT fucked. Goto See; RifleBurst: MAR2 A 0 A_JumpIf (AmmoUsed > 19,"RunForReload"); //Run out of sight of the target to reload. diff --git a/readme.md b/readme.md index 2546874..99dbc9f 100644 --- a/readme.md +++ b/readme.md @@ -15,6 +15,7 @@ Smart marines are NPCs for Doom that are far more intelligent than vanilla monst - Run away from the grenades they throw, if those grenades are about to explode. - Intelligently dodge enemy or extremely harmful projectiles heading their way. More information can be found inside Document.txt - Dynamically take cover behind sufficiently tall level geometry or shootable actors when attacking enemies. More information can be found inside Document.txt +- Can be set up into squads in which they stick together and follow a designated leader. And can change leaders if the previous one died. More information can be found inside Document.txt - They have multiple death animations that can play randomly. - Reload their rifles for every 20 rounds they fire in total. - They first try a few times to run away from their target until out of sight, to not reload while vulnerable. @@ -36,6 +37,7 @@ Smart marines are NPCs for Doom that are far more intelligent than vanilla monst - Orange - Pink - Dark Green + - Dark Red - Random (Randomly pick a color.) - Default (Use the default shade of green. Similar to just leaving User_Color empty.) - Most of the marines' behaviour can be configured on a per actor basis using user variables.