private void warpWorldCallback(object sender, VideoProcessor.TemplateMatchEventArgs e) { if (!Properties.Settings.Default.WarpWorldEnabled) { return; } if (e.template.eventType != "exit") { return; } var latestLevel = tracker.latest("level"); if (latestLevel == null) { log.Debug("[WarpWorld] Latest was null, no update"); return; } if (tracker.latest("clear") != null) { log.Debug("[WarpWorld] Level was already cleared, not sending loss."); return; } Thread.Sleep(20000); var currentLevel = tracker.latest("level"); if (currentLevel == null || currentLevel.time.Ticks == latestLevel.time.Ticks || currentLevel.data != latestLevel.data) { log.Debug("[WarpWorld] Loss"); WarpWorld?.lose(); } else { log.Debug("[WarpWorld] Rejoined level, not sending loss."); } }
/// <summary> /// Event Callback for the Black Screen event generated by the VideoProcessor /// </summary> private void VideoProcessor_BlackScreen(object sender, VideoProcessor.BlackScreenEventArgs e) { log.Debug(String.Format("Detected a black screen [{0}]", e.seconds)); BeginInvoke((MethodInvoker)(() => processingLabel.Text = "Processing black screen...")); double imageMatchPercent = ImageLibrary.CompareImages(e.currentFrame, levelDetailScreen); // Is this frame a 90% match to a level screen? if (imageMatchPercent > 0.90) { log.Info(String.Format("Detected new level. [{0}]", e.seconds)); BeginInvoke((MethodInvoker)(() => processingLabel.Text = "Processing level screen...")); Level level = OCRLibrary.GetLevelFromFrame(e.currentFrame); writeLevelToFile(level); SMMServer.BroadcastLevel(level); BeginInvoke((MethodInvoker)(() => ocrTextBox.Text = level.code + " | " + level.author + " | " + level.name)); BeginInvoke((MethodInvoker)(() => processingLabel.Text = "")); previewer.SetLastMatch(e.currentFrame); } else { // Not a new level, see if we can detect a template. Dictionary <String, bool> events = new Dictionary <String, bool> { { "death", false }, { "restart", false }, { "exit", false }, { "gameover", false }, { "skip", false } }; BeginInvoke((MethodInvoker)(() => processingLabel.Text = "Processing events...")); foreach (Image <Bgr, byte> f in e.frameBuffer) { // Skip any empty frames in the buffer if (f == null) { continue; } Image <Gray, byte> grayscaleFrame = f.Mat.ToImage <Gray, byte>().Resize(640, 480, Inter.Cubic); //grayscaleFrame.Save("frame_" + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".png"); // XXX: Useful for debugging template false-negatives, and for getting templates List <Rectangle> boundaries = new List <Rectangle>(); foreach (EventTemplate tmpl in templates) { if (events[tmpl.eventType]) { continue; } Point loc = tmpl.getLocation(grayscaleFrame); if (!loc.IsEmpty) { events[tmpl.eventType] = true; boundaries.Add(ImageLibrary.ChangeSize(new Rectangle(loc.X, loc.Y, tmpl.template.Width, tmpl.template.Height), grayscaleFrame.Size, f.Size)); previewer.SetLastMatch(f, boundaries.ToArray()); } } } BeginInvoke((MethodInvoker)(() => processingLabel.Text = "")); foreach (var evt in events) { if (evt.Value) { log.Info(String.Format("Detected {0} [{1}].", evt.Key, e.seconds)); SMMServer.BroadcastEvent(evt.Key); // Even though this will get sent on exiting after a clear, it only matters if a entry is active, and after marking it as a win it goes inactive. if (evt.Key == "exit") { if (Properties.Settings.Default.WarpWorldEnabled) { WarpWorld?.lose(); } if (JsonSettings.ClearOnExit) { clearJsonFile(); } } if (evt.Key == "skip" && JsonSettings.ClearOnSkip) { clearJsonFile(); } if (evt.Key == "gameover" && JsonSettings.ClearOnGameover) { clearJsonFile(); } } } } }