state("scummvm") {} startup { Assembly.Load(File.ReadAllBytes("Components/scummvm-help")).CreateInstance("Groovie"); vars.ScummVM.LogChangedWatchers(); vars.ScummVM.LogResolvedPaths(); const int RIDDLE = 1; const int PUZZLE = 2; vars.Splits = new Dictionary>> { { 1, new Dictionary> { { "Tonic water", Tuple.Create(RIDDLE, 1) }, { "CASH REGISTER", Tuple.Create(PUZZLE, 0xF8) }, { "Cork", Tuple.Create(RIDDLE, 2) }, { "KNIGHTS", Tuple.Create(PUZZLE, 0xFB) }, { "Satyr", Tuple.Create(RIDDLE, 3) }, { "BOOKCASE", Tuple.Create(PUZZLE, 0xEE) }, { "Globe", Tuple.Create(RIDDLE, 4) }, { "MOUSE", Tuple.Create(PUZZLE, 0xEF) }, { "Pill", Tuple.Create(RIDDLE, 5) }, { "Robin", Tuple.Create(RIDDLE, 6) }, }}, { 2, new Dictionary> { { "Desk drawers", Tuple.Create(RIDDLE, 1) }, { "Torso", Tuple.Create(RIDDLE, 2) }, { "Champagne bottle", Tuple.Create(RIDDLE, 3) }, { "POOL BALLS", Tuple.Create(PUZZLE, 0xF4) }, { "Setter", Tuple.Create(RIDDLE, 4) }, { "Clock face", Tuple.Create(RIDDLE, 5) }, { "SPIDERS", Tuple.Create(PUZZLE, 0xF9) }, { "Razor", Tuple.Create(RIDDLE, 6) }, { "Orange", Tuple.Create(RIDDLE, 7) }, { "MIRROR", Tuple.Create(PUZZLE, 0xF0) }, { "Picture above fireplace", Tuple.Create(RIDDLE, 8) }, { "Great Dane", Tuple.Create(RIDDLE, 9) }, }}, { 3, new Dictionary> { { "Broken TV", Tuple.Create(RIDDLE, 1) }, { "Pipe organ", Tuple.Create(RIDDLE, 2) }, { "TOY TRAINS", Tuple.Create(PUZZLE, 0xFA) }, { "Rook", Tuple.Create(RIDDLE, 3) }, { "Torch", Tuple.Create(RIDDLE, 4) }, { "PLATES", Tuple.Create(PUZZLE, 0xF1) }, { "Cheese grater", Tuple.Create(RIDDLE, 5) }, { "7th Guest disc", Tuple.Create(RIDDLE, 6) }, { "Toothpaste", Tuple.Create(RIDDLE, 7) }, { "DICE CUBE", Tuple.Create(PUZZLE, 0xF3) }, { "Guillotine", Tuple.Create(RIDDLE, 8) }, { "White flower", Tuple.Create(RIDDLE, 9) }, { "PYRAMID", Tuple.Create(PUZZLE, 0xED) }, { "Red rose", Tuple.Create(RIDDLE, 49) }, { "JEWELRY BOX", Tuple.Create(PUZZLE, 0xF2) }, { "Earring", Tuple.Create(RIDDLE, 50) }, }}, { 4, new Dictionary> { { "FURNITURE", Tuple.Create(PUZZLE, 0xEC) }, { "Harp", Tuple.Create(RIDDLE, 1) }, { "Toy Soldier", Tuple.Create(RIDDLE, 2) }, { "Eyeball", Tuple.Create(RIDDLE, 3) }, { "Dagger", Tuple.Create(RIDDLE, 4) }, { "Train", Tuple.Create(RIDDLE, 5) }, { "Bed Sheets", Tuple.Create(RIDDLE, 6) }, { "Cleaver", Tuple.Create(RIDDLE, 7) }, }}, { 5, new Dictionary> { { "Lion statue", Tuple.Create(RIDDLE, 1) }, { "Glass of port", Tuple.Create(RIDDLE, 2) }, { "BISHOPS", Tuple.Create(PUZZLE, 0xF6) }, { "Baby rattle", Tuple.Create(RIDDLE, 3) }, { "XI on the clock", Tuple.Create(RIDDLE, 4) }, { "Inkstand", Tuple.Create(RIDDLE, 5) }, { "DOLL HOUSE PENTE", Tuple.Create(PUZZLE, 0xEB) }, }} }; vars.ChapterSplits = new Dictionary { { "MODERN ART", 1 }, { "TRIANGLE", 2 }, { "BEEHIVE", 3 }, { "DESSERT", 4 } }; settings.Add("Splits", true, "Splits"); foreach (var kv in vars.Splits) { var chapter = kv.Key; var splits = kv.Value; settings.Add(chapter.ToString(), true, "Chapter " + chapter.ToString(), "Splits"); foreach (var split in splits) { var splitName = split.Key; settings.Add(splitName, false, splitName, chapter.ToString()); } } foreach (var kv in vars.ChapterSplits) { var puzzle = kv.Key; var chapter = kv.Value.ToString(); settings.Add(puzzle, false, puzzle + " (Finish chapter " + chapter + ")", chapter); } settings.Add("End", true, "Pick a door (Trigger an ending)", "5"); vars.Info = (Action)((msg) => { print("[The 11th Hour ASL] " + msg); }); } init { vars.ScummVM.Init(); // GENERAL WATCHERS vars.ScummVM["video"] = vars.ScummVM.Watch("_script", "_videoRef"); vars.ScummVM["chapter"] = vars.ScummVM.Watch("_script", "_variables", 0x8F); vars.ScummVM["riddle"] = vars.ScummVM.Watch("_script", "_variables", 0x90); vars.ScummVM["room"] = vars.ScummVM.Watch("_script", "_variables", 0x8C); vars.ScummVM["bytes"] = vars.ScummVM.WatchBytes(10, "_script", "_variables", 0x0); // PUZZLE WATCHERS foreach (var kv in vars.Splits) { var splits = kv.Value; foreach (var split in splits) { var splitName = split.Key; var type = split.Value.Item1; var offset = split.Value.Item2; if (type == 2) { // PUZZLE vars.ScummVM[splitName] = vars.ScummVM.Watch("_script", "_variables", offset); } } } vars.StartTransitions = new HashSet { 0x124F, // turn left 0x124A, // move to the left door 0x1205, // move up the stairs 0x1218, // move to the right door 0x1206 // turn right }; vars.EndingCutscenes = new HashSet { 0x100F, // Door 1 (Marie) 0x100E, // Door 2 (Samantha) 0x100D // Door 3 (Robin) }; vars.GameBook = (Func)(() => { if (vars.ScummVM["bytes"].Changed) { byte[] bytes = vars.ScummVM["bytes"].Current; return bytes.SequenceEqual( new byte[] {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01} ); } return false; }); vars.SolvedRiddle = (Func)((riddle) => { if (vars.ScummVM["riddle"].Old == riddle) { if (vars.ScummVM["riddle"].Current == vars.ScummVM["riddle"].Old + 1) { return true; } // After 9, the counter goes straight to 49 and then increments normally again if (vars.ScummVM["riddle"].Current == vars.ScummVM["riddle"].Old + 40) { return true; } } return false; }); vars.completedSplits = new HashSet(); } update { vars.ScummVM.Update(); } start { // Time starts on first movement, i.e. on one of the start transitions if ((old.video == 0 || old.video == -1) && vars.StartTransitions.Contains(current.video)) { return true; } // Or on opening the Game Book if (current.room == 0x0100 && vars.GameBook()) { return true; } } onStart { vars.completedSplits.Clear(); } reset { // Reset on returning to the ScummVM launcher return vars.ScummVM["g_engine"].Changed && vars.ScummVM["g_engine"].Current == 0; } split { // FINAL SPLIT // Pick one of the doors if (current.room == 0x0902 && vars.EndingCutscenes.Contains(current.video)) { if (settings["End"] && vars.completedSplits.Add("End")) { vars.Info("SPLIT: Triggered one of the endings."); return true; } } // RIDDLES AND PUZZLES if (current.chapter > 0 && current.chapter < 6) { foreach (var split in vars.Splits[current.chapter]) { var name = split.Key; var type = split.Value.Item1; if (type == 1) { // RIDDLE var riddle = split.Value.Item2; if (vars.SolvedRiddle(riddle)) { vars.Info("SPLIT: Solved riddle: " + name); return true; } } else if (type == 2) { // PUZZLE if (vars.ScummVM[name].Changed && vars.ScummVM[name].Current >= 0x05) { vars.Info("SPLIT: Solved puzzle: " + name); return true; } } } } // CHAPTERS foreach (var split in vars.ChapterSplits) { var puzzle = split.Key; var chapter = split.Value; if (old.chapter == chapter && current.chapter == old.chapter + 1) { vars.Info("SPLIT: Solved " + puzzle + " & completed Chapter " + chapter); return true; } } }