From 0b51c77dad9ed0f1d7d9635596dd0b849b62a27f Mon Sep 17 00:00:00 2001 From: LTS-FFXIV <127939494+LTS-FFXIV@users.noreply.github.com> Date: Sat, 8 Feb 2025 21:36:14 -0600 Subject: [PATCH 1/3] Add BioMove option, update logic, and enhance debug UI Added a new configuration option `BioMove` to the `MCH_Default` and `MCH_HighEnd` classes, allowing Bioblaster use while moving. Updated logic to check `BioMove` setting before using Bioblaster, ensuring it can be used if `BioMove` is enabled or if the player is not moving. Updated subproject commit reference in `ECommons` to `a1df6434ddac33eab306b3f4f605f59f0d0a7279`. Enhanced `RotationConfigWindow` to display stack count of statuses. --- BasicRotations/Ranged/MCH_Default.cs | 5 ++++- BasicRotations/Ranged/MCH_HighEnd.cs | 5 ++++- ECommons | 2 +- RotationSolver/UI/RotationConfigWindow.cs | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/BasicRotations/Ranged/MCH_Default.cs b/BasicRotations/Ranged/MCH_Default.cs index 343d8391e..f221afdb8 100644 --- a/BasicRotations/Ranged/MCH_Default.cs +++ b/BasicRotations/Ranged/MCH_Default.cs @@ -26,6 +26,9 @@ public sealed class MCH_Default : MachinistRotation [RotationConfig(CombatType.PvE, Name = "Prevent the use of defense abilties during hypercharge burst")] private bool BurstDefense { get; set; } = false; + + [RotationConfig(CombatType.PvE, Name = "Use Bioblaster while moving")] + private bool BioMove { get; set; } = true; #endregion #region Countdown logic @@ -131,7 +134,7 @@ protected override bool GeneralGCD(out IAction? act) if (HeatBlastPvE.CanUse(out act)) return true; // drill's aoe version - if (BioblasterPvE.CanUse(out act, usedUp: true)) return true; + if ((BioMove || (!IsMoving && !BioMove)) && BioblasterPvE.CanUse(out act, usedUp: true)) return true; // single target --- need to update this strange condition writing!!! if (!SpreadShotPvE.CanUse(out _)) diff --git a/BasicRotations/Ranged/MCH_HighEnd.cs b/BasicRotations/Ranged/MCH_HighEnd.cs index 15e5a142c..db5e3e62e 100644 --- a/BasicRotations/Ranged/MCH_HighEnd.cs +++ b/BasicRotations/Ranged/MCH_HighEnd.cs @@ -14,6 +14,9 @@ public sealed class MCH_HighEnd : MachinistRotation [RotationConfig(CombatType.PvE, Name = "Use burst medicine midfight when Air Anchor, Barrel Stabilizer, and Wildfire are about to come off cooldown")] private bool MidfightBurstMeds { get; set; } = false; + + [RotationConfig(CombatType.PvE, Name = "Use Bioblaster while moving")] + private bool BioMove { get; set; } = true; #endregion private const float HYPERCHARGE_DURATION = 8f; @@ -147,7 +150,7 @@ protected override bool GeneralGCD(out IAction? act) if (HeatBlastPvE.CanUse(out act)) return true; // drill's aoe version - if (BioblasterPvE.CanUse(out act, usedUp: true)) return true; + if ((BioMove || (!IsMoving && !BioMove)) && BioblasterPvE.CanUse(out act, usedUp: true)) return true; // single target --- need to update this strange condition writing!!! if (!SpreadShotPvE.CanUse(out _)) diff --git a/ECommons b/ECommons index 3d56bab99..a1df6434d 160000 --- a/ECommons +++ b/ECommons @@ -1 +1 @@ -Subproject commit 3d56bab99a34e0af322015c74600a6e95fd68766 +Subproject commit a1df6434ddac33eab306b3f4f605f59f0d0a7279 diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index bc3843860..f6216ad77 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -2814,7 +2814,9 @@ private static unsafe void DrawStatus() foreach (var status in Player.Object.StatusList) { var source = status.SourceId == Player.Object.GameObjectId ? "You" : Svc.Objects.SearchById(status.SourceId) == null ? "None" : "Others"; - ImGui.Text($"{status.GameData.Value.Name}: {status.StatusId} From: {source}"); + byte stacks = Player.Object.StatusStack(true, (StatusID)status.StatusId); + string stackDisplay = stacks == byte.MaxValue ? "N/A" : stacks.ToString(); // Convert 255 to "N/A" + ImGui.Text($"{status.GameData.Value.Name}: {status.StatusId} From: {source} Stacks: {stackDisplay}"); } } From 86bd2569ae7c3b08728c7d625fa91138dec9351a Mon Sep 17 00:00:00 2001 From: Crito Date: Thu, 13 Feb 2025 17:40:41 -0600 Subject: [PATCH 2/3] add L1 --- new_docs/RSRDemystified.md | 171 +++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 new_docs/RSRDemystified.md diff --git a/new_docs/RSRDemystified.md b/new_docs/RSRDemystified.md new file mode 100644 index 000000000..056676ecb --- /dev/null +++ b/new_docs/RSRDemystified.md @@ -0,0 +1,171 @@ +# A Lecture on How RSR Does What It Does, Demystification +## Rotation Solver Reborn (RSR) in Brief, Purpose + +Hopefully you know that Rotation Solver Reborn (RSR) is a Dalamud plugin for FFXIV that assists users in performing rotations. By default, the plugin makes a best effort attempt to provide an optimal action choice for the user, but also provides the ability to be shown the action instead of performing it itself. How RSR makes this determination is the core purpose of this document. This may assist you in understanding why RSR does what it does. I will only be using pseudocode examples throughout this document, and trivializing some portions of the program for the sake of brevity and neccessity. Any asterisks you see are indications of such leaps. + +## RSR is Not Smart + +If you do not have a strong understanding of programming - or, if you are lost in the current AI wave - then you might be under the impression that RSR is somehow making strategic decisions (considering all known and *quid sit* quantites to come to a determination that will benefit the user) when in reality it is making prescriptive choices. It does not decide anything. Let's get into the anatomy of that which is the primary concern of the average reader: the rotation. + +During this section I will be building out a diagram of RSR's "brain" using the Default Machinist Rotation as my running example. + +## The Anatomy of a Rotation +It is first worth getting out into the open that, in the simplest of terms, RSR does what it does because a human told it too. It does these things based off the following: + +### The GCD / Ability Loop +As a FFXIV player you should understand that there are two kinds of actions: spells/weaponskills (GCDs) and abilities (oGCDs). It should be further understood that putting oGCDs inbetween GCDs is called "Weaving." You generally do not want to use more than 2 oGCDs per GCD. Let's piece together the first layer of our RSR brain diagram. + +```mermaid +stateDiagram-v2 + GCD --> oGCD1 + oGCD1 --> oGCD2 + oGCD1 --> GCD + oGCD2 --> GCD +``` + +RSR harshly maintains this cadence through priority of choice, but we'll cover that later. It does not want you "Triple weaving" and god forbid you truly succeed (enjoy your flag.) + +Let me now include some pretty word-y "conditional phrases." I will also include the assumed "not currently in combat" or "nothing to do" state indicators, but will omit them after this diagram. + +```mermaid +stateDiagram-v2 + [*] --> GCD : If I have a target, and my GCD is not cooling down, use... + GCD --> oGCD1 : While my global GCD is cooling down, if I can use an oGCD, use... + oGCD1 --> oGCD2 : While my global GCD is cooling down, if I can use an oGCD, use... + oGCD1 --> GCD : If I can't use an oGCD, use... + oGCD2 --> GCD : I "must" use a GCD, use... + GCD --> [*] : If I don't currently have any valid actions, do nothing. +``` + +Let's populate this with some example Machinist actions, including some conditional phrasing. + +```mermaid +stateDiagram-v2 + GCD : Split Shot + oGCD1 : Ricochet + oGCD2 : Gauss Round + GCD --> oGCD1 : If I have a charge of Richochet, use... + oGCD1 --> oGCD2 : If I have a charge of Gauss Round, use... + oGCD1 --> GCD + oGCD2 --> GCD +``` + +This example would be a completely valid rotation. Not a good one, but a completely valid one. We have told, in code, RSR to use Split Shot and while it is cooling down use two iconic Machinist oGCDs. You check each conditional statement in order, from top to bottom, returning to the top whenever you use an action of any kind. Let's assume that return to the top going forward, and the oGCD / GCD ratio "restriction", so our diagram now looks like this. + +```mermaid +stateDiagram-v2 + GCD : Split Shot + oGCD1 : Ricochet + oGCD2 : Gauss Round + GCD --> oGCD1 : If I have a charge of Richochet, use... + oGCD1 --> oGCD2 : If I have a charge of Gauss Round, use... +``` + +### The Priority of Choice +Let's get straight into some pseudocode for this part. + +``` +EmergencyAbility{ + +} + +GeneralGCD{ + +} + +AttackAbility{ + +} +``` +I've laid out the above pseudocode in the way that RSR manages things behind the scenes (what we in the business call the DataCenter), though how any rotation is laid out doesn't actually matter function to function. GeneralGCD could be at the end, etc., RSR doesn't care. + +The three sections I've displayed here (there are many more) are: + +1. Emergency Ability: These always fire off, with utmost priority, if they can be used. +2. GeneralGCD: If you can use a GCD, you will use something from here. +3. AttackAbility: If your GCD is cooling down, you will use one of these attack oGCDs. + +Here is what our diagram looks like in the new pseudocode, I'll also include Machinist's Reassemble and dress up the code a little: + +``` +MCH_Default : MachinistRotation { + EmergencyAbility{ + if can use Reassemble: use Reassemble + } + + GeneralGCD{ + if can use SplitShot: use SplitShot + } + + AttackAbility{ + if can use Ricochet: use Ricochet + if can use GaussRound: use GaussRound + } +} +``` + +Again, quite simple. A human could read down this list and follow these orders. If you can press Reassemble, press it. If you can't, press SplitShit. If you can't press SplitShot (say, because it's cool down), press Ricochet. If you can't press Ricochet, press GaussRound. If at any time you succeed at pressing one of the buttons, return to the top. + +That means our Machinist, as written, would use Reassmble twice (wasting one charge) then use SplitShot. Skip Reassemble (no charges), skip SplitShot(GCD is cooling down), and use Ricochet. Right back down the list, and use Ricochet again. Then SplitShot, then Ricochet, then GaussRound because it skipped over the still cooling down Reassemble, SplitShot, and out of charges Ricochet. + +Not a great rotation. However, one of the final missing pieces is the intricate web of logic that goes into a much more ffective rotation. Everything before this point should be enough to demystify how RSR works from an end user rotational point of view. There is no magic, there is no AI at work. It is simply a recipe of can I do it, and if not, what can I do next instead? + +### Logic and Conditionals +What gives the facade of a higher order of intelligence over a simple recipe is the amount of work the rotation author does or does not put into the rotation. A rotation that will clear normal, simple content could be quite normal and simple. A rotation that performs at a level useful in EX+ is much more complex. Consider the following: + +``` +MCH_Default : MachinistRotation { + EmergencyAbility{ + if can use Reassemble AND: + 1. You don't have Reassemble buff currently active. + 2. Reassemble has more than 0 charges. + 3. The next GCD you could use is either ChainSaw or Excavator + OR + 1. You don't have the level for ChainSaw, but you'd hit a lot of targets with your shotgun. + OR + 1. The next action isn't AirAnchor + OR + 1. You're not high enough level for ChainSaw + 2. Your next GCD would be Drill + OR + 1. You're not high enough level for Drill. + 2. Your next GCD would be CleanShot + OR + 1. You're not high enough level for CleanShot + 2. Your next GCD isn't HotShot + : use Reassemble + } + + GeneralGCD{ + if can use AutoCrossbow: use AutoCrossbow + if can use HeatBlast: use HeatBlast + if can use Bioblaster: use Bioblaster + + if can use SpreadShot: use SpreadShot + + if can use CleanShot: use CleanShot + if can use SlugShot: use SlugShot + if can use SplitShot: use SplitShot + } + + AttackAbility{ + if the player has the WildFire buff: use Hypercharge + if can use Ricochet and GaussRound has been cooling down for less time than Ricochet: use Ricochet + if can use GaussRound: use GaussRound + } +} +``` + +You can see from the brief expasnion of pseudocode above that there is a mixture of limited, simple, and multifaceted logic. This example here being a greatly parred down MCH rotation from what is live. Such rotations and logic are built iteratively and based off the experience or research of the author. This also doesn't include the settings that live within the rotation file either. + +### A note on the Interface versus the Mechanism +When it comes to writing rotations the author will generally be interacting with something that can be referred to as an Interface. Not to be confused with a UI; an Interface in programming is a layer intended to be used by another programmer for purposes of extending the capabilities of that program. The rotations that you can find in #3rd-party-rotations in the Discord are examples of authors producing rotation files that use this Interface. It standardizes what the author can write and interact with. It also remvoes the need for the author to build the entire plugin from source. + +What is my point here? You see in the pseudocode above where it says `MCH_Default : MachinistRotation`? This is telling us that MCH_Default is our rotation - our interaction with the Interface - and it is sitting over the MachinistRotation "Mechanism." Which the moment you cross that colon into the Mechanism you get into the guts of the RSR machine. Things like the targetting system, the conditional system, all the little lights and switches you can flick throughout RSR. This is the beast that is contended with when a question outside of things like "why does the dragoon dot wear off?" go. + +## Final Thoughts +If you made it this far I want to thank you for taking the time to read, or at least skim through the content I've written here. As always this document is only as good as it is young, so there will be drift in time. Further, espcially there at the end, I hope it inspires you to consider writing rotation content for RSR. While it may seem pretty big at first I gaurantee you rotation writing is only as complex as you make it, and potentially a good way to get into coding if you haven't before. I might even produce a series on how to get started some day. Or I might not, that's the beauty of RSR. + +Take care and remember someone has to press all these buttons, + +Crito and the RSR Team \ No newline at end of file From 78321ce5400cdedcda6b11ed1e791c8afb8b323e Mon Sep 17 00:00:00 2001 From: Crito Date: Thu, 13 Feb 2025 17:44:13 -0600 Subject: [PATCH 3/3] spelling, grammar i forgot to hit save --- new_docs/RSRDemystified.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/new_docs/RSRDemystified.md b/new_docs/RSRDemystified.md index 056676ecb..02607e25c 100644 --- a/new_docs/RSRDemystified.md +++ b/new_docs/RSRDemystified.md @@ -1,19 +1,19 @@ -# A Lecture on How RSR Does What It Does, Demystification +# A Lecture on How RSR Does What It Does: Demystified ## Rotation Solver Reborn (RSR) in Brief, Purpose -Hopefully you know that Rotation Solver Reborn (RSR) is a Dalamud plugin for FFXIV that assists users in performing rotations. By default, the plugin makes a best effort attempt to provide an optimal action choice for the user, but also provides the ability to be shown the action instead of performing it itself. How RSR makes this determination is the core purpose of this document. This may assist you in understanding why RSR does what it does. I will only be using pseudocode examples throughout this document, and trivializing some portions of the program for the sake of brevity and neccessity. Any asterisks you see are indications of such leaps. +Hopefully you know that Rotation Solver Reborn (RSR) is a Dalamud plugin for FFXIV that assists users in performing rotations. By default, the plugin makes a best effort attempt to provide an optimal action choice for the user, but also provides the ability to be shown the action instead of performing it itself. How RSR makes this determination is the core purpose of this document. This may assist you in understanding why RSR does what it does. I will only be using pseudocode examples throughout this document, and trivializing some portions of the program for the sake of brevity and necessity. Any asterisks you see are indications of such leaps. ## RSR is Not Smart -If you do not have a strong understanding of programming - or, if you are lost in the current AI wave - then you might be under the impression that RSR is somehow making strategic decisions (considering all known and *quid sit* quantites to come to a determination that will benefit the user) when in reality it is making prescriptive choices. It does not decide anything. Let's get into the anatomy of that which is the primary concern of the average reader: the rotation. +If you do not have a strong understanding of programming - or, if you are lost in the current AI wave - then you might be under the impression that RSR is somehow making strategic decisions (considering all known and *quid sit* quantities to come to a determination that will benefit the user) when in reality it is making prescriptive choices. It does not decide anything. Let's get into the anatomy of that which is the primary concern of the average reader: the rotation. During this section I will be building out a diagram of RSR's "brain" using the Default Machinist Rotation as my running example. ## The Anatomy of a Rotation -It is first worth getting out into the open that, in the simplest of terms, RSR does what it does because a human told it too. It does these things based off the following: +It is first worth getting out into the open that, in the simplest of terms, RSR does what it does because a human told it to. It does these things based off the following: ### The GCD / Ability Loop -As a FFXIV player you should understand that there are two kinds of actions: spells/weaponskills (GCDs) and abilities (oGCDs). It should be further understood that putting oGCDs inbetween GCDs is called "Weaving." You generally do not want to use more than 2 oGCDs per GCD. Let's piece together the first layer of our RSR brain diagram. +As a FFXIV player you should understand that there are two kinds of actions: spells/weaponskills (GCDs) and abilities (oGCDs). It should be further understood that putting oGCDs between GCDs is called "Weaving." You generally do not want to use more than 2 oGCDs per GCD. Let's piece together the first layer of our RSR brain diagram. ```mermaid stateDiagram-v2 @@ -104,11 +104,11 @@ MCH_Default : MachinistRotation { } ``` -Again, quite simple. A human could read down this list and follow these orders. If you can press Reassemble, press it. If you can't, press SplitShit. If you can't press SplitShot (say, because it's cool down), press Ricochet. If you can't press Ricochet, press GaussRound. If at any time you succeed at pressing one of the buttons, return to the top. +Again, quite simple. A human could read down this list and follow these orders. If you can press Reassemble, press it. If you can't, press SplitShot. If you can't press SplitShot (say, because it's cool down), press Ricochet. If you can't press Ricochet, press GaussRound. If at any time you succeed at pressing one of the buttons, return to the top. -That means our Machinist, as written, would use Reassmble twice (wasting one charge) then use SplitShot. Skip Reassemble (no charges), skip SplitShot(GCD is cooling down), and use Ricochet. Right back down the list, and use Ricochet again. Then SplitShot, then Ricochet, then GaussRound because it skipped over the still cooling down Reassemble, SplitShot, and out of charges Ricochet. +That means our Machinist, as written, would use Reassemble twice (wasting one charge) then use SplitShot. Skip Reassemble (no charges), skip SplitShot(GCD is cooling down), and use Ricochet. Right back down the list, and use Ricochet again. Then SplitShot, then Ricochet, then GaussRound because it skipped over the still cooling down Reassemble, SplitShot, and out of charges Ricochet. -Not a great rotation. However, one of the final missing pieces is the intricate web of logic that goes into a much more ffective rotation. Everything before this point should be enough to demystify how RSR works from an end user rotational point of view. There is no magic, there is no AI at work. It is simply a recipe of can I do it, and if not, what can I do next instead? +Not a great rotation. However, one of the final missing pieces is the intricate web of logic that goes into a much more effective rotation. Everything before this point should be enough to demystify how RSR works from an end user rotational point of view. There is no magic, there is no AI at work. It is simply a recipe of can I do it, and if not, what can I do next instead? ### Logic and Conditionals What gives the facade of a higher order of intelligence over a simple recipe is the amount of work the rotation author does or does not put into the rotation. A rotation that will clear normal, simple content could be quite normal and simple. A rotation that performs at a level useful in EX+ is much more complex. Consider the following: @@ -156,15 +156,15 @@ MCH_Default : MachinistRotation { } ``` -You can see from the brief expasnion of pseudocode above that there is a mixture of limited, simple, and multifaceted logic. This example here being a greatly parred down MCH rotation from what is live. Such rotations and logic are built iteratively and based off the experience or research of the author. This also doesn't include the settings that live within the rotation file either. +You can see from the brief expasnion of pseudocode above that there is a mixture of limited, simple, and multifaceted logic. This example here being a greatly pared down MCH rotation from what is live. Such rotations and logic are built iteratively and based off the experience or research of the author. This also doesn't include the settings that live within the rotation file either. ### A note on the Interface versus the Mechanism -When it comes to writing rotations the author will generally be interacting with something that can be referred to as an Interface. Not to be confused with a UI; an Interface in programming is a layer intended to be used by another programmer for purposes of extending the capabilities of that program. The rotations that you can find in #3rd-party-rotations in the Discord are examples of authors producing rotation files that use this Interface. It standardizes what the author can write and interact with. It also remvoes the need for the author to build the entire plugin from source. +When it comes to writing rotations the author will generally be interacting with something that can be referred to as an Interface. Not to be confused with a UI; an Interface in programming is a layer intended to be used by another programmer for purposes of extending the capabilities of that program. The rotations that you can find in #3rd-party-rotations in the Discord are examples of authors producing rotation files that use this Interface. It standardizes what the author can write and interact with. It also removes the need for the author to build the entire plugin from source. What is my point here? You see in the pseudocode above where it says `MCH_Default : MachinistRotation`? This is telling us that MCH_Default is our rotation - our interaction with the Interface - and it is sitting over the MachinistRotation "Mechanism." Which the moment you cross that colon into the Mechanism you get into the guts of the RSR machine. Things like the targetting system, the conditional system, all the little lights and switches you can flick throughout RSR. This is the beast that is contended with when a question outside of things like "why does the dragoon dot wear off?" go. ## Final Thoughts -If you made it this far I want to thank you for taking the time to read, or at least skim through the content I've written here. As always this document is only as good as it is young, so there will be drift in time. Further, espcially there at the end, I hope it inspires you to consider writing rotation content for RSR. While it may seem pretty big at first I gaurantee you rotation writing is only as complex as you make it, and potentially a good way to get into coding if you haven't before. I might even produce a series on how to get started some day. Or I might not, that's the beauty of RSR. +If you made it this far I want to thank you for taking the time to read, or at least skim through the content I've written here. As always this document is only as good as it is young, so there will be drift in time. Further, espcially there at the end, I hope it inspires you to consider writing rotation content for RSR. While it may seem pretty big at first I guarantee you rotation writing is only as complex as you make it, and potentially a good way to get into coding if you haven't before. I might even produce a series on how to get started some day. Or I might not, that's the beauty of RSR. Take care and remember someone has to press all these buttons,