- Updated tileset configuration in 'tileset.tsj' to include new tiles and adjust dimensions. - Added 'settings.json' for key bindings related to climbing and movement actions. - Enhanced game logging in 'game.log' to track game start events and map loading. - Updated the game binary to the latest version.
249 lines
6.4 KiB
Go
249 lines
6.4 KiB
Go
package entities
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
|
|
"github.com/go-gl/mathgl/mgl64"
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
)
|
|
|
|
const (
|
|
TileSize = 32.0 // Varje block ar 32x32 world units
|
|
)
|
|
|
|
type World struct {
|
|
Width, Height, Depth int
|
|
Grid [][][]Entity
|
|
PortalTrigger string // Håller reda på om vi ska byta rum
|
|
}
|
|
|
|
func NewWorld(w, h, d int) *World {
|
|
grid := make([][][]Entity, w)
|
|
for x := 0; x < w; x++ {
|
|
grid[x] = make([][]Entity, h)
|
|
for y := 0; y < h; y++ {
|
|
grid[x][y] = make([]Entity, d)
|
|
}
|
|
}
|
|
return &World{Width: w, Height: h, Depth: d, Grid: grid, PortalTrigger: ""}
|
|
}
|
|
|
|
func (w *World) GetEntityAt(x, y, z int) Entity {
|
|
if x < 0 || x >= w.Width || y < 0 || y >= w.Height || z < 0 || z >= w.Depth {
|
|
return nil
|
|
}
|
|
return w.Grid[x][y][z]
|
|
}
|
|
|
|
func (w *World) SetEntityAt(x, y, z int, e Entity) {
|
|
if x < 0 || x >= w.Width || y < 0 || y >= w.Height || z < 0 || z >= w.Depth {
|
|
return
|
|
}
|
|
w.Grid[x][y][z] = e
|
|
if e != nil {
|
|
e.SetPos(mgl64.Vec3{float64(x), float64(y), float64(z)})
|
|
}
|
|
}
|
|
|
|
// Convert absolute 3D position to Grid index
|
|
func ToGridIndex(pos mgl64.Vec3) (int, int, int) {
|
|
return int(pos[0] / TileSize), int(pos[1] / TileSize), int(pos[2] / TileSize)
|
|
}
|
|
|
|
type TiledLayer struct {
|
|
Data []int `json:"data"`
|
|
Height int `json:"height"`
|
|
Name string `json:"name"`
|
|
Width int `json:"width"`
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
type TiledMap struct {
|
|
Height int `json:"height"`
|
|
Width int `json:"width"`
|
|
Layers []TiledLayer `json:"layers"`
|
|
}
|
|
|
|
type Portal struct {
|
|
pos mgl64.Vec3
|
|
TargetMap string
|
|
SideImg *ebiten.Image
|
|
TopImg *ebiten.Image
|
|
}
|
|
|
|
func (p *Portal) Pos() mgl64.Vec3 { return p.pos }
|
|
func (p *Portal) SetPos(pos mgl64.Vec3) { p.pos = pos }
|
|
func (p *Portal) IsBlocking() bool { return false }
|
|
func (p *Portal) IsMovable() bool { return false }
|
|
func (p *Portal) IsClimbable() bool { return false }
|
|
func (p *Portal) Move(dx, dy, dz float64) {}
|
|
func (p *Portal) Damage(amount int) {}
|
|
func (p *Portal) Pickup() bool { return false }
|
|
func (p *Portal) GetHealth() int { return 100 }
|
|
func (p *Portal) DrawSide(screen interface{}, x, y float64, alpha float32) {
|
|
scr := screen.(*ebiten.Image)
|
|
if p.SideImg != nil {
|
|
op := &ebiten.DrawImageOptions{}
|
|
_, sh := p.SideImg.Bounds().Dx(), p.SideImg.Bounds().Dy()
|
|
op.GeoM.Translate(x, y-float64(sh-int(TileSize)))
|
|
op.ColorScale.Scale(alpha, alpha, alpha, alpha)
|
|
scr.DrawImage(p.SideImg, op)
|
|
}
|
|
}
|
|
func (p *Portal) DrawTop(screen interface{}, x, y float64, alpha float32) {
|
|
scr := screen.(*ebiten.Image)
|
|
if p.TopImg != nil {
|
|
op := &ebiten.DrawImageOptions{}
|
|
_, sh := p.TopImg.Bounds().Dx(), p.TopImg.Bounds().Dy()
|
|
op.GeoM.Translate(x, y-float64(sh-int(TileSize)))
|
|
op.ColorScale.Scale(alpha, alpha, alpha, alpha)
|
|
scr.DrawImage(p.TopImg, op)
|
|
}
|
|
}
|
|
|
|
// EntityDef definierar egenskaperna för ett tile-id
|
|
type EntityDef struct {
|
|
ID int `json:"id"`
|
|
TiledID int `json:"tiled_id"`
|
|
Solid bool `json:"solid"`
|
|
Climbable bool `json:"climbable,omitempty"`
|
|
TargetMap string `json:"target_map,omitempty"`
|
|
SideImg *ebiten.Image `json:"-"`
|
|
TopImg *ebiten.Image `json:"-"`
|
|
Sprites struct {
|
|
Top string `json:"top"`
|
|
Side string `json:"side"`
|
|
} `json:"sprites"`
|
|
}
|
|
|
|
type EntityDefs struct {
|
|
EntityTypes map[string]EntityDef `json:"entity_types"`
|
|
}
|
|
|
|
var loadedDefs *EntityDefs
|
|
var loadedImages map[string]*ebiten.Image
|
|
|
|
// Ladda en värld från Tiled Export (.tmj)
|
|
func LoadWorldFromTiled(tmjPath string) (*World, error) {
|
|
log.Printf("Laddar värld från '%s'...", tmjPath)
|
|
if loadedDefs == nil {
|
|
defsData, err := os.ReadFile("assets/entity_defs.json")
|
|
if err == nil {
|
|
var defs EntityDefs
|
|
json.Unmarshal(defsData, &defs)
|
|
loadedDefs = &defs
|
|
log.Printf("Laddade %d st entity definitions från JSON.", len(defs.EntityTypes))
|
|
} else {
|
|
log.Printf("Varning: Kunde inte läsa entity_defs.json: %v", err)
|
|
}
|
|
}
|
|
|
|
fileData, err := os.ReadFile(tmjPath)
|
|
if err != nil {
|
|
log.Printf("Kunde inte läsa map %s: %v", tmjPath, err)
|
|
return nil, fmt.Errorf("kunde inte lasa map %s: %v", tmjPath, err)
|
|
}
|
|
|
|
var tMap TiledMap
|
|
if err := json.Unmarshal(fileData, &tMap); err != nil {
|
|
log.Printf("Kunde inte parsa JSON i %s: %v", tmjPath, err)
|
|
return nil, err
|
|
}
|
|
|
|
log.Printf("Map parsead med %d lager.", len(tMap.Layers))
|
|
|
|
w := NewWorld(tMap.Width, 10, tMap.Height) // 10 i djup(height y), tileHeight z = height i Json
|
|
|
|
// Preload images (simple abstraction)
|
|
loadImage := func(name string) *ebiten.Image {
|
|
if loadedImages == nil {
|
|
loadedImages = make(map[string]*ebiten.Image)
|
|
}
|
|
if img, ok := loadedImages[name]; ok {
|
|
return img
|
|
}
|
|
imgPath := "assets/images/1 Tiles/" + name + ".png"
|
|
if len(name) > 3 && name[:3] == "../" {
|
|
imgPath = "assets/images/" + name[3:] + ".png"
|
|
}
|
|
|
|
img, _, err := ebitenutil.NewImageFromFile(imgPath)
|
|
if err != nil {
|
|
fmt.Println("Warning, missing image:", imgPath, err)
|
|
}
|
|
loadedImages[name] = img
|
|
return img
|
|
}
|
|
|
|
// Mappa ID till Def
|
|
idToDef := make(map[int]EntityDef)
|
|
if loadedDefs != nil {
|
|
for _, def := range loadedDefs.EntityTypes {
|
|
idToDef[def.TiledID] = def
|
|
}
|
|
}
|
|
|
|
tile12 := loadImage("Tile_12")
|
|
tile02 := loadImage("Tile_02")
|
|
tile31 := loadImage("Tile_31")
|
|
|
|
for _, layer := range tMap.Layers {
|
|
if layer.Type != "tilelayer" {
|
|
continue
|
|
}
|
|
|
|
var yLevel int
|
|
addedTiles := 0
|
|
fmt.Sscanf(layer.Name, "Y%d", &yLevel)
|
|
if yLevel < 0 || yLevel >= 10 {
|
|
continue
|
|
}
|
|
|
|
for i, tileID := range layer.Data {
|
|
if tileID == 0 {
|
|
continue
|
|
}
|
|
addedTiles++
|
|
x := i % layer.Width
|
|
z := i / layer.Width
|
|
|
|
var ent Entity
|
|
|
|
def, ok := idToDef[tileID]
|
|
if ok {
|
|
topImg := loadImage(def.Sprites.Top) // Om vi har generiska namn
|
|
sideImg := loadImage(def.Sprites.Side)
|
|
|
|
if def.TargetMap != "" {
|
|
ent = &Portal{TargetMap: def.TargetMap, SideImg: sideImg, TopImg: topImg}
|
|
} else {
|
|
ent = &Tile{SideImg: sideImg, TopImg: topImg, Climbable: def.Climbable}
|
|
}
|
|
} else {
|
|
// Fallback om def saknas
|
|
if tileID == 3 { // Portal ID
|
|
target := "room2"
|
|
if tmjPath == "assets/maps/room2.tmj" {
|
|
target = "room1"
|
|
}
|
|
ent = &Portal{TargetMap: target}
|
|
} else {
|
|
if yLevel < 9 {
|
|
ent = &Tile{SideImg: tile12, TopImg: tile12}
|
|
} else {
|
|
ent = &Tile{SideImg: tile02, TopImg: tile31}
|
|
}
|
|
}
|
|
}
|
|
|
|
w.SetEntityAt(x, yLevel, z, ent)
|
|
}
|
|
}
|
|
|
|
return w, nil
|
|
}
|