Jump to content
Naval Games Community

Recommended Posts

Posted (edited)

For @o Barão I did a dive into how ships get autodesigned. I'm posting my writeup as a separate topic here in case it's of use to other modders.

 

A high level overview: In campaign mode, the AI picks a shiptype that doesn't have a design (or needs a new design). It then picks a random hull to use. It then tries multiple times to generate a ship for that hull. This involves setting a random tonnage, beam, and draught, setting base speed and armor and range and bulkheads, and then adding parts (via hull-specific recipes) to the ship. It then updates components from their defaults, and tries to add (or remove) armor/speed/range/bulkheads until the ship is at the tonnage limit. It then verifies the ship is within limits and not too unstable. If it fails, it tries again with that hull. Some notes:

  • The game has a consistent bug where the "casemate" params often apply to guns < 2in in caliber, rather than actually to casemates. For example gun min length; you'd expect the regular one to be used for normal gun parts and the 'casemates' version to be used for casemates, but in fact the 'casemates' version is used for guns <2in in caliber, and the other is used for all other guns. I have no idea why the devs did this, and I've fixed it whenever I've rewritten code in my mod, but it's worth pointing out.
  • While there are very few hardcoded things, a lot of ship design is very random. For example crew quarters, op range, survivability (what the "Bulkheads" slider is referred to as internally), and speed are all essentially random, as is part placement (and part selection!!) within the constraints set out above.
  • There's a bunch of bugs I noticed, some of them blurring the line between "bug" and "the easiest way to disable a feature is to make it so buggy it doesn't ever apply".
  • There's nothing in there to do more intelligent selection of hulls or parts or shiptypes or whatever, something like "the finances aren't great, so choose a cheaper hull", let alone even just "this hull is newer and has better stats so it should be picked". And in terms of actually setting the ship's stats and parts, it just tries hundreds of times making random choices (within constraints) until it gets a result that meets the shipType's requirements.

 

First, let's cover part availability to a hull. A part is treated as available for a hull only if:

  • There is overlap between the hull's set of param tags, and every single need() block in the part.
  • There is no overlap between the hull's set of param tags and any exclude() block in the part.

Example. Consider two hulls. Both are bb and they have the following params:

Hull bb_6_iowa: "type(bb)         BB_Modern_Iowa         bb         g4         Only_Quads         quad_guns_need"

Hull bb_5_iowa*: "type(bb)         BB_Modern_Iowa         bb         g3"

*modified for this example

Now consider three parts, A, B, and C

A - "need(bc;bb;ca)         need(g4)" 

B -  "need(bc;bb;ca)      need(BB_Modern_Iowa)   exclude(g4)"

C - "need(bb)   exclude(BB_Wide)"

