M .air.toml => .air.toml +1 -1
@@ 9,7 9,7 @@ delay = 1000 # ms
stop_on_error = true
send_interrupt = false
kill_delay = 500 # ms
-include_ext = ["gemif", "go", "tpl", "tmpl", "html"]
+include_ext = ["yml", "go", "tpl", "tmpl", "html"]
[misc]
clean_on_exit = true
M pkg/gamemanager/gamemanager.go => pkg/gamemanager/gamemanager.go +15 -19
@@ 38,9 38,11 @@ func NewGameManager(book StoryBook, config EngineConfig) (*GameManager, error) {
// StartGame creates a new GameState representing the start of a given storyID.
func (gm *GameManager) StartGame(storyID string) (GameState, error) {
+ startingScene, _ := gm.GetRoomByID(storyID, gm.Book.Stories[storyID].Metadata.StartingScene)
+
return GameState{
StoryID: storyID,
- CurrentRoom: gm.Book.Stories[storyID].Rooms[0].ID,
+ CurrentRoom: startingScene.ID,
Conditions: []string{},
}, nil
}
@@ 147,25 149,19 @@ func (gm *GameManager) ConstructSpeculativeStates(gameState GameState, currentRo
}
// ConstructStartingState generates a starting stateToken for each loaded story.
-func (gm *GameManager) ConstructStartingState(stories []StoryMetadata) ([]SpeculativeState, error) {
- speculativeStates := make([]SpeculativeState, len(stories))
-
- for i, s := range stories {
- startState, startErr := gm.StartGame(s.ID)
- if startErr != nil {
- return speculativeStates, fmt.Errorf("could not generate starting state: %w", startErr)
- }
-
- startToken, serializeError := gm.SerializeState(startState)
- if serializeError != nil {
- return speculativeStates, fmt.Errorf("could not serialize state: %w", serializeError)
- }
+func (gm *GameManager) ConstructStartingState(story StoryMetadata) (SpeculativeState, error) {
+ startState, startErr := gm.StartGame(story.ID)
+ if startErr != nil {
+ return SpeculativeState{}, fmt.Errorf("could not generate starting state: %w", startErr)
+ }
- speculativeStates[i] = SpeculativeState{
- Description: fmt.Sprintf("%s by %s - %s", s.Name, s.Author, s.Description),
- StateToken: startToken,
- }
+ startToken, serializeError := gm.SerializeState(startState)
+ if serializeError != nil {
+ return SpeculativeState{}, fmt.Errorf("could not serialize state: %w", serializeError)
}
- return speculativeStates, nil
+ return SpeculativeState{
+ Description: fmt.Sprintf("%s by %s - %s", story.Name, story.Author, story.Description),
+ StateToken: startToken,
+ }, nil
}
M pkg/gamemanager/storybook.go => pkg/gamemanager/storybook.go +6 -4
@@ 27,10 27,12 @@ type Story struct {
// StoryMetadata contains story metadata - authorship information, etc.
type StoryMetadata struct {
- ID string `yaml:"id"`
- Name string `yaml:"name"`
- Description string `yaml:"description"`
- Author string `yaml:"author"`
+ ID string `yaml:"id"`
+ Name string `yaml:"name"`
+ Description string `yaml:"description"`
+ ContentWarnings []string `yaml:"cw"`
+ StartingScene string `yaml:"starting_scene"`
+ Author string `yaml:"author"`
}
// NewStoryBook makes a new Config object and loads in all of the stories from the given path.
M pkg/gemserver/router.go => pkg/gemserver/router.go +1 -0
@@ 32,6 32,7 @@ func StartRouter(gm *gamemanager.GameManager, gc ServerConfig) {
g.Handle("/", handleHome(gm, gc.Version))
g.Handle("/game/:statetoken", handleGame(gm, sceneRenderer))
+ g.Handle("/story/:storyid", handleStory(gm))
g.Handle("/docs*", handleStatic)
g.Handle("/robots.txt", handleRobots)
g.Handle("/favicon.txt", handleFavicon)
M pkg/gemserver/routes.go => pkg/gemserver/routes.go +23 -7
@@ 43,23 43,39 @@ func handleHome(gm *gamemanager.GameManager, version string) func(gig.Context) e
return func(c gig.Context) error {
stories := gm.GetStories()
- startStates, startErr := gm.ConstructStartingState(stories)
- if startErr != nil {
- return fmt.Errorf("couldn't load game: %w", startErr)
- }
+ // startStates, startErr := gm.ConstructStartingState(stories)
+ // if startErr != nil {
+ // return fmt.Errorf("couldn't load game: %w", startErr)
+ // }
- sort.Slice(startStates, func(a int, b int) bool {
- return startStates[a].Description < startStates[b].Description
+ sort.Slice(stories, func(a int, b int) bool {
+ return stories[a].Name < stories[b].Name
})
return c.Render("git.sr.ht/~nromdotcom/gemif:/static/templates/index.gmi.tmpl", map[string]interface{}{
- "Stories": startStates,
+ "Stories": stories,
"Banner": getRandomBanner(),
"Version": version,
})
}
}
+func handleStory(gm *gamemanager.GameManager) func(gig.Context) error {
+ return func(c gig.Context) error {
+ story := gm.Book.Stories[c.Param("storyid")]
+
+ startState, startErr := gm.ConstructStartingState(story.Metadata)
+ if startErr != nil {
+ return fmt.Errorf("couldn't load game: %w", startErr)
+ }
+
+ return c.Render("git.sr.ht/~nromdotcom/gemif:/static/templates/story.gmi.tmpl", map[string]interface{}{
+ "Metadata": story.Metadata,
+ "StartingState": startState,
+ })
+ }
+}
+
func handleGame(gm *gamemanager.GameManager, renderer scenerenderer.Template) func(gig.Context) error {
return func(c gig.Context) error {
gameState, gameErr := gm.DeserializeState(c.Param("statetoken"))
M static/templates/index.gmi.tmpl => static/templates/index.gmi.tmpl +7 -6
@@ 11,16 11,17 @@ Stories allow users to make choices, moving from scene to scene. Simple logic ca
If you want to run your own instance, check out the repo linked below. There are instructions in the README.
-Wanna try it out?
+## Stories
{{- range $key, $value := .Stories}}
-=> /game/{{$value.StateToken}} {{$value.Description}}
+=> /story/{{$value.ID}} {{$value.Name}} -
+{{- if gt (len $value.Description) 75}} {{slice $value.Description 0 74}}...
+{{- else}} {{$value.Description}}
+{{- end}} (by {{$value.Author}})
{{- end}}
-Or learn more?
+## Learn More
=> https://git.sr.ht/~nromdotcom/gemif [https] source at sr.ht
-
-Want me to host your story for you?
-=> mailto:~nromdotcom/gemif@lists.sr.ht [mailto] Send it to the project mailing list
+=> mailto:~nromdotcom/gemif@lists.sr.ht [mailto] Send in your stories for free hosting
For anything else, feel free to email the project mailing list `~nromdotcom/gemif@lists.sr.ht` or me personally at `norm@iwritethe.codes`.=
\ No newline at end of file
A static/templates/story.gmi.tmpl => static/templates/story.gmi.tmpl +13 -0
@@ 0,0 1,13 @@
+=> / Go back
+
+# {{ .Metadata.Name }} by {{ .Metadata.Author }}
+
+{{ .Metadata.Description }}
+
+{{if gt (len .Metadata.ContentWarnings) 0}}
+Content Warnings:
+{{- range $key, $value := .Metadata.ContentWarnings}}
+* {{ $value -}}
+{{end}}
+{{end}}
+=> /game/{{ .StartingState.StateToken }} Let's Play!<
\ No newline at end of file