CleverRaven / Cataclysm-DDA

Cataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.
http://cataclysmdda.org
Other
10.71k stars 4.2k forks source link

Vitamin-based drugs. Examples of realization with explanation #77022

Closed IdleSol closed 1 month ago

IdleSol commented 1 month ago

Useful links:

  1. VITAMIN.md https://github.com/CleverRaven/Cataclysm-DDA/blob/master/doc/VITAMIN.md
  2. EFFECTS_JSON.md https://github.com/CleverRaven/Cataclysm-DDA/blob/master/doc/EFFECTS_JSON.md
  3. 70239

  4. 76905

Contents

  1. The vitamin system and its features 1.1. Method of adding vitamins to a drug 1.2. How to put a vitamin into a character's body 1.3. The lifetime of a single vitamin or how accurately a drug should work 1.4. Using a vitamin to add effect 1.5. A vitamin that decays into another vitamin 1.6. A little about effects

  2. A drug model and an example of its implementation 2.1. A simple drug model. Or the first level of complexity link 2.2. Drug model. The second level of complexity link 2.3. Drug model. Third level of complexity link 2.4. Combined drug model. Or fourth level of complexity link 2.5. Exploitation of the features of existing vitamin implementation link

  3. How it could be link

Explanation of the difference between effect and effect. So that there is no confusion. Effect is just a word, in the standard sense. Effect is an indication that we are talking about a relevant parameter. For example, the effect of a painkiller has the effects of a painkiller and a sleeping pill.

Or:

    "type": "effect_type",
    "id": "painkiller",
…
    "pkill_XXX …
    "sleepiness_XXX …

1. The vitamin system and its features

There are several ways to add vitamins to an item in the game. And vitamins can be of several types. For more details, see topic #76905.

1.1. Method of adding vitamins to a drug

I will use the following method:

    "id": "some_drug",
...
    "use_action": {
      "type": "consume_drug",
      "activation_message": "You take some %s.",
      "vitamins": [ [ "some_vit", 100 ]]
    }

Where some_vit is a vitamin of type: "vit_type": "drug".

Because when using use_action:

  1. The behavior of a vitamin is independent of vit_type.
  2. The vitamin immediately enters the character's body in full (100 units from the example).

Note. If you use the method:

    "id": "some_med",
    "vitamins": [ [ "some_vit", 100 ]]

And set "vit_type": "counter". The vitamin will enter the character's body between 30:01 and 60:00 minutes. And in the amount of 2 units, not 100 units, as specified in the example. And then every half an hour it will arrive in one more unit. The variation is related to the stomach cycle, which is equal to 30 minutes. The drug is delivered after two stomach cycles:

  • the stomach cycle in which the drug is taken
  • the stomach cycle that follows it. And since the time of ingestion can be any time, the time of onset of action of the drug will be random.

And for "vit_type": "drug", the drug will arrive in full, but after the current stomach cycle is complete, that is, between 1 second and 30 minutes.

1.2. How to put a vitamin into a character's body

The main method is to take a drug that has a vitamin section in the description. It is convenient because you open the json record and you can immediately see what is how much and where. But it is only suitable for simple versions of drugs. By simple versions, I mean drugs that act immediately after use. No delayed actions or different effects with different durations. There is a vitamin - there is effect, no vitamin - no effect.

There are additional methods of adding a vitamin:

In the examples, I will use the decay of vitamins into other vitamins. Because, I just haven't dealt with generating vitamins through effect. They have their own mechanics and how they interact (or not) with the mechanics of "standard" vitamins, I haven't looked into.

1.3. The lifetime of a single vitamin or how accurately a drug should work

A vitamin has a parameter rate. The documentation says:

How long it takes to lose one unit of this vitamin.

But the documentation doesn't specify that this doesn't work for the first vitamin ingested. It only works for all other vitamins. If this value is small and the concentration is large, it is hardly noticeable. But:

    "id": "some_med",
...
    "use_action": {
      "type": "consume_drug",
      "activation_message": "You take some %s.",
      "vitamins": [ [ "some_vit", 1 ]]
    }
    "id": "some_vit",
    "vit_type": "drug",
     "rate": "24 h",

Here's a vitamin that can disappear (be absorbed) 1 second after ingestion. Or any other random number between 1 second and 24 hours. Because absorption occurs at exactly midnight. And you can take the drug at any time of day.

Clarification. It is assumed that as long as there is a vitamin in the body, a beneficial effect is in effect. If it is used to generate a new vitamin, then the drug will take effect at a random time between 1 second and 24 hours. Because it will decay at that time into another vitamin that gives the beneficial effect.

Why is that? Because time is split into segments of a long rate. And the starting point is at 0:00:00 of the 1st day of spring. The standard game starts at 8:00:00 on the 61st day of spring. Or after 5 212 800 moves. For "rate": "24 h", there goes the 61st segment.

This is important to remember if you want to make "accurate" drugs.

