Talk:Oil of Garlic

From Stardew Valley Wiki
Jump to navigation Jump to search
This talk page is for discussing Oil of Garlic.
  • Sign and date your posts by typing four tildes (~~~~).
  • Put new text below old text.
  • Be polite.
  • Assume good faith.
  • Don't delete discussions.

Effect on Monster Spawning

I was looking at the code, and saw this:

In Locations\MineShaft::adjustLevelChances (systematically called by MineShaft::populateLevel)

bool num = AnyOnlineFarmerHasBuff(23);
flag = AnyOnlineFarmerHasBuff(24);
if (num && getMineArea() != 121)
{
	if (!flag)
	{
		monsterChance = 0.0;
	}
}

23 is Oil of Garlic, 24 is Monster Musk (can be double checked in Buffs.cs)

I don't see any modification to monsterChance after adjustLevelChances() has been called. I guess it would mean using Oil of Garlic (without Monster Musk) remove all monsters in the Mines (not in Skull Cavern, for which getMineArea() == 121).

I checked in-game quickly. I encountered no monster in the Mines, except on "quarry levels" (this is specified in the code right after my excerpt in the same function), and level 91; looking in the game code I don't find why, probably just a bug?

-- Charly (talk) 18:38, 23 November 2021 (UTC)

Well, this is huge! The game didn't used to function this way, I remember thinking Oil of Garlic was pretty much worthless if I was carrying a staircase, because it only prevented swarms/infested levels. I wonder when it changed?
Anyway, thanks for doing the work and for testing in-game. I'm not sure what to report as the bug -- if preventing all monsters in the mines was intended (why wasn't it in the change notes?), or just limiting it to "weak" monsters (not including Haunted Skulls). ??? margotbean (talk) 21:06, 23 November 2021 (UTC)
I looked up in the Forums, Discord, and YouTube trying to have some infos about the Oil of Garlic history, but I couldn't find anything conclusive. There are a lot of references to the wiki page. My guess is the access to the Oil is quite late in the game to people already have reached level 120 in the Mines by that time (and a lot of people just rely on the wiki), so it probably hasn't been tested a lot.
To make myself more clear about the bug thing:
  1. I think the removing of monsters, except for quarry levels (so Haunted Skull and Slimes can spawn in there), is a feature, as it is written clearly and very likely purposely in the code.
  2. More suspicious: Entering level 91 once I got two Shadow mobs (it was a normal lava level). I tried to reproduce this. I went with staircases through the Mines with the Buff, and I encountered one or two mobs very rarely: e.g. 1 Bug in level 3, 1 Green Slime in level 15, 2 Grubs in level 19, 1 Bug in level 39, 1 Shadow Brute on level 94, 1 Red Slime on level 115... (Note that's not just one straight up-to-bottom travel, I made some back-and-forth.)
I dug a bit into that. My first guess following a bug hypothesis was it could be linked to the fact a "lesser or equal" is used in the code to compare with monsterChance, i.e. mineRandom.NextDouble() <= monsterChance. But it would be quite a surprise that a random double between 0 and 1 could equal 0 so often.
So I checked every characters.Add(...) calls in the Locations\MineShaft.cs file to inspect monster spawning processes. (In passing, I can say spawnFlyingMonsterOffScreen() should never trigger when the Buff is active if I'm not mistaken.)
Checking those, I think the rare spawning is due to this chunk of code towards the end of the populateLevel() function, which does not depend on the monsterChance value, and sometimes replace a stone with a mob (if I'm reading it right: for each 35 stones batch on the level, a 1.5 to 3.5 square around one random stone is defined, and each stone in this square, if it is at more than 5 tiles to the elevator or entering ladder, has a 1.2% chance to be turned into a mob). I can't say if the omission of monsterChance check (like != 0) is voluntary or oblivious (could indeed be oblivious in my opinion).
if (stonesLeftOnThisLevel > 35)
{
	int num4 = stonesLeftOnThisLevel / 35;
	for (int l = 0; l < num4; l++)
	{
		Vector2 key2 = objects.Keys.ElementAt(mineRandom.Next(objects.Count()));
		if (!objects[key2].name.Equals("Stone"))
		{
			continue;
		}
		int num5 = mineRandom.Next(3, 8);
		bool flag3 = mineRandom.NextDouble() < 0.1;
		for (int m = (int)key2.X - num5 / 2; (float)m < key2.X + (float)(num5 / 2); m++)
		{
			for (int n = (int)key2.Y - num5 / 2; (float)n < key2.Y + (float)(num5 / 2); n++)
			{
				Vector2 key3 = new Vector2(m, n);
				if (objects.ContainsKey(key3) && objects[key3].name.Equals("Stone"))
				{
					objects.Remove(key3);
					stonesLeftOnThisLevel--;
					if (getDistanceFromStart(m, n) > 5f && flag3 && mineRandom.NextDouble() < 0.12)
					{
						Monster monster3 = BuffMonsterIfNecessary(getMonsterForThisLevel(mineLevel, m, n));
						characters.Add((NPC)monster3);
					}
				}
			}
		}
	}
}
-- Charly (talk) 01:21, 24 November 2021 (UTC)


Quarry Levels

In-game testing shows that Oil of Garlic prevents floors from being Quarry levels, except in the dangerous mines.

The relevant code seems to be MineShaft::loadLevel.

            if ((!this.AnyOnlineFarmerHasBuff(23) || this.getMineArea() == 121) && r.NextDouble() < 0.044 && mapNumberToLoad % 5 != 0 && mapNumberToLoad % 40 > 5 && mapNumberToLoad % 40 < 30 && mapNumberToLoad % 40 != 19)
            {
                if (r.NextDouble() < 0.5)
                {
                    this.isMonsterArea = true;
                }
                else
                {
                    this.isSlimeArea = true;
                }
                if (this.getMineArea() == 121 && this.mineLevel > 126 && r.NextDouble() < 0.5)
                {
                    this.isDinoArea = true;
                    this.isSlimeArea = false;
                    this.isMonsterArea = false;
                }
            }
            else if (this.mineLevel < 121 && r.NextDouble() < 0.044 && Utility.doesMasterPlayerHaveMailReceivedButNotMailForTomorrow("ccCraftsRoom") && Game1.MasterPlayer.hasOrWillReceiveMail("VisitedQuarryMine") && mapNumberToLoad % 40 > 1 && mapNumberToLoad % 5 != 0)
            {
                this.isQuarryArea = true;
                if (r.NextDouble() < 0.25)
                {
                    this.isMonsterArea = true;
                }
            }

The "else if" conditions aren't met when I step through the code, and I don't know why. mineLevel is < 121, the save has the Quarry Mine unlocked, and the levels I tried were 22 & 63. Both were persistent quarry levels, until I drank Oil of Garlic. Any insights are very much appreciated!

Also, there seems to be a 4.4% chance of a level being a quarry level, but I found that levels 22 & 63 were consistently quarry levels (until I drank Oil of Garlic). This is also odd to me. I'm stumped. margotbean (talk) 19:34, 11 September 2022 (UTC)

That last bit, about quarry levels and where they appear, changes on a daily basis. Next day they will be on different levels. This can be checked in the Predictor site located... somewhere :-/ The other bit with Oil of garlic is a bit odd, but maybe those enemies are seen as not dangerous enough, and therefore the Quarry won't spawn at all? Pangaea (talk) 20:00, 11 September 2022 (UTC)
I used MouseyPounds' predictor utility to find the levels to check before I started testing. ;)
(Side note: I have messaged Mousey about a couple of updates to the site (like the fact that Quarry levels can no longer be infested as of v1.5), but the site hasn't been updated yet. He may be waiting for v1.6, or some other reason...)
The other code about spawning flying monsters offscreen and populating the level happen after the level type is set, and the monsters do update every 10 in-game minutes, but again, only after the level type is set.
I literally hit this.isQuarryArea = true; each time until I drank Oil of Garlic, and then the whole block got skipped. Something in the else if conditions didn't get met, and I don't see what it is. I guess my next step is to break the code up into separate "if" statements, so I can check each condition separately. :/ margotbean (talk) 20:40, 11 September 2022 (UTC)
I broke the else if conditions up into separate if statements, and the failure is happening on if (r.NextDouble() < 0.044). But, only after drinking Oil of Garlic. This doesn't make sense to me at all.
Therefore, I'm going to add it to the page without a code reference. It's repeatedly reproduceable in-game, so I believe it's a true statement. If anyone can provide any insight into the game code, it will be appreciated! margotbean (talk) 15:27, 13 September 2022 (UTC)
Ah! It's subtle, but explainable! Randoms always produce numbers in a specific order. So what was happening was roughly:
  • Without Oil of Garlic: passes !this.AnyOnlineFarmerHasBuff(23), asks the Random r for a random number, fails somewhere in the next few checks, goes to else if, asks the Random for another random number, passes that check.
  • With Oil of Garlic: fails !this.AnyOnlineFarmerHasBuff(23), doesn't ask the Random r in the if statement and goes to the else if directly. Because the random wasn't queried, the numbers produced will in effect shift by one, which then caused it to fail (which is very likely, given there's only a 4.4% chance of passing).
I suspect it's perfectly possible to get a quarry floor with Oil of Garlic, what you're seeing is just a consequence of how Random works.
Anyways, the `Game1.MasterPlayer.hasOrWillReceiveMail("VisitedQuarryMine")` part of the check was added in 1.5, otherwise it seems date from 1.4.
Atravita (talk) 19:11, 17 September 2022 (UTC)
O.M.G. and holy tamales... I stepped through that code for 3 days! Genius, Atravita, genius!! When you say "subtle", you aren't kidding.
I'm going to revert my edits now that we have an explanation. I'll be floored by your insight for quite a while. 👍
And thank you to IBugOne for the assistance offered on my talk page too! I do have the v1.4 decompiled code, it's just a matter of finding a LAN cable and transferring it from the old PC to the new one. (For context, it's been almost a year and it's still on my "to do" list.) 😁
Wonderful work, guys! Really wonderful! margotbean (talk) 19:38, 17 September 2022 (UTC)