Skip to content
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

Closed
wants to merge 11 commits into from

Conversation

gardspirito
Copy link

@gardspirito gardspirito commented Jun 23, 2024

About the PR

Adds a new "Russian roulette" drink to the game. Comes with two new recipes:

  1. 1u white russian + 1u black russian + 2u tricordrazine = russian roulette blank rounds.
  2. 1u white russian + 1u black russian + 2u beanbags (that can be made by juicing beanbag shells) = russian roulette live rounds.
    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 create RussianRouletteLive).

This PR adds two new ReagentEffects: ActivateRandom and PlaySound.
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 with 0.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 of RussianRoulette it is used to play a gunshot sound in the "lives" outcome.

Media

bildo

2024-06-23.20-41-07.mp4
  • I have added screenshots/videos to this PR showcasing its changes ingame, or this PR does not require an ingame showcase

Breaking changes

Changelog

🆑

  • add: Russian Roulette drink that can either heal or damage the drinker has been added to the game. Test your luck at the bar!

@gardspirito
Copy link
Author

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.

@github-actions github-actions bot added the Changes: Sprites Changes: Might require knowledge of spriting or visual design. label Jun 28, 2024
@gardspirito
Copy link
Author

bildo

Copy link
Contributor

github-actions bot commented Jun 28, 2024

RSI Diff Bot; head commit 0e89a2d merging into c723493
This PR makes changes to 1 or more RSIs. Here is a summary of all changes:

Resources/Textures/Objects/Consumable/Drinks/russianroulette.rsi

State Old New Status
fill-1 Added
fill-2 Added
icon Added
icon_empty Added

Edit: diff updated after 0e89a2d

@gardspirito gardspirito marked this pull request as ready for review June 28, 2024 11:17
@UbaserB
Copy link
Member

UbaserB commented Jun 28, 2024

image

This line at the end is a bit weird

@gardspirito
Copy link
Author

image

This line at the end is a bit weird

I'm afraid I can't do anything about it.
It's defined in ReagentEffect base class:

public string? GuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
{
var effect = ReagentEffectGuidebookText(prototype, entSys);
if (effect is null)
return null;
return Loc.GetString(ReagentEffectFormat, ("effect", effect), ("chance", Probability),
("conditionCount", Conditions?.Length ?? 0),
("conditions",
ContentLocalizationManager.FormatList(Conditions?.Select(x => x.GuidebookExplanation(prototype)).ToList() ??
new List<string>())));
}
}

... 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.

@UbaserB
Copy link
Member

UbaserB commented Jun 28, 2024

I thought it was something you could edit but nvm, was mistaken. You're good.

@gardspirito
Copy link
Author

gardspirito commented Jul 2, 2024

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.

  1. RussianRouletteLive/RussianRouletteBlank can be present in the metabolizer without RussianRoulette. This allows for them to be accumulated in the body for a later bang.
  2. RussianRoulette can be present in the metabolizer in quantities lower this 12.5u, which is a semi-intentionally allows bartender to brew the drink the wrong way. Side effects of this just include massive dehydration, nothing lethal.

... 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.

@luizwritescode
Copy link
Contributor

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.

  1. Also, having two separate recipes for the "blank" and the "live" removes randomness and kinda defeats the purpose and thrill of the gimmick. Maybe we shouldn't let players see if their drink has live rounds even while using beer goggles.

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!

@gardspirito
Copy link
Author

Thank you.

Couldn't it just be one chemical?

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).
Unifying all three is possible as well, but has gameplay implications: currently, it's possible to separate chemicals via ChemMaster, if we were to unify chemicals, this isn't going to be possible any longer, so this way you can't easily accumulate lots of lives/blanks in the organism before drinking/force-feeding the activator.

Also, having two separate recipes for the "blank" and the "live" removes randomness and kinda defeats the purpose and thrill of the gimmick.

Sure, but this again has gameplay implications:
It's no longer possible for the bartender to have any kind of control over the chances. Moreover, the current system allows adding new "ammo" into the game.

Also, I believe that instead of forcing the randomness down the throat, it's better to incentivize the player to take the risk.
In the current implementation, healing is greatly boosted if there were some Lives in the mixture.

Comment on lines 54 to 57
if (baseArgs is not EntityEffectReagentArgs)
return;
EntityEffectReagentArgs args = (EntityEffectReagentArgs) baseArgs;

Copy link
Contributor

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.