Recommendation. Select the rate dimension to a level lower than the required accuracy. If the drug is to take effect for hours, specify rate = 1 minute. If the effect of the drug lasts for a few minutes? Then rate= 1 second.

And use the concentration to adjust the duration. Then a vitamin with a concentration of 60 and "rate": "1 m" will act from 59 minutes to 60 minutes. And if "rate": "1 s" then 64 seconds (the time of drug use is 6 seconds, during which 2 units of vitamin are absorbed)

1.4. Using a vitamin to add effect

The game gives us two tools: deficiency and excess. These parameters specify what effect to apply in case of vitamin deficiency or excess. And the value of thresholds is specified in disease and disease_excess.

    "min": 0,
    "max": 90,
    "deficiency": "EFF_1",
    "excess": "EFF_2",
    "disease": [ [ 31, 60 ]],
    "disease_excess": [ [ 1, 30 ], [ 61, 90 ] ],

And for most drugs, only one of them can be used. Either deficiency or excess. Excess is better, since the value of vitamins is a positive number. But not necessarily. (And it doesn't have to be positive, you can set min = -90). It's just more convenient.

This is due to the fact that the drug works as long as the vitamin is present. Yes it can have different effects, if you work hard and pick up the initial values and their modifiers from the intensity of the effect (focused on the levels prescribed in disease and disease_excess), you can add or remove effects. But all of them should be prescribed in one effect or spread over different vitamins (complex drugs with effects of different duration, about them later).

But if we are talking about fantastic variants or magic variants, you can use the variant from the example above.

So at concentrations of 1 to 30 and 61 to 90, the EFF_2 effect works. And between them EFF_1.

Clarification. You can't exactly use the same thresholds for deficiency and excess. Only the deficiency effect worked for me. But I didn't check the operation with overlapping ranges.

1.5. A vitamin that decays into another vitamin

    "id": "vit_1",
    "decays_into": [ ["vit_3" , 2] ]

Vitamin vit_1 at the end of its lifetime (defined by rate) decays into 2 vit_3 vitamins.

There is a peculiarity. The last vitamin is not decayed, but simply absorbed. Therefore, to get vit_3, the concentration of vit_1 must be greater than 1.

1.6. A little about effects

I won't go through the effects in full. I will only focus on what I use in the examples. Namely, sending a message to the logs and displaying the name of the effect in the character window.

    "id": "some_vit",
    "excess": "TM_some_vit",
    "min": 0,
    "max": 60,
    "disease_excess": [ [ 1, 30 ], [ 31, 60 ] ]
    "type": "effect_type",
    "id": "TM_some_vit",
    "max_intensity": 2,
    "name": [
      "LVL 1",
      "LVL 2"
    ],
    "desc": [
      "some text for LVL1 ",
      "some text for LVL2"
    ],
    "apply_message": [
      ["START Effect none > lvl 1", "mixed" ],
      ["Change lvl 1 > 2", "mixed" ]
    ],
    "decay_messages": [
      ["Change lvl 2 > 1", "mixed"]
    ],
    "remove_message": "STOP Effect lvl 1 > none"

Vitamin some_vit has two ranges 1..30 and 31..60. These are converted into intensity levels of the effect TM_some_vit. The current amount of vitamin determines which intensity level of the effect is in effect at a given time.

So if you take a drug with a vitamin concentration of 60. Then the second level will act, which after some time will decrease to the first level. And after some more time, the concentration will drop to 0 and the effect will disappear. The log will show the following messages:

  1. Change lvl 1 > 2
  2. Change lvl 2 > 1
  3. STOP Effect lvl 1 > none

And if you use an additional vitamin that will decay by some_vit. Then its concentration will smoothly increase from 0 to 60, then smoothly fall from 60 to 0. And the effect will go from 1 level of intensity to 2 then back to 1. The log will show the following messages:

  1. START Effect none > lvl 1
  2. Change lvl 1 > 2
  3. Change lvl 2 > 1
  4. STOP Effect lvl 1 > none

The maximum possible intensity level is specified in "max_intensity": 2. For the example, it is equal to 2. It may not coincide with the number of ranges in vitamins. This will not cause a game error, but may affect the correct operation of the drug.

name and desc - name and descriptions, first for level 1, then for level 2 and so on up to max_intensity value. It is possible not to fill and leave empty space or not to specify these parameters. They are responsible for displaying the effect in the character window (@).

apply_message - the message when the effect occurs and when switching from a low intensity level to a high one (for example, from 1 to 2). Mixed - responsible for the color of the message.

remove_message - message when the effect is finished.

decay_messages - message at transition from high to low intensity level (for example, from 2 to 1).

Some related problems are indicated #76886

Important: Do not use one effect for different vitamins. #76963

File with examples: test_med.json

IdleSol commented 1 month ago

2. A drug model and an example of its implementation

2.1. A simple drug model. Or the first level of complexity

This model assumes that the drug takes effect immediately after administration.

pict 1

The maximum concentration of the vitamin occurs at the time of drug consumption. Over time, the concentration decreases to zero. The effect lasts either all the time or up to a certain concentration value. A blood test shows the presence of the drug while the effect is in effect.

This model can be used for all types of drugs that require a syringe to use. For example morphine.

Example. TEST A-MED: Simple A drug that takes effect immediately after use. Time of action is 1 minute.

Since the drug must act immediately after use, the vitamin that has the effect is specified in the use_action section. Its vit_type takes the value drug.

The action time is 1 minute. This is a rather small action time, it is better to choose a precision close to 1 second. Or "rate": "1 s". Then the concentration of the vitamin is equal to 60 second / 1 second = 60 units.

This is enough to describe the drug itself:

  {
    "id": "test_AMED_simple",
    "type": "COMESTIBLE",
    "comestible_type": "MED",
    "name": { "str_sp": "TEST A-MED: Simple" },
    "description": "Abstract medicine: simple",
  ...
    "use_action": {
      "type": "consume_drug",
      "activation_message": "You take some %s.",
      "vitamins": [ [ "vit_AMED_simple", 56 ] ]
    }
  },

Clarification. Why 56 and not 60? Because the time of consumption of the drug affects the duration. And at this concentration, the effect will wear off after 60 seconds. But taking a second dose would only add 56 units. So instead of two minutes, it would be one minute and 56 seconds.

The example specifically sets this concentration. This shows that an accurate calculation is possible, provided that certain conditions are met.

In practice, I would recommend putting exactly as much as you get. That is, 60 units. And do not pay attention to the error. It is all the more noticeable on drugs with a duration of action of less than a minute. For 5 minutes, 4 seconds it is almost imperceptible. Plus it's easier for other people to understand. 60 units of 1 second means it lasts 1 minute.

What can we say about the vitamin?

We know that its concentration can reach 60 units. How many doses taken at the same time will be affected? Let's say two doses. Then the maximum concentration is 120 units.

The presence of the vitamin in the character's body should have an effect. So we need a field to specify the effect identifier: excess. Let's assume that the effect does not depend on the concentration of the vitamin and is always active when the vitamin is present. Then the range of the effect is from 1 to 120 units of the vitamin: "disease_excess": [ [ 1, 120 ] ]

This is generally enough to describe the vitamin:

  {
    "id": "vit_AMED_simple",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "A-MED: Simple" },
    "excess": "TEST_MESSAGE_AMED_simple",
    "min": 0,
    "max": 120,
    "rate": "1 s",
    "disease_excess": [ [ 1, 120 ] ]
  },

