~nromdotcom/gemif

3c3cfafab93e35e669c16702dd63c9ca558fe44b — Norm MacLennan 11 months ago 737d67f
Add additional story metadata and intro page
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