More optimizations
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/atridad/LilGuy/internal/save"
|
"github.com/atridad/LilGuy/internal/save"
|
||||||
"github.com/atridad/LilGuy/internal/screens"
|
"github.com/atridad/LilGuy/internal/screens"
|
||||||
"github.com/atridad/LilGuy/internal/ui/menu"
|
"github.com/atridad/LilGuy/internal/ui/menu"
|
||||||
|
"github.com/atridad/LilGuy/internal/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -149,10 +150,22 @@ func newState() *state {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize Map Manager
|
||||||
|
mapManager := maps.NewManager()
|
||||||
|
plains, desert := maps.CreateDefaultMaps(float64(ScreenWidth), float64(ScreenHeight))
|
||||||
|
|
||||||
|
// Bake maps for performance
|
||||||
|
plains.Bake()
|
||||||
|
desert.Bake()
|
||||||
|
|
||||||
|
mapManager.RegisterMap(plains)
|
||||||
|
mapManager.RegisterMap(desert)
|
||||||
|
mapManager.SetCurrentMap("plains")
|
||||||
|
|
||||||
// Initialize screens
|
// Initialize screens
|
||||||
s.splashScreen = screens.NewSplashScreen()
|
s.splashScreen = screens.NewSplashScreen()
|
||||||
s.titleScreen = screens.NewTitleScreen()
|
s.titleScreen = screens.NewTitleScreen()
|
||||||
s.gameplayScreen = screens.NewGameplayScreen(ScreenWidth, ScreenHeight, &s.fpsEnabled, &s.portalVisibility)
|
s.gameplayScreen = screens.NewGameplayScreen(ScreenWidth, ScreenHeight, mapManager, &s.fpsEnabled, &s.portalVisibility)
|
||||||
s.pauseMenu = menu.NewPauseMenu()
|
s.pauseMenu = menu.NewPauseMenu()
|
||||||
|
|
||||||
// Wire up settings references
|
// Wire up settings references
|
||||||
|
|||||||
56
internal/maps/manager.go
Normal file
56
internal/maps/manager.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package maps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/atridad/LilGuy/internal/portal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
maps map[string]*Map
|
||||||
|
currentMap *Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager() *Manager {
|
||||||
|
return &Manager{
|
||||||
|
maps: make(map[string]*Map),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) RegisterMap(mp *Map) {
|
||||||
|
m.maps[mp.ID] = mp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetMap(id string) *Map {
|
||||||
|
return m.maps[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) SetCurrentMap(id string) error {
|
||||||
|
mp, ok := m.maps[id]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("map not found: %s", id)
|
||||||
|
}
|
||||||
|
m.currentMap = mp
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) CurrentMap() *Map {
|
||||||
|
return m.currentMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) ResolveTransition(event portal.TransitionEvent) (*Map, *portal.Portal) {
|
||||||
|
destMap := m.GetMap(event.DestinationMap)
|
||||||
|
if destMap == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
destPortal := destMap.GetPortalByID(event.DestinationPortal)
|
||||||
|
return destMap, destPortal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Reset() {
|
||||||
|
for _, mp := range m.maps {
|
||||||
|
mp.Reset()
|
||||||
|
}
|
||||||
|
// Reset to default map if needed, or let the caller do it
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package maps
|
|||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/atridad/LilGuy/internal/portal"
|
"github.com/atridad/LilGuy/internal/portal"
|
||||||
"github.com/atridad/LilGuy/internal/world"
|
"github.com/atridad/LilGuy/internal/world"
|
||||||
)
|
)
|
||||||
@@ -16,6 +17,8 @@ type Map struct {
|
|||||||
World *world.World
|
World *world.World
|
||||||
Portals []*portal.Portal
|
Portals []*portal.Portal
|
||||||
BackgroundColor color.NRGBA
|
BackgroundColor color.NRGBA
|
||||||
|
|
||||||
|
bakedImage *ebiten.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMap(id string, number int, displayName string, width, height float64) *Map {
|
func NewMap(id string, number int, displayName string, width, height float64) *Map {
|
||||||
@@ -44,6 +47,38 @@ func (m *Map) GetPortalByID(id string) *portal.Portal {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Renders the static world geometry to an offscreen image
|
||||||
|
func (m *Map) Bake() {
|
||||||
|
if m.Width <= 0 || m.Height <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.bakedImage = ebiten.NewImage(int(m.Width), int(m.Height))
|
||||||
|
m.bakedImage.Fill(m.BackgroundColor)
|
||||||
|
m.World.Draw(m.bakedImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) Draw(screen *ebiten.Image) {
|
||||||
|
if m.bakedImage != nil {
|
||||||
|
screen.DrawImage(m.bakedImage, nil)
|
||||||
|
} else {
|
||||||
|
screen.Fill(m.BackgroundColor)
|
||||||
|
m.World.Draw(screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw portals
|
||||||
|
for _, p := range m.Portals {
|
||||||
|
p.Draw(screen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resets the map state
|
||||||
|
func (m *Map) Reset() {
|
||||||
|
for _, p := range m.Portals {
|
||||||
|
p.Enabled = true
|
||||||
|
p.Visible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func CreateDefaultMaps(screenWidth, screenHeight float64) (*Map, *Map) {
|
func CreateDefaultMaps(screenWidth, screenHeight float64) (*Map, *Map) {
|
||||||
plains := CreatePlains(screenWidth, screenHeight)
|
plains := CreatePlains(screenWidth, screenHeight)
|
||||||
desert := CreateDesert(screenWidth, screenHeight)
|
desert := CreateDesert(screenWidth, screenHeight)
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type GameplayScreen struct {
|
|||||||
world *world.World
|
world *world.World
|
||||||
projectiles *projectile.Manager
|
projectiles *projectile.Manager
|
||||||
portals *portal.Manager
|
portals *portal.Manager
|
||||||
|
mapManager *maps.Manager
|
||||||
bounds hero.Bounds
|
bounds hero.Bounds
|
||||||
lastTick time.Time
|
lastTick time.Time
|
||||||
gameStartTime time.Time
|
gameStartTime time.Time
|
||||||
@@ -47,22 +48,19 @@ type GameplayScreen struct {
|
|||||||
showSaveNotification bool
|
showSaveNotification bool
|
||||||
|
|
||||||
portalVisibility *bool
|
portalVisibility *bool
|
||||||
|
|
||||||
currentMap *maps.Map
|
|
||||||
allMaps map[string]*maps.Map
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGameplayScreen(screenWidth, screenHeight int, fpsEnabled *bool, portalVisibility *bool) *GameplayScreen {
|
func NewGameplayScreen(screenWidth, screenHeight int, mapManager *maps.Manager, fpsEnabled *bool, portalVisibility *bool) *GameplayScreen {
|
||||||
cfg := config.Default()
|
cfg := config.Default()
|
||||||
plains, desert := maps.CreateDefaultMaps(float64(screenWidth), float64(screenHeight))
|
|
||||||
|
|
||||||
allMaps := make(map[string]*maps.Map)
|
// Ensure we have a current map
|
||||||
allMaps["plains"] = plains
|
if mapManager.CurrentMap() == nil {
|
||||||
allMaps["desert"] = desert
|
// Fallback or error later
|
||||||
|
}
|
||||||
|
currentMap := mapManager.CurrentMap()
|
||||||
|
|
||||||
portalMgr := portal.NewManager()
|
portalMgr := portal.NewManager()
|
||||||
|
for _, p := range currentMap.Portals {
|
||||||
for _, p := range plains.Portals {
|
|
||||||
portalMgr.AddPortal(p)
|
portalMgr.AddPortal(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,11 +85,12 @@ func NewGameplayScreen(screenWidth, screenHeight int, fpsEnabled *bool, portalVi
|
|||||||
X: cfg.HUD.X,
|
X: cfg.HUD.X,
|
||||||
Y: cfg.HUD.Y,
|
Y: cfg.HUD.Y,
|
||||||
Color: color.White,
|
Color: color.White,
|
||||||
ScreenName: "Plains",
|
ScreenName: currentMap.DisplayName,
|
||||||
},
|
},
|
||||||
world: plains.World,
|
world: currentMap.World,
|
||||||
projectiles: projectile.NewManager(),
|
projectiles: projectile.NewManager(),
|
||||||
portals: portalMgr,
|
portals: portalMgr,
|
||||||
|
mapManager: mapManager,
|
||||||
bounds: hero.Bounds{
|
bounds: hero.Bounds{
|
||||||
Width: float64(screenWidth),
|
Width: float64(screenWidth),
|
||||||
Height: float64(screenHeight),
|
Height: float64(screenHeight),
|
||||||
@@ -101,8 +100,6 @@ func NewGameplayScreen(screenWidth, screenHeight int, fpsEnabled *bool, portalVi
|
|||||||
gameStartTime: time.Now(),
|
gameStartTime: time.Now(),
|
||||||
fpsEnabled: fpsEnabled,
|
fpsEnabled: fpsEnabled,
|
||||||
portalVisibility: portalVisibility,
|
portalVisibility: portalVisibility,
|
||||||
currentMap: plains,
|
|
||||||
allMaps: allMaps,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gs.portals.OnTransition = gs.handlePortalTransition
|
gs.portals.OnTransition = gs.handlePortalTransition
|
||||||
@@ -166,13 +163,13 @@ func (g *GameplayScreen) checkPortalCollision() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GameplayScreen) handlePortalTransition(event portal.TransitionEvent) {
|
func (g *GameplayScreen) handlePortalTransition(event portal.TransitionEvent) {
|
||||||
destMap, exists := g.allMaps[event.DestinationMap]
|
destMap, destPortal := g.mapManager.ResolveTransition(event)
|
||||||
if !exists {
|
if destMap == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch to destination map
|
// Switch to destination map
|
||||||
g.currentMap = destMap
|
g.mapManager.SetCurrentMap(destMap.ID)
|
||||||
g.world = destMap.World
|
g.world = destMap.World
|
||||||
|
|
||||||
// Clear and reload portals for new map
|
// Clear and reload portals for new map
|
||||||
@@ -190,7 +187,6 @@ func (g *GameplayScreen) handlePortalTransition(event portal.TransitionEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find destination portal and position hero
|
// Find destination portal and position hero
|
||||||
destPortal := destMap.GetPortalByID(event.DestinationPortal)
|
|
||||||
if destPortal != nil {
|
if destPortal != nil {
|
||||||
// Position hero based on which side they're entering from
|
// Position hero based on which side they're entering from
|
||||||
switch destPortal.Side {
|
switch destPortal.Side {
|
||||||
@@ -229,21 +225,18 @@ func (g *GameplayScreen) trackFPS(delta time.Duration) {
|
|||||||
// Rendering
|
// Rendering
|
||||||
func (g *GameplayScreen) Draw(screen *ebiten.Image) {
|
func (g *GameplayScreen) Draw(screen *ebiten.Image) {
|
||||||
cfg := config.Default()
|
cfg := config.Default()
|
||||||
bgColor := cfg.Visual.BackgroundColor
|
|
||||||
if g.currentMap != nil {
|
|
||||||
bgColor = g.currentMap.BackgroundColor
|
|
||||||
}
|
|
||||||
screen.Fill(bgColor)
|
|
||||||
|
|
||||||
g.world.Draw(screen)
|
currentMap := g.mapManager.CurrentMap()
|
||||||
g.portals.Draw(screen)
|
if currentMap != nil {
|
||||||
|
currentMap.Draw(screen)
|
||||||
|
g.hud.ScreenName = currentMap.DisplayName
|
||||||
|
} else {
|
||||||
|
screen.Fill(cfg.Visual.BackgroundColor)
|
||||||
|
}
|
||||||
|
|
||||||
g.projectiles.Draw(screen)
|
g.projectiles.Draw(screen)
|
||||||
g.hero.Draw(screen)
|
g.hero.Draw(screen)
|
||||||
|
|
||||||
if g.currentMap != nil {
|
|
||||||
g.hud.ScreenName = g.currentMap.DisplayName
|
|
||||||
}
|
|
||||||
|
|
||||||
staminaColor := cfg.Visual.StaminaNormalColor
|
staminaColor := cfg.Visual.StaminaNormalColor
|
||||||
if g.hero.Stamina < g.hero.MaxStamina*config.StaminaLowThreshold {
|
if g.hero.Stamina < g.hero.MaxStamina*config.StaminaLowThreshold {
|
||||||
staminaColor = cfg.Visual.StaminaLowColor
|
staminaColor = cfg.Visual.StaminaLowColor
|
||||||
@@ -326,11 +319,9 @@ func (g *GameplayScreen) Reset() {
|
|||||||
screenWidth := int(g.bounds.Width)
|
screenWidth := int(g.bounds.Width)
|
||||||
screenHeight := int(g.bounds.Height)
|
screenHeight := int(g.bounds.Height)
|
||||||
|
|
||||||
plains, desert := maps.CreateDefaultMaps(float64(screenWidth), float64(screenHeight))
|
g.mapManager.Reset()
|
||||||
g.allMaps = make(map[string]*maps.Map)
|
g.mapManager.SetCurrentMap("plains")
|
||||||
g.allMaps["plains"] = plains
|
currentMap := g.mapManager.CurrentMap()
|
||||||
g.allMaps["desert"] = desert
|
|
||||||
g.currentMap = plains
|
|
||||||
|
|
||||||
g.hero = hero.New(hero.Config{
|
g.hero = hero.New(hero.Config{
|
||||||
StartX: cfg.Hero.StartX,
|
StartX: cfg.Hero.StartX,
|
||||||
@@ -342,10 +333,10 @@ func (g *GameplayScreen) Reset() {
|
|||||||
StaminaDrain: cfg.Hero.StaminaDrain,
|
StaminaDrain: cfg.Hero.StaminaDrain,
|
||||||
StaminaRegen: cfg.Hero.StaminaRegen,
|
StaminaRegen: cfg.Hero.StaminaRegen,
|
||||||
})
|
})
|
||||||
g.world = plains.World
|
g.world = currentMap.World
|
||||||
g.projectiles = projectile.NewManager()
|
g.projectiles = projectile.NewManager()
|
||||||
g.portals = portal.NewManager()
|
g.portals = portal.NewManager()
|
||||||
for _, p := range plains.Portals {
|
for _, p := range currentMap.Portals {
|
||||||
g.portals.AddPortal(p)
|
g.portals.AddPortal(p)
|
||||||
}
|
}
|
||||||
g.portals.OnTransition = g.handlePortalTransition
|
g.portals.OnTransition = g.handlePortalTransition
|
||||||
@@ -372,9 +363,9 @@ func (g *GameplayScreen) Reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GameplayScreen) SaveState() *save.GameState {
|
func (g *GameplayScreen) SaveState() *save.GameState {
|
||||||
currentMapID := "map1"
|
currentMapID := "plains"
|
||||||
if g.currentMap != nil {
|
if g.mapManager.CurrentMap() != nil {
|
||||||
currentMapID = g.currentMap.ID
|
currentMapID = g.mapManager.CurrentMap().ID
|
||||||
}
|
}
|
||||||
return &save.GameState{
|
return &save.GameState{
|
||||||
CurrentMap: currentMapID,
|
CurrentMap: currentMapID,
|
||||||
@@ -390,17 +381,18 @@ func (g *GameplayScreen) LoadState(state *save.GameState) {
|
|||||||
screenWidth := int(g.bounds.Width)
|
screenWidth := int(g.bounds.Width)
|
||||||
screenHeight := int(g.bounds.Height)
|
screenHeight := int(g.bounds.Height)
|
||||||
|
|
||||||
plains, desert := maps.CreateDefaultMaps(float64(screenWidth), float64(screenHeight))
|
g.mapManager.Reset()
|
||||||
g.allMaps = make(map[string]*maps.Map)
|
|
||||||
g.allMaps["plains"] = plains
|
|
||||||
g.allMaps["desert"] = desert
|
|
||||||
|
|
||||||
// load the saved map or default to plains
|
mapID := state.CurrentMap
|
||||||
savedMap := g.allMaps[state.CurrentMap]
|
if mapID == "" {
|
||||||
if savedMap == nil {
|
mapID = "plains"
|
||||||
savedMap = plains
|
|
||||||
}
|
}
|
||||||
g.currentMap = savedMap
|
|
||||||
|
if err := g.mapManager.SetCurrentMap(mapID); err != nil {
|
||||||
|
// Fallback to plains if map not found
|
||||||
|
g.mapManager.SetCurrentMap("plains")
|
||||||
|
}
|
||||||
|
currentMap := g.mapManager.CurrentMap()
|
||||||
|
|
||||||
g.hero = hero.New(hero.Config{
|
g.hero = hero.New(hero.Config{
|
||||||
StartX: state.HeroX,
|
StartX: state.HeroX,
|
||||||
@@ -413,10 +405,10 @@ func (g *GameplayScreen) LoadState(state *save.GameState) {
|
|||||||
StaminaRegen: cfg.Hero.StaminaRegen,
|
StaminaRegen: cfg.Hero.StaminaRegen,
|
||||||
})
|
})
|
||||||
g.hero.Stamina = state.HeroStamina
|
g.hero.Stamina = state.HeroStamina
|
||||||
g.world = g.currentMap.World
|
g.world = currentMap.World
|
||||||
g.projectiles = projectile.NewManager()
|
g.projectiles = projectile.NewManager()
|
||||||
g.portals = portal.NewManager()
|
g.portals = portal.NewManager()
|
||||||
for _, p := range g.currentMap.Portals {
|
for _, p := range currentMap.Portals {
|
||||||
g.portals.AddPortal(p)
|
g.portals.AddPortal(p)
|
||||||
}
|
}
|
||||||
g.portals.OnTransition = g.handlePortalTransition
|
g.portals.OnTransition = g.handlePortalTransition
|
||||||
|
|||||||
Reference in New Issue
Block a user