Now we need to describe the effect. In our case, it's quite simple:

  {
    "type": "effect_type",
    "id": "TEST_MESSAGE_AMED_simple",
    "name": ["A-MED: Simple"],
    "max_intensity": 1,
    "rating": "good",
    "apply_message": "A-MED: Simple. BEGIN",
    "remove_message": "A-MED: Simple. END",
    "blood_analysis_description": "A-MED: Simple"
  },

When the effect begins and ends, the corresponding messages are output to the log. And for blood analysis, the message specified in blood_analysis_description is output.

Because it is a simple model, it scales well with the number of doses taken. Each successive dose increases the concentration of the vitamin, thus prolonging the effect of the drug.

Intensity levels can be added, from taking additional doses:

    "disease_excess": [ [ 1, 60 ] , [ 61, 120 ] ]
----
    "max_intensity": 2

That's two levels of intensity. Where [ 1, 60 ] corresponds to one tablet taken and [ 61, 120 ] to two tablets. The important thing is to remember to change the maximum concentration: "max": 120.

Clarification. If you take a preparation with a vitamin concentration of 1000, but leave the value "max": 120. The character will get only 120 units. But it will be displayed that you have consumed 1000 units in a day.

You can add a threshold beyond which the vitamin has no effect, but continues to display a message for the analyzer. There are two ways to do this.

A more complicated way is to calculate the base values of the effects and their modifications. So that they would not work in a certain range. Or use adding an effect through the deficiency field.

You can see how it works on the example: TEST A-MED: Simple v2. This is the analog of TEST A-MED: Simple. But when the concentration of the vitamin drops to 10 units, the effect is replaced.

  {
    "id": "vit_AMED_simple_v2",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "A-MED: Simple v2" },
    "deficiency": "TEST_MESSAGE_AMED_simple_v3",
    "excess": "TEST_MESSAGE_AMED_simple_v2",
    "min": 0,
    "max": 120,
    "rate": "1 s",
    "disease": [ [ 1, 10 ] ],
    "disease_excess": [ [ 11, 60 ] ]
  },
  {
    "type": "effect_type",
    "id": "TEST_MESSAGE_AMED_simple_v2",
    "name": ["A-MED: Simple v2"],
    "max_intensity": 1,
    "rating": "good",
    "apply_message": "A-MED: Simple v2 [ 11, 60 ]. BEGIN",
    "remove_message": "A-MED: Simple v2 [ 11, 60 ]. END",
    "blood_analysis_description": "A-MED: Simple"
  },
  {
    "type": "effect_type",
    "id": "TEST_MESSAGE_AMED_simple_v3",
    "name": ["A-MED: Simple v3"],
    "max_intensity": 1,
    "rating": "good",
    "apply_message": "A-MED: Simple v3 [ 1, 10 ]. BEGIN",
    "remove_message": "A-MED: Simple v3 [ 1, 10 ]. END",
    "blood_analysis_description": "AMED: Simple"
  },

