Kerbalism
Kerbalism copied to clipboard
Architecture changes
The general idea is:
- ~~remove Profiles~~
- ~~have Rules and Components in a dependency tree~~
- ~~let the user enable Rules/Components individually when starting a new game~~
- ~~Rule and~~ Components store set of Process, Resource handlers[, Telemetry and Alerts]
- Component also store configurability/reliability info [and a set of Disable and Support]
- ~~Rules are like implicit components added to all vessels~~
- Components are tagged
- Controller module is used to 'install' a Component in a part (selecting from a set of names and tags)
- The list of allowed Components in a Controller is 'filtered' from set of user-enabled components
- An empty Controller module degenerate into no-op (disappear)
- Controller with a single valid Component allowed just appear to add the component to the part
- Controller with multiple valid Components allowed let the user select which one to install
- Modifier-expression is a mathematical function returning a scalar, that can use env/hab values
- Process consume/produce resources on a vessel, the rates can be scaled by modifier-expression
- [Telemetry provide a modifier-expression driven reading to the monitor and planner UI]
- [Alert check a condition specified as a modifier-expression, and notify the user (can be toggled on/off)]
- Resource handlers add/remove and toggle flow of a resource
- [Disable simply disable a PartModule when installed]
- [Support add ad-hoc, hard-coded support/emulation for a specific type of PartModule]
- Configurability and reliability are about enabling/disabling modules: they merge in the same concept
- The whole simulation is represented by the active rules, a set of Components and the environment
Update 2017-05-22: Reduced the scope of changes after some more thinking. Discarded concepts are striked ~~like this~~ Dubious concepts are represented [like this]
Proposed by @gotmachine:
- Modifier-expressions can be defined once and named, and can then be used as variables from other modifier-expressions
- PlannerPanel/PlannerLine entries in Rules: to define precisely the data shown to the user in planner when a rule is enabled (instead of using Telemetry entries in both planner and monitor)
- Generic per-kerbal properties
Motivating example
Rule
{
name = biology
title = Simulate the physical health of Kerbals
desc = Kerbals need to breath <b>Oxygen</b>, eat <b>Food</b> and drink <b>Water</b> to survive
Process
{
name = breathing
modifier = crew.count * env.not_breathable
input = Oxygen@...
output = WasteAtmosphere@...
dump = WasteAtmosphere
}
Process
{
name = eating
modifier = crew.count
input = Food@...
output = Waste@...
dump = Waste
}
Process
{
name = drinking
modifier = crew.count
input = Water@...
output = WasteWater@...
dump = WasteWater
}
Process
{
name = no_oxygen
modifier = res.Oxygen.amount < 0.01
degen = health@... // lower 'health' property of crew
}
Process
{
name = no_food
modifier = res.Food.amount < 0.01
degen = health@...
}
Process
{
name = no_water
modifier = res.Water.amount < 0.01
degen = health@...
}
Process
{
name = recovery // this will show on the info about health
// you can split modifier on multiple lines, multiplication between them is implied
modifier = res.Oxygen.amount > 0.01 // if oxygen is present
modifier = res.Food.amount > 0.01 // and food is present
modifier = res.Water.amount > 0.01 // and water is present
modifier = health<1.0 // and health is not full (this is only used to avoid 'recovery' ui label always present)
regen = health@... // increase 'health' property of crew
}
Alert
{
name = asphyxia_imminent
condition = res.Oxygen.amount < 0.01
icon = problem-asphyxia // add an arbitrary problem icon
log = true // add entry to vessel log
message = true // show a warning message
stopwarp = true // stop timewarp
}
Alert
{
name = oxygen_low
modifier = res.Oxygen.level<0.33
icon = // none
log = true
message = true
stopwarp = false // don't stop warp
}
}
Rule
{
name = environment_control
title = Simulate the internal atmosphere of vessels
desc = <b>CarbonDioxide</b> need to be scrubbed, pressure need to be maintained using <b>Nitrogen</b>
require = biology // this rule can only be enabled if biology is also enabled
Process
{
name = co2_poisoning
modifier = res.WasteAtmo.level>0.008
degen = health@...
}
Process
{
name = atmosphere_leaking
modifier = hab.surface * env.not_breathable
input = [email protected]
}
Alert
{
name = co2poisoning
modifier = res.WasteAtmo.level>0.004 // a bit lower than actual effect on health start
icon = problem-poisoning
log = true
message = true
stopwarp = true
}
}
Rule
{
name = radiation
title = Simulate radiation and shielding
desc = A complex radiation environment, and the means to deal with it.
Process
{
name = uv_irradiation
modifier = hab.uv // EM flux in a particular bandwidth reaching the internal habitat
degen = health@...
}
Process
{
name = xray_irradiation
modifier = hab.xray
degen = health@...
}
Process
{
name = gamma_irradiation
modifier = hab.gamma
degen = health@...
}
Process
{
name = plasma_irradiation
modifier = hab.plasma
degen = health@...
}
Process
{
modifier = res.ShieldHighZ.amount / hab.surface // amount of HighZ shielding per-m²
absorb = xray@...
absorb = gamma@...
}
Process
{
modifier = res.ShieldHighZ.amount / hab.surface // amount of HighZ shielding per-m²
absorb = plasma@...
emit = gamma@...
}
Process
{
modifier = res.ShieldLowZ.amount / hab.surface // amount of LowZ shielding per m²
absorb = plasma@...
absorb = xray@...
}
Process
{
modifier = res.ShieldLowZ.amount / hab.surface // amount of LowZ shielding per m²
absorb = gamma@...
emit = xray@...
}
Process
{
modifier = res.ShieldGradedZ.amount / hab.surface // amount of GradedZ shielding per m²
absorb = plasma@...
absorb = xray@...
absorb = gamma@...
}
// bonus: you can use this to implement implicit shielding from amount of water or other resources
}
Component
{
name = none
title = Nothing selected
desc = If you can't or wan't to take anything else.
// does nothing
}
Component
{
// scaling convention: enough to scrub 1 kerbal, with something to spare
name = scrubber
title = Sequester co2 bla bla
desc = Arbitrary long description.
require = atmosphere_control
tag = eclss
toggle = true // if the resource flow state and process can be toggled
running = false // the resource flow starting state and the process starting state
scalable = true // if the scale unit can be tweaked by the user in VAB (arbitrary module values are not scaled)
hardware = 5 // how many Hardware units are required to install or repair this component
skill = engineer@2 // trait and experience required to install this component
mtbf = ... // how often does the component fail
mass = ... // how much extra mass this component add when installed
cost = ... // extra cost in space-bucks when component is installed
tech = ... // technology required to unlock
Process
{
modifier = env.not_breathable
input = WasteAtmo@...
output = CarbonDioxide@...
dump = true
}
}
Component
{
// scale convention: volume in m²
name = oxygen_supply
title = Oxygen supply
desc = ...
require = biology // only enable if biology is enabled
tag = supplies
toggle = false
running = true
scalable = false
hardware = 0.05 // i'll elaborate on this another time
skill = engineer@1
mtbf = ... // leaks?
mass = 0 // no extra mass/cost here
cost = 0
resource = Oxygen@120
}
// scale convention: habitat volume in m³
Component
{
name = habitat_interior
require = atmosphere_control
resource = Atmosphere@1
resource = WasteAtmosphere@0/1 // can specify amount and capacity separately
toggle = true
running = true
}
// scale convention: habitat surface in m²
Component
{
name = habitat_exterior
require = radiation
resource = ShieldingLowZ@0/1
resource = ShieldingHighZ@0/1
resource = ShieldingGradedZ@0/1
}
// now we add a component to a part
@PART[mk1pod]
{
MODULE
{
name = Controller
allow = none, eclss@crew, pod_experiments // the parameter after '@' is the scale: you can use a number or one of:
// - 'crew' to scale using crew capacity of part
// - 'volume' to scale using part volume
// - 'surface' to scale using part surface
}
MODULE
{
name = Controller
allow = [email protected] // here we are allowing anything tagged as 'supplies', passing 0.05m³ volume
// (these components here are assumed to use container volume as scaling convention)
}
MODULE
{
name = Controller
allow = habitat_interior@volume
}
MODULE
{
name = Controller
allow = habitat_exterior@surface
}
}
Major advantages
A level of indirection between enabled rule-set and what's added to parts, thanks to Controller ability to take a list of names and tags of components. So it make sense to add a Controller with allow=eclss to all manned pods, irregardless of the rule-set the user will choose. Then if no enabled component is tagged as eclss in a particular savegame, no extra functionality is added to the part. But if one or more components match 'eclss', then functionality is added to the part.
Removal of profiles and focus on rules allow composition between mechanics provided by different authors. Distribute a set of rules, and the user may enable/disable those rules when starting a new game. Without these rules being mutually exclusive to others distributed by somebody else.
Enabled rule-set specified per-savegame thanks to the lack of reliance on MM for most things. The user will choose what rules/components to enable when starting a new game, using a configuration window. The rule-set is then serialized into the savegame.
Module enable/disable technique use is generalized to implement: installation/dismantling of components, reliability (fixing a failed component mean re-installing it) and disabling stock/third-party modules without relying on MM (so we can do it per-savegame).
Much less verbose and hackish modifiers when implementing relatively complex mechanics, thanks to modifier-expressions.
Third-party modules emulation is explicit, defined per-component, so ideally other mod authors can specify when and where one of their modules is emulated.
Arbitrary telemetry and alerts will be a powerful way to enrich your own rules, and allow to remove most (if not all) of the hard-coded concepts in the UI.
Can use MM patches against Rules and Components, with all the flexibility that gives.
This is awesome. One thing that seems absent (or than I didn't understand) is how the telemetry info is defined and linked to all this (and specifically to alerts). Could you add an example ?
A few remarks :
- I can't see anything that define when death/breakdown events happen. I guess this could be added to the Alert{} nodes with a "consequence = death/breakdown" line
- Module{ allow = } "@" parameter could also have a "mass" option in addition to crew, volume & surface
As for my proposals :
- For reference, here is the proposed "GUIpanel/GUIline/GUItooltip" nodes system to define telemetry/planner UI :
GUIpanel
{
title = comfort // the name/gui title of the panel
index = 3 // add the panel to the third "panel space" in the planner
}
GUIline
{
panel = comfort // planner panel the line is added to, added to telemetry panel is unspecified
index = 1 // order of the line in the telemetry UI / planner panel
title = breakdown // the line left text
// example note : "comfort" is a degen value like "health" in your example
value = comfort.capacity / [rate_formula] // the line right text, modifier-expression or use "" to evaluate as a string
value_format = duration // returned value is Lib.HumanReadableDuration(value)
visible = info.comfort < 0.2 // modifier-expression that determine visibility
}
GUIline
{
panel = comfort // planner panel the line is added to, added to telemetry panel is unspecified
index = 2 // order of the line in the telemetry UI / planner panel
panel = comfort // planner panel the line is added to, added to telemetry panel is unspecified
title = comfort // the line left text
value = "poor" // the line right text, modifier-expression or use "" to evaluate as a string
color = #ff0000 // color of the value
visible = info.comfort < 0.2 // modifier-expression that determine visibility
}
GUIline
{
panel = comfort
index = 2
title = comfort
value = "modest"
color = #ff8300
visible = (info.comfort >= 0.2) && (info.comfort < 0.4)
}
GUItooltip
{
line_title = comfort // show tooltip on GUIline whose title = comfort
title = firm ground // the line left text
value = info.firm_ground // the line right text, modifier-expression or use "" to evaluate as a string
value_format = boolean // returned value is Lib.HumanReadableBoolean(bool value) {return value ? "true" : "false";}
color = #00ff00 // color of the value
}
- If I understand your example correctly, your are already proposing "per-kerbal" properties in the form of the process{regen/degen} lines. Missing from your example is a way to define them, so maybe something like that :
Degeneration
{
name = health // this can be used like a resource (amount/capacity/level...)
capacity = some_modifier // modifier that define capacity, capacity = amount if undefined
ksc_reset = true // is the KerbalValue amount set to 0.0 when the kerbal get back to KSC
vessel_reset = true // is the KerbalValue amount set to 0.0 when the vessel the kerbal is in is modified
}
@gotmachine Each vessel has a set of Process, Telemetry and Alerts compiled from the active rule-set (that apply to all vessels) and from the components actually installed in parts of the vessel.
An habitat part may provide telemetry about pressure. Or that may be provided by a pressure control ECLSS component instead. Or even by the atmosphere_control rule. Telemetry entries with the same name 'collapse' into a single one (with average value, and individual readings in its tooltip). The collapsed set of telemetries is then shown in the omonimous panel in Monitor.
For Alerts, it is pretty much the same as above. You can imagine a pressure control ECLSS component that include an alert about pressure dropping. The alerts in a vessel will be shown in a new panel in Monitor, where they can be toggled individually per-vessel. Each enabled alert in essence check if a modifier-expression condition evaluate to true (more than zero). If that's the case, it show a custom problem-icon (that is just a png image, there are only width/height constrains), a warning and/or stop timewarp.
Component
{
name = pressure_control
...
Telemetry
{
name = habitat_pressure
title = pressure
value = Atmosphere.level * 101
format = F2
unit = kPA
}
...
Alert
{
name = pressure_dropping
title = Pressure is dropping
condition = Atmosphere.rate < 0.0
problem-icon = my_path/my_custom_png_icon_without_extension
message = Atmospheric pressure in the internal habitat is dropping
stopwarp = false
}
...
}
For killing/breaking kerbals, I was thinking of simplifing the system so that there are only two 'hard-coded properties': health and sanity. Both start from a value of 1.0. When health reach zero, there is death. When sanity reach zero, there is mental breakdown. Each rule/component can increment/decrement these two hard-coded properties using 'degen/regen' fields in Process. Both properties will also get the special vitals 'icons' in the Telemetry panel of Monitor.
The 'motivating example' was written before your suggestion about custom per-kerbal properties, that make sense. But there is some elegance in having a unique mapping between 'property' and 'consequence'. Anyway, if kerbal properties end up being not hard-coded, their definition may look something like this:
Property
{
name = health
consequence = death
warning-threshold = 0.5
danger-threshold = 0.2
nominal-icon = Kerbalism/icons/heart-gray
warning-icon = Kerbalism/icons/heart-yellow
danger-icon = Kerbalism/icons/heart-red
}
with implicit 'capacity' of 1, and 'fatal-threshold' of 0.
About 'full recovery on kerbin': if properties are not reset once a kerbal is back at KSC, we'll need a dedicated UI to show the user their values. So that's more work. I can also imagine players firing/hiring kerbals to get around it.
Module{ allow = } "@" parameter could also have a "mass" option in addition to crew, volume & surface
Agreed. I think these 4 scaling types should cover pretty much every possible use case.
For reference, here is the proposed "GUIpanel/GUIline/GUItooltip" nodes system to define telemetry/planner UI [...]
Thanks for the efforts on that. I anticipate it will be not easy keep the current planner structure intact. An alternative may be to essentially re-use Telemetry entries for both monitor and planner. That would be a fundamentally different planner, so I'm not sure.
If I may throw in, I would go for having the Kerbal properties not hard coded, I like the idea of the ability to make kerbal's little delicate snowflakes or hard ass gangsters, with values chosen at random or specific kerbal's getting their own personal set. i.e Jeb could have a higher danger-threshold and lower warning-threshold. Can I assume even if the monitor and planner are obviously going to change that they will still be using the window class to be displayed, as I am currently working on a way to stop the annoying click through effect.
@PiezPiedPy There is a 'per-kerbal variance' that can be specified in degeneration, and that will probably be preserved in the new rule system. But that is only meant to avoid 'all crew suffocating or going crazy at once' scenarios. There will be no way to specify that a particular kerbal is different than the others.
Can I assume even if the monitor and planner are obviously going to change that they will still be using the window class to be displayed, as I am currently working on a way to stop the annoying click through effect
I don't think that the overall architecture of the UI library will require any changes at all. The changes will be related mostly to content. Monitor & Planner are not using Window, but any changes you make in Window should be trivial to back-port to them afterward.
Isn't having a single health variable going to be problematic ? Various rules regen/degen will interfere : eating will regen health lost by radiation, for example.
The telemetry concept feel great to represent a rate or amount of something. And the planner or monitor could make use of a structured hierarchical condition-visibility based string-able line/tooltip system.
@gotmachine The idea was to have health/sanity like an 'health bar' in other videogames, with 'idle recovery'. But you are right, and on further analysis having common properties for food and radiation is not going to work. I think this aspect need more thinking.
The issue has been updated, and the scope of changes reduced sensibly. Further reduction is likely before implementation start.