Skip to content

Commit d91d312

Browse files
Add separate guide for authoring routes
1 parent 3afc263 commit d91d312

File tree

3 files changed

+136
-93
lines changed

3 files changed

+136
-93
lines changed

README.md

Lines changed: 13 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -44,91 +44,23 @@ Launching and using SwiftSplit is fairly simple, with only three broad steps.
4444
Safari doesn't like the way SwiftSplit communicates with LiveSplit One, so LiveSplit stalls when trying to connect.
4545

4646
### Firefox - Mostly compatible
47-
If you only have one display, Firefox will slow down the LiveSplit One tab, causing it to record incorrect times.
48-
If you keep the LiveSplit window open on a second display this isn't an issue.
47+
Unless you can keep LiveSplit open and visible, Firefox will slow down the LiveSplit One tab, causing it to record
48+
incorrect times. If you keep the LiveSplit window open on a second display this isn't an issue.
4949

5050
### Chrome - Compatible
5151
Chrome doesn't seem to have the same optimization as Firefox, which in this case is a good thing. If you're using
5252
Firefox and your splits are coming out wrong, try with Chrome and see if that fixes it.
5353

5454
## Pre-made splits
55-
Pre-made splits and route JSON files for Any% and the B-sides can be found [here](https://github.com/thecodewarrior/SwiftSplit/tree/master/example).
56-
The `.lss` files can be imported directly into LiveSplit One and the corresponding `.json` files can be loaded into SwiftSplit.
57-
58-
# Route JSON
59-
Routes are configured using a JSON file and use "events" generated by SwiftSplit. They consist of a reset event and a
60-
list of route events. SwiftSplit expects route events in a specific order and triggers splits on those events. The reset
61-
event can trigger at any point during the route and will reset the run. If the next event in the route is itself the
62-
reset event, that will take priority and the run won't be reset. This can be used to implement returning to map into
63-
your route.
64-
65-
Here's an example for Old Site Any%:
66-
```json
67-
{
68-
"useFileTime": false,
69-
"reset": "reset chapter",
70-
"route": [
71-
"start chapter 2 ## Start",
72-
"d8 > d3 ## - Mirror",
73-
"3x > 3 ## Intervention",
74-
"10 > 2 ## - Escape",
75-
"13 > end_0 ## Awake",
76-
"complete chapter 2"
77-
]
78-
}
79-
```
80-
81-
## Events
82-
Events are triggered when SwiftSplit observes a change in the game state, which is checked 30 times every second. A
83-
single state change frequently causes multiple events, generally with differing levels of specificity.
84-
85-
Note that the *exact* text of an event is important. Spaces and capitalization have to match, with a couple additions:
86-
- Inserting an exclamation point (`!`) at the beginning of an event will cause that event to not trigger a split. This
87-
can be useful when your route passes between two screens multiple times but you only want one split.
88-
- Anything after `##` will be trimmed off. This can be useful for explaining events.
89-
- Any event entries that start with `#` will be ignored, allowing you to "comment out" events.
90-
91-
SwiftSplit has an "Event Stream" panel that displays events as they are triggered, which can be useful when creating
92-
route files. (You can copy the text out of the panel to paste directly into the route file too).
93-
94-
### Chapter start/end events
95-
- `leave chapter` - Triggered when leaving any chapter (either by restarting the chapter, returning to the map, or
96-
using "Save and Quit")
97-
- `leave chapter <n>` - Triggered when leaving chapter `<n>`
98-
- `start chapter` - Triggered when any chapter is started
99-
- `start chapter <n>` - Triggered when chapter `<n>` is started
100-
- `complete chapter` - Triggered when any chapter is completed
101-
- `complete chapter <n>` - Triggered when chapter `<n>` is completed
102-
- **A-side specific:**
103-
- `start a-side <n>` - Triggered when chapter `<n>`'s A-side is started
104-
- `leave a-side <n>` - Triggered when leaving chapter `<n>`'s A-side
105-
- `complete a-side <n>` - Triggered when chapter `<n>`'s A-side is completed
106-
- **B-side specific:**
107-
- `start b-side <n>` - Triggered when chapter `<n>`'s B-side is started
108-
- `leave b-side <n>` - Triggered when leaving chapter `<n>`'s B-side
109-
- `complete b-side <n>` - Triggered when chapter `<n>`'s B-side is completed
110-
- **C-side specific:**
111-
- `start c-side <n>` - Triggered when chapter `<n>`'s C-side is started
112-
- `leave c-side <n>` - Triggered when leaving chapter `<n>`'s C-side
113-
- `complete c-side <n>` - Triggered when chapter `<n>`'s C-side is completed
114-
115-
### Screen transition event
116-
- `<from screen> > <to screen>` - Triggered when transitioning between two screens (you can find the screen IDs by
117-
enabling debug and hovering over the screen in the map editor.)
118-
119-
### Collectable events
120-
- **Cassettes:**
121-
- `cassette` - Triggered when any cassette is collected
122-
- `chapter <n> cassette` - Triggered when the cassette in the specified chapter is collected
123-
- `<n> total cassettes` - Triggered when a cassette is collected. `<n>` is the total number of cassettes collected in
124-
the current file
125-
- **Heart Gems:**
126-
- `heart` - Triggered when any heart gem is collected
127-
- `chapter <n> heart` - Triggered when the heart gem in the specified chapter is collected
128-
- `<n> total hearts` - Triggered when a heart gem is collected. `<n>` is the total number of heart gems collected in
129-
the current file
130-
- **Strawberries:**
131-
- `strawberry` - Triggered when any strawberry is collected
132-
- `<n> chapter strawberries` - Triggered when a total of `<n>` strawberries are collected in a chapter
133-
- `<n> file strawberries` - Triggered when a total of `<n>` strawberries are collected in the file
55+
You can get pre-made splits from the [examples directory](https://github.com/thecodewarrior/SwiftSplit/tree/master/example).
56+
Included are:
57+
- Full-game
58+
- Any%
59+
- IL (chapters 1–9)
60+
- Any%
61+
- B-side
62+
- C-side
63+
- Icons (chapters, berry, and celeste mountain) for use as split icons
64+
For each of these the `.lss` file can be imported directly into LiveSplit One and the corresponding `.json` files can
65+
be loaded into SwiftSplit.
13466

SwiftSplit/CelesteSplitter.swift

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -214,33 +214,29 @@ class CelesteSplitter {
214214
events.append(Event(
215215
.normal("collect cassette", specificity: 0),
216216
.normal("collect chapter \(new.chapter) cassette", specificity: 0),
217-
.normal("collect \(new.fileCassettes) total cassettes", specificity: 0),
217+
.normal("\(new.fileCassettes) total cassettes", specificity: 0),
218218
// compat:
219219
.legacy("cassette"),
220-
.legacy("chapter \(new.chapter) cassette"),
221-
.legacy("\(new.fileCassettes) total cassettes")
220+
.legacy("chapter \(new.chapter) cassette")
222221
))
223222
}
224223
if new.chapterHeart && !old.chapterHeart {
225224
events.append(Event(
226225
.normal("collect heart", specificity: 0),
227226
.normal("collect chapter \(new.chapter) heart", specificity: 0),
228-
.normal("collect \(new.fileHearts) total hearts", specificity: 0),
227+
.normal("\(new.fileHearts) total hearts", specificity: 0),
229228
// compat:
230229
.legacy("heart"),
231-
.legacy("chapter \(new.chapter) heart"),
232-
.legacy("\(new.fileHearts) total hearts")
230+
.legacy("chapter \(new.chapter) heart")
233231
))
234232
}
235233
if new.chapterStrawberries > old.chapterStrawberries {
236234
events.append(Event(
237235
.normal("collect strawberry", specificity: 0),
238-
.normal("collect \(new.chapterStrawberries) chapter strawberries", specificity: 0),
239-
.normal("collect \(new.fileStrawberries) file strawberries", specificity: 0),
236+
.normal("\(new.chapterStrawberries) chapter strawberries", specificity: 0),
237+
.normal("\(new.fileStrawberries) file strawberries", specificity: 0),
240238
// compat:
241-
.legacy("strawberry"),
242-
.legacy("\(new.chapterStrawberries) chapter strawberries"),
243-
.legacy("\(new.fileStrawberries) file strawberries")
239+
.legacy("strawberry")
244240
))
245241
}
246242
return events
@@ -342,7 +338,7 @@ class RouteEvent {
342338
}
343339

344340
private static let pattern = try! NSRegularExpression(
345-
pattern: #"^(!)?\s*(.*?)\s*(##.*)?$"#
341+
pattern: #"^\s*(!)?\s*(.*?)\s*(#.*)?$"#
346342
)
347343
}
348344

authoring_routes.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Route JSON
2+
Routes are configured using a JSON file and use "events" generated by SwiftSplit.
3+
4+
Here's an example for Old Site Any%:
5+
```json
6+
{
7+
"useFileTime": false,
8+
"reset": "reset chapter",
9+
"route": [
10+
"enter chapter 2 # Start",
11+
"d8 > d3 # - Mirror",
12+
"3x > 3 # Intervention",
13+
"10 > 2 # - Escape",
14+
"13 > end_0 # Awake",
15+
"complete chapter 2"
16+
]
17+
}
18+
```
19+
20+
A route consists of a "reset" event and a list of "route" events. SwiftSplit expects route events in a specific order
21+
and triggers splits on those events. The reset event can trigger at any point during the route and will instruct
22+
LiveSplit to reset the run. There are mechanisms in place to allow leaving a chapter mid-run (either via Save and Quit
23+
or Return to Map). See the [Expected Resets](#expected-resets) section for more on that.
24+
25+
## Events
26+
Events are triggered when SwiftSplit observes a change in the game state, which is checked 30 times every second. A
27+
single event may have multiple variants, generally with differing levels of specificity (e.g. `leave chapter`,
28+
`leave chapter 1`, and `leave a-side 1`).
29+
30+
Note that the *exact* text of an event is important. Spaces and capitalization have to match, with a couple additions:
31+
- Whitespace *around* an event doesn't matter. (e.g. `"leave chapter"` is equivalent to `" leave chapter "`)
32+
- Inserting an exclamation point (`!`) at the beginning of an event will cause that event to be "silent" and not trigger
33+
a split. This can be useful for situations like [expected resets](#expected-resets).
34+
- Anything after `#` will be trimmed off. This can be useful for explaining events.
35+
- If after applying the previous two rules the event is empty, it's simply ignored. (e.g. `! # comment` will be ignored)
36+
37+
SwiftSplit has an "Event Stream" panel that displays events as they are triggered, which can be useful when creating
38+
route files. (You can copy the text out of the panel to paste directly into the route file).
39+
40+
### Chapter start/end events
41+
- `leave chapter` - Triggered when leaving any chapter (either by restarting the chapter, returning to the map, or
42+
using "Save and Quit")
43+
- `leave chapter <n>` - Triggered when leaving chapter `<n>`
44+
- `enter chapter` - Triggered when any chapter is entered
45+
- `enter chapter <n>` - Triggered when chapter `<n>` is entered
46+
- `complete chapter` - Triggered when any chapter is completed
47+
- `complete chapter <n>` - Triggered when chapter `<n>` is completed
48+
- **A-side specific:**
49+
- `enter a-side <n>` - Triggered when chapter `<n>`'s A-side is entered
50+
- `leave a-side <n>` - Triggered when leaving chapter `<n>`'s A-side
51+
- `complete a-side <n>` - Triggered when chapter `<n>`'s A-side is completed
52+
- **B-side specific:**
53+
- `enter b-side <n>` - Triggered when chapter `<n>`'s B-side is entered
54+
- `leave b-side <n>` - Triggered when leaving chapter `<n>`'s B-side
55+
- `complete b-side <n>` - Triggered when chapter `<n>`'s B-side is completed
56+
- **C-side specific:**
57+
- `enter c-side <n>` - Triggered when chapter `<n>`'s C-side is entered
58+
- `leave c-side <n>` - Triggered when leaving chapter `<n>`'s C-side
59+
- `complete c-side <n>` - Triggered when chapter `<n>`'s C-side is completed
60+
61+
### Screen transition event
62+
- `<from screen> > <to screen>` - Triggered when transitioning between two screens (you can find the screen IDs by
63+
enabling debug and hovering over the screen in the map editor.)
64+
65+
### Collectable events
66+
- **Cassettes:**
67+
- `collect cassette` - Triggered when any cassette is collected
68+
- `collect chapter <n> cassette` - Triggered when the cassette in the specified chapter is collected
69+
- `<n> total cassettes` - Triggered when a cassette is collected. `<n>` is the total number of cassettes collected in
70+
the current file
71+
- **Heart Gems:**
72+
- `collect heart` - Triggered when any heart gem is collected
73+
- `collect chapter <n> heart` - Triggered when the heart gem in the specified chapter is collected
74+
- `<n> total hearts` - Triggered when a heart gem is collected. `<n>` is the total number of heart gems collected in
75+
the current file
76+
- **Strawberries:**
77+
- `collect strawberry` - Triggered when any strawberry is collected
78+
- `<n> chapter strawberries` - Triggered when a total of `<n>` strawberries are collected in a chapter
79+
- `<n> file strawberries` - Triggered when a total of `<n>` strawberries are collected in the file
80+
81+
## Return to Map & Save and Quit
82+
83+
Without the proper route file, both of these count as resetting a chapter. It's impossible for SwiftSplit to tell the
84+
difference between a reset, return to map, or save and quit. To get around this, you can define in your route where
85+
leaving the chapter is *expected.*
86+
87+
Here's what the reset for the 1A might look like:
88+
```json
89+
"route": [
90+
"enter chapter 1 # Start",
91+
"5 > 6 # Crossing",
92+
"!collect heart",
93+
"!leave chapter",
94+
"9 > 9b # Chasm",
95+
"complete chapter 1"
96+
]
97+
```
98+
99+
The reason we put `!collect heart` before `!leave chapter` is because any time that SwiftSplit is waiting for you to
100+
leave the chapter *you can not automatically reset the run.* Any attempt to restart the run will just result in
101+
progressing through the route. By putting the collect heart event before the leave chapter event we make sure that
102+
SwiftSplit only starts waiting for the leave event right before we do it.
103+
104+
For restarting after collecting berries you'd want to have two events: one when you enter the room you'll save and quit
105+
in, and the next when you collect the berry:
106+
107+
```json
108+
"route": [
109+
"...",
110+
"!a00 > a03",
111+
"!collect strawberry",
112+
"!leave chapter",
113+
"..."
114+
]
115+
```

0 commit comments

Comments
 (0)