In the example, two effects have the same text for a blood test. Thus the player will not know that the effect has been replaced, but will know that he still has the corresponding drug in his body.

IdleSol commented 1 month ago

2.2. Drug model. The second level of complexity

This model assumes that the drug starts to act immediately after use. But it reaches its maximum effect after some time. After that, there is a gradual decrease in concentration.

pict 2

This means that the vitamin is delivered immediately after taking the drug. Within a given time, the concentration rises and reaches a peak. After that, the concentration decreases to zero. The rates of increase and decrease can be different.

The effect lasts either all the time or as long as the concentration of the vitamin is above a certain value. A blood test shows the presence of the drug while the effect is in effect.

This model can be used for cigarettes and some pills.

How to realize it? Pay attention to the real tablets. This is the active ingredient encapsulated in a gelatin capsule. This is what I will imitate, as well as exploit the possibility of decomposition of one vitamin into another vitamin.

So, we need a vitamin to realize the capsule. Its entire concentration enters the body when the drug is taken. Then it gradually drops to zero... Look at a simple model of drug.

All we need to do is add the breakdown of that vitamin into a vitamin that will cause the effect we want.

pict 3

Example. TEST A-MED: Second A drug that begins to take effect immediately after use, reaches its maximum concentration one minute later. After another 2 minutes, it disappears completely.

I won't repeat the reasoning from the example for the simple model. We need two vitamins: a capsule and an active ingredient. Duration of action 60 and 120 seconds. Decay rate of 1s to improve accuracy. This is what the drug will look like:

  {
    "id": "test_AMED_second",
    "type": "COMESTIBLE",
    "comestible_type": "MED",
    "name": { "str_sp": "TEST A-MED: Second" },
    "description": "Abstract medicine: second",
  ...
    "use_action": {
      "type": "consume_drug",
      "activation_message": "You take some %s.",
      "vitamins": [ [ "vit_capsule_AMED_second", 60 ] ]
    }
  },

As you can see, this is actually a repeat of the drug from the first example. The names and the vitamin used, which emulates a dissolving capsule, have been changed.

The description of the capsule vitamin and its effect also repeat the first example:

   {
    "id": "vit_capsule_AMED_second",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "Cap AMED: Second" },
    "excess": "TEST_MESSAGE_capsule_AMED_second",
    "min": 0,
    "max": 120,
    "rate": "1 s",
    "disease_excess": [ [ 1, 120 ] ],
    "decays_into": [ [ "vit_AMED_second" , 3 ] ]
  },
  {
    "type": "effect_type",
    "id": "TEST_MESSAGE_capsule_AMED_second",
    "name": ["Capsule AMED: Second"],
    "max_intensity": 1,
    "rating": "good",
    "apply_message": "Capsule dissolved. BEGIN",
    "remove_message": "Capsule dissolved. END"
  },

Clarification: there are some problems with the length of vitamin names. At a certain name length, the vitamin concentration does not fit in the debug menu window. If you encounter this, just change the name. It took me a long time to figure out why I was getting a concentration of 11 after taking two vitamins with a concentration of 60. And there just the last digit was just outside the window boundary.

The effect "lost" the parameter responsible for displaying a message during blood test. Accordingly, the blood test does not show the presence of the capsule in the character's body.

Line "decays_into": [ ["vit_AMED_second" , 3] ] has been added for vitamin vit_capsule_AMED_second.

According to the conditions of the task, the effect should end 2 minutes after reaching the maximum concentration. Absorption rate is 1 second. This means that 1 minute after taking the drug, we need to get a concentration equal to 120 units. But during this minute, an additional 60 units of vit_AMED_second will be absorbed into the body. This means that we need 180 units, of which 60 units are absorbed during the first minute and 120 units during the next 2 minutes.

And since the concentration of vit_capsule_AMED_second vitamin is 60 units. We must convert each vitamin of the capsule into 3 vit_AMED_second vitamins.

Note 1. Remember that the last vitamin does not decay, so we get 118 vitamins instead of 120. And also that a 60 vitamin capsule is about 64 seconds, instead of 60... And this can be adjusted, for more accurate results. But for simplicity's sake, I'm leaving it as is.

Note 2. By varying the concentrations and rate values, you can adjust the rates of entry and exit of the drug from the body. For example, you can get 1000 vitamins in seconds, but take days to get them out.

Note 3. But there is a limitation. The concentration of a vitamin must be an integer. You cannot decompose 1 vitamin into half a vitamin. This must be taken into account when choosing the rate value. So that you do not get a calculated concentration value that cannot be output in a given time.

