diff --git a/README.md b/README.md index b0f6d8d..1f0e0b2 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ Launching and using SwiftSplit is fairly simple, with only three broad steps. 5. Click "Connect to Server" in LiveSplit One and paste that URL - Note: navigating away from the timer screen may make the server button say it isn't connected any more. This is a bug. You can check that it's still connected with the "LiveSplit clients" field in SwiftSplit, which should read - "1". If that field reads "0", you need to connect. If it reads "2" or higher then you've connected multiple times - and will need to save your splits and reload LiveSplit One, otherwise each split will count multiple times. + "1". If that field reads "0", you need to connect. If it reads "2" or higher then you will need to save your splits + and reload the LiveSplit One tab, otherwise each split will trigger multiple times. ### Loading the Run 6. Load up your splits in LiveSplit One @@ -66,16 +66,59 @@ Here's an example for Old Site Any%: } ``` -The currently defined events are (`<...>` indicates a fill-in parameter): -- `start chapter ` - Triggered when the specified chapter number is started -- `reset chapter` - Triggered when the current chapter is reset (either by restarting the chapter or exiting to the map) -- `complete chapter ` - Triggered when the specified chapter number is completed -- ` > ` - Triggered when transitioning between two screens (you can find the screen IDS by - looking at the `Level: Name:` when connected to Celeste, or by enabling debug and hovering over the screen in the map - editor. +## Events +Events are triggered when SwiftSplit observes a change in the game state, which is checked 10 times every second. A +single state change frequently causes multiple events, generally with differing levels of specificity. -Note that the *exact* text is important. Spaces and capitalization have to match, with a couple additions: +Note that the *exact* text of an event is important. Spaces and capitalization have to match, with a couple additions: - Inserting an exclamation point (`!`) at the beginning of an event will cause that event to not trigger a split. This can be useful when your route passes between two screens multiple times but you only want one split. - Anything after a ` ##` (*exactly* one space and two pound signs) will be trimmed off. This can be useful for explaining events. + +SwiftSplit has an "Event Stream" panel that displays events as they are triggered, which can be useful when creating +route files. (You can copy the text out of the panel to paste directly into the route file too). + +### Chapter start/end events +- `reset chapter` - Triggered when any chapter is reset (either by restarting the chapter or exiting to the map) +- `start chapter ` - Triggered when chapter `` is started +- `reset chapter ` - Triggered when chapter `` is reset +- `complete chapter ` - Triggered when chapter `` is completed +- **A-side specific:** + - `start a-side ` - Triggered when chapter ``'s A-side is started + - `reset a-side ` - Triggered when chapter ``'s A-side is reset + - `complete a-side ` - Triggered when chapter ``'s A-side is completed +- **B-side specific:** + - `start b-side ` - Triggered when chapter ``'s B-side is started + - `reset b-side ` - Triggered when chapter ``'s B-side is reset + - `complete b-side ` - Triggered when chapter ``'s B-side is completed +- **C-side specific:** + - `start c-side ` - Triggered when chapter ``'s C-side is started + - `reset c-side ` - Triggered when chapter ``'s C-side is reset + - `complete c-side ` - Triggered when chapter ``'s C-side is completed + +### Screen transition event +- ` > ` - Triggered when transitioning between two screens (you can find the screen IDs by + enabling debug and hovering over the screen in the map editor.) + +### Collectable events +- **Cassettes:** + - `cassette` - Triggered when any cassette is collected + - `chapter cassette` - Triggered when the cassette in the specified chapter is collected + - ` total cassettes` - Triggered when a cassette is collected. `` is the total number of cassettes collected in + the current file +- **Heart Gems:** + - `heart` - Triggered when any heart gem is collected + - `chapter heart` - Triggered when the heart gem in the specified chapter is collected + - ` total hearts` - Triggered when a heart gem is collected. `` is the total number of heart gems collected in + the current file +- **Strawberries:** + - `strawberry` - Triggered when any strawberry is collected + - ` chapter strawberries` - Triggered when a total of `` strawberries are collected in a chapter + - ` file strawberries` - Triggered when a total of `` strawberries are collected in the file + +### Some nuances regarding resets: + +If the next expected event in the route is the same as the reset event, the route will take precedence and the run +won't be reset. However, if the first event in the route is then detected (though again route takes precedence), the +run will reset and start over. diff --git a/SwiftSplit.xcodeproj/project.pbxproj b/SwiftSplit.xcodeproj/project.pbxproj index 11587f9..209b274 100644 --- a/SwiftSplit.xcodeproj/project.pbxproj +++ b/SwiftSplit.xcodeproj/project.pbxproj @@ -386,6 +386,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = V2EPDFA9PV; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = SwiftSplit/Info.plist; @@ -393,7 +394,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = dev.thecodewarrior.SwiftSplit; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "SwiftSplit/SwiftSplit-Bridging-Header.h"; @@ -412,6 +413,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = V2EPDFA9PV; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = SwiftSplit/Info.plist; @@ -419,7 +421,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = dev.thecodewarrior.SwiftSplit; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "SwiftSplit/SwiftSplit-Bridging-Header.h"; diff --git a/SwiftSplit/CelesteScanner.swift b/SwiftSplit/CelesteScanner.swift index a9748cd..6e5fe13 100644 --- a/SwiftSplit/CelesteScanner.swift +++ b/SwiftSplit/CelesteScanner.swift @@ -169,12 +169,12 @@ class CelesteScanner { 10b212968c7f0000 00000000 00000000 b83c632d01000000 f1aaaa1f f1bbbb1f 01010100 00000000 - placeholders --------######## -------- ######## --------######## -------- ######## -------- ######## 0 4 8 12 16 20 24 28 32 36 - 10e28b84a07f0000 00000000 00000000 b009150f01000000 ffffffff ffffffff 00000000 00000000 - AUTOSPLIT save state + 10e28b84a07f0000 00000000 00000000 b009150f01000000 ffffffff ffffffff 00000000 00000000 - Fresh launch state 11ffeeddccbbaa11 f1cccc1f 01010000 11addeefbeadde11 f1dddd1f f1eeee1f f1ffff1f 00000000 - placeholders --------######## -------- ######## --------######## -------- ######## -------- ######## 40 44 48 52 56 60 64 68 72 76 - 0000000000000000 00000000 00000000 11addeefbeadde11 00000000 00000000 00000000 00000000 - AUTOSPLIT save state + 0000000000000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 - Fresh launch state ``` */ struct AutoSplitterData: Equatable { @@ -243,6 +243,13 @@ struct AutoSplitterData: Equatable { } } +enum ChapterMode { + case Normal + case BSide + case CSide + case Other(value: Int) +} + /** A swift representation of the C# AutoSplitterInfo */ @@ -250,7 +257,7 @@ class AutoSplitterInfo { let data: AutoSplitterData let chapter: Int - let mode: Int + let mode: ChapterMode let level: String let timerActive: Bool let chapterStarted: Bool @@ -268,7 +275,7 @@ class AutoSplitterInfo { self.data = AutoSplitterData() self.chapter = 0 - self.mode = 0 + self.mode = .Normal self.level = "" self.timerActive = false self.chapterStarted = false @@ -287,7 +294,16 @@ class AutoSplitterInfo { self.data = data self.chapter = Int(data.chapter) - self.mode = Int(data.mode) + switch data.mode { + case 0: + self.mode = .Normal + case 1: + self.mode = .BSide + case 2: + self.mode = .CSide + default: + self.mode = .Other(value: Int(data.mode)) + } self.level = try AutoSplitterData.parseCSharpString(at: data.levelPointer, target: target) self.timerActive = data.timerActive != 0 self.chapterStarted = data.chapterStarted != 0 diff --git a/SwiftSplit/CelesteSplitter.swift b/SwiftSplit/CelesteSplitter.swift index e60ae61..88e4bb4 100644 --- a/SwiftSplit/CelesteSplitter.swift +++ b/SwiftSplit/CelesteSplitter.swift @@ -67,18 +67,54 @@ class CelesteSplitter { func getEvents(from old: AutoSplitterInfo, to new: AutoSplitterInfo) -> [String] { var events: [String] = [] - if new.level != old.level && old.level != "" && new.level != "" { - events.append("\(old.level) > \(new.level)") - } + // if we don't check `new.chapterComplete`, the summit credits trigger the autosplitter if new.chapterStarted && !old.chapterStarted && !new.chapterComplete { events.append("start chapter \(new.chapter)") + switch new.mode { + case .Normal: events.append("start a-side \(new.chapter)") + case .BSide: events.append("start b-side \(new.chapter)") + case .CSide: events.append("start c-side \(new.chapter)") + default: break + } } if !new.chapterStarted && old.chapterStarted && !old.chapterComplete { events.append("reset chapter") + events.append("reset chapter \(old.chapter)") + switch new.mode { + case .Normal: events.append("reset a-side \(old.chapter)") + case .BSide: events.append("reset b-side \(old.chapter)") + case .CSide: events.append("reset c-side \(old.chapter)") + default: break + } } if new.chapterComplete && !old.chapterComplete { events.append("complete chapter \(old.chapter)") + switch new.mode { + case .Normal: events.append("complete a-side \(old.chapter)") + case .BSide: events.append("complete b-side \(old.chapter)") + case .CSide: events.append("complete c-side \(old.chapter)") + default: break + } + } + + if new.level != old.level && old.level != "" && new.level != "" { + events.append("\(old.level) > \(new.level)") + } + if new.chapterCassette && !old.chapterCassette { + events.append("cassette") + events.append("chapter \(new.chapter) cassette") + events.append("\(new.fileCassettes) total cassettes") + } + if new.chapterHeart && !old.chapterHeart { + events.append("heart") + events.append("chapter \(new.chapter) heart") + events.append("\(new.fileHearts) total hearts") + } + if new.chapterStrawberries > old.chapterStrawberries { + events.append("strawberry") + events.append("\(new.chapterStrawberries) chapter strawberries") + events.append("\(new.fileStrawberries) file strawberries") } return events } diff --git a/SwiftSplit/Info.plist b/SwiftSplit/Info.plist index 40baa90..1fcad20 100644 --- a/SwiftSplit/Info.plist +++ b/SwiftSplit/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright diff --git a/SwiftSplit/ViewController.swift b/SwiftSplit/ViewController.swift index 2709bc0..9e05cd8 100644 --- a/SwiftSplit/ViewController.swift +++ b/SwiftSplit/ViewController.swift @@ -216,7 +216,9 @@ class ViewController: NSViewController, RouteBoxDelegate { gameTimeLabel.stringValue = timeString - nextEventLabel.stringValue = splitter.routeIndex < routeConfig.route.count ? "\"\(routeConfig.route[splitter.routeIndex])\"" : "" + nextEventLabel.stringValue = splitter.routeIndex < routeConfig.route.count ? + "\"\(routeConfig.route[splitter.routeIndex])\" (\(splitter.routeIndex + 1)/\(routeConfig.route.count))" + : "" } func openRouteFile(url: URL) {