Part A will be available for only bb_6. (Well, it'll be available for _any_ hull that has "bb" in its params and "g4" in its params; it will not be available for a hull that has bb but is generation 3). Part B will be available for only bb_5, because the exclude knocks out bb_6. Part C will be available for both bb_5 and bb_6.

Note of course that these are purely data-driven; you can add whatever strings you want, and as many need and/or exclude clauses as you want.

Next, Terminology: 

  • prewarming is the setup phase of the campaign.
  • lerp means linearly interpolate (i.e. to lerp between a and b using t, you get a + (b - a) * t). If t is less than 0 or greater than 1 it's clamped.
  • inverse lerp means go the other direction (i.e. inverselerp(a, b, val) = (val - a) / (b - a). If val is less than a or greater than b it's clamped.
  • Remap a value from1->to1 to from2->to2 means inverse lerp between from1 and to1 and then use that to lerp between from2 and to2. Sometimes this is clamped and sometimes it's not, I'll try to note when it's clamped; usually in the designer it's clamped, but elsewhere in the code it's often not.

 

Now we'll cover GenerateArmor, a function used in a bunch of places. It's used by the designer independently, and also as part of Ship.AdjustHullStats (which is called when you change hull, and as part of the autodesign process). It takes a an armor value called armorMaximal (in mm) and applies it to the armor zones. To help describe this, I'll describe a function which I'll call ConvertYearMultToArmor. It takes a year-based multiplier and generates an armor value. Pick a random number between 0.05 and 1.1 if the shiptype is dd, otheriwse a random number between 0.75 and 1.1. Multiply that by the armorMaximal value that got passed to GenerateArmor and divide that by 11. Finally multiply by the year multiplier that this function gets passed. Now that that function is defined, the actual zones can be covered. The first is the main belt. Here we set a shiptype multiplier: 1.5 for bb, 1.385 for bc, 1.05 for ca, 0.38 for cl, 1.2 for dd, and 1 for everything else. Now the year multiplier becomes a clamped remap current year from 1900->1940 to 12->(14 x shiptype multiplier). (Yes, this means cl armor multipler goes down over time whereas others go up.) That is passed to ConvertYearMultToArmor and the resulting value is set as the main belt armor. The fore/aft belt armor is ConvertYearMultToArmor of a clamped remap of year from 1900->1940 to 3.5->7.5. Main deck is ConvertYearMultToArmor of a clamped remap from 1900->1940 to 1.25->7.5. Fore and Aft decks are the same except changing 1.25 to 1.5 (yes, that means in 1900 fore and aft decks default to thicker than main). ConningTower is the converted remap of 14.0->16.0. Superstructure is the converted remap of 1.0->(random between 0.5 and 1.5). However, if it's an academy mission and its params has 'crutch_ironclad' and the hull's name is 'monitor' or 'virginia' then the remap has 9 added to it (before being passed to Convert). Turret Side is another shiptype dependent one. The shiptype mult here is: 0.065 for bb, 0.08 for bc, 2 for ca, 5 for cl, 40 for dd, and 1 for everything else. That is then multiplied by the year remap of 1900->1940 to armorMaximal->(armorMaximal x 1.7). If it's a mission with 'crutch_ironclad' and the hull name is monitor or virgina, add 13 to this value. Then do the usual convert step. Turret top armor is a traditional convert of the year remap to 3.0->12.0. Barbette type-based, first the converted remap of armorMaximal x 3.75->armorMaximal x 5. Unlike the other ship type specific ones however, the multiplication here happens after conversion. So the conversion result is then multiplied by 0.0135 for bb, 0.04 for bc, 1.8 for ca, 50.0 for cl, 75 for dd, and 1.0 for all other types. Once this is all done, for each armor zone visible in the UI (i.e. all the zones available to the ship), the current value is clamped between the min value allowed (which is just the shiptype's min armor) and the max value allowed (which is based on the gun caliber for turret/barbette armor, and the outer layers for citadel armor). The citadel armor is generated next. For each layer of citadel armor, the usual remap->Convert steps are done. Belt1 uses 3->9, Belt2 uses 2->7, Belt3 and Deck1 use 1.5->5, deck2 uses 1.25->4, and deck3 uses 1.2->3. If the ship already has armor, these values are clamped based on their outer citadel/belt layers, otherwise they are left unclamped and will be clamped later.

 

Now, on to the design process itself.

 

Every turn, there's a 1/3 chance the AI decides to do ship designing (100% chance if prewarming). If there's a ship type with no active design, the AI decides to design something for that. As far as I can tell the following logic seems to be trying to have the AI produce multiple starting designs if prewarming (there's a prewarming and >=5 designs check) but I don't think it's working properly, and I'm not going to investigate too hard. Similarly there's a check for if it's been a random time (3-5 years or so) since a design was designed and if so that becomes eligible for replacement (again, doesn't look like it would work right). Lastly, if the shared design setting is Always, and there's a shared design that would work here (right type and year and techs), designing is skipped.

With a shiptype selected, the AI selects a hull. This is simply (a) a random hull that the player can build (tech tonnage limit and shipyard tonnage limit and hull unlocked by tech) and (b) hasn't already been used in a design this iteration. Then the game enters the Constructor scene and calls Ship.GenerateRandomShip(). 

This is where things start if it's a mission or the AI is designing a ship based on human request, since the shiptype and hull are already known.

Ship.GenerateRandomShip keeps trying to generate until the number of designing tries allocated is exceeded. When calling the Generate function, it says to use a small number of tries (4) instead of the usual number (20) that you'd get by engaging the dsigner yourself. It allos all adjustments and has no custom overrides.

The generate function does the following steps in a loop (waiting a frame after each one):
1. Setup: It saves the speed if refitting, otherwise it sets a min (well, min starting) speed of whichever is greater, the shiptype's min speed or the hull's speed limiter x 1.02.

2. Part removal: If it's a refit, it sees if any parts are invalid. If so it removes them and sets the refit to not be a simple refit (if it was set to be a simple refit). If it's not a refit, it removes all parts.

3. Beam/Draught: If it's not a simple refit, it sets random beam and draught, with the randomness a kind of pseudo-normal distribution centered on the ship's current beam and draught (which will be 0 at the start). If it is a simple refit, it just sets random beam and draught. This logic seems the opposite of what I would expect, and I wonder if there was a typo here.

4. Tonnage: There are multiple cases. The standard case, the 'random_tonnage' case, and the 'random_tonnage_low' case. The standard case first. Set a tonnage floor by multiplying tonnage_not_maximal_ratio by a random number between 0.01 and 1.5. Use that to lerp between min tonnage and max tonnage (as limited by tech and by shipyard), that's the tonnage floor. If the shiptype's params contains 'random_tonnage' then use a random number between min and max tonnage as the tonnage floor instead. If the shiptype's params contains 'random_tonnage_low', then 80% of the time lerp between min and max tonnage using tonnage_not_maximal_ratio, and then use a random number between that and min tonnage as the tonnage floor. 20% of the time, pick a random number between min tonnage and max tonnage and use that as the tonnage floor. To get the final tonnage, pick a random number between the tonnage floor and the max tonnage.

5. Regardless of if it's a refit, clamp the tonnage to within the tech and shipyard limits. If it's > 1.2x the limit, set tonnage to between 40% and 100% of the limit and set new random beam and draught that are weighted above minimums; if it's only a bit over, just set new random beam and draught and hope it works out. If it's under the limit, just refresh the hull geometry.

6. Set base hull stats. Pick a random level of crew quarters (and, if not campaign, crew training between 17 and 100). Pick a random speed between 0.9 and 1.1x the speedlimiter of the hull and clamp that to the min and max speeds of the shiptype. Set a random operational range. Pick a random bulkhead level. If it's less than high, set it to medium if the ship type is DD or TB, otherwise clamp it back to high. Generate random armor levels for the ship: remap the design year from 1890->1940 to 1.0->0.85 and then multiply that by a random number between 1 and 1.3. Then multiply that by the shiptype's armor value and by 25.4 (inches to millimeters). Finally pass that as armorMaximal to GenerateArmor (see above).

7. Adjust Hull Stats: Ship.AdjustHullStats gets called to reduce speed, armor, oprange, and crew quarters in order to make sure enough free tonnage is available. However, this function is bugged in multiple ways. First let's discuss the stop condition for the adjustments. That condition is reached when the ratio of used weight divided by total tonnage is less than a ratio computed by: remap year from 1890->1940 to 0.8->0.6, multiply that by a random number from 0.875 to 1.075, and subtract all that from 1. Finally clamp the result to between 0.25 and 0.55. Once inside AdjustHullStats, if the ship's weight to tonnage ratio already meets the stop condition, nothing happens. If not, we continue. First we establish a min operational range. It's Very Low if the shiptype has no min_range, otherwise it's whatever the min_range is. (This is never used due to a bug, see below.) Next, if the shiptype has armor_min_hint that's used to multiply shiptype's armorMin times 25.4 and fed to GenerateArmor, which is then stored as the min armor. Otherwise if the ship is type dd then a random multiplier from 1 to 1.3 times 25.4 times a remap of year from 1890->1940 to 1->0.85 is passed to GenerateArmor and stored. If it's a different shiptype, no min override is stored. Next the current speed, crew quarters, and armor are stored. The ship's speed is set to the min speed set in step 1. The ship's crew quarters are decued to cramped, and the ship's armor is reduced to the min armor calculated above (or 0, if none was stored above). Now, if the code weren't bugged, it would then go on and start adjusting things around to meet the stop condition, but one of the bugs in this function is that instead of comparing the passed target ratio (which is the same as that used for the stop condition) to weight / tonnage at this point, it compares the passed ratio to weight itself. Since the weight will always be greater than the target ratio (which is in the range 0-1 by definition), the function quits here and it doesn't continue to adjust stats and test against the stop condition. (Another bug with this function is that it thinks shipType's min and max speeds are in m/s like how speed for ships is stored; they're not, which means when speed is clamped, it's clamped wrongly). Anyway the end result of the bug is that most of the time the ship will end this step with no or minimal and armor and cramped crew quarters.

8. Update Hull Stats. This updates all the calculated stats like range multiplier from beam, engine weight from draught, etc. (See stats and statsEffects TextAssets.)

9. Add parts to the ship. This is controlled by the TextAssets randParts and randPartsRefit. Let's look at a line in randParts. Well, the header plus a line.

@name,enabled,shipTypes,chance,min,max,type,paired,group,effect,center,side,rangeZFrom,rangeZTo,condition,param,#,#

40/gun//mc/main_center/c//main_cal/and(tag[danton_style]),,"bb, bc",,1,1,gun,,mc,main_center,c,,0.5,1,main_cal,and(tag[danton_style]),,

The name is just a concatenation of its row. Enabled is the usual. shipTypes is self-explanatory, as is chance, the min and max number, and the part type. Paired means should it be paired across the deck (i.e. side guns, torps) and if the number chosen between min and max is odd, it will randomly pick whether to add an extra one on the other side or not. Group is so that the same partdata (to be clear, a Part is the game object that get has the model etc; PartData is the data that it uses, it's in the parts TextAsset) gets used for all lines sharing that group. So as the designer walks the list of randparts to try to place, whenever it encounters the first line with a group it stores the partdata it picks; when it sees that group again, it uses that partdata again. (For example, everything with group "mc" will be the same gun part, so all the main guns are twin 6in guns once the first one is).

Effect is used for figuring out which parts (amongst all the available parts, see above) fit this rule. In addition, certain effects are used for matching center/side guns together. So if you put main_center here, and there's already a part with param main_side on the ship, then the randpart will select from those partdatas with main_side or ms with a matching caliber. This is like a gun-specific version of group, above. That special handling is in addition to the normal functioning, so if you put main_center here, the eligible parts are only those which have main_center in their params, and whose caliber match any previously-placed parts with main_center in their params (and, of course, if this rule has a group, the only eligible part will be the one whose data matches a previous placed part of that group).

Center and Side determine where the part is placed; f means free placement, c in the center column means center-only, s in the side column means side-only. RangeZFrom and rangeZto control where along the Z axis (length) of the ship the part can be placed; ZFrom of 0 and ZTo of 1 means only in the front half, -1 to 0 means the back half, -0.333 to 0.333 means the center third, etc.

Condition, like effect, allows only certain values.  The randParts file should give you a decent idea of the options., they're for main/sec/tertiary cal and for casemate or no. Note (and this is true for mount below) ! means "not", so !casemate means not casemate. Note that the main/sec/ter calibers mentioned here vary by ship size and year. It's tunable by a bunch of params but is not worth a deep dive at present.

Finally, params has the rest of the logic. delete_unmounted means to go back and delete any unmounted parts; mount() means the randpart needs a mount of that type; and then there are the 'and' and 'or' operators, each of which take one or more tags. 'and' means all the tags must be met by the hull (i.e. the hull's params must contain all those strings), 'or' means that at least one tag must be present in the hull's params.

Annoyingly, there's some special rules:

  • For a ship of g4, you can't ask for a barbette or funnel between -0.3 and 0.625 until after the main tower is placed
  • For a ship of g4, you can't ask for a gun, barbette, or funnel between -0.45 and 0.25 until after the sec tower is placed
  • You can't ask for a funnel aft of -0.5 until the secondary tower is placed (or if none is needed)

 

10. AdjustHullStats again, this time to increase weight. Here the ship tonnage is passed in as the target ratio, which gets around that bug. Also a function that always returns false is passed in as the stop condition (!). As before min op range and armor min hints are calculated, and existing speed and crew quarters and armor are stored. Then the function tries a maximal approach to using tonnage. It sets crew quarters to spacious, sets armor to the maximum possible for each zone, and sets speed to the following. First remap year from 1890->1940 to 1->1.12, multiply that by the hull's speedLimiter times 0.51444 (i.e. kts to m/s, what speed is stored as on ships) and multiply that by 1.15 if the hull is cl or dd, and 0.9 otherwise. If all that is still less that 99.7% of total tonnage, stop there. Otherwise reset speed, armor, and crew quarters to what they were. Then enter a loop to increase stats bit by bit. Calculate a new speed which is current speed plus speed_step knots more. Clamp that new speed to between the saved min speed in step 1, and speedLimiter times (1.7 if cl or dd, 0.95 otherwise) times the clamped remap of year from 1890->1940 to 1->1.05. Then the next bug in this function occurs, the previously-mentioned one about m/s vs knots. Because this new speed (which is in m/s) is then clamped to between the shiptype's min and max speeds (which are both in knots). That means in effect that the actual (minimum speed for the ship (if applied by this function) will be 1.944 times the shipType's minimum speed. If 1.944x that speed is higher than 0.95 (or 1.7) times the speedLimiter speed, then speed will always be kept at 1.944x shipType's min speed (because the two clamps will fight each other, one bringing the speed down to its max, and the second bringing it up to its min). After setting that potential new speed, next new potential crew quarters are set, as 1 above the current value. The new potential op range is set to one higher than current, and that clamped to between the min op range (or Medium, if none is specified in the shipType). Finally a weighted-random armor zone is selected to increase. (The weighted-random selection works like this: sum up all the weights, select a random number between 0 and the total, and keep going through items adding their weights until you pass that number.) You can see the armor priorities here: https://github.com/NathanKell/UADRealism/blob/010803fbad56381565df9bd9e27160a34a4f3c52/Harmony/Ship.cs#L685-L701 This zone gets its armor increased by armorstep (whatever the armor step is in the UI, fractions of an inch or mm). Once all that is done, a value is selected to increase by weighted-random. The weights are: speed is 350, crew quarters is 150, armor is 850. If any of the values are unchanged (e.g. crew quarters already at maximum), they're not included as options. Once an option is selected, the ship's new weight is tested and if it's greater than 1.2x the ship's tonnage, everything gets reset to the start of the loop (except armor, it's kept increased!) and it adds the next item, if any exist. If weight is less than or equal to 1.2x tonnage, then we stop if this is not a mission, or the ship's owning player isn't the main player of the mission (i.e. the human), or the ship isn't the main ship of the mission, or the cost is less than or equal to the player's cash in the mission. Otherwise the next item gets applied. If there are no items left to apply and none of those conditions are met (i.e. weight is greater than 1.2x tonnage, or it's a mission and the main player and...) then the function stops increasing weight and returns. Otherwise the stop condition is tested. Since the stop condition here always returns false (see above), it never stops, which means the next set of increases are calculated. It's worth noting that (a) op range is never actually increased (the line was probably commented out?), (b) in practice this loop will keep executing until weight is above 1.2x tonnage or cost is above player cash for a main-ship-main-player-mission case.

11. All parts on the ship get updated (their colliders updated, their caliber lengths calculated, etc).

12. Verify guns. If this is not a simple refit, then make sure that parts aren't overlapping and weapons have reasonable firing arcs. Any that fail are removed. Next check that there are at least as many main caliber turrets as the hull requires and as many barrels across those turrets as the hull requires. If not, go back to step 2. If so, or if it's a simple refit, continue.

13. Select Components and reduce weight. It's important to point out that until now, the hull has had the default components for the current tech and the hull / ship type. Obviously that means the weights before will be screwy, which is why adding random parts succeeds so long as weight is less than 1.5x tonnage, and AdjustHullStats goes up to 1.2x tonnage. The component selection logic is simple. For each allowable component type (i.e. available to the ship type and the UI would display the component choice, so no torpedo type components if there are no torpedo parts--this is why component selection occurs after part addition, I assume), find all allowable components for that component type. Then select one via weighted-random (using the weights as defined in the components TextAsset). Once that is done Ship.ReduceWeightByReducingCharacteristics is called. This returns immediately if the ship is type tr. It also returns if weight is not over tonnage, or if the ship is type dd and has no armor set. Then it sets up an armor reduction queue. This is pulled from the ship for bb, bc, ca, cl, and dd. There are individual queues for each, but they all have the same values: Aft Deck, Fore Deck, Aft Belt, Fore Belt, Main Belt, Superstructure, Turret Top, Turret Side, Conning Tower, Main Deck, Inner Belt_1st,Inner Belt_2nd, Inner Belt_3rd, Inner Deck_1st, Inner Deck_2nd, Inner Deck_3rd,Barbette. The order for everything else is Fore Belt, Aft Belt, Fore Deck, Conning Tower, Aft Deck, Main Deck, Fore Belt, Belt1, Belt2, Belt3, Deck1, Deck2, Deck3, Turret Side, Turret Top, Barbette, Superstructure. Then it starts a loop which runs while weight is greater than tonnage. Each iteration it goes through the armor reduction queue, reducing armor in that area by armor step times 20 (it looks like the code tried to scale by tonnage but either that was disabled weirdly, or it's yet anothre bug, because it always returns 20 now). This reduced value is then clamped between the min and max armor for the zone, and set to the zone. If weight is now at or under tonnage, the queue traversal stops, otherwise it continues. At the end of this the main loop checks weight vs. tonnage again and continues if necessary. Then it does the whole thing again in reverse if weight is now too low, adding back armor the same way it took it away (note the priority is the same, so it will add back the armor that was the highest priority to remove, not armor in the order of the add priority queue, see below). Next it moves on to speed. If the type is dd, potential new speed is the clamped remap of year from 1890->1940 to hull speedLimiter x 0.7 -> hull speedLimiter x (random number from 0.925 to 1.0). The minSpeed is the speed from step 1 plus speed step. Otherwise, if the hull is generation 1, potential new speed is the clamped remap of year from 1890->1940 to hull speedLimiter x 0.85 -> speedLimiter x (a random number from 0.8 to 0.89). For generation 2, the values are remap to speedLimiter x 0.75 -> speedLimiter x (random between 0.85 and 0.94). For generation 3, the values are remap to speedLimiter x 0.75 -> speedLimiter x (random 0.81 to 0.92). For generation 4, the values are remap to speedLimiter x 0.7 -> speedLimiter x (random 0.8 to 0.92). For anything else, the values are remap to speedLimiter x 0.7 -> speedLimiter x (random 0.89 to 0.92). In all cases the new speed is clamped to between minSpeed and the ship type's maximum speed. Once this is calculted, if weight is greater than tonnage and this new speed is greater than current speed, speed is reduced by speed_step at a time until either it's at the new speed or weight is no longer above tonnage. If weight is still above tonnage, and operational range is greater than Medium, operational range is reduced one step at a time (including down to Very Low--it's weird this can't happen if it already starts below Medium!). Finally reduce Bulkheads to high one step at a time (if they're not already at High) until weight is under tonnage. With all that done, finally any unused barbettes are removed from the ship.

14. Add tonnage usage. This is broadly the reverse of the previous step and is designed to bring the ship up to its tonnage limit. It stops immediately if the ship is type amc. Then it calculates three stop points: weight to stop increasing armor (the full ship tonnage), weight to stop increasing op range (some level below full tonnage), and weight to stop increasing bulkheads (another level below full tonnage). It then creates the armor add priority queues. Specific ones are specified for bb, bc, ca, cl, and dd, but as before it's the same for all of them: Turret Side, Barbette, Main Belt, Main Deck, Fore Belt, Aft Belt, Fore Deck, Aft Deck, Inner Belt_1st, Inner Deck_1st, Inner Belt_2nd, Inner Deck_2nd, Inner Belt_3rd, Inner Deck_3rd, Turret Top, Superstructure, Conning Tower. The default used by everything else  is turret side, barbette, main belt, main deck, belt fore, deck fore, turret top, belt aft, deck aft, conning tower, belt1, deck1, belt2, deck2, belt3, deck3, superstructure. The logic for armor is the same as reduction: loop until all armor is added or at the weight limit. On each iteration, loop through the queue. Again, the function that returns the multiplier to the amount to change things by is bugged; it's supposed to change based on ship tonnage, but due to checking vs weight rather than weight / tonnage, it always reutrns the small numbers (2 for armor, 1 for everything else). So armor change is always armor step times 2. It then runs a second copy of this whole process if weight is now above tonnage, and reduces armor by a step each in priority order (note, armor add priority not armor reduce priority...) until weight is back at or below tonnage. Next it moves on to speed. If speed is below the shiptype's max speed, it increases speed by speed_step at a time until either the ship is at the shiptype's max speed, or weight is at the ship's tonnage. Then, if step 13 reduced op range, it increases it one step at a time until at the weight limit for op range increases (see start of step 14). Then, if step 13 decreased bulkheads, it increases bulkheads one step at a time until at the weight limit for bulkhead increases (see start of step 14).

15. If weight is less than 99.2% of max, it runs step 14 again.

16. If weight is greater than 100.1% of max, it runs step 13 again.

17. If weight is less than 99.2% of max, it runs step 14 again. Then it checks instability. It calculates stats and gets the stat values for Instability X and Instability Z. If the hull is not b1_maine and the ship is not type tr, it passes if X is 0 and the absolute value of Z is less than or equal to 100. Otherwise it passes if the absolute value of X is greater than or equal to 0 (Z only fails if its absolute value is less than 0, so it always succeeds). Then cost and requirements are checked. If it's a mission and the ship cost is greater than player cash and crew training can't be reduced, it fails. If the ship fails to pass any shiptype requierement, it fails. If any parts report they can't (that is, can no longer be) placed, it fails. Then it checks barbettes and rechecks weight and cost. If weight or cost checks fail, or there is a barbette with no part mounted to it, it fails. If the ship's tonnage is not allowed for the player (due to tech or shipyard), the design fails. If the ship passes all those checks, the design is good and the designer stops. Otherwise it starts again at step 2.

If after exhausting tries there's still not a valid ship, and Shared Design usage is Selective, then it tries to find a shared design that matches year and tech and, if found, uses that.

Edited by NathanKell
  • Like 1
  • NathanKell changed the title to This is how the Autodesigner / AI Designer works
Posted
On 6/16/2024 at 10:36 PM, NathanKell said:

3. Beam/Draught: If it's not a simple refit, it sets random beam and draught, with the randomness a kind of pseudo-normal distribution centered on the ship's current beam and draught (which will be 0 at the start). If it is a simple refit, it just sets random beam and draught. This logic seems the opposite of what I would expect, and I wonder if there was a typo here.

I'd agree here. When simple refit, it should not switch beam and draught whatsoever.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...