For example above, if we needed to remove the entire vitamin vit_AMED_second in 30 seconds, instead of 2 minutes. We could not do this with a vit_capsule_AMED_second concentration of 60 units. It would have to be reduced to 30 with rate = 2s. Then after 60 seconds we would have 90 units of vit_AMED_second of which 60 units would have already been absorbed. That would leave 30.

But the rate is responsible for the accuracy of matching the calculated and actual duration of the drug. In this case, the error would be 1 second. Rate = 1 minute? The margin of error is 0 to 59 seconds.

Back to the example. After completing the calculation, we have enough information to describe the second vitamin and its effect:

  {
    "id": "vit_AMED_second",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "A-MED: second" },
    "excess": "TEST_MESSAGE_AMED_second",
    "min": 0,
    "max": 240,
    "rate": "1 s",
    "disease_excess": [ [ 1, 10 ], [ 11, 120 ], [ 121, 240 ] ]
  },
  {
    "type": "effect_type",
    "id": "TEST_MESSAGE_AMED_second",
    "name": ["A-MED: Second"],
    "max_intensity": 3,
    "apply_message": [
      [ "A-MED: Second. 0 -> [ 1, 10 ]", "good"],
      [ "A-MED: Second. [ 1, 10 ] -> [ 11, 120 ]", "good"],
      [ "A-MED: Second. [ 11, 120 ] -> [ 121, 240 ]", "good"]
    ],
    "decay_messages": [
      ["A-MED: Second. [ 11, 120 ] -> [ 1, 10 ]", "bad" ],
      ["A-MED: Second. [ 121, 240 ] -> [ 11, 120 ]", "bad" ]
    ],
    "remove_message": "A-MED: Second. [ 1, 10 ] -> 0",
    "blood_analysis_description": "AMED: Second"
  },

The concentration ranges and intensity levels of the effect are set to show in which range the vit_AMED_second concentration is. It is possible to monitor via the debug menu, but not very convenient. For a real drug, the values for the effects should be specified here. Something like:

Note. There may be unaccounted transitional periods. For the example above, this is the moment between 4 and 5 seconds of drug action (or 8 and 9 seconds from the time the drug is taken). At the time of 4 seconds, the concentration of the vitamin is 8. As we move from 4 to 5 seconds, 3 vitamins are added and the concentration becomes 11 (get the message), then immediately one vitamin is ingested and the concentration drops to 10 (get the second message). Not that it makes any real difference when implementing effects. But if you are supposed to use message output as in the example, it can be considered a bug.

Scaling is no longer as smooth as in the simple model. If in reality you take two pills at the same time, they will be digested at the same time. Simply because they will be digested independently of each other. And you will get the total amount of active ingredient of that drug. I don't know if the rate of drug absorption depends on the concentration of the drug. I assume not or not for all types of active ingredient.

In the game, however, the situation will be different. Taking two pills still gives the total amount of vitamins. But the process will be stretched in time. As if the second pill starts to dissolve only when the first pill has finished dissolving.

If you use numbers. In reality. Taking the two tablets from the example above would give 240 units of usable vitamin 60 seconds after ingestion. And 2-4 minutes later, the drug would have been eliminated from the body. Total total time of action is 3-5 minutes.

In the game. We will get 120 units of the capsule, which will take 2 minutes to dissolve. After 60 seconds from ingestion, we'll have 120 units of usable vitamin. And only after 120 seconds from ingestion, we will have these 240 units of usable vitamin. After that, we need another 4 minutes to excrete it. That's a total total action time of 6 minutes.

In fact, in the game, taking a second dose not only increases the strength of the effect, but also increases the duration. This is especially important for drugs if the dissolution time of the capsule is comparable to the elimination time of the vitamin from the body.

IdleSol commented 1 month ago

2.3. Drug model. Third level of complexity

This model assumes that the drug has some delay before the capsule begins to dissolve. And then it acts as a model of the second level of complexity.

pict 4

During this delay, the character's body lacks the vitamin that has the main effect. After the delay ends, there is a smooth increase in concentration, followed by a smooth decrease in concentration.

I'm pretty sure this could be implemented differently. By plugging in EoC or additional effects. But I'm going to use vitamins.

This model assumes the use of supplemental vitamins, which will decrease the concentration of other vitamins, at the same point in time that this concentration will increase. But due to the difference in concentration, and therefore duration, the necessary delay will be achieved.

An important point will be the use of a feature: the last vitamin is not decayed into other vitamins.

Examples are medicines with a delayed onset of action.

Example. TEST A-MED: Third The drug from the past model, but there is a 1 minute delay before the action begins.

pict 5

  {
    "id": "test_AMED_third",
    "type": "COMESTIBLE",
    "comestible_type": "MED",
    "name": { "str_sp": "TEST A-MED: Third" },
    "description": "Abstract medicine: third",
...
    "use_action": {
      "type": "consume_drug",
      "activation_message": "You take some %s.",
      "vitamins": [ [ "vit_delay_I_AMED_third", 60 ], [ "vit_delay_II_AMED_third", 59 ]]
    }
  },

