-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add RussianRoulette drink #29390
Add RussianRoulette drink #29390
Conversation
Code is actually ready for review, but textures for the drink are pending. They will be added later. Marking it as "draft" for now I guess. |
RSI Diff Bot; head commit 0e89a2d merging into c723493 Resources/Textures/Objects/Consumable/Drinks/russianroulette.rsi
|
I'm afraid I can't do anything about it. space-station-14/Content.Shared/Chemistry/Reagent/ReagentEffect.cs Lines 57 to 69 in e99e659
... and is not overridable. It just means that all effects above are activated when there is more than 9.5u (or 12.5u in the latest iteration of the PR) of RussianRoulette in the metabolizer. |
I thought it was something you could edit but nvm, was mistaken. You're good. |
592a97f
to
9660c38
Compare
Honestly, I have a feeling that I accidentally made one of the most complex chemicals in the game and that guidebook auto-generated explanation isn't that good at explaining of how it needs to be applied. First of all, what could potentially be seen as a problem is that RussianRoulette is split into 3 reagents, being RussianRoulette, RussianRouletteLive and RussianRouletteBlank. It makes sense because all three can exist separately, even inside metabolizer.
... and this approach allows to investigate the contents of the drink with beer glasses. So it makes sense (at least I think so), but can be very confusing. To brew this drink correctly, the bartender needs to mix 7.5u of White Russian with 7.5u of Black Russian with 10u of Beanbag/Tricordrazine in any proportions, but this isn't explicitly reflected anywhere. |
Firstly, the idea is awesome and it shouldn't have trouble making it into the game. However, I have two suggestions: 1. only going by the drink effect wall of text makes it hard for a player to understand what's going on since there are three chemicals with one being an "activator". Couldn't it just be one chemical? or at least hide the "blanks" and "lives" part from the guidebook and leave just as "russian roullette". Having one chemical would also prevent bartenders from brewing it "incorrectly" as you said.
Reading your last comment sounds like you might have overcooked with the complexity, dumbing down the system sounds like a good option even if it's painful to do so as a developer. I hope my light criticism turns out to be constructive, and good luck! |
Thank you.
I believe the updated reagent system makes it possible to merge [blanks, lives] or even [blanks, lives, activator] into a single chemical. Merging just blanks and lives doesn't sound to be a problem, just no ability for players to inspect the glass with beer goggles and probably some extra work to update the guidebook entry (because it then needs to reflect that although there are two different recipes to russian roulette that produce seemingly same chemicals, it's actually not the same).
Sure, but this again has gameplay implications: Also, I believe that instead of forcing the randomness down the throat, it's better to incentivize the player to take the risk. |
if (baseArgs is not EntityEffectReagentArgs) | ||
return; | ||
EntityEffectReagentArgs args = (EntityEffectReagentArgs) baseArgs; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use is not syntax to do this in one line and avoid trying to cast twice.
[UsedImplicitly] | ||
public sealed partial class ActivateRandom : EntityEffect { | ||
/// <summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ should be on newline for the project.
return Loc.GetString("reagent-effect-guidebook-activate-random", | ||
("chance", Probability), | ||
("cases", string.Join("\n", Cases.ToList().Select(case_ => | ||
"- " | ||
+ string.Join(", ", case_.Activates.ToList() | ||
.Select(activate => | ||
prototype.TryIndex(activate.Reagent.Prototype, out ReagentPrototype? reagentProto) | ||
? Loc.GetString("reagent-effect-guidebook-activate-random-activate", | ||
("factor", activate.Factor), | ||
("catalyst", !activate.Consume), | ||
("reagent", reagentProto.LocalizedName)) | ||
: null) | ||
.Where(x => x is not null)) | ||
+ ":\n" | ||
+ string.Join("\n", case_.Effects.ToList() | ||
.Select(effect => | ||
{ | ||
var desc = effect.GuidebookEffectDescription(prototype, entSys); | ||
return desc is null ? null : " " + desc; | ||
}) | ||
.Where(x => x is not null)))) | ||
+ "\n")); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LINQ allocates for every chain in C# and doing this by hand will probably make this a lot more readable.
|
||
// Next, select a "random" set. Sets of higher propotions are prioritized. | ||
int index = 0; | ||
IRobustRandom _random = IoCManager.Resolve<IRobustRandom>(); | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should avoid doing IoC resolves as they use thread-locals, especially in hot loops. Just put it as a dependency on the effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can this be done? I tried using
[Dependency] private readonly IRobustRandom _random = default!;
before, but it doesn't seem to work on EntityEffect
s.
{ | ||
var _case = Cases[i]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C# style guide says no prefix _.
// present in the solution. | ||
float[] chances = new float[Cases.Length]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here we use var for styleguide.
// present in the solution. | |
float[] chances = new float[Cases.Length]; | |
// present in the solution. | |
var chances = new float[Cases.Length]; |
// When some set is activated, ActivateRandom removes reagents of the set marked as "consumed". | ||
// When the effects of the set are run, the amount of *consumed* reagents as passed to them. | ||
// This is useful, for example, in conjuction with HealthChange's scaleByQuantity | ||
FixedPoint2 consumed = 0f; | ||
foreach (var activate in Cases[index].Activates) { | ||
if (activate.Consume && args.Source.TryGetReagent(activate.Reagent, out var quantity)) { | ||
consumed += quantity.Quantity; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as above on newline, also it should be FixedPoint2.Empty or FixedPoint2.Zero I forget which.
args.Scale); | ||
foreach (var effect in Cases[index].Effects) { | ||
if (!effect.ShouldApply(newArgs, _random)) | ||
continue; | ||
|
||
if (effect.ShouldLog) | ||
{ | ||
var _adminLogger = IoCManager.Resolve<ISharedAdminLogManager>(); | ||
_adminLogger.Add( | ||
LogType.ReagentEffect, | ||
effect.LogImpact, | ||
$"Metabolism effect {effect.GetType().Name:effect}" | ||
+ $" of reagent {args.Reagent?.LocalizedName:reagent}" | ||
+ $" applied on entity {args.TargetEntity:entity}" | ||
+ $" at {args.EntityManager.GetComponent<TransformComponent>(args.TargetEntity).Coordinates:coordinates}" | ||
); | ||
} | ||
effect.Effect(newArgs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above:
- newline
- Don't resolve logger inside here.
- Styleguide for no prefix _.
Test failure isn't related to my PR, right?.. |
Yeah |
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you change it so that you don't need to grind beanbag shells (and hence creating an entirely new reagent) for the live rounds?
Are you coming back to this? |
…to russian-roulette
Oh yes, sorry for the delay. In fact, I was just fixing the conflict just now. However, I believe there is also another problem with this PR. The problem is about metamorphic glasses: they render the name of the chemical that's present the most in the solution, and I believe it's not good when people are drinking "russian roulette live rounds glass" instead of just "russian roulette glass". I originally attempted to circumvent this by making proportions the way so that "russian roulette" chemical is always prevalent in the solution, but this is dumb, makes mixing everything harder due to strange proportions and, takes a deep breath, makes the drinker suffer if he doesn't drink the mix quick enough. And I'm not sure how to fix these issues. Changing how metamorphic works so it doesn't give "russian roulette live rounds" sounds like an unnecessary change for the sake of one single chemical. I was thinking of ReagentData to make both chemicals undistinguishable, but I have a feeling it's not a suitable solution to store the quantity information. Deleting beanbags at the moment. |
I love this |
I whipped up a patch that makes live & blank rounds non-metamorphic: I need permission to merge the patch into this PR. EDIT: The only problem that remains is that the glass must be full of 30u of russian roulette to work. If less, it doesn't work, and I believe this could be considered intended, but not sure how to convey this information in-game besides the "when there'a at least 4.1 units of this reagent" in the Effects description of Russian Roulette. |
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Lonely here. |
Hello! Thank you for your contribution! We've discussed this PR internally further and while the concept of having a "russian roulette" style drink is neat, we believe that this implementation is not the way to go about it. The way to mix the drinks to get the desired effects isn't fully intuitive and it becomes its own unique system just for this drink interaction. For example a similar way to do this currently in the game using existing systems is to mix drinks in multiple opaque containers and/or use tasteless reagents (e.g. amatoxin). Because of this, the decision has been made to close this PR. Wish you the best in future contributions. |
About the PR
Adds a new "Russian roulette" drink to the game. Comes with two new recipes:
When drank, either blank or live rounds are activated, causing healing or harm respectively. Chances for each outcome are based on the amount of respective chemicals present in the solution, with ethanol contributing to the "blanks" outcome. This way, players can themselves define the risk they want to go through, and even "survive" 100% "lives" drink given they are drunk enough (have enough ethanol in their stomach) and fortune is on their side.
Activation mechanism (chemical plainly called "russian roulette") is separate from "blanks" and "lives", but is produced alongside with blanks and lives. The activation happens only when there is more than 9.5u of russian roulette in the metabolizer. Non-activated blanks and lives stay in the player and very slowly metabolize, accumulating up to 40u (40u of lives = 80 damage on activation).
Healing of blanks is ~doubled if there are at least 4u of lives present in the solution.
Lives have generic "alcohol" flavour and can be easily mixed into someone's drink, but are, again, harmless on their own.
Why / Balance
Bartending lacks fun things. Russian roulette is a (hopefully) pro-social drink that introduces new RP situations.
Technical details
This PR adds 4 new reagents:
RussianRouletteLive
/RussianRouletteBlank
(slow-metabolizing drinks that accumulates up to 40u and carry no functionality by themselves),RussianRoulette
(activator drink, which, when over 9.5u, conumes one of the former chemicals and applies respective effects once),Beanbag
(a reagent that can be made by juicing beanbag shells and is used to createRussianRouletteLive
).This PR adds two new
ReagentEffects
:ActivateRandom
andPlaySound
.ActivateRandom
is a reagent effect used by the activators. For each outcome, it assesses the amount of chemicals in the solution that contribute to it, selects one of the outcome (randomly, preferring the group with greater presence in the solution), removes all non-catalyst reagents and fires the effects, providing total amount of removed chemicals as quantity to the reagent effects being run.Each contributing chemical of each outcome can be either consumed or catalyst, and has a factor that is used to identify how much each unit contributes to the assessed presence of the entire group. For example, in case of
RussianRoulette
,Ethanol
is considered a catalyst chemical with0.25
factor that contributes to the "blanks" outcome.ActivateRandom does not remove the activator, in case of
RussianRoulette
this is made manually by each outcome, making it one-shot reagent effect.PlaySound
is a reagent effect that continuously plays some sound. In case ofRussianRoulette
it is used to play a gunshot sound in the "lives" outcome.Media
2024-06-23.20-41-07.mp4
Breaking changes
Changelog
🆑