Fixing the "60 Seconds!" Miracle achievement

To make this work, you'll need to replace a .dll file with a patched version. You can do this in two ways:
  1. Downloading a copy of the file from me, and installing it. This means you have to trust me, some internet nobody who you already know hacks games, to give you a compiled executable file and not hack your machine.
  2. Editing the file yourself. This turns you into a HACKING GOD and is absolutely 100% the way you should do this.

Option 1: Download the file and risk being hacked. Be lame and uncool.

Option 2: Edit it yourself, and become AWESOME!

For this, you'll need a piece of amazing software called "dnSpy" (and, I admit, you need to trust whoever wrote THAT software, a guy called "0xd4d"... if you trust him, then I suppose there's an argument that you could just as well trust me. But ignore that: take this path anyway. It is SO MUCH COOLER in the long run. Poking through the innards of games like this is what led to me starting a career as a programmer. It's a very empowering thing. Try it!

Go to https://github.com/0xd4d/dnSpy/releases - click that to get to the download page. Download dnSpy-net472.zip. Download it, unzip it into some folder, and click the dnSpy.exe file in there. It should open up a program with three panes in.

Back up the Assembly-CSharp.dll file as I described above.

Now drag the original file Assembly-CSharp.dll, into the left-hand pane of dnSpy. That'll load it in - you should see "Assembly-CSharp" listed in that pane (maybe some other files too, that's fine).

Expand the little arrow beside it, and navigate to: Assembly-CSharp -> Assembly-CSharp.dll -> {} - -> SurvivalInteraction -> PlayRadio. Clicking that should load some code in the right-hand panel.