Note, two vitamins vit_delay_I_AMED_third and vit_delay_II_AMED_third are used. But their concentration differs by 1 unit. This difference is always used and is independent of the duration of the required delay. The duration is determined by the concentration of vit_delay_I_AMED_third and the value of rate. In this example it is 60 seconds.

Here's a description of these vitamins:

  {
    "id": "vit_delay_I_AMED_third",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "Del I AMED: Third" },
    "min": 0,
    "max": 120,
    "rate": "1 s",
    "decays_into": [ [ "vit_delay_AMED_third" , 2 ] ]
  },
  {
    "id": "vit_delay_II_AMED_third",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "Del II AMED: Third" },
    "min": 0,
    "max": 118,
    "rate": "1 s",
    "decays_into": [ [ "vit_delay_AMED_third" , -2 ] ]
  },

For the delays to work properly, they must be the same. Except for two parameters: max and decays_into.

The parameter max is responsible for the maximum concentration. It is calculated based on the required duration multiplied by the number of doses that have an effect. In this example, we need a delay of 60 seconds. This means that we need to administer 60 vitamins. Let's assume that only two pills have an effect. Then 60x2=120.

Hence the value "max": 120 for vit_delay_I_AMED_third.

Vitamin vit_delay_II _AMED_third should act for 1 tick of time less, in this case 1 second. Therefore, its concentration is 59. And the maximum value is 59x2 = 118.

Both of these vitamins are decayed into a third vitamin vit_delay_AMED_third, which plays the role of delay. But while Del I decays to a positive value, Del II decays to a negative value. Which results in a 0.

Here's a description of vit_delay_AMED_third:

  {
    "id": "vit_delay_AMED_third",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "Del AMED: Third" },
    "min": 0,
    "max": 2,
    "rate": "1 s",
    "decays_into": [ [ "vit_capsule_AMED_third" , 60 ] ]
  },

It must necessarily have the same rate value as vitamin Del I and Del II. And its maximum concentration should be limited to 2 units.

How it works?

name \ time 61 sec 62 sec 63 sec 64 sec 65 sec
Del I 3 2 1 0 0
Del II 2 1 0 0 0
Del 0 0 2 1 0
Capsule 0 0 0 60 59

61 seconds. Both vitamin Del I and Del II are decayed into vitamin Del = (2) + (-2) = 0 62 seconds. Del II does not decay to -2 Del because it is the last unit. And so at 63 sec, the concentration of Del increases to 2. 63 seconds. The concentration of Del I is 1, so there's no decay. And the Del concentration is 2 and there's a decay into 60 capsule units. 64 seconds. We have 60 capsule units. Del is 1, so again no decay occurs.

So what happens if you take two pills? The difference between the concentration of vitamin I and II will increase to 2 units.

name \ time 120 sec 121 sec 122 sec 123 sec 124 sec 125 sec
Del I 4 3 2 1 0 0
Del II 2 1 0 0 0 0
Del 0 0 2 2 1 0
Capsule 0 0 0 60 119 118

And this is where the max parameter for vitamin Del comes in, which prevents the concentration from increasing above 2.

121 seconds. Decays Del I into two vitamins Del. 122 sec. Del I decays again into two Del vitamins. And its concentration should have become 4. But of those 4 Del vitamins, 1 is decayed into 60 vitamins of the capsule. So the concentration should become 3. But we have a limitation. So the concentration remains equal to 2. 123 seconds. Del I stops decaying into Del. And vitamin Del decays into an additional 60 capsules. 124 seconds. The concentration of Del is equal to one. No new capsule vitamins.

Note. I haven't checked, but I assume that if you add an effect to these three vitamins and output messages to the log. Every second there will be a message about the start and end of this effect.

The remaining vitamins and effects are the vitamins and effects from the second model. In which the word second has been replaced by the word third. So I won't take them apart, I'll just give you the code:

  {
    "id": "vit_capsule_AMED_third",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "Cap AMED: Third" },
    "excess": "TEST_MESSAGE_capsule_AMED_third",
    "min": 0,
    "max": 180,
    "rate": "1 s",
    "disease_excess": [ [ 1, 120 ] ],
    "decays_into": [ [ "vit_AMED_third" , 3 ] ]
  },
  {
    "type": "effect_type",
    "id": "TEST_MESSAGE_capsule_AMED_third",
    "name": ["Capsule AMED: Third"],
    "max_intensity": 1,
    "rating": "good",
    "apply_message": "Capsule dissolved. BEGIN",
    "remove_message": "Capsule dissolved. END"
  },
  {
    "id": "vit_AMED_third",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "A-MED: Third" },
    "excess": "TEST_MESSAGE_AMED_third",
    "min": 0,
    "max": 240,
    "rate": "1 s",
    "disease_excess": [ [ 1, 10 ], [ 11, 120 ], [ 121, 240 ] ]
  },
  {
    "type": "effect_type",
    "id": "TEST_MESSAGE_AMED_third",
    "name": ["A-MED: Third"],
    "max_intensity": 3,
    "apply_message": [
      [ "A-MED: Third. 0 -> [ 1, 10 ]", "good"],
      [ "A-MED: Third. [ 1, 10 ] -> [ 11, 120 ]", "good"],
      [ "A-MED: Third. [ 11, 120 ] -> [ 121, 240 ]", "good"]
    ],
    "decay_messages": [
      ["A-MED: Third. [ 11, 120 ] -> [ 1, 10 ]", "bad" ],
      ["A-MED: Third. [ 121, 240 ] -> [ 11, 120 ]", "bad" ]
    ],
    "remove_message": "A-MED: Third. [ 1, 10 ] -> 0",
    "blood_analysis_description": "AMED: Third"
  },

