map
This commit is contained in:
parent
001f398137
commit
9f38f9b17d
17 changed files with 530 additions and 79 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,4 +5,5 @@ legendsbrowser
|
|||
*.png
|
||||
/*.json
|
||||
.DS_Store
|
||||
bin
|
||||
bin
|
||||
/inputs/*
|
|
@ -5,6 +5,36 @@
|
|||
"df_world|historical_events|historical_event+KnowledgeDiscovered|knowledge": true
|
||||
},
|
||||
"AdditionalFields": {
|
||||
"DfWorld": [
|
||||
{
|
||||
"Name": "FilePath",
|
||||
"Type": "string"
|
||||
},
|
||||
{
|
||||
"Name": "PlusFilePath",
|
||||
"Type": "string"
|
||||
},
|
||||
{
|
||||
"Name": "MapReady",
|
||||
"Type": "bool"
|
||||
},
|
||||
{
|
||||
"Name": "MapData",
|
||||
"Type": "[]byte"
|
||||
},
|
||||
{
|
||||
"Name": "Width",
|
||||
"Type": "int"
|
||||
},
|
||||
{
|
||||
"Name": "Height",
|
||||
"Type": "int"
|
||||
},
|
||||
{
|
||||
"Name": "EndYear",
|
||||
"Type": "int"
|
||||
}
|
||||
],
|
||||
"Structure": [
|
||||
{
|
||||
"Name": "SiteId",
|
||||
|
|
|
@ -10,6 +10,7 @@ require (
|
|||
github.com/pkg/profile v1.6.0
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171
|
||||
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
@ -29,9 +29,13 @@ github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPR
|
|||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE=
|
||||
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
|
|
@ -3,10 +3,9 @@ package main
|
|||
import (
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/pkg/profile"
|
||||
|
@ -26,6 +25,8 @@ func main() {
|
|||
|
||||
templates.DebugTemplates = *d
|
||||
|
||||
var world *model.DfWorld
|
||||
|
||||
if len(*f) > 0 {
|
||||
if *p {
|
||||
defer profile.Start(profile.ProfilePath(".")).Stop()
|
||||
|
@ -36,14 +37,15 @@ func main() {
|
|||
|
||||
w, err := model.Parse(*f, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
runtime.GC()
|
||||
server.StartServer(w, static)
|
||||
world = w
|
||||
}
|
||||
|
||||
} else {
|
||||
server.StartServer(nil, static)
|
||||
err := server.StartServer(world, static)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"html/template"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -23,6 +24,21 @@ var LinkMusicalForm = func(w *DfWorld, id int) template.HTML { return template.H
|
|||
var LinkPoeticForm = func(w *DfWorld, id int) template.HTML { return template.HTML((&Context{World: w}).poeticForm(id)) }
|
||||
var LinkWrittenContent = func(w *DfWorld, id int) template.HTML { return template.HTML((&Context{World: w}).writtenContent(id)) }
|
||||
|
||||
var AddMapSite = func(w *DfWorld, id int) template.HTML {
|
||||
if site, ok := w.Sites[id]; ok {
|
||||
coords := strings.Split(site.Rectangle, ":")
|
||||
c1 := strings.Split(coords[0], ",")
|
||||
x1, _ := strconv.ParseFloat(c1[0], 32)
|
||||
y1, _ := strconv.ParseFloat(c1[1], 32)
|
||||
c2 := strings.Split(coords[1], ",")
|
||||
x2, _ := strconv.ParseFloat(c2[0], 32)
|
||||
y2, _ := strconv.ParseFloat(c2[1], 32)
|
||||
return template.HTML(fmt.Sprintf(`<script>addSite("%s", %f, %f, %f, %f, "#FF0", "")</script>`, site.Name(), x1/16.0, y1/16.0-1, x2/16.0, y2/16.0-1))
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func andList(list []string) string {
|
||||
if len(list) > 1 {
|
||||
return strings.Join(list[:len(list)-1], ", ") + " and " + list[len(list)-1]
|
||||
|
|
94
backend/model/map.go
Normal file
94
backend/model/map.go
Normal file
|
@ -0,0 +1,94 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/png"
|
||||
_ "image/png"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "golang.org/x/image/bmp"
|
||||
)
|
||||
|
||||
func (w *DfWorld) LoadMap() {
|
||||
w.LoadDimensions()
|
||||
|
||||
path := ""
|
||||
files, err := filepath.Glob(strings.ReplaceAll(w.FilePath, "-legends.xml", "-world_map.*"))
|
||||
if err == nil && len(files) > 0 {
|
||||
path = files[len(files)-1]
|
||||
}
|
||||
files, err = filepath.Glob(strings.ReplaceAll(w.FilePath, "-legends.xml", "-detailed.*"))
|
||||
if err == nil && len(files) > 0 {
|
||||
path = files[len(files)-1]
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
return
|
||||
}
|
||||
|
||||
mapImage, err := os.Open(path)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Found Map", path)
|
||||
img, format, err := image.Decode(mapImage)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("loaded img", format)
|
||||
buf := new(bytes.Buffer)
|
||||
err = png.Encode(buf, img)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
w.MapData = buf.Bytes()
|
||||
w.MapReady = true
|
||||
}
|
||||
|
||||
func (w *DfWorld) LoadDimensions() {
|
||||
files, err := filepath.Glob(filepath.Join(filepath.Dir(w.FilePath), "*-world_gen_param.txt"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
path := ""
|
||||
for _, f := range files {
|
||||
prefix := filepath.Base(f)[:len(filepath.Base(f))-len("world_gen_param.txt")]
|
||||
if strings.HasPrefix(filepath.Base(w.FilePath), prefix) {
|
||||
path = f
|
||||
break
|
||||
}
|
||||
}
|
||||
if path == "" {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Found Worldgen", path)
|
||||
|
||||
content, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
text := string(content)
|
||||
fmt.Println(text)
|
||||
|
||||
r := regexp.MustCompile(`\[DIM:(\d+):(\d+)\]`)
|
||||
result := r.FindAllStringSubmatch(text, 1)
|
||||
if result == nil {
|
||||
return
|
||||
}
|
||||
w.Width, _ = strconv.Atoi(result[0][2])
|
||||
w.Height, _ = strconv.Atoi(result[0][1])
|
||||
}
|
|
@ -1561,6 +1561,13 @@ type DfWorld struct {
|
|||
UndergroundRegions map[int]*UndergroundRegion `json:"undergroundRegions" legend:"both"` // underground_regions
|
||||
WorldConstructions map[int]*WorldConstruction `json:"worldConstructions" legend:"both"` // world_constructions
|
||||
WrittenContents map[int]*WrittenContent `json:"writtenContents" legend:"both"` // written_contents
|
||||
EndYear int `json:"endYear" legend:"add"` // EndYear
|
||||
FilePath string `json:"filePath" legend:"add"` // FilePath
|
||||
Height int `json:"height" legend:"add"` // Height
|
||||
MapData []byte `json:"mapData" legend:"add"` // MapData
|
||||
MapReady bool `json:"mapReady" legend:"add"` // MapReady
|
||||
PlusFilePath string `json:"plusFilePath" legend:"add"` // PlusFilePath
|
||||
Width int `json:"width" legend:"add"` // Width
|
||||
}
|
||||
|
||||
func NewDfWorld() *DfWorld {
|
||||
|
@ -1582,6 +1589,9 @@ func NewDfWorld() *DfWorld {
|
|||
UndergroundRegions: make(map[int]*UndergroundRegion),
|
||||
WorldConstructions: make(map[int]*WorldConstruction),
|
||||
WrittenContents: make(map[int]*WrittenContent),
|
||||
EndYear: -1,
|
||||
Height: -1,
|
||||
Width: -1,
|
||||
}
|
||||
}
|
||||
func (x *DfWorld) Name() string { return x.Name_ }
|
||||
|
|
|
@ -2,10 +2,8 @@ package model
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
@ -98,6 +96,7 @@ BaseLoop:
|
|||
}
|
||||
}
|
||||
}
|
||||
world.FilePath = file
|
||||
|
||||
bar.Finish()
|
||||
|
||||
|
@ -133,15 +132,18 @@ BaseLoop:
|
|||
}
|
||||
}
|
||||
}
|
||||
world.PlusFilePath = file
|
||||
|
||||
bar.Finish()
|
||||
}
|
||||
|
||||
same, err := json.MarshalIndent(exportSameFields(), "", " ")
|
||||
if err != nil {
|
||||
return world, err
|
||||
}
|
||||
ioutil.WriteFile("same.json", same, 0644)
|
||||
// same, err := json.MarshalIndent(exportSameFields(), "", " ")
|
||||
// if err != nil {
|
||||
// return world, err
|
||||
// }
|
||||
// ioutil.WriteFile("same.json", same, 0644)
|
||||
|
||||
world.LoadMap()
|
||||
|
||||
world.process()
|
||||
|
||||
|
|
66
backend/server/config.go
Normal file
66
backend/server/config.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
LastPath string
|
||||
LastFile string
|
||||
}
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
path, err := configPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Println("OPEN", err)
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Println("EX", err)
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Config{LastPath: home}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c := &Config{}
|
||||
json.Unmarshal(data, c)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Config) Save() error {
|
||||
path, err := configPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(path, file, 0644)
|
||||
}
|
||||
|
||||
func configPath() (string, error) {
|
||||
path, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
path = filepath.Join(path, ".legendsbrowser")
|
||||
os.MkdirAll(path, os.ModePerm)
|
||||
|
||||
return filepath.Join(path, "config.json"), nil
|
||||
}
|
|
@ -63,7 +63,7 @@ func (h loadHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
Current: path,
|
||||
}
|
||||
if p.Current == "" {
|
||||
p.Current = "."
|
||||
p.Current = h.server.context.config.LastPath
|
||||
}
|
||||
var err error
|
||||
p.Current, err = filepath.Abs(p.Current)
|
||||
|
@ -74,6 +74,9 @@ func (h loadHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if f, err := os.Stat(p.Current); err == nil {
|
||||
if f.IsDir() {
|
||||
h.server.context.config.LastPath = p.Current
|
||||
h.server.context.config.Save()
|
||||
|
||||
p.List, err = ioutil.ReadDir(p.Current)
|
||||
if err != nil {
|
||||
httpError(w, err)
|
||||
|
|
|
@ -3,9 +3,7 @@ package server
|
|||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
|
@ -16,6 +14,7 @@ import (
|
|||
)
|
||||
|
||||
type DfServerContext struct {
|
||||
config *Config
|
||||
world *model.DfWorld
|
||||
isLoading bool
|
||||
progress *model.LoadProgress
|
||||
|
@ -28,10 +27,16 @@ type DfServer struct {
|
|||
context *DfServerContext
|
||||
}
|
||||
|
||||
func StartServer(world *model.DfWorld, static embed.FS) {
|
||||
func StartServer(world *model.DfWorld, static embed.FS) error {
|
||||
config, err := LoadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srv := &DfServer{
|
||||
router: mux.NewRouter().StrictSlash(true),
|
||||
context: &DfServerContext{
|
||||
config: config,
|
||||
world: world,
|
||||
isLoading: false,
|
||||
progress: &model.LoadProgress{},
|
||||
|
@ -99,6 +104,12 @@ func StartServer(world *model.DfWorld, static embed.FS) {
|
|||
}
|
||||
})
|
||||
|
||||
srv.router.HandleFunc("/map", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(srv.loader.server.context.world.MapData)
|
||||
})
|
||||
|
||||
srv.router.PathPrefix("/search").Handler(searchHandler{server: srv})
|
||||
|
||||
srv.router.PathPrefix("/load").Handler(srv.loader)
|
||||
|
@ -110,6 +121,7 @@ func StartServer(world *model.DfWorld, static embed.FS) {
|
|||
|
||||
fmt.Println("Serving at :8080")
|
||||
http.ListenAndServe(":8080", srv.router)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *DfServer) findStructure(p Parms) any {
|
||||
|
@ -127,53 +139,6 @@ func (srv *DfServer) findStructure(p Parms) any {
|
|||
return nil
|
||||
}
|
||||
|
||||
type spaHandler struct {
|
||||
server *DfServer
|
||||
staticFS embed.FS
|
||||
staticPath string
|
||||
indexPath string
|
||||
}
|
||||
|
||||
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// get the absolute path to prevent directory traversal
|
||||
path := r.URL.Path
|
||||
// if err != nil {
|
||||
// // if we failed to get the absolute path respond with a 400 bad request and stop
|
||||
// http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
// return
|
||||
// }
|
||||
// prepend the path with the path to the static directory
|
||||
path = h.staticPath + path
|
||||
|
||||
_, err := h.staticFS.Open(path)
|
||||
if os.IsNotExist(err) {
|
||||
// file does not exist, serve index.html
|
||||
fmt.Println(path)
|
||||
index, err := h.staticFS.ReadFile(h.staticPath + "/" + h.indexPath)
|
||||
if err != nil {
|
||||
h.server.notFound(w)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
w.Write(index)
|
||||
return
|
||||
} else if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// get the subdirectory of the static dir
|
||||
statics, err := fs.Sub(h.staticFS, h.staticPath)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise, use http.FileServer to serve the static dir
|
||||
http.FileServer(http.FS(statics)).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (srv *DfServer) notFound(w http.ResponseWriter) {
|
||||
err := srv.templates.Render(w, "notFound.html", nil)
|
||||
if err != nil {
|
||||
|
|
56
backend/server/static.go
Normal file
56
backend/server/static.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
type spaHandler struct {
|
||||
server *DfServer
|
||||
staticFS embed.FS
|
||||
staticPath string
|
||||
indexPath string
|
||||
}
|
||||
|
||||
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// get the absolute path to prevent directory traversal
|
||||
path := r.URL.Path
|
||||
// if err != nil {
|
||||
// // if we failed to get the absolute path respond with a 400 bad request and stop
|
||||
// http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
// return
|
||||
// }
|
||||
// prepend the path with the path to the static directory
|
||||
path = h.staticPath + path
|
||||
|
||||
_, err := h.staticFS.Open(path)
|
||||
if os.IsNotExist(err) {
|
||||
// file does not exist, serve index.html
|
||||
fmt.Println(path)
|
||||
index, err := h.staticFS.ReadFile(h.staticPath + "/" + h.indexPath)
|
||||
if err != nil {
|
||||
h.server.notFound(w)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
w.Write(index)
|
||||
return
|
||||
} else if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// get the subdirectory of the static dir
|
||||
statics, err := fs.Sub(h.staticFS, h.staticPath)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise, use http.FileServer to serve the static dir
|
||||
http.FileServer(http.FS(statics)).ServeHTTP(w, r)
|
||||
}
|
|
@ -21,13 +21,19 @@ func (srv *DfServer) LoadTemplates() {
|
|||
}
|
||||
return nil
|
||||
},
|
||||
"title": util.Title,
|
||||
"kebab": func(s string) string { return strcase.ToKebab(s) },
|
||||
"title": util.Title,
|
||||
"kebab": func(s string) string { return strcase.ToKebab(s) },
|
||||
"world": func() *model.DfWorld { return srv.context.world },
|
||||
"initMap": func() template.HTML {
|
||||
return template.HTML(fmt.Sprintf(`<script>var worldWidth = %d, worldHeight = %d;</script><script src="/js/map.js"></script>`,
|
||||
srv.context.world.Width, srv.context.world.Height))
|
||||
},
|
||||
"hf": func(id int) template.HTML { return model.LinkHf(srv.context.world, id) },
|
||||
"getHf": func(id int) *model.HistoricalFigure { return srv.context.world.HistoricalFigures[id] },
|
||||
"entity": func(id int) template.HTML { return model.LinkEntity(srv.context.world, id) },
|
||||
"getEntity": func(id int) *model.Entity { return srv.context.world.Entities[id] },
|
||||
"site": func(id int) template.HTML { return model.LinkSite(srv.context.world, id) },
|
||||
"addSite": func(id int) template.HTML { return model.AddMapSite(srv.context.world, id) },
|
||||
"getSite": func(id int) *model.Site { return srv.context.world.Sites[id] },
|
||||
"structure": func(siteId, id int) template.HTML { return model.LinkStructure(srv.context.world, siteId, id) },
|
||||
"region": func(id int) template.HTML { return model.LinkRegion(srv.context.world, id) },
|
||||
|
|
198
backend/static/js/map.js
Normal file
198
backend/static/js/map.js
Normal file
|
@ -0,0 +1,198 @@
|
|||
var sitesLayer = L.layerGroup();
|
||||
var constructionsLayer = L.layerGroup();
|
||||
var landmassesLayer = L.layerGroup();
|
||||
var regionsLayer = L.layerGroup();
|
||||
var mountainsLayer = L.layerGroup();
|
||||
var evilnessLayer = L.layerGroup();
|
||||
|
||||
var map = L.map('map', {
|
||||
maxZoom: 6,
|
||||
minZoom: 0,
|
||||
crs: L.CRS.Simple,
|
||||
layers: [sitesLayer, constructionsLayer, mountainsLayer, evilnessLayer]
|
||||
});
|
||||
|
||||
map.getBoundsZoom = function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
|
||||
bounds = L.latLngBounds(bounds);
|
||||
|
||||
var zoom = this.getMinZoom() - (inside ? 1 : 0),
|
||||
maxZoom = this.getMaxZoom(),
|
||||
size = this.getSize(),
|
||||
|
||||
nw = bounds.getNorthWest(),
|
||||
se = bounds.getSouthEast(),
|
||||
|
||||
zoomNotFound = true,
|
||||
boundsSize;
|
||||
|
||||
padding = L.point(padding || [0, 0]);
|
||||
|
||||
var incement = 0.02;
|
||||
do {
|
||||
zoom += incement;
|
||||
boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding).floor();
|
||||
zoomNotFound = !inside ? size.contains(boundsSize) : boundsSize.x < size.x || boundsSize.y < size.y;
|
||||
|
||||
} while (zoomNotFound && zoom <= maxZoom);
|
||||
|
||||
if (zoomNotFound && inside) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return inside ? zoom : zoom - incement;
|
||||
}
|
||||
|
||||
var bounds = new L.LatLngBounds([0, 0], [worldWidth,
|
||||
worldHeight]);
|
||||
map.setMaxBounds(bounds);
|
||||
map.fitBounds(bounds);
|
||||
|
||||
map.options.minZoom = map.getZoom();
|
||||
|
||||
var imageUrl = '/map'
|
||||
var imageBounds = [[0, 0],
|
||||
[worldWidth, worldHeight]];
|
||||
|
||||
var overlayMaps = {
|
||||
"Sites": sitesLayer,
|
||||
"World Constructions": constructionsLayer,
|
||||
"Mountain Peaks": mountainsLayer,
|
||||
"Landmasses": landmassesLayer,
|
||||
"Regions": regionsLayer,
|
||||
"Evilness": evilnessLayer,
|
||||
};
|
||||
|
||||
L.control.layers(null, overlayMaps).addTo(map);
|
||||
|
||||
var imageLayer = L.imageOverlay(imageUrl, imageBounds, { opacity: 0.5 });
|
||||
imageLayer.addTo(map);
|
||||
|
||||
// var opacitySlider = new L.Control.opacitySlider();
|
||||
// map.addControl(opacitySlider);
|
||||
// opacitySlider.setOpacityLayer(imageLayer);
|
||||
|
||||
|
||||
var minx = 1000, miny = 1000, maxx = 0, maxy = 0;
|
||||
|
||||
function zoomTo(y, x, zoom) {
|
||||
x = worldWidth - x - 1;
|
||||
map.setView([x, y], zoom);
|
||||
}
|
||||
|
||||
function zoom() {
|
||||
var sw = L.latLng(minx, miny), ne = L.latLng(maxx + 1, maxy + 1);
|
||||
var bounds = new L.LatLngBounds(sw, ne);
|
||||
console.log(sw, ne, bounds);
|
||||
map.fitBounds(bounds);
|
||||
}
|
||||
|
||||
|
||||
var siteOffset = 0.1;
|
||||
var structureOffset = 0.35;
|
||||
var battleOffset = 0.2;
|
||||
var mountainOffset = -0.2;
|
||||
|
||||
function coordO(y, x, yo, xo) {
|
||||
var c = coord(y, x);
|
||||
return [c[0] + yo, c[1] + xo]
|
||||
}
|
||||
|
||||
function square(y, x, o) {
|
||||
return [coordO(y, x, o, o), coordO(y, x, 1 - o, o), coordO(y, x, 1 - o, 1 - o), coordO(y, x, o, 1 - o)];
|
||||
}
|
||||
|
||||
function attachTooltip(layer, tip) {
|
||||
layer.bindTooltip(tip, { direction: 'top' }).bindPopup(tip);
|
||||
}
|
||||
|
||||
function addSite(name, y1, x1, y2, x2, color, glyph) {
|
||||
/* resize tiny sites like lairs */
|
||||
var MIN_SIZE = .3;
|
||||
if (y2 - y1 < MIN_SIZE) {
|
||||
y1 = (y1 + y2) / 2 - MIN_SIZE / 2;
|
||||
y2 = y1 + MIN_SIZE;
|
||||
}
|
||||
if (x2 - x1 < MIN_SIZE) {
|
||||
x1 = (x1 + x2) / 2 - MIN_SIZE / 2;
|
||||
x2 = x1 + MIN_SIZE;
|
||||
}
|
||||
/* TODO: use glyph of the site instead of a polygon? */
|
||||
var polygon = L.polygon(
|
||||
[coord(y1, x1), coord(y2, x1), coord(y2, x2), coord(y1, x2)], {
|
||||
color: color,
|
||||
opacity: 1, fillOpacity: 0.7,
|
||||
weight: 3
|
||||
}).addTo(sitesLayer);
|
||||
|
||||
attachTooltip(polygon, name);
|
||||
}
|
||||
|
||||
function addWc(name, y, x, color) {
|
||||
var polygon = L.polygon(square(y, x, structureOffset), {
|
||||
color: color,
|
||||
opacity: 1, fillOpacity: 0.7,
|
||||
weight: 3
|
||||
}).addTo(constructionsLayer);
|
||||
|
||||
attachTooltip(polygon, name);
|
||||
}
|
||||
|
||||
function addRegion(name, y1, x1, y2, x2, color) {
|
||||
x1--; y2++;
|
||||
var polygon = L.polygon(
|
||||
[coord(y1, x1), coord(y2, x1), coord(y2, x2), coord(y1, x2)], {
|
||||
color: color,
|
||||
opacity: 0.5, fillOpacity: 0.3,
|
||||
weight: 1
|
||||
}).addTo(landmassesLayer);
|
||||
|
||||
attachTooltip(polygon, name);
|
||||
}
|
||||
|
||||
function addMountain(name, y, x, color) {
|
||||
x = worldWidth - x - 1;
|
||||
var polygon = L.polygon(
|
||||
[[x + mountainOffset / 2, y + mountainOffset], [x + mountainOffset / 2, y + 1 - mountainOffset], [x + 1 - mountainOffset, y + 0.5]], {
|
||||
color: color,
|
||||
opacity: 1, fillOpacity: 0.7,
|
||||
weight: 3
|
||||
}).addTo(mountainsLayer);
|
||||
|
||||
attachTooltip(polygon, name);
|
||||
|
||||
minx = Math.min(x, minx);
|
||||
miny = Math.min(y, miny);
|
||||
maxx = Math.max(x, maxx);
|
||||
maxy = Math.max(y, maxy);
|
||||
}
|
||||
|
||||
function coord(y, x) {
|
||||
x = worldWidth - x - 1;
|
||||
|
||||
minx = Math.min(x, minx);
|
||||
miny = Math.min(y, miny);
|
||||
maxx = Math.max(x, maxx);
|
||||
maxy = Math.max(y, maxy);
|
||||
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
function addBattle(name, y, x) {
|
||||
x = worldWidth - x - 1;
|
||||
var polygon = L.polygon(
|
||||
[[x + 0.5, y + battleOffset],
|
||||
[x + battleOffset, y + 0.5],
|
||||
[x + 0.5, y + 1 - battleOffset],
|
||||
[x + 1 - battleOffset, y + 0.5]], {
|
||||
color: '#f00',
|
||||
opacity: 1, fillOpacity: 0.7,
|
||||
weight: 3
|
||||
}).addTo(map);
|
||||
|
||||
attachTooltip(polygon, name);
|
||||
|
||||
minx = Math.min(x, minx);
|
||||
miny = Math.min(y, miny);
|
||||
maxx = Math.max(x, maxx);
|
||||
maxy = Math.max(y, maxy);
|
||||
}
|
|
@ -17,15 +17,13 @@
|
|||
</ul>
|
||||
|
||||
<div id="map" style="height: 400px"></div>
|
||||
<script>
|
||||
var map = L.map('map').setView([5, 5], 13);
|
||||
{{initMap}}
|
||||
{{- range $race, $civs := .Civilizations }}
|
||||
{{- range $civs }}
|
||||
{{- range .Sites }}
|
||||
{{ addSite . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
var imageUrl = '$suburi/map'
|
||||
var imageBounds = [[0, 0],
|
||||
[10, 10]];
|
||||
|
||||
var imageLayer = L.imageOverlay(imageUrl, imageBounds, { opacity: 0.5 });
|
||||
imageLayer.addTo(map);
|
||||
|
||||
</script>
|
||||
{{- end }}
|
|
@ -88,7 +88,6 @@
|
|||
|
||||
<script>
|
||||
var hash = document.location.hash;
|
||||
console.log("hah", hash)
|
||||
if (hash && hash.startsWith("#nav-")) {
|
||||
var hashPieces = hash.split('?');
|
||||
activeTab = $('.nav-link[data-bs-target="' + hashPieces[0] + '"]');
|
||||
|
|
Loading…
Reference in a new issue