Rightclick on PlayRadio there in the left-hand panel and select Edit Method (C#) (note: not the similar "Edit Method" option right above it. We want to edit the C#, not the method properties.) This should bring up a window with the same code that is in the righthand window. and two buttons, "Compile" and "cancel".

Select all, delete, and replace with:

using System;
using System.Text;
using UnityEngine;

// Token: 0x0200024F RID: 591
[Serializable]
public partial class SurvivalInteraction
{
	// Token: 0x06001A80 RID: 6784 RVA: 0x00091C84 File Offset: 0x00090084
	private static void PlayRadio(SurvivalInteraction interaction, string[] parameters)
	{
		// Apparently this pankrowa profile var used to be stored in a different way, which blocked this achievement from some users.
		int pk1 = Settings.Data.PlayerProfile.GetRecord<int>("Pankrowa", false);
		if ((0 != pk1) && (1 != pk1) && (10 != pk1) && (11 != pk1)) { // If it's not one of the four valid values...
			Settings.Data.PlayerProfile.SetRecord("Pankrowa", 0);      // ... clear it!
			
			// This message is not currently displayed. This makes me sad.
			int pk2 = Settings.Data.PlayerProfile.GetRecord<int>("Pankrowa", false);
			string pkstr = "Profile var was " + pk1;
			if (0 == pk2) {
				pkstr += " - Fixed!";
			} else {
				pkstr += " - NOT Fixed, is now " + pk2;
			}
		}
	
		// DevConsole.LogRadio(parameters);
		if (SurvivalInteraction.ValidateParameters(parameters, false, 1, 9999))
		{
			string text = parameters[0];
			string text2 = text;
			int daysSurvived = GlobalTools.GetController<SurvivalControl>().Data.DaysSurvived;
			int num = 0;
			int num2 = 2;
			int num3 = 3;
			while (parameters.Length > num2 + 1)
			{
				int num4 = 0;
				if (int.TryParse(parameters[num2], out num4) && num4 > 0 && daysSurvived % num4 == 0)
				{
					int num5 = 0;
					if (int.TryParse(parameters[num2 + 1], out num5) && interaction.TimesRanToday == num5)
					{
						text2 = parameters[num2 - 1];

						// Record this radio message, if we haven't already seen it.						
						if (!Settings.Data.PlayerProfile.TestRecordBitfield("Pankrowa", num))
						{
							Settings.Data.PlayerProfile.AddRecordBitfield("Pankrowa", num);
						}
						
						// We check for the achievement whether or not we have already heard this sound.
						int num6 = 0;
						int num7 = parameters.Length / num3;
						for (int i = 0; i < num7; i++)
						{
							num6 += (int)Mathf.Pow(10f, (float)i);
						}
						if (Settings.Data.PlayerProfile.TestRecordBitfieldValue("Pankrowa", num6))
						{
							Settings.Data.PlayerProfile.UnlockAchievement("ACH_PAN_KROWA");
						}
						break;
					}
				}
				num2 += num3;
				num++;
			}
			if (!string.IsNullOrEmpty(text2))
			{
				AudioSource audioSource = GlobalTools.PlaySound(text2, false, 0f, float.MaxValue, float.MaxValue, null);
				if (audioSource != null)
				{
					interaction.Delay = audioSource.clip.length;
				}
			}
		}
	}
}

You're just moving that one } curly bracket that was before the break up ten lines or so, to the line immediately after the call to AddRecordBitfield - that's all! But what that does, is it takes the activation of the achievement out of the check to see if you had already listened to that sound. So now you can still get the achievement even if you already heard the sound!

Click the Compile button, then click File menu, and select "Save Module". Click OK to accept the default settings (which should say you're overwriting the DLL file that you dragged in).

YOU JUST EDITED THE GAME!

You're done. Click the radio twice on day 3, four times on day 7, and you should get the "Miracle" achievement. Start the game, play it on easy mode, don't leave the vault or let anyone in, and you should survive until day 7 with no problems.

But wait... if we edited the game, that means we can do so much more! Well, yes.

OK, so how can I hack other cool stuff???

The sky's the limit, really. Down in the bottom right tab, there's a "search" window which can help you search for useful strings, though it only seems to find declarations, not usages. To find where a method or variable is used, rightclick the method and click "Analyze". For more info, click the hep and read the wiki - the debugger is suuuuper cool but beyond the scope of this doc!

Here's a few things you can do, to get you started.

1) Make the clock count down three times slower, giving you three minutes to collect everything! That should be plenty to get EVERYTHING!

In the method: Assembly-CSharp -> Assembly-CSharp.dll -> {} - -> Watch -> WatchTick

Change:

		yield return new WaitForSeconds(1f);

to:

		yield return new WaitForSeconds(3f);

2) Make clicking the lights in the shelter enable the developer console, which lets you add and remove items, trigger quests, and more. (Type "help" in the console to get a list of commands). I'd like to have this come off a keypress, but can't easily see how to do that, so... clicking the light is the easiest alternative.

In the method: Assembly-CSharp -> Assembly-CSharp.dll -> {} - -> SurvivalInteraction -> FlickerLights

Change:

	GlobalTools.GetController<SurvivalControl>().FlickerLightsGo();

to:

	GlobalTools.GetController<SurvivalControl>().FlickerLightsGo();
	GlobalTools.ActivateDevConsole();
This will bring up the console, but we also need a way to close it again, so let's make the console command "close" close the console:

In the method: Assembly-CSharp -> Assembly-CSharp.dll -> {} - -> DevConsole -> ProcessCommand

Change:

		case "help":

to:

		case "close": GlobalTools.ActivateDevConsole(); break;
		case "help":

We should also edit the help command to list our new "close" command... or find a way to make it close on the ` key. I'll leave that as an exercise for the reader (do let me know if you figure it out, since I have not!)

I'm not gonna give you a pre-compiled DLL file for those cool parts. I want you to experience the magic! Let me know if you find any other cool hacks.

Now... dnSpy works with ALL Unity games. What other Unity games do you have? Just think what you can do with them!

REMEMBER: file->save module after compiling your changes. I keep forgetting this, and wondering why my changes had no effect!