Version 1.0
This commit is contained in:
parent
3d58303936
commit
3871273730
11 changed files with 197 additions and 33 deletions
12
Dockerfile
Normal file
12
Dockerfile
Normal file
|
@ -0,0 +1,12 @@
|
|||
FROM golang:1.16.2-alpine AS builder
|
||||
RUN apk update
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
RUN GOOS=linux go build -o kartograph cmd/kartograph-map-generator/main.go
|
||||
|
||||
FROM alpine:latest
|
||||
RUN apk update
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/kartograph /app/
|
||||
EXPOSE 80
|
||||
ENTRYPOINT ["/app/kartograph"]
|
34
README.mkd
Normal file
34
README.mkd
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Der Kartograph Map Generator
|
||||
|
||||
Generate your own maps for "Der Kartograph" easily.
|
||||
|
||||
## Build
|
||||
|
||||
You need to build the source first. The simplest way is to use the
|
||||
provided Dockerfile, which will build the software and create a
|
||||
convenient image (This is a multi stage Dockerfile, so the resulting
|
||||
image is a small Alpine Linux image with ust the map generator binary.)
|
||||
|
||||
$ docker build -t kartograph .
|
||||
|
||||
## Run
|
||||
|
||||
See all available options:
|
||||
|
||||
$ docker run -it kartograph -help
|
||||
|
||||
Spawn a webserver:
|
||||
|
||||
$ docker run -it kartograph -web
|
||||
|
||||
Generate a map with the seed "hey jim" and output it as a SVG image:
|
||||
|
||||
$ docker run -it kartograph -output=svg -seed='hey jim' > map.svg
|
||||
|
||||
You want an epic game? Lets generate a big map with a lot of wasteland
|
||||
and ruins:
|
||||
|
||||
$ docker run -it kartograph -seed=aaron \
|
||||
-size=20 -wastelands=100 -ruins=10 \
|
||||
-output=svg > map.svg
|
||||
|
|
@ -45,7 +45,7 @@ func main() {
|
|||
|
||||
func init() {
|
||||
flag.BoolVar(&startWebserver, "web", false, "Spawn a webserver")
|
||||
flag.StringVar(&address, "address", "0.0.0.0", "IP to bin the service to")
|
||||
flag.StringVar(&address, "address", "0.0.0.0", "IP to bind the service to")
|
||||
flag.IntVar(&port, "port", 80, "Port to bind the service to")
|
||||
|
||||
flag.StringVar(&output, "output", "svg", "Output format (svg, ascii, json)")
|
||||
|
|
1
go.mod
1
go.mod
|
@ -4,5 +4,4 @@ go 1.16
|
|||
|
||||
require (
|
||||
github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb
|
||||
github.com/jung-kurt/gofpdf v1.16.2
|
||||
)
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package generator
|
||||
|
||||
import ()
|
||||
|
||||
func (w World) PDF() {
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@ import (
|
|||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TerritoryType int
|
||||
|
@ -69,7 +70,7 @@ type World struct {
|
|||
Mountains int `json:"mountains"`
|
||||
|
||||
World []Tile `json:"tiles"`
|
||||
Seed int
|
||||
Seed string
|
||||
}
|
||||
|
||||
func (w World) Plot() string {
|
||||
|
@ -90,23 +91,15 @@ func (w World) JSON() string {
|
|||
return string(output)
|
||||
}
|
||||
|
||||
func (w World) ExportToPDF(filename string) error {
|
||||
// TODO: Draw the PDF
|
||||
return nil
|
||||
}
|
||||
|
||||
func New(size int, numWastelands int, numMountains int, numRuins int, seed string) World {
|
||||
// Fix seed
|
||||
h := md5.New()
|
||||
io.WriteString(h, seed)
|
||||
var intSeed uint64 = binary.BigEndian.Uint64(h.Sum(nil))
|
||||
rand.Seed(int64(intSeed))
|
||||
InitSeed(seed)
|
||||
|
||||
w := World{
|
||||
Size: size,
|
||||
Wastelands: numWastelands,
|
||||
Mountains: numMountains,
|
||||
Ruins: numRuins,
|
||||
Seed: seed,
|
||||
}
|
||||
|
||||
// All empty for start
|
||||
|
@ -174,6 +167,24 @@ func New(size int, numWastelands int, numMountains int, numRuins int, seed strin
|
|||
return w
|
||||
}
|
||||
|
||||
func InitSeed(seed string) {
|
||||
h := md5.New()
|
||||
io.WriteString(h, seed)
|
||||
var intSeed uint64 = binary.BigEndian.Uint64(h.Sum(nil))
|
||||
rand.Seed(int64(intSeed))
|
||||
}
|
||||
|
||||
func RandomSeed() string {
|
||||
InitSeed(time.Now().String())
|
||||
chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
var seed []byte
|
||||
for i := 0; i < 10; i++ {
|
||||
seed = append(seed, chars[roll(25)])
|
||||
}
|
||||
|
||||
return string(seed)
|
||||
}
|
||||
|
||||
func (w World) neighbour(direction int, pos int) (int, error) {
|
||||
switch direction {
|
||||
case UpDirection:
|
||||
|
|
|
@ -41,15 +41,15 @@ func mapHandler(w http.ResponseWriter, req *http.Request) {
|
|||
if seed == "" {
|
||||
seed = time.Now().String()
|
||||
}
|
||||
wastelands, err := strconv.Atoi(req.URL.Query().Get("wastelands"))
|
||||
wastelands, err := strconv.Atoi(req.URL.Query().Get("w"))
|
||||
if err != nil {
|
||||
wastelands = 7
|
||||
}
|
||||
mountains, err := strconv.Atoi(req.URL.Query().Get("mountains"))
|
||||
mountains, err := strconv.Atoi(req.URL.Query().Get("m"))
|
||||
if err != nil {
|
||||
mountains = 5
|
||||
}
|
||||
ruins, err := strconv.Atoi(req.URL.Query().Get("ruins"))
|
||||
ruins, err := strconv.Atoi(req.URL.Query().Get("r"))
|
||||
if err != nil {
|
||||
ruins = 6
|
||||
}
|
||||
|
@ -63,12 +63,15 @@ func mapHandler(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
func indexHandler(w http.ResponseWriter, req *http.Request) {
|
||||
log.Printf("GET / (%v)", req.RemoteAddr)
|
||||
seed := generator.RandomSeed()
|
||||
world := generator.New(11, 7, 5, 6, seed)
|
||||
|
||||
log.Printf("GET / (%v) Seed: %v", req.RemoteAddr, seed)
|
||||
|
||||
tpl, err := template.ParseFS(templateFiles, "templates/index.tpl.html")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
tpl.Execute(w, nil)
|
||||
tpl.Execute(w, world)
|
||||
}
|
||||
|
|
51
pkg/web/static/generator.js
Normal file
51
pkg/web/static/generator.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let generate = function(seed) {
|
||||
let size = document.querySelector('input[name="size"]').value
|
||||
let wastelands = document.querySelector('input[name="wastelands"]').value
|
||||
let mountains = document.querySelector('input[name="mountains"]').value
|
||||
let ruins = document.querySelector('input[name="ruins"]').value
|
||||
|
||||
let map = document.querySelector('.map')
|
||||
// Generate new random seed
|
||||
let newMapUrl = '/map.svg?seed=' + seed +
|
||||
'&s=' + size +
|
||||
'&w=' + wastelands +
|
||||
'&m=' + mountains +
|
||||
'&r=' + ruins
|
||||
document.querySelector('.map').setAttribute('src', newMapUrl);
|
||||
document.querySelector('#map-link').setAttribute('href', newMapUrl);
|
||||
};
|
||||
|
||||
let buttons = document.querySelectorAll('input[type="button"]')
|
||||
Array.prototype.forEach.call(buttons, function(button, i) {
|
||||
button.addEventListener('click', function(e) {
|
||||
// Reset to default buttons
|
||||
if (e.target.hasAttribute('data-default')) {
|
||||
let defaultValue = e.target.getAttribute('data-default');
|
||||
let name = e.target.getAttribute('name').substr(8);
|
||||
let targetInput = document.querySelector('input[type="text"][name="'+name+'"]')
|
||||
targetInput.value = defaultValue;
|
||||
}
|
||||
|
||||
// Roll the dice
|
||||
if (e.target.getAttribute('name') === 'random-seed') {
|
||||
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
let seed = '';
|
||||
for (let i=0; i<10; i++) {
|
||||
let index = Math.floor(Math.random() * 25)
|
||||
seed += chars[index]
|
||||
}
|
||||
|
||||
let seedElement = document.querySelector('input[name="seed"]');
|
||||
seedElement.value = seed;
|
||||
generate(seed);
|
||||
}
|
||||
|
||||
// Refresh
|
||||
if (e.target.getAttribute('name') === 'refresh') {
|
||||
let seed = document.querySelector('input[name="seed"]').value;
|
||||
generate(seed);
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
Binary file not shown.
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 31 KiB |
|
@ -1,10 +1,38 @@
|
|||
body {
|
||||
width: 900px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p > img {
|
||||
float: right;
|
||||
width: 250px;
|
||||
float: left;
|
||||
width: 100px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.map {
|
||||
margin: auto;
|
||||
display: block;
|
||||
width: 350px;
|
||||
float: right;
|
||||
width: 450px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 90px;
|
||||
}
|
||||
input[name="seed"] {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
width: 370px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/static/styles.css" rel="stylesheet" type="text/css" media="all">
|
||||
<script src="/static/generator.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Map Generator für "Der Kartograph"</h1>
|
||||
<a id="map-link" href="/map.svg?seed={{ .Seed }}&s={{ .Size }}&w={{ .Wastelands }}&r={{ .Ruins }}&m={{ .Mountains }}"><img src="/map.svg?seed={{ .Seed }}&s={{ .Size }}&w={{ .Wastelands }}&r={{ .Ruins }}&m={{ .Mountains }}" class="map"></a>
|
||||
<p><img src="/static/images/logo.jpg">
|
||||
Wem der beigelegte Block zu eintönig wird oder schon leergespielt hat, kann sich
|
||||
entweder die Mini-Erweiterung kaufen, in dem ein weiterer Block enthalten ist,
|
||||
|
@ -17,8 +19,39 @@
|
|||
Die generierten Karten entsprechen den erweiterten Regeln, können aber für etwas
|
||||
mehr Spaß angepasst werden.</p>
|
||||
|
||||
<a href="/">Neue Map generieren</a>
|
||||
<br><br>
|
||||
<img src="/map.svg" class="map">
|
||||
{{ with . }}
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>Seed</legend>
|
||||
<input type="text" name="seed" value="{{ .Seed }}">
|
||||
<input type="button" name="random-seed" value="Zufall">
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Spielfeldgröße</legend>
|
||||
<input type="text" name="size" value="{{ .Size }}">
|
||||
<input type="button" name="default-size" value="Standard (11)" class="default-button" data-default="11">
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Parameter</legend>
|
||||
<label for="wastelands">Ödland:</label>
|
||||
<input type="text" name="wastelands" value="{{ .Wastelands }}">
|
||||
<input type="button" name="default-wastelands" value="Standard (7)" class="default-button" data-default="7">
|
||||
<br>
|
||||
<label for="mountains">Berge:</label>
|
||||
<input type="text" name="mountains" value="{{ .Mountains }}">
|
||||
<input type="button" name="default-mountains" value="Standard (5)" class="default-button" data-default="5">
|
||||
<br>
|
||||
<label for="ruins">Ruinen:</label>
|
||||
<input type="text" name="ruins" value="{{ .Ruins }}">
|
||||
<input type="button" name="default-ruins" value="Standard (6)" class="default-button" data-default="6">
|
||||
<br>
|
||||
</fieldset>
|
||||
|
||||
<input type="button" name="refresh" value="Neu generieren">
|
||||
<input type="button" name="print" value="Drucken">
|
||||
</form>
|
||||
{{ end }}
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in a new issue