Render parts of the Yggdrasil generator (hardcoded map for now)

This commit is contained in:
Feufochmar 2020-04-12 19:07:06 +02:00
parent d3f4c21576
commit 5372579751
6 changed files with 411 additions and 1 deletions

View File

@ -13,7 +13,8 @@
"src/pages/home.rkt"
"src/pages/other-generators.rkt"
"src/pages/arnytron.rkt"
"src/pages/floraverse.rkt")
"src/pages/floraverse.rkt"
"src/pages/yggdrasil.rkt")
; Website
(define *website*
(website
@ -35,6 +36,11 @@
("Calendar" weblet pages:floraverse-calendar)
("Calendar/{month}/{day}" matching-weblet pages:floraverse-calendar)
)
; Yggdrasil generator
("Yggdrasil" weblet pages:yggdrasil
("About" weblet pages:yggdrasil-about)
("Raw" weblet pages:yggdrasil-raw-image)
)
; Other generators
("Generators" weblet pages:other-generators
("ColorScheme" weblet pages:color-scheme-generator)
@ -61,6 +67,9 @@
("Calendar" "/Floraverse/Calendar" #f)
("About the character generator" "/Floraverse/AboutCharacterGenerator" #f)
)
("Yggdrasil" "/Yggdrasil" #t
("About the Yggdrasil generator" "/Yggdrasil/About" #f)
)
("Miscellaneous Generators" "/Generators" #f
("Color Scheme" "/Generators/ColorScheme" #f)
("Pictogrammic Adventurer" "/Generators/PictogrammicAdventurer" #t)

View File

@ -0,0 +1,39 @@
#lang racket/base
; Dungeon generator
; Name from Etrian Odyssey, as the original output style was to look like Etrian Odyssey maps
; The generator outputs a 2d-array of cells, that should be given to a renderer
(require
racket/string
"../collection/2d-array.rkt"
"yggdrasil/cell.rkt"
)
(provide
yggdrasil-generate)
; Generator function
; The constraints are a hash of parameters, with the possible values:
; - (TODO)
(define (yggdrasil-generate [constraints (make-hash)])
; Fixed for now to test the rest
(yggdrasil-hard-coded-generate
'("################"
"#.L.....+..<...#"
"#.......#..#v#^#"
"#Omhtc!?#7.>...#"
"################")))
; Conversion from a hard-coded list of strings
(define (yggdrasil-hard-coded-generate strlst)
(define height (length strlst))
(define width (string-length (car strlst)))
(define charvec (string-join strlst ""))
;
(build-2d-array
width
height
(lambda (i j)
(character->yggdrasil-cell
(string-ref charvec (+ i (* j width)))))))

View File

@ -0,0 +1,101 @@
#lang racket/base
(require
racket/syntax)
; Cell of Yggdrasil generator
(provide
yggdrasil-cell-type
yggdrasil-cell-feature
; Accessors to premade cells
get-yggdrasil-cell
yggdrasil-cell-undefined
; Conversion from/to characters, for hard-coded blocks of dungeon
character->yggdrasil-cell
yggdrasil-cell->character
; Some features of the cells
yggdrasil-cell-defined?
yggdrasil-cell-blocker?
)
; Structure
(struct yggdrasil-cell
(type ; Type of cell
feature ; Feature of cell
character ; Character representing the cell
))
; Type of cell is among:
; #f (not defined)
; path
; wall
; Feature of cell is among:
; #f (no feature)
; door
; shortcut-EW
; shortcut-WE
; shortcut-NS
; shortcut-SN
; staircase-down
; staircase-up
; event
; check
; resource-mine
; resource-take
; resource-chop
; resource-harvest
; chest
; Collection of cells
(define *yggdrasil-cells*
(make-immutable-hash
`((undefined . ,(yggdrasil-cell #f #f #\$))
(wall . ,(yggdrasil-cell 'wall #f #\#))
(path . ,(yggdrasil-cell 'path #f #\.))
(staircase-down . ,(yggdrasil-cell 'path 'staircase-down #\L))
(staircase-up . ,(yggdrasil-cell 'path 'staircase-up #\7))
(shortcut-EW . ,(yggdrasil-cell 'wall 'shortcut-EW #\<))
(shortcut-WE . ,(yggdrasil-cell 'wall 'shortcut-WE #\>))
(shortcut-NS . ,(yggdrasil-cell 'wall 'shortcut-NS #\v))
(shortcut-SN . ,(yggdrasil-cell 'wall 'shortcut-SN #\^))
(door . ,(yggdrasil-cell 'wall 'door #\+))
(event . ,(yggdrasil-cell 'path 'event #\!))
(check . ,(yggdrasil-cell 'path 'check #\?))
(chest . ,(yggdrasil-cell 'path 'chest #\O))
(resource-mine . ,(yggdrasil-cell 'path 'resource-mine #\m))
(resource-take . ,(yggdrasil-cell 'path 'resource-take #\t))
(resource-chop . ,(yggdrasil-cell 'path 'resource-chop #\c))
(resource-harvest . ,(yggdrasil-cell 'path 'resource-harvest #\h))
)))
; Characters to cells
(define *char->cell*
(make-immutable-hash
(map
(lambda (c)
(cons
(yggdrasil-cell-character c)
c))
(hash-values *yggdrasil-cells*))))
; Accessor to premade cells
(define (get-yggdrasil-cell id)
(hash-ref *yggdrasil-cells* id (yggdrasil-cell-undefined)))
(define (yggdrasil-cell-undefined)
(hash-ref *yggdrasil-cells* 'undefined))
; Additional predicates
; Indicate if a cell is defined
(define (yggdrasil-cell-defined? cl)
(not (not (yggdrasil-cell-type cl))))
; Indicate if a cell is blocking the path
(define (yggdrasil-cell-blocker? cl)
(eq? 'wall (yggdrasil-cell-type cl)))
; Conversion from/to characters
(define (character->yggdrasil-cell chr)
(hash-ref *char->cell* chr (yggdrasil-cell-undefined)))
(define (yggdrasil-cell->character cl)
(yggdrasil-cell-character cl))

View File

@ -0,0 +1,120 @@
#lang racket/base
; Renderer of a dungeon generated by Yggdrasil
; A renderer is a function that takes a cell 2d-array and an output stream.
;
; Default renderer: outputs a PNG looking-like maps of Etrian Odyssey
; Uses a tileset and a color to draw grid and walls.
(require
"../../collection/2d-array.rkt"
"cell.rkt"
"../../graphics/color.rkt"
"../../graphics/image.rkt"
)
(provide
make-tile-renderer)
; Tileset
; From image path and tile size
(define (make-tileset path size)
; Function to define the tiles from their position
; (the sprite bank allows sprites of aribtrary sizes, arbitrary positioned in the image)
(define (resize lst)
(map
(lambda (x)
(cons
(car x)
(append
(map (lambda (n) (* size n)) (cdr x))
(list size size))))
lst))
;
(make-sprite-bank
path
(resize
'((wall 0 0) (path 1 0) (staircase-down 2 0) (staircase-up 3 0)
(shortcut-EW 0 1) (shortcut-WE 1 1) (shortcut-NS 2 1) (shortcut-SN 3 1)
(event 0 2) (check 1 2) (chest 2 2) (door 3 2)
(resource-chop 0 3) (resource-mine 1 3) (resource-take 2 3) (resource-harvest 3 3)))))
; Tile renderer structure
(struct tile-renderer
(tile-size
tileset
grid-color))
; Drawing function
(define (draw-dungeon renderer dungeon output)
(define pen-grid (make-pen (tile-renderer-grid-color renderer) 1))
(define pen-wall (make-pen (tile-renderer-grid-color renderer) 3))
(define cell-size (tile-renderer-tile-size renderer))
(define image
(make-image (* (2d-array-width dungeon) cell-size)
(* (2d-array-height dungeon) cell-size)))
; Draw cells by type: path or wall
(2d-array-for-each/indexes
dungeon
(lambda (cell i j)
(image-draw-sprite!
image
(* i cell-size)
(* j cell-size)
(tile-renderer-tileset renderer)
(if (yggdrasil-cell-blocker? cell)
'wall
'path))))
; Draw the grid
(2d-array-for-each/indexes
dungeon
(lambda (cell i j)
; separator between (i,j) and (i+1,j)
(when (2d-array-inside? dungeon (+ i 1) j)
(define next-cell (2d-array-ref dungeon (+ i 1) j))
(image-draw-line!
image
(* (+ i 1) cell-size)
(* j cell-size)
(* (+ i 1) cell-size)
(* (+ j 1) cell-size)
(if (eq? (yggdrasil-cell-type cell) (yggdrasil-cell-type next-cell))
pen-grid
pen-wall)))
; separator between (i,j) and (i,j+1)
(when (2d-array-inside? dungeon i (+ j 1))
(define next-cell (2d-array-ref dungeon i (+ j 1)))
(image-draw-line!
image
(* i cell-size)
(* (+ j 1) cell-size)
(* (+ i 1) cell-size)
(* (+ j 1) cell-size)
(if (eq? (yggdrasil-cell-type cell) (yggdrasil-cell-type next-cell))
pen-grid
pen-wall)))
))
; Draw the features
(2d-array-for-each/indexes
dungeon
(lambda (cell i j)
(define feature (yggdrasil-cell-feature cell))
(when feature
(image-draw-sprite!
image
(* i cell-size)
(* j cell-size)
(tile-renderer-tileset renderer)
feature))))
; Save image
(image-save image output))
;
(define (make-tile-renderer tile-path tile-size grid-color)
(define renderer
(tile-renderer
tile-size
(make-tileset tile-path tile-size)
grid-color))
(lambda (dungeon output)
(draw-dungeon renderer dungeon output)))

79
src/pages/yggdrasil.rkt Normal file
View File

@ -0,0 +1,79 @@
#lang racket/base
; Yggdrasil pages
(require
racket/string
"templates.rkt"
"../webcontainer/weblets.rkt"
"../generators/yggdrasil.rkt"
"../generators/yggdrasil/tile-renderer.rkt"
"../graphics/color.rkt")
(provide
pages:yggdrasil
pages:yggdrasil-raw-image
pages:yggdrasil-about)
; Default renderer
(define yggdrasil-default (make-tile-renderer "static/images/yggdrasil-default.png" 15 (named-color "black")))
; Raw page
(define pages:yggdrasil-raw-image
(raw-data-weblet
#:content-type #"image/png"
#:body
(lambda (param)
(define output-port (open-output-bytes))
(yggdrasil-default
(yggdrasil-generate param)
output-port)
(get-output-bytes output-port))))
; Yggdrasil page
(define pages:yggdrasil
(pages:template
#:title "Yggdrasil Generator"
#:author "feuforeve.fr"
#:stylesheets '("/css/yggdrasil.css")
#:content
'(article
(a ((href "/Yggdrasil")) "New level")
(p
(section ((style "display: inline-block;"))
(img ((alt "Generated Maze") (src "/Yggdrasil/Raw"))))
(section ((class "keys"))
(h4 "Keys")
(span ((class "yggdrasil-default")(id "wall"))) " Wall" (br)
(span ((class "yggdrasil-default")(id "path"))) " Path" (br)
(span ((class "yggdrasil-default")(id "staircase-down"))) " Staircase Start point (go to previous level)" (br)
(span ((class "yggdrasil-default")(id "staircase-up"))) " Staircase End point (go to next level)" (br)
(span ((class "yggdrasil-default")(id "door"))) " Door" (br)
(span ((class "yggdrasil-default")(id "chest"))) " Chest" (br)
(span ((class "yggdrasil-default")(id "event"))) " Event (immediate)" (br)
(span ((class "yggdrasil-default")(id "check"))) " Event (must check)" (br)
(span ((class "yggdrasil-default")(id "resource-chop"))) " Collect spot (Chop)" (br)
(span ((class "yggdrasil-default")(id "resource-mine"))) " Collect spot (Mine)" (br)
(span ((class "yggdrasil-default")(id "resource-take"))) " Collect spot (Take)" (br)
(span ((class "yggdrasil-default")(id "resource-harvest"))) " Collect spot (Harvest)" (br)
(span ((class "yggdrasil-default")(id "shortcut-EW"))) " Shortcut (east-west, unlocked from east)" (br)
(span ((class "yggdrasil-default")(id "shortcut-WE"))) " Shortcut (east-west, unlocked from west)" (br)
(span ((class "yggdrasil-default")(id "shortcut-NS"))) " Shortcut (north-south, unlocked from north)" (br)
(span ((class "yggdrasil-default")(id "shortcut-SN"))) " Shortcut (north-south, unlocked from south)" (br)
)))
))
; Yggdrasil about page
(define pages:yggdrasil-about
(pages:template
#:title "About the Yggdrasil generator"
#:author "Feufochmar"
#:date "2020-04-12"
#:content
'(article
(p "This generator makes dungeons maps. "
"By default, it displays them in the style of the game series " (a ((href "https://en.wikipedia.org/wiki/Etrian_Odyssey")) "Etrian Odyssey") ". " (br)
"This generator is named after the giant tree Yggdrasil hosting the labyrinth in those games. " (br)
"This generator was initially made for the " (a ((href "https://itch.io/jam/procjam2017/rate/193843")) "ProcJam 2017") ". " (br)
))
))

62
static/css/yggdrasil.css Normal file
View File

@ -0,0 +1,62 @@
.keys {
display: inline-block;
vertical-align: top;
margin-left: 1em;
}
.yggdrasil-default {
width: 15;
height: 15;
background-image: url('/images/yggdrasil-default.png');
background-repeat: no-repeat;
display: inline-block;
}
#wall.yggdrasil-default {
background-position: 0px 0px;
}
#path.yggdrasil-default {
background-position: -15px 0px;
}
#staircase-down.yggdrasil-default {
background-position: -30px 0px;
}
#staircase-up.yggdrasil-default {
background-position: -45px 0px;
}
#shortcut-EW.yggdrasil-default {
background-position: 0px -15px;
}
#shortcut-WE.yggdrasil-default {
background-position: -15px -15px;
}
#shortcut-NS.yggdrasil-default {
background-position: -30px -15px;
}
#shortcut-SN.yggdrasil-default {
background-position: -45px -15px;
}
#event.yggdrasil-default {
background-position: 0px -30px;
}
#check.yggdrasil-default {
background-position: -15px -30px;
}
#chest.yggdrasil-default {
background-position: -30px -30px;
}
#door.yggdrasil-default {
background-position: -45px -30px;
}
#resource-chop.yggdrasil-default {
background-position: 0px -45px;
}
#resource-mine.yggdrasil-default {
background-position: -15px -45px;
}
#resource-take.yggdrasil-default {
background-position: -30px -45px;
}
#resource-harvest.yggdrasil-default {
background-position: -45px -45px;
}