The scaling problems are the same as for the second level model. Only they stretch the duration of the drugs' effect even further. There may be something else I haven't encountered yet.

And the main problem is the massive increase in the number of vitamins. 5 separate vitamins for one drug. More precisely on one line of drugs, which differ from each other only in the duration of the delay. (Change the concentration of vitamins Del I and Del II).

Note. With some calculations, it is possible to increase the difference between their concentrations, which will give an extra dose of capsule vitamin. But would probably break scalability from multiple doses.

IdleSol commented 1 month ago

2.4. Combined drug model. Or fourth level of complexity

We take the first three models and include them in one drug. If you try hard enough, you can emulate complex functions, for example, logarithms or exponentials. We simply divide them into sections and replace them with a linear function. In general, there is a lot of room for imagination and a lot of math.

So, if I wanted to implement analgin: 1 hour of work as a painkiller, 10 days as a blood thinner.

I would take a level 2 model to work as a painkiller. And a level three model for the blood thinner. I would set a delay until about the end of the painkiller effect (I don't understand anything about medical drugs), after which I would implement a simple model for the effect of the vitamin for 9-10 days.

I didn't create a ready-made example.

IdleSol commented 1 month ago

2.5. Exploitation of the features of existing vitamin implementation

At the beginning of the topic (1.3), I said that time is divided into segments of length equal to rate. And this can be exploited. When we need not the duration of action of a drug, but let's say the conditions for the occurrence of a certain time. It's easier to give an example. You know the Cinderella story, right? Midnight and the carriage turns into a pumpkin.

  {
    "id": "test_AMED_midnight",
    "type": "COMESTIBLE",
    "comestible_type": "MED",
    "name": { "str_sp": "TEST A-MED: Midnight" },
    "description": "Feel like Cinderella",
...
    "use_action": {
      "type": "consume_drug",
      "activation_message": "You take some %s.",
      "vitamins": [ [ "vit_capsule_AMED_midnight", 2 ] ]
    }
  },
  {
    "id": "vit_capsule_AMED_midnight",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "Cap A-MED: Midnight" },
    "min": 0,
    "max": 10,
    "rate": "24 h",
    "decays_into": [ [ "vit_AMED_midnight" , 2 ] ]
  },
  {
    "id": "vit_AMED_midnight",
    "type": "vitamin",
    "vit_type": "drug",
    "name": { "str": "A-MED: midnight" },
    "excess": "TEST_MESSAGE_AMED_midnight",
    "min": 0,
    "max": 2,
    "rate": "1 s",
    "disease_excess": [ [ 1, 2 ] ],
    "decays_into": [ ["vit_capsule_AMED_midnight" , -1] ]
  },
  {
    "type": "effect_type",
    "id": "TEST_MESSAGE_AMED_midnight",
    "max_intensity": 1,
    "rating": "good",
    "apply_message": "Midnight, time to turn the carriages into pumpkins."
  }

Vitamin vit_capsule_AMED_midnight decays at exactly midnight into two vit_AMED_midnight vitamins. Which in turn outputs a message to the log and removes the remaining vit_capsule_AMED_midnight vitamin.

In this case, this drug does not depend on what time it is taken. It can be taken in the morning or at night.

This doesn't work well for medical preparations, but it works well for magic potions.

IdleSol commented 1 month ago

3. How it could be

A drug is a means of delivering the active ingredient into the character's body. All we need is a description of a function representing the increase in concentration of the active drug. In general, the curve of concentration change from dissolving the drug in the body, without taking into account what happens to the active ingredient itself. Whether it is absorbed, disintegrated or excreted from the body is not important to us.

And the description of the active substance is a description of the function of concentration reduction and the effects on different parts of this function.

Then the drug could look like this:

    "id": "some_drug",
    "type": "COMESTIBLE",
...
    "use_action": {
      "type": "consume_drug",
      "activation_message": "You take some %s."
    },
    "active_ingredient": [ 
      [ "vit_1", "main": true, "concentration": [ 0, 120 ], "delta_t": "1 s", "period": [ 0, "10 s" ] ],
      [ "vit_1", "main": true, "concentration": [ 120, 240 ], "delta_t": "10 s", "period": [ "10s", "70 s" ] ],
      [ "vit_1", "main": true, "concentration": [ 240, 240 ], "delta_t": "10 m", "period": [ "70s", "10 m 70 s" ] ],
      [ "vit_1", "main": true, "concentration": [ 240, 480 ], "delta_t": "10 s", "period": [ "10 m 70 s", "11 m 70 s" ] ],
      [ "vit_2", "main": false, "concentration": [ 0, 120 ], "delta_t": "1 h", "period": [ "1h", "24h" ] ],
...
      [ "vit_N", "main": false, "concentration": [ 0, 120 ], "delta_t": "1 h", "period": [ "1h", "24h" ] ],
    ]

Where: vit_1 is the id of the active ingredient ("type": "active_ingredient"). New type to have compatibility with existing vitamins. main is an indicator for the main active ingredient. Which sets the duration of the drug and its effects. concentration is the concentration of the active ingredient. The initial value and the final value at the time interval defined in the period parameter. delta_t - shows how often the concentration change should occur. period - time interval for which the concentration is described.

In fact, we take a function of concentration from time. Break it down into periods so that it looks like a linear function. Find the concentration value. And specify the frequency with which the active ingredient should be added.

Moreover, if we sum up all the periods, we will get the time of dissolution of the drug in the character's body. If in the first period (or periods), the concentration remains at the value 0. This is the time of delaying the effect of the drug.

Knowing the id of the active ingredient and the concentration values, you can calculate the duration of action of the drug. And looking at the effects listed for that ingredient, you can pull out the names.

And all of this can be displayed in the drug description. Most importantly, in automatic mode.

What's more, you can memorize the time of drug use. And count all the values from it.

And this is what the active ingredient could look like:

    "id": "some_drug",
    "type": "active_ingredient",
    "name": { "str": "A-MED: Simple" },
    "min": 0,
    "max": 960,
    "rates": [
      ["concentration": [ 0, 120 ], "rate": "1 m", "count": 1 ],
      ["concentration": [ 121, 300 ], "rate": "10 s", "count": 1 ],
      ["concentration": [ 301, 960 ], "rate": "1 s", "count": 5 ],
    ],
    "effect": [ 
      [ "eff_1", "main": true, "concentration": [ 0, 120 ] ],
      [ "eff_1", "main": true, "concentration": [ 121, 240 ] ],
      [ "eff_1", "main": true, "concentration": [ 241, 480 ] ],
      [ "eff_1", "main": true, "concentration": [ 481, 960 ] ],
...
      [ "eff_N", "main": false, "concentration": [ 0, 120 ] ]
       ] 
    ]

The rates section describes the concentration ranges of the active ingredient and the rate of change of concentration within that range. For example:

      ["concentration": [ 0, 120 ], "rate": "1 m", "count": 1 ],

This means that 1 unit of active ingredient is lost every minute. And in a range over 300 units: "rate": "1 s", "count": 5, 5 units are lost every second.

Similarly with effects. Multiple concentration ranges and multiple effects. Each effect has its own number of ranges, which are converted to intensity levels. Either add an intensity parameter and specify the desired level at once.

We can also memorize the time the active ingredient appears in the body. And use that as a reference point. And use it until the concentration drops to 0. I.e. taking a new portion of the active ingredient does not affect the reference point if the concentration is greater than zero. And it sets a new time if the concentration is 0.

Maddremor commented 1 month ago

So what is the suggestion here?

IdleSol commented 1 month ago

Does it have to be a suggestion? Then you might consider point 3. And expand the functionality of the vitamins.

UPD: Or you could get into updating existing drugs (or fixing their problems #76830). Then this topic is a guide. But is it right to use such a method?

So this is a guide - a discussion - a request for change.

Maddremor commented 1 month ago

If you cannot describe what you are proposing in the first 6 messages, perhaps you need to rewrite things. If you want to implement as system the space to do so is in a PR.

Hyperseeker commented 1 month ago

It may not be the ideal contribution – that being a well-tested PR with clean code – but it is a contribution with the potential to spark discussion and novel design considerations. Ideas are cheap, but detailed elaborations are few and far between. DDA should encourage in-depth analysis like this, not discourage it because it's imperfect.

harakka commented 1 month ago

Detailed elaborations are useful when they're clear and concise enough that for people who want to do the work, it is obvious from the description what the problem is that the issue is describing, and what the proposed concrete solution is. I can't get either from this wall of text.

Hyperseeker commented 1 month ago

"This is an interesting concept. However, it doesn't meet our standards when it comes to feature proposals. Please make sure the proposal is:"

And then you list those exact italicized points.

Harnessing the stray talent of users motivated enough to write walls of text can be beneficial to the project. For one, you're making the users feel involved, rather than alienated, for making the effort.

IdleSol commented 1 month ago

I would like to get an answer from you. Is it possible to create a drug that satisfies the following conditions:

  1. The drug is taken on the first day of the game (61 days of spring). The time of ingestion is between 8:00:00 and 23:59:00.
  2. At 7:00:00 the next day (day 62), any message is output to the log. Arbitrary text.

Also I would like to get the json to see how it works.