Comment on lines 17 to 19
[UsedImplicitly]
public sealed partial class ActivateRandom : EntityEffect {
/// <summary>
Copy link
Contributor

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.

Comment on lines 27 to 49
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"));
}
Copy link
Contributor

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.

Comment on lines 83 to 87

// Next, select a "random" set. Sets of higher propotions are prioritized.
int index = 0;
IRobustRandom _random = IoCManager.Resolve<IRobustRandom>();
{
Copy link
Contributor

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.

Copy link
Author

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 EntityEffects.

Comment on lines 71 to 72
{
var _case = Cases[i];
Copy link
Contributor

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 _.

Comment on lines 66 to 67
// present in the solution.
float[] chances = new float[Cases.Length];
Copy link
Contributor

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.

Suggested change
// present in the solution.
float[] chances = new float[Cases.Length];
// present in the solution.
var chances = new float[Cases.Length];

Comment on lines 95 to 101
// 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;
Copy link
Contributor

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.

Comment on lines 116 to 133
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above:

  1. newline
  2. Don't resolve logger inside here.
  3. Styleguide for no prefix _.

@metalgearsloth metalgearsloth added the S: Awaiting Changes Status: Changes are required before another review can happen label Jul 7, 2024
@github-actions github-actions bot added S: Needs Review Status: Requires additional reviews before being fully accepted and removed S: Awaiting Changes Status: Changes are required before another review can happen labels Jul 7, 2024
@gardspirito
Copy link
Author

Test failure isn't related to my PR, right?..

@Winkarst-cpu
Copy link
Contributor

Yeah

@github-actions github-actions bot added the S: Merge Conflict Status: Needs to resolve merge conflicts before it can be accepted label Aug 13, 2024
Copy link
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot removed the S: Merge Conflict Status: Needs to resolve merge conflicts before it can be accepted label Aug 13, 2024
Copy link
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot added the S: Merge Conflict Status: Needs to resolve merge conflicts before it can be accepted label Aug 29, 2024
Copy link
Member

@UbaserB UbaserB left a 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?

@UbaserB UbaserB added S: Awaiting Changes Status: Changes are required before another review can happen S: Undergoing Maintainer Discussion Status: Currently going through an extended discussion amongst maintainers, as per procedure. and removed S: Needs Review Status: Requires additional reviews before being fully accepted labels Aug 29, 2024
@UbaserB
Copy link
Member

UbaserB commented Sep 4, 2024

Are you coming back to this?

@gardspirito
Copy link
Author

Are you coming back to this?

Oh yes, sorry for the delay. In fact, I was just fixing the conflict just now.
Yes, it's easy to remove beanbag, I believe I could just drop it entirely from the recipe by allowing players to make live rounds via black russian + white russian mix shaking and then let them just allow to add tricordrazine into the mix to make blank rounds.

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.

@github-actions github-actions bot removed the S: Merge Conflict Status: Needs to resolve merge conflicts before it can be accepted label Sep 4, 2024
@gardspirito
Copy link
Author

Current band-aid proportions:
bildo

@gardspirito gardspirito requested a review from UbaserB September 5, 2024 10:23
@github-actions github-actions bot added S: Needs Review Status: Requires additional reviews before being fully accepted and removed S: Awaiting Changes Status: Changes are required before another review can happen labels Sep 5, 2024
@Everturning
Copy link

I love this

@gardspirito
Copy link
Author

gardspirito commented Sep 5, 2024

I whipped up a patch that makes live & blank rounds non-metamorphic:
gardspirito@161a333
bildo
This fixes the problem of ugly proportions. Although that doesn't fix the issue that slow drinking yields in bang not happening since activator metabolizes before enough is accumulated... Actually, it helps a lot, I tested, and I believe that slow drinking shouldn't be a problem — it needs to be comically slow for the roulette to not fire.

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.

@github-actions github-actions bot added the S: Merge Conflict Status: Needs to resolve merge conflicts before it can be accepted label Sep 13, 2024
Copy link
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot removed the S: Merge Conflict Status: Needs to resolve merge conflicts before it can be accepted label Sep 13, 2024
@gardspirito
Copy link
Author

Lonely here.
🥲

@SlamBamActionman
Copy link
Member

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.

@gardspirito gardspirito deleted the russian-roulette branch November 1, 2024 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changes: Sprites Changes: Might require knowledge of spriting or visual design. S: Needs Review Status: Requires additional reviews before being fully accepted S: Undergoing Maintainer Discussion Status: Currently going through an extended discussion amongst maintainers, as per procedure.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants