Mixer objects should encapsulate any abnormal behavior for when two liquids come in contact.
(Note that contact may not mean that they mix or combine or whatever, since sometimes liquids float on other liquids -- such floating should also be considered contact between liquids.)
The mixer object should at least have a mix() method. This should probably take as arguments the two liquid instances making contact. The mixer object will then perform whatever action is necessary on one or both of the two liquids, perhaps changing them into a third liquid, or a third and fourth liquid. The mixer might make them explode or vaporize the container or make any other adjustments to the game state.
For every pair of liquids there can be a mixer object. Whenever the liquids are supposed to combine according to the liquid library defaults, a mixer object will not be necessary for the user to code; mixer objects are only necessary when the user wants some special behavior when the liquids come into contact.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The mixing handler will keep track of mixer objects. A preinit object should initialize the mixing handler, and create a lookup table keyed by liquid pairs.
Whenever two liquids come in contact with one another, the lookup table is consulted, and if the pair of liquids return a match for a mixer object, the mixer object's mix() method is called after the liquid has been moved into contact.
If the lookup table does not return a mixer object, the combination of liquids proceeds according to liquid library defaults.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
If the two liquids coming in contact with each other are already mixtures of multiple components, the order in which the mixer objects are called should be able to be determined by the user.
For example, if there's a "stabilizer" and a "agitator" mixed together in one container, and an "explosive" in another container. The stabilizer+explosive has one mixer object (to neutralize the explosive), and the agitator+explosive has another mixer object (to activate the explosion). When the player combines the three, we need to be able to determine whether the stabilizer neutralizes the explosive before the agitator agitates it, or if the agitator makes the explosion explode before the stabilizer stabilizes it.
Mixer objects of course should be able to check what other components are in the liquids being combined, and adjust their behavior accordingly, but it probably matters what order the mixer objects' mix() methods are called. This order should probably be definable by the user.
-------------
Some sort of initial check against the mixer objects' chemistry should be possible. For example, if we are going to mix an explosive with an agitator, we should be able to check if this action is "dangerous" before the action is processed. (Note that "dangerous" actions are not supposed to be permitted as implicit actions or assumed based on logical ranking; this is a technical distinction that the main library makes.)
There are probably other reasons we might want to be able to anticipate the mixer object's actions before we actually process the mixing action. Or, if this is the only concern, we might make a mixerObject isDangerous boolean property, and make that check a part of the mixture verification procedure. I haven't worked this through, and even this might be more complicated than it sounds.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Sorry about being quiet for some time, but real life has kept me occupied lately.
Brifely, I like some of the ideas you've come up with here and I'm trying to put an updated test-game together (it'll probably be done one of these days).
The stabilizer/agitator timing problem you mention probably won't be a problem, since mixing a stabilizer and an agitator would normally produce a liquid neither agitating nor stabilizing. (If equal amounts of agitator and stabilizer was mixed, anyway. In case there's more og one than another, one would simply "win", as I see it.)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
What algorithm are you considering for processing actions ("Drink" for example)?
Is it like this?
The verb calls the action processing of the liquid game-object.
The liquid game-object silently calls the relevant methods for each of the components. Or checks a property in each component which signals that a special action is defined for that verb in that component.
If one special method is found, the action is handled through that method. If two special methods are found, both methods are called.
If none are found, the default method is called.
-------
This isn't a suggestion. It's just a question how you intend to handle this problem of figuring out how special methods conflicting or otherwise interacting.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Well, I figure that whenever you have a liquid with components A, B and C, and you drink/light/douse/boil/etc. the liquid, each component will be drunk/lit/doused/boiled/etc. "separately".
That is, each component have the same effect when mixed in a liquid, as it has when it's the only component of a liquid.
(Of course, this could be overriden by the author.)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
>Well, I figure that whenever you have a liquid with
>components A, B and C, and you
>drink/light/douse/boil/etc. the liquid, each component
>will be drunk/lit/doused/boiled/etc. "separately".
This might be the best possible default behavior, but it doesn't sound good. At the least we'll have to make a override system for authors who want to combine component methods. I've been thinking about this a lot, and almost have a good conception of the problem. I'll write something up today or tomorrow. -- You might want to hold off until then if you're going to put these ideas into code. Or you might not. ;)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Perhaps a default mixer object should be defined, and should process all default mixing. This would presumably involve the creation of a new liquid instance, and update the component data. Then every component pair gets a mixer object, but it's just the default mixer object unless the user defines a custom mixer object.
The user can define a custom mixer object based on the default mixer, and then can modify the mixing mechanism instead of having to rewrite the entire mixing mechanism when customizing.
What is involved in "the mixing mechanism"? I.e., what actions should the default mixer object govern?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
To mix two liquids, you'd first build a linear combination of the components (combining like kinds into single components of larger volumes), then you'd run through each possible pair in the surviving set, looking for a chemistry op (i.e., mixer object) for the pair. Each time you find a chemistry op, you'd deduct the pair of liquids involved from the survivor list, run the op, and add the yield products back into the list. When you've run out of pairs, the surviving list is the resulting mixture.
There's the possibility of non-convergence (like in real chemistry!), in that the output of one reaction could feed into another reaction, so it might be necessary to keep reaction products in a separate list until all of the original inputs have been examined; after exhausting the list, we'd combine
whatever's left of the inputs with the outputs, and then presumably we'd run another pass, doing the same thing again; and then we'd do the same thing again after that, repeating until we run out of reactions.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Mixer objects should encapsulate any abnormal behavior for when two liquids come in contact.
(Note that contact may not mean that they mix or combine or whatever, since sometimes liquids float on other liquids -- such floating should also be considered contact between liquids.)
The mixer object should at least have a mix() method. This should probably take as arguments the two liquid instances making contact. The mixer object will then perform whatever action is necessary on one or both of the two liquids, perhaps changing them into a third liquid, or a third and fourth liquid. The mixer might make them explode or vaporize the container or make any other adjustments to the game state.
For every pair of liquids there can be a mixer object. Whenever the liquids are supposed to combine according to the liquid library defaults, a mixer object will not be necessary for the user to code; mixer objects are only necessary when the user wants some special behavior when the liquids come into contact.
The mixing handler will keep track of mixer objects. A preinit object should initialize the mixing handler, and create a lookup table keyed by liquid pairs.
Whenever two liquids come in contact with one another, the lookup table is consulted, and if the pair of liquids return a match for a mixer object, the mixer object's mix() method is called after the liquid has been moved into contact.
If the lookup table does not return a mixer object, the combination of liquids proceeds according to liquid library defaults.
Two problems with this model:
------------------
If the two liquids coming in contact with each other are already mixtures of multiple components, the order in which the mixer objects are called should be able to be determined by the user.
For example, if there's a "stabilizer" and a "agitator" mixed together in one container, and an "explosive" in another container. The stabilizer+explosive has one mixer object (to neutralize the explosive), and the agitator+explosive has another mixer object (to activate the explosion). When the player combines the three, we need to be able to determine whether the stabilizer neutralizes the explosive before the agitator agitates it, or if the agitator makes the explosion explode before the stabilizer stabilizes it.
Mixer objects of course should be able to check what other components are in the liquids being combined, and adjust their behavior accordingly, but it probably matters what order the mixer objects' mix() methods are called. This order should probably be definable by the user.
-------------
Some sort of initial check against the mixer objects' chemistry should be possible. For example, if we are going to mix an explosive with an agitator, we should be able to check if this action is "dangerous" before the action is processed. (Note that "dangerous" actions are not supposed to be permitted as implicit actions or assumed based on logical ranking; this is a technical distinction that the main library makes.)
There are probably other reasons we might want to be able to anticipate the mixer object's actions before we actually process the mixing action. Or, if this is the only concern, we might make a mixerObject isDangerous boolean property, and make that check a part of the mixture verification procedure. I haven't worked this through, and even this might be more complicated than it sounds.
Sorry about being quiet for some time, but real life has kept me occupied lately.
Brifely, I like some of the ideas you've come up with here and I'm trying to put an updated test-game together (it'll probably be done one of these days).
The stabilizer/agitator timing problem you mention probably won't be a problem, since mixing a stabilizer and an agitator would normally produce a liquid neither agitating nor stabilizing. (If equal amounts of agitator and stabilizer was mixed, anyway. In case there's more og one than another, one would simply "win", as I see it.)
What algorithm are you considering for processing actions ("Drink" for example)?
Is it like this?
The verb calls the action processing of the liquid game-object.
The liquid game-object silently calls the relevant methods for each of the components. Or checks a property in each component which signals that a special action is defined for that verb in that component.
If one special method is found, the action is handled through that method. If two special methods are found, both methods are called.
If none are found, the default method is called.
-------
This isn't a suggestion. It's just a question how you intend to handle this problem of figuring out how special methods conflicting or otherwise interacting.
Well, I figure that whenever you have a liquid with components A, B and C, and you drink/light/douse/boil/etc. the liquid, each component will be drunk/lit/doused/boiled/etc. "separately".
That is, each component have the same effect when mixed in a liquid, as it has when it's the only component of a liquid.
(Of course, this could be overriden by the author.)
>Well, I figure that whenever you have a liquid with
>components A, B and C, and you
>drink/light/douse/boil/etc. the liquid, each component
>will be drunk/lit/doused/boiled/etc. "separately".
This might be the best possible default behavior, but it doesn't sound good. At the least we'll have to make a override system for authors who want to combine component methods. I've been thinking about this a lot, and almost have a good conception of the problem. I'll write something up today or tomorrow. -- You might want to hold off until then if you're going to put these ideas into code. Or you might not. ;)
Perhaps a default mixer object should be defined, and should process all default mixing. This would presumably involve the creation of a new liquid instance, and update the component data. Then every component pair gets a mixer object, but it's just the default mixer object unless the user defines a custom mixer object.
The user can define a custom mixer object based on the default mixer, and then can modify the mixing mechanism instead of having to rewrite the entire mixing mechanism when customizing.
What is involved in "the mixing mechanism"? I.e., what actions should the default mixer object govern?
How the mixer object would perform:
To mix two liquids, you'd first build a linear combination of the components (combining like kinds into single components of larger volumes), then you'd run through each possible pair in the surviving set, looking for a chemistry op (i.e., mixer object) for the pair. Each time you find a chemistry op, you'd deduct the pair of liquids involved from the survivor list, run the op, and add the yield products back into the list. When you've run out of pairs, the surviving list is the resulting mixture.
There's the possibility of non-convergence (like in real chemistry!), in that the output of one reaction could feed into another reaction, so it might be necessary to keep reaction products in a separate list until all of the original inputs have been examined; after exhausting the list, we'd combine
whatever's left of the inputs with the outputs, and then presumably we'd run another pass, doing the same thing again; and then we'd do the same thing again after that, repeating until we run out of reactions.