Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track additional characteristics in Scenario #3024

Merged
merged 52 commits into from
Dec 28, 2021

Conversation

AaronGullickson
Copy link
Member

@AaronGullickson AaronGullickson commented Dec 26, 2021

This PR refactors some features out of AtBScenario into Scenario and adds some additional features to Scenario. This PR is tied to the Story Arc PR #2997, and should allow creators to create pre-made scenarios for story arcs. However, it is generalizable enough that it could have other potential uses as well.

Current State

I believe this PR is ready for review. I am going to look and see what I can clean up based on the automated reports, but feel free to give feedback at this point.

Refactoring

The following variables and related methods were re-factored out of AtBScenario to Scenario:

  1. botForces and botForcesStubs. This allows basic scenarios to include pre-loaded OpFor and Allied units. I also borrowed code from AtBGameThread and inserted it into GameThread so that these botForces are actually loaded. At a basic level, this feature can include a fixed set of entities listed in the BotForce object, but I also added an additional class described below that can do randomization of botForces. There is another variable in AtBScenario for allies (alliesPlayer) but I did not refactor this variable because allies can be created in BotForce simply enough by assigning them to team 1 (always the player's team).
  2. Map generation variables, specifically terrainType, mapSizeX, mapSizeY, map, and usingFixedMap. These function identically as they did in AtBScenario.
  3. PlanetaryConditions variables. I also added additional variables for temperature, electro-magnetic variables, and blowing sand. These variables are not currently used by AtBScenario but could be added.
  4. The start variable that gives the starting deployment position of the player's units.

I don't believe any of these changes will affect AtB functioning at all because AtBScenario inherits from Scenario and I made sure to keep methods the same. I did have to give some of the variables protected rather than private access because the initialize method in AtBScenario assigns some of these variables directly.

I also did not need to do anything to ScenarioObjectives. These were already a part of the basic Scenario object and they can be used more or less directly, although I did need to make a few changes to make sure they were checked for in basic scenarios and not just AtB games.

GUI changes

I made a variety of changes to ScenarioViewPanel so that all of these new features in scenario will actually show up to the player. I borrowed quite a bit from AtBScenarioViewPanel but ScenarioViewPanel involves no dynamic buttons just information. I also did a lot of cleanup on the code there to make it more compact and produce a more pleasing display. Here is an example of the new look:

Screen Shot 2021-12-27 at 4 11 29 PM

New Features

BotForceRandomizer

I wanted people to be able to create randomized BotForces as well as fixed ones. I also wanted these to be able to scaled to variable player deployments. I originally thought I could just use or rework the code from ScenarioForceTemplate and AtBDynamicScenarioFactory. However the logic of these objects is quite complex and difficult to separate from the expectations of AtB game play.

So, instead, I created a new class called BotForceRandomizer that can be added to a BotForce as a variable. It is a lightweight object that just tracks some parameters for random generation such as faction, quality, unit type, and skill level. The code for generating units borrows substantially from code in AtBDynamicScenarioFactory but is much, much simpler. Part of the reason for this is that, players/creators can mix fixed unit lists and random forces in BotForces. So rather than try to consider all the very specific cases that AtB needs to consider like artillery, aeros with bombs, dropship support, etc., this class is set up so that creators can specify special stuff like that in fixed units and then just roll for the remaining units.

I currently have two scaling techniques which are specified in a private enum. The first is just BV. The second is an "adjusted weight" scaling. This is basically just the total weight of units, but those weights are "weighted" by unit type. So tank weight only counts for 60% of mech weight and so forth. This is loosely based on some old point based systems used in Battletech products. The idea is that this balances forces based on rough unit type and weight parity but does not adjust for skill level, tech level, etc. Right now the unit type weights used are hard coded but this could be made user editable in the future. It should also be easy based on the way I have this set up to add other scaling methods in the future.

Right now the generator works pretty decently, but there is definitely room for improvement. My initial goal here is just to get the architecture set up and then tinker with the details later based on how it plays.

ScenarioDeploymentLimit

This feature enables limits on what units the player can deploy to a scenario. All of the parameters for this leave in the ScenarioDeploymentLimit class and an instance of this class can optionally be added to Scenario. If it is not present, then limits are not enforced. Limits are enforced in the following way.

  • allowedUnitTypes - This List defines what unit types can be deployed to the scenario. If it is empty, then all unit types are accepted.
  • quantityLimit. This variable places a maximum value on units that can be deployed. That maximum limit is flexible depending on the value of QuantityType and CountType enums in ScenarioDeploymentLimit. QuantityType can be PERCENT or ABSOLUTE. If PERCENT is used then quantityLimit is treated as a percent of the player's valid forces for the scenario. So you could set the limit to be 50% of the player's forces for example. What exactly quantityLimit measures can be changed by CountType which can be BV or UNIT. If BV, then quantity limits are based on BV. If UNIT then the limit is number of units. So, quantityLimit=50, QuantityType=PERCENT, and CountType=UNIT would allow the player to field half of their eligible units to the scenario.
  • requiredPersonnel - this can be used to require certain personnel in the scenario by their UUID
  • requiredUnits - just like requiredPersonnel but for units

Most of these limits are enforced by the canDeployForces and canDeployUnits methods in Scenario, which I refactored from AtBScenario. The methods in AtBScenario are unchanged and just override the super methods. I also added a new Scenario#canStartScenario method that is checked in the GUI to see if all of the scenario buttons should be enabled, which helped to clean up some real messy code. Again AtBScenario has an override method for this that adds one additional condition.

This required adding a scenario variable to the basic GameThread and changing constructors to include it.
…ap settings

I would prefer to handle this by generating an actual MapSettings object in Scenario.java but that would require some XML input and output functions on MapSettings itself.
BotForces are now in basic scenario so this works in all cases
Forgot this in a previous commit
I also want to add EMI and temperature to base Scenario but I will do that in a subsequent commit
This object allows for a simpler method of random generation of bot forces for scenarios. It can be added to any BotForce object and is tracked via XML I/O. It includes variables that parameterize the random generation process to find opfor. These additional random forces are generated and added to the game in GameThread. Currently, I just have a non-interesting test case in the generateForce method, but it should be straightforward to build in the more complex logic needed in subsequent commits.
Instead of BV, this technique tries to balance the weight of units after applying some modifiers based on unit type. So tank weight only counts for 60% of mech weight and so on. These multipliers are currently hard-coded into the static getSimplePointScore method, but could be made customizable at some point.
…Randomizer

If the unit type is set to Mek or Aero and percentConventional is greater than zero then there is some probability that the Mek or Aero will be replaced by a Tank/VTOL or Conventional Fighter, respectively.
Units will be generated in groups of size equal to the variable lanceSize which will help units be a little more consistent. It can however lead to going over the maxPoints pretty substantially when small numbers of units are deployed so may need to tweak how that works.
I also added the apache common math library to build.gradle so that I can use their GammaDistribution function to create variation. The center of this distribution is either a focal weight class provided by the object or the mean weight class of the player units. The gamma distribution then samples around this value and the results are trimmed to plausible values and then rounded to an integer.
…nitialize method in AtBScenario

I am not sure why these variables need to be set in the AtBScenario#initialize method rather than the constructor, but I am not going to mess with it.
@AaronGullickson AaronGullickson self-assigned this Dec 26, 2021
@AaronGullickson AaronGullickson marked this pull request as ready for review December 28, 2021 00:26
Copy link
Contributor

@Windchild292 Windchild292 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general this looks good. However, there's a bunch of comments with changes of varying severity, with the largest and most significant being issues in MekHqXmlUtil usage and File I/O in general. Outside of that the comments are largely related to missing brackets, spacing, other text accessibility, and some light code enhancements.

Also, there's a lot of places in BotForceRandomizer and ScenarioDeploymentLimit that could be converted to use streams to improve their readability, but that's just a personal opinion.

MekHQ/src/mekhq/GameThread.java Outdated Show resolved Hide resolved
MekHQ/src/mekhq/GameThread.java Outdated Show resolved Hide resolved
MekHQ/src/mekhq/GameThread.java Outdated Show resolved Hide resolved
MekHQ/src/mekhq/GameThread.java Outdated Show resolved Hide resolved
MekHQ/src/mekhq/GameThread.java Outdated Show resolved Hide resolved
@AaronGullickson
Copy link
Member Author

Thanks for the very detailed review @Windchild292. I have made all of the requested changes except the two cases of modernizing the XML file I/O. Can you tell me a class that is fully modernized so I can use it as an example?

@AaronGullickson
Copy link
Member Author

Thanks for the very detailed review @Windchild292. I have made all of the requested changes except the two cases of modernizing the XML file I/O. Can you tell me a class that is fully modernized so I can use it as an example?

Never mind I think I fixed this in the latest commit.

Copy link
Contributor

@Windchild292 Windchild292 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few remaining spacing/brackets issues, then this will be good to merge

Comment on lines +278 to +279
MekHqXmlUtil.writeSimpleXMLTag(pw1, indent, "destinationEdge", behaviorSettings.getDestinationEdge().ordinal());
MekHqXmlUtil.writeSimpleXMLTag(pw1, indent, "retreatEdge", behaviorSettings.getRetreatEdge().ordinal());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized these are enums being parsed through their ordinals instead of names...

I've added a note to fix this, as it can cause migration issues, and to move the file I/O for behaviour settings to that file.

(Nothing to do, just commenting on something I noticed and how I'm planning on fixing it)

MekHQ/src/mekhq/campaign/mission/BotForceRandomizer.java Outdated Show resolved Hide resolved
MekHQ/src/mekhq/campaign/mission/BotForceRandomizer.java Outdated Show resolved Hide resolved
MekHQ/src/mekhq/campaign/mission/BotForceRandomizer.java Outdated Show resolved Hide resolved
MekHQ/src/mekhq/campaign/mission/BotForceRandomizer.java Outdated Show resolved Hide resolved
Comment on lines 397 to 401
MekHqXmlUtil.writeSimpleXMLOpenTag(pw1, indent++, "allowedUnitTypes");
for(int type : allowedUnitTypes) {
pw1.println(MekHqXmlUtil.indentStr(indent+2)
+"<allowedUnitType>"
+type
+"</allowedUnitType>");
MekHqXmlUtil.writeSimpleXMLTag(pw1, indent, "allowedUnitType", type);
}
pw1.println(MekHqXmlUtil.indentStr(indent+1) + "</allowedUnitTypes>");
MekHqXmlUtil.writeSimpleXMLCloseTag(pw1, --indent, "allowedUnitTypes");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd normally suggest using the new array writing methods instead of this.

However, this is a magic number we'll need to convert to an enum... and this method is so much easier to convert. Just a comment for future implementations, please don't do it here.

MekHQ/src/mekhq/campaign/mission/Scenario.java Outdated Show resolved Hide resolved
MekHQ/src/mekhq/campaign/mission/Scenario.java Outdated Show resolved Hide resolved
AaronGullickson and others added 3 commits December 28, 2021 15:31
@AaronGullickson
Copy link
Member Author

Ok @Windchild292. Think I got all of them.

@Windchild292
Copy link
Contributor

Approved pending CI (just for safety)

@Windchild292 Windchild292 merged commit 4a26810 into MegaMek:master Dec 28, 2021
@AaronGullickson AaronGullickson deleted the better_scenarios branch January 21